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