LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dxf - ogrdxf_feature.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 130 150 86.7 %
Date: 2025-08-01 10:10:57 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  DXF Translator
       4             :  * Purpose:  Provides additional functionality for DXF features
       5             :  * Author:   Alan Thomas, alant@outlook.com.au
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2017, Alan Thomas <alant@outlook.com.au>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogr_dxf.h"
      14             : #include "cpl_string.h"
      15             : 
      16             : /************************************************************************/
      17             : /*                            OGRDXFFeature()                           */
      18             : /************************************************************************/
      19             : 
      20        4542 : OGRDXFFeature::OGRDXFFeature(OGRFeatureDefn *poFeatureDefn)
      21             :     : OGRFeature(poFeatureDefn), oOCS(0.0, 0.0, 1.0), bIsBlockReference(false),
      22             :       dfBlockAngle(0.0), oBlockScale(1.0, 1.0, 1.0),
      23        4542 :       oOriginalCoords(0.0, 0.0, 0.0)
      24             : {
      25        4542 : }
      26             : 
      27             : OGRDXFFeature::~OGRDXFFeature() = default;
      28             : 
      29             : /************************************************************************/
      30             : /*                          CloneDXFFeature()                           */
      31             : /*                                                                      */
      32             : /*      Replacement for OGRFeature::Clone() for DXF features.           */
      33             : /************************************************************************/
      34             : 
      35        3696 : OGRDXFFeature *OGRDXFFeature::CloneDXFFeature()
      36             : {
      37        3696 :     OGRDXFFeature *poNew = new OGRDXFFeature(GetDefnRef());
      38        3696 :     if (poNew == nullptr)
      39           0 :         return nullptr;
      40             : 
      41        3696 :     if (!CopySelfTo(poNew))
      42             :     {
      43           0 :         delete poNew;
      44           0 :         return nullptr;
      45             :     }
      46             : 
      47        3696 :     poNew->oOCS = oOCS;
      48        3696 :     poNew->bIsBlockReference = bIsBlockReference;
      49        3696 :     poNew->osBlockName = osBlockName;
      50        3696 :     poNew->dfBlockAngle = dfBlockAngle;
      51        3696 :     poNew->oBlockScale = oBlockScale;
      52        3696 :     poNew->oOriginalCoords = oOriginalCoords;
      53        3696 :     poNew->osAttributeTag = osAttributeTag;
      54        3696 :     poNew->oStyleProperties = oStyleProperties;
      55             : 
      56        3696 :     if (poASMTransform)
      57             :     {
      58           2 :         poNew->poASMTransform = std::unique_ptr<OGRDXFAffineTransform>(
      59           2 :             new OGRDXFAffineTransform(*poASMTransform));
      60             :     }
      61             : 
      62           2 :     for (const std::unique_ptr<OGRDXFFeature> &poAttribFeature :
      63        3698 :          apoAttribFeatures)
      64             :     {
      65             :         poNew->apoAttribFeatures.emplace_back(
      66           2 :             poAttribFeature->CloneDXFFeature());
      67             :     }
      68             : 
      69        3696 :     return poNew;
      70             : }
      71             : 
      72             : /************************************************************************/
      73             : /*                        ApplyOCSTransformer()                         */
      74             : /*                                                                      */
      75             : /*      Applies the OCS transformation stored in this feature to        */
      76             : /*      the specified geometry.                                         */
      77             : /************************************************************************/
      78             : 
      79        3809 : void OGRDXFFeature::ApplyOCSTransformer(OGRGeometry *const poGeometry) const
      80             : {
      81        3809 :     if (poGeometry == nullptr)
      82           0 :         return;
      83             : 
      84             :     double adfN[3];
      85        3809 :     oOCS.ToArray(adfN);
      86             : 
      87        7618 :     OGRDXFOCSTransformer oTransformer(adfN);
      88             : 
      89             :     // Promote to 3D, in case the OCS transformation introduces a
      90             :     // third dimension to the geometry.
      91        3809 :     const bool bInitially2D = !poGeometry->Is3D();
      92        3809 :     if (bInitially2D)
      93         207 :         poGeometry->set3D(TRUE);
      94             : 
      95        3809 :     poGeometry->transform(&oTransformer);
      96             : 
      97             :     // If the geometry was 2D to begin with, and is still 2D after the
      98             :     // OCS transformation, flatten it back to 2D.
      99        3809 :     if (bInitially2D)
     100             :     {
     101         207 :         OGREnvelope3D oEnvelope;
     102         207 :         poGeometry->getEnvelope(&oEnvelope);
     103         207 :         if (oEnvelope.MaxZ == 0.0 && oEnvelope.MinZ == 0.0)
     104         190 :             poGeometry->flattenTo2D();
     105             :     }
     106             : }
     107             : 
     108             : /************************************************************************/
     109             : /*                        ApplyOCSTransformer()                         */
     110             : /************************************************************************/
     111             : 
     112           3 : void OGRDXFFeature::ApplyOCSTransformer(OGRDXFAffineTransform *const poCT) const
     113             : {
     114           3 :     if (!poCT)
     115           0 :         return;
     116             : 
     117             :     double adfN[3];
     118           3 :     oOCS.ToArray(adfN);
     119             : 
     120           6 :     OGRDXFOCSTransformer oTransformer(adfN);
     121             : 
     122           3 :     oTransformer.ComposeOnto(*poCT);
     123             : }
     124             : 
     125             : /************************************************************************/
     126             : /*                              GetColor()                              */
     127             : /*                                                                      */
     128             : /*      Gets the hex color string for this feature, using the given     */
     129             : /*      data source to fetch layer properties.                          */
     130             : /*                                                                      */
     131             : /*      For usage info about poBlockFeature, see                        */
     132             : /*      OGRDXFLayer::PrepareFeatureStyle.                               */
     133             : /************************************************************************/
     134             : 
     135             : const CPLString
     136        3082 : OGRDXFFeature::GetColor(OGRDXFDataSource *const poDS,
     137             :                         OGRDXFFeature *const poBlockFeature /* = NULL */)
     138             : {
     139        6164 :     CPLString osLayer = GetFieldAsString("Layer");
     140             : 
     141             :     /* -------------------------------------------------------------------- */
     142             :     /*      Is the layer or object hidden/off (1) or frozen (2)?            */
     143             :     /* -------------------------------------------------------------------- */
     144             : 
     145        3082 :     int iHidden = 0;
     146             : 
     147        8283 :     if (oStyleProperties.count("Hidden") > 0 ||
     148        4238 :         (poBlockFeature &&
     149        5201 :          poBlockFeature->oStyleProperties.count("Hidden") > 0))
     150             :     {
     151             :         // Hidden objects should never be shown no matter what happens
     152         207 :         iHidden = 1;
     153         207 :         oStyleProperties["Hidden"] = "1";
     154             :     }
     155             :     else
     156             :     {
     157        5750 :         auto osHidden = poDS->LookupLayerProperty(osLayer, "Hidden");
     158        2875 :         if (osHidden)
     159        2710 :             iHidden = atoi(osHidden->c_str());
     160             : 
     161             :         // Is the block feature on a frozen layer? If so, hide this feature
     162        2875 :         if (!iHidden && poBlockFeature)
     163             :         {
     164             :             const CPLString osBlockLayer =
     165        4010 :                 poBlockFeature->GetFieldAsString("Layer");
     166             :             auto osBlockHidden =
     167        4010 :                 poDS->LookupLayerProperty(osBlockLayer, "Hidden");
     168        2005 :             if (osBlockHidden && atoi(osBlockHidden->c_str()) == 2)
     169          32 :                 iHidden = 2;
     170             :         }
     171             : 
     172             :         // If this feature is on a frozen layer (other than layer 0), make the
     173             :         // object totally hidden so it won't reappear if we regenerate the style
     174             :         // string again during block insertion
     175        2875 :         if (iHidden == 2 && !EQUAL(GetFieldAsString("Layer"), "0"))
     176          64 :             oStyleProperties["Hidden"] = "1";
     177             :     }
     178             : 
     179             :     // Helpful constants
     180        3082 :     const int C_BYLAYER = 256;
     181        3082 :     const int C_BYBLOCK = 0;
     182        3082 :     const int C_TRUECOLOR = -100;  // not used in DXF - for our purposes only
     183        3082 :     const int C_BYLAYER_FORCE0 =
     184             :         -101;  // not used in DXF - for our purposes only
     185             : 
     186             :     /* -------------------------------------------------------------------- */
     187             :     /*      MULTILEADER entities store colors by directly outputting        */
     188             :     /*      the AcCmEntityColor struct as a 32-bit integer.                 */
     189             :     /* -------------------------------------------------------------------- */
     190             : 
     191        3082 :     int nColor = C_BYLAYER;
     192        3082 :     unsigned int nTrueColor = 0;
     193             : 
     194        3082 :     if (oStyleProperties.count("TrueColor") > 0)
     195             :     {
     196          20 :         nTrueColor = atoi(oStyleProperties["TrueColor"]);
     197          20 :         nColor = C_TRUECOLOR;
     198             :     }
     199        3062 :     else if (oStyleProperties.count("Color") > 0)
     200             :     {
     201        2234 :         nColor = atoi(oStyleProperties["Color"]);
     202             :     }
     203             : 
     204        3082 :     const unsigned char byColorMethod = (nColor & 0xFF000000) >> 24;
     205        3082 :     switch (byColorMethod)
     206             :     {
     207             :         // ByLayer
     208           0 :         case 0xC0:
     209           0 :             nColor = C_BYLAYER;
     210           0 :             break;
     211             : 
     212             :         // ByBlock
     213          16 :         case 0xC1:
     214          16 :             nColor = C_BYBLOCK;
     215          16 :             break;
     216             : 
     217             :         // RGB true color
     218           0 :         case 0xC2:
     219           0 :             nTrueColor = nColor & 0xFFFFFF;
     220           0 :             nColor = C_TRUECOLOR;
     221           0 :             break;
     222             : 
     223             :         // Indexed color
     224           2 :         case 0xC3:
     225           2 :             nColor &= 0xFF;
     226           2 :             break;
     227             :     }
     228             : 
     229             :     /* -------------------------------------------------------------------- */
     230             :     /*      Work out the indexed color for this feature.                    */
     231             :     /* -------------------------------------------------------------------- */
     232             : 
     233             :     // Use ByBlock color?
     234        3082 :     if (nColor == C_BYBLOCK && poBlockFeature)
     235             :     {
     236         555 :         if (poBlockFeature->oStyleProperties.count("TrueColor") > 0)
     237             :         {
     238             :             // Inherit true color from the owning block
     239           3 :             nTrueColor = atoi(poBlockFeature->oStyleProperties["TrueColor"]);
     240           3 :             nColor = C_TRUECOLOR;
     241             : 
     242             :             // Use the inherited color if we regenerate the style string
     243             :             // again during block insertion
     244           6 :             oStyleProperties["TrueColor"] =
     245           9 :                 poBlockFeature->oStyleProperties["TrueColor"];
     246             :         }
     247         552 :         else if (poBlockFeature->oStyleProperties.count("Color") > 0)
     248             :         {
     249             :             // Inherit color from the owning block
     250         387 :             nColor = atoi(poBlockFeature->oStyleProperties["Color"]);
     251             : 
     252             :             // Use the inherited color if we regenerate the style string
     253             :             // again during block insertion
     254         774 :             oStyleProperties["Color"] =
     255        1161 :                 poBlockFeature->oStyleProperties["Color"];
     256             :         }
     257             :         else
     258             :         {
     259             :             // If the owning block has no explicit color, assume ByLayer,
     260             :             // but take the color from the owning block's layer
     261         165 :             nColor = C_BYLAYER;
     262         165 :             osLayer = poBlockFeature->GetFieldAsString("Layer");
     263             : 
     264             :             // If we regenerate the style string again during
     265             :             // block insertion, treat as ByLayer, but when
     266             :             // not in block insertion, treat as layer 0
     267         165 :             oStyleProperties["Color"] = std::to_string(C_BYLAYER_FORCE0);
     268             :         }
     269             :     }
     270             : 
     271             :     // Strange special case: consider the following scenario:
     272             :     //
     273             :     //                             Block  Color    Layer
     274             :     //                             -----  -------  -------
     275             :     //  Drawing contains:  INSERT  BLK1   ByBlock  MYLAYER
     276             :     //     BLK1 contains:  INSERT  BLK2   ByLayer  0
     277             :     //     BLK2 contains:  LINE           ByBlock  0
     278             :     //
     279             :     // When viewing the drawing, the line is displayed in
     280             :     // MYLAYER's layer colour, not black as might be expected.
     281        3082 :     if (nColor == C_BYLAYER_FORCE0)
     282             :     {
     283          35 :         if (poBlockFeature)
     284          35 :             osLayer = poBlockFeature->GetFieldAsString("Layer");
     285             :         else
     286           0 :             osLayer = "0";
     287             : 
     288          35 :         nColor = C_BYLAYER;
     289             :     }
     290             : 
     291             :     // Use layer color?
     292        3082 :     if (nColor == C_BYLAYER)
     293             :     {
     294        2484 :         auto osTrueColor = poDS->LookupLayerProperty(osLayer, "TrueColor");
     295        1242 :         if (osTrueColor)
     296             :         {
     297           1 :             nTrueColor = atoi(osTrueColor->c_str());
     298           1 :             nColor = C_TRUECOLOR;
     299             : 
     300           1 :             if (poBlockFeature && osLayer != "0")
     301             :             {
     302             :                 // Use the inherited color if we regenerate the style string
     303             :                 // again during block insertion (except when the entity is
     304             :                 // on layer 0)
     305           1 :                 oStyleProperties["TrueColor"] = *osTrueColor;
     306             :             }
     307             :         }
     308             :         else
     309             :         {
     310        2482 :             auto osColor = poDS->LookupLayerProperty(osLayer, "Color");
     311        1241 :             if (osColor)
     312             :             {
     313        1097 :                 nColor = atoi(osColor->c_str());
     314             : 
     315        1097 :                 if (poBlockFeature && osLayer != "0")
     316             :                 {
     317             :                     // Use the inherited color if we regenerate the style string
     318             :                     // again during block insertion (except when the entity is
     319             :                     // on layer 0)
     320         436 :                     oStyleProperties["Color"] = *osColor;
     321             :                 }
     322             :             }
     323             :         }
     324             :     }
     325             : 
     326             :     // If no color is available, use the default black/white color
     327        3082 :     if (nColor != C_TRUECOLOR && (nColor < 1 || nColor > 255))
     328         369 :         nColor = 7;
     329             : 
     330             :     /* -------------------------------------------------------------------- */
     331             :     /*      Translate the DWG/DXF color index to a hex color string.        */
     332             :     /* -------------------------------------------------------------------- */
     333             : 
     334        3082 :     CPLString osResult;
     335             : 
     336        3082 :     if (nColor == C_TRUECOLOR)
     337             :     {
     338          24 :         osResult.Printf("#%06x", nTrueColor);
     339             :     }
     340             :     else
     341             :     {
     342        3058 :         const unsigned char *pabyDXFColors = ACGetColorTable();
     343             : 
     344        3058 :         osResult.Printf("#%02x%02x%02x", pabyDXFColors[nColor * 3 + 0],
     345        3058 :                         pabyDXFColors[nColor * 3 + 1],
     346        3058 :                         pabyDXFColors[nColor * 3 + 2]);
     347             :     }
     348             : 
     349        3082 :     if (iHidden)
     350         369 :         osResult += "00";
     351             :     else
     352             :     {
     353        2713 :         int nOpacity = -1;
     354             : 
     355        2713 :         if (oStyleProperties.count("Transparency") > 0)
     356             :         {
     357         166 :             int nTransparency = atoi(oStyleProperties["Transparency"]);
     358         166 :             if ((nTransparency & 0x02000000) != 0)
     359             :             {
     360           3 :                 nOpacity = nTransparency & 0xFF;
     361             :             }
     362         163 :             else if ((nTransparency & 0x01000000) != 0)  // By block ?
     363             :             {
     364         295 :                 if (poBlockFeature &&
     365         295 :                     poBlockFeature->oStyleProperties.count("Transparency") > 0)
     366             :                 {
     367           0 :                     nOpacity =
     368           0 :                         atoi(poBlockFeature->oStyleProperties["Transparency"]) &
     369             :                         0xFF;
     370             : 
     371             :                     // Use the inherited transparency if we regenerate the style string
     372             :                     // again during block insertion
     373           0 :                     oStyleProperties["Transparency"] =
     374           0 :                         poBlockFeature->oStyleProperties["Transparency"];
     375             :                 }
     376             :             }
     377             :         }
     378             :         else
     379             :         {
     380             :             auto osTransparency =
     381        5094 :                 poDS->LookupLayerProperty(osLayer, "Transparency");
     382        2547 :             if (osTransparency)
     383             :             {
     384           0 :                 nOpacity = atoi(osTransparency->c_str()) & 0xFF;
     385             : 
     386           0 :                 if (poBlockFeature && osLayer != "0")
     387             :                 {
     388             :                     // Use the inherited transparency if we regenerate the style string
     389             :                     // again during block insertion (except when the entity is
     390             :                     // on layer 0)
     391           0 :                     oStyleProperties["Transparency"] = *osTransparency;
     392             :                 }
     393             :             }
     394             :         }
     395             : 
     396        2713 :         if (nOpacity >= 0)
     397           3 :             osResult += CPLSPrintf("%02x", nOpacity & 0xFF);
     398             :     }
     399             : 
     400        6164 :     return osResult;
     401             : }

Generated by: LCOV version 1.14