Coverage Report

Created: 2025-07-23 09:13

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