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