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