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