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