Coverage Report

Created: 2025-06-09 08:44

/src/gdal/ogr/ogrsf_frmts/dgn/dgnhelp.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  Microstation DGN Access Library
4
 * Purpose:  Application visible helper functions for parsing DGN information.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2002, Avenza Systems Inc, http://www.avenza.com/
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "dgnlibp.h"
14
15
static const unsigned char abyDefaultPCT[256][3] = {
16
    {255, 255, 255}, {0, 0, 255},     {0, 255, 0},     {255, 0, 0},
17
    {255, 255, 0},   {255, 0, 255},   {255, 127, 0},   {0, 255, 255},
18
    {64, 64, 64},    {192, 192, 192}, {254, 0, 96},    {160, 224, 0},
19
    {0, 254, 160},   {128, 0, 160},   {176, 176, 176}, {0, 240, 240},
20
    {240, 240, 240}, {0, 0, 240},     {0, 240, 0},     {240, 0, 0},
21
    {240, 240, 0},   {240, 0, 240},   {240, 122, 0},   {0, 240, 240},
22
    {240, 240, 240}, {0, 0, 240},     {0, 240, 0},     {240, 0, 0},
23
    {240, 240, 0},   {240, 0, 240},   {240, 122, 0},   {0, 225, 225},
24
    {225, 225, 225}, {0, 0, 225},     {0, 225, 0},     {225, 0, 0},
25
    {225, 225, 0},   {225, 0, 225},   {225, 117, 0},   {0, 225, 225},
26
    {225, 225, 225}, {0, 0, 225},     {0, 225, 0},     {225, 0, 0},
27
    {225, 225, 0},   {225, 0, 225},   {225, 117, 0},   {0, 210, 210},
28
    {210, 210, 210}, {0, 0, 210},     {0, 210, 0},     {210, 0, 0},
29
    {210, 210, 0},   {210, 0, 210},   {210, 112, 0},   {0, 210, 210},
30
    {210, 210, 210}, {0, 0, 210},     {0, 210, 0},     {210, 0, 0},
31
    {210, 210, 0},   {210, 0, 210},   {210, 112, 0},   {0, 195, 195},
32
    {195, 195, 195}, {0, 0, 195},     {0, 195, 0},     {195, 0, 0},
33
    {195, 195, 0},   {195, 0, 195},   {195, 107, 0},   {0, 195, 195},
34
    {195, 195, 195}, {0, 0, 195},     {0, 195, 0},     {195, 0, 0},
35
    {195, 195, 0},   {195, 0, 195},   {195, 107, 0},   {0, 180, 180},
36
    {180, 180, 180}, {0, 0, 180},     {0, 180, 0},     {180, 0, 0},
37
    {180, 180, 0},   {180, 0, 180},   {180, 102, 0},   {0, 180, 180},
38
    {180, 180, 180}, {0, 0, 180},     {0, 180, 0},     {180, 0, 0},
39
    {180, 180, 0},   {180, 0, 180},   {180, 102, 0},   {0, 165, 165},
40
    {165, 165, 165}, {0, 0, 165},     {0, 165, 0},     {165, 0, 0},
41
    {165, 165, 0},   {165, 0, 165},   {165, 97, 0},    {0, 165, 165},
42
    {165, 165, 165}, {0, 0, 165},     {0, 165, 0},     {165, 0, 0},
43
    {165, 165, 0},   {165, 0, 165},   {165, 97, 0},    {0, 150, 150},
44
    {150, 150, 150}, {0, 0, 150},     {0, 150, 0},     {150, 0, 0},
45
    {150, 150, 0},   {150, 0, 150},   {150, 92, 0},    {0, 150, 150},
46
    {150, 150, 150}, {0, 0, 150},     {0, 150, 0},     {150, 0, 0},
47
    {150, 150, 0},   {150, 0, 150},   {150, 92, 0},    {0, 135, 135},
48
    {135, 135, 135}, {0, 0, 135},     {0, 135, 0},     {135, 0, 0},
49
    {135, 135, 0},   {135, 0, 135},   {135, 87, 0},    {0, 135, 135},
50
    {135, 135, 135}, {0, 0, 135},     {0, 135, 0},     {135, 0, 0},
51
    {135, 135, 0},   {135, 0, 135},   {135, 87, 0},    {0, 120, 120},
52
    {120, 120, 120}, {0, 0, 120},     {0, 120, 0},     {120, 0, 0},
53
    {120, 120, 0},   {120, 0, 120},   {120, 82, 0},    {0, 120, 120},
54
    {120, 120, 120}, {0, 0, 120},     {0, 120, 0},     {120, 0, 0},
55
    {120, 120, 0},   {120, 0, 120},   {120, 82, 0},    {0, 105, 105},
56
    {105, 105, 105}, {0, 0, 105},     {0, 105, 0},     {105, 0, 0},
57
    {105, 105, 0},   {105, 0, 105},   {105, 77, 0},    {0, 105, 105},
58
    {105, 105, 105}, {0, 0, 105},     {0, 105, 0},     {105, 0, 0},
59
    {105, 105, 0},   {105, 0, 105},   {105, 77, 0},    {0, 90, 90},
60
    {90, 90, 90},    {0, 0, 90},      {0, 90, 0},      {90, 0, 0},
61
    {90, 90, 0},     {90, 0, 90},     {90, 72, 0},     {0, 90, 90},
62
    {90, 90, 90},    {0, 0, 90},      {0, 90, 0},      {90, 0, 0},
63
    {90, 90, 0},     {90, 0, 90},     {90, 72, 0},     {0, 75, 75},
64
    {75, 75, 75},    {0, 0, 75},      {0, 75, 0},      {75, 0, 0},
65
    {75, 75, 0},     {75, 0, 75},     {75, 67, 0},     {0, 75, 75},
66
    {75, 75, 75},    {0, 0, 75},      {0, 75, 0},      {75, 0, 0},
67
    {75, 75, 0},     {75, 0, 75},     {75, 67, 0},     {0, 60, 60},
68
    {60, 60, 60},    {0, 0, 60},      {0, 60, 0},      {60, 0, 0},
69
    {60, 60, 0},     {60, 0, 60},     {60, 62, 0},     {0, 60, 60},
70
    {60, 60, 60},    {0, 0, 60},      {0, 60, 0},      {60, 0, 0},
71
    {60, 60, 0},     {60, 0, 60},     {60, 62, 0},     {0, 45, 45},
72
    {45, 45, 45},    {0, 0, 45},      {0, 45, 0},      {45, 0, 0},
73
    {45, 45, 0},     {45, 0, 45},     {45, 57, 0},     {0, 45, 45},
74
    {45, 45, 45},    {0, 0, 45},      {0, 45, 0},      {45, 0, 0},
75
    {45, 45, 0},     {45, 0, 45},     {45, 57, 0},     {0, 30, 30},
76
    {30, 30, 30},    {0, 0, 30},      {0, 30, 0},      {30, 0, 0},
77
    {30, 30, 0},     {30, 0, 30},     {30, 52, 0},     {0, 30, 30},
78
    {30, 30, 30},    {0, 0, 30},      {0, 30, 0},      {30, 0, 0},
79
    {30, 30, 0},     {30, 0, 30},     {192, 192, 192}, {28, 0, 100}};
80
81
/************************************************************************/
82
/*                           DGNLookupColor()                           */
83
/************************************************************************/
84
85
/**
86
 * Translate color index into RGB values.
87
 *
88
 * If no color table has yet been encountered in the file a hard-coded
89
 * "default" color table will be used.  This seems to be what Microstation
90
 * uses as a color table when there isn't one in a DGN file but I am not
91
 * absolutely convinced it is appropriate.
92
 *
93
 * @param hDGN the file.
94
 * @param color_index the color index to lookup.
95
 * @param red location to put red component.
96
 * @param green location to put green component.
97
 * @param blue location to put blue component.
98
 *
99
 * @return TRUE on success or FALSE on failure.  May fail if color_index is
100
 * out of range.
101
 */
102
103
int DGNLookupColor(DGNHandle hDGN, int color_index, int *red, int *green,
104
                   int *blue)
105
106
1.19M
{
107
1.19M
    if (color_index < 0 || color_index > 255)
108
0
        return FALSE;
109
110
1.19M
    DGNInfo *psDGN = (DGNInfo *)hDGN;
111
112
1.19M
    if (!psDGN->got_color_table)
113
861k
    {
114
861k
        *red = abyDefaultPCT[color_index][0];
115
861k
        *green = abyDefaultPCT[color_index][1];
116
861k
        *blue = abyDefaultPCT[color_index][2];
117
861k
    }
118
334k
    else
119
334k
    {
120
334k
        *red = psDGN->color_table[color_index][0];
121
334k
        *green = psDGN->color_table[color_index][1];
122
334k
        *blue = psDGN->color_table[color_index][2];
123
334k
    }
124
125
1.19M
    return TRUE;
126
1.19M
}
127
128
/************************************************************************/
129
/*                        DGNGetShapeFillInfo()                         */
130
/************************************************************************/
131
132
/**
133
 * Fetch fill color for a shape.
134
 *
135
 * This method will check for a 0x0041 user attribute linkaged with fill
136
 * color information for the element.  If found the function returns TRUE,
137
 * and places the fill color in *pnColor, otherwise FALSE is returned and
138
 * *pnColor is not updated.
139
 *
140
 * @param hDGN the file.
141
 * @param psElem the element.
142
 * @param pnColor the location to return the fill color.
143
 *
144
 * @return TRUE on success or FALSE on failure.
145
 */
146
147
int DGNGetShapeFillInfo(DGNHandle hDGN, DGNElemCore *psElem, int *pnColor)
148
149
77.7k
{
150
80.6k
    for (int iLink = 0; true; iLink++)
151
80.6k
    {
152
80.6k
        int nLinkType = 0;
153
80.6k
        int nLinkSize = 0;
154
80.6k
        unsigned char *pabyData = DGNGetLinkage(hDGN, psElem, iLink, &nLinkType,
155
80.6k
                                                nullptr, nullptr, &nLinkSize);
156
80.6k
        if (pabyData == nullptr)
157
77.6k
            return FALSE;
158
159
2.95k
        if (nLinkType == DGNLT_SHAPE_FILL && nLinkSize >= 9)
160
75
        {
161
75
            *pnColor = pabyData[8];
162
75
            return TRUE;
163
75
        }
164
2.95k
    }
165
77.7k
}
166
167
/************************************************************************/
168
/*                        DGNGetAssocID()                               */
169
/************************************************************************/
170
171
/**
172
 * Fetch association id for an element.
173
 *
174
 * This method will check if an element has an association id, and if so
175
 * returns it, otherwise returning -1.  Association ids are kept as a
176
 * user attribute linkage where present.
177
 *
178
 * @param hDGN the file.
179
 * @param psElem the element.
180
 *
181
 * @return The id or -1 on failure.
182
 */
183
184
int DGNGetAssocID(DGNHandle hDGN, DGNElemCore *psElem)
185
186
0
{
187
0
    for (int iLink = 0; true; iLink++)
188
0
    {
189
0
        int nLinkType = 0;
190
0
        int nLinkSize = 0;
191
0
        unsigned char *pabyData = DGNGetLinkage(hDGN, psElem, iLink, &nLinkType,
192
0
                                                nullptr, nullptr, &nLinkSize);
193
0
        if (pabyData == nullptr)
194
0
            return -1;
195
196
0
        if (nLinkType == DGNLT_ASSOC_ID && nLinkSize >= 8)
197
0
        {
198
0
            return pabyData[4] + pabyData[5] * 256 + pabyData[6] * 256 * 256 +
199
0
                   pabyData[7] * 256 * 256 * 256;
200
0
        }
201
0
    }
202
0
}
203
204
/************************************************************************/
205
/*                          DGNRad50ToAscii()                           */
206
/*                                                                      */
207
/*      Convert one 16-bits Radix-50 to ASCII (3 chars).                */
208
/************************************************************************/
209
210
void DGNRad50ToAscii(unsigned short sRad50, char *str)
211
425k
{
212
425k
    char ch = '\0';
213
425k
    unsigned short saQuots[3] = {1600, 40, 1};
214
215
1.70M
    for (int i = 0; i < 3; i++)
216
1.27M
    {
217
1.27M
        unsigned short sValue = sRad50;
218
1.27M
        sValue /= saQuots[i];
219
        /* Map 0..39 to ASCII */
220
1.27M
        if (sValue == 0)
221
832k
            ch = ' '; /* space */
222
442k
        else if (/*sValue >= 1 &&*/ sValue <= 26)
223
336k
            ch = (char)(sValue - 1 + 'A'); /* printable alpha A..Z */
224
105k
        else if (sValue == 27)
225
6.05k
            ch = '$'; /* dollar */
226
99.7k
        else if (sValue == 28)
227
5.18k
            ch = '.'; /* period */
228
94.5k
        else if (sValue == 29)
229
2.80k
            ch = ' '; /* unused char, emit a space instead */
230
91.7k
        else if (/*sValue >= 30 &&*/ sValue <= 39)
231
80.3k
            ch = (char)(sValue - 30 + '0'); /* digit 0..9 */
232
1.27M
        *str = ch;
233
1.27M
        str++;
234
235
1.27M
        sRad50 -= (sValue * saQuots[i]);
236
1.27M
    }
237
238
    /* Do zero-terminate */
239
425k
    *str = '\0';
240
425k
}
241
242
/************************************************************************/
243
/*                          DGNAsciiToRad50()                           */
244
/************************************************************************/
245
246
void DGNAsciiToRad50(const char *str, unsigned short *pRad50)
247
248
0
{
249
0
    unsigned short rad50 = 0;
250
251
0
    for (int i = 0; i < 3; i++)
252
0
    {
253
0
        if (i >= (int)strlen(str))
254
0
        {
255
0
            rad50 = rad50 * 40;
256
0
            continue;
257
0
        }
258
259
0
        unsigned short value = 0;
260
261
0
        if (str[i] == '$')
262
0
            value = 27;
263
0
        else if (str[i] == '.')
264
0
            value = 28;
265
0
        else if (str[i] == ' ')
266
0
            value = 29;
267
0
        else if (str[i] >= '0' && str[i] <= '9')
268
0
            value = str[i] - '0' + 30;
269
0
        else if (str[i] >= 'a' && str[i] <= 'z')
270
0
            value = str[i] - 'a' + 1;
271
0
        else if (str[i] >= 'A' && str[i] <= 'Z')
272
0
            value = str[i] - 'A' + 1;
273
0
        else
274
0
            value = 0;
275
276
0
        rad50 = rad50 * 40 + value;
277
0
    }
278
279
0
    *pRad50 = rad50;
280
0
}
281
282
/************************************************************************/
283
/*                        DGNGetLineStyleName()                         */
284
/*                                                                      */
285
/*      Read the line style name from symbol table.                     */
286
/*      The got name is stored in psLine.                               */
287
/************************************************************************/
288
#ifdef unused
289
int DGNGetLineStyleName(CPL_UNUSED DGNInfo *psDGN, DGNElemMultiPoint *psLine,
290
                        char szLineStyle[65])
291
{
292
    if (psLine->core.attr_bytes > 0 && psLine->core.attr_data[1] == 0x10 &&
293
        psLine->core.attr_data[2] == 0xf9 && psLine->core.attr_data[3] == 0x79)
294
    {
295
#ifdef notdef
296
        for (int i = 0; i < SYMBOL_TABLE_SIZE; i++)
297
        {
298
            if (*((unsigned char *)psDGN->buffer + 0x21e5 + i) ==
299
                    psLine->core.attr_data[4] &&
300
                *((unsigned char *)psDGN->buffer + 0x21e6 + i) ==
301
                    psLine->core.attr_data[5] &&
302
                *((unsigned char *)psDGN->buffer + 0x21e7 + i) ==
303
                    psLine->core.attr_data[6] &&
304
                *((unsigned char *)psDGN->buffer + 0x21e8 + i) ==
305
                    psLine->core.attr_data[7])
306
            {
307
                memcpy(szLineStyle, (unsigned char *)psDGN->buffer + 0x21e9 + i,
308
                       64);
309
                szLineStyle[64] = '\0';
310
                return TRUE;
311
            }
312
        }
313
#endif
314
        return FALSE;
315
    }
316
    else
317
    {
318
        szLineStyle[0] = '\0';
319
        return FALSE;
320
    }
321
}
322
#endif
323
324
/************************************************************************/
325
/*                           DGNDumpElement()                           */
326
/************************************************************************/
327
328
/**
329
 * Emit textual report of an element.
330
 *
331
 * This function exists primarily for debugging, and will produce a textual
332
 * report about any element type to the designated file.
333
 *
334
 * @param hDGN the file from which the element originated.
335
 * @param psElement the element to report on.
336
 * @param fp the file (such as stdout) to report the element information to.
337
 */
338
339
void DGNDumpElement(DGNHandle hDGN, const DGNElemCore *psElement, FILE *fp)
340
341
0
{
342
0
    DGNInfo *psInfo = (DGNInfo *)hDGN;
343
344
0
    fprintf(fp, "\n");
345
0
    fprintf(fp, "Element:%-12s Level:%2d id:%-6d ",
346
0
            DGNTypeToName(psElement->type), psElement->level,
347
0
            psElement->element_id);
348
349
0
    if (psElement->complex)
350
0
        fprintf(fp, "(Complex) ");
351
352
0
    if (psElement->deleted)
353
0
        fprintf(fp, "(DELETED) ");
354
355
0
    fprintf(fp, "\n");
356
357
0
    fprintf(fp, "  offset=%d  size=%d bytes\n", psElement->offset,
358
0
            psElement->size);
359
360
0
    fprintf(fp, "  graphic_group:%-3d color:%d weight:%d style:%d\n",
361
0
            psElement->graphic_group, psElement->color, psElement->weight,
362
0
            psElement->style);
363
364
0
    if (psElement->properties != 0)
365
0
    {
366
0
        fprintf(fp, "  properties=%d", psElement->properties);
367
0
        if (psElement->properties & DGNPF_HOLE)
368
0
            fprintf(fp, ",HOLE");
369
0
        if (psElement->properties & DGNPF_SNAPPABLE)
370
0
            fprintf(fp, ",SNAPPABLE");
371
0
        if (psElement->properties & DGNPF_PLANAR)
372
0
            fprintf(fp, ",PLANAR");
373
0
        if (psElement->properties & DGNPF_ORIENTATION)
374
0
            fprintf(fp, ",ORIENTATION");
375
0
        if (psElement->properties & DGNPF_ATTRIBUTES)
376
0
            fprintf(fp, ",ATTRIBUTES");
377
0
        if (psElement->properties & DGNPF_MODIFIED)
378
0
            fprintf(fp, ",MODIFIED");
379
0
        if (psElement->properties & DGNPF_NEW)
380
0
            fprintf(fp, ",NEW");
381
0
        if (psElement->properties & DGNPF_LOCKED)
382
0
            fprintf(fp, ",LOCKED");
383
384
0
        int nClass = psElement->properties & DGNPF_CLASS;
385
0
        if (nClass == DGNC_PATTERN_COMPONENT)
386
0
            fprintf(fp, ",PATTERN_COMPONENT");
387
0
        else if (nClass == DGNC_CONSTRUCTION_ELEMENT)
388
0
            fprintf(fp, ",CONSTRUCTION ELEMENT");
389
0
        else if (nClass == DGNC_DIMENSION_ELEMENT)
390
0
            fprintf(fp, ",DIMENSION ELEMENT");
391
0
        else if (nClass == DGNC_PRIMARY_RULE_ELEMENT)
392
0
            fprintf(fp, ",PRIMARY RULE ELEMENT");
393
0
        else if (nClass == DGNC_LINEAR_PATTERNED_ELEMENT)
394
0
            fprintf(fp, ",LINEAR PATTERNED ELEMENT");
395
0
        else if (nClass == DGNC_CONSTRUCTION_RULE_ELEMENT)
396
0
            fprintf(fp, ",CONSTRUCTION_RULE_ELEMENT");
397
398
0
        fprintf(fp, "\n");
399
0
    }
400
401
0
    switch (psElement->stype)
402
0
    {
403
0
        case DGNST_MULTIPOINT:
404
0
        {
405
0
            auto psLine =
406
0
                reinterpret_cast<const DGNElemMultiPoint *>(psElement);
407
408
0
            for (int i = 0; i < psLine->num_vertices; i++)
409
0
                fprintf(fp, "  (%.6f,%.6f,%.6f)\n", psLine->vertices[i].x,
410
0
                        psLine->vertices[i].y, psLine->vertices[i].z);
411
0
        }
412
0
        break;
413
414
0
        case DGNST_CELL_HEADER:
415
0
        {
416
0
            auto psCell =
417
0
                reinterpret_cast<const DGNElemCellHeader *>(psElement);
418
419
0
            fprintf(
420
0
                fp,
421
0
                "  totlength=%d, name=%s, class=%x, levels=%02x%02x%02x%02x\n",
422
0
                psCell->totlength, psCell->name, psCell->cclass,
423
0
                psCell->levels[0], psCell->levels[1], psCell->levels[2],
424
0
                psCell->levels[3]);
425
0
            fprintf(fp,
426
0
                    "  rnglow=(%.5f,%.5f,%.5f)\n"
427
0
                    "  rnghigh=(%.5f,%.5f,%.5f)\n",
428
0
                    psCell->rnglow.x, psCell->rnglow.y, psCell->rnglow.z,
429
0
                    psCell->rnghigh.x, psCell->rnghigh.y, psCell->rnghigh.z);
430
0
            fprintf(fp, "  origin=(%.5f,%.5f,%.5f)\n", psCell->origin.x,
431
0
                    psCell->origin.y, psCell->origin.z);
432
433
0
            if (psInfo->dimension == 2)
434
0
                fprintf(fp, "  xscale=%g, yscale=%g, rotation=%g\n",
435
0
                        psCell->xscale, psCell->yscale, psCell->rotation);
436
0
            else
437
0
                fprintf(fp, "  trans=%g,%g,%g,%g,%g,%g,%g,%g,%g\n",
438
0
                        psCell->trans[0], psCell->trans[1], psCell->trans[2],
439
0
                        psCell->trans[3], psCell->trans[4], psCell->trans[5],
440
0
                        psCell->trans[6], psCell->trans[7], psCell->trans[8]);
441
0
        }
442
0
        break;
443
444
0
        case DGNST_CELL_LIBRARY:
445
0
        {
446
0
            auto psCell =
447
0
                reinterpret_cast<const DGNElemCellLibrary *>(psElement);
448
449
0
            fprintf(
450
0
                fp,
451
0
                "  name=%s, class=%x, levels=%02x%02x%02x%02x, numwords=%d\n",
452
0
                psCell->name, psCell->cclass, psCell->levels[0],
453
0
                psCell->levels[1], psCell->levels[2], psCell->levels[3],
454
0
                psCell->numwords);
455
0
            fprintf(fp, "  dispsymb=%d, description=%s\n", psCell->dispsymb,
456
0
                    psCell->description);
457
0
        }
458
0
        break;
459
460
0
        case DGNST_SHARED_CELL_DEFN:
461
0
        {
462
0
            auto psShared =
463
0
                reinterpret_cast<const DGNElemSharedCellDefn *>(psElement);
464
465
0
            fprintf(fp, "  totlength=%d\n", psShared->totlength);
466
0
        }
467
0
        break;
468
469
0
        case DGNST_ARC:
470
0
        {
471
0
            auto psArc = reinterpret_cast<const DGNElemArc *>(psElement);
472
473
0
            if (psInfo->dimension == 2)
474
0
                fprintf(fp, "  origin=(%.5f,%.5f), rotation=%f\n",
475
0
                        psArc->origin.x, psArc->origin.y, psArc->rotation);
476
0
            else
477
0
                fprintf(fp, "  origin=(%.5f,%.5f,%.5f), quat=%d,%d,%d,%d\n",
478
0
                        psArc->origin.x, psArc->origin.y, psArc->origin.z,
479
0
                        psArc->quat[0], psArc->quat[1], psArc->quat[2],
480
0
                        psArc->quat[3]);
481
0
            fprintf(fp, "  axes=(%.5f,%.5f), start angle=%f, sweep=%f\n",
482
0
                    psArc->primary_axis, psArc->secondary_axis, psArc->startang,
483
0
                    psArc->sweepang);
484
0
        }
485
0
        break;
486
487
0
        case DGNST_TEXT:
488
0
        {
489
0
            auto psText = reinterpret_cast<const DGNElemText *>(psElement);
490
491
0
            fprintf(fp,
492
0
                    "  origin=(%.5f,%.5f), rotation=%f\n"
493
0
                    "  font=%d, just=%d, length_mult=%g, height_mult=%g\n"
494
0
                    "  string = \"%s\"\n",
495
0
                    psText->origin.x, psText->origin.y, psText->rotation,
496
0
                    psText->font_id, psText->justification, psText->length_mult,
497
0
                    psText->height_mult, psText->string);
498
0
        }
499
0
        break;
500
501
0
        case DGNST_TEXT_NODE:
502
0
        {
503
0
            auto psNode = reinterpret_cast<const DGNElemTextNode *>(psElement);
504
505
0
            fprintf(fp, "  totlength=%d, num_texts=%d\n", psNode->totlength,
506
0
                    psNode->numelems);
507
0
            fprintf(fp,
508
0
                    "  origin=(%.5f,%.5f), rotation=%f\n"
509
0
                    "  font=%d, just=%d, length_mult=%g, height_mult=%g\n",
510
0
                    psNode->origin.x, psNode->origin.y, psNode->rotation,
511
0
                    psNode->font_id, psNode->justification, psNode->length_mult,
512
0
                    psNode->height_mult);
513
0
            fprintf(fp, "  max_length=%d, used=%d,", psNode->max_length,
514
0
                    psNode->max_used);
515
0
            fprintf(fp, "  node_number=%d\n", psNode->node_number);
516
0
        }
517
0
        break;
518
519
0
        case DGNST_COMPLEX_HEADER:
520
0
        {
521
0
            auto psHdr =
522
0
                reinterpret_cast<const DGNElemComplexHeader *>(psElement);
523
524
0
            fprintf(fp, "  totlength=%d, numelems=%d\n", psHdr->totlength,
525
0
                    psHdr->numelems);
526
0
            if (psElement->type == DGNT_3DSOLID_HEADER ||
527
0
                psElement->type == DGNT_3DSURFACE_HEADER)
528
0
            {
529
0
                fprintf(fp, "  surftype=%d, boundelms=%d\n", psHdr->surftype,
530
0
                        psHdr->boundelms);
531
0
            }
532
0
        }
533
0
        break;
534
535
0
        case DGNST_COLORTABLE:
536
0
        {
537
0
            auto psCT = reinterpret_cast<const DGNElemColorTable *>(psElement);
538
539
0
            fprintf(fp, "  screen_flag: %d\n", psCT->screen_flag);
540
0
            for (int i = 0; i < 256; i++)
541
0
            {
542
0
                fprintf(fp, "  %3d: (%3u,%3u,%3u)\n", i, psCT->color_info[i][0],
543
0
                        psCT->color_info[i][1], psCT->color_info[i][2]);
544
0
            }
545
0
        }
546
0
        break;
547
548
0
        case DGNST_TCB:
549
0
        {
550
0
            auto psTCB = reinterpret_cast<const DGNElemTCB *>(psElement);
551
552
0
            fprintf(fp, "  dimension = %d\n", psTCB->dimension);
553
0
            fprintf(fp, "  uor_per_subunit = %ld, subunits = `%s'\n",
554
0
                    psTCB->uor_per_subunit, psTCB->sub_units);
555
0
            fprintf(fp, "  subunits_per_master = %ld, master units = `%s'\n",
556
0
                    psTCB->subunits_per_master, psTCB->master_units);
557
0
            fprintf(fp, "  origin = (%.5f,%.5f,%.5f)\n", psTCB->origin_x,
558
0
                    psTCB->origin_y, psTCB->origin_z);
559
560
0
            for (int iView = 0; iView < 8; iView++)
561
0
            {
562
0
                const DGNViewInfo *psView = psTCB->views + iView;
563
564
0
                fprintf(fp,
565
0
                        "  View%d: flags=%04X, "
566
0
                        "levels=%02X%02X%02X%02X%02X%02X%02X%02X\n",
567
0
                        iView, psView->flags, psView->levels[0],
568
0
                        psView->levels[1], psView->levels[2], psView->levels[3],
569
0
                        psView->levels[4], psView->levels[5], psView->levels[6],
570
0
                        psView->levels[7]);
571
0
                fprintf(fp,
572
0
                        "        origin=(%g,%g,%g)\n        delta=(%g,%g,%g)\n",
573
0
                        psView->origin.x, psView->origin.y, psView->origin.z,
574
0
                        psView->delta.x, psView->delta.y, psView->delta.z);
575
0
                fprintf(fp, "       trans=(%g,%g,%g,%g,%g,%g,%g,%g,%g)\n",
576
0
                        psView->transmatrx[0], psView->transmatrx[1],
577
0
                        psView->transmatrx[2], psView->transmatrx[3],
578
0
                        psView->transmatrx[4], psView->transmatrx[5],
579
0
                        psView->transmatrx[6], psView->transmatrx[7],
580
0
                        psView->transmatrx[8]);
581
0
            }
582
0
        }
583
0
        break;
584
585
0
        case DGNST_TAG_SET:
586
0
        {
587
0
            auto psTagSet = reinterpret_cast<const DGNElemTagSet *>(psElement);
588
589
0
            fprintf(fp, "  tagSetName=%s, tagSet=%d, tagCount=%d, flags=%d\n",
590
0
                    psTagSet->tagSetName, psTagSet->tagSet, psTagSet->tagCount,
591
0
                    psTagSet->flags);
592
0
            for (int iTag = 0; iTag < psTagSet->tagCount; iTag++)
593
0
            {
594
0
                const DGNTagDef *psTagDef = psTagSet->tagList + iTag;
595
596
0
                fprintf(fp, "    %d: name=%s, type=%d, prompt=%s", psTagDef->id,
597
0
                        psTagDef->name, psTagDef->type, psTagDef->prompt);
598
0
                if (psTagDef->type == 1)
599
0
                    fprintf(fp, ", default=%s\n",
600
0
                            psTagDef->defaultValue.string);
601
0
                else if (psTagDef->type == 3 || psTagDef->type == 5)
602
0
                    fprintf(fp, ", default=%d\n",
603
0
                            psTagDef->defaultValue.integer);
604
0
                else if (psTagDef->type == 4)
605
0
                    fprintf(fp, ", default=%g\n", psTagDef->defaultValue.real);
606
0
                else
607
0
                    fprintf(fp, ", default=<unknown>\n");
608
0
            }
609
0
        }
610
0
        break;
611
612
0
        case DGNST_TAG_VALUE:
613
0
        {
614
0
            auto psTag = reinterpret_cast<const DGNElemTagValue *>(psElement);
615
616
0
            fprintf(fp, "  tagType=%d, tagSet=%d, tagIndex=%d, tagLength=%d\n",
617
0
                    psTag->tagType, psTag->tagSet, psTag->tagIndex,
618
0
                    psTag->tagLength);
619
0
            if (psTag->tagType == 1)
620
0
                fprintf(fp, "  value=%s\n", psTag->tagValue.string);
621
0
            else if (psTag->tagType == 3)
622
0
                fprintf(fp, "  value=%d\n", psTag->tagValue.integer);
623
0
            else if (psTag->tagType == 4)
624
0
                fprintf(fp, "  value=%g\n", psTag->tagValue.real);
625
0
        }
626
0
        break;
627
628
0
        case DGNST_CONE:
629
0
        {
630
0
            auto psCone = reinterpret_cast<const DGNElemCone *>(psElement);
631
632
0
            fprintf(fp,
633
0
                    "  center_1=(%g,%g,%g) radius=%g\n"
634
0
                    "  center_2=(%g,%g,%g) radius=%g\n"
635
0
                    "  quat=%d,%d,%d,%d unknown=%d\n",
636
0
                    psCone->center_1.x, psCone->center_1.y, psCone->center_1.z,
637
0
                    psCone->radius_1, psCone->center_2.x, psCone->center_2.y,
638
0
                    psCone->center_2.z, psCone->radius_2, psCone->quat[0],
639
0
                    psCone->quat[1], psCone->quat[2], psCone->quat[3],
640
0
                    psCone->unknown);
641
0
        }
642
0
        break;
643
644
0
        case DGNST_BSPLINE_SURFACE_HEADER:
645
0
        {
646
0
            auto psSpline =
647
0
                reinterpret_cast<const DGNElemBSplineSurfaceHeader *>(
648
0
                    psElement);
649
650
0
            fprintf(fp, "  desc_words=%ld, curve type=%u\n",
651
0
                    psSpline->desc_words, psSpline->curve_type);
652
653
0
            fprintf(fp, "  U: properties=%02x", psSpline->u_properties);
654
0
            if (psSpline->u_properties != 0)
655
0
            {
656
0
                if (psSpline->u_properties & DGNBSC_CURVE_DISPLAY)
657
0
                {
658
0
                    fprintf(fp, ",CURVE_DISPLAY");
659
0
                }
660
0
                if (psSpline->u_properties & DGNBSC_POLY_DISPLAY)
661
0
                {
662
0
                    fprintf(fp, ",POLY_DISPLAY");
663
0
                }
664
0
                if (psSpline->u_properties & DGNBSC_RATIONAL)
665
0
                {
666
0
                    fprintf(fp, ",RATIONAL");
667
0
                }
668
0
                if (psSpline->u_properties & DGNBSC_CLOSED)
669
0
                {
670
0
                    fprintf(fp, ",CLOSED");
671
0
                }
672
0
            }
673
0
            fprintf(fp, "\n");
674
0
            fprintf(fp, "     order=%u\n  %d poles, %d knots, %d rule lines\n",
675
0
                    psSpline->u_order, psSpline->num_poles_u,
676
0
                    psSpline->num_knots_u, psSpline->rule_lines_u);
677
678
0
            fprintf(fp, "  V: properties=%02x", psSpline->v_properties);
679
0
            if (psSpline->v_properties != 0)
680
0
            {
681
0
                if (psSpline->v_properties & DGNBSS_ARC_SPACING)
682
0
                {
683
0
                    fprintf(fp, ",ARC_SPACING");
684
0
                }
685
0
                if (psSpline->v_properties & DGNBSS_CLOSED)
686
0
                {
687
0
                    fprintf(fp, ",CLOSED");
688
0
                }
689
0
            }
690
0
            fprintf(fp, "\n");
691
0
            fprintf(fp, "     order=%u\n  %d poles, %d knots, %d rule lines\n",
692
0
                    psSpline->v_order, psSpline->num_poles_v,
693
0
                    psSpline->num_knots_v, psSpline->rule_lines_v);
694
0
        }
695
0
        break;
696
697
0
        case DGNST_BSPLINE_CURVE_HEADER:
698
0
        {
699
0
            auto psSpline =
700
0
                reinterpret_cast<const DGNElemBSplineCurveHeader *>(psElement);
701
702
0
            fprintf(fp,
703
0
                    "  desc_words=%ld, curve type=%u\n"
704
0
                    "  properties=%02x",
705
0
                    psSpline->desc_words, psSpline->curve_type,
706
0
                    psSpline->properties);
707
0
            if (psSpline->properties != 0)
708
0
            {
709
0
                if (psSpline->properties & DGNBSC_CURVE_DISPLAY)
710
0
                {
711
0
                    fprintf(fp, ",CURVE_DISPLAY");
712
0
                }
713
0
                if (psSpline->properties & DGNBSC_POLY_DISPLAY)
714
0
                {
715
0
                    fprintf(fp, ",POLY_DISPLAY");
716
0
                }
717
0
                if (psSpline->properties & DGNBSC_RATIONAL)
718
0
                {
719
0
                    fprintf(fp, ",RATIONAL");
720
0
                }
721
0
                if (psSpline->properties & DGNBSC_CLOSED)
722
0
                {
723
0
                    fprintf(fp, ",CLOSED");
724
0
                }
725
0
            }
726
0
            fprintf(fp, "\n");
727
0
            fprintf(fp, "  order=%u\n  %d poles, %d knots\n", psSpline->order,
728
0
                    psSpline->num_poles, psSpline->num_knots);
729
0
        }
730
0
        break;
731
732
0
        case DGNST_BSPLINE_SURFACE_BOUNDARY:
733
0
        {
734
0
            auto psBounds =
735
0
                reinterpret_cast<const DGNElemBSplineSurfaceBoundary *>(
736
0
                    psElement);
737
738
0
            fprintf(fp, "  boundary number=%d, # vertices=%d\n",
739
0
                    psBounds->number, psBounds->numverts);
740
0
            for (int i = 0; i < psBounds->numverts; i++)
741
0
            {
742
0
                fprintf(fp, "  (%.6f,%.6f)\n", psBounds->vertices[i].x,
743
0
                        psBounds->vertices[i].y);
744
0
            }
745
0
        }
746
0
        break;
747
748
0
        case DGNST_KNOT_WEIGHT:
749
0
        {
750
0
            auto psArray =
751
0
                reinterpret_cast<const DGNElemKnotWeight *>(psElement);
752
753
0
            const int numelems = (psArray->core.size - 36) / 4;
754
0
            for (int i = 0; i < numelems; i++)
755
0
            {
756
0
                fprintf(fp, "  %.6f\n", psArray->array[i]);
757
0
            }
758
0
        }
759
0
        break;
760
761
0
        default:
762
0
            break;
763
0
    }
764
765
0
    if (psElement->attr_bytes > 0)
766
0
    {
767
0
        fprintf(fp, "Attributes (%d bytes):\n", psElement->attr_bytes);
768
769
0
        for (int iLink = 0; true; iLink++)
770
0
        {
771
0
            int nLinkType = 0;
772
0
            int nEntityNum = 0;
773
0
            int nMSLink = 0;
774
0
            int nLinkSize = 0;
775
            // coverity[tained_data]
776
0
            unsigned char *pabyData =
777
0
                DGNGetLinkage(hDGN, psElement, iLink, &nLinkType, &nEntityNum,
778
0
                              &nMSLink, &nLinkSize);
779
0
            if (pabyData == nullptr)
780
0
                break;
781
782
0
            fprintf(fp, "Type=0x%04x", nLinkType);
783
0
            if (nMSLink != 0 || nEntityNum != 0)
784
0
                fprintf(fp, ", EntityNum=%d, MSLink=%d", nEntityNum, nMSLink);
785
786
0
            int nBytes = static_cast<int>(psElement->attr_data +
787
0
                                          psElement->attr_bytes - pabyData);
788
0
            if (nBytes < nLinkSize)
789
0
            {
790
0
                CPLError(CE_Failure, CPLE_AppDefined,
791
0
                         "Corrupt linkage, element id:%d, link:%d",
792
0
                         psElement->element_id, iLink);
793
0
                fprintf(fp, " (Corrupt, declared size: %d, assuming size: %d)",
794
0
                        nLinkSize, nBytes);
795
0
                nLinkSize = nBytes;
796
0
            }
797
0
            fprintf(fp, "\n  0x");
798
799
0
            for (int i = 0; i < nLinkSize; i++)
800
0
                fprintf(fp, "%02x", pabyData[i]);
801
0
            fprintf(fp, "\n");
802
0
        }
803
0
    }
804
0
}
805
806
/************************************************************************/
807
/*                           DGNTypeToName()                            */
808
/************************************************************************/
809
810
/**
811
 * Convert type to name.
812
 *
813
 * Returns a human readable name for an element type such as DGNT_LINE.
814
 *
815
 * @param nType the DGNT_* type code to translate.
816
 *
817
 * @return a pointer to an internal string with the translation.  This string
818
 * should not be modified or freed.
819
 */
820
821
const char *DGNTypeToName(int nType)
822
823
0
{
824
0
    static char szNumericResult[16] = {};
825
826
0
    switch (nType)
827
0
    {
828
0
        case DGNT_CELL_LIBRARY:
829
0
            return "Cell Library";
830
831
0
        case DGNT_CELL_HEADER:
832
0
            return "Cell Header";
833
834
0
        case DGNT_LINE:
835
0
            return "Line";
836
837
0
        case DGNT_LINE_STRING:
838
0
            return "Line String";
839
840
0
        case DGNT_POINT_STRING:
841
0
            return "Point String";
842
843
0
        case DGNT_GROUP_DATA:
844
0
            return "Group Data";
845
846
0
        case DGNT_SHAPE:
847
0
            return "Shape";
848
849
0
        case DGNT_TEXT_NODE:
850
0
            return "Text Node";
851
852
0
        case DGNT_DIGITIZER_SETUP:
853
0
            return "Digitizer Setup";
854
855
0
        case DGNT_TCB:
856
0
            return "TCB";
857
858
0
        case DGNT_LEVEL_SYMBOLOGY:
859
0
            return "Level Symbology";
860
861
0
        case DGNT_CURVE:
862
0
            return "Curve";
863
864
0
        case DGNT_COMPLEX_CHAIN_HEADER:
865
0
            return "Complex Chain Header";
866
867
0
        case DGNT_COMPLEX_SHAPE_HEADER:
868
0
            return "Complex Shape Header";
869
870
0
        case DGNT_ELLIPSE:
871
0
            return "Ellipse";
872
873
0
        case DGNT_ARC:
874
0
            return "Arc";
875
876
0
        case DGNT_TEXT:
877
0
            return "Text";
878
879
0
        case DGNT_BSPLINE_POLE:
880
0
            return "B-Spline Pole";
881
882
0
        case DGNT_BSPLINE_SURFACE_HEADER:
883
0
            return "B-Spline Surface Header";
884
885
0
        case DGNT_BSPLINE_SURFACE_BOUNDARY:
886
0
            return "B-Spline Surface Boundary";
887
888
0
        case DGNT_BSPLINE_KNOT:
889
0
            return "B-Spline Knot";
890
891
0
        case DGNT_BSPLINE_CURVE_HEADER:
892
0
            return "B-Spline Curve Header";
893
894
0
        case DGNT_BSPLINE_WEIGHT_FACTOR:
895
0
            return "B-Spline Weight Factor";
896
897
0
        case DGNT_APPLICATION_ELEM:
898
0
            return "Application Element";
899
900
0
        case DGNT_SHARED_CELL_DEFN:
901
0
            return "Shared Cell Definition";
902
903
0
        case DGNT_SHARED_CELL_ELEM:
904
0
            return "Shared Cell Element";
905
906
0
        case DGNT_TAG_VALUE:
907
0
            return "Tag Value";
908
909
0
        case DGNT_CONE:
910
0
            return "Cone";
911
912
0
        case DGNT_3DSURFACE_HEADER:
913
0
            return "3D Surface Header";
914
915
0
        case DGNT_3DSOLID_HEADER:
916
0
            return "3D Solid Header";
917
918
0
        default:
919
0
            snprintf(szNumericResult, sizeof(szNumericResult), "%d", nType);
920
0
            return szNumericResult;
921
0
    }
922
0
}
923
924
/************************************************************************/
925
/*                         DGNGetAttrLinkSize()                         */
926
/************************************************************************/
927
928
/**
929
 * Get attribute linkage size.
930
 *
931
 * Returns the size, in bytes, of the attribute linkage starting at byte
932
 * offset nOffset.  On failure a value of 0 is returned.
933
 *
934
 * @param hDGN the file from which the element originated.
935
 * @param psElement the element to report on.
936
 * @param nOffset byte offset within attribute data of linkage to check.
937
 *
938
 * @return size of linkage in bytes, or zero.
939
 */
940
941
int DGNGetAttrLinkSize(CPL_UNUSED DGNHandle hDGN, const DGNElemCore *psElement,
942
                       int nOffset)
943
4.01M
{
944
4.01M
    if (psElement->attr_bytes < nOffset + 4)
945
1.26M
        return 0;
946
947
    /* DMRS Linkage */
948
2.75M
    if ((psElement->attr_data[nOffset + 0] == 0 &&
949
2.75M
         psElement->attr_data[nOffset + 1] == 0) ||
950
2.75M
        (psElement->attr_data[nOffset + 0] == 0 &&
951
1.42M
         psElement->attr_data[nOffset + 1] == 0x80))
952
1.33M
        return 8;
953
954
    /* If low order bit of second byte is set, first byte is length */
955
1.41M
    if (psElement->attr_data[nOffset + 1] & 0x10)
956
1.41M
        return psElement->attr_data[nOffset + 0] * 2 + 2;
957
958
    /* unknown */
959
4.73k
    return 0;
960
1.41M
}
961
962
/************************************************************************/
963
/*                           DGNGetLinkage()                            */
964
/************************************************************************/
965
966
/**
967
 * Returns requested linkage raw data.
968
 *
969
 * A pointer to the raw data for the requested attribute linkage is returned
970
 * as well as (potentially) various information about the linkage including
971
 * the linkage type, database entity number and MSLink value, and the length
972
 * of the raw linkage data in bytes.
973
 *
974
 * If the requested linkage (iIndex) does not exist a value of zero is
975
 * returned.
976
 *
977
 * The entity number is (loosely speaking) the index of the table within
978
 * the current database to which the MSLINK value will refer.  The entity
979
 * number should be used to lookup the table name in the MSCATALOG table.
980
 * The MSLINK value is the key value for the record in the target table.
981
 *
982
 * @param hDGN the file from which the element originated.
983
 * @param psElement the element to report on.
984
 * @param iIndex the zero based index of the linkage to fetch.
985
 * @param pnLinkageType variable to return linkage type.  This may be one of
986
 * the predefined DGNLT_ values or a different value. This pointer may be NULL.
987
 * @param pnEntityNum variable to return the entity number in or NULL if not
988
 * required.
989
 * @param pnMSLink variable to return the MSLINK value in, or NULL if not
990
 * required.
991
 * @param pnLength variable to returned the linkage size in bytes or NULL.
992
 *
993
 * @return pointer to raw internal linkage data.  This data should not be
994
 * altered or freed.  NULL returned on failure.
995
 */
996
997
unsigned char *DGNGetLinkage(DGNHandle hDGN, const DGNElemCore *psElement,
998
                             int iIndex, int *pnLinkageType, int *pnEntityNum,
999
                             int *pnMSLink, int *pnLength)
1000
1001
1.29M
{
1002
1.29M
    int nLinkSize = 0;
1003
1004
1.29M
    for (int iLinkage = 0, nAttrOffset = 0;
1005
4.01M
         (nLinkSize = DGNGetAttrLinkSize(hDGN, psElement, nAttrOffset)) != 0;
1006
2.72M
         iLinkage++, nAttrOffset += nLinkSize)
1007
2.74M
    {
1008
2.74M
        if (iLinkage == iIndex)
1009
20.0k
        {
1010
20.0k
            if (nLinkSize <= 4)
1011
533
            {
1012
533
                CPLError(CE_Failure, CPLE_AssertionFailed, "nLinkSize <= 4");
1013
533
                return nullptr;
1014
533
            }
1015
19.4k
            if (nLinkSize + nAttrOffset > psElement->attr_bytes)
1016
140
            {
1017
140
                CPLError(CE_Failure, CPLE_AssertionFailed,
1018
140
                         "nLinkSize + nAttrOffset > psElement->attr_bytes");
1019
140
                return nullptr;
1020
140
            }
1021
1022
19.3k
            int nLinkageType = 0;
1023
19.3k
            int nEntityNum = 0;
1024
19.3k
            int nMSLink = 0;
1025
19.3k
            if (psElement->attr_bytes >= nAttrOffset + 7 &&
1026
19.3k
                psElement->attr_data[nAttrOffset + 0] == 0x00 &&
1027
19.3k
                (psElement->attr_data[nAttrOffset + 1] == 0x00 ||
1028
12.0k
                 psElement->attr_data[nAttrOffset + 1] == 0x80))
1029
12.0k
            {
1030
12.0k
                nLinkageType = DGNLT_DMRS;
1031
12.0k
                nEntityNum = psElement->attr_data[nAttrOffset + 2] +
1032
12.0k
                             psElement->attr_data[nAttrOffset + 3] * 256;
1033
12.0k
                nMSLink = psElement->attr_data[nAttrOffset + 4] +
1034
12.0k
                          psElement->attr_data[nAttrOffset + 5] * 256 +
1035
12.0k
                          psElement->attr_data[nAttrOffset + 6] * 65536;
1036
12.0k
            }
1037
7.26k
            else if (psElement->attr_bytes >= nAttrOffset + 4)
1038
7.26k
                nLinkageType = psElement->attr_data[nAttrOffset + 2] +
1039
7.26k
                               psElement->attr_data[nAttrOffset + 3] * 256;
1040
1041
            // Possibly an external database linkage?
1042
19.3k
            if (nLinkSize == 16 && nLinkageType != DGNLT_SHAPE_FILL &&
1043
19.3k
                psElement->attr_bytes >= nAttrOffset + 12)
1044
15
            {
1045
15
                nEntityNum = psElement->attr_data[nAttrOffset + 6] +
1046
15
                             psElement->attr_data[nAttrOffset + 7] * 256;
1047
15
                nMSLink = psElement->attr_data[nAttrOffset + 8] |
1048
15
                          (psElement->attr_data[nAttrOffset + 9] << 8) |
1049
15
                          (psElement->attr_data[nAttrOffset + 10] << 16) |
1050
15
                          (psElement->attr_data[nAttrOffset + 11] << 24);
1051
15
            }
1052
1053
19.3k
            if (pnLinkageType != nullptr)
1054
19.3k
                *pnLinkageType = nLinkageType;
1055
19.3k
            if (pnEntityNum != nullptr)
1056
16.3k
                *pnEntityNum = nEntityNum;
1057
19.3k
            if (pnMSLink != nullptr)
1058
16.3k
                *pnMSLink = nMSLink;
1059
19.3k
            if (pnLength != nullptr)
1060
19.3k
                *pnLength = nLinkSize;
1061
1062
19.3k
            return psElement->attr_data + nAttrOffset;
1063
19.4k
        }
1064
2.74M
    }
1065
1066
1.27M
    return nullptr;
1067
1.29M
}
1068
1069
/************************************************************************/
1070
/*                         DGNRotationToQuat()                          */
1071
/*                                                                      */
1072
/*      Compute a quaternion for a given Z rotation.                    */
1073
/************************************************************************/
1074
1075
void DGNRotationToQuaternion(double dfRotation, int *panQuaternion)
1076
1077
0
{
1078
0
    const double dfRadianRot = (dfRotation / 180.0) * M_PI;
1079
1080
0
    panQuaternion[0] = (int)(cos(-dfRadianRot / 2.0) * 2147483647);
1081
0
    panQuaternion[1] = 0;
1082
0
    panQuaternion[2] = 0;
1083
0
    panQuaternion[3] = (int)(sin(-dfRadianRot / 2.0) * 2147483647);
1084
0
}
1085
1086
/************************************************************************/
1087
/*                         DGNQuaternionToMatrix()                      */
1088
/*                                                                      */
1089
/*      Compute a rotation matrix for a given quaternion                */
1090
/* FIXME: Write documentation on how to use this matrix                 */
1091
/* (i.e. things like row/column major, OpenGL style or not)             */
1092
/* kintel 20030819                                                      */
1093
/************************************************************************/
1094
1095
void DGNQuaternionToMatrix(int *quat, float *mat)
1096
0
{
1097
0
    const double q[4] = {1.0 * quat[1] / (1U << 31), 1.0 * quat[2] / (1U << 31),
1098
0
                         1.0 * quat[3] / (1U << 31),
1099
0
                         1.0 * quat[0] / (1U << 31)};
1100
1101
0
    mat[0 * 3 + 0] =
1102
0
        (float)(q[0] * q[0] - q[1] * q[1] - q[2] * q[2] + q[3] * q[3]);
1103
0
    mat[0 * 3 + 1] = (float)(2 * (q[2] * q[3] + q[0] * q[1]));
1104
0
    mat[0 * 3 + 2] = (float)(2 * (q[0] * q[2] - q[1] * q[3]));
1105
0
    mat[1 * 3 + 0] = (float)(2 * (q[0] * q[1] - q[2] * q[3]));
1106
0
    mat[1 * 3 + 1] =
1107
0
        (float)(-q[0] * q[0] + q[1] * q[1] - q[2] * q[2] + q[3] * q[3]);
1108
0
    mat[1 * 3 + 2] = (float)(2 * (q[0] * q[3] + q[1] * q[2]));
1109
0
    mat[2 * 3 + 0] = (float)(2 * (q[0] * q[2] + q[1] * q[3]));
1110
0
    mat[2 * 3 + 1] = (float)(2 * (q[1] * q[2] - q[0] * q[3]));
1111
0
    mat[2 * 3 + 2] =
1112
0
        (float)(-q[0] * q[0] - q[1] * q[1] + q[2] * q[2] + q[3] * q[3]);
1113
0
}
1114
1115
/************************************************************************/
1116
/*                  DGNTransformPointWithQuaternion()                   */
1117
/************************************************************************/
1118
1119
#ifdef unused
1120
void DGNTransformPointWithQuaternionVertex(CPL_UNUSED int *quat,
1121
                                           CPL_UNUSED DGNPoint *v1,
1122
                                           CPL_UNUSED DGNPoint *v2)
1123
{
1124
    /* ==================================================================== */
1125
    /*      Original code provided by kintel 20030819, but assumed to be    */
1126
    /*      incomplete.                                                     */
1127
    /* ==================================================================== */
1128
1129
#ifdef notdef
1130
    See below for sketched implementation. kintel 20030819.
1131
                               float x,y,z,w;
1132
    // FIXME: Convert quat to x,y,z,w
1133
    v2.x = w * w * v1.x + 2 * y * w * v1.z - 2 * z * w * v1.y + x * x * v1.x +
1134
           2 * y * x * v1.y + 2 * z * x * v1.z - z * z * v1.x - y * y * v1.x;
1135
    v2.y = 2 * x * y * v1.x + y * y * v1.y + 2 * z * y * v1.z +
1136
           2 * w * z * v1.x - z * z * v1.y + w * w * v1.y - 2 * x * w * v1.z -
1137
           x * x * v1.y;
1138
    v2.z = 2 * x * z * v1.x + 2 * y * z * v1.y + z * z * v1.z -
1139
           2 * w * y * v1.x - y * y * v1.z + 2 * w * x * v1.y - x * x * v1.z +
1140
           w * w * v1.z;
1141
#endif
1142
1143
    /* ==================================================================== */
1144
    /*      Implementation provided by Peggy Jung - 2004/03/05.            */
1145
    /*      peggy.jung at moskito-gis dot de.  I haven't tested it.         */
1146
    /* ==================================================================== */
1147
1148
    /*  Version: 0.1                                 Datum: 26.01.2004
1149
1150
    IN:
1151
    x,y,z               // DGNPoint &v1
1152
    quat[]              //
1153
1154
    OUT:
1155
    newX, newY, newZ    // DGNPoint &v2
1156
1157
    Author: Peggy Jung
1158
    */
1159
    /*
1160
        double ROT[12];  //rotation matrix for a given quaternion
1161
        double xx, xy, xz, xw, yy, yz, yw, zz, zw;
1162
        double a, b, c, d, n, x, y, z;
1163
1164
        x = v1->x;
1165
        y = v1->y;
1166
        z = v1->z;
1167
1168
        n =
1169
       sqrt((double)PDP2PC_long(quat[0])*(double)PDP2PC_long(quat[0])+(double)PDP2PC_long(quat[1])*(double)PDP2PC_long(quat[1])+
1170
                 (double)PDP2PC_long(quat[2])*(double)PDP2PC_long(quat[2])+(double)PDP2PC_long(quat[3])*(double)PDP2PC_long(quat[3]));
1171
1172
        a = (double)PDP2PC_long(quat[0])/n; //w
1173
        b = (double)PDP2PC_long(quat[1])/n; //x
1174
        c = (double)PDP2PC_long(quat[2])/n; //y
1175
        d = (double)PDP2PC_long(quat[3])/n; //z
1176
1177
        xx      = b*b;
1178
        xy      = b*c;
1179
        xz      = b*d;
1180
        xw      = b*a;
1181
1182
        yy      = c*c;
1183
        yz      = c*d;
1184
        yw      = c*a;
1185
1186
        zz      = d*d;
1187
        zw      = d+a;
1188
1189
        ROT[0] = 1 - 2 * yy - 2 * zz ;
1190
        ROT[1] =     2 * xy - 2 * zw ;
1191
        ROT[2] =     2 * xz + 2 * yw ;
1192
1193
        ROT[4] =     2 * xy + 2 * zw ;
1194
        ROT[5] = 1 - 2 * xx - 2 * zz ;
1195
        ROT[6] =     2 * yz - 2 * xw ;
1196
1197
        ROT[8] =     2 * xz - 2 * yw ;
1198
        ROT[9] =     2 * yz + 2 * xw ;
1199
        ROT[10] = 1 - 2 * xx - 2 * yy ;
1200
1201
        v2->x = ROT[0]*x + ROT[1]*y + ROT[2]*z;
1202
        v2->y = ROT[4]*x + ROT[5]*y + ROT[6]*z;
1203
        v2->z = ROT[8]*x + ROT[9]*y + ROT[10]*z;
1204
    */
1205
}
1206
#endif