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