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