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