Coverage Report

Created: 2025-12-31 08:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/dgn/dgnwrite.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  Microstation DGN Access Library
4
 * Purpose:  DGN Access functions related to writing DGN elements.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "dgnlibp.h"
15
16
#include <cmath>
17
18
#include <algorithm>
19
20
static void DGNPointToInt(DGNInfo *psDGN, DGNPoint *psPoint,
21
                          unsigned char *pabyTarget);
22
23
/************************************************************************/
24
/*                          DGNResizeElement()                          */
25
/************************************************************************/
26
27
/**
28
 * Resize an existing element.
29
 *
30
 * If the new size is the same as the old nothing happens.
31
 *
32
 * Otherwise, the old element in the file is marked as deleted, and the
33
 * DGNElemCore.offset and element_id are set to -1 indicating that the
34
 * element should be written to the end of file when next written by
35
 * DGNWriteElement().  The internal raw data buffer is updated to the new
36
 * size.
37
 *
38
 * Only elements with "raw_data" loaded may be moved.
39
 *
40
 * In normal use the DGNResizeElement() call would be called on a previously
41
 * loaded element, and afterwards the raw_data would be updated before calling
42
 * DGNWriteElement().  If DGNWriteElement() isn't called after
43
 * DGNResizeElement() then the element will be lost having been marked as
44
 * deleted in its old position but never written at the new location.
45
 *
46
 * @param hDGN the DGN file on which the element lives.
47
 * @param psElement the element to alter.
48
 * @param nNewSize the desired new size of the element in bytes.  Must be
49
 * a multiple of 2.
50
 *
51
 * @return TRUE on success, or FALSE on error.
52
 */
53
54
int DGNResizeElement(DGNHandle hDGN, DGNElemCore *psElement, int nNewSize)
55
56
0
{
57
0
    DGNInfo *psDGN = (DGNInfo *)hDGN;
58
59
    /* -------------------------------------------------------------------- */
60
    /*      Check various conditions.                                       */
61
    /* -------------------------------------------------------------------- */
62
0
    if (psElement->raw_bytes == 0 || psElement->raw_bytes != psElement->size)
63
0
    {
64
0
        CPLError(CE_Failure, CPLE_AppDefined,
65
0
                 "Raw bytes not loaded, or not matching element size.");
66
0
        return FALSE;
67
0
    }
68
69
0
    if (nNewSize % 2 == 1)
70
0
    {
71
0
        CPLError(CE_Failure, CPLE_AppDefined,
72
0
                 "DGNResizeElement(%d): "
73
0
                 "can't change to odd (not divisible by two) size.",
74
0
                 nNewSize);
75
0
        return FALSE;
76
0
    }
77
78
0
    if (nNewSize == psElement->raw_bytes)
79
0
        return TRUE;
80
81
    /* -------------------------------------------------------------------- */
82
    /*      Mark the existing element as deleted if the element has to      */
83
    /*      move to the end of the file.                                    */
84
    /* -------------------------------------------------------------------- */
85
86
0
    if (psElement->offset != -1)
87
0
    {
88
0
        vsi_l_offset nOldFLoc = VSIFTellL(psDGN->fp);
89
0
        unsigned char abyLeader[2];
90
91
0
        if (VSIFSeekL(psDGN->fp, psElement->offset, SEEK_SET) != 0 ||
92
0
            VSIFReadL(abyLeader, sizeof(abyLeader), 1, psDGN->fp) != 1)
93
0
        {
94
0
            CPLError(CE_Failure, CPLE_AppDefined,
95
0
                     "Failed seek or read when trying to mark existing\n"
96
0
                     "element as deleted in DGNResizeElement()\n");
97
0
            return FALSE;
98
0
        }
99
100
0
        abyLeader[1] |= 0x80;
101
102
0
        if (VSIFSeekL(psDGN->fp, psElement->offset, SEEK_SET) != 0 ||
103
0
            VSIFWriteL(abyLeader, sizeof(abyLeader), 1, psDGN->fp) != 1 ||
104
0
            VSIFSeekL(psDGN->fp, nOldFLoc, SEEK_SET) != 0)
105
0
        {
106
0
            CPLError(CE_Failure, CPLE_AppDefined,
107
0
                     "Failed seek or write when trying to mark existing\n"
108
0
                     "element as deleted in DGNResizeElement()\n");
109
0
            return FALSE;
110
0
        }
111
112
0
        if (psElement->element_id != -1 && psDGN->index_built)
113
0
            psDGN->element_index[psElement->element_id].flags |= DGNEIF_DELETED;
114
0
    }
115
116
0
    psElement->offset = -1; /* move to end of file. */
117
0
    psElement->element_id = -1;
118
119
    /* -------------------------------------------------------------------- */
120
    /*      Set the new size information, and realloc the raw data buffer.  */
121
    /* -------------------------------------------------------------------- */
122
0
    psElement->size = nNewSize;
123
0
    psElement->raw_data =
124
0
        (unsigned char *)CPLRealloc(psElement->raw_data, nNewSize);
125
0
    psElement->raw_bytes = nNewSize;
126
127
    /* -------------------------------------------------------------------- */
128
    /*      Update the size information within the raw buffer.              */
129
    /* -------------------------------------------------------------------- */
130
0
    const int nWords = (nNewSize / 2) - 2;
131
132
0
    psElement->raw_data[2] = (unsigned char)(nWords % 256);
133
0
    psElement->raw_data[3] = (unsigned char)(nWords / 256);
134
135
0
    return TRUE;
136
0
}
137
138
/************************************************************************/
139
/*                          DGNWriteElement()                           */
140
/************************************************************************/
141
142
/**
143
 * Write element to file.
144
 *
145
 * Only elements with "raw_data" loaded may be written.  This should
146
 * include elements created with the various DGNCreate*() functions, and
147
 * those read from the file with the DGNO_CAPTURE_RAW_DATA flag turned on
148
 * with DGNSetOptions().
149
 *
150
 * The passed element is written to the indicated file.  If the
151
 * DGNElemCore.offset field is -1 then the element is written at the end of
152
 * the file (and offset/element are reset properly) otherwise the element
153
 * is written back to the location indicated by DGNElemCore.offset.
154
 *
155
 * If the element is added at the end of the file, and if an element index
156
 * has already been built, it will be updated to reference the new element.
157
 *
158
 * This function takes care of ensuring that the end-of-file marker is
159
 * maintained after the last element.
160
 *
161
 * @param hDGN the file to write the element to.
162
 * @param psElement the element to write.
163
 *
164
 * @return TRUE on success or FALSE in case of failure.
165
 */
166
167
int DGNWriteElement(DGNHandle hDGN, DGNElemCore *psElement)
168
169
0
{
170
0
    DGNInfo *psDGN = (DGNInfo *)hDGN;
171
172
    /* ==================================================================== */
173
    /*      If this element hasn't been positioned yet, place it at the     */
174
    /*      end of the file.                                                */
175
    /* ==================================================================== */
176
0
    if (psElement->offset == -1)
177
0
    {
178
        // We must have an index, in order to properly assign the
179
        // element id of the newly written element.  Ensure it is built.
180
0
        if (!psDGN->index_built)
181
0
            DGNBuildIndex(psDGN);
182
183
        // Read the current "last" element.
184
0
        if (!DGNGotoElement(hDGN, psDGN->element_count - 1))
185
0
            return FALSE;
186
187
0
        int nJunk = 0;
188
0
        if (!DGNLoadRawElement(psDGN, &nJunk, &nJunk))
189
0
            return FALSE;
190
191
        // Establish the position of the new element.
192
0
        psElement->offset = static_cast<int>(VSIFTellL(psDGN->fp));
193
0
        psElement->element_id = psDGN->element_count;
194
195
        // Grow element buffer if needed.
196
0
        if (psDGN->element_count == psDGN->max_element_count)
197
0
        {
198
0
            psDGN->max_element_count += 500;
199
200
0
            psDGN->element_index = (DGNElementInfo *)CPLRealloc(
201
0
                psDGN->element_index,
202
0
                psDGN->max_element_count * sizeof(DGNElementInfo));
203
0
        }
204
205
        // Set up the element info
206
0
        DGNElementInfo *psInfo = psDGN->element_index + psDGN->element_count;
207
0
        psInfo->level = (unsigned char)psElement->level;
208
0
        psInfo->type = (unsigned char)psElement->type;
209
0
        psInfo->stype = (unsigned char)psElement->stype;
210
0
        psInfo->offset = psElement->offset;
211
0
        if (psElement->complex)
212
0
            psInfo->flags = DGNEIF_COMPLEX;
213
0
        else
214
0
            psInfo->flags = 0;
215
216
0
        psDGN->element_count++;
217
0
    }
218
219
    /* -------------------------------------------------------------------- */
220
    /*      Write out the element.                                          */
221
    /* -------------------------------------------------------------------- */
222
0
    if (VSIFSeekL(psDGN->fp, psElement->offset, SEEK_SET) != 0 ||
223
0
        VSIFWriteL(psElement->raw_data, psElement->raw_bytes, 1, psDGN->fp) !=
224
0
            1)
225
0
    {
226
0
        CPLError(CE_Failure, CPLE_AppDefined,
227
0
                 "Error seeking or writing new element of %d bytes at %d.",
228
0
                 psElement->offset, psElement->raw_bytes);
229
0
        return FALSE;
230
0
    }
231
232
0
    psDGN->next_element_id = psElement->element_id + 1;
233
234
    /* -------------------------------------------------------------------- */
235
    /*      Write out the end of file 0xffff marker (if we were             */
236
    /*      extending the file), but push the file pointer back before      */
237
    /*      this EOF when done.                                             */
238
    /* -------------------------------------------------------------------- */
239
0
    if (psDGN->next_element_id == psDGN->element_count)
240
0
    {
241
0
        const unsigned char abyEOF[2] = {0xff, 0xff};
242
243
0
        VSIFWriteL(abyEOF, 2, 1, psDGN->fp);
244
0
        VSIFSeekL(psDGN->fp, VSIFTellL(psDGN->fp) - 2, SEEK_SET);
245
0
    }
246
247
0
    return TRUE;
248
0
}
249
250
/************************************************************************/
251
/*                             DGNCreate()                              */
252
/************************************************************************/
253
254
/**
255
 * Create new DGN file.
256
 *
257
 * This function will create a new DGN file based on the provided seed
258
 * file, and return a handle on which elements may be read and written.
259
 *
260
 * The following creation flags may be passed:
261
 * <ul>
262
 * <li> DGNCF_USE_SEED_UNITS: The master and subunit resolutions and names
263
 * from the seed file will be used in the new file.  The nMasterUnitPerSubUnit,
264
 * nUORPerSubUnit, pszMasterUnits, and pszSubUnits arguments will be ignored.
265
 * <li> DGNCF_USE_SEED_ORIGIN: The origin from the seed file will be used
266
 * and the X, Y and Z origin passed into the call will be ignored.
267
 * <li> DGNCF_COPY_SEED_FILE_COLOR_TABLE: Should the first color table occurring
268
 * in the seed file also be copied?
269
 * <li> DGNCF_COPY_WHOLE_SEED_FILE: By default only the first three elements
270
 * (TCB, Digitizer Setup and Level Symbology) are copied from the seed file.
271
 * If this flag is provided the entire seed file is copied verbatim (with the
272
 * TCB origin and units possibly updated).
273
 * </ul>
274
 *
275
 * @param pszNewFilename the filename to create.  If it already exists
276
 * it will be overwritten.
277
 * @param pszSeedFile the seed file to copy header from.
278
 * @param nCreationFlags An ORing of DGNCF_* flags that are to take effect.
279
 * @param dfOriginX the X origin for the file.
280
 * @param dfOriginY the Y origin for the file.
281
 * @param dfOriginZ the Z origin for the file.
282
 * @param nSubUnitsPerMasterUnit the number of subunits in one master unit.
283
 * @param nUORPerSubUnit the number of UOR (units of resolution) per subunit.
284
 * @param pszMasterUnits the name of the master units (2 characters).
285
 * @param pszSubUnits the name of the subunits (2 characters).
286
 */
287
288
DGNHandle DGNCreate(const char *pszNewFilename, const char *pszSeedFile,
289
                    int nCreationFlags, double dfOriginX, double dfOriginY,
290
                    double dfOriginZ, int nSubUnitsPerMasterUnit,
291
                    int nUORPerSubUnit, const char *pszMasterUnits,
292
                    const char *pszSubUnits)
293
294
0
{
295
    /* -------------------------------------------------------------------- */
296
    /*      Open output file.                                               */
297
    /* -------------------------------------------------------------------- */
298
0
    VSILFILE *fpNew = VSIFOpenL(pszNewFilename, "wb");
299
0
    if (fpNew == nullptr)
300
0
    {
301
0
        CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open output file: %s",
302
0
                 pszNewFilename);
303
0
        return nullptr;
304
0
    }
305
306
    /* -------------------------------------------------------------------- */
307
    /*      Open seed file, and read TCB element.                           */
308
    /* -------------------------------------------------------------------- */
309
0
    DGNInfo *psSeed = (DGNInfo *)DGNOpen(pszSeedFile, FALSE);
310
0
    if (psSeed == nullptr)
311
0
    {
312
0
        VSIFCloseL(fpNew);
313
0
        return nullptr;
314
0
    }
315
316
0
    DGNSetOptions(psSeed, DGNO_CAPTURE_RAW_DATA);
317
318
0
    DGNElemCore *psSrcTCB = DGNReadElement(psSeed);
319
320
0
    CPLAssert(psSrcTCB->raw_bytes >= 1536);
321
322
    /* -------------------------------------------------------------------- */
323
    /*      Modify TCB appropriately for the output file.                   */
324
    /* -------------------------------------------------------------------- */
325
0
    GByte *pabyRawTCB = static_cast<GByte *>(CPLMalloc(psSrcTCB->raw_bytes));
326
327
0
    memcpy(pabyRawTCB, psSrcTCB->raw_data, psSrcTCB->raw_bytes);
328
329
0
    if (!(nCreationFlags & DGNCF_USE_SEED_UNITS))
330
0
    {
331
0
        memcpy(pabyRawTCB + 1120, pszMasterUnits, 2);
332
0
        memcpy(pabyRawTCB + 1122, pszSubUnits, 2);
333
334
0
        DGN_WRITE_INT32(nUORPerSubUnit, pabyRawTCB + 1116);
335
0
        DGN_WRITE_INT32(nSubUnitsPerMasterUnit, pabyRawTCB + 1112);
336
0
    }
337
0
    else
338
0
    {
339
0
        nUORPerSubUnit = DGN_INT32(pabyRawTCB + 1116);
340
0
        nSubUnitsPerMasterUnit = DGN_INT32(pabyRawTCB + 1112);
341
0
    }
342
343
0
    if (!(nCreationFlags & DGNCF_USE_SEED_ORIGIN))
344
0
    {
345
0
        dfOriginX *= (nUORPerSubUnit * nSubUnitsPerMasterUnit);
346
0
        dfOriginY *= (nUORPerSubUnit * nSubUnitsPerMasterUnit);
347
0
        dfOriginZ *= (nUORPerSubUnit * nSubUnitsPerMasterUnit);
348
349
0
        memcpy(pabyRawTCB + 1240, &dfOriginX, 8);
350
0
        memcpy(pabyRawTCB + 1248, &dfOriginY, 8);
351
0
        memcpy(pabyRawTCB + 1256, &dfOriginZ, 8);
352
353
0
        IEEE2DGNDouble(pabyRawTCB + 1240);
354
0
        IEEE2DGNDouble(pabyRawTCB + 1248);
355
0
        IEEE2DGNDouble(pabyRawTCB + 1256);
356
0
    }
357
358
    /* -------------------------------------------------------------------- */
359
    /*      Write TCB and EOF to new file.                                  */
360
    /* -------------------------------------------------------------------- */
361
0
    VSIFWriteL(pabyRawTCB, psSrcTCB->raw_bytes, 1, fpNew);
362
0
    CPLFree(pabyRawTCB);
363
364
0
    unsigned char abyEOF[2] = {0xff, 0xff};
365
366
0
    VSIFWriteL(abyEOF, 2, 1, fpNew);
367
368
0
    DGNFreeElement(psSeed, psSrcTCB);
369
370
    /* -------------------------------------------------------------------- */
371
    /*      Close and re-open using DGN API.                                */
372
    /* -------------------------------------------------------------------- */
373
0
    VSIFCloseL(fpNew);
374
375
0
    DGNInfo *psDGN = (DGNInfo *)DGNOpen(pszNewFilename, TRUE);
376
377
    /* -------------------------------------------------------------------- */
378
    /*      Now copy over elements according to options in effect.          */
379
    /* -------------------------------------------------------------------- */
380
0
    DGNElemCore *psSrcElement = nullptr;
381
0
    DGNElemCore *psDstElement = nullptr;
382
383
0
    while ((psSrcElement = DGNReadElement(psSeed)) != nullptr)
384
0
    {
385
0
        if ((nCreationFlags & DGNCF_COPY_WHOLE_SEED_FILE) ||
386
0
            (psSrcElement->stype == DGNST_COLORTABLE &&
387
0
             nCreationFlags & DGNCF_COPY_SEED_FILE_COLOR_TABLE) ||
388
0
            psSrcElement->element_id <= 2)
389
0
        {
390
0
            psDstElement = DGNCloneElement(psSeed, psDGN, psSrcElement);
391
0
            DGNWriteElement(psDGN, psDstElement);
392
0
            DGNFreeElement(psDGN, psDstElement);
393
0
        }
394
395
0
        DGNFreeElement(psSeed, psSrcElement);
396
0
    }
397
398
0
    DGNClose(psSeed);
399
400
0
    return psDGN;
401
0
}
402
403
/************************************************************************/
404
/*                          DGNCloneElement()                           */
405
/************************************************************************/
406
407
/**
408
 * Clone a retargeted element.
409
 *
410
 * Creates a copy of an element in a suitable form to write to a
411
 * different file than that it was read from.
412
 *
413
 * NOTE: At this time the clone operation will fail if the source
414
 * and destination file have a different origin or master/sub units.
415
 *
416
 * @param hDGNSrc the source file (from which psSrcElement was read).
417
 * @param hDGNDst the destination file (to which the returned element may be
418
 * written).
419
 * @param psSrcElement the element to be cloned (from hDGNSrc).
420
 *
421
 * @return NULL on failure, or an appropriately modified copy of
422
 * the source element suitable to write to hDGNDst.
423
 */
424
425
DGNElemCore *DGNCloneElement(CPL_UNUSED DGNHandle hDGNSrc, DGNHandle hDGNDst,
426
                             const DGNElemCore *psSrcElement)
427
428
0
{
429
0
    DGNElemCore *psClone = nullptr;
430
431
0
    DGNLoadTCB(hDGNDst);
432
433
    /* -------------------------------------------------------------------- */
434
    /*      Per structure specific copying.  The core is fixed up later.    */
435
    /* -------------------------------------------------------------------- */
436
0
    if (psSrcElement->stype == DGNST_CORE)
437
0
    {
438
0
        psClone = static_cast<DGNElemCore *>(CPLMalloc(sizeof(DGNElemCore)));
439
0
        memcpy(psClone, psSrcElement, sizeof(DGNElemCore));
440
0
    }
441
0
    else if (psSrcElement->stype == DGNST_MULTIPOINT)
442
0
    {
443
0
        const auto psSrcMP =
444
0
            reinterpret_cast<const DGNElemMultiPoint *>(psSrcElement);
445
446
0
        const size_t nSize = sizeof(DGNElemMultiPoint) +
447
0
                             sizeof(DGNPoint) * (psSrcMP->num_vertices - 1);
448
449
0
        DGNElemMultiPoint *psMP =
450
0
            static_cast<DGNElemMultiPoint *>(CPLMalloc(nSize));
451
0
        memcpy(psMP, psSrcElement, nSize);
452
453
0
        psClone = reinterpret_cast<DGNElemCore *>(psMP);
454
0
    }
455
0
    else if (psSrcElement->stype == DGNST_ARC)
456
0
    {
457
0
        DGNElemArc *psArc =
458
0
            static_cast<DGNElemArc *>(CPLMalloc(sizeof(DGNElemArc)));
459
0
        memcpy(psArc, psSrcElement, sizeof(DGNElemArc));
460
461
0
        psClone = reinterpret_cast<DGNElemCore *>(psArc);
462
0
    }
463
0
    else if (psSrcElement->stype == DGNST_TEXT)
464
0
    {
465
0
        const auto psSrcText =
466
0
            reinterpret_cast<const DGNElemText *>(psSrcElement);
467
0
        const size_t nSize = sizeof(DGNElemText) + strlen(psSrcText->string);
468
469
0
        DGNElemText *psText = static_cast<DGNElemText *>(CPLMalloc(nSize));
470
0
        memcpy(psText, psSrcElement, nSize);
471
472
0
        psClone = reinterpret_cast<DGNElemCore *>(psText);
473
0
    }
474
0
    else if (psSrcElement->stype == DGNST_TEXT_NODE)
475
0
    {
476
0
        DGNElemTextNode *psNode =
477
0
            static_cast<DGNElemTextNode *>(CPLMalloc(sizeof(DGNElemTextNode)));
478
0
        memcpy(psNode, psSrcElement, sizeof(DGNElemTextNode));
479
480
0
        psClone = reinterpret_cast<DGNElemCore *>(psNode);
481
0
    }
482
0
    else if (psSrcElement->stype == DGNST_COMPLEX_HEADER)
483
0
    {
484
0
        DGNElemComplexHeader *psCH = static_cast<DGNElemComplexHeader *>(
485
0
            CPLMalloc(sizeof(DGNElemComplexHeader)));
486
0
        memcpy(psCH, psSrcElement, sizeof(DGNElemComplexHeader));
487
488
0
        psClone = reinterpret_cast<DGNElemCore *>(psCH);
489
0
    }
490
0
    else if (psSrcElement->stype == DGNST_COLORTABLE)
491
0
    {
492
0
        DGNElemColorTable *psCT = static_cast<DGNElemColorTable *>(
493
0
            CPLMalloc(sizeof(DGNElemColorTable)));
494
0
        memcpy(psCT, psSrcElement, sizeof(DGNElemColorTable));
495
496
0
        psClone = reinterpret_cast<DGNElemCore *>(psCT);
497
0
    }
498
0
    else if (psSrcElement->stype == DGNST_TCB)
499
0
    {
500
0
        DGNElemTCB *psTCB =
501
0
            static_cast<DGNElemTCB *>(CPLMalloc(sizeof(DGNElemTCB)));
502
0
        memcpy(psTCB, psSrcElement, sizeof(DGNElemTCB));
503
504
0
        psClone = reinterpret_cast<DGNElemCore *>(psTCB);
505
0
    }
506
0
    else if (psSrcElement->stype == DGNST_CELL_HEADER)
507
0
    {
508
0
        DGNElemCellHeader *psCH = static_cast<DGNElemCellHeader *>(
509
0
            CPLMalloc(sizeof(DGNElemCellHeader)));
510
0
        memcpy(psCH, psSrcElement, sizeof(DGNElemCellHeader));
511
512
0
        psClone = reinterpret_cast<DGNElemCore *>(psCH);
513
0
    }
514
0
    else if (psSrcElement->stype == DGNST_CELL_LIBRARY)
515
0
    {
516
0
        DGNElemCellLibrary *psCL = static_cast<DGNElemCellLibrary *>(
517
0
            CPLMalloc(sizeof(DGNElemCellLibrary)));
518
0
        memcpy(psCL, psSrcElement, sizeof(DGNElemCellLibrary));
519
520
0
        psClone = reinterpret_cast<DGNElemCore *>(psCL);
521
0
    }
522
0
    else if (psSrcElement->stype == DGNST_TAG_VALUE)
523
0
    {
524
0
        DGNElemTagValue *psTV =
525
0
            static_cast<DGNElemTagValue *>(CPLMalloc(sizeof(DGNElemTagValue)));
526
0
        memcpy(psTV, psSrcElement, sizeof(DGNElemTagValue));
527
528
0
        if (psTV->tagType == 1)
529
0
            psTV->tagValue.string = CPLStrdup(psTV->tagValue.string);
530
531
0
        psClone = reinterpret_cast<DGNElemCore *>(psTV);
532
0
    }
533
0
    else if (psSrcElement->stype == DGNST_TAG_SET)
534
0
    {
535
0
        DGNElemTagSet *psTS =
536
0
            static_cast<DGNElemTagSet *>(CPLMalloc(sizeof(DGNElemTagSet)));
537
0
        memcpy(psTS, psSrcElement, sizeof(DGNElemTagSet));
538
539
0
        psTS->tagSetName = CPLStrdup(psTS->tagSetName);
540
541
0
        DGNTagDef *pasTagList = static_cast<DGNTagDef *>(
542
0
            CPLMalloc(sizeof(DGNTagDef) * psTS->tagCount));
543
0
        memcpy(pasTagList, psTS->tagList, sizeof(DGNTagDef) * psTS->tagCount);
544
545
0
        for (int iTag = 0; iTag < psTS->tagCount; iTag++)
546
0
        {
547
0
            pasTagList[iTag].name = CPLStrdup(pasTagList[iTag].name);
548
0
            pasTagList[iTag].prompt = CPLStrdup(pasTagList[iTag].prompt);
549
0
            if (pasTagList[iTag].type == 1)
550
0
                pasTagList[iTag].defaultValue.string =
551
0
                    CPLStrdup(pasTagList[iTag].defaultValue.string);
552
0
        }
553
554
0
        psTS->tagList = pasTagList;
555
0
        psClone = reinterpret_cast<DGNElemCore *>(psTS);
556
0
    }
557
0
    else if (psSrcElement->stype == DGNST_CONE)
558
0
    {
559
0
        DGNElemCone *psCone =
560
0
            static_cast<DGNElemCone *>(CPLMalloc(sizeof(DGNElemCone)));
561
0
        memcpy(psCone, psSrcElement, sizeof(DGNElemCone));
562
563
0
        psClone = reinterpret_cast<DGNElemCore *>(psCone);
564
0
    }
565
0
    else if (psSrcElement->stype == DGNST_BSPLINE_SURFACE_HEADER)
566
0
    {
567
0
        DGNElemBSplineSurfaceHeader *psSurface =
568
0
            static_cast<DGNElemBSplineSurfaceHeader *>(
569
0
                CPLMalloc(sizeof(DGNElemBSplineSurfaceHeader)));
570
0
        memcpy(psSurface, psSrcElement, sizeof(DGNElemBSplineSurfaceHeader));
571
572
0
        psClone = reinterpret_cast<DGNElemCore *>(psSurface);
573
0
    }
574
0
    else if (psSrcElement->stype == DGNST_BSPLINE_CURVE_HEADER)
575
0
    {
576
0
        DGNElemBSplineCurveHeader *psCurve =
577
0
            static_cast<DGNElemBSplineCurveHeader *>(
578
0
                CPLMalloc(sizeof(DGNElemBSplineCurveHeader)));
579
0
        memcpy(psCurve, psSrcElement, sizeof(DGNElemBSplineCurveHeader));
580
581
0
        psClone = reinterpret_cast<DGNElemCore *>(psCurve);
582
0
    }
583
0
    else if (psSrcElement->stype == DGNST_BSPLINE_SURFACE_BOUNDARY)
584
0
    {
585
0
        const auto psSrcBSB =
586
0
            reinterpret_cast<const DGNElemBSplineSurfaceBoundary *>(
587
0
                psSrcElement);
588
589
0
        const size_t nSize = sizeof(DGNElemBSplineSurfaceBoundary) +
590
0
                             sizeof(DGNPoint) * (psSrcBSB->numverts - 1);
591
592
0
        DGNElemBSplineSurfaceBoundary *psBSB =
593
0
            static_cast<DGNElemBSplineSurfaceBoundary *>(CPLMalloc(nSize));
594
0
        memcpy(psBSB, psSrcElement, nSize);
595
596
0
        psClone = reinterpret_cast<DGNElemCore *>(psBSB);
597
0
    }
598
0
    else if (psSrcElement->stype == DGNST_KNOT_WEIGHT)
599
0
    {
600
        // FIXME: Is it OK to assume that the # of elements corresponds
601
        // directly to the element size? kintel 20051218.
602
0
        const int numelems =
603
0
            (psSrcElement->size - 36 - psSrcElement->attr_bytes) / 4;
604
605
        /* DGNElemKnotWeight *psSrcArray = (DGNElemKnotWeight *) psSrcElement;
606
         */
607
608
0
        const size_t nSize =
609
0
            sizeof(DGNElemKnotWeight) + sizeof(long) * (numelems - 1);
610
611
0
        DGNElemKnotWeight *psArray =
612
0
            static_cast<DGNElemKnotWeight *>(CPLMalloc(nSize));
613
0
        memcpy(psArray, psSrcElement, nSize);
614
615
0
        psClone = reinterpret_cast<DGNElemCore *>(psArray);
616
0
    }
617
0
    else if (psSrcElement->stype == DGNST_SHARED_CELL_DEFN)
618
0
    {
619
0
        DGNElemSharedCellDefn *psCH = static_cast<DGNElemSharedCellDefn *>(
620
0
            CPLMalloc(sizeof(DGNElemSharedCellDefn)));
621
0
        memcpy(psCH, psSrcElement, sizeof(DGNElemSharedCellDefn));
622
623
0
        psClone = reinterpret_cast<DGNElemCore *>(psCH);
624
0
    }
625
0
    else
626
0
    {
627
0
        CPLAssert(false);
628
0
        return nullptr;
629
0
    }
630
631
    /* -------------------------------------------------------------------- */
632
    /*      Copy core raw data, and attributes.                             */
633
    /* -------------------------------------------------------------------- */
634
0
    if (psClone->raw_bytes != 0)
635
0
    {
636
0
        psClone->raw_data =
637
0
            static_cast<unsigned char *>(CPLMalloc(psClone->raw_bytes));
638
0
        memcpy(psClone->raw_data, psSrcElement->raw_data, psClone->raw_bytes);
639
0
    }
640
641
0
    if (psClone->attr_bytes != 0)
642
0
    {
643
0
        psClone->attr_data =
644
0
            static_cast<unsigned char *>(CPLMalloc(psClone->attr_bytes));
645
0
        memcpy(psClone->attr_data, psSrcElement->attr_data,
646
0
               psClone->attr_bytes);
647
0
    }
648
649
    /* -------------------------------------------------------------------- */
650
    /*      Clear location and id information.                              */
651
    /* -------------------------------------------------------------------- */
652
0
    psClone->offset = -1;
653
0
    psClone->element_id = -1;
654
655
0
    return psClone;
656
0
}
657
658
/************************************************************************/
659
/*                         DGNUpdateElemCore()                          */
660
/************************************************************************/
661
662
/**
663
 * Change element core values.
664
 *
665
 * The indicated values in the element are updated in the structure, as well
666
 * as in the raw data.  The updated element is not written to disk.  That
667
 * must be done with DGNWriteElement().   The element must have raw_data
668
 * loaded.
669
 *
670
 * @param hDGN the file on which the element belongs.
671
 * @param psElement the element to modify.
672
 * @param nLevel the new level value.
673
 * @param nGraphicGroup the new graphic group value.
674
 * @param nColor the new color index.
675
 * @param nWeight the new element weight.
676
 * @param nStyle the new style value for the element.
677
 *
678
 * @return Returns TRUE on success or FALSE on failure.
679
 */
680
681
int DGNUpdateElemCore(DGNHandle hDGN, DGNElemCore *psElement, int nLevel,
682
                      int nGraphicGroup, int nColor, int nWeight, int nStyle)
683
684
0
{
685
0
    psElement->level = nLevel;
686
0
    psElement->graphic_group = nGraphicGroup;
687
0
    psElement->color = nColor;
688
0
    psElement->weight = nWeight;
689
0
    psElement->style = nStyle;
690
691
0
    return DGNUpdateElemCoreExtended(hDGN, psElement);
692
0
}
693
694
/************************************************************************/
695
/*                     DGNUpdateElemCoreExtended()                      */
696
/************************************************************************/
697
698
/**
699
 * Update internal raw data representation.
700
 *
701
 * The raw_data representation of the passed element is updated to reflect
702
 * the various core fields.  The DGNElemCore level, type, complex, deleted,
703
 * graphic_group, properties, color, weight and style values are all
704
 * applied to the raw_data representation.  Spatial bounds, element type
705
 * specific information and attributes are not updated in the raw data.
706
 *
707
 * @param hDGN the file to which the element belongs.
708
 * @param psElement the element to be updated.
709
 *
710
 * @return TRUE on success, or FALSE on failure.
711
 */
712
713
int DGNUpdateElemCoreExtended(CPL_UNUSED DGNHandle hDGN, DGNElemCore *psElement)
714
0
{
715
0
    GByte *rd = psElement->raw_data;
716
0
    const int nWords = (psElement->raw_bytes / 2) - 2;
717
718
0
    if (psElement->raw_data == nullptr || psElement->raw_bytes < 36)
719
0
    {
720
0
        CPLAssert(false);
721
0
        return FALSE;
722
0
    }
723
724
    /* -------------------------------------------------------------------- */
725
    /*      Setup first four bytes.                                         */
726
    /* -------------------------------------------------------------------- */
727
0
    rd[0] = (GByte)psElement->level;
728
0
    if (psElement->complex)
729
0
        rd[0] |= 0x80;
730
731
0
    rd[1] = (GByte)psElement->type;
732
0
    if (psElement->deleted)
733
0
        rd[1] |= 0x80;
734
735
0
    rd[2] = (GByte)(nWords % 256);
736
0
    rd[3] = (GByte)(nWords / 256);
737
738
    /* -------------------------------------------------------------------- */
739
    /*      If the attribute offset hasn't been set, set it now under       */
740
    /*      the assumption it should point to the end of the element.       */
741
    /* -------------------------------------------------------------------- */
742
0
    if (psElement->raw_data[30] == 0 && psElement->raw_data[31] == 0)
743
0
    {
744
0
        const int nAttIndex = (psElement->raw_bytes - 32) / 2;
745
746
0
        psElement->raw_data[30] = (GByte)(nAttIndex % 256);
747
0
        psElement->raw_data[31] = (GByte)(nAttIndex / 256);
748
0
    }
749
    /* -------------------------------------------------------------------- */
750
    /*      Handle the graphic properties.                                  */
751
    /* -------------------------------------------------------------------- */
752
0
    if (psElement->raw_bytes > 36 && DGNElemTypeHasDispHdr(psElement->type))
753
0
    {
754
0
        rd[28] = (GByte)(psElement->graphic_group % 256);
755
0
        rd[29] = (GByte)(psElement->graphic_group / 256);
756
0
        rd[32] = (GByte)(psElement->properties % 256);
757
0
        rd[33] = (GByte)(psElement->properties / 256);
758
0
        rd[34] = (GByte)(psElement->style | (psElement->weight << 3));
759
0
        rd[35] = (GByte)psElement->color;
760
0
    }
761
762
0
    return TRUE;
763
0
}
764
765
/************************************************************************/
766
/*                         DGNInitializeElemCore()                      */
767
/************************************************************************/
768
769
static void DGNInitializeElemCore(CPL_UNUSED DGNHandle hDGN,
770
                                  DGNElemCore *psElement)
771
0
{
772
0
    memset(psElement, 0, sizeof(DGNElemCore));
773
774
0
    psElement->offset = -1;
775
0
    psElement->element_id = -1;
776
0
}
777
778
/************************************************************************/
779
/*                           DGNWriteBounds()                           */
780
/*                                                                      */
781
/*      Write bounds to element raw data.                               */
782
/************************************************************************/
783
784
static void DGNWriteBounds(DGNInfo *psInfo, DGNElemCore *psElement,
785
                           DGNPoint *psMin, DGNPoint *psMax)
786
787
0
{
788
0
    CPLAssert(psElement->raw_bytes >= 28);
789
790
0
    DGNInverseTransformPointToInt(psInfo, psMin, psElement->raw_data + 4);
791
0
    DGNInverseTransformPointToInt(psInfo, psMax, psElement->raw_data + 16);
792
793
    /* convert from twos complement to "binary offset" format. */
794
795
0
    psElement->raw_data[5] ^= 0x80;
796
0
    psElement->raw_data[9] ^= 0x80;
797
0
    psElement->raw_data[13] ^= 0x80;
798
0
    psElement->raw_data[17] ^= 0x80;
799
0
    psElement->raw_data[21] ^= 0x80;
800
0
    psElement->raw_data[25] ^= 0x80;
801
0
}
802
803
/************************************************************************/
804
/*                      DGNCreateMultiPointElem()                       */
805
/************************************************************************/
806
807
/**
808
 * Create new multi-point element.
809
 *
810
 * The newly created element will still need to be written to file using
811
 * DGNWriteElement(). Also the level and other core values will be defaulted.
812
 * Use DGNUpdateElemCore() on the element before writing to set these values.
813
 *
814
 * NOTE: There are restrictions on the nPointCount for some elements. For
815
 * instance, DGNT_LINE can only have 2 points. Maximum element size
816
 * precludes very large numbers of points.
817
 *
818
 * @param hDGN the file on which the element will eventually be written.
819
 * @param nType the type of the element to be created.  It must be one of
820
 * DGNT_LINE, DGNT_LINE_STRING, DGNT_SHAPE, DGNT_CURVE or DGNT_BSPLINE_POLE.
821
 * @param nPointCount the number of points in the pasVertices list.
822
 * @param pasVertices the list of points to be written.
823
 *
824
 * @return the new element (a DGNElemMultiPoint structure) or NULL on failure.
825
 */
826
827
DGNElemCore *DGNCreateMultiPointElem(DGNHandle hDGN, int nType, int nPointCount,
828
                                     DGNPoint *pasVertices)
829
830
0
{
831
0
    DGNInfo *psDGN = (DGNInfo *)hDGN;
832
833
0
    CPLAssert(nType == DGNT_LINE || nType == DGNT_LINE_STRING ||
834
0
              nType == DGNT_SHAPE || nType == DGNT_CURVE ||
835
0
              nType == DGNT_BSPLINE_POLE);
836
837
0
    DGNLoadTCB(hDGN);
838
839
    /* -------------------------------------------------------------------- */
840
    /*      Is this too many vertices to write to a single element?         */
841
    /* -------------------------------------------------------------------- */
842
0
    if (nPointCount > 101)
843
0
    {
844
0
        CPLError(CE_Failure, CPLE_ElementTooBig,
845
0
                 "Attempt to create %s element with %d points failed.\n"
846
0
                 "Element would be too large.",
847
0
                 DGNTypeToName(nType), nPointCount);
848
0
        return nullptr;
849
0
    }
850
851
    /* -------------------------------------------------------------------- */
852
    /*      Allocate element.                                               */
853
    /* -------------------------------------------------------------------- */
854
0
    DGNElemMultiPoint *psMP = static_cast<DGNElemMultiPoint *>(CPLCalloc(
855
0
        sizeof(DGNElemMultiPoint) + sizeof(DGNPoint) * (nPointCount - 1), 1));
856
0
    DGNElemCore *psCore = &(psMP->core);
857
858
0
    DGNInitializeElemCore(hDGN, psCore);
859
0
    psCore->stype = DGNST_MULTIPOINT;
860
0
    psCore->type = nType;
861
862
    /* -------------------------------------------------------------------- */
863
    /*      Set multipoint specific information in the structure.           */
864
    /* -------------------------------------------------------------------- */
865
0
    psMP->num_vertices = nPointCount;
866
    // coverity[overrun-buffer-arg]
867
0
    memcpy(psMP->vertices + 0, pasVertices, sizeof(DGNPoint) * nPointCount);
868
869
    /* -------------------------------------------------------------------- */
870
    /*      Setup Raw data for the multipoint section.                      */
871
    /* -------------------------------------------------------------------- */
872
0
    if (nType == DGNT_LINE)
873
0
    {
874
0
        CPLAssert(nPointCount == 2);
875
876
0
        psCore->raw_bytes = 36 + psDGN->dimension * 4 * nPointCount;
877
878
0
        psCore->raw_data =
879
0
            static_cast<unsigned char *>(CPLCalloc(psCore->raw_bytes, 1));
880
881
0
        DGNInverseTransformPointToInt(psDGN, pasVertices + 0,
882
0
                                      psCore->raw_data + 36);
883
0
        DGNInverseTransformPointToInt(psDGN, pasVertices + 1,
884
0
                                      psCore->raw_data + 36 +
885
0
                                          psDGN->dimension * 4);
886
0
    }
887
0
    else
888
0
    {
889
0
        CPLAssert(nPointCount >= 2);
890
891
0
        psCore->raw_bytes = 38 + psDGN->dimension * 4 * nPointCount;
892
0
        psCore->raw_data =
893
0
            static_cast<unsigned char *>(CPLCalloc(psCore->raw_bytes, 1));
894
895
0
        psCore->raw_data[36] = (unsigned char)(nPointCount % 256);
896
0
        psCore->raw_data[37] = (unsigned char)(nPointCount / 256);
897
898
0
        for (int i = 0; i < nPointCount; i++)
899
0
            DGNInverseTransformPointToInt(psDGN, pasVertices + i,
900
0
                                          psCore->raw_data + 38 +
901
0
                                              psDGN->dimension * i * 4);
902
0
    }
903
904
    /* -------------------------------------------------------------------- */
905
    /*      Set the core raw data, including the bounds.                    */
906
    /* -------------------------------------------------------------------- */
907
0
    DGNUpdateElemCoreExtended(hDGN, psCore);
908
909
0
    DGNPoint sMin = pasVertices[0];
910
0
    DGNPoint sMax = pasVertices[0];
911
0
    for (int i = 1; i < nPointCount; i++)
912
0
    {
913
0
        sMin.x = std::min(pasVertices[i].x, sMin.x);
914
0
        sMin.y = std::min(pasVertices[i].y, sMin.y);
915
0
        sMin.z = std::min(pasVertices[i].z, sMin.z);
916
0
        sMax.x = std::max(pasVertices[i].x, sMax.x);
917
0
        sMax.y = std::max(pasVertices[i].y, sMax.y);
918
0
        sMax.z = std::max(pasVertices[i].z, sMax.z);
919
0
    }
920
921
0
    DGNWriteBounds(psDGN, psCore, &sMin, &sMax);
922
923
0
    return reinterpret_cast<DGNElemCore *>(psMP);
924
0
}
925
926
/************************************************************************/
927
/*                         DGNCreateArcElem2D()                         */
928
/************************************************************************/
929
930
DGNElemCore *DGNCreateArcElem2D(DGNHandle hDGN, int nType, double dfOriginX,
931
                                double dfOriginY, double dfPrimaryAxis,
932
                                double dfSecondaryAxis, double dfRotation,
933
                                double dfStartAngle, double dfSweepAngle)
934
935
0
{
936
0
    return DGNCreateArcElem(hDGN, nType, dfOriginX, dfOriginY, 0.0,
937
0
                            dfPrimaryAxis, dfSecondaryAxis, dfStartAngle,
938
0
                            dfSweepAngle, dfRotation, nullptr);
939
0
}
940
941
/************************************************************************/
942
/*                          DGNCreateArcElem()                          */
943
/************************************************************************/
944
945
/**
946
 * Create Arc or Ellipse element.
947
 *
948
 * Create a new 2D or 3D arc or ellipse element.  The start angle, and sweep
949
 * angle are ignored for DGNT_ELLIPSE but used for DGNT_ARC.
950
 *
951
 * The newly created element will still need to be written to file using
952
 * DGNWriteElement(). Also the level and other core values will be defaulted.
953
 * Use DGNUpdateElemCore() on the element before writing to set these values.
954
 *
955
 * @param hDGN the DGN file on which the element will eventually be written.
956
 * @param nType either DGNT_ELLIPSE or DGNT_ARC to select element type.
957
 * @param dfOriginX the origin (center of rotation) of the arc (X).
958
 * @param dfOriginY the origin (center of rotation) of the arc (Y).
959
 * @param dfOriginZ the origin (center of rotation) of the arc (Y).
960
 * @param dfPrimaryAxis the length of the primary axis.
961
 * @param dfSecondaryAxis the length of the secondary axis.
962
 * @param dfStartAngle start angle, degrees counterclockwise of primary axis.
963
 * @param dfSweepAngle sweep angle, degrees
964
 * @param dfRotation Counterclockwise rotation in degrees.
965
 * @param panQuaternion 3D orientation quaternion (NULL to use rotation).
966
 *
967
 * @return the new element (DGNElemArc) or NULL on failure.
968
 */
969
970
DGNElemCore *DGNCreateArcElem(DGNHandle hDGN, int nType, double dfOriginX,
971
                              double dfOriginY, double dfOriginZ,
972
                              double dfPrimaryAxis, double dfSecondaryAxis,
973
                              double dfStartAngle, double dfSweepAngle,
974
                              double dfRotation, int *panQuaternion)
975
976
0
{
977
0
    CPLAssert(nType == DGNT_ARC || nType == DGNT_ELLIPSE);
978
979
0
    DGNInfo *psDGN = (DGNInfo *)hDGN;
980
0
    DGNLoadTCB(hDGN);
981
982
    /* -------------------------------------------------------------------- */
983
    /*      Allocate element.                                               */
984
    /* -------------------------------------------------------------------- */
985
0
    DGNElemArc *psArc =
986
0
        static_cast<DGNElemArc *>(CPLCalloc(sizeof(DGNElemArc), 1));
987
0
    DGNElemCore *psCore = &(psArc->core);
988
989
0
    DGNInitializeElemCore(hDGN, psCore);
990
0
    psCore->stype = DGNST_ARC;
991
0
    psCore->type = nType;
992
993
    /* -------------------------------------------------------------------- */
994
    /*      Set arc specific information in the structure.                  */
995
    /* -------------------------------------------------------------------- */
996
0
    DGNPoint sOrigin = {dfOriginX, dfOriginY, dfOriginZ};
997
998
0
    psArc->origin = sOrigin;
999
0
    psArc->primary_axis = dfPrimaryAxis;
1000
0
    psArc->secondary_axis = dfSecondaryAxis;
1001
0
    memset(psArc->quat, 0, sizeof(int) * 4);
1002
0
    psArc->startang = dfStartAngle;
1003
0
    psArc->sweepang = dfSweepAngle;
1004
1005
0
    psArc->rotation = dfRotation;
1006
0
    if (panQuaternion == nullptr)
1007
0
    {
1008
0
        DGNRotationToQuaternion(dfRotation, psArc->quat);
1009
0
    }
1010
0
    else
1011
0
    {
1012
0
        memcpy(psArc->quat, panQuaternion, sizeof(int) * 4);
1013
0
    }
1014
1015
    /* -------------------------------------------------------------------- */
1016
    /*      Setup Raw data for the arc section.                             */
1017
    /* -------------------------------------------------------------------- */
1018
0
    if (nType == DGNT_ARC)
1019
0
    {
1020
0
        double dfScaledAxis;
1021
1022
0
        if (psDGN->dimension == 3)
1023
0
            psCore->raw_bytes = 100;
1024
0
        else
1025
0
            psCore->raw_bytes = 80;
1026
0
        psCore->raw_data =
1027
0
            static_cast<unsigned char *>(CPLCalloc(psCore->raw_bytes, 1));
1028
1029
        /* start angle */
1030
0
        GInt32 nAngle = (int)(dfStartAngle * 360000.0);
1031
0
        DGN_WRITE_INT32(nAngle, psCore->raw_data + 36);
1032
1033
        /* sweep angle */
1034
0
        if (dfSweepAngle < 0.0)
1035
0
        {
1036
0
            nAngle = static_cast<int>(std::abs(dfSweepAngle) * 360000.0);
1037
0
            nAngle |= 0x80000000;
1038
0
        }
1039
0
        else if (dfSweepAngle > 364.9999)
1040
0
        {
1041
0
            nAngle = 0;
1042
0
        }
1043
0
        else
1044
0
        {
1045
0
            nAngle = (int)(dfSweepAngle * 360000.0);
1046
0
        }
1047
0
        DGN_WRITE_INT32(nAngle, psCore->raw_data + 40);
1048
1049
        /* axes */
1050
0
        dfScaledAxis = dfPrimaryAxis / psDGN->scale;
1051
0
        memcpy(psCore->raw_data + 44, &dfScaledAxis, 8);
1052
0
        IEEE2DGNDouble(psCore->raw_data + 44);
1053
1054
0
        dfScaledAxis = dfSecondaryAxis / psDGN->scale;
1055
0
        memcpy(psCore->raw_data + 52, &dfScaledAxis, 8);
1056
0
        IEEE2DGNDouble(psCore->raw_data + 52);
1057
1058
0
        if (psDGN->dimension == 3)
1059
0
        {
1060
            /* quaternion */
1061
0
            DGN_WRITE_INT32(psArc->quat[0], psCore->raw_data + 60);
1062
0
            DGN_WRITE_INT32(psArc->quat[1], psCore->raw_data + 64);
1063
0
            DGN_WRITE_INT32(psArc->quat[2], psCore->raw_data + 68);
1064
0
            DGN_WRITE_INT32(psArc->quat[3], psCore->raw_data + 72);
1065
1066
            /* origin */
1067
0
            DGNInverseTransformPoint(psDGN, &sOrigin);
1068
0
            memcpy(psCore->raw_data + 76, &(sOrigin.x), 8);
1069
0
            memcpy(psCore->raw_data + 84, &(sOrigin.y), 8);
1070
0
            memcpy(psCore->raw_data + 92, &(sOrigin.z), 8);
1071
0
            IEEE2DGNDouble(psCore->raw_data + 76);
1072
0
            IEEE2DGNDouble(psCore->raw_data + 84);
1073
0
            IEEE2DGNDouble(psCore->raw_data + 92);
1074
0
        }
1075
0
        else
1076
0
        {
1077
            /* rotation */
1078
0
            nAngle = (int)(dfRotation * 360000.0);
1079
0
            DGN_WRITE_INT32(nAngle, psCore->raw_data + 60);
1080
1081
            /* origin */
1082
0
            DGNInverseTransformPoint(psDGN, &sOrigin);
1083
0
            memcpy(psCore->raw_data + 64, &(sOrigin.x), 8);
1084
0
            memcpy(psCore->raw_data + 72, &(sOrigin.y), 8);
1085
0
            IEEE2DGNDouble(psCore->raw_data + 64);
1086
0
            IEEE2DGNDouble(psCore->raw_data + 72);
1087
0
        }
1088
0
    }
1089
1090
    /* -------------------------------------------------------------------- */
1091
    /*      Setup Raw data for the ellipse section.                         */
1092
    /* -------------------------------------------------------------------- */
1093
0
    else
1094
0
    {
1095
0
        double dfScaledAxis;
1096
1097
0
        if (psDGN->dimension == 3)
1098
0
            psCore->raw_bytes = 92;
1099
0
        else
1100
0
            psCore->raw_bytes = 72;
1101
0
        psCore->raw_data = (unsigned char *)CPLCalloc(psCore->raw_bytes, 1);
1102
1103
        /* axes */
1104
0
        dfScaledAxis = dfPrimaryAxis / psDGN->scale;
1105
0
        memcpy(psCore->raw_data + 36, &dfScaledAxis, 8);
1106
0
        IEEE2DGNDouble(psCore->raw_data + 36);
1107
1108
0
        dfScaledAxis = dfSecondaryAxis / psDGN->scale;
1109
0
        memcpy(psCore->raw_data + 44, &dfScaledAxis, 8);
1110
0
        IEEE2DGNDouble(psCore->raw_data + 44);
1111
1112
0
        if (psDGN->dimension == 3)
1113
0
        {
1114
            /* quaternion */
1115
0
            DGN_WRITE_INT32(psArc->quat[0], psCore->raw_data + 52);
1116
0
            DGN_WRITE_INT32(psArc->quat[1], psCore->raw_data + 56);
1117
0
            DGN_WRITE_INT32(psArc->quat[2], psCore->raw_data + 60);
1118
0
            DGN_WRITE_INT32(psArc->quat[3], psCore->raw_data + 64);
1119
1120
            /* origin */
1121
0
            DGNInverseTransformPoint(psDGN, &sOrigin);
1122
0
            memcpy(psCore->raw_data + 68, &(sOrigin.x), 8);
1123
0
            memcpy(psCore->raw_data + 76, &(sOrigin.y), 8);
1124
0
            memcpy(psCore->raw_data + 84, &(sOrigin.z), 8);
1125
0
            IEEE2DGNDouble(psCore->raw_data + 68);
1126
0
            IEEE2DGNDouble(psCore->raw_data + 76);
1127
0
            IEEE2DGNDouble(psCore->raw_data + 84);
1128
0
        }
1129
0
        else
1130
0
        {
1131
            /* rotation */
1132
0
            GInt32 nAngle = (int)(dfRotation * 360000.0);
1133
0
            DGN_WRITE_INT32(nAngle, psCore->raw_data + 52);
1134
1135
            /* origin */
1136
0
            DGNInverseTransformPoint(psDGN, &sOrigin);
1137
0
            memcpy(psCore->raw_data + 56, &(sOrigin.x), 8);
1138
0
            memcpy(psCore->raw_data + 64, &(sOrigin.y), 8);
1139
0
            IEEE2DGNDouble(psCore->raw_data + 56);
1140
0
            IEEE2DGNDouble(psCore->raw_data + 64);
1141
0
        }
1142
1143
0
        psArc->startang = 0.0;
1144
0
        psArc->sweepang = 360.0;
1145
0
    }
1146
1147
    /* -------------------------------------------------------------------- */
1148
    /*      Set the core raw data, including the bounds.                    */
1149
    /* -------------------------------------------------------------------- */
1150
0
    DGNUpdateElemCoreExtended(hDGN, psCore);
1151
1152
0
    DGNPoint sMin = {dfOriginX - std::max(dfPrimaryAxis, dfSecondaryAxis),
1153
0
                     dfOriginY - std::max(dfPrimaryAxis, dfSecondaryAxis),
1154
0
                     dfOriginZ - std::max(dfPrimaryAxis, dfSecondaryAxis)};
1155
0
    DGNPoint sMax = {dfOriginX + std::max(dfPrimaryAxis, dfSecondaryAxis),
1156
0
                     dfOriginY + std::max(dfPrimaryAxis, dfSecondaryAxis),
1157
0
                     dfOriginZ + std::max(dfPrimaryAxis, dfSecondaryAxis)};
1158
1159
0
    DGNWriteBounds(psDGN, psCore, &sMin, &sMax);
1160
1161
0
    return reinterpret_cast<DGNElemCore *>(psArc);
1162
0
}
1163
1164
/************************************************************************/
1165
/*                          DGNCreateConeElem()                         */
1166
/************************************************************************/
1167
1168
/**
1169
 * Create Cone element.
1170
 *
1171
 * Create a new 3D cone element.
1172
 *
1173
 * The newly created element will still need to be written to file using
1174
 * DGNWriteElement(). Also the level and other core values will be defaulted.
1175
 * Use DGNUpdateElemCore() on the element before writing to set these values.
1176
 *
1177
 * @param hDGN the DGN file on which the element will eventually be written.
1178
 * @param dfCenter_1X the center of the first bounding circle (X).
1179
 * @param dfCenter_1Y the center of the first bounding circle (Y).
1180
 * @param dfCenter_1Z the center of the first bounding circle (Z).
1181
 * @param dfRadius_1 the radius of the first bounding circle.
1182
 * @param dfCenter_2X the center of the second bounding circle (X).
1183
 * @param dfCenter_2Y the center of the second bounding circle (Y).
1184
 * @param dfCenter_2Z the center of the second bounding circle (Z).
1185
 * @param dfRadius_2 the radius of the second bounding circle.
1186
 * @param panQuaternion 3D orientation quaternion (NULL for default orientation
1187
 * - circles parallel to the X-Y plane).
1188
 *
1189
 * @return the new element (DGNElemCone) or NULL on failure.
1190
 */
1191
1192
DGNElemCore *DGNCreateConeElem(DGNHandle hDGN, double dfCenter_1X,
1193
                               double dfCenter_1Y, double dfCenter_1Z,
1194
                               double dfRadius_1, double dfCenter_2X,
1195
                               double dfCenter_2Y, double dfCenter_2Z,
1196
                               double dfRadius_2, int *panQuaternion)
1197
0
{
1198
0
    DGNInfo *psDGN = (DGNInfo *)hDGN;
1199
1200
0
    DGNLoadTCB(hDGN);
1201
1202
    /* -------------------------------------------------------------------- */
1203
    /*      Allocate element.                                               */
1204
    /* -------------------------------------------------------------------- */
1205
0
    DGNElemCone *psCone = (DGNElemCone *)CPLCalloc(sizeof(DGNElemCone), 1);
1206
0
    DGNElemCore *psCore = &(psCone->core);
1207
1208
0
    DGNInitializeElemCore(hDGN, psCore);
1209
0
    psCore->stype = DGNST_CONE;
1210
0
    psCore->type = DGNT_CONE;
1211
1212
    /* -------------------------------------------------------------------- */
1213
    /*      Set cone specific information in the structure.                 */
1214
    /* -------------------------------------------------------------------- */
1215
0
    DGNPoint sCenter_1 = {dfCenter_1X, dfCenter_1Y, dfCenter_1Z};
1216
0
    DGNPoint sCenter_2 = {dfCenter_2X, dfCenter_2Y, dfCenter_2Z};
1217
0
    psCone->center_1 = sCenter_1;
1218
0
    psCone->center_2 = sCenter_2;
1219
0
    psCone->radius_1 = dfRadius_1;
1220
0
    psCone->radius_2 = dfRadius_2;
1221
1222
0
    memset(psCone->quat, 0, sizeof(int) * 4);
1223
0
    if (panQuaternion != nullptr)
1224
0
    {
1225
0
        memcpy(psCone->quat, panQuaternion, sizeof(int) * 4);
1226
0
    }
1227
0
    else
1228
0
    {
1229
0
        psCone->quat[0] = static_cast<int>(1U << 31);
1230
0
        psCone->quat[1] = 0;
1231
0
        psCone->quat[2] = 0;
1232
0
        psCone->quat[3] = 0;
1233
0
    }
1234
1235
    /* -------------------------------------------------------------------- */
1236
    /*      Setup Raw data for the cone.                                    */
1237
    /* -------------------------------------------------------------------- */
1238
0
    psCore->raw_bytes = 118;
1239
0
    psCore->raw_data = (unsigned char *)CPLCalloc(psCore->raw_bytes, 1);
1240
1241
    /* unknown data */
1242
0
    psCore->raw_data[36] = 0;
1243
0
    psCore->raw_data[37] = 0;
1244
1245
    /* quaternion */
1246
0
    DGN_WRITE_INT32(psCone->quat[0], psCore->raw_data + 38);
1247
0
    DGN_WRITE_INT32(psCone->quat[1], psCore->raw_data + 42);
1248
0
    DGN_WRITE_INT32(psCone->quat[2], psCore->raw_data + 46);
1249
0
    DGN_WRITE_INT32(psCone->quat[3], psCore->raw_data + 50);
1250
1251
    /* center_1 */
1252
0
    DGNInverseTransformPoint(psDGN, &sCenter_1);
1253
0
    memcpy(psCore->raw_data + 54, &sCenter_1.x, 8);
1254
0
    memcpy(psCore->raw_data + 62, &sCenter_1.y, 8);
1255
0
    memcpy(psCore->raw_data + 70, &sCenter_1.z, 8);
1256
0
    IEEE2DGNDouble(psCore->raw_data + 54);
1257
0
    IEEE2DGNDouble(psCore->raw_data + 62);
1258
0
    IEEE2DGNDouble(psCore->raw_data + 70);
1259
1260
    /* radius_1 */
1261
0
    double dfScaledRadius = psCone->radius_1 / psDGN->scale;
1262
0
    memcpy(psCore->raw_data + 78, &dfScaledRadius, 8);
1263
0
    IEEE2DGNDouble(psCore->raw_data + 78);
1264
1265
    /* center_2 */
1266
0
    DGNInverseTransformPoint(psDGN, &sCenter_2);
1267
0
    memcpy(psCore->raw_data + 86, &sCenter_2.x, 8);
1268
0
    memcpy(psCore->raw_data + 94, &sCenter_2.y, 8);
1269
0
    memcpy(psCore->raw_data + 102, &sCenter_2.z, 8);
1270
0
    IEEE2DGNDouble(psCore->raw_data + 86);
1271
0
    IEEE2DGNDouble(psCore->raw_data + 94);
1272
0
    IEEE2DGNDouble(psCore->raw_data + 102);
1273
1274
    /* radius_2 */
1275
0
    dfScaledRadius = psCone->radius_2 / psDGN->scale;
1276
0
    memcpy(psCore->raw_data + 110, &dfScaledRadius, 8);
1277
0
    IEEE2DGNDouble(psCore->raw_data + 110);
1278
1279
    /* -------------------------------------------------------------------- */
1280
    /*      Set the core raw data, including the bounds.                    */
1281
    /* -------------------------------------------------------------------- */
1282
0
    DGNUpdateElemCoreExtended(hDGN, psCore);
1283
1284
    // FIXME: Calculate bounds. Do we need to take the quaternion into account?
1285
    //  kintel 20030819
1286
1287
    // Old implementation attempt:
1288
    // What if center_1.z > center_2.z ?
1289
    //     double largestRadius =
1290
    //       psCone->radius_1>psCone->radius_2?psCone->radius_1:psCone->radius_2;
1291
    //     sMin.x = psCone->center_1.x-largestRadius;
1292
    //     sMin.y = psCone->center_1.y-largestRadius;
1293
    //     sMin.z = psCone->center_1.z;
1294
    //     sMax.x = psCone->center_2.x+largestRadius;
1295
    //     sMax.y = psCone->center_2.y+largestRadius;
1296
    //     sMax.z = psCone->center_2.z;
1297
1298
0
    DGNPoint sMin = {0.0, 0.0, 0.0};
1299
0
    DGNPoint sMax = {0.0, 0.0, 0.0};
1300
0
    DGNWriteBounds(psDGN, psCore, &sMin, &sMax);
1301
1302
0
    return reinterpret_cast<DGNElemCore *>(psCone);
1303
0
}
1304
1305
/************************************************************************/
1306
/*                         DGNCreateTextElem()                          */
1307
/************************************************************************/
1308
1309
/**
1310
 * Create text element.
1311
 *
1312
 * The newly created element will still need to be written to file using
1313
 * DGNWriteElement(). Also the level and other core values will be defaulted.
1314
 * Use DGNUpdateElemCore() on the element before writing to set these values.
1315
 *
1316
 * @param hDGN the file on which the element will eventually be written.
1317
 * @param pszText the string of text.
1318
 * @param nFontId microstation font id for the text.  1 may be used as default.
1319
 * @param nJustification text justification.  One of DGNJ_LEFT_TOP,
1320
 * DGNJ_LEFT_CENTER, DGNJ_LEFT_BOTTOM, DGNJ_CENTER_TOP, DGNJ_CENTER_CENTER,
1321
 * DGNJ_CENTER_BOTTOM, DGNJ_RIGHT_TOP, DGNJ_RIGHT_CENTER, DGNJ_RIGHT_BOTTOM.
1322
 * @param dfLengthMult character width in master units.
1323
 * @param dfHeightMult character height in master units.
1324
 * @param dfRotation Counterclockwise text rotation in degrees.
1325
 * @param panQuaternion 3D orientation quaternion (NULL to use rotation).
1326
 * @param dfOriginX Text origin (X).
1327
 * @param dfOriginY Text origin (Y).
1328
 * @param dfOriginZ Text origin (Z).
1329
 *
1330
 * @return the new element (DGNElemText) or NULL on failure.
1331
 */
1332
1333
DGNElemCore *DGNCreateTextElem(DGNHandle hDGN, const char *pszText, int nFontId,
1334
                               int nJustification, double dfLengthMult,
1335
                               double dfHeightMult, double dfRotation,
1336
                               int *panQuaternion, double dfOriginX,
1337
                               double dfOriginY, double dfOriginZ)
1338
1339
0
{
1340
0
    DGNInfo *psDGN = (DGNInfo *)hDGN;
1341
1342
0
    DGNLoadTCB(hDGN);
1343
1344
    /* -------------------------------------------------------------------- */
1345
    /*      Allocate element.                                               */
1346
    /* -------------------------------------------------------------------- */
1347
0
    DGNElemText *psText =
1348
0
        (DGNElemText *)CPLCalloc(sizeof(DGNElemText) + strlen(pszText), 1);
1349
0
    DGNElemCore *psCore = &(psText->core);
1350
1351
0
    DGNInitializeElemCore(hDGN, psCore);
1352
0
    psCore->stype = DGNST_TEXT;
1353
0
    psCore->type = DGNT_TEXT;
1354
1355
    /* -------------------------------------------------------------------- */
1356
    /*      Set arc specific information in the structure.                  */
1357
    /* -------------------------------------------------------------------- */
1358
0
    psText->font_id = nFontId;
1359
0
    psText->justification = nJustification;
1360
0
    psText->length_mult = dfLengthMult;
1361
0
    psText->height_mult = dfHeightMult;
1362
0
    psText->rotation = dfRotation;
1363
0
    psText->origin.x = dfOriginX;
1364
0
    psText->origin.y = dfOriginY;
1365
0
    psText->origin.z = dfOriginZ;
1366
0
    strcpy(psText->string, pszText);
1367
1368
    /* -------------------------------------------------------------------- */
1369
    /*      Setup Raw data for the text specific portion.                   */
1370
    /* -------------------------------------------------------------------- */
1371
0
    if (psDGN->dimension == 2)
1372
0
        psCore->raw_bytes = 60 + static_cast<int>(strlen(pszText));
1373
0
    else
1374
0
        psCore->raw_bytes = 76 + static_cast<int>(strlen(pszText));
1375
1376
0
    psCore->raw_bytes += (psCore->raw_bytes % 2);
1377
0
    psCore->raw_data = (unsigned char *)CPLCalloc(psCore->raw_bytes, 1);
1378
1379
0
    psCore->raw_data[36] = (unsigned char)nFontId;
1380
0
    psCore->raw_data[37] = (unsigned char)nJustification;
1381
1382
0
    GInt32 nIntValue =
1383
0
        static_cast<int>(dfLengthMult * 1000.0 / (psDGN->scale * 6.0) + 0.5);
1384
0
    DGN_WRITE_INT32(nIntValue, psCore->raw_data + 38);
1385
1386
0
    nIntValue = (int)(dfHeightMult * 1000.0 / (psDGN->scale * 6.0) + 0.5);
1387
0
    DGN_WRITE_INT32(nIntValue, psCore->raw_data + 42);
1388
1389
0
    GInt32 nBase = 0;
1390
1391
0
    if (psDGN->dimension == 2)
1392
0
    {
1393
0
        nIntValue = (int)(dfRotation * 360000.0);
1394
0
        DGN_WRITE_INT32(nIntValue, psCore->raw_data + 46);
1395
1396
0
        DGNInverseTransformPointToInt(psDGN, &(psText->origin),
1397
0
                                      psCore->raw_data + 50);
1398
1399
0
        nBase = 58;
1400
0
    }
1401
0
    else
1402
0
    {
1403
0
        int anQuaternion[4];
1404
1405
0
        if (panQuaternion == nullptr)
1406
0
            DGNRotationToQuaternion(dfRotation, anQuaternion);
1407
0
        else
1408
0
            memcpy(anQuaternion, panQuaternion, sizeof(int) * 4);
1409
1410
0
        DGN_WRITE_INT32(anQuaternion[0], psCore->raw_data + 46);
1411
0
        DGN_WRITE_INT32(anQuaternion[1], psCore->raw_data + 50);
1412
0
        DGN_WRITE_INT32(anQuaternion[2], psCore->raw_data + 54);
1413
0
        DGN_WRITE_INT32(anQuaternion[3], psCore->raw_data + 58);
1414
1415
0
        DGNInverseTransformPointToInt(psDGN, &(psText->origin),
1416
0
                                      psCore->raw_data + 62);
1417
0
        nBase = 74;
1418
0
    }
1419
1420
0
    psCore->raw_data[nBase] = (unsigned char)strlen(pszText);
1421
0
    psCore->raw_data[nBase + 1] = 0; /* edflds? */
1422
0
    memcpy(psCore->raw_data + nBase + 2, pszText, strlen(pszText));
1423
1424
    /* -------------------------------------------------------------------- */
1425
    /*      Set the core raw data, including the bounds.                    */
1426
    /*                                                                      */
1427
    /*      Code contributed by Mart Kelder.                                */
1428
    /* -------------------------------------------------------------------- */
1429
0
    DGNUpdateElemCoreExtended(hDGN, psCore);
1430
1431
    // calculate bounds if rotation is 0
1432
0
    DGNPoint sMin = {dfOriginX, dfOriginY, 0.0};
1433
0
    DGNPoint sMax = {dfOriginX + dfLengthMult * strlen(pszText),
1434
0
                     dfOriginY + dfHeightMult, 0.0};
1435
1436
#if 0
1437
    //calculate rotated bounding box coordinates
1438
    const double length = sMax.x-sMin.x;
1439
    const double height = sMax.y-sMin.y;
1440
    const double diagonal=sqrt(length*length+height*height);
1441
    const DGNPoint sLowLeft = { sMin.x, sMin.y, 0.0 };
1442
    const DGNPoint sLowRight = {
1443
       sMin.x+cos(psText->rotation*M_PI/180.0)*length,
1444
       sMin.y+sin(psText->rotation*M_PI/180.0)*length,
1445
       0.0
1446
    };
1447
    const DGNPoint sUpRight = {
1448
        sMin.x+cos((psText->rotation*M_PI/180.0)+atan(height/length))*diagonal,
1449
        sMin.y+sin((psText->rotation*M_PI/180.0)+atan(height/length))*diagonal,
1450
        0.0
1451
    };
1452
    const DGNPoint sUpLeft = {
1453
        sMin.x+cos((psText->rotation+90.0)*M_PI/180.0)*height,
1454
        sMin.y+sin((psText->rotation+90.0)*M_PI/180.0)*height,
1455
        0.0
1456
    };
1457
1458
    //calculate new values for bounding box
1459
    sMin.x = std::min(sLowLeft.x,
1460
                      std::min(sLowRight.x, std::min(sUpLeft.x, sUpRight.x)));
1461
    sMin.y = std::min(sLowLeft.y,
1462
                      std::min(sLowRight.y, std::min(sUpLeft.y, sUpRight.y)));
1463
    sMax.x = std::max(sLowLeft.x,
1464
                      std::max(sLowRight.x, std::max(sUpLeft.x, sUpRight.x)));
1465
    sMax.y = std::max(sLowLeft.y,
1466
                      std::max(sLowRight.y, std::max(sUpLeft.y, sUpRight.y)));
1467
#endif
1468
0
    sMin.x = dfOriginX - dfLengthMult * strlen(pszText);
1469
0
    sMin.y = dfOriginY - dfHeightMult;
1470
0
    sMin.z = 0.0;
1471
0
    sMax.x = dfOriginX + dfLengthMult * strlen(pszText);
1472
0
    sMax.y = dfOriginY + dfHeightMult;
1473
0
    sMax.z = 0.0;
1474
1475
0
    DGNWriteBounds(psDGN, psCore, &sMin, &sMax);
1476
1477
0
    return reinterpret_cast<DGNElemCore *>(psText);
1478
0
}
1479
1480
/************************************************************************/
1481
/*                      DGNCreateColorTableElem()                       */
1482
/************************************************************************/
1483
1484
/**
1485
 * Create color table element.
1486
 *
1487
 * Creates a color table element with the indicated color table.
1488
 *
1489
 * Note that color table elements are actually of type DGNT_GROUP_DATA(5)
1490
 * and always on level 1.  Do not alter the level with DGNUpdateElemCore()
1491
 * or the element will essentially be corrupt.
1492
 *
1493
 * The newly created element will still need to be written to file using
1494
 * DGNWriteElement(). Also the level and other core values will be defaulted.
1495
 * Use DGNUpdateElemCore() on the element before writing to set these values.
1496
 *
1497
 * @param hDGN the file to which the element will eventually be written.
1498
 * @param nScreenFlag the screen to which the color table applies
1499
 * (0 = left, 1 = right).
1500
 * @param abyColorInfo array of 256 color entries. The first is
1501
 * the background color.
1502
 *
1503
 * @return the new element (DGNElemColorTable) or NULL on failure.
1504
 */
1505
1506
DGNElemCore *DGNCreateColorTableElem(DGNHandle hDGN, int nScreenFlag,
1507
                                     GByte abyColorInfo[256][3])
1508
1509
0
{
1510
    /* -------------------------------------------------------------------- */
1511
    /*      Allocate element.                                               */
1512
    /* -------------------------------------------------------------------- */
1513
0
    DGNElemColorTable *psCT =
1514
0
        (DGNElemColorTable *)CPLCalloc(sizeof(DGNElemColorTable), 1);
1515
0
    DGNElemCore *psCore = &(psCT->core);
1516
1517
0
    DGNInitializeElemCore(hDGN, psCore);
1518
0
    psCore->stype = DGNST_COLORTABLE;
1519
0
    psCore->type = DGNT_GROUP_DATA;
1520
0
    psCore->level = DGN_GDL_COLOR_TABLE;
1521
1522
    /* -------------------------------------------------------------------- */
1523
    /*      Set colortable specific information in the structure.           */
1524
    /* -------------------------------------------------------------------- */
1525
0
    psCT->screen_flag = nScreenFlag;
1526
0
    memcpy(psCT->color_info, abyColorInfo, 768);
1527
1528
    /* -------------------------------------------------------------------- */
1529
    /*      Setup Raw data for the color table specific portion.            */
1530
    /* -------------------------------------------------------------------- */
1531
0
    psCore->raw_bytes = 41 + (256 - 1) * 3;
1532
0
    psCore->raw_data = (unsigned char *)CPLCalloc(psCore->raw_bytes, 1);
1533
1534
0
    psCore->raw_data[36] = (unsigned char)(nScreenFlag % 256);
1535
0
    psCore->raw_data[37] = (unsigned char)(nScreenFlag / 256);
1536
1537
0
    memcpy(psCore->raw_data + 38, abyColorInfo[255], 3);
1538
0
    memcpy(psCore->raw_data + 41, abyColorInfo, (256 - 1) * 3);
1539
1540
    /* -------------------------------------------------------------------- */
1541
    /*      Set the core raw data.                                          */
1542
    /* -------------------------------------------------------------------- */
1543
0
    DGNUpdateElemCoreExtended(hDGN, psCore);
1544
1545
0
    return reinterpret_cast<DGNElemCore *>(psCT);
1546
0
}
1547
1548
/************************************************************************/
1549
/*                     DGNCreateComplexHeaderElem()                     */
1550
/************************************************************************/
1551
1552
/**
1553
 * Create complex chain/shape header.
1554
 *
1555
 * The newly created element will still need to be written to file using
1556
 * DGNWriteElement(). Also the level and other core values will be defaulted.
1557
 * Use DGNUpdateElemCore() on the element before writing to set these values.
1558
 *
1559
 * The nTotLength is the sum of the size of all elements in the complex
1560
 * group plus 5.  The DGNCreateComplexHeaderFromGroup() can be used to build
1561
 * a complex element from the members more conveniently.
1562
 *
1563
 * @param hDGN the file on which the element will be written.
1564
 * @param nType DGNT_COMPLEX_CHAIN_HEADER or DGNT_COMPLEX_SHAPE_HEADER.
1565
 * depending on whether the list is open or closed (last point equal to last)
1566
 * or if the object represents a surface or a solid.
1567
 * @param nTotLength the value of the totlength field in the element.
1568
 * @param nNumElems the number of elements in the complex group not including
1569
 * the header element.
1570
 *
1571
 * @return the new element (DGNElemComplexHeader) or NULL on failure.
1572
 */
1573
DGNElemCore *DGNCreateComplexHeaderElem(DGNHandle hDGN, int nType,
1574
                                        int nTotLength, int nNumElems)
1575
0
{
1576
0
    unsigned char abyRawZeroLinkage[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1577
1578
0
    CPLAssert(nType == DGNT_COMPLEX_CHAIN_HEADER ||
1579
0
              nType == DGNT_COMPLEX_SHAPE_HEADER);
1580
1581
0
    DGNLoadTCB(hDGN);
1582
1583
    /* -------------------------------------------------------------------- */
1584
    /*      Allocate element.                                               */
1585
    /* -------------------------------------------------------------------- */
1586
0
    DGNElemComplexHeader *psCH =
1587
0
        (DGNElemComplexHeader *)CPLCalloc(sizeof(DGNElemComplexHeader), 1);
1588
0
    DGNElemCore *psCore = &(psCH->core);
1589
1590
0
    DGNInitializeElemCore(hDGN, psCore);
1591
0
    psCore->complex = TRUE;
1592
0
    psCore->stype = DGNST_COMPLEX_HEADER;
1593
0
    psCore->type = nType;
1594
1595
    /* -------------------------------------------------------------------- */
1596
    /*      Set complex header specific information in the structure.       */
1597
    /* -------------------------------------------------------------------- */
1598
0
    psCH->totlength = nTotLength - 4;
1599
0
    psCH->numelems = nNumElems;
1600
0
    psCH->surftype = 0;
1601
0
    psCH->boundelms = 0;
1602
1603
    /* -------------------------------------------------------------------- */
1604
    /*      Setup Raw data for the complex specific portion.                */
1605
    /* -------------------------------------------------------------------- */
1606
0
    psCore->raw_bytes = 40;
1607
0
    psCore->raw_data = (unsigned char *)CPLCalloc(psCore->raw_bytes, 1);
1608
1609
0
    psCore->raw_data[36] = (unsigned char)((nTotLength - 4) % 256);
1610
0
    psCore->raw_data[37] = (unsigned char)((nTotLength - 4) / 256);
1611
0
    psCore->raw_data[38] = (unsigned char)(nNumElems % 256);
1612
0
    psCore->raw_data[39] = (unsigned char)(nNumElems / 256);
1613
1614
    /* -------------------------------------------------------------------- */
1615
    /*      Set the core raw data.                                          */
1616
    /* -------------------------------------------------------------------- */
1617
0
    DGNUpdateElemCoreExtended(hDGN, psCore);
1618
1619
    /* -------------------------------------------------------------------- */
1620
    /*      Elements have to be at least 48 bytes long, so we have to       */
1621
    /*      add a dummy bit of attribute data to fill out the length.       */
1622
    /* -------------------------------------------------------------------- */
1623
0
    DGNAddRawAttrLink(hDGN, psCore, 8, abyRawZeroLinkage);
1624
1625
0
    return reinterpret_cast<DGNElemCore *>(psCH);
1626
0
}
1627
1628
/************************************************************************/
1629
/*                  DGNCreateComplexHeaderFromGroup()                   */
1630
/************************************************************************/
1631
1632
/**
1633
 * Create complex chain/shape header.
1634
 *
1635
 * This function is similar to DGNCreateComplexHeaderElem(), but it takes
1636
 * care of computing the total size of the set of elements being written,
1637
 * and collecting the bounding extents.  It also takes care of some other
1638
 * convenience issues, like marking all the member elements as complex, and
1639
 * setting the level based on the level of the member elements.
1640
 *
1641
 * @param hDGN the file on which the element will be written.
1642
 * @param nType DGNT_COMPLEX_CHAIN_HEADER or DGNT_COMPLEX_SHAPE_HEADER.
1643
 * depending on whether the list is open or closed (last point equal to last)
1644
 * or if the object represents a surface or a solid.
1645
 * @param nNumElems the number of elements in the complex group not including
1646
 * the header element.
1647
 * @param papsElems array of pointers to nNumElems elements in the complex
1648
 * group.  Some updates may be made to these elements.
1649
 *
1650
 * @return the new element (DGNElemComplexHeader) or NULL on failure.
1651
 */
1652
1653
DGNElemCore *DGNCreateComplexHeaderFromGroup(DGNHandle hDGN, int nType,
1654
                                             int nNumElems,
1655
                                             DGNElemCore **papsElems)
1656
1657
0
{
1658
0
    DGNLoadTCB(hDGN);
1659
1660
0
    if (nNumElems < 1 || papsElems == nullptr)
1661
0
    {
1662
0
        CPLError(CE_Failure, CPLE_AppDefined,
1663
0
                 "Need at least one element to form a complex group.");
1664
0
        return nullptr;
1665
0
    }
1666
1667
    /* -------------------------------------------------------------------- */
1668
    /*      Collect the total size, and bounds.                             */
1669
    /* -------------------------------------------------------------------- */
1670
0
    int nTotalLength = 5;
1671
0
    const int nLevel = papsElems[0]->level;
1672
0
    DGNPoint sMin = {0.0, 0.0, 0.0};
1673
0
    DGNPoint sMax = {0.0, 0.0, 0.0};
1674
1675
0
    for (int i = 0; i < nNumElems; i++)
1676
0
    {
1677
0
        nTotalLength += papsElems[i]->raw_bytes / 2;
1678
1679
0
        papsElems[i]->complex = TRUE;
1680
0
        papsElems[i]->raw_data[0] |= 0x80;
1681
1682
0
        if (papsElems[i]->level != nLevel)
1683
0
        {
1684
0
            CPLError(CE_Warning, CPLE_AppDefined,
1685
0
                     "Not all level values matching in a complex set group!");
1686
0
        }
1687
1688
0
        DGNPoint sThisMin = {0.0, 0.0, 0.0};
1689
0
        DGNPoint sThisMax = {0.0, 0.0, 0.0};
1690
1691
0
        DGNGetElementExtents(hDGN, papsElems[i], &sThisMin, &sThisMax);
1692
0
        if (i == 0)
1693
0
        {
1694
0
            sMin = sThisMin;
1695
0
            sMax = sThisMax;
1696
0
        }
1697
0
        else
1698
0
        {
1699
0
            sMin.x = std::min(sMin.x, sThisMin.x);
1700
0
            sMin.y = std::min(sMin.y, sThisMin.y);
1701
0
            sMin.z = std::min(sMin.z, sThisMin.z);
1702
0
            sMax.x = std::max(sMax.x, sThisMax.x);
1703
0
            sMax.y = std::max(sMax.y, sThisMax.y);
1704
0
            sMax.z = std::max(sMax.z, sThisMax.z);
1705
0
        }
1706
0
    }
1707
1708
    /* -------------------------------------------------------------------- */
1709
    /*      Create the corresponding complex header.                        */
1710
    /* -------------------------------------------------------------------- */
1711
0
    DGNElemCore *psCH =
1712
0
        DGNCreateComplexHeaderElem(hDGN, nType, nTotalLength, nNumElems);
1713
0
    DGNUpdateElemCore(hDGN, psCH, papsElems[0]->level, psCH->graphic_group,
1714
0
                      psCH->color, psCH->weight, psCH->style);
1715
1716
0
    DGNWriteBounds((DGNInfo *)hDGN, psCH, &sMin, &sMax);
1717
1718
0
    return psCH;
1719
0
}
1720
1721
/************************************************************************/
1722
/*                     DGNCreateSolidHeaderElem()                       */
1723
/************************************************************************/
1724
1725
/**
1726
 * Create 3D solid/surface.
1727
 *
1728
 * The newly created element will still need to be written to file using
1729
 * DGNWriteElement(). Also the level and other core values will be defaulted.
1730
 * Use DGNUpdateElemCore() on the element before writing to set these values.
1731
 *
1732
 * The nTotLength is the sum of the size of all elements in the solid
1733
 * group plus 6.  The DGNCreateSolidHeaderFromGroup() can be used to build
1734
 * a solid element from the members more conveniently.
1735
 *
1736
 * @param hDGN the file on which the element will be written.
1737
 * @param nType DGNT_3DSURFACE_HEADER or DGNT_3DSOLID_HEADER.
1738
 * @param nSurfType the surface/solid type, one of DGNSUT_* or DGNSOT_*.
1739
 * @param nBoundElems the number of elements in each boundary.
1740
 * @param nTotLength the value of the totlength field in the element.
1741
 * @param nNumElems the number of elements in the solid not including
1742
 * the header element.
1743
 *
1744
 * @return the new element (DGNElemComplexHeader) or NULL on failure.
1745
 */
1746
DGNElemCore *DGNCreateSolidHeaderElem(DGNHandle hDGN, int nType, int nSurfType,
1747
                                      int nBoundElems, int nTotLength,
1748
                                      int nNumElems)
1749
0
{
1750
0
    CPLAssert(nType == DGNT_3DSURFACE_HEADER || nType == DGNT_3DSOLID_HEADER);
1751
1752
0
    DGNLoadTCB(hDGN);
1753
1754
    /* -------------------------------------------------------------------- */
1755
    /*      Allocate element.                                               */
1756
    /* -------------------------------------------------------------------- */
1757
0
    DGNElemComplexHeader *psCH =
1758
0
        (DGNElemComplexHeader *)CPLCalloc(sizeof(DGNElemComplexHeader), 1);
1759
0
    DGNElemCore *psCore = &(psCH->core);
1760
1761
0
    DGNInitializeElemCore(hDGN, psCore);
1762
0
    psCore->complex = TRUE;
1763
0
    psCore->stype = DGNST_COMPLEX_HEADER;
1764
0
    psCore->type = nType;
1765
1766
    /* -------------------------------------------------------------------- */
1767
    /*      Set solid header specific information in the structure.         */
1768
    /* -------------------------------------------------------------------- */
1769
0
    psCH->totlength = nTotLength - 4;
1770
0
    psCH->numelems = nNumElems;
1771
0
    psCH->surftype = nSurfType;
1772
0
    psCH->boundelms = nBoundElems;
1773
1774
    /* -------------------------------------------------------------------- */
1775
    /*      Setup Raw data for the solid specific portion.                  */
1776
    /* -------------------------------------------------------------------- */
1777
0
    psCore->raw_bytes = 42;
1778
1779
0
    psCore->raw_data = (unsigned char *)CPLCalloc(psCore->raw_bytes, 1);
1780
1781
0
    psCore->raw_data[36] = (unsigned char)((nTotLength - 4) % 256);
1782
0
    psCore->raw_data[37] = (unsigned char)((nTotLength - 4) / 256);
1783
0
    psCore->raw_data[38] = (unsigned char)(nNumElems % 256);
1784
0
    psCore->raw_data[39] = (unsigned char)(nNumElems / 256);
1785
0
    psCore->raw_data[40] = (unsigned char)psCH->surftype;
1786
0
    psCore->raw_data[41] = (unsigned char)psCH->boundelms - 1;
1787
1788
    /* -------------------------------------------------------------------- */
1789
    /*      Set the core raw data.                                          */
1790
    /* -------------------------------------------------------------------- */
1791
0
    DGNUpdateElemCoreExtended(hDGN, psCore);
1792
1793
    /* -------------------------------------------------------------------- */
1794
    /*      Elements have to be at least 48 bytes long, so we have to       */
1795
    /*      add a dummy bit of attribute data to fill out the length.       */
1796
    /* -------------------------------------------------------------------- */
1797
0
    unsigned char abyRawZeroLinkage[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1798
0
    DGNAddRawAttrLink(hDGN, psCore, 8, abyRawZeroLinkage);
1799
1800
0
    return reinterpret_cast<DGNElemCore *>(psCH);
1801
0
}
1802
1803
/************************************************************************/
1804
/*                  DGNCreateSolidHeaderFromGroup()                     */
1805
/************************************************************************/
1806
1807
/**
1808
 * Create 3D solid/surface header.
1809
 *
1810
 * This function is similar to DGNCreateSolidHeaderElem(), but it takes
1811
 * care of computing the total size of the set of elements being written,
1812
 * and collecting the bounding extents.  It also takes care of some other
1813
 * convenience issues, like marking all the member elements as complex, and
1814
 * setting the level based on the level of the member elements.
1815
 *
1816
 * @param hDGN the file on which the element will be written.
1817
 * @param nType DGNT_3DSURFACE_HEADER or DGNT_3DSOLID_HEADER.
1818
 * @param nSurfType the surface/solid type, one of DGNSUT_* or DGNSOT_*.
1819
 * @param nBoundElems the number of boundary elements.
1820
 * @param nNumElems the number of elements in the solid not including
1821
 * the header element.
1822
 * @param papsElems array of pointers to nNumElems elements in the solid.
1823
 * Some updates may be made to these elements.
1824
 *
1825
 * @return the new element (DGNElemComplexHeader) or NULL on failure.
1826
 */
1827
1828
DGNElemCore *DGNCreateSolidHeaderFromGroup(DGNHandle hDGN, int nType,
1829
                                           int nSurfType, int nBoundElems,
1830
                                           int nNumElems,
1831
                                           DGNElemCore **papsElems)
1832
1833
0
{
1834
0
    DGNLoadTCB(hDGN);
1835
1836
0
    if (nNumElems < 1 || papsElems == nullptr)
1837
0
    {
1838
0
        CPLError(CE_Failure, CPLE_AppDefined,
1839
0
                 "Need at least one element to form a solid.");
1840
0
        return nullptr;
1841
0
    }
1842
1843
    /* -------------------------------------------------------------------- */
1844
    /*      Collect the total size, and bounds.                             */
1845
    /* -------------------------------------------------------------------- */
1846
0
    const int nLevel = papsElems[0]->level;
1847
0
    int nTotalLength = 6;
1848
0
    DGNPoint sMin = {0.0, 0.0, 0.0};
1849
0
    DGNPoint sMax = {0.0, 0.0, 0.0};
1850
1851
0
    for (int i = 0; i < nNumElems; i++)
1852
0
    {
1853
0
        nTotalLength += papsElems[i]->raw_bytes / 2;
1854
1855
0
        papsElems[i]->complex = TRUE;
1856
0
        papsElems[i]->raw_data[0] |= 0x80;
1857
1858
0
        if (papsElems[i]->level != nLevel)
1859
0
        {
1860
0
            CPLError(CE_Warning, CPLE_AppDefined,
1861
0
                     "Not all level values matching in a complex set group!");
1862
0
        }
1863
1864
0
        DGNPoint sThisMin = {0.0, 0.0, 0.0};
1865
0
        DGNPoint sThisMax = {0.0, 0.0, 0.0};
1866
0
        DGNGetElementExtents(hDGN, papsElems[i], &sThisMin, &sThisMax);
1867
0
        if (i == 0)
1868
0
        {
1869
0
            sMin = sThisMin;
1870
0
            sMax = sThisMax;
1871
0
        }
1872
0
        else
1873
0
        {
1874
0
            sMin.x = std::min(sMin.x, sThisMin.x);
1875
0
            sMin.y = std::min(sMin.y, sThisMin.y);
1876
0
            sMin.z = std::min(sMin.z, sThisMin.z);
1877
0
            sMax.x = std::max(sMax.x, sThisMax.x);
1878
0
            sMax.y = std::max(sMax.y, sThisMax.y);
1879
0
            sMax.z = std::max(sMax.z, sThisMax.z);
1880
0
        }
1881
0
    }
1882
1883
    /* -------------------------------------------------------------------- */
1884
    /*      Create the corresponding solid header.                          */
1885
    /* -------------------------------------------------------------------- */
1886
0
    DGNElemCore *psCH = DGNCreateSolidHeaderElem(
1887
0
        hDGN, nType, nSurfType, nBoundElems, nTotalLength, nNumElems);
1888
0
    DGNUpdateElemCore(hDGN, psCH, papsElems[0]->level, psCH->graphic_group,
1889
0
                      psCH->color, psCH->weight, psCH->style);
1890
1891
0
    DGNWriteBounds((DGNInfo *)hDGN, psCH, &sMin, &sMax);
1892
1893
0
    return psCH;
1894
0
}
1895
1896
/************************************************************************/
1897
/*                      DGNCreateCellHeaderElem()                       */
1898
/************************************************************************/
1899
1900
DGNElemCore CPL_DLL *
1901
DGNCreateCellHeaderElem(DGNHandle hDGN, int nTotLength, const char *pszName,
1902
                        short nClass, short *panLevels, DGNPoint *psRangeLow,
1903
                        DGNPoint *psRangeHigh, DGNPoint *psOrigin,
1904
                        double dfXScale, double dfYScale, double dfRotation)
1905
1906
/**
1907
 * Create cell header.
1908
 *
1909
 * The newly created element will still need to be written to file using
1910
 * DGNWriteElement(). Also the level and other core values will be defaulted.
1911
 * Use DGNUpdateElemCore() on the element before writing to set these values.
1912
 *
1913
 * Generally speaking the function DGNCreateCellHeaderFromGroup() should
1914
 * be used instead of this function.
1915
 *
1916
 * @param hDGN the file handle on which the element is to be written.
1917
 * @param nTotLength total length of cell in words not including the 38 bytes
1918
 * of the cell header that occur before the totlength indicator.
1919
 * @param nClass the class value for the cell.
1920
 * @param panLevels an array of shorts holding the bit mask of levels in
1921
 * effect for this cell.  This array should contain 4 shorts (64 bits).
1922
 * @param psRangeLow the cell diagonal origin in original cell file
1923
 * coordinates.
1924
 * @param psRangeHigh the cell diagonal top left corner in original cell file
1925
 * coordinates.
1926
 * @param psOrigin the origin of the cell in output file coordinates.
1927
 * @param dfXScale the amount of scaling applied in the X dimension in
1928
 * mapping from cell file coordinates to output file coordinates.
1929
 * @param dfYScale the amount of scaling applied in the Y dimension in
1930
 * mapping from cell file coordinates to output file coordinates.
1931
 * @param dfRotation the amount of rotation (degrees counterclockwise) in
1932
 * mapping from cell coordinates to output file coordinates.
1933
 *
1934
 * @return the new element (DGNElemCellHeader) or NULL on failure.
1935
 */
1936
1937
0
{
1938
0
    DGNInfo *psInfo = (DGNInfo *)hDGN;
1939
1940
0
    DGNLoadTCB(hDGN);
1941
1942
    /* -------------------------------------------------------------------- */
1943
    /*      Allocate element.                                               */
1944
    /* -------------------------------------------------------------------- */
1945
0
    DGNElemCellHeader *psCH =
1946
0
        (DGNElemCellHeader *)CPLCalloc(sizeof(DGNElemCellHeader), 1);
1947
0
    DGNElemCore *psCore = &(psCH->core);
1948
1949
0
    DGNInitializeElemCore(hDGN, psCore);
1950
0
    psCore->stype = DGNST_CELL_HEADER;
1951
0
    psCore->type = DGNT_CELL_HEADER;
1952
1953
    /* -------------------------------------------------------------------- */
1954
    /*      Set complex header specific information in the structure.       */
1955
    /* -------------------------------------------------------------------- */
1956
0
    psCH->totlength = nTotLength;
1957
1958
    /* -------------------------------------------------------------------- */
1959
    /*      Setup Raw data for the cell header specific portion.            */
1960
    /* -------------------------------------------------------------------- */
1961
0
    if (psInfo->dimension == 2)
1962
0
        psCore->raw_bytes = 92;
1963
0
    else
1964
0
        psCore->raw_bytes = 124;
1965
0
    psCore->raw_data = (unsigned char *)CPLCalloc(psCore->raw_bytes, 1);
1966
1967
0
    psCore->raw_data[36] = (unsigned char)(nTotLength % 256);
1968
0
    psCore->raw_data[37] = (unsigned char)(nTotLength / 256);
1969
1970
0
    DGNAsciiToRad50(pszName,
1971
0
                    reinterpret_cast<unsigned short *>(psCore->raw_data + 38));
1972
0
    if (strlen(pszName) > 3)
1973
0
        DGNAsciiToRad50(pszName + 3, reinterpret_cast<unsigned short *>(
1974
0
                                         psCore->raw_data + 40));
1975
1976
0
    psCore->raw_data[42] = (unsigned char)(nClass % 256);
1977
0
    psCore->raw_data[43] = (unsigned char)(nClass / 256);
1978
1979
0
    memcpy(psCore->raw_data + 44, panLevels, 8);
1980
1981
0
    if (psInfo->dimension == 2)
1982
0
    {
1983
0
        DGNPointToInt(psInfo, psRangeLow, psCore->raw_data + 52);
1984
0
        DGNPointToInt(psInfo, psRangeHigh, psCore->raw_data + 60);
1985
1986
0
        DGNInverseTransformPointToInt(psInfo, psOrigin, psCore->raw_data + 84);
1987
0
    }
1988
0
    else
1989
0
    {
1990
0
        DGNPointToInt(psInfo, psRangeLow, psCore->raw_data + 52);
1991
0
        DGNPointToInt(psInfo, psRangeHigh, psCore->raw_data + 64);
1992
1993
0
        DGNInverseTransformPointToInt(psInfo, psOrigin, psCore->raw_data + 112);
1994
0
    }
1995
1996
    /* -------------------------------------------------------------------- */
1997
    /*      Produce a transformation matrix that approximates the           */
1998
    /*      requested scaling and rotation.                                 */
1999
    /* -------------------------------------------------------------------- */
2000
0
    if (psInfo->dimension == 2)
2001
0
    {
2002
0
        long anTrans[4];
2003
0
        double cos_a = cos(-dfRotation * M_PI / 180.0);
2004
0
        double sin_a = sin(-dfRotation * M_PI / 180.0);
2005
2006
0
        anTrans[0] = (long)(cos_a * dfXScale * 214748);
2007
0
        anTrans[1] = (long)(sin_a * dfYScale * 214748);
2008
0
        anTrans[2] = (long)(-sin_a * dfXScale * 214748);
2009
0
        anTrans[3] = (long)(cos_a * dfYScale * 214748);
2010
2011
0
        DGN_WRITE_INT32(anTrans[0], psCore->raw_data + 68);
2012
0
        DGN_WRITE_INT32(anTrans[1], psCore->raw_data + 72);
2013
0
        DGN_WRITE_INT32(anTrans[2], psCore->raw_data + 76);
2014
0
        DGN_WRITE_INT32(anTrans[3], psCore->raw_data + 80);
2015
0
    }
2016
0
    else
2017
0
    {
2018
0
        long anTrans[9];
2019
2020
        // NOTE: This is still just rotation in the plane
2021
0
        double cos_a = cos(-dfRotation * M_PI / 180.0);
2022
0
        double sin_a = sin(-dfRotation * M_PI / 180.0);
2023
0
        double dfZScale = 1.0;  // Should we get this from somewhere?
2024
2025
0
        anTrans[0] = (long)(cos_a * dfXScale * 214748);
2026
0
        anTrans[1] = (long)(sin_a * dfYScale * 214748);
2027
0
        anTrans[2] = (long)(sin_a * dfZScale * 214748);
2028
2029
0
        anTrans[3] = (long)(-sin_a * dfXScale * 214748);
2030
0
        anTrans[4] = (long)(cos_a * dfYScale * 214748);
2031
0
        anTrans[5] = (long)(sin_a * dfZScale * 214748);
2032
2033
0
        anTrans[6] = (long)(-sin_a * dfXScale * 214748);
2034
0
        anTrans[7] = (long)(-sin_a * dfYScale * 214748);
2035
0
        anTrans[8] = (long)(cos_a * dfZScale * 214748);
2036
2037
0
        DGN_WRITE_INT32(anTrans[0], psCore->raw_data + 76);
2038
0
        DGN_WRITE_INT32(anTrans[1], psCore->raw_data + 80);
2039
0
        DGN_WRITE_INT32(anTrans[2], psCore->raw_data + 84);
2040
0
        DGN_WRITE_INT32(anTrans[3], psCore->raw_data + 88);
2041
0
        DGN_WRITE_INT32(anTrans[4], psCore->raw_data + 92);
2042
0
        DGN_WRITE_INT32(anTrans[5], psCore->raw_data + 96);
2043
0
        DGN_WRITE_INT32(anTrans[6], psCore->raw_data + 100);
2044
0
        DGN_WRITE_INT32(anTrans[7], psCore->raw_data + 104);
2045
0
        DGN_WRITE_INT32(anTrans[8], psCore->raw_data + 108);
2046
0
    }
2047
2048
    /* -------------------------------------------------------------------- */
2049
    /*      Set the core raw data.                                          */
2050
    /* -------------------------------------------------------------------- */
2051
0
    DGNUpdateElemCoreExtended(hDGN, psCore);
2052
2053
0
    return reinterpret_cast<DGNElemCore *>(psCH);
2054
0
}
2055
2056
/************************************************************************/
2057
/*                           DGNPointToInt()                            */
2058
/*                                                                      */
2059
/*      Convert a point directly to integer coordinates and write to    */
2060
/*      the indicate memory location.  Intended to be used for the      */
2061
/*      range section of the CELL HEADER.                               */
2062
/************************************************************************/
2063
2064
static void DGNPointToInt(DGNInfo *psDGN, DGNPoint *psPoint,
2065
                          unsigned char *pabyTarget)
2066
2067
0
{
2068
0
    double adfCT[3] = {psPoint->x, psPoint->y, psPoint->z};
2069
2070
0
    const int nIter = std::min(3, psDGN->dimension);
2071
0
    for (int i = 0; i < nIter; i++)
2072
0
    {
2073
0
        GInt32 nCTI = static_cast<GInt32>(
2074
0
            std::max(-2147483647.0, std::min(2147483647.0, adfCT[i])));
2075
0
        unsigned char abyCTI[4];
2076
0
        memcpy(abyCTI, &nCTI, sizeof(GInt32));
2077
2078
#ifdef WORDS_BIGENDIAN
2079
        pabyTarget[i * 4 + 0] = abyCTI[1];
2080
        pabyTarget[i * 4 + 1] = abyCTI[0];
2081
        pabyTarget[i * 4 + 2] = abyCTI[3];
2082
        pabyTarget[i * 4 + 3] = abyCTI[2];
2083
#else
2084
0
        pabyTarget[i * 4 + 3] = abyCTI[1];
2085
0
        pabyTarget[i * 4 + 2] = abyCTI[0];
2086
0
        pabyTarget[i * 4 + 1] = abyCTI[3];
2087
0
        pabyTarget[i * 4 + 0] = abyCTI[2];
2088
0
#endif
2089
0
    }
2090
0
}
2091
2092
/************************************************************************/
2093
/*                    DGNCreateCellHeaderFromGroup()                    */
2094
/************************************************************************/
2095
2096
/**
2097
 * Create cell header from a group of elements.
2098
 *
2099
 * The newly created element will still need to be written to file using
2100
 * DGNWriteElement(). Also the level and other core values will be defaulted.
2101
 * Use DGNUpdateElemCore() on the element before writing to set these values.
2102
 *
2103
 * This function will compute the total length, bounding box, and diagonal
2104
 * range values from the set of provided elements.  Note that the proper
2105
 * diagonal range values will only be written if 1.0 is used for the x and y
2106
 * scale values, and 0.0 for the rotation.  Use of other values will result
2107
 * in incorrect scaling handles being presented to the user in Microstation
2108
 * when they select the element.
2109
 *
2110
 * @param hDGN the file handle on which the element is to be written.
2111
 * @param nClass the class value for the cell.
2112
 * @param panLevels an array of shorts holding the bit mask of levels in
2113
 * effect for this cell.  This array should contain 4 shorts (64 bits).
2114
 * This array would normally be passed in as NULL, and the function will
2115
 * build a mask from the passed list of elements.
2116
 * @param psOrigin the origin of the cell in output file coordinates.
2117
 * @param dfXScale the amount of scaling applied in the X dimension in
2118
 * mapping from cell file coordinates to output file coordinates.
2119
 * @param dfYScale the amount of scaling applied in the Y dimension in
2120
 * mapping from cell file coordinates to output file coordinates.
2121
 * @param dfRotation the amount of rotation (degrees counterclockwise) in
2122
 * mapping from cell coordinates to output file coordinates.
2123
 *
2124
 * @return the new element (DGNElemCellHeader) or NULL on failure.
2125
 */
2126
2127
DGNElemCore *DGNCreateCellHeaderFromGroup(DGNHandle hDGN, const char *pszName,
2128
                                          short nClass, short *panLevels,
2129
                                          int nNumElems,
2130
                                          DGNElemCore **papsElems,
2131
                                          DGNPoint *psOrigin, double dfXScale,
2132
                                          double dfYScale, double dfRotation)
2133
2134
0
{
2135
0
    DGNInfo *psInfo = (DGNInfo *)hDGN;
2136
2137
0
    DGNLoadTCB(hDGN);
2138
2139
0
    if (nNumElems < 1 || papsElems == nullptr)
2140
0
    {
2141
0
        CPLError(CE_Failure, CPLE_AppDefined,
2142
0
                 "Need at least one element to form a cell.");
2143
0
        return nullptr;
2144
0
    }
2145
2146
    /* -------------------------------------------------------------------- */
2147
    /*      Collect the total size, and bounds.                             */
2148
    /* -------------------------------------------------------------------- */
2149
0
    int nTotalLength = psInfo->dimension == 2 ? 27 : 43;
2150
    // nLevel = papsElems[0]->level;x
2151
0
    DGNPoint sMin = {0.0, 0.0, 0.0};
2152
0
    DGNPoint sMax = {0.0, 0.0, 0.0};
2153
0
    unsigned char abyLevelsOccurring[8] = {0, 0, 0, 0, 0, 0, 0, 0};
2154
2155
0
    for (int i = 0; i < nNumElems; i++)
2156
0
    {
2157
0
        nTotalLength += papsElems[i]->raw_bytes / 2;
2158
2159
        /* mark as complex */
2160
0
        papsElems[i]->complex = TRUE;
2161
0
        papsElems[i]->raw_data[0] |= 0x80;
2162
2163
        /* establish level */
2164
0
        int nLevel = papsElems[i]->level;
2165
0
        nLevel = std::max(1, std::min(nLevel, 64));
2166
0
        abyLevelsOccurring[(nLevel - 1) >> 3] |= (0x1 << ((nLevel - 1) & 0x7));
2167
2168
0
        DGNPoint sThisMin = {0.0, 0.0, 0.0};
2169
0
        DGNPoint sThisMax = {0.0, 0.0, 0.0};
2170
0
        DGNGetElementExtents(hDGN, papsElems[i], &sThisMin, &sThisMax);
2171
0
        if (i == 0)
2172
0
        {
2173
0
            sMin = sThisMin;
2174
0
            sMax = sThisMax;
2175
0
        }
2176
0
        else
2177
0
        {
2178
0
            sMin.x = std::min(sMin.x, sThisMin.x);
2179
0
            sMin.y = std::min(sMin.y, sThisMin.y);
2180
0
            sMin.z = std::min(sMin.z, sThisMin.z);
2181
0
            sMax.x = std::max(sMax.x, sThisMax.x);
2182
0
            sMax.y = std::max(sMax.y, sThisMax.y);
2183
0
            sMax.z = std::max(sMax.z, sThisMax.z);
2184
0
        }
2185
0
    }
2186
2187
/* -------------------------------------------------------------------- */
2188
/*      It seems that the range needs to be adjusted according to       */
2189
/*      the rotation and scaling.                                       */
2190
/*                                                                      */
2191
/*      NOTE: Omitting code ... this is already done in                 */
2192
/*      DGNInverseTransformPoint() called from DGNWriteBounds().        */
2193
/* -------------------------------------------------------------------- */
2194
#ifdef notdef
2195
    sMin.x -= psOrigin->x;
2196
    sMin.y -= psOrigin->y;
2197
    sMin.z -= psOrigin->z;
2198
    sMax.x -= psOrigin->x;
2199
    sMax.y -= psOrigin->y;
2200
    sMax.z -= psOrigin->z;
2201
2202
    sMin.x /= ((DGNInfo *)hDGN)->scale;
2203
    sMin.y /= ((DGNInfo *)hDGN)->scale;
2204
    sMin.z /= ((DGNInfo *)hDGN)->scale;
2205
    sMax.x /= ((DGNInfo *)hDGN)->scale;
2206
    sMax.y /= ((DGNInfo *)hDGN)->scale;
2207
    sMax.z /= ((DGNInfo *)hDGN)->scale;
2208
#endif
2209
2210
    /* -------------------------------------------------------------------- */
2211
    /*      Create the corresponding cell header.                           */
2212
    /* -------------------------------------------------------------------- */
2213
0
    if (panLevels == nullptr)
2214
0
        panLevels = reinterpret_cast<short *>(abyLevelsOccurring);
2215
2216
0
    DGNElemCore *psCH = DGNCreateCellHeaderElem(
2217
0
        hDGN, nTotalLength, pszName, nClass, panLevels, &sMin, &sMax, psOrigin,
2218
0
        dfXScale, dfYScale, dfRotation);
2219
0
    DGNWriteBounds((DGNInfo *)hDGN, psCH, &sMin, &sMax);
2220
2221
0
    return psCH;
2222
0
}
2223
2224
/************************************************************************/
2225
/*                            DGNAddMSLink()                            */
2226
/************************************************************************/
2227
2228
/**
2229
 * Add a database link to element.
2230
 *
2231
 * The target element must already have raw_data loaded, and it will be
2232
 * resized (see DGNResizeElement()) as needed for the new attribute data.
2233
 * Note that the element is not written to disk immediate.  Use
2234
 * DGNWriteElement() for that.
2235
 *
2236
 * @param hDGN the file to which the element corresponds.
2237
 * @param psElement the element being updated.
2238
 * @param nLinkageType link type (DGNLT_*).  Usually one of DGNLT_DMRS,
2239
 * DGNLT_INFORMIX, DGNLT_ODBC, DGNLT_ORACLE, DGNLT_RIS, DGNLT_SYBASE,
2240
 * or DGNLT_XBASE.
2241
 * @param nEntityNum indicator of the table referenced on target database.
2242
 * @param nMSLink indicator of the record referenced on target table.
2243
 *
2244
 * @return -1 on failure, or the link index.
2245
 */
2246
2247
int DGNAddMSLink(DGNHandle hDGN, DGNElemCore *psElement, int nLinkageType,
2248
                 int nEntityNum, int nMSLink)
2249
2250
0
{
2251
0
    unsigned char abyLinkage[32] = {};
2252
0
    int nLinkageSize = 0;
2253
2254
0
    if (nLinkageType == DGNLT_DMRS)
2255
0
    {
2256
0
        nLinkageSize = 8;
2257
0
        abyLinkage[0] = 0x00;
2258
0
        abyLinkage[1] = 0x00;
2259
0
        abyLinkage[2] = (GByte)(nEntityNum % 256);
2260
0
        abyLinkage[3] = (GByte)(nEntityNum / 256);
2261
0
        abyLinkage[4] = (GByte)(nMSLink % 256);
2262
0
        abyLinkage[5] = (GByte)((nMSLink / 256) % 256);
2263
0
        abyLinkage[6] = (GByte)(nMSLink / 65536);
2264
0
        abyLinkage[7] = 0x01;
2265
0
    }
2266
0
    else
2267
0
    {
2268
0
        nLinkageSize = 16;
2269
0
        abyLinkage[0] = 0x07;
2270
0
        abyLinkage[1] = 0x10;
2271
0
        abyLinkage[2] = (GByte)(nLinkageType % 256);
2272
0
        abyLinkage[3] = (GByte)(nLinkageType / 256);
2273
0
        abyLinkage[4] = (GByte)(0x81);
2274
0
        abyLinkage[5] = (GByte)(0x0F);
2275
0
        abyLinkage[6] = (GByte)(nEntityNum % 256);
2276
0
        abyLinkage[7] = (GByte)(nEntityNum / 256);
2277
0
        abyLinkage[8] = (GByte)(nMSLink % 256);
2278
0
        abyLinkage[9] = (GByte)((nMSLink / 256) % 256);
2279
0
        abyLinkage[10] = (GByte)((nMSLink / 65536) % 256);
2280
0
        abyLinkage[11] = (GByte)(nMSLink / 16777216);
2281
0
        abyLinkage[12] = 0x00;
2282
0
        abyLinkage[13] = 0x00;
2283
0
        abyLinkage[14] = 0x00;
2284
0
        abyLinkage[15] = 0x00;
2285
0
    }
2286
2287
0
    return DGNAddRawAttrLink(hDGN, psElement, nLinkageSize, abyLinkage);
2288
0
}
2289
2290
/************************************************************************/
2291
/*                         DGNAddRawAttrLink()                          */
2292
/************************************************************************/
2293
2294
/**
2295
 * Add a raw attribute linkage to element.
2296
 *
2297
 * Given a raw data buffer, append it to this element as an attribute linkage
2298
 * without trying to interpret the linkage data.
2299
 *
2300
 * The target element must already have raw_data loaded, and it will be
2301
 * resized (see DGNResizeElement()) as needed for the new attribute data.
2302
 * Note that the element is not written to disk immediate.  Use
2303
 * DGNWriteElement() for that.
2304
 *
2305
 * This function will take care of updating the "totlength" field of
2306
 * complex chain or shape headers to account for the extra attribute space
2307
 * consumed in the header element.
2308
 *
2309
 * @param hDGN the file to which the element corresponds.
2310
 * @param psElement the element being updated.
2311
 * @param nLinkSize the size of the linkage in bytes. Must be a multiple of 2.
2312
 * @param pabyRawLinkData the raw linkage data (nLinkSize bytes worth).
2313
 *
2314
 * @return -1 on failure, or the link index.
2315
 */
2316
2317
int DGNAddRawAttrLink(DGNHandle hDGN, DGNElemCore *psElement, int nLinkSize,
2318
                      unsigned char *pabyRawLinkData)
2319
2320
0
{
2321
0
    CPLAssert((nLinkSize % 2) == 0);
2322
2323
0
    if (psElement->size + nLinkSize > 768)
2324
0
    {
2325
0
        CPLError(CE_Failure, CPLE_ElementTooBig,
2326
0
                 "Attempt to add %d byte linkage to element exceeds maximum"
2327
0
                 " element size.",
2328
0
                 nLinkSize);
2329
0
        return -1;
2330
0
    }
2331
2332
    /* -------------------------------------------------------------------- */
2333
    /*      Ensure the attribute linkage bit is set.                        */
2334
    /* -------------------------------------------------------------------- */
2335
0
    psElement->properties |= DGNPF_ATTRIBUTES;
2336
2337
    /* -------------------------------------------------------------------- */
2338
    /*      Append the attribute linkage to the linkage area.               */
2339
    /* -------------------------------------------------------------------- */
2340
0
    psElement->attr_bytes += nLinkSize;
2341
0
    psElement->attr_data = (unsigned char *)CPLRealloc(psElement->attr_data,
2342
0
                                                       psElement->attr_bytes);
2343
2344
0
    memcpy(psElement->attr_data + (psElement->attr_bytes - nLinkSize),
2345
0
           pabyRawLinkData, nLinkSize);
2346
2347
    /* -------------------------------------------------------------------- */
2348
    /*      Grow the raw data, if we have rawdata.                          */
2349
    /* -------------------------------------------------------------------- */
2350
0
    psElement->raw_bytes += nLinkSize;
2351
0
    psElement->raw_data =
2352
0
        (unsigned char *)CPLRealloc(psElement->raw_data, psElement->raw_bytes);
2353
2354
0
    memcpy(psElement->raw_data + (psElement->raw_bytes - nLinkSize),
2355
0
           pabyRawLinkData, nLinkSize);
2356
2357
    /* -------------------------------------------------------------------- */
2358
    /*      If the element is a shape or chain complex header, then we      */
2359
    /*      need to increase the total complex group size appropriately.    */
2360
    /* -------------------------------------------------------------------- */
2361
0
    if (psElement->stype == DGNST_COMPLEX_HEADER ||
2362
0
        psElement->stype == DGNST_TEXT_NODE)  // compatible structures
2363
0
    {
2364
0
        DGNElemComplexHeader *psCT =
2365
0
            reinterpret_cast<DGNElemComplexHeader *>(psElement);
2366
2367
0
        psCT->totlength += (nLinkSize / 2);
2368
2369
0
        psElement->raw_data[36] = (unsigned char)(psCT->totlength % 256);
2370
0
        psElement->raw_data[37] = (unsigned char)(psCT->totlength / 256);
2371
0
    }
2372
2373
    /* -------------------------------------------------------------------- */
2374
    /*      Ensure everything is updated properly, including element        */
2375
    /*      length and properties.                                          */
2376
    /* -------------------------------------------------------------------- */
2377
0
    DGNUpdateElemCoreExtended(hDGN, psElement);
2378
2379
    /* -------------------------------------------------------------------- */
2380
    /*      Figure out what the linkage index is.                           */
2381
    /* -------------------------------------------------------------------- */
2382
0
    int iLinkage = 0;  // Used after for.
2383
0
    for (;; iLinkage++)
2384
0
    {
2385
0
        if (DGNGetLinkage(hDGN, psElement, iLinkage, nullptr, nullptr, nullptr,
2386
0
                          nullptr) == nullptr)
2387
0
            break;
2388
0
    }
2389
2390
0
    return iLinkage - 1;
2391
0
}
2392
2393
/************************************************************************/
2394
/*                        DGNAddShapeFileInfo()                         */
2395
/************************************************************************/
2396
2397
/**
2398
 * Add a shape fill attribute linkage.
2399
 *
2400
 * The target element must already have raw_data loaded, and it will be
2401
 * resized (see DGNResizeElement()) as needed for the new attribute data.
2402
 * Note that the element is not written to disk immediate.  Use
2403
 * DGNWriteElement() for that.
2404
 *
2405
 * @param hDGN the file to which the element corresponds.
2406
 * @param psElement the element being updated.
2407
 * @param nColor fill color (color index from palette).
2408
 *
2409
 * @return -1 on failure, or the link index.
2410
 */
2411
2412
int DGNAddShapeFillInfo(DGNHandle hDGN, DGNElemCore *psElement, int nColor)
2413
2414
0
{
2415
0
    unsigned char abyFillInfo[16] = {0x07, 0x10, 0x41, 0x00, 0x02, 0x08,
2416
0
                                     0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
2417
0
                                     0x00, 0x00, 0x00, 0x00};
2418
2419
0
    abyFillInfo[8] = (unsigned char)nColor;
2420
2421
    // coverity[overrun-buffer-arg]
2422
0
    return DGNAddRawAttrLink(hDGN, psElement, 16, abyFillInfo);
2423
0
}