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