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: 118 131 90.1 %
Date: 2025-07-09 17:50:03 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        4505 : 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        4505 :       oOriginalCoords(0.0, 0.0, 0.0)
      24             : {
      25        4505 : }
      26             : 
      27             : OGRDXFFeature::~OGRDXFFeature() = default;
      28             : 
      29             : /************************************************************************/
      30             : /*                          CloneDXFFeature()                           */
      31             : /*                                                                      */
      32             : /*      Replacement for OGRFeature::Clone() for DXF features.           */
      33             : /************************************************************************/
      34             : 
      35        3692 : OGRDXFFeature *OGRDXFFeature::CloneDXFFeature()
      36             : {
      37        3692 :     OGRDXFFeature *poNew = new OGRDXFFeature(GetDefnRef());
      38        3692 :     if (poNew == nullptr)
      39           0 :         return nullptr;
      40             : 
      41        3692 :     if (!CopySelfTo(poNew))
      42             :     {
      43           0 :         delete poNew;
      44           0 :         return nullptr;
      45             :     }
      46             : 
      47        3692 :     poNew->oOCS = oOCS;
      48        3692 :     poNew->bIsBlockReference = bIsBlockReference;
      49        3692 :     poNew->osBlockName = osBlockName;
      50        3692 :     poNew->dfBlockAngle = dfBlockAngle;
      51        3692 :     poNew->oBlockScale = oBlockScale;
      52        3692 :     poNew->oOriginalCoords = oOriginalCoords;
      53        3692 :     poNew->osAttributeTag = osAttributeTag;
      54        3692 :     poNew->oStyleProperties = oStyleProperties;
      55             : 
      56        3692 :     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        3694 :          apoAttribFeatures)
      64             :     {
      65             :         poNew->apoAttribFeatures.emplace_back(
      66           2 :             poAttribFeature->CloneDXFFeature());
      67             :     }
      68             : 
      69        3692 :     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        3785 : void OGRDXFFeature::ApplyOCSTransformer(OGRGeometry *const poGeometry) const
      80             : {
      81        3785 :     if (poGeometry == nullptr)
      82           0 :         return;
      83             : 
      84             :     double adfN[3];
      85        3785 :     oOCS.ToArray(adfN);
      86             : 
      87        7570 :     OGRDXFOCSTransformer oTransformer(adfN);
      88             : 
      89             :     // Promote to 3D, in case the OCS transformation introduces a
      90             :     // third dimension to the geometry.
      91        3785 :     const bool bInitially2D = !poGeometry->Is3D();
      92        3785 :     if (bInitially2D)
      93         189 :         poGeometry->set3D(TRUE);
      94             : 
      95        3785 :     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        3785 :     if (bInitially2D)
     100             :     {
     101         189 :         OGREnvelope3D oEnvelope;
     102         189 :         poGeometry->getEnvelope(&oEnvelope);
     103         189 :         if (oEnvelope.MaxZ == 0.0 && oEnvelope.MinZ == 0.0)
     104         172 :             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        3049 : OGRDXFFeature::GetColor(OGRDXFDataSource *const poDS,
     137             :                         OGRDXFFeature *const poBlockFeature /* = NULL */)
     138             : {
     139        6098 :     CPLString osLayer = GetFieldAsString("Layer");
     140             : 
     141             :     /* -------------------------------------------------------------------- */
     142             :     /*      Is the layer or object hidden/off (1) or frozen (2)?            */
     143             :     /* -------------------------------------------------------------------- */
     144             : 
     145        3049 :     int iHidden = 0;
     146             : 
     147        8217 :     if (oStyleProperties.count("Hidden") > 0 ||
     148        4238 :         (poBlockFeature &&
     149        5168 :          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        2842 :         const char *pszHidden = poDS->LookupLayerProperty(osLayer, "Hidden");
     158        2842 :         if (pszHidden)
     159        2842 :             iHidden = atoi(pszHidden);
     160             : 
     161             :         // Is the block feature on a frozen layer? If so, hide this feature
     162        2842 :         if (!iHidden && poBlockFeature)
     163             :         {
     164             :             const CPLString osBlockLayer =
     165        4010 :                 poBlockFeature->GetFieldAsString("Layer");
     166             :             const char *pszBlockHidden =
     167        2005 :                 poDS->LookupLayerProperty(osBlockLayer, "Hidden");
     168        2005 :             if (pszBlockHidden && atoi(pszBlockHidden) == 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        2842 :         if (iHidden == 2 && !EQUAL(GetFieldAsString("Layer"), "0"))
     176          64 :             oStyleProperties["Hidden"] = "1";
     177             :     }
     178             : 
     179             :     // Helpful constants
     180        3049 :     const int C_BYLAYER = 256;
     181        3049 :     const int C_BYBLOCK = 0;
     182        3049 :     const int C_TRUECOLOR = -100;  // not used in DXF - for our purposes only
     183        3049 :     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        3049 :     int nColor = C_BYLAYER;
     192        3049 :     unsigned int nTrueColor = 0;
     193             : 
     194        3049 :     if (oStyleProperties.count("TrueColor") > 0)
     195             :     {
     196          11 :         nTrueColor = atoi(oStyleProperties["TrueColor"]);
     197          11 :         nColor = C_TRUECOLOR;
     198             :     }
     199        3038 :     else if (oStyleProperties.count("Color") > 0)
     200             :     {
     201        2222 :         nColor = atoi(oStyleProperties["Color"]);
     202             :     }
     203             : 
     204        3049 :     const unsigned char byColorMethod = (nColor & 0xFF000000) >> 24;
     205        3049 :     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        3049 :     if (nColor == C_BYBLOCK && poBlockFeature)
     235             :     {
     236         559 :         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         556 :         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         169 :             nColor = C_BYLAYER;
     262         169 :             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         169 :             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        3049 :     if (nColor == C_BYLAYER_FORCE0)
     282             :     {
     283          32 :         if (poBlockFeature)
     284          32 :             osLayer = poBlockFeature->GetFieldAsString("Layer");
     285             :         else
     286           0 :             osLayer = "0";
     287             : 
     288          32 :         nColor = C_BYLAYER;
     289             :     }
     290             : 
     291             :     // Use layer color?
     292        3049 :     if (nColor == C_BYLAYER)
     293             :     {
     294             :         const char *pszTrueColor =
     295        1231 :             poDS->LookupLayerProperty(osLayer, "TrueColor");
     296        1231 :         if (pszTrueColor != nullptr && *pszTrueColor)
     297             :         {
     298           1 :             nTrueColor = atoi(pszTrueColor);
     299           1 :             nColor = C_TRUECOLOR;
     300             : 
     301           1 :             if (poBlockFeature && osLayer != "0")
     302             :             {
     303             :                 // Use the inherited color if we regenerate the style string
     304             :                 // again during block insertion (except when the entity is
     305             :                 // on layer 0)
     306           1 :                 oStyleProperties["TrueColor"] = pszTrueColor;
     307             :             }
     308             :         }
     309             :         else
     310             :         {
     311        1230 :             const char *pszColor = poDS->LookupLayerProperty(osLayer, "Color");
     312        1230 :             if (pszColor != nullptr)
     313             :             {
     314        1230 :                 nColor = atoi(pszColor);
     315             : 
     316        1230 :                 if (poBlockFeature && osLayer != "0")
     317             :                 {
     318             :                     // Use the inherited color if we regenerate the style string
     319             :                     // again during block insertion (except when the entity is
     320             :                     // on layer 0)
     321         443 :                     oStyleProperties["Color"] = pszColor;
     322             :                 }
     323             :             }
     324             :         }
     325             :     }
     326             : 
     327             :     // If no color is available, use the default black/white color
     328        3049 :     if (nColor != C_TRUECOLOR && (nColor < 1 || nColor > 255))
     329         366 :         nColor = 7;
     330             : 
     331             :     /* -------------------------------------------------------------------- */
     332             :     /*      Translate the DWG/DXF color index to a hex color string.        */
     333             :     /* -------------------------------------------------------------------- */
     334             : 
     335        3049 :     CPLString osResult;
     336             : 
     337        3049 :     if (nColor == C_TRUECOLOR)
     338             :     {
     339          15 :         osResult.Printf("#%06x", nTrueColor);
     340             :     }
     341             :     else
     342             :     {
     343        3034 :         const unsigned char *pabyDXFColors = ACGetColorTable();
     344             : 
     345        3034 :         osResult.Printf("#%02x%02x%02x", pabyDXFColors[nColor * 3 + 0],
     346        3034 :                         pabyDXFColors[nColor * 3 + 1],
     347        3034 :                         pabyDXFColors[nColor * 3 + 2]);
     348             :     }
     349             : 
     350        3049 :     if (iHidden)
     351         369 :         osResult += "00";
     352             : 
     353        6098 :     return osResult;
     354             : }

Generated by: LCOV version 1.14