Line data Source code
1 : /******************************************************************************
2 : * File: ogrdxf_polyline_smooth.cpp
3 : *
4 : * Project: Interpolation support for smooth POLYLINE and LWPOLYLINE entities.
5 : * Purpose: Implementation of classes for OGR .dxf driver.
6 : * Author: TJ Snider, timsn@thtree.com
7 : * Ray Gardener, Daylon Graphics Ltd.
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2010 Daylon Graphics Ltd.
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "stdlib.h"
16 : #include "math.h"
17 : #include "ogrdxf_polyline_smooth.h"
18 :
19 : /************************************************************************/
20 : /* Local helper functions */
21 : /************************************************************************/
22 :
23 18 : static double GetRadius(double bulge, double length)
24 : {
25 18 : const double h = (bulge * length) / 2;
26 18 : return (h / 2) + (length * length / (8 * h));
27 : }
28 :
29 228 : static double GetLength(const DXFSmoothPolylineVertex &start,
30 : const DXFSmoothPolylineVertex &end)
31 : {
32 228 : return sqrt(pow(end.x - start.x, 2) + pow(end.y - start.y, 2));
33 : }
34 :
35 36 : static double GetAngle(const DXFSmoothPolylineVertex &start,
36 : const DXFSmoothPolylineVertex &end)
37 : {
38 36 : return atan2((start.y - end.y), (start.x - end.x)) * 180.0 / M_PI;
39 : }
40 :
41 36 : static double GetOGRangle(double angle)
42 : {
43 36 : return angle > 0.0 ? -(angle - 180.0) : -(angle + 180.0);
44 : }
45 :
46 : /************************************************************************/
47 : /* DXFSmoothPolyline::Tessellate() */
48 : /************************************************************************/
49 :
50 72 : OGRGeometry *DXFSmoothPolyline::Tessellate(bool bAsPolygon) const
51 : {
52 72 : assert(!m_vertices.empty());
53 :
54 : /* -------------------------------------------------------------------- */
55 : /* If polyline is one vertex, convert it to a point */
56 : /* -------------------------------------------------------------------- */
57 :
58 72 : if (m_vertices.size() == 1)
59 : {
60 : OGRPoint *poPt =
61 0 : new OGRPoint(m_vertices[0].x, m_vertices[0].y, m_vertices[0].z);
62 0 : if (m_vertices[0].z == 0 || m_dim == 2)
63 0 : poPt->flattenTo2D();
64 0 : return poPt;
65 : }
66 :
67 : /* -------------------------------------------------------------------- */
68 : /* Otherwise, presume a line string */
69 : /* -------------------------------------------------------------------- */
70 :
71 72 : OGRLinearRing *poLR = m_bClosed && bAsPolygon ? new OGRLinearRing : nullptr;
72 72 : OGRLineString *poLS = poLR ? poLR : new OGRLineString;
73 :
74 72 : m_blinestringstarted = false;
75 :
76 : std::vector<DXFSmoothPolylineVertex>::const_iterator oIter =
77 72 : m_vertices.begin();
78 : std::vector<DXFSmoothPolylineVertex>::const_iterator oEndIter =
79 72 : m_vertices.end();
80 :
81 72 : --oEndIter;
82 :
83 72 : DXFSmoothPolylineVertex begin = *oIter;
84 :
85 300 : while (oIter != oEndIter)
86 : {
87 228 : ++oIter;
88 228 : DXFSmoothPolylineVertex end = *oIter;
89 :
90 228 : const double len = GetLength(begin, end);
91 :
92 : // Don't bother handling bulge for non-constant Z
93 228 : if (len == 0 || begin.bulge == 0 || begin.z != end.z)
94 : {
95 210 : EmitLine(begin, end, poLS);
96 : }
97 : else
98 : {
99 18 : const double radius = GetRadius(begin.bulge, len);
100 18 : EmitArc(begin, end, radius, len, begin.bulge, poLS, begin.z);
101 : }
102 :
103 : // Move to next vertex
104 228 : begin = end;
105 : }
106 :
107 : /* -------------------------------------------------------------------- */
108 : /* Flatten to 2D if necessary */
109 : /* -------------------------------------------------------------------- */
110 :
111 72 : if (m_dim == 2)
112 46 : poLS->flattenTo2D();
113 :
114 72 : if (poLR)
115 : {
116 : // Wrap as polygon.
117 1 : OGRPolygon *poPoly = new OGRPolygon();
118 1 : poPoly->addRingDirectly(poLR);
119 :
120 1 : return poPoly;
121 : }
122 :
123 71 : return poLS;
124 : }
125 :
126 : /************************************************************************/
127 : /* DXFSmoothPolyline::EmitArc() */
128 : /************************************************************************/
129 :
130 18 : void DXFSmoothPolyline::EmitArc(const DXFSmoothPolylineVertex &start,
131 : const DXFSmoothPolylineVertex &end,
132 : double radius, double len, double bulge,
133 : OGRLineString *poLS, double dfZ) const
134 : {
135 18 : assert(poLS);
136 :
137 18 : double ogrArcRotation = 0.0;
138 18 : const double ogrArcRadius = fabs(radius);
139 :
140 : /* -------------------------------------------------------------------- */
141 : /* Set arc's direction and keep bulge positive */
142 : /* -------------------------------------------------------------------- */
143 :
144 18 : const bool bClockwise = (bulge < 0);
145 :
146 18 : if (bClockwise)
147 6 : bulge *= -1;
148 :
149 : /* -------------------------------------------------------------------- */
150 : /* Get arc's center point */
151 : /* -------------------------------------------------------------------- */
152 :
153 18 : const double saggita = fabs(bulge * (len / 2.0));
154 18 : const double apo =
155 18 : bClockwise ? -(ogrArcRadius - saggita) : -(saggita - ogrArcRadius);
156 :
157 18 : DXFSmoothPolylineVertex v;
158 18 : v.x = start.x - end.x;
159 18 : v.y = start.y - end.y;
160 :
161 : #ifdef notdef
162 : const bool bMathissue = (v.x == 0.0 || v.y == 0.0);
163 : #endif
164 :
165 18 : DXFSmoothPolylineVertex midpoint;
166 18 : midpoint.x = end.x + 0.5 * v.x;
167 18 : midpoint.y = end.y + 0.5 * v.y;
168 :
169 18 : DXFSmoothPolylineVertex pperp;
170 18 : pperp.x = v.y;
171 18 : pperp.y = -v.x;
172 18 : pperp.normalize();
173 :
174 18 : DXFSmoothPolylineVertex ogrArcCenter;
175 18 : ogrArcCenter.x = midpoint.x + (pperp.x * apo);
176 18 : ogrArcCenter.y = midpoint.y + (pperp.y * apo);
177 :
178 : /* -------------------------------------------------------------------- */
179 : /* Get the line's general vertical direction (-1 = down, +1 = up) */
180 : /* -------------------------------------------------------------------- */
181 :
182 18 : const double linedir = end.y > start.y ? 1.0 : -1.0;
183 :
184 : /* -------------------------------------------------------------------- */
185 : /* Get arc's starting angle. */
186 : /* -------------------------------------------------------------------- */
187 :
188 18 : double a = GetAngle(ogrArcCenter, start);
189 :
190 18 : if (bClockwise && (linedir == 1.0))
191 3 : a += (linedir * 180.0);
192 :
193 18 : double ogrArcStartAngle = GetOGRangle(a);
194 :
195 : /* -------------------------------------------------------------------- */
196 : /* Get arc's ending angle. */
197 : /* -------------------------------------------------------------------- */
198 :
199 18 : a = GetAngle(ogrArcCenter, end);
200 :
201 18 : if (bClockwise && (linedir == 1.0))
202 3 : a += (linedir * 180.0);
203 :
204 18 : double ogrArcEndAngle = GetOGRangle(a);
205 :
206 18 : if (!bClockwise && (ogrArcStartAngle < ogrArcEndAngle))
207 2 : ogrArcEndAngle = -180.0 + (linedir * a);
208 :
209 18 : if (bClockwise && (ogrArcStartAngle > ogrArcEndAngle))
210 0 : ogrArcEndAngle += 360.0;
211 :
212 : /* -------------------------------------------------------------------- */
213 : /* Flip arc's rotation if necessary. */
214 : /* -------------------------------------------------------------------- */
215 :
216 18 : if (bClockwise && (linedir == 1.0))
217 3 : ogrArcRotation = linedir * 180.0;
218 :
219 : /* -------------------------------------------------------------------- */
220 : /* Tessellate the arc segment and append to the linestring. */
221 : /* -------------------------------------------------------------------- */
222 :
223 18 : if (fabs(ogrArcEndAngle - ogrArcStartAngle) <= 361.0)
224 : {
225 : auto poArc = std::unique_ptr<OGRLineString>(
226 : OGRGeometryFactory::approximateArcAngles(
227 : ogrArcCenter.x, ogrArcCenter.y, dfZ, ogrArcRadius, ogrArcRadius,
228 : ogrArcRotation, ogrArcStartAngle, ogrArcEndAngle, 0.0,
229 18 : m_bUseMaxGapWhenTessellatingArcs)
230 36 : ->toLineString());
231 :
232 : // Make sure extremities exactly match input start and end point.
233 : // This is in particular important if the polyline is closed.
234 18 : if (poArc->getNumPoints() >= 2)
235 : {
236 18 : poArc->setPoint(0, start.x, start.y);
237 18 : poArc->setPoint(poArc->getNumPoints() - 1, end.x, end.y);
238 : }
239 :
240 18 : poLS->addSubLineString(poArc.get());
241 : }
242 : else
243 : {
244 : // TODO: emit error ?
245 : }
246 18 : }
247 :
248 : /************************************************************************/
249 : /* DXFSmoothPolyline::EmitLine() */
250 : /************************************************************************/
251 :
252 210 : void DXFSmoothPolyline::EmitLine(const DXFSmoothPolylineVertex &start,
253 : const DXFSmoothPolylineVertex &end,
254 : OGRLineString *poLS) const
255 : {
256 210 : assert(poLS);
257 :
258 210 : if (!m_blinestringstarted)
259 : {
260 72 : poLS->addPoint(start.x, start.y, start.z);
261 72 : m_blinestringstarted = true;
262 : }
263 :
264 210 : poLS->addPoint(end.x, end.y, end.z);
265 210 : }
266 :
267 : /************************************************************************/
268 : /* DXFSmoothPolyline::Close() */
269 : /************************************************************************/
270 :
271 43 : void DXFSmoothPolyline::Close()
272 : {
273 43 : assert(!m_bClosed);
274 :
275 43 : if (m_vertices.size() >= 2)
276 : {
277 : const bool bVisuallyClosed =
278 43 : (m_vertices.back().shares_2D_pos(m_vertices[0]));
279 :
280 43 : if (!bVisuallyClosed)
281 : {
282 35 : m_vertices.push_back(m_vertices[0]);
283 : }
284 43 : m_bClosed = true;
285 : }
286 43 : }
|