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 : }