Coverage Report

Created: 2026-02-14 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/dgn/dgnstroke.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  Microstation DGN Access Library
4
 * Purpose:  Code to stroke Arcs/Ellipses into polylines.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2001, Avenza Systems Inc, http://www.avenza.com/
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "dgnlibp.h"
14
#include <cmath>
15
16
constexpr double DEG_TO_RAD = M_PI / 180.0;
17
18
/************************************************************************/
19
/*                         ComputePointOnArc()                          */
20
/************************************************************************/
21
22
static void ComputePointOnArc2D(double dfPrimary, double dfSecondary,
23
                                double dfAxisRotation, double dfAngle,
24
                                double *pdfX, double *pdfY)
25
26
7.87M
{
27
    // dfAxisRotation and dfAngle are supposed to be in Radians
28
7.87M
    const double dfCosRotation = cos(dfAxisRotation);
29
7.87M
    const double dfSinRotation = sin(dfAxisRotation);
30
7.87M
    const double dfEllipseX = dfPrimary * cos(dfAngle);
31
7.87M
    const double dfEllipseY = dfSecondary * sin(dfAngle);
32
33
7.87M
    *pdfX = dfEllipseX * dfCosRotation - dfEllipseY * dfSinRotation;
34
7.87M
    *pdfY = dfEllipseX * dfSinRotation + dfEllipseY * dfCosRotation;
35
7.87M
}
36
37
/************************************************************************/
38
/*                            DGNStrokeArc()                            */
39
/************************************************************************/
40
41
/**
42
 * Generate a polyline approximation of an arc.
43
 *
44
 * Produce a series of equidistant (actually equi-angle) points along
45
 * an arc.  Currently this only works for 2D arcs (and ellipses).
46
 *
47
 * @param hFile the DGN file to which the arc belongs (currently not used).
48
 * @param psArc the arc to be approximated.
49
 * @param nPoints the number of points to use to approximate the arc.
50
 * @param pasPoints the array of points into which to put the results.
51
 * There must be room for at least nPoints points.
52
 *
53
 * @return TRUE on success or FALSE on failure.
54
 */
55
56
int DGNStrokeArc(CPL_UNUSED DGNHandle hFile, DGNElemArc *psArc, int nPoints,
57
                 DGNPoint *pasPoints)
58
189k
{
59
189k
    if (nPoints < 2)
60
0
        return FALSE;
61
62
189k
    if (psArc->primary_axis == 0.0 || psArc->secondary_axis == 0.0)
63
63.5k
    {
64
63.5k
        CPLError(CE_Warning, CPLE_AppDefined,
65
63.5k
                 "Zero primary or secondary axis in DGNStrokeArc().");
66
63.5k
        return FALSE;
67
63.5k
    }
68
69
125k
    const double dfAngleStep = psArc->sweepang / (nPoints - 1);
70
7.99M
    for (int i = 0; i < nPoints; i++)
71
7.87M
    {
72
7.87M
        const double dfAngle = (psArc->startang + dfAngleStep * i) * DEG_TO_RAD;
73
74
7.87M
        ComputePointOnArc2D(psArc->primary_axis, psArc->secondary_axis,
75
7.87M
                            psArc->rotation * DEG_TO_RAD, dfAngle,
76
7.87M
                            &(pasPoints[i].x), &(pasPoints[i].y));
77
7.87M
        pasPoints[i].x += psArc->origin.x;
78
7.87M
        pasPoints[i].y += psArc->origin.y;
79
7.87M
        pasPoints[i].z = psArc->origin.z;
80
7.87M
    }
81
82
125k
    return TRUE;
83
189k
}
84
85
/************************************************************************/
86
/*                           DGNStrokeCurve()                           */
87
/************************************************************************/
88
89
/**
90
 * Generate a polyline approximation of an curve.
91
 *
92
 * Produce a series of equidistant points along a microstation curve element.
93
 * Currently this only works for 2D.
94
 *
95
 * @param hFile the DGN file to which the arc belongs (currently not used).
96
 * @param psCurve the curve to be approximated.
97
 * @param nPoints the number of points to use to approximate the curve.
98
 * @param pasPoints the array of points into which to put the results.
99
 * There must be room for at least nPoints points.
100
 *
101
 * @return TRUE on success or FALSE on failure.
102
 */
103
104
int DGNStrokeCurve(CPL_UNUSED DGNHandle hFile, DGNElemMultiPoint *psCurve,
105
                   int nPoints, DGNPoint *pasPoints)
106
12.9k
{
107
12.9k
    const int nDGNPoints = psCurve->num_vertices;
108
109
12.9k
    if (nDGNPoints < 6)
110
4.19k
        return FALSE;
111
112
8.76k
    if (nPoints < nDGNPoints - 4)
113
0
        return FALSE;
114
115
    /* -------------------------------------------------------------------- */
116
    /*      Compute the Compute the slopes/distances of the segments.       */
117
    /* -------------------------------------------------------------------- */
118
8.76k
    double *padfMx =
119
8.76k
        static_cast<double *>(CPLMalloc(sizeof(double) * nDGNPoints));
120
8.76k
    double *padfMy =
121
8.76k
        static_cast<double *>(CPLMalloc(sizeof(double) * nDGNPoints));
122
8.76k
    double *padfD =
123
8.76k
        static_cast<double *>(CPLMalloc(sizeof(double) * nDGNPoints));
124
8.76k
    double *padfTx =
125
8.76k
        static_cast<double *>(CPLMalloc(sizeof(double) * nDGNPoints));
126
8.76k
    double *padfTy =
127
8.76k
        static_cast<double *>(CPLMalloc(sizeof(double) * nDGNPoints));
128
129
8.76k
    double dfTotalD = 0.0;
130
131
8.76k
    DGNPoint *pasDGNPoints = psCurve->vertices;
132
133
5.82M
    for (int k = 0; k < nDGNPoints - 1; k++)
134
5.81M
    {
135
        /* coverity[overrun-local] */
136
5.81M
        padfD[k] = sqrt((pasDGNPoints[k + 1].x - pasDGNPoints[k].x) *
137
5.81M
                            (pasDGNPoints[k + 1].x - pasDGNPoints[k].x) +
138
5.81M
                        (pasDGNPoints[k + 1].y - pasDGNPoints[k].y) *
139
5.81M
                            (pasDGNPoints[k + 1].y - pasDGNPoints[k].y));
140
5.81M
        if (padfD[k] == 0.0)
141
4.92M
        {
142
4.92M
            padfD[k] = 0.0001;
143
4.92M
            padfMx[k] = 0.0;
144
4.92M
            padfMy[k] = 0.0;
145
4.92M
        }
146
889k
        else
147
889k
        {
148
889k
            padfMx[k] = (pasDGNPoints[k + 1].x - pasDGNPoints[k].x) / padfD[k];
149
889k
            padfMy[k] = (pasDGNPoints[k + 1].y - pasDGNPoints[k].y) / padfD[k];
150
889k
        }
151
152
5.81M
        if (k > 1 && k < nDGNPoints - 3)
153
5.78M
            dfTotalD += padfD[k];
154
5.81M
    }
155
156
    /* -------------------------------------------------------------------- */
157
    /*      Compute the Tx, and Ty coefficients for each segment.           */
158
    /* -------------------------------------------------------------------- */
159
5.79M
    for (int k = 2; k < nDGNPoints - 2; k++)
160
5.79M
    {
161
5.79M
        if (fabs(padfMx[k + 1] - padfMx[k]) == 0.0 &&
162
4.93M
            fabs(padfMx[k - 1] - padfMx[k - 2]) == 0.0)
163
4.65M
        {
164
4.65M
            padfTx[k] = (padfMx[k] + padfMx[k - 1]) / 2;
165
4.65M
        }
166
1.13M
        else
167
1.13M
        {
168
1.13M
            padfTx[k] = (padfMx[k - 1] * fabs(padfMx[k + 1] - padfMx[k]) +
169
1.13M
                         padfMx[k] * fabs(padfMx[k - 1] - padfMx[k - 2])) /
170
1.13M
                        (std::abs(padfMx[k + 1] - padfMx[k]) +
171
1.13M
                         std::abs(padfMx[k - 1] - padfMx[k - 2]));
172
1.13M
        }
173
174
5.79M
        if (fabs(padfMy[k + 1] - padfMy[k]) == 0.0 &&
175
4.91M
            fabs(padfMy[k - 1] - padfMy[k - 2]) == 0.0)
176
4.63M
        {
177
4.63M
            padfTy[k] = (padfMy[k] + padfMy[k - 1]) / 2;
178
4.63M
        }
179
1.15M
        else
180
1.15M
        {
181
1.15M
            padfTy[k] = (padfMy[k - 1] * fabs(padfMy[k + 1] - padfMy[k]) +
182
1.15M
                         padfMy[k] * fabs(padfMy[k - 1] - padfMy[k - 2])) /
183
1.15M
                        (std::abs(padfMy[k + 1] - padfMy[k]) +
184
1.15M
                         std::abs(padfMy[k - 1] - padfMy[k - 2]));
185
1.15M
        }
186
5.79M
    }
187
188
    /* -------------------------------------------------------------------- */
189
    /*      Determine a step size in D.  We scale things so that we have    */
190
    /*      roughly equidistant steps in D, but assume we also want to      */
191
    /*      include every node along the way.                               */
192
    /* -------------------------------------------------------------------- */
193
8.76k
    double dfStepSize = dfTotalD / (nPoints - (nDGNPoints - 4) - 1);
194
195
    /* ==================================================================== */
196
    /*      Process each of the segments.                                   */
197
    /* ==================================================================== */
198
8.76k
    double dfD = dfStepSize;
199
8.76k
    int iOutPoint = 0;
200
201
5.79M
    for (int k = 2; k < nDGNPoints - 3; k++)
202
5.78M
    {
203
        /* --------------------------------------------------------------------
204
         */
205
        /*      Compute the "x" coefficients for this segment. */
206
        /* --------------------------------------------------------------------
207
         */
208
5.78M
        const double dfCx = padfTx[k];
209
5.78M
        const double dfBx =
210
5.78M
            (3.0 * (pasDGNPoints[k + 1].x - pasDGNPoints[k].x) / padfD[k] -
211
5.78M
             2.0 * padfTx[k] - padfTx[k + 1]) /
212
5.78M
            padfD[k];
213
5.78M
        const double dfAx =
214
5.78M
            (padfTx[k] + padfTx[k + 1] -
215
5.78M
             2 * (pasDGNPoints[k + 1].x - pasDGNPoints[k].x) / padfD[k]) /
216
5.78M
            (padfD[k] * padfD[k]);
217
218
        /* --------------------------------------------------------------------
219
         */
220
        /*      Compute the Y coefficients for this segment. */
221
        /* --------------------------------------------------------------------
222
         */
223
5.78M
        const double dfCy = padfTy[k];
224
5.78M
        const double dfBy =
225
5.78M
            (3.0 * (pasDGNPoints[k + 1].y - pasDGNPoints[k].y) / padfD[k] -
226
5.78M
             2.0 * padfTy[k] - padfTy[k + 1]) /
227
5.78M
            padfD[k];
228
5.78M
        const double dfAy =
229
5.78M
            (padfTy[k] + padfTy[k + 1] -
230
5.78M
             2 * (pasDGNPoints[k + 1].y - pasDGNPoints[k].y) / padfD[k]) /
231
5.78M
            (padfD[k] * padfD[k]);
232
233
        /* --------------------------------------------------------------------
234
         */
235
        /*      Add the start point for this segment. */
236
        /* --------------------------------------------------------------------
237
         */
238
5.78M
        pasPoints[iOutPoint].x = pasDGNPoints[k].x;
239
5.78M
        pasPoints[iOutPoint].y = pasDGNPoints[k].y;
240
5.78M
        pasPoints[iOutPoint].z = 0.0;
241
5.78M
        iOutPoint++;
242
243
        /* --------------------------------------------------------------------
244
         */
245
        /*      Step along, adding intermediate points. */
246
        /* --------------------------------------------------------------------
247
         */
248
29.1M
        while (dfD < padfD[k] && iOutPoint < nPoints - (nDGNPoints - k - 1))
249
23.3M
        {
250
23.3M
            pasPoints[iOutPoint].x = dfAx * dfD * dfD * dfD + dfBx * dfD * dfD +
251
23.3M
                                     dfCx * dfD + pasDGNPoints[k].x;
252
23.3M
            pasPoints[iOutPoint].y = dfAy * dfD * dfD * dfD + dfBy * dfD * dfD +
253
23.3M
                                     dfCy * dfD + pasDGNPoints[k].y;
254
23.3M
            pasPoints[iOutPoint].z = 0.0;
255
23.3M
            iOutPoint++;
256
257
23.3M
            dfD += dfStepSize;
258
23.3M
        }
259
260
5.78M
        dfD -= padfD[k];
261
5.78M
    }
262
263
    /* -------------------------------------------------------------------- */
264
    /*      Add the start point for this segment.                           */
265
    /* -------------------------------------------------------------------- */
266
35.0k
    while (iOutPoint < nPoints)
267
26.2k
    {
268
26.2k
        pasPoints[iOutPoint].x = pasDGNPoints[nDGNPoints - 3].x;
269
26.2k
        pasPoints[iOutPoint].y = pasDGNPoints[nDGNPoints - 3].y;
270
26.2k
        pasPoints[iOutPoint].z = 0.0;
271
26.2k
        iOutPoint++;
272
26.2k
    }
273
274
    /* -------------------------------------------------------------------- */
275
    /*      Cleanup.                                                        */
276
    /* -------------------------------------------------------------------- */
277
8.76k
    CPLFree(padfMx);
278
8.76k
    CPLFree(padfMy);
279
8.76k
    CPLFree(padfD);
280
8.76k
    CPLFree(padfTx);
281
8.76k
    CPLFree(padfTy);
282
283
8.76k
    return TRUE;
284
8.76k
}
285
286
/************************************************************************/
287
/*                                main()                                */
288
/*                                                                      */
289
/*      test mainline                                                   */
290
/************************************************************************/
291
#ifdef notdef
292
int main(int argc, char **argv)
293
294
{
295
    if (argc != 5)
296
    {
297
        printf(  // ok
298
            "Usage: stroke primary_axis secondary_axis axis_rotation angle\n");
299
        exit(1);
300
    }
301
302
    const double dfPrimary = CPLAtof(argv[1]);
303
    const double dfSecondary = CPLAtof(argv[2]);
304
    const double dfAxisRotation = CPLAtof(argv[3]) / 180 * M_PI;
305
    const double dfAngle = CPLAtof(argv[4]) / 180 * M_PI;
306
307
    double dfX = 0.0;
308
    double dfY = 0.0;
309
    ComputePointOnArc2D(dfPrimary, dfSecondary, dfAxisRotation, dfAngle, &dfX,
310
                        &dfY);
311
312
    printf("X=%.2f, Y=%.2f\n", dfX, dfY);  // ok
313
314
    return 0;
315
}
316
317
#endif