/src/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_leader.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: DXF Translator |
4 | | * Purpose: Implements translation support for LEADER and MULTILEADER |
5 | | * elements as a part of the OGRDXFLayer class. |
6 | | * Author: Alan Thomas, alant@outlook.com.au |
7 | | * |
8 | | ****************************************************************************** |
9 | | * Copyright (c) 2017, Alan Thomas <alant@outlook.com.au> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "ogr_dxf.h" |
15 | | #include "cpl_conv.h" |
16 | | #include "../../../alg/gdallinearsystem.h" |
17 | | #include <stdexcept> |
18 | | #include <algorithm> |
19 | | |
20 | | static void InterpolateSpline(OGRLineString *const poLine, |
21 | | const DXFTriple &oEndTangentDirection); |
22 | | |
23 | | /************************************************************************/ |
24 | | /* PointDist() */ |
25 | | /************************************************************************/ |
26 | | |
27 | | #ifndef PointDist_defined |
28 | | #define PointDist_defined |
29 | | |
30 | | inline static double PointDist(double x1, double y1, double x2, double y2) |
31 | 238k | { |
32 | 238k | return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); |
33 | 238k | } |
34 | | #endif |
35 | | |
36 | | inline static double PointDist(double x1, double y1, double z1, double x2, |
37 | | double y2, double z2) |
38 | 520k | { |
39 | 520k | return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + |
40 | 520k | (z2 - z1) * (z2 - z1)); |
41 | 520k | } |
42 | | |
43 | | /************************************************************************/ |
44 | | /* TranslateLEADER() */ |
45 | | /************************************************************************/ |
46 | | |
47 | | OGRDXFFeature *OGRDXFLayer::TranslateLEADER() |
48 | | |
49 | 80.7k | { |
50 | 80.7k | char szLineBuf[257]; |
51 | 80.7k | int nCode; |
52 | 80.7k | OGRDXFFeature *poFeature = new OGRDXFFeature(poFeatureDefn); |
53 | | |
54 | 80.7k | OGRLineString *poLine = new OGRLineString(); |
55 | 80.7k | bool bHaveX = false; |
56 | 80.7k | bool bHaveY = false; |
57 | 80.7k | bool bHaveZ = false; |
58 | 80.7k | double dfCurrentX = 0.0; |
59 | 80.7k | double dfCurrentY = 0.0; |
60 | 80.7k | double dfCurrentZ = 0.0; |
61 | 80.7k | int nNumVertices = 0; |
62 | | |
63 | 80.7k | bool bHorizontalDirectionFlip = true; |
64 | 80.7k | double dfHorizontalDirectionX = 1.0; |
65 | 80.7k | double dfHorizontalDirectionY = 0.0; |
66 | 80.7k | double dfHorizontalDirectionZ = 0.0; |
67 | 80.7k | bool bHasTextAnnotation = false; |
68 | 80.7k | double dfTextAnnotationWidth = 0.0; |
69 | 80.7k | bool bIsSpline = false; |
70 | | |
71 | | // spec is silent as to default, but AutoCAD assumes true |
72 | 80.7k | bool bWantArrowhead = true; |
73 | | |
74 | 80.7k | bool bReadyForDimstyleOverride = false; |
75 | | |
76 | 80.7k | std::map<CPLString, CPLString> oDimStyleProperties; |
77 | 80.7k | poDS->PopulateDefaultDimStyleProperties(oDimStyleProperties); |
78 | | |
79 | 1.96M | while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0) |
80 | 1.88M | { |
81 | 1.88M | switch (nCode) |
82 | 1.88M | { |
83 | 86.1k | case 3: |
84 | | // 3 is the dimension style name. We don't need to store it, |
85 | | // let's just fetch the dimension style properties |
86 | 86.1k | poDS->LookupDimStyle(szLineBuf, oDimStyleProperties); |
87 | 86.1k | break; |
88 | | |
89 | 285k | case 10: |
90 | | // add the previous point onto the linestring |
91 | 285k | if (bHaveX && bHaveY && bHaveZ) |
92 | 50.4k | { |
93 | 50.4k | poLine->setPoint(nNumVertices++, dfCurrentX, dfCurrentY, |
94 | 50.4k | dfCurrentZ); |
95 | 50.4k | bHaveY = bHaveZ = false; |
96 | 50.4k | } |
97 | 285k | dfCurrentX = CPLAtof(szLineBuf); |
98 | 285k | bHaveX = true; |
99 | 285k | break; |
100 | | |
101 | 238k | case 20: |
102 | | // add the previous point onto the linestring |
103 | 238k | if (bHaveX && bHaveY && bHaveZ) |
104 | 16.3k | { |
105 | 16.3k | poLine->setPoint(nNumVertices++, dfCurrentX, dfCurrentY, |
106 | 16.3k | dfCurrentZ); |
107 | 16.3k | bHaveX = bHaveZ = false; |
108 | 16.3k | } |
109 | 238k | dfCurrentY = CPLAtof(szLineBuf); |
110 | 238k | bHaveY = true; |
111 | 238k | break; |
112 | | |
113 | 213k | case 30: |
114 | | // add the previous point onto the linestring |
115 | 213k | if (bHaveX && bHaveY && bHaveZ) |
116 | 37.8k | { |
117 | 37.8k | poLine->setPoint(nNumVertices++, dfCurrentX, dfCurrentY, |
118 | 37.8k | dfCurrentZ); |
119 | 37.8k | bHaveX = bHaveY = false; |
120 | 37.8k | } |
121 | 213k | dfCurrentZ = CPLAtof(szLineBuf); |
122 | 213k | bHaveZ = true; |
123 | 213k | break; |
124 | | |
125 | 3.56k | case 41: |
126 | 3.56k | dfTextAnnotationWidth = CPLAtof(szLineBuf); |
127 | 3.56k | break; |
128 | | |
129 | 6.18k | case 71: |
130 | 6.18k | bWantArrowhead = atoi(szLineBuf) != 0; |
131 | 6.18k | break; |
132 | | |
133 | 91.3k | case 72: |
134 | 91.3k | bIsSpline = atoi(szLineBuf) != 0; |
135 | 91.3k | break; |
136 | | |
137 | 12.4k | case 73: |
138 | 12.4k | bHasTextAnnotation = atoi(szLineBuf) == 0; |
139 | 12.4k | break; |
140 | | |
141 | 9.79k | case 74: |
142 | | // DXF spec seems to have this backwards. A value of 0 actually |
143 | | // indicates no flipping occurs, and 1 (flip) is the default |
144 | 9.79k | bHorizontalDirectionFlip = atoi(szLineBuf) != 0; |
145 | 9.79k | break; |
146 | | |
147 | 5.61k | case 211: |
148 | 5.61k | dfHorizontalDirectionX = CPLAtof(szLineBuf); |
149 | 5.61k | break; |
150 | | |
151 | 337 | case 221: |
152 | 337 | dfHorizontalDirectionY = CPLAtof(szLineBuf); |
153 | 337 | break; |
154 | | |
155 | 4.94k | case 231: |
156 | 4.94k | dfHorizontalDirectionZ = CPLAtof(szLineBuf); |
157 | 4.94k | break; |
158 | | |
159 | 8.22k | case 1001: |
160 | 8.22k | bReadyForDimstyleOverride = EQUAL(szLineBuf, "ACAD"); |
161 | 8.22k | break; |
162 | | |
163 | 8.86k | case 1070: |
164 | 8.86k | if (bReadyForDimstyleOverride) |
165 | 7.30k | { |
166 | | // Store DIMSTYLE override values in the dimension |
167 | | // style property map. The nInnerCode values match the |
168 | | // group codes used in the DIMSTYLE table. |
169 | 7.30k | const int nInnerCode = atoi(szLineBuf); |
170 | 7.30k | const char *pszProperty = |
171 | 7.30k | ACGetDimStylePropertyName(nInnerCode); |
172 | 7.30k | if (pszProperty) |
173 | 6.57k | { |
174 | 6.57k | nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf)); |
175 | 6.57k | if (nCode == 1005 || nCode == 1040 || nCode == 1070) |
176 | 4.88k | oDimStyleProperties[pszProperty] = szLineBuf; |
177 | 6.57k | } |
178 | 7.30k | } |
179 | 8.86k | break; |
180 | | |
181 | 910k | default: |
182 | 910k | TranslateGenericProperty(poFeature, nCode, szLineBuf); |
183 | 910k | break; |
184 | 1.88M | } |
185 | 1.88M | } |
186 | | |
187 | 80.7k | if (nCode == 0) |
188 | 78.7k | poDS->UnreadValue(); |
189 | | |
190 | 80.7k | if (bHaveX && bHaveY && bHaveZ) |
191 | 15.2k | poLine->setPoint(nNumVertices++, dfCurrentX, dfCurrentY, dfCurrentZ); |
192 | | |
193 | | // Unpack the dimension style |
194 | 80.7k | bool bWantExtension = atoi(oDimStyleProperties["DIMTAD"]) > 0; |
195 | 80.7k | double dfTextOffset = CPLAtof(oDimStyleProperties["DIMGAP"]); |
196 | 80.7k | double dfScale = CPLAtof(oDimStyleProperties["DIMSCALE"]); |
197 | 80.7k | double dfArrowheadSize = CPLAtof(oDimStyleProperties["DIMASZ"]); |
198 | 80.7k | int nLeaderColor = atoi(oDimStyleProperties["DIMCLRD"]); |
199 | | // DIMLDRBLK is the entity handle of the BLOCK_RECORD table entry that |
200 | | // corresponds to the arrowhead block. |
201 | 80.7k | CPLString osArrowheadBlockHandle = oDimStyleProperties["DIMLDRBLK"]; |
202 | | |
203 | | // Zero scale has a special meaning which we aren't interested in, |
204 | | // so we can change it to 1.0 |
205 | 80.7k | if (dfScale == 0.0) |
206 | 367 | dfScale = 1.0; |
207 | | |
208 | | // Use the color from the dimension style if it is not ByBlock |
209 | 80.7k | if (nLeaderColor > 0) |
210 | 991 | poFeature->oStyleProperties["Color"] = oDimStyleProperties["DIMCLRD"]; |
211 | | |
212 | | /* -------------------------------------------------------------------- */ |
213 | | /* Add an arrowhead to the start of the leader line. */ |
214 | | /* -------------------------------------------------------------------- */ |
215 | | |
216 | 80.7k | if (bWantArrowhead && nNumVertices >= 2) |
217 | 26.8k | { |
218 | 26.8k | InsertArrowhead(poFeature, osArrowheadBlockHandle, poLine, |
219 | 26.8k | dfArrowheadSize * dfScale); |
220 | 26.8k | } |
221 | | |
222 | 80.7k | if (bHorizontalDirectionFlip) |
223 | 78.6k | { |
224 | 78.6k | dfHorizontalDirectionX *= -1; |
225 | 78.6k | dfHorizontalDirectionX *= -1; |
226 | 78.6k | dfHorizontalDirectionX *= -1; |
227 | 78.6k | } |
228 | | |
229 | | /* -------------------------------------------------------------------- */ |
230 | | /* For a spline leader, determine the end tangent direction */ |
231 | | /* and interpolate the spline vertices. */ |
232 | | /* -------------------------------------------------------------------- */ |
233 | | |
234 | 80.7k | if (bIsSpline) |
235 | 42.0k | { |
236 | 42.0k | DXFTriple oEndTangent; |
237 | 42.0k | if (bHasTextAnnotation) |
238 | 5.47k | { |
239 | 5.47k | oEndTangent = |
240 | 5.47k | DXFTriple(dfHorizontalDirectionX, dfHorizontalDirectionY, |
241 | 5.47k | dfHorizontalDirectionZ); |
242 | 5.47k | } |
243 | 42.0k | InterpolateSpline(poLine, oEndTangent); |
244 | 42.0k | } |
245 | | |
246 | | /* -------------------------------------------------------------------- */ |
247 | | /* Add an extension to the end of the leader line. This is not */ |
248 | | /* properly documented in the DXF spec, but it is needed to */ |
249 | | /* replicate the way AutoCAD displays leader objects. */ |
250 | | /* */ |
251 | | /* When $DIMTAD (77) is nonzero, the leader line is extended */ |
252 | | /* under the text annotation. This extension is not stored as an */ |
253 | | /* additional vertex, so we need to create it ourselves. */ |
254 | | /* -------------------------------------------------------------------- */ |
255 | | |
256 | 80.7k | if (bWantExtension && bHasTextAnnotation && poLine->getNumPoints() >= 2) |
257 | 41 | { |
258 | 41 | OGRPoint oLastVertex; |
259 | 41 | poLine->getPoint(poLine->getNumPoints() - 1, &oLastVertex); |
260 | | |
261 | 41 | double dfExtensionX = oLastVertex.getX(); |
262 | 41 | double dfExtensionY = oLastVertex.getY(); |
263 | 41 | double dfExtensionZ = oLastVertex.getZ(); |
264 | | |
265 | 41 | double dfExtensionLength = |
266 | 41 | (dfTextOffset * dfScale) + dfTextAnnotationWidth; |
267 | 41 | dfExtensionX += dfHorizontalDirectionX * dfExtensionLength; |
268 | 41 | dfExtensionY += dfHorizontalDirectionY * dfExtensionLength; |
269 | 41 | dfExtensionZ += dfHorizontalDirectionZ * dfExtensionLength; |
270 | | |
271 | 41 | poLine->setPoint(poLine->getNumPoints(), dfExtensionX, dfExtensionY, |
272 | 41 | dfExtensionZ); |
273 | 41 | } |
274 | | |
275 | 80.7k | poFeature->SetGeometryDirectly(poLine); |
276 | | |
277 | 80.7k | PrepareLineStyle(poFeature); |
278 | | |
279 | 80.7k | return poFeature; |
280 | 80.7k | } |
281 | | |
282 | | /************************************************************************/ |
283 | | /* DXFMLEADERVertex, DXFMLEADERLeaderLine, DXFMLEADERLeader */ |
284 | | /************************************************************************/ |
285 | | |
286 | | struct DXFMLEADERVertex |
287 | | { |
288 | | DXFTriple oCoords; |
289 | | std::vector<std::pair<DXFTriple, DXFTriple>> aoBreaks; |
290 | | |
291 | 1.05M | DXFMLEADERVertex(double dfX, double dfY) : oCoords(DXFTriple(dfX, dfY, 0.0)) |
292 | 1.05M | { |
293 | 1.05M | } |
294 | | }; |
295 | | |
296 | | struct DXFMLEADERLeader |
297 | | { |
298 | | double dfLandingX = 0; |
299 | | double dfLandingY = 0; |
300 | | double dfDoglegVectorX = 0; |
301 | | double dfDoglegVectorY = 0; |
302 | | double dfDoglegLength = 0; |
303 | | std::vector<std::pair<DXFTriple, DXFTriple>> aoDoglegBreaks; |
304 | | std::vector<std::vector<DXFMLEADERVertex>> aaoLeaderLines; |
305 | | }; |
306 | | |
307 | | /************************************************************************/ |
308 | | /* TranslateMLEADER() */ |
309 | | /************************************************************************/ |
310 | | |
311 | | OGRDXFFeature *OGRDXFLayer::TranslateMLEADER() |
312 | | |
313 | 99.5k | { |
314 | | // The MLEADER line buffer has to be very large, as the text contents |
315 | | // (group code 304) do not wrap and may be arbitrarily long |
316 | 99.5k | char szLineBuf[4096]; |
317 | 99.5k | int nCode = 0; |
318 | | |
319 | | // This is a dummy feature object used to store style properties |
320 | | // and the like. We end up deleting it without returning it |
321 | 99.5k | OGRDXFFeature *poOverallFeature = new OGRDXFFeature(poFeatureDefn); |
322 | | |
323 | 99.5k | DXFMLEADERLeader oLeader; |
324 | 99.5k | std::vector<DXFMLEADERLeader> aoLeaders; |
325 | | |
326 | 99.5k | std::vector<DXFMLEADERVertex> oLeaderLine; |
327 | 99.5k | double dfCurrentX = 0.0; |
328 | 99.5k | double dfCurrentY = 0.0; |
329 | 99.5k | double dfCurrentX2 = 0.0; |
330 | 99.5k | double dfCurrentY2 = 0.0; |
331 | 99.5k | size_t nCurrentVertex = 0; |
332 | | |
333 | 99.5k | double dfScale = 1.0; |
334 | 99.5k | bool bHasDogleg = true; |
335 | 99.5k | CPLString osLeaderColor = "0"; |
336 | | |
337 | 99.5k | CPLString osText; |
338 | 99.5k | CPLString osTextStyleHandle; |
339 | 99.5k | double dfTextX = 0.0; |
340 | 99.5k | double dfTextY = 0.0; |
341 | 99.5k | int nTextAlignment = 1; // 1 = left, 2 = center, 3 = right |
342 | 99.5k | double dfTextAngle = 0.0; |
343 | 99.5k | double dfTextHeight = 4.0; |
344 | | |
345 | 99.5k | CPLString osBlockHandle; |
346 | 99.5k | OGRDXFInsertTransformer oBlockTransformer; |
347 | 99.5k | CPLString osBlockAttributeHandle; |
348 | | // Map of ATTDEF handles to attribute text |
349 | 99.5k | std::map<CPLString, CPLString> oBlockAttributes; |
350 | | |
351 | 99.5k | CPLString osArrowheadBlockHandle; |
352 | 99.5k | double dfArrowheadSize = 4.0; |
353 | | |
354 | | // The different leader line types |
355 | 99.5k | const int MLT_NONE = 0; |
356 | 99.5k | const int MLT_STRAIGHT = 1; |
357 | 99.5k | const int MLT_SPLINE = 2; |
358 | 99.5k | int nLeaderLineType = MLT_STRAIGHT; |
359 | | |
360 | | // Group codes mean different things in different sections of the |
361 | | // MLEADER entity. We need to keep track of the section we are in. |
362 | 99.5k | const int MLS_COMMON = 0; |
363 | 99.5k | const int MLS_CONTEXT_DATA = 1; |
364 | 99.5k | const int MLS_LEADER = 2; |
365 | 99.5k | const int MLS_LEADER_LINE = 3; |
366 | 99.5k | int nSection = MLS_COMMON; |
367 | | |
368 | | // The way the 30x group codes work is missing from the DXF docs. |
369 | | // We assume that the sections are always nested as follows: |
370 | | |
371 | | // ... [this part is identified as MLS_COMMON] |
372 | | // 300 CONTEXT_DATA{ |
373 | | // ... |
374 | | // 302 LEADER{ |
375 | | // ... |
376 | | // 304 LEADER_LINE{ |
377 | | // ... |
378 | | // 305 } |
379 | | // 304 LEADER_LINE{ |
380 | | // ... |
381 | | // 305 } |
382 | | // ... |
383 | | // 303 } |
384 | | // 302 LEADER{ |
385 | | // ... |
386 | | // 303 } |
387 | | // ... |
388 | | // 301 } |
389 | | // ... [MLS_COMMON] |
390 | | |
391 | 3.28M | while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0) |
392 | 3.18M | { |
393 | 3.18M | switch (nSection) |
394 | 3.18M | { |
395 | 551k | case MLS_COMMON: |
396 | 551k | switch (nCode) |
397 | 551k | { |
398 | 49.2k | case 300: |
399 | 49.2k | nSection = MLS_CONTEXT_DATA; |
400 | 49.2k | break; |
401 | | |
402 | 7.38k | case 342: |
403 | | // 342 is the entity handle of the BLOCK_RECORD table |
404 | | // entry that corresponds to the arrowhead block. |
405 | 7.38k | osArrowheadBlockHandle = szLineBuf; |
406 | 7.38k | break; |
407 | | |
408 | 2.66k | case 42: |
409 | | // TODO figure out difference between 42 and 140 for |
410 | | // arrowheadsize |
411 | 2.66k | dfArrowheadSize = CPLAtof(szLineBuf); |
412 | 2.66k | break; |
413 | | |
414 | 34.0k | case 330: |
415 | 34.0k | osBlockAttributeHandle = szLineBuf; |
416 | 34.0k | break; |
417 | | |
418 | 25.2k | case 302: |
419 | 25.2k | if (osBlockAttributeHandle != "") |
420 | 11.9k | { |
421 | 11.9k | oBlockAttributes[osBlockAttributeHandle] = |
422 | 11.9k | TextUnescape(szLineBuf, true); |
423 | 11.9k | osBlockAttributeHandle = ""; |
424 | 11.9k | } |
425 | 25.2k | break; |
426 | | |
427 | 85.1k | case 91: |
428 | 85.1k | osLeaderColor = szLineBuf; |
429 | 85.1k | break; |
430 | | |
431 | 3.87k | case 170: |
432 | 3.87k | nLeaderLineType = atoi(szLineBuf); |
433 | 3.87k | break; |
434 | | |
435 | 1.27k | case 291: |
436 | 1.27k | bHasDogleg = atoi(szLineBuf) != 0; |
437 | 1.27k | break; |
438 | | |
439 | 342k | default: |
440 | 342k | TranslateGenericProperty(poOverallFeature, nCode, |
441 | 342k | szLineBuf); |
442 | 342k | break; |
443 | 551k | } |
444 | 551k | break; |
445 | | |
446 | 551k | case MLS_CONTEXT_DATA: |
447 | 485k | switch (nCode) |
448 | 485k | { |
449 | 3.28k | case 301: |
450 | 3.28k | nSection = MLS_COMMON; |
451 | 3.28k | break; |
452 | | |
453 | 38.7k | case 302: |
454 | 38.7k | nSection = MLS_LEADER; |
455 | 38.7k | break; |
456 | | |
457 | 33.9k | case 304: |
458 | 33.9k | osText = TextUnescape(szLineBuf, true); |
459 | 33.9k | break; |
460 | | |
461 | 11.4k | case 40: |
462 | 11.4k | dfScale = CPLAtof(szLineBuf); |
463 | 11.4k | break; |
464 | | |
465 | 20.1k | case 340: |
466 | | // 340 is the entity handle of the STYLE table entry |
467 | | // that corresponds to the text style. |
468 | 20.1k | osTextStyleHandle = szLineBuf; |
469 | 20.1k | break; |
470 | | |
471 | 10.9k | case 12: |
472 | 10.9k | dfTextX = CPLAtof(szLineBuf); |
473 | 10.9k | break; |
474 | | |
475 | 11.6k | case 22: |
476 | 11.6k | dfTextY = CPLAtof(szLineBuf); |
477 | 11.6k | break; |
478 | | |
479 | 3.89k | case 41: |
480 | 3.89k | dfTextHeight = CPLAtof(szLineBuf); |
481 | 3.89k | break; |
482 | | |
483 | 2.74k | case 42: |
484 | 2.74k | dfTextAngle = CPLAtof(szLineBuf) * 180 / M_PI; |
485 | 2.74k | break; |
486 | | |
487 | 1.24k | case 171: |
488 | 1.24k | nTextAlignment = atoi(szLineBuf); |
489 | 1.24k | break; |
490 | | |
491 | 14.2k | case 341: |
492 | | // 341 is the entity handle of the BLOCK_RECORD table |
493 | | // entry that corresponds to the block content of this |
494 | | // MLEADER. |
495 | 14.2k | osBlockHandle = szLineBuf; |
496 | 14.2k | break; |
497 | | |
498 | 3.83k | case 15: |
499 | 3.83k | oBlockTransformer.dfXOffset = CPLAtof(szLineBuf); |
500 | 3.83k | break; |
501 | | |
502 | 1.01k | case 25: |
503 | 1.01k | oBlockTransformer.dfYOffset = CPLAtof(szLineBuf); |
504 | 1.01k | break; |
505 | | |
506 | 1.69k | case 16: |
507 | 1.69k | oBlockTransformer.dfXScale = CPLAtof(szLineBuf); |
508 | 1.69k | break; |
509 | | |
510 | 1.22k | case 26: |
511 | 1.22k | oBlockTransformer.dfYScale = CPLAtof(szLineBuf); |
512 | 1.22k | break; |
513 | | |
514 | 1.78k | case 46: |
515 | 1.78k | oBlockTransformer.dfAngle = CPLAtof(szLineBuf); |
516 | 1.78k | break; |
517 | 485k | } |
518 | 485k | break; |
519 | | |
520 | 538k | case MLS_LEADER: |
521 | 538k | switch (nCode) |
522 | 538k | { |
523 | 24.4k | case 303: |
524 | 24.4k | nSection = MLS_CONTEXT_DATA; |
525 | 24.4k | aoLeaders.emplace_back(std::move(oLeader)); |
526 | 24.4k | oLeader = DXFMLEADERLeader(); |
527 | 24.4k | break; |
528 | | |
529 | 48.6k | case 304: |
530 | 48.6k | nSection = MLS_LEADER_LINE; |
531 | 48.6k | break; |
532 | | |
533 | 18.3k | case 10: |
534 | 18.3k | oLeader.dfLandingX = CPLAtof(szLineBuf); |
535 | 18.3k | break; |
536 | | |
537 | 31.3k | case 20: |
538 | 31.3k | oLeader.dfLandingY = CPLAtof(szLineBuf); |
539 | 31.3k | break; |
540 | | |
541 | 12.5k | case 11: |
542 | 12.5k | oLeader.dfDoglegVectorX = CPLAtof(szLineBuf); |
543 | 12.5k | break; |
544 | | |
545 | 21.0k | case 21: |
546 | 21.0k | oLeader.dfDoglegVectorY = CPLAtof(szLineBuf); |
547 | 21.0k | break; |
548 | | |
549 | 8.43k | case 12: |
550 | 8.43k | dfCurrentX = CPLAtof(szLineBuf); |
551 | 8.43k | break; |
552 | | |
553 | 8.86k | case 22: |
554 | 8.86k | dfCurrentY = CPLAtof(szLineBuf); |
555 | 8.86k | break; |
556 | | |
557 | 4.33k | case 13: |
558 | 4.33k | dfCurrentX2 = CPLAtof(szLineBuf); |
559 | 4.33k | break; |
560 | | |
561 | 24.0k | case 23: |
562 | 24.0k | dfCurrentY2 = CPLAtof(szLineBuf); |
563 | 24.0k | oLeader.aoDoglegBreaks.push_back(std::make_pair( |
564 | 24.0k | DXFTriple(dfCurrentX, dfCurrentY, 0.0), |
565 | 24.0k | DXFTriple(dfCurrentX2, dfCurrentY2, 0.0))); |
566 | 24.0k | break; |
567 | | |
568 | 16.5k | case 40: |
569 | 16.5k | oLeader.dfDoglegLength = CPLAtof(szLineBuf); |
570 | 16.5k | break; |
571 | 538k | } |
572 | 538k | break; |
573 | | |
574 | 1.60M | case MLS_LEADER_LINE: |
575 | 1.60M | switch (nCode) |
576 | 1.60M | { |
577 | 43.0k | case 305: |
578 | 43.0k | nSection = MLS_LEADER; |
579 | 43.0k | oLeader.aaoLeaderLines.emplace_back( |
580 | 43.0k | std::move(oLeaderLine)); |
581 | 43.0k | oLeaderLine = std::vector<DXFMLEADERVertex>(); |
582 | 43.0k | break; |
583 | | |
584 | 14.1k | case 10: |
585 | 14.1k | dfCurrentX = CPLAtof(szLineBuf); |
586 | 14.1k | break; |
587 | | |
588 | 1.05M | case 20: |
589 | 1.05M | dfCurrentY = CPLAtof(szLineBuf); |
590 | 1.05M | oLeaderLine.push_back( |
591 | 1.05M | DXFMLEADERVertex(dfCurrentX, dfCurrentY)); |
592 | 1.05M | break; |
593 | | |
594 | 4.38k | case 90: |
595 | 4.38k | nCurrentVertex = atoi(szLineBuf); |
596 | 4.38k | if (nCurrentVertex >= oLeaderLine.size()) |
597 | 351 | { |
598 | 351 | CPLError(CE_Warning, CPLE_AppDefined, |
599 | 351 | "Wrong group code 90 in LEADER_LINE: %s", |
600 | 351 | szLineBuf); |
601 | 351 | DXF_LAYER_READER_ERROR(); |
602 | 351 | delete poOverallFeature; |
603 | 351 | return nullptr; |
604 | 351 | } |
605 | 4.03k | break; |
606 | | |
607 | 6.83k | case 11: |
608 | 6.83k | dfCurrentX = CPLAtof(szLineBuf); |
609 | 6.83k | break; |
610 | | |
611 | 51.8k | case 21: |
612 | 51.8k | dfCurrentY = CPLAtof(szLineBuf); |
613 | 51.8k | break; |
614 | | |
615 | 2.99k | case 12: |
616 | 2.99k | dfCurrentX2 = CPLAtof(szLineBuf); |
617 | 2.99k | break; |
618 | | |
619 | 49.3k | case 22: |
620 | 49.3k | if (nCurrentVertex >= oLeaderLine.size()) |
621 | 710 | { |
622 | 710 | CPLError(CE_Warning, CPLE_AppDefined, |
623 | 710 | "Misplaced group code 22 in LEADER_LINE"); |
624 | 710 | DXF_LAYER_READER_ERROR(); |
625 | 710 | delete poOverallFeature; |
626 | 710 | return nullptr; |
627 | 710 | } |
628 | 48.6k | dfCurrentY2 = CPLAtof(szLineBuf); |
629 | 48.6k | oLeaderLine[nCurrentVertex].aoBreaks.push_back( |
630 | 48.6k | std::make_pair( |
631 | 48.6k | DXFTriple(dfCurrentX, dfCurrentY, 0.0), |
632 | 48.6k | DXFTriple(dfCurrentX2, dfCurrentY2, 0.0))); |
633 | 48.6k | break; |
634 | 1.60M | } |
635 | 1.60M | break; |
636 | 3.18M | } |
637 | 3.18M | } |
638 | | |
639 | 98.4k | if (nCode < 0) |
640 | 5.05k | { |
641 | 5.05k | DXF_LAYER_READER_ERROR(); |
642 | 5.05k | delete poOverallFeature; |
643 | 5.05k | return nullptr; |
644 | 5.05k | } |
645 | 93.4k | if (nCode == 0) |
646 | 93.4k | poDS->UnreadValue(); |
647 | | |
648 | | // Convert the block handle to a block name. If there is no block, |
649 | | // osBlockName will remain empty. |
650 | 93.4k | CPLString osBlockName; |
651 | | |
652 | 93.4k | if (osBlockHandle != "") |
653 | 5.68k | osBlockName = poDS->GetBlockNameByRecordHandle(osBlockHandle); |
654 | | |
655 | | /* -------------------------------------------------------------------- */ |
656 | | /* Add the landing and arrowhead onto each leader line, and add */ |
657 | | /* the dogleg, if present, onto the leader. */ |
658 | | /* -------------------------------------------------------------------- */ |
659 | 93.4k | OGRDXFFeature *poLeaderFeature = poOverallFeature->CloneDXFFeature(); |
660 | 93.4k | poLeaderFeature->oStyleProperties["Color"] = osLeaderColor; |
661 | | |
662 | 93.4k | OGRMultiLineString *poMLS = new OGRMultiLineString(); |
663 | | |
664 | | // Arrowheads should be the same color as the leader line. If the leader |
665 | | // line is ByBlock or ByLayer then the arrowhead should be "owned" by the |
666 | | // overall feature for styling purposes. |
667 | 93.4k | OGRDXFFeature *poArrowheadOwningFeature = poLeaderFeature; |
668 | 93.4k | if ((atoi(osLeaderColor) & 0xC2000000) == 0xC0000000) |
669 | 412 | poArrowheadOwningFeature = poOverallFeature; |
670 | | |
671 | 93.4k | for (std::vector<DXFMLEADERLeader>::iterator oIt = aoLeaders.begin(); |
672 | 116k | nLeaderLineType != MLT_NONE && oIt != aoLeaders.end(); ++oIt) |
673 | 23.2k | { |
674 | 23.2k | const bool bLeaderHasDogleg = |
675 | 23.2k | bHasDogleg && nLeaderLineType != MLT_SPLINE && |
676 | 23.2k | oIt->dfDoglegLength != 0.0 && |
677 | 23.2k | (oIt->dfDoglegVectorX != 0.0 || oIt->dfDoglegVectorY != 0.0); |
678 | | |
679 | | // We assume that the dogleg vector in the DXF is a unit vector. |
680 | | // Safe assumption? Who knows. The documentation is so bad. |
681 | 23.2k | const double dfDoglegX = |
682 | 23.2k | oIt->dfLandingX + oIt->dfDoglegVectorX * oIt->dfDoglegLength; |
683 | 23.2k | const double dfDoglegY = |
684 | 23.2k | oIt->dfLandingY + oIt->dfDoglegVectorY * oIt->dfDoglegLength; |
685 | | |
686 | | // When the dogleg is turned off or we are in spline mode, it seems |
687 | | // that the dogleg and landing data are still present in the DXF file, |
688 | | // but they are not supposed to be drawn. |
689 | 23.2k | if (!bHasDogleg || nLeaderLineType == MLT_SPLINE) |
690 | 1.08k | { |
691 | 1.08k | oIt->dfLandingX = dfDoglegX; |
692 | 1.08k | oIt->dfLandingY = dfDoglegY; |
693 | 1.08k | } |
694 | | |
695 | | // Iterate through each leader line |
696 | 23.2k | for (const auto &aoLineVertices : oIt->aaoLeaderLines) |
697 | 39.2k | { |
698 | 39.2k | if (aoLineVertices.empty()) |
699 | 19.7k | continue; |
700 | | |
701 | 19.4k | OGRLineString *poLeaderLine = new OGRLineString(); |
702 | | |
703 | | // Get the first line segment for arrowhead purposes |
704 | 19.4k | poLeaderLine->addPoint(aoLineVertices[0].oCoords.dfX, |
705 | 19.4k | aoLineVertices[0].oCoords.dfY); |
706 | | |
707 | 19.4k | if (aoLineVertices.size() > 1) |
708 | 6.46k | { |
709 | 6.46k | poLeaderLine->addPoint(aoLineVertices[1].oCoords.dfX, |
710 | 6.46k | aoLineVertices[1].oCoords.dfY); |
711 | 6.46k | } |
712 | 12.9k | else |
713 | 12.9k | { |
714 | 12.9k | poLeaderLine->addPoint(oIt->dfLandingX, oIt->dfLandingY); |
715 | 12.9k | } |
716 | | |
717 | | // Add an arrowhead if required |
718 | 19.4k | InsertArrowhead(poArrowheadOwningFeature, osArrowheadBlockHandle, |
719 | 19.4k | poLeaderLine, dfArrowheadSize * dfScale); |
720 | | |
721 | 19.4k | poLeaderLine->setNumPoints(1); |
722 | | |
723 | | // Go through the vertices of the leader line, adding them, |
724 | | // as well as break start and end points, to the linestring. |
725 | 838k | for (size_t iVertex = 0; iVertex < aoLineVertices.size(); iVertex++) |
726 | 818k | { |
727 | 818k | if (iVertex > 0) |
728 | 799k | { |
729 | 799k | poLeaderLine->addPoint(aoLineVertices[iVertex].oCoords.dfX, |
730 | 799k | aoLineVertices[iVertex].oCoords.dfY); |
731 | 799k | } |
732 | | |
733 | | // Breaks are ignored for spline leaders |
734 | 818k | if (nLeaderLineType != MLT_SPLINE) |
735 | 15.8k | { |
736 | 15.8k | for (const auto &oBreak : aoLineVertices[iVertex].aoBreaks) |
737 | 10.5k | { |
738 | 10.5k | poLeaderLine->addPoint(oBreak.first.dfX, |
739 | 10.5k | oBreak.first.dfY); |
740 | | |
741 | 10.5k | poMLS->addGeometryDirectly(poLeaderLine); |
742 | 10.5k | poLeaderLine = new OGRLineString(); |
743 | | |
744 | 10.5k | poLeaderLine->addPoint(oBreak.second.dfX, |
745 | 10.5k | oBreak.second.dfY); |
746 | 10.5k | } |
747 | 15.8k | } |
748 | 818k | } |
749 | | |
750 | | // Add the final vertex (the landing) to the end of the line |
751 | 19.4k | poLeaderLine->addPoint(oIt->dfLandingX, oIt->dfLandingY); |
752 | | |
753 | | // Make the spline geometry for spline leaders |
754 | 19.4k | if (nLeaderLineType == MLT_SPLINE) |
755 | 8.15k | { |
756 | 8.15k | DXFTriple oEndTangent; |
757 | 8.15k | if (osBlockName.empty()) |
758 | 7.78k | { |
759 | 7.78k | oEndTangent = DXFTriple(oIt->dfDoglegVectorX, |
760 | 7.78k | oIt->dfDoglegVectorY, 0); |
761 | 7.78k | } |
762 | 8.15k | InterpolateSpline(poLeaderLine, oEndTangent); |
763 | 8.15k | } |
764 | | |
765 | 19.4k | poMLS->addGeometryDirectly(poLeaderLine); |
766 | 19.4k | } |
767 | | |
768 | | // Add the dogleg as a separate line in the MLS |
769 | 23.2k | if (bLeaderHasDogleg) |
770 | 3.71k | { |
771 | 3.71k | OGRLineString *poDoglegLine = new OGRLineString(); |
772 | 3.71k | poDoglegLine->addPoint(oIt->dfLandingX, oIt->dfLandingY); |
773 | | |
774 | | // Interrupt the dogleg line at breaks |
775 | 3.71k | for (const auto &oBreak : oIt->aoDoglegBreaks) |
776 | 3.78k | { |
777 | 3.78k | poDoglegLine->addPoint(oBreak.first.dfX, oBreak.first.dfY); |
778 | | |
779 | 3.78k | poMLS->addGeometryDirectly(poDoglegLine); |
780 | 3.78k | poDoglegLine = new OGRLineString(); |
781 | | |
782 | 3.78k | poDoglegLine->addPoint(oBreak.second.dfX, oBreak.second.dfY); |
783 | 3.78k | } |
784 | | |
785 | 3.71k | poDoglegLine->addPoint(dfDoglegX, dfDoglegY); |
786 | 3.71k | poMLS->addGeometryDirectly(poDoglegLine); |
787 | 3.71k | } |
788 | 23.2k | } |
789 | | |
790 | 93.4k | poLeaderFeature->SetGeometryDirectly(poMLS); |
791 | | |
792 | 93.4k | PrepareLineStyle(poLeaderFeature, poOverallFeature); |
793 | | |
794 | | /* -------------------------------------------------------------------- */ |
795 | | /* If we have block content, insert that block. */ |
796 | | /* -------------------------------------------------------------------- */ |
797 | | |
798 | 93.4k | if (osBlockName != "") |
799 | 3.06k | { |
800 | 3.06k | oBlockTransformer.dfXScale *= dfScale; |
801 | 3.06k | oBlockTransformer.dfYScale *= dfScale; |
802 | | |
803 | 3.06k | DXFBlockDefinition *poBlock = poDS->LookupBlock(osBlockName); |
804 | | |
805 | 3.06k | std::map<OGRDXFFeature *, CPLString> oBlockAttributeValues; |
806 | | |
807 | | // If we have block attributes and will need to output them, |
808 | | // go through all the features on this block, looking for |
809 | | // ATTDEFs whose handle is in our list of attribute handles |
810 | 3.06k | if (poBlock && !oBlockAttributes.empty() && |
811 | 3.06k | (poDS->InlineBlocks() || |
812 | 1.58k | poOverallFeature->GetFieldIndex("BlockAttributes") != -1)) |
813 | 1.17k | { |
814 | 1.17k | for (std::vector<OGRDXFFeature *>::iterator oIt = |
815 | 1.17k | poBlock->apoFeatures.begin(); |
816 | 82.8k | oIt != poBlock->apoFeatures.end(); ++oIt) |
817 | 81.6k | { |
818 | 81.6k | const char *pszHandle = |
819 | 81.6k | (*oIt)->GetFieldAsString("EntityHandle"); |
820 | | |
821 | 81.6k | if (pszHandle && oBlockAttributes.count(pszHandle) > 0) |
822 | 60.3k | oBlockAttributeValues[*oIt] = oBlockAttributes[pszHandle]; |
823 | 81.6k | } |
824 | 1.17k | } |
825 | | |
826 | 3.06k | OGRDXFFeature *poBlockFeature = poOverallFeature->CloneDXFFeature(); |
827 | | |
828 | | // If not inlining the block, insert a reference and add attributes |
829 | | // to this feature. |
830 | 3.06k | if (!poDS->InlineBlocks()) |
831 | 1.47k | { |
832 | 1.47k | poBlockFeature = InsertBlockReference( |
833 | 1.47k | osBlockName, oBlockTransformer, poBlockFeature); |
834 | | |
835 | 1.47k | if (!oBlockAttributes.empty() && |
836 | 1.47k | poOverallFeature->GetFieldIndex("BlockAttributes") != -1) |
837 | 0 | { |
838 | 0 | std::vector<char *> apszAttribs; |
839 | |
|
840 | 0 | for (std::map<OGRDXFFeature *, CPLString>::iterator oIt = |
841 | 0 | oBlockAttributeValues.begin(); |
842 | 0 | oIt != oBlockAttributeValues.end(); ++oIt) |
843 | 0 | { |
844 | | // Store the attribute tag and the text value as |
845 | | // a space-separated entry in the BlockAttributes field |
846 | 0 | CPLString osAttribString = oIt->first->osAttributeTag; |
847 | 0 | osAttribString += " "; |
848 | 0 | osAttribString += oIt->second; |
849 | |
|
850 | 0 | apszAttribs.push_back( |
851 | 0 | new char[osAttribString.length() + 1]); |
852 | 0 | CPLStrlcpy(apszAttribs.back(), osAttribString.c_str(), |
853 | 0 | osAttribString.length() + 1); |
854 | 0 | } |
855 | |
|
856 | 0 | apszAttribs.push_back(nullptr); |
857 | |
|
858 | 0 | poBlockFeature->SetField("BlockAttributes", &apszAttribs[0]); |
859 | 0 | } |
860 | | |
861 | 1.47k | apoPendingFeatures.push(poBlockFeature); |
862 | 1.47k | } |
863 | 1.59k | else |
864 | 1.59k | { |
865 | | // Insert the block inline. |
866 | 1.59k | OGRDXFFeatureQueue apoExtraFeatures; |
867 | 1.59k | try |
868 | 1.59k | { |
869 | 1.59k | poBlockFeature = InsertBlockInline( |
870 | 1.59k | CPLGetErrorCounter(), osBlockName, oBlockTransformer, |
871 | 1.59k | poBlockFeature, apoExtraFeatures, true, |
872 | 1.59k | poDS->ShouldMergeBlockGeometries()); |
873 | 1.59k | } |
874 | 1.59k | catch (const std::invalid_argument &) |
875 | 1.59k | { |
876 | | // Block doesn't exist |
877 | 210 | delete poBlockFeature; |
878 | 210 | poBlockFeature = nullptr; |
879 | 210 | } |
880 | | |
881 | | // Add the block geometries to the pending feature stack. |
882 | 1.59k | if (poBlockFeature) |
883 | 652 | { |
884 | 652 | apoPendingFeatures.push(poBlockFeature); |
885 | 652 | } |
886 | 75.9k | while (!apoExtraFeatures.empty()) |
887 | 74.3k | { |
888 | 74.3k | apoPendingFeatures.push(apoExtraFeatures.front()); |
889 | 74.3k | apoExtraFeatures.pop(); |
890 | 74.3k | } |
891 | | |
892 | | // Also add any attributes to the pending feature stack. |
893 | 1.59k | for (std::map<OGRDXFFeature *, CPLString>::iterator oIt = |
894 | 1.59k | oBlockAttributeValues.begin(); |
895 | 61.8k | oIt != oBlockAttributeValues.end(); ++oIt) |
896 | 60.3k | { |
897 | 60.3k | OGRDXFFeature *poAttribFeature = oIt->first->CloneDXFFeature(); |
898 | | |
899 | 60.3k | poAttribFeature->SetField("Text", oIt->second); |
900 | | |
901 | | // Replace text in the style string |
902 | 60.3k | const char *poStyleString = poAttribFeature->GetStyleString(); |
903 | 60.3k | if (poStyleString && STARTS_WITH(poStyleString, "LABEL(")) |
904 | 3.00k | { |
905 | 3.00k | CPLString osNewStyle = poStyleString; |
906 | 3.00k | const size_t nTextStartPos = osNewStyle.find(",t:\""); |
907 | 3.00k | if (nTextStartPos != std::string::npos) |
908 | 3.00k | { |
909 | 3.00k | size_t nTextEndPos = nTextStartPos + 4; |
910 | 219k | while (nTextEndPos < osNewStyle.size() && |
911 | 219k | osNewStyle[nTextEndPos] != '\"') |
912 | 216k | { |
913 | 216k | nTextEndPos++; |
914 | 216k | if (osNewStyle[nTextEndPos] == '\\') |
915 | 4.92k | nTextEndPos++; |
916 | 216k | } |
917 | | |
918 | 3.00k | if (nTextEndPos < osNewStyle.size()) |
919 | 2.14k | { |
920 | 2.14k | osNewStyle.replace( |
921 | 2.14k | nTextStartPos + 4, |
922 | 2.14k | nTextEndPos - (nTextStartPos + 4), oIt->second); |
923 | 2.14k | poAttribFeature->SetStyleString(osNewStyle); |
924 | 2.14k | } |
925 | 3.00k | } |
926 | 3.00k | } |
927 | | |
928 | | // The following bits are copied from |
929 | | // OGRDXFLayer::InsertBlockInline |
930 | 60.3k | if (poAttribFeature->GetGeometryRef()) |
931 | 60.0k | { |
932 | 60.0k | poAttribFeature->GetGeometryRef()->transform( |
933 | 60.0k | &oBlockTransformer); |
934 | 60.0k | } |
935 | | |
936 | 60.3k | if (EQUAL(poAttribFeature->GetFieldAsString("Layer"), "0") && |
937 | 60.3k | !EQUAL(poOverallFeature->GetFieldAsString("Layer"), "")) |
938 | 3 | { |
939 | 3 | poAttribFeature->SetField( |
940 | 3 | "Layer", poOverallFeature->GetFieldAsString("Layer")); |
941 | 3 | } |
942 | | |
943 | 60.3k | PrepareFeatureStyle(poAttribFeature, poOverallFeature); |
944 | | |
945 | 60.3k | ACAdjustText(oBlockTransformer.dfAngle * 180 / M_PI, |
946 | 60.3k | oBlockTransformer.dfXScale, |
947 | 60.3k | oBlockTransformer.dfYScale, poAttribFeature); |
948 | | |
949 | 60.3k | if (!EQUAL(poOverallFeature->GetFieldAsString("EntityHandle"), |
950 | 60.3k | "")) |
951 | 264 | { |
952 | 264 | poAttribFeature->SetField( |
953 | 264 | "EntityHandle", |
954 | 264 | poOverallFeature->GetFieldAsString("EntityHandle")); |
955 | 264 | } |
956 | | |
957 | 60.3k | apoPendingFeatures.push(poAttribFeature); |
958 | 60.3k | } |
959 | 1.59k | } |
960 | 3.06k | } |
961 | | |
962 | | /* -------------------------------------------------------------------- */ |
963 | | /* Prepare a new feature to serve as the leader text label */ |
964 | | /* refeature. We will push it onto the layer as a pending */ |
965 | | /* feature for the next feature read. */ |
966 | | /* -------------------------------------------------------------------- */ |
967 | | |
968 | 93.4k | if (osText.empty() || osText == " ") |
969 | 82.2k | { |
970 | 82.2k | delete poOverallFeature; |
971 | 82.2k | return poLeaderFeature; |
972 | 82.2k | } |
973 | | |
974 | 11.2k | OGRDXFFeature *poLabelFeature = poOverallFeature->CloneDXFFeature(); |
975 | | |
976 | 11.2k | poLabelFeature->SetField("Text", osText); |
977 | 11.2k | poLabelFeature->SetGeometryDirectly(new OGRPoint(dfTextX, dfTextY)); |
978 | | |
979 | 11.2k | CPLString osStyle; |
980 | 11.2k | char szBuffer[64]; |
981 | | |
982 | 11.2k | const CPLString osStyleName = |
983 | 11.2k | poDS->GetTextStyleNameByHandle(osTextStyleHandle); |
984 | | |
985 | | // Font name |
986 | 11.2k | osStyle.Printf("LABEL(f:\""); |
987 | | |
988 | | // Preserve legacy behavior of specifying "Arial" as a default font name. |
989 | 11.2k | osStyle += poDS->LookupTextStyleProperty(osStyleName, "Font", "Arial"); |
990 | | |
991 | 11.2k | osStyle += "\""; |
992 | | |
993 | | // Bold, italic |
994 | 11.2k | if (EQUAL(poDS->LookupTextStyleProperty(osStyleName, "Bold", "0"), "1")) |
995 | 291 | { |
996 | 291 | osStyle += ",bo:1"; |
997 | 291 | } |
998 | 11.2k | if (EQUAL(poDS->LookupTextStyleProperty(osStyleName, "Italic", "0"), "1")) |
999 | 291 | { |
1000 | 291 | osStyle += ",it:1"; |
1001 | 291 | } |
1002 | | |
1003 | 11.2k | osStyle += |
1004 | 11.2k | CPLString().Printf(",t:\"%s\",p:%d", osText.c_str(), |
1005 | 11.2k | nTextAlignment + 6); // 7,8,9: vertical align top |
1006 | | |
1007 | 11.2k | if (dfTextAngle != 0.0) |
1008 | 690 | { |
1009 | 690 | CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfTextAngle); |
1010 | 690 | osStyle += CPLString().Printf(",a:%s", szBuffer); |
1011 | 690 | } |
1012 | | |
1013 | 11.2k | if (dfTextHeight != 0.0) |
1014 | 10.7k | { |
1015 | 10.7k | CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfTextHeight); |
1016 | 10.7k | osStyle += CPLString().Printf(",s:%sg", szBuffer); |
1017 | 10.7k | } |
1018 | | |
1019 | 11.2k | const char *pszWidthFactor = |
1020 | 11.2k | poDS->LookupTextStyleProperty(osStyleName, "Width", "1"); |
1021 | 11.2k | if (pszWidthFactor && CPLAtof(pszWidthFactor) != 1.0) |
1022 | 73 | { |
1023 | 73 | CPLsnprintf(szBuffer, sizeof(szBuffer), "%.4g", |
1024 | 73 | CPLAtof(pszWidthFactor) * 100.0); |
1025 | 73 | osStyle += CPLString().Printf(",w:%s", szBuffer); |
1026 | 73 | } |
1027 | | |
1028 | | // Color |
1029 | 11.2k | osStyle += ",c:"; |
1030 | 11.2k | osStyle += poLabelFeature->GetColor(poDS); |
1031 | | |
1032 | 11.2k | osStyle += ")"; |
1033 | | |
1034 | 11.2k | poLabelFeature->SetStyleString(osStyle); |
1035 | | |
1036 | 11.2k | apoPendingFeatures.push(poLabelFeature); |
1037 | | |
1038 | 11.2k | delete poOverallFeature; |
1039 | 11.2k | return poLeaderFeature; |
1040 | 93.4k | } |
1041 | | |
1042 | | /************************************************************************/ |
1043 | | /* GenerateDefaultArrowhead() */ |
1044 | | /* */ |
1045 | | /* Generates the default DWG/DXF arrowhead (a filled triangle */ |
1046 | | /* with a 3:1 aspect ratio) on the end of the line segment */ |
1047 | | /* defined by the two points. */ |
1048 | | /************************************************************************/ |
1049 | | static void GenerateDefaultArrowhead(OGRDXFFeature *const poArrowheadFeature, |
1050 | | const OGRPoint &oPoint1, |
1051 | | const OGRPoint &oPoint2, |
1052 | | const double dfArrowheadScale) |
1053 | | |
1054 | 71.9k | { |
1055 | | // calculate the baseline to be expanded out into arrowheads |
1056 | 71.9k | const double dfParallelPartX = |
1057 | 71.9k | dfArrowheadScale * (oPoint2.getX() - oPoint1.getX()); |
1058 | 71.9k | const double dfParallelPartY = |
1059 | 71.9k | dfArrowheadScale * (oPoint2.getY() - oPoint1.getY()); |
1060 | | // ...and drop a perpendicular |
1061 | 71.9k | const double dfPerpPartX = dfParallelPartY; |
1062 | 71.9k | const double dfPerpPartY = -dfParallelPartX; |
1063 | | |
1064 | 71.9k | OGRLinearRing *poLinearRing = new OGRLinearRing(); |
1065 | 71.9k | poLinearRing->setPoint( |
1066 | 71.9k | 0, oPoint1.getX() + dfParallelPartX + dfPerpPartX / 6, |
1067 | 71.9k | oPoint1.getY() + dfParallelPartY + dfPerpPartY / 6, oPoint1.getZ()); |
1068 | 71.9k | poLinearRing->setPoint(1, oPoint1.getX(), oPoint1.getY(), oPoint1.getZ()); |
1069 | 71.9k | poLinearRing->setPoint( |
1070 | 71.9k | 2, oPoint1.getX() + dfParallelPartX - dfPerpPartX / 6, |
1071 | 71.9k | oPoint1.getY() + dfParallelPartY - dfPerpPartY / 6, oPoint1.getZ()); |
1072 | 71.9k | poLinearRing->closeRings(); |
1073 | | |
1074 | 71.9k | OGRPolygon *poPoly = new OGRPolygon(); |
1075 | 71.9k | poPoly->addRingDirectly(poLinearRing); |
1076 | | |
1077 | 71.9k | poArrowheadFeature->SetGeometryDirectly(poPoly); |
1078 | 71.9k | } |
1079 | | |
1080 | | /************************************************************************/ |
1081 | | /* InsertArrowhead() */ |
1082 | | /* */ |
1083 | | /* Inserts the specified arrowhead block at the start of the */ |
1084 | | /* first segment of the given line string (or the end of the */ |
1085 | | /* last segment if bReverse is false). 2D only. */ |
1086 | | /* */ |
1087 | | /* The first (last) point of the line string may be updated. */ |
1088 | | /************************************************************************/ |
1089 | | void OGRDXFLayer::InsertArrowhead(OGRDXFFeature *const poFeature, |
1090 | | const CPLString &osBlockHandle, |
1091 | | OGRLineString *const poLine, |
1092 | | const double dfArrowheadSize, |
1093 | | const bool bReverse /* = false */) |
1094 | 238k | { |
1095 | 238k | OGRPoint oPoint1, oPoint2; |
1096 | 238k | poLine->getPoint(bReverse ? poLine->getNumPoints() - 1 : 0, &oPoint1); |
1097 | 238k | poLine->getPoint(bReverse ? poLine->getNumPoints() - 2 : 1, &oPoint2); |
1098 | | |
1099 | 238k | const double dfFirstSegmentLength = PointDist( |
1100 | 238k | oPoint1.getX(), oPoint1.getY(), oPoint2.getX(), oPoint2.getY()); |
1101 | | |
1102 | | // AutoCAD only displays an arrowhead if the length of the arrowhead |
1103 | | // is less than or equal to half the length of the line segment |
1104 | 238k | if (dfArrowheadSize == 0.0 || dfFirstSegmentLength == 0.0 || |
1105 | 238k | dfArrowheadSize > 0.5 * dfFirstSegmentLength) |
1106 | 165k | { |
1107 | 165k | return; |
1108 | 165k | } |
1109 | | |
1110 | 72.7k | OGRDXFFeature *poArrowheadFeature = poFeature->CloneDXFFeature(); |
1111 | | |
1112 | | // Convert the block handle to a block name. |
1113 | 72.7k | CPLString osBlockName = ""; |
1114 | | |
1115 | 72.7k | if (osBlockHandle != "") |
1116 | 5.83k | osBlockName = poDS->GetBlockNameByRecordHandle(osBlockHandle); |
1117 | | |
1118 | 72.7k | OGRDXFFeatureQueue apoExtraFeatures; |
1119 | | |
1120 | | // If the block doesn't exist, we need to fall back to the |
1121 | | // default arrowhead. |
1122 | 72.7k | if (osBlockName == "") |
1123 | 71.9k | { |
1124 | 71.9k | GenerateDefaultArrowhead(poArrowheadFeature, oPoint1, oPoint2, |
1125 | 71.9k | dfArrowheadSize / dfFirstSegmentLength); |
1126 | | |
1127 | 71.9k | PrepareBrushStyle(poArrowheadFeature); |
1128 | 71.9k | } |
1129 | 796 | else |
1130 | 796 | { |
1131 | | // Build a transformer to insert the arrowhead block with the |
1132 | | // required location, angle and scale. |
1133 | 796 | OGRDXFInsertTransformer oTransformer; |
1134 | 796 | oTransformer.dfXOffset = oPoint1.getX(); |
1135 | 796 | oTransformer.dfYOffset = oPoint1.getY(); |
1136 | 796 | oTransformer.dfZOffset = oPoint1.getZ(); |
1137 | | // Arrowhead blocks always point to the right (--->) |
1138 | 796 | oTransformer.dfAngle = atan2(oPoint2.getY() - oPoint1.getY(), |
1139 | 796 | oPoint2.getX() - oPoint1.getX()) + |
1140 | 796 | M_PI; |
1141 | 796 | oTransformer.dfXScale = oTransformer.dfYScale = oTransformer.dfZScale = |
1142 | 796 | dfArrowheadSize; |
1143 | | |
1144 | | // Insert the block. |
1145 | 796 | try |
1146 | 796 | { |
1147 | 796 | poArrowheadFeature = InsertBlockInline( |
1148 | 796 | CPLGetErrorCounter(), osBlockName, std::move(oTransformer), |
1149 | 796 | poArrowheadFeature, apoExtraFeatures, true, false); |
1150 | 796 | } |
1151 | 796 | catch (const std::invalid_argument &) |
1152 | 796 | { |
1153 | | // Supposedly the block doesn't exist. But what has probably |
1154 | | // happened is that the block exists in the DXF, but it contains |
1155 | | // no entities, so the data source didn't read it in. |
1156 | | // In this case, no arrowhead is required. |
1157 | 291 | delete poArrowheadFeature; |
1158 | 291 | poArrowheadFeature = nullptr; |
1159 | 291 | } |
1160 | 796 | } |
1161 | | |
1162 | | // Add the arrowhead geometries to the pending feature stack. |
1163 | 72.7k | if (poArrowheadFeature) |
1164 | 71.9k | { |
1165 | 71.9k | apoPendingFeatures.push(poArrowheadFeature); |
1166 | 71.9k | } |
1167 | 113k | while (!apoExtraFeatures.empty()) |
1168 | 40.9k | { |
1169 | 40.9k | apoPendingFeatures.push(apoExtraFeatures.front()); |
1170 | 40.9k | apoExtraFeatures.pop(); |
1171 | 40.9k | } |
1172 | | |
1173 | | // Move the endpoint of the line out of the way of the arrowhead. |
1174 | | // We assume that arrowheads are 1 unit long, except for a list |
1175 | | // of specific block names which are treated as having no length |
1176 | | |
1177 | 72.7k | static const char *apszSpecialArrowheads[] = { |
1178 | 72.7k | "_ArchTick", "_DotSmall", "_Integral", "_None", "_Oblique", "_Small"}; |
1179 | | |
1180 | 72.7k | if (std::find(apszSpecialArrowheads, apszSpecialArrowheads + 6, |
1181 | 72.7k | osBlockName) == (apszSpecialArrowheads + 6)) |
1182 | 72.3k | { |
1183 | 72.3k | oPoint1.setX(oPoint1.getX() + dfArrowheadSize * |
1184 | 72.3k | (oPoint2.getX() - oPoint1.getX()) / |
1185 | 72.3k | dfFirstSegmentLength); |
1186 | 72.3k | oPoint1.setY(oPoint1.getY() + dfArrowheadSize * |
1187 | 72.3k | (oPoint2.getY() - oPoint1.getY()) / |
1188 | 72.3k | dfFirstSegmentLength); |
1189 | | |
1190 | 72.3k | poLine->setPoint(bReverse ? poLine->getNumPoints() - 1 : 0, &oPoint1); |
1191 | 72.3k | } |
1192 | 72.7k | } |
1193 | | |
1194 | | /************************************************************************/ |
1195 | | /* basis(), rbspline2() */ |
1196 | | /* */ |
1197 | | /* Spline calculation functions defined in intronurbs.cpp. */ |
1198 | | /************************************************************************/ |
1199 | | void basis(int c, double t, int npts, double x[], double N[]); |
1200 | | void rbspline2(int npts, int k, int p1, double b[], double h[], |
1201 | | bool bCalculateKnots, double x[], double p[]); |
1202 | | |
1203 | | #if defined(__GNUC__) && __GNUC__ >= 6 |
1204 | | #pragma GCC diagnostic push |
1205 | | #pragma GCC diagnostic ignored "-Wnull-dereference" |
1206 | | #endif |
1207 | | |
1208 | | namespace |
1209 | | { |
1210 | | inline void setRow(GDALMatrix &m, int row, DXFTriple const &t) |
1211 | 155k | { |
1212 | 155k | m(row, 0) = t.dfX; |
1213 | 155k | m(row, 1) = t.dfY; |
1214 | 155k | m(row, 2) = t.dfZ; |
1215 | 155k | } |
1216 | | } // namespace |
1217 | | |
1218 | | /************************************************************************/ |
1219 | | /* GetBSplineControlPoints() */ |
1220 | | /* */ |
1221 | | /* Evaluates the control points for the B-spline of given degree */ |
1222 | | /* that interpolates the given data points, using the given */ |
1223 | | /* parameters, start tangent and end tangent. The parameters */ |
1224 | | /* and knot vector must be increasing sequences with first */ |
1225 | | /* element 0 and last element 1. Given n data points, there */ |
1226 | | /* must be n parameters and n + nDegree + 3 knots. */ |
1227 | | /* */ |
1228 | | /* It is recommended to match AutoCAD by generating a knot */ |
1229 | | /* vector from the parameters as follows: */ |
1230 | | /* 0 0 ... 0 adfParameters 1 1 ... 1 */ |
1231 | | /* (nDegree zeros) (nDegree ones) */ |
1232 | | /* To fully match AutoCAD's behavior, a chord-length */ |
1233 | | /* parameterisation should be used, and the start and end */ |
1234 | | /* tangent vectors should be multiplied by the total chord */ |
1235 | | /* length of all chords. */ |
1236 | | /* */ |
1237 | | /* Reference: Piegl, L., Tiller, W. (1995), The NURBS Book, */ |
1238 | | /* 2nd ed. (Springer), sections 2.2 and 9.2. */ |
1239 | | /* Although this book contains implementations of algorithms, */ |
1240 | | /* this function is an original implementation based on the */ |
1241 | | /* concepts discussed in the book and was written without */ |
1242 | | /* reference to Piegl and Tiller's implementations. */ |
1243 | | /************************************************************************/ |
1244 | | static std::vector<DXFTriple> |
1245 | | GetBSplineControlPoints(const std::vector<double> &adfParameters, |
1246 | | const std::vector<double> &adfKnots, |
1247 | | const std::vector<DXFTriple> &aoDataPoints, |
1248 | | const int nDegree, DXFTriple oStartTangent, |
1249 | | DXFTriple oEndTangent) |
1250 | 28.7k | { |
1251 | 28.7k | CPLAssert(nDegree > 1); |
1252 | | |
1253 | | // Count the number of data points |
1254 | | // Note: The literature often sets n to one less than the number of data |
1255 | | // points for some reason, but we don't do that here |
1256 | 28.7k | const int nPoints = static_cast<int>(aoDataPoints.size()); |
1257 | | |
1258 | 28.7k | CPLAssert(nPoints > 0); |
1259 | 28.7k | CPLAssert(nPoints == static_cast<int>(adfParameters.size())); |
1260 | | |
1261 | | // RAM consumption is quadratic in the number of control points. |
1262 | 28.7k | if (nPoints > |
1263 | 28.7k | atoi(CPLGetConfigOption("DXF_MAX_BSPLINE_CONTROL_POINTS", "2000"))) |
1264 | 138 | { |
1265 | 138 | CPLError(CE_Failure, CPLE_AppDefined, |
1266 | 138 | "Too many control points (%d) for spline leader. " |
1267 | 138 | "Set DXF_MAX_BSPLINE_CONTROL_POINTS configuration " |
1268 | 138 | "option to a higher value to remove this limitation " |
1269 | 138 | "(at the cost of significant RAM consumption)", |
1270 | 138 | nPoints); |
1271 | 138 | return std::vector<DXFTriple>(); |
1272 | 138 | } |
1273 | | |
1274 | | // We want to solve the linear system NP=D for P, where N is a coefficient |
1275 | | // matrix made up of values of the basis functions at each parameter |
1276 | | // value, with two additional rows for the endpoint tangent information. |
1277 | | // Each row relates to a different parameter. |
1278 | | |
1279 | | // Set up D as a matrix consisting initially of the data points |
1280 | 28.6k | GDALMatrix D(nPoints + 2, 3); |
1281 | | |
1282 | 28.6k | setRow(D, 0, aoDataPoints[0]); |
1283 | 69.4k | for (int iIndex = 1; iIndex < nPoints - 1; iIndex++) |
1284 | 40.8k | setRow(D, iIndex + 1, aoDataPoints[iIndex]); |
1285 | 28.6k | setRow(D, nPoints + 1, aoDataPoints[nPoints - 1]); |
1286 | | |
1287 | 28.6k | const double dfStartMultiplier = adfKnots[nDegree + 1] / nDegree; |
1288 | 28.6k | oStartTangent *= dfStartMultiplier; |
1289 | 28.6k | setRow(D, 1, oStartTangent); |
1290 | | |
1291 | 28.6k | const double dfEndMultiplier = (1.0 - adfKnots[nPoints + 1]) / nDegree; |
1292 | 28.6k | oEndTangent *= dfEndMultiplier; |
1293 | 28.6k | setRow(D, nPoints, oEndTangent); |
1294 | | |
1295 | 28.6k | GDALMatrix N(nPoints + 2, nPoints + 2); |
1296 | | // First control point will be the first data point |
1297 | 28.6k | N(0, 0) = 1.0; |
1298 | | |
1299 | | // Start tangent determines the second control point |
1300 | 28.6k | N(1, 0) = -1.0; |
1301 | 28.6k | N(1, 1) = 1.0; |
1302 | | |
1303 | | // Fill the middle rows of the matrix with basis function values. We |
1304 | | // have to use a temporary vector, because intronurbs' basis function |
1305 | | // requires an additional nDegree entries for temporary storage. |
1306 | 28.6k | std::vector<double> adfTempRow(nPoints + 2 + nDegree, 0.0); |
1307 | 69.4k | for (int iRow = 2; iRow < nPoints; iRow++) |
1308 | 40.8k | { |
1309 | 40.8k | basis(nDegree + 1, adfParameters[iRow - 1], nPoints + 2, |
1310 | 40.8k | const_cast<double *>(&adfKnots[0]) - 1, &adfTempRow[0] - 1); |
1311 | 1.06M | for (int iCol = 0; iCol < nPoints + 2; ++iCol) |
1312 | 1.02M | N(iRow, iCol) = adfTempRow[iCol]; |
1313 | 40.8k | } |
1314 | | |
1315 | | // End tangent determines the second-last control point |
1316 | 28.6k | N(nPoints, nPoints) = -1.0; |
1317 | 28.6k | N(nPoints, nPoints + 1) = 1.0; |
1318 | | |
1319 | | // Last control point will be the last data point |
1320 | 28.6k | N(nPoints + 1, nPoints + 1) = 1.0; |
1321 | | |
1322 | | // Solve the linear system |
1323 | 28.6k | GDALMatrix P(nPoints + 2, 3); |
1324 | 28.6k | GDALLinearSystemSolve(N, D, P); |
1325 | | |
1326 | 28.6k | std::vector<DXFTriple> aoControlPoints(nPoints + 2); |
1327 | 183k | for (int iRow = 0; iRow < nPoints + 2; iRow++) |
1328 | 155k | { |
1329 | 155k | aoControlPoints[iRow].dfX = P(iRow, 0); |
1330 | 155k | aoControlPoints[iRow].dfY = P(iRow, 1); |
1331 | 155k | aoControlPoints[iRow].dfZ = P(iRow, 2); |
1332 | 155k | } |
1333 | | |
1334 | 28.6k | return aoControlPoints; |
1335 | 28.7k | } |
1336 | | |
1337 | | #if defined(__GNUC__) && __GNUC__ >= 6 |
1338 | | #pragma GCC diagnostic pop |
1339 | | #endif |
1340 | | |
1341 | | /************************************************************************/ |
1342 | | /* InterpolateSpline() */ |
1343 | | /* */ |
1344 | | /* Interpolates a cubic spline between the data points of the */ |
1345 | | /* given line string. The line string is updated with the new */ |
1346 | | /* spline geometry. */ |
1347 | | /* */ |
1348 | | /* If an end tangent of (0,0,0) is given, the direction vector */ |
1349 | | /* of the last chord (line segment) is used. */ |
1350 | | /************************************************************************/ |
1351 | | static void InterpolateSpline(OGRLineString *const poLine, |
1352 | | const DXFTriple &oEndTangentDirection) |
1353 | 50.2k | { |
1354 | 50.2k | int nDataPoints = static_cast<int>(poLine->getNumPoints()); |
1355 | 50.2k | if (nDataPoints < 2) |
1356 | 17.4k | return; |
1357 | | |
1358 | | // Transfer line vertices into DXFTriple objects |
1359 | 32.8k | std::vector<DXFTriple> aoDataPoints; |
1360 | 32.8k | OGRPoint oPrevPoint; |
1361 | 944k | for (int iIndex = 0; iIndex < nDataPoints; iIndex++) |
1362 | 911k | { |
1363 | 911k | OGRPoint oPoint; |
1364 | 911k | poLine->getPoint(iIndex, &oPoint); |
1365 | | |
1366 | | // Remove sequential duplicate points |
1367 | 911k | if (iIndex > 0 && oPrevPoint.Equals(&oPoint)) |
1368 | 357k | continue; |
1369 | | |
1370 | 554k | aoDataPoints.push_back( |
1371 | 554k | DXFTriple(oPoint.getX(), oPoint.getY(), oPoint.getZ())); |
1372 | 554k | oPrevPoint = std::move(oPoint); |
1373 | 554k | } |
1374 | 32.8k | nDataPoints = static_cast<int>(aoDataPoints.size()); |
1375 | 32.8k | if (nDataPoints < 2) |
1376 | 3.17k | return; |
1377 | | |
1378 | | // Work out the chord length parameterisation |
1379 | 29.6k | std::vector<double> adfParameters; |
1380 | 29.6k | adfParameters.push_back(0.0); |
1381 | 548k | for (int iIndex = 1; iIndex < nDataPoints; iIndex++) |
1382 | 520k | { |
1383 | 520k | const double dfParameter = |
1384 | 520k | adfParameters[iIndex - 1] + |
1385 | 520k | PointDist(aoDataPoints[iIndex - 1].dfX, |
1386 | 520k | aoDataPoints[iIndex - 1].dfY, |
1387 | 520k | aoDataPoints[iIndex - 1].dfZ, aoDataPoints[iIndex].dfX, |
1388 | 520k | aoDataPoints[iIndex].dfY, aoDataPoints[iIndex].dfZ); |
1389 | | |
1390 | | // Bail out in pathological cases. This will happen when |
1391 | | // some lengths are very large (above 10^16) and others are |
1392 | | // very small (such as 1) |
1393 | 520k | if (dfParameter == adfParameters[iIndex - 1]) |
1394 | 879 | return; |
1395 | | |
1396 | 519k | adfParameters.push_back(dfParameter); |
1397 | 519k | } |
1398 | | |
1399 | 28.7k | const double dfTotalChordLength = adfParameters.back(); |
1400 | | |
1401 | | // Start tangent can be worked out from the first chord |
1402 | 28.7k | DXFTriple oStartTangent(aoDataPoints[1].dfX - aoDataPoints[0].dfX, |
1403 | 28.7k | aoDataPoints[1].dfY - aoDataPoints[0].dfY, |
1404 | 28.7k | aoDataPoints[1].dfZ - aoDataPoints[0].dfZ); |
1405 | 28.7k | oStartTangent *= dfTotalChordLength / adfParameters[1]; |
1406 | | |
1407 | | // If end tangent is zero, it is worked out from the last chord |
1408 | 28.7k | DXFTriple oEndTangent = oEndTangentDirection; |
1409 | 28.7k | if (oEndTangent.dfX == 0.0 && oEndTangent.dfY == 0.0 && |
1410 | 28.7k | oEndTangent.dfZ == 0.0) |
1411 | 23.5k | { |
1412 | 23.5k | oEndTangent = DXFTriple(aoDataPoints[nDataPoints - 1].dfX - |
1413 | 23.5k | aoDataPoints[nDataPoints - 2].dfX, |
1414 | 23.5k | aoDataPoints[nDataPoints - 1].dfY - |
1415 | 23.5k | aoDataPoints[nDataPoints - 2].dfY, |
1416 | 23.5k | aoDataPoints[nDataPoints - 1].dfZ - |
1417 | 23.5k | aoDataPoints[nDataPoints - 2].dfZ); |
1418 | 23.5k | oEndTangent /= dfTotalChordLength - adfParameters[nDataPoints - 2]; |
1419 | 23.5k | } |
1420 | | |
1421 | | // End tangent direction is multiplied by total chord length |
1422 | 28.7k | oEndTangent *= dfTotalChordLength; |
1423 | | |
1424 | | // Normalise the parameter vector |
1425 | 546k | for (int iIndex = 1; iIndex < nDataPoints; iIndex++) |
1426 | 517k | adfParameters[iIndex] /= dfTotalChordLength; |
1427 | | |
1428 | | // Generate a knot vector |
1429 | 28.7k | const int nDegree = 3; |
1430 | 28.7k | std::vector<double> adfKnots(aoDataPoints.size() + nDegree + 3, 0.0); |
1431 | 28.7k | std::copy(adfParameters.begin(), adfParameters.end(), |
1432 | 28.7k | adfKnots.begin() + nDegree); |
1433 | 28.7k | std::fill(adfKnots.end() - nDegree, adfKnots.end(), 1.0); |
1434 | | |
1435 | | // Calculate the spline control points |
1436 | 28.7k | std::vector<DXFTriple> aoControlPoints = |
1437 | 28.7k | GetBSplineControlPoints(adfParameters, adfKnots, aoDataPoints, nDegree, |
1438 | 28.7k | oStartTangent, oEndTangent); |
1439 | 28.7k | const int nControlPoints = static_cast<int>(aoControlPoints.size()); |
1440 | | |
1441 | 28.7k | if (nControlPoints == 0) |
1442 | 138 | return; |
1443 | | |
1444 | | // Interpolate the spline using the intronurbs code |
1445 | 28.6k | int nWantedPoints = nControlPoints * 8; |
1446 | 28.6k | std::vector<double> adfWeights(nControlPoints, 1.0); |
1447 | 28.6k | std::vector<double> adfPoints(3 * nWantedPoints, 0.0); |
1448 | | |
1449 | 28.6k | rbspline2(nControlPoints, nDegree + 1, nWantedPoints, |
1450 | 28.6k | reinterpret_cast<double *>(&aoControlPoints[0]) - 1, |
1451 | 28.6k | &adfWeights[0] - 1, false, &adfKnots[0] - 1, &adfPoints[0] - 1); |
1452 | | |
1453 | | // Preserve 2D/3D status as we add the interpolated points to the line |
1454 | 28.6k | const int bIs3D = poLine->Is3D(); |
1455 | 28.6k | poLine->empty(); |
1456 | 1.27M | for (int iIndex = 0; iIndex < nWantedPoints; iIndex++) |
1457 | 1.24M | { |
1458 | 1.24M | poLine->addPoint(adfPoints[iIndex * 3], adfPoints[iIndex * 3 + 1], |
1459 | 1.24M | adfPoints[iIndex * 3 + 2]); |
1460 | 1.24M | } |
1461 | 28.6k | if (!bIs3D) |
1462 | 7.30k | poLine->flattenTo2D(); |
1463 | 28.6k | } |