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 : /************************************************************************/
37 : /* Local helper functions */
38 : /************************************************************************/
39 :
40 16 : static double GetRadius(double bulge, double length)
41 : {
42 16 : const double h = (bulge * length) / 2;
43 16 : return (h / 2) + (length * length / (8 * h));
44 : }
45 :
46 :
47 58 : static double GetLength
48 : (
49 : const DXFSmoothPolylineVertex& start,
50 : const DXFSmoothPolylineVertex& end
51 : )
52 : {
53 58 : return sqrt(pow(end.x - start.x, 2) + pow(end.y - start.y, 2));
54 : }
55 :
56 :
57 32 : static double GetAngle
58 : (
59 : const DXFSmoothPolylineVertex& start,
60 : const DXFSmoothPolylineVertex& end
61 : )
62 : {
63 32 : return atan2((start.y - end.y), (start.x - end.x)) * 180.0 / M_PI;
64 : }
65 :
66 :
67 32 : static double GetOGRangle(double angle)
68 : {
69 : return angle > 0.0
70 : ? -(angle - 180.0)
71 32 : : -(angle + 180.0);
72 : }
73 :
74 :
75 : /************************************************************************/
76 : /* DXFSmoothPolyline::Tesselate() */
77 : /************************************************************************/
78 :
79 26 : OGRGeometry* DXFSmoothPolyline::Tesselate() const
80 : {
81 26 : assert(!m_vertices.empty());
82 :
83 :
84 : /* -------------------------------------------------------------------- */
85 : /* If polyline is one vertex, convert it to a point */
86 : /* -------------------------------------------------------------------- */
87 :
88 26 : if(m_vertices.size() == 1)
89 : {
90 0 : OGRPoint* poPt = new OGRPoint(m_vertices[0].x, m_vertices[0].y, m_vertices[0].z);
91 0 : if(m_vertices[0].z == 0 || m_dim == 2)
92 0 : poPt->flattenTo2D();
93 0 : return poPt;
94 : }
95 :
96 :
97 : /* -------------------------------------------------------------------- */
98 : /* Otherwise, presume a line string */
99 : /* -------------------------------------------------------------------- */
100 :
101 26 : OGRLineString* poLS = new OGRLineString;
102 :
103 26 : m_blinestringstarted = false;
104 :
105 26 : std::vector<DXFSmoothPolylineVertex>::const_iterator iter = m_vertices.begin();
106 26 : std::vector<DXFSmoothPolylineVertex>::const_iterator eiter = m_vertices.end();
107 :
108 26 : eiter--;
109 :
110 26 : DXFSmoothPolylineVertex begin = *iter;
111 :
112 26 : double dfZ = 0.0;
113 26 : const bool bConstantZ = this->HasConstantZ(dfZ);
114 :
115 110 : while(iter != eiter)
116 : {
117 58 : iter++;
118 58 : DXFSmoothPolylineVertex end = *iter;
119 :
120 58 : const double len = GetLength(begin,end);
121 :
122 100 : if((len == 0) || (begin.bulge == 0))
123 : {
124 42 : this->EmitLine(begin, end, poLS, bConstantZ, dfZ);
125 : }
126 : else
127 : {
128 16 : const double radius = GetRadius(begin.bulge,len);
129 16 : this->EmitArc(begin, end, radius, len, begin.bulge, poLS, dfZ);
130 : }
131 :
132 : // Move to next vertex
133 58 : begin = end;
134 : }
135 :
136 :
137 : /* -------------------------------------------------------------------- */
138 : /* Flatten to 2D if necessary */
139 : /* -------------------------------------------------------------------- */
140 :
141 26 : if(bConstantZ && dfZ == 0.0 && m_dim == 2)
142 22 : poLS->flattenTo2D();
143 :
144 :
145 : /* -------------------------------------------------------------------- */
146 : /* If polyline is closed, convert linestring to a linear ring */
147 : /* */
148 : /* Actually, on review I'm not convinced this is a good idea. */
149 : /* Note that most (all) "filled polygons" are expressed with */
150 : /* hatches which are now handled fairly well and they tend to */
151 : /* echo linear polylines. */
152 : /* -------------------------------------------------------------------- */
153 : #ifdef notdef
154 : if(m_bClosed)
155 : {
156 : OGRLinearRing *poLR = new OGRLinearRing();
157 : poLR->addSubLineString( poLS, 0 );
158 : delete poLS;
159 :
160 : // Wrap as polygon.
161 : OGRPolygon *poPoly = new OGRPolygon();
162 : poPoly->addRingDirectly( poLR );
163 :
164 : return poPoly;
165 : }
166 : #endif
167 :
168 26 : return poLS;
169 : }
170 :
171 :
172 : /************************************************************************/
173 : /* DXFSmoothPolyline::EmitArc() */
174 : /************************************************************************/
175 :
176 16 : void DXFSmoothPolyline::EmitArc
177 : (
178 : const DXFSmoothPolylineVertex& start,
179 : const DXFSmoothPolylineVertex& end,
180 : double radius, double len, double bulge,
181 : OGRLineString* poLS,
182 : double dfZ
183 : ) const
184 : {
185 16 : assert(poLS);
186 :
187 16 : double ogrArcRotation = 0.0,
188 16 : ogrArcRadius = fabs(radius);
189 :
190 :
191 : /* -------------------------------------------------------------------- */
192 : /* Set arc's direction and keep bulge positive */
193 : /* -------------------------------------------------------------------- */
194 :
195 16 : const bool bClockwise = (bulge < 0);
196 :
197 16 : if(bClockwise)
198 8 : bulge *= -1;
199 :
200 :
201 : /* -------------------------------------------------------------------- */
202 : /* Get arc's center point */
203 : /* -------------------------------------------------------------------- */
204 :
205 16 : const double saggita = fabs(bulge * (len / 2.0));
206 : const double apo = bClockwise
207 : ? -(ogrArcRadius - saggita)
208 16 : : -(saggita - ogrArcRadius);
209 :
210 16 : DXFSmoothPolylineVertex v;
211 16 : v.x = start.x - end.x;
212 16 : v.y = start.y - end.y;
213 :
214 : #ifdef notdef
215 : const bool bMathissue = (v.x == 0.0 || v.y == 0.0);
216 : #endif
217 :
218 16 : DXFSmoothPolylineVertex midpoint;
219 16 : midpoint.x = end.x + 0.5 * v.x;
220 16 : midpoint.y = end.y + 0.5 * v.y;
221 :
222 16 : DXFSmoothPolylineVertex pperp;
223 16 : pperp.x = v.y;
224 16 : pperp.y = -v.x;
225 16 : pperp.normalize();
226 :
227 16 : DXFSmoothPolylineVertex ogrArcCenter;
228 16 : ogrArcCenter.x = midpoint.x + (pperp.x * apo);
229 16 : ogrArcCenter.y = midpoint.y + (pperp.y * apo);
230 :
231 :
232 : /* -------------------------------------------------------------------- */
233 : /* Get the line's general vertical direction (-1 = down, +1 = up) */
234 : /* -------------------------------------------------------------------- */
235 :
236 16 : const double linedir = end.y > start.y ? 1.0 : -1.0;
237 :
238 :
239 : /* -------------------------------------------------------------------- */
240 : /* Get arc's starting angle. */
241 : /* -------------------------------------------------------------------- */
242 :
243 16 : double a = GetAngle(ogrArcCenter, start);
244 :
245 16 : if(bClockwise && (linedir == 1.0))
246 4 : a += (linedir * 180.0);
247 :
248 16 : double ogrArcStartAngle = GetOGRangle(a);
249 :
250 :
251 : /* -------------------------------------------------------------------- */
252 : /* Get arc's ending angle. */
253 : /* -------------------------------------------------------------------- */
254 :
255 16 : a = GetAngle(ogrArcCenter, end);
256 :
257 16 : if(bClockwise && (linedir == 1.0))
258 4 : a += (linedir * 180.0);
259 :
260 16 : double ogrArcEndAngle = GetOGRangle(a);
261 :
262 16 : if(!bClockwise && (ogrArcStartAngle < ogrArcEndAngle))
263 4 : ogrArcEndAngle = -180.0 + (linedir * a);
264 :
265 :
266 : /* -------------------------------------------------------------------- */
267 : /* Flip arc's rotation if necessary. */
268 : /* -------------------------------------------------------------------- */
269 :
270 16 : if(bClockwise && (linedir == 1.0))
271 4 : ogrArcRotation = linedir * 180.0;
272 :
273 :
274 : /* -------------------------------------------------------------------- */
275 : /* Tesselate the arc segment and append to the linestring. */
276 : /* -------------------------------------------------------------------- */
277 :
278 : OGRLineString* poArcpoLS =
279 : (OGRLineString*)OGRGeometryFactory::approximateArcAngles(
280 : ogrArcCenter.x, ogrArcCenter.y, dfZ,
281 : ogrArcRadius, ogrArcRadius, ogrArcRotation,
282 : ogrArcStartAngle, ogrArcEndAngle,
283 16 : 0.0);
284 :
285 16 : poLS->addSubLineString(poArcpoLS);
286 :
287 16 : delete poArcpoLS;
288 16 : }
289 :
290 :
291 :
292 : /************************************************************************/
293 : /* DXFSmoothPolyline::EmitLine() */
294 : /************************************************************************/
295 :
296 42 : void DXFSmoothPolyline::EmitLine
297 : (
298 : const DXFSmoothPolylineVertex& start,
299 : const DXFSmoothPolylineVertex& end,
300 : OGRLineString* poLS,
301 : bool bConstantZ,
302 : double dfZ
303 : ) const
304 : {
305 42 : assert(poLS);
306 :
307 42 : if(!m_blinestringstarted)
308 : {
309 : poLS->addPoint(start.x, start.y,
310 26 : bConstantZ ? dfZ : start.z);
311 26 : m_blinestringstarted = true;
312 : }
313 :
314 : poLS->addPoint(end.x, end.y,
315 42 : bConstantZ ? dfZ : end.z);
316 42 : }
317 :
318 :
319 : /************************************************************************/
320 : /* DXFSmoothPolyline::Close() */
321 : /************************************************************************/
322 :
323 8 : void DXFSmoothPolyline::Close()
324 : {
325 8 : assert(!m_bClosed);
326 :
327 8 : if(m_vertices.size() >= 2)
328 : {
329 : const bool bVisuallyClosed =
330 8 : (m_vertices[m_vertices.size() - 1].shares_2D_pos(m_vertices[0]));
331 :
332 8 : if(!bVisuallyClosed)
333 : {
334 4 : m_vertices.push_back(m_vertices[0]);
335 : }
336 8 : m_bClosed = true;
337 : }
338 8 : }
339 :
340 :
341 : /************************************************************************/
342 : /* DXFSmoothPolyline::HasConstantZ() */
343 : /************************************************************************/
344 :
345 26 : bool DXFSmoothPolyline::HasConstantZ(double& dfZ) const
346 : {
347 : // Treat the polyline as having constant Z if all Z members
348 : // are equal or if any bulge attribute exists. In the latter case,
349 : // set dfZ to zero. Leave dfZ unassigned if false is returned.
350 :
351 26 : assert(!m_vertices.empty());
352 :
353 26 : const double d = m_vertices[0].z;
354 :
355 64 : for(size_t i = 1; i < m_vertices.size(); i++)
356 : {
357 42 : if(m_vertices[i].bulge != 0.0)
358 : {
359 4 : dfZ = 0.0;
360 4 : return true;
361 : }
362 38 : if(m_vertices[i].z != d)
363 0 : return false;
364 : }
365 22 : dfZ = d;
366 22 : return true;
367 : }
368 :
|