/src/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_feature.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | OGRDXFFeature::OGRDXFFeature(OGRFeatureDefn *poFeatureDefn) |
21 | 27.9M | : OGRFeature(poFeatureDefn), oOCS(0.0, 0.0, 1.0), bIsBlockReference(false), |
22 | 27.9M | dfBlockAngle(0.0), oBlockScale(1.0, 1.0, 1.0), |
23 | 27.9M | oOriginalCoords(0.0, 0.0, 0.0) |
24 | 27.9M | { |
25 | 27.9M | } |
26 | | |
27 | 27.9M | OGRDXFFeature::~OGRDXFFeature() = default; |
28 | | |
29 | | /************************************************************************/ |
30 | | /* CloneDXFFeature() */ |
31 | | /* */ |
32 | | /* Replacement for OGRFeature::Clone() for DXF features. */ |
33 | | /************************************************************************/ |
34 | | |
35 | | OGRDXFFeature *OGRDXFFeature::CloneDXFFeature() |
36 | 26.9M | { |
37 | 26.9M | OGRDXFFeature *poNew = new OGRDXFFeature(GetDefnRef()); |
38 | 26.9M | if (poNew == nullptr) |
39 | 0 | return nullptr; |
40 | | |
41 | 26.9M | if (!CopySelfTo(poNew)) |
42 | 0 | { |
43 | 0 | delete poNew; |
44 | 0 | return nullptr; |
45 | 0 | } |
46 | | |
47 | 26.9M | poNew->oOCS = oOCS; |
48 | 26.9M | poNew->bIsBlockReference = bIsBlockReference; |
49 | 26.9M | poNew->osBlockName = osBlockName; |
50 | 26.9M | poNew->dfBlockAngle = dfBlockAngle; |
51 | 26.9M | poNew->oBlockScale = oBlockScale; |
52 | 26.9M | poNew->oOriginalCoords = oOriginalCoords; |
53 | 26.9M | poNew->osAttributeTag = osAttributeTag; |
54 | 26.9M | poNew->oStyleProperties = oStyleProperties; |
55 | | |
56 | 26.9M | if (poASMTransform) |
57 | 0 | { |
58 | 0 | poNew->poASMTransform = std::unique_ptr<OGRDXFAffineTransform>( |
59 | 0 | new OGRDXFAffineTransform(*poASMTransform)); |
60 | 0 | } |
61 | | |
62 | 26.9M | for (const std::unique_ptr<OGRDXFFeature> &poAttribFeature : |
63 | 26.9M | apoAttribFeatures) |
64 | 6.99k | { |
65 | 6.99k | poNew->apoAttribFeatures.emplace_back( |
66 | 6.99k | poAttribFeature->CloneDXFFeature()); |
67 | 6.99k | } |
68 | | |
69 | 26.9M | return poNew; |
70 | 26.9M | } |
71 | | |
72 | | /************************************************************************/ |
73 | | /* ApplyOCSTransformer() */ |
74 | | /* */ |
75 | | /* Applies the OCS transformation stored in this feature to */ |
76 | | /* the specified geometry. */ |
77 | | /************************************************************************/ |
78 | | |
79 | | void OGRDXFFeature::ApplyOCSTransformer(OGRGeometry *const poGeometry) const |
80 | 24.2M | { |
81 | 24.2M | if (poGeometry == nullptr) |
82 | 0 | return; |
83 | | |
84 | 24.2M | double adfN[3]; |
85 | 24.2M | oOCS.ToArray(adfN); |
86 | | |
87 | 24.2M | OGRDXFOCSTransformer oTransformer(adfN); |
88 | | |
89 | | // Promote to 3D, in case the OCS transformation introduces a |
90 | | // third dimension to the geometry. |
91 | 24.2M | const bool bInitially2D = !poGeometry->Is3D(); |
92 | 24.2M | if (bInitially2D) |
93 | 11.0M | poGeometry->set3D(TRUE); |
94 | | |
95 | 24.2M | 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 | 24.2M | if (bInitially2D) |
100 | 11.0M | { |
101 | 11.0M | OGREnvelope3D oEnvelope; |
102 | 11.0M | poGeometry->getEnvelope(&oEnvelope); |
103 | 11.0M | if (oEnvelope.MaxZ == 0.0 && oEnvelope.MinZ == 0.0) |
104 | 8.45M | poGeometry->flattenTo2D(); |
105 | 11.0M | } |
106 | 24.2M | } |
107 | | |
108 | | /************************************************************************/ |
109 | | /* ApplyOCSTransformer() */ |
110 | | /************************************************************************/ |
111 | | |
112 | | void OGRDXFFeature::ApplyOCSTransformer(OGRDXFAffineTransform *const poCT) const |
113 | 0 | { |
114 | 0 | if (!poCT) |
115 | 0 | return; |
116 | | |
117 | 0 | double adfN[3]; |
118 | 0 | oOCS.ToArray(adfN); |
119 | |
|
120 | 0 | OGRDXFOCSTransformer oTransformer(adfN); |
121 | |
|
122 | 0 | oTransformer.ComposeOnto(*poCT); |
123 | 0 | } |
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 | | OGRDXFFeature::GetColor(OGRDXFDataSource *const poDS, |
137 | | OGRDXFFeature *const poBlockFeature /* = NULL */) |
138 | 11.6M | { |
139 | 11.6M | CPLString osLayer = GetFieldAsString("Layer"); |
140 | | |
141 | | /* -------------------------------------------------------------------- */ |
142 | | /* Is the layer or object hidden/off (1) or frozen (2)? */ |
143 | | /* -------------------------------------------------------------------- */ |
144 | | |
145 | 11.6M | int iHidden = 0; |
146 | | |
147 | 11.6M | if (oStyleProperties.count("Hidden") > 0 || |
148 | 11.6M | (poBlockFeature && |
149 | 10.7M | poBlockFeature->oStyleProperties.count("Hidden") > 0)) |
150 | 1.01M | { |
151 | | // Hidden objects should never be shown no matter what happens |
152 | 1.01M | iHidden = 1; |
153 | 1.01M | oStyleProperties["Hidden"] = "1"; |
154 | 1.01M | } |
155 | 10.5M | else |
156 | 10.5M | { |
157 | 10.5M | auto osHidden = poDS->LookupLayerProperty(osLayer, "Hidden"); |
158 | 10.5M | if (osHidden) |
159 | 57.6k | iHidden = atoi(osHidden->c_str()); |
160 | | |
161 | | // Is the block feature on a frozen layer? If so, hide this feature |
162 | 10.5M | if (!iHidden && poBlockFeature) |
163 | 7.56M | { |
164 | 7.56M | const CPLString osBlockLayer = |
165 | 7.56M | poBlockFeature->GetFieldAsString("Layer"); |
166 | 7.56M | auto osBlockHidden = |
167 | 7.56M | poDS->LookupLayerProperty(osBlockLayer, "Hidden"); |
168 | 7.56M | if (osBlockHidden && atoi(osBlockHidden->c_str()) == 2) |
169 | 1.67k | iHidden = 2; |
170 | 7.56M | } |
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 | 10.5M | if (iHidden == 2 && !EQUAL(GetFieldAsString("Layer"), "0")) |
176 | 3.40k | oStyleProperties["Hidden"] = "1"; |
177 | 10.5M | } |
178 | | |
179 | | // Helpful constants |
180 | 11.6M | const int C_BYLAYER = 256; |
181 | 11.6M | const int C_BYBLOCK = 0; |
182 | 11.6M | const int C_TRUECOLOR = -100; // not used in DXF - for our purposes only |
183 | 11.6M | const int C_BYLAYER_FORCE0 = |
184 | 11.6M | -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 | 11.6M | int nColor = C_BYLAYER; |
192 | 11.6M | unsigned int nTrueColor = 0; |
193 | | |
194 | 11.6M | if (oStyleProperties.count("TrueColor") > 0) |
195 | 30.2k | { |
196 | 30.2k | nTrueColor = atoi(oStyleProperties["TrueColor"]); |
197 | 30.2k | nColor = C_TRUECOLOR; |
198 | 30.2k | } |
199 | 11.5M | else if (oStyleProperties.count("Color") > 0) |
200 | 557k | { |
201 | 557k | nColor = atoi(oStyleProperties["Color"]); |
202 | 557k | } |
203 | | |
204 | 11.6M | const unsigned char byColorMethod = (nColor & 0xFF000000) >> 24; |
205 | 11.6M | switch (byColorMethod) |
206 | 11.6M | { |
207 | | // ByLayer |
208 | 167 | case 0xC0: |
209 | 167 | nColor = C_BYLAYER; |
210 | 167 | break; |
211 | | |
212 | | // ByBlock |
213 | 482 | case 0xC1: |
214 | 482 | nColor = C_BYBLOCK; |
215 | 482 | break; |
216 | | |
217 | | // RGB true color |
218 | 358 | case 0xC2: |
219 | 358 | nTrueColor = nColor & 0xFFFFFF; |
220 | 358 | nColor = C_TRUECOLOR; |
221 | 358 | break; |
222 | | |
223 | | // Indexed color |
224 | 649 | case 0xC3: |
225 | 649 | nColor &= 0xFF; |
226 | 649 | break; |
227 | 11.6M | } |
228 | | |
229 | | /* -------------------------------------------------------------------- */ |
230 | | /* Work out the indexed color for this feature. */ |
231 | | /* -------------------------------------------------------------------- */ |
232 | | |
233 | | // Use ByBlock color? |
234 | 11.6M | if (nColor == C_BYBLOCK && poBlockFeature) |
235 | 176k | { |
236 | 176k | if (poBlockFeature->oStyleProperties.count("TrueColor") > 0) |
237 | 834 | { |
238 | | // Inherit true color from the owning block |
239 | 834 | nTrueColor = atoi(poBlockFeature->oStyleProperties["TrueColor"]); |
240 | 834 | nColor = C_TRUECOLOR; |
241 | | |
242 | | // Use the inherited color if we regenerate the style string |
243 | | // again during block insertion |
244 | 834 | oStyleProperties["TrueColor"] = |
245 | 834 | poBlockFeature->oStyleProperties["TrueColor"]; |
246 | 834 | } |
247 | 175k | else if (poBlockFeature->oStyleProperties.count("Color") > 0) |
248 | 15.9k | { |
249 | | // Inherit color from the owning block |
250 | 15.9k | nColor = atoi(poBlockFeature->oStyleProperties["Color"]); |
251 | | |
252 | | // Use the inherited color if we regenerate the style string |
253 | | // again during block insertion |
254 | 15.9k | oStyleProperties["Color"] = |
255 | 15.9k | poBlockFeature->oStyleProperties["Color"]; |
256 | 15.9k | } |
257 | 159k | else |
258 | 159k | { |
259 | | // If the owning block has no explicit color, assume ByLayer, |
260 | | // but take the color from the owning block's layer |
261 | 159k | nColor = C_BYLAYER; |
262 | 159k | 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 | 159k | oStyleProperties["Color"] = std::to_string(C_BYLAYER_FORCE0); |
268 | 159k | } |
269 | 176k | } |
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 | 11.6M | if (nColor == C_BYLAYER_FORCE0) |
282 | 301k | { |
283 | 301k | if (poBlockFeature) |
284 | 301k | osLayer = poBlockFeature->GetFieldAsString("Layer"); |
285 | 1 | else |
286 | 1 | osLayer = "0"; |
287 | | |
288 | 301k | nColor = C_BYLAYER; |
289 | 301k | } |
290 | | |
291 | | // Use layer color? |
292 | 11.6M | if (nColor == C_BYLAYER) |
293 | 11.4M | { |
294 | 11.4M | auto osTrueColor = poDS->LookupLayerProperty(osLayer, "TrueColor"); |
295 | 11.4M | if (osTrueColor) |
296 | 12.5k | { |
297 | 12.5k | nTrueColor = atoi(osTrueColor->c_str()); |
298 | 12.5k | nColor = C_TRUECOLOR; |
299 | | |
300 | 12.5k | if (poBlockFeature && osLayer != "0") |
301 | 8.05k | { |
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 | 8.05k | oStyleProperties["TrueColor"] = *osTrueColor; |
306 | 8.05k | } |
307 | 12.5k | } |
308 | 11.4M | else |
309 | 11.4M | { |
310 | 11.4M | auto osColor = poDS->LookupLayerProperty(osLayer, "Color"); |
311 | 11.4M | if (osColor) |
312 | 3.83k | { |
313 | 3.83k | nColor = atoi(osColor->c_str()); |
314 | | |
315 | 3.83k | if (poBlockFeature && osLayer != "0") |
316 | 2.00k | { |
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 | 2.00k | oStyleProperties["Color"] = *osColor; |
321 | 2.00k | } |
322 | 3.83k | } |
323 | 11.4M | } |
324 | 11.4M | } |
325 | | |
326 | | // If no color is available, use the default black/white color |
327 | 11.6M | if (nColor != C_TRUECOLOR && (nColor < 1 || nColor > 255)) |
328 | 11.5M | nColor = 7; |
329 | | |
330 | | /* -------------------------------------------------------------------- */ |
331 | | /* Translate the DWG/DXF color index to a hex color string. */ |
332 | | /* -------------------------------------------------------------------- */ |
333 | | |
334 | 11.6M | CPLString osResult; |
335 | | |
336 | 11.6M | if (nColor == C_TRUECOLOR) |
337 | 44.0k | { |
338 | 44.0k | osResult.Printf("#%06x", nTrueColor); |
339 | 44.0k | } |
340 | 11.5M | else |
341 | 11.5M | { |
342 | 11.5M | const unsigned char *pabyDXFColors = ACGetColorTable(); |
343 | | |
344 | 11.5M | osResult.Printf("#%02x%02x%02x", pabyDXFColors[nColor * 3 + 0], |
345 | 11.5M | pabyDXFColors[nColor * 3 + 1], |
346 | 11.5M | pabyDXFColors[nColor * 3 + 2]); |
347 | 11.5M | } |
348 | | |
349 | 11.6M | if (iHidden) |
350 | 1.03M | osResult += "00"; |
351 | 10.5M | else |
352 | 10.5M | { |
353 | 10.5M | int nOpacity = -1; |
354 | | |
355 | 10.5M | if (oStyleProperties.count("Transparency") > 0) |
356 | 5.02k | { |
357 | 5.02k | int nTransparency = atoi(oStyleProperties["Transparency"]); |
358 | 5.02k | if ((nTransparency & 0x02000000) != 0) |
359 | 1.28k | { |
360 | 1.28k | nOpacity = nTransparency & 0xFF; |
361 | 1.28k | } |
362 | 3.73k | else if ((nTransparency & 0x01000000) != 0) // By block ? |
363 | 0 | { |
364 | 0 | if (poBlockFeature && |
365 | 0 | poBlockFeature->oStyleProperties.count("Transparency") > 0) |
366 | 0 | { |
367 | 0 | nOpacity = |
368 | 0 | atoi(poBlockFeature->oStyleProperties["Transparency"]) & |
369 | 0 | 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 | 0 | } |
376 | 0 | } |
377 | 5.02k | } |
378 | 10.5M | else |
379 | 10.5M | { |
380 | 10.5M | auto osTransparency = |
381 | 10.5M | poDS->LookupLayerProperty(osLayer, "Transparency"); |
382 | 10.5M | if (osTransparency) |
383 | 2.04k | { |
384 | 2.04k | nOpacity = atoi(osTransparency->c_str()) & 0xFF; |
385 | | |
386 | 2.04k | if (poBlockFeature && osLayer != "0") |
387 | 1.05k | { |
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 | 1.05k | oStyleProperties["Transparency"] = *osTransparency; |
392 | 1.05k | } |
393 | 2.04k | } |
394 | 10.5M | } |
395 | | |
396 | 10.5M | if (nOpacity >= 0) |
397 | 3.32k | osResult += CPLSPrintf("%02x", nOpacity & 0xFF); |
398 | 10.5M | } |
399 | | |
400 | 11.6M | return osResult; |
401 | 11.6M | } |