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 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : ****************************************************************************/
30 :
31 : #include "stdlib.h"
32 : #include "math.h"
33 : #include "ogrdxf_polyline_smooth.h"
34 :
35 : /************************************************************************/
36 : /* Local helper functions */
37 : /************************************************************************/
38 :
39 12 : static double GetRadius(double bulge, double length)
40 : {
41 12 : const double h = (bulge * length) / 2;
42 12 : return (h / 2) + (length * length / (8 * h));
43 : }
44 :
45 212 : static double GetLength(const DXFSmoothPolylineVertex &start,
46 : const DXFSmoothPolylineVertex &end)
47 : {
48 212 : return sqrt(pow(end.x - start.x, 2) + pow(end.y - start.y, 2));
49 : }
50 :
51 24 : static double GetAngle(const DXFSmoothPolylineVertex &start,
52 : const DXFSmoothPolylineVertex &end)
53 : {
54 24 : return atan2((start.y - end.y), (start.x - end.x)) * 180.0 / M_PI;
55 : }
56 :
57 24 : static double GetOGRangle(double angle)
58 : {
59 24 : return angle > 0.0 ? -(angle - 180.0) : -(angle + 180.0);
60 : }
61 :
62 : /************************************************************************/
63 : /* DXFSmoothPolyline::Tessellate() */
64 : /************************************************************************/
65 :
66 70 : OGRGeometry *DXFSmoothPolyline::Tessellate() const
67 : {
68 70 : assert(!m_vertices.empty());
69 :
70 : /* -------------------------------------------------------------------- */
71 : /* If polyline is one vertex, convert it to a point */
72 : /* -------------------------------------------------------------------- */
73 :
74 70 : if (m_vertices.size() == 1)
75 : {
76 : OGRPoint *poPt =
77 0 : new OGRPoint(m_vertices[0].x, m_vertices[0].y, m_vertices[0].z);
78 0 : if (m_vertices[0].z == 0 || m_dim == 2)
79 0 : poPt->flattenTo2D();
80 0 : return poPt;
81 : }
82 :
83 : /* -------------------------------------------------------------------- */
84 : /* Otherwise, presume a line string */
85 : /* -------------------------------------------------------------------- */
86 :
87 70 : OGRLineString *poLS = new OGRLineString;
88 :
89 70 : m_blinestringstarted = false;
90 :
91 : std::vector<DXFSmoothPolylineVertex>::const_iterator oIter =
92 70 : m_vertices.begin();
93 : std::vector<DXFSmoothPolylineVertex>::const_iterator oEndIter =
94 70 : m_vertices.end();
95 :
96 70 : --oEndIter;
97 :
98 70 : DXFSmoothPolylineVertex begin = *oIter;
99 :
100 282 : while (oIter != oEndIter)
101 : {
102 212 : ++oIter;
103 212 : DXFSmoothPolylineVertex end = *oIter;
104 :
105 212 : const double len = GetLength(begin, end);
106 :
107 : // Don't bother handling bulge for non-constant Z
108 212 : if (len == 0 || begin.bulge == 0 || begin.z != end.z)
109 : {
110 200 : EmitLine(begin, end, poLS);
111 : }
112 : else
113 : {
114 12 : const double radius = GetRadius(begin.bulge, len);
115 12 : EmitArc(begin, end, radius, len, begin.bulge, poLS, begin.z);
116 : }
117 :
118 : // Move to next vertex
119 212 : begin = end;
120 : }
121 :
122 : /* -------------------------------------------------------------------- */
123 : /* Flatten to 2D if necessary */
124 : /* -------------------------------------------------------------------- */
125 :
126 70 : if (m_dim == 2)
127 44 : poLS->flattenTo2D();
128 :
129 : /* -------------------------------------------------------------------- */
130 : /* If polyline is closed, convert linestring to a linear ring */
131 : /* */
132 : /* Actually, on review I'm not convinced this is a good idea. */
133 : /* Note that most (all) "filled polygons" are expressed with */
134 : /* hatches which are now handled fairly well and they tend to */
135 : /* echo linear polylines. */
136 : /* -------------------------------------------------------------------- */
137 : #ifdef notdef
138 : if (m_bClosed)
139 : {
140 : OGRLinearRing *poLR = new OGRLinearRing();
141 : poLR->addSubLineString(poLS, 0);
142 : delete poLS;
143 :
144 : // Wrap as polygon.
145 : OGRPolygon *poPoly = new OGRPolygon();
146 : poPoly->addRingDirectly(poLR);
147 :
148 : return poPoly;
149 : }
150 : #endif
151 :
152 70 : return poLS;
153 : }
154 :
155 : /************************************************************************/
156 : /* DXFSmoothPolyline::EmitArc() */
157 : /************************************************************************/
158 :
159 12 : void DXFSmoothPolyline::EmitArc(const DXFSmoothPolylineVertex &start,
160 : const DXFSmoothPolylineVertex &end,
161 : double radius, double len, double bulge,
162 : OGRLineString *poLS, double dfZ) const
163 : {
164 12 : assert(poLS);
165 :
166 12 : double ogrArcRotation = 0.0;
167 12 : const double ogrArcRadius = fabs(radius);
168 :
169 : /* -------------------------------------------------------------------- */
170 : /* Set arc's direction and keep bulge positive */
171 : /* -------------------------------------------------------------------- */
172 :
173 12 : const bool bClockwise = (bulge < 0);
174 :
175 12 : if (bClockwise)
176 6 : bulge *= -1;
177 :
178 : /* -------------------------------------------------------------------- */
179 : /* Get arc's center point */
180 : /* -------------------------------------------------------------------- */
181 :
182 12 : const double saggita = fabs(bulge * (len / 2.0));
183 12 : const double apo =
184 12 : bClockwise ? -(ogrArcRadius - saggita) : -(saggita - ogrArcRadius);
185 :
186 12 : DXFSmoothPolylineVertex v;
187 12 : v.x = start.x - end.x;
188 12 : v.y = start.y - end.y;
189 :
190 : #ifdef notdef
191 : const bool bMathissue = (v.x == 0.0 || v.y == 0.0);
192 : #endif
193 :
194 12 : DXFSmoothPolylineVertex midpoint;
195 12 : midpoint.x = end.x + 0.5 * v.x;
196 12 : midpoint.y = end.y + 0.5 * v.y;
197 :
198 12 : DXFSmoothPolylineVertex pperp;
199 12 : pperp.x = v.y;
200 12 : pperp.y = -v.x;
201 12 : pperp.normalize();
202 :
203 12 : DXFSmoothPolylineVertex ogrArcCenter;
204 12 : ogrArcCenter.x = midpoint.x + (pperp.x * apo);
205 12 : ogrArcCenter.y = midpoint.y + (pperp.y * apo);
206 :
207 : /* -------------------------------------------------------------------- */
208 : /* Get the line's general vertical direction (-1 = down, +1 = up) */
209 : /* -------------------------------------------------------------------- */
210 :
211 12 : const double linedir = end.y > start.y ? 1.0 : -1.0;
212 :
213 : /* -------------------------------------------------------------------- */
214 : /* Get arc's starting angle. */
215 : /* -------------------------------------------------------------------- */
216 :
217 12 : double a = GetAngle(ogrArcCenter, start);
218 :
219 12 : if (bClockwise && (linedir == 1.0))
220 3 : a += (linedir * 180.0);
221 :
222 12 : double ogrArcStartAngle = GetOGRangle(a);
223 :
224 : /* -------------------------------------------------------------------- */
225 : /* Get arc's ending angle. */
226 : /* -------------------------------------------------------------------- */
227 :
228 12 : a = GetAngle(ogrArcCenter, end);
229 :
230 12 : if (bClockwise && (linedir == 1.0))
231 3 : a += (linedir * 180.0);
232 :
233 12 : double ogrArcEndAngle = GetOGRangle(a);
234 :
235 12 : if (!bClockwise && (ogrArcStartAngle < ogrArcEndAngle))
236 2 : ogrArcEndAngle = -180.0 + (linedir * a);
237 :
238 12 : if (bClockwise && (ogrArcStartAngle > ogrArcEndAngle))
239 0 : ogrArcEndAngle += 360.0;
240 :
241 : /* -------------------------------------------------------------------- */
242 : /* Flip arc's rotation if necessary. */
243 : /* -------------------------------------------------------------------- */
244 :
245 12 : if (bClockwise && (linedir == 1.0))
246 3 : ogrArcRotation = linedir * 180.0;
247 :
248 : /* -------------------------------------------------------------------- */
249 : /* Tessellate the arc segment and append to the linestring. */
250 : /* -------------------------------------------------------------------- */
251 :
252 12 : if (fabs(ogrArcEndAngle - ogrArcStartAngle) <= 361.0)
253 : {
254 : OGRLineString *poArcpoLS =
255 : OGRGeometryFactory::approximateArcAngles(
256 : ogrArcCenter.x, ogrArcCenter.y, dfZ, ogrArcRadius, ogrArcRadius,
257 : ogrArcRotation, ogrArcStartAngle, ogrArcEndAngle, 0.0,
258 12 : m_bUseMaxGapWhenTessellatingArcs)
259 12 : ->toLineString();
260 :
261 12 : poLS->addSubLineString(poArcpoLS);
262 :
263 12 : delete poArcpoLS;
264 : }
265 : else
266 : {
267 : // TODO: emit error ?
268 : }
269 12 : }
270 :
271 : /************************************************************************/
272 : /* DXFSmoothPolyline::EmitLine() */
273 : /************************************************************************/
274 :
275 200 : void DXFSmoothPolyline::EmitLine(const DXFSmoothPolylineVertex &start,
276 : const DXFSmoothPolylineVertex &end,
277 : OGRLineString *poLS) const
278 : {
279 200 : assert(poLS);
280 :
281 200 : if (!m_blinestringstarted)
282 : {
283 70 : poLS->addPoint(start.x, start.y, start.z);
284 70 : m_blinestringstarted = true;
285 : }
286 :
287 200 : poLS->addPoint(end.x, end.y, end.z);
288 200 : }
289 :
290 : /************************************************************************/
291 : /* DXFSmoothPolyline::Close() */
292 : /************************************************************************/
293 :
294 41 : void DXFSmoothPolyline::Close()
295 : {
296 41 : assert(!m_bClosed);
297 :
298 41 : if (m_vertices.size() >= 2)
299 : {
300 : const bool bVisuallyClosed =
301 41 : (m_vertices.back().shares_2D_pos(m_vertices[0]));
302 :
303 41 : if (!bVisuallyClosed)
304 : {
305 33 : m_vertices.push_back(m_vertices[0]);
306 : }
307 41 : m_bClosed = true;
308 : }
309 41 : }
|