LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/dgn - dgnwrite.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 802 291 36.3 %
Date: 2011-12-18 Functions: 24 12 50.0 %

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

Generated by: LCOV version 1.7