1 : /******************************************************************************
2 : * $Id: ogrdxf_dimension.cpp 19643 2010-05-08 21:56:18Z rouault $
3 : *
4 : * Project: DXF Translator
5 : * Purpose: Implements translation support for DIMENSION elements as a part
6 : * of the OGRDXFLayer class.
7 : * Author: Frank Warmerdam, warmerdam@pobox.com
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
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 "ogr_dxf.h"
32 : #include "cpl_conv.h"
33 :
34 : CPL_CVSID("$Id: ogrdxf_dimension.cpp 19643 2010-05-08 21:56:18Z rouault $");
35 :
36 : #ifndef PI
37 : #define PI 3.14159265358979323846
38 : #endif
39 :
40 : /************************************************************************/
41 : /* TranslateDIMENSION() */
42 : /************************************************************************/
43 :
44 18 : OGRFeature *OGRDXFLayer::TranslateDIMENSION()
45 :
46 : {
47 : char szLineBuf[257];
48 18 : int nCode, nDimType = 0;
49 18 : OGRFeature *poFeature = new OGRFeature( poFeatureDefn );
50 18 : double dfArrowX1 = 0.0, dfArrowY1 = 0.0, dfArrowZ1 = 0.0;
51 18 : double dfTargetX1 = 0.0, dfTargetY1 = 0.0, dfTargetZ1 = 0.0;
52 18 : double dfTargetX2 = 0.0, dfTargetY2 = 0.0, dfTargetZ2 = 0.0;
53 18 : double dfTextX = 0.0, dfTextY = 0.0, dfTextZ = 0.0;
54 18 : double dfAngle = 0.0;
55 18 : double dfHeight = CPLAtof(poDS->GetVariable("$DIMTXT", "2.5"));
56 :
57 18 : CPLString osText;
58 :
59 546 : while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 )
60 : {
61 510 : switch( nCode )
62 : {
63 : case 10:
64 18 : dfArrowX1 = CPLAtof(szLineBuf);
65 18 : break;
66 :
67 : case 20:
68 18 : dfArrowY1 = CPLAtof(szLineBuf);
69 18 : break;
70 :
71 : case 30:
72 18 : dfArrowZ1 = CPLAtof(szLineBuf);
73 18 : break;
74 :
75 : case 11:
76 18 : dfTextX = CPLAtof(szLineBuf);
77 18 : break;
78 :
79 : case 21:
80 18 : dfTextY = CPLAtof(szLineBuf);
81 18 : break;
82 :
83 : case 31:
84 18 : dfTextZ = CPLAtof(szLineBuf);
85 18 : break;
86 :
87 : case 13:
88 18 : dfTargetX2 = CPLAtof(szLineBuf);
89 18 : break;
90 :
91 : case 23:
92 18 : dfTargetY2 = CPLAtof(szLineBuf);
93 18 : break;
94 :
95 : case 33:
96 18 : dfTargetZ2 = CPLAtof(szLineBuf);
97 18 : break;
98 :
99 : case 14:
100 18 : dfTargetX1 = CPLAtof(szLineBuf);
101 18 : break;
102 :
103 : case 24:
104 18 : dfTargetY1 = CPLAtof(szLineBuf);
105 18 : break;
106 :
107 : case 34:
108 18 : dfTargetZ1 = CPLAtof(szLineBuf);
109 18 : break;
110 :
111 : case 70:
112 18 : nDimType = atoi(szLineBuf);
113 18 : break;
114 :
115 : case 1:
116 18 : osText = szLineBuf;
117 18 : break;
118 :
119 : default:
120 258 : TranslateGenericProperty( poFeature, nCode, szLineBuf );
121 : break;
122 : }
123 : }
124 :
125 18 : if( nCode == 0 )
126 18 : poDS->UnreadValue();
127 :
128 : /*************************************************************************
129 :
130 : DIMENSION geometry layout
131 :
132 : (11,21)(text center point)
133 : | DimText |
134 : (10,20) X<--------------------------------->X (Arrow2 - computed)
135 : (Arrow1)| |
136 : | |
137 : | X (13,23) (Target2)
138 : |
139 : X (14,24) (Target1)
140 :
141 :
142 : Given:
143 : Locations Arrow1, Target1, and Target2 we need to compute Arrow2.
144 :
145 : Steps:
146 : 1) Compute direction vector from Target1 to Arrow1 (Vec1).
147 : 2) Compute direction vector for arrow as perpendicular to Vec1 (call Vec2).
148 : 3) Compute Arrow2 location as intersection between line defined by
149 : Vec2 and Arrow1 and line defined by Target2 and direction Vec1 (call Arrow2)
150 :
151 : Then we can draw lines for the various components.
152 :
153 : Note that Vec1 and Vec2 may be horizontal, vertical or on an angle but
154 : the approach is as above in all these cases.
155 :
156 : *************************************************************************/
157 :
158 : ;
159 :
160 : /* -------------------------------------------------------------------- */
161 : /* Step 1, compute direction vector between Target1 and Arrow1. */
162 : /* -------------------------------------------------------------------- */
163 : double dfVec1X, dfVec1Y;
164 :
165 18 : dfVec1X = (dfArrowX1 - dfTargetX1);
166 18 : dfVec1Y = (dfArrowY1 - dfTargetY1);
167 :
168 : /* -------------------------------------------------------------------- */
169 : /* Step 2, compute the direction vector from Arrow1 to Arrow2 */
170 : /* as a perpendicluar to Vec1. */
171 : /* -------------------------------------------------------------------- */
172 : double dfVec2X, dfVec2Y;
173 :
174 18 : dfVec2X = dfVec1Y;
175 18 : dfVec2Y = -dfVec1X;
176 :
177 : /* -------------------------------------------------------------------- */
178 : /* Step 3, compute intersection of line from target2 along */
179 : /* direction vector 1, with the line through Arrow1 and */
180 : /* direction vector 2. */
181 : /* -------------------------------------------------------------------- */
182 : double dfL1M, dfL1B, dfL2M, dfL2B;
183 : double dfArrowX2, dfArrowY2;
184 :
185 : // special case if vec1 is vertical.
186 18 : if( dfVec1X == 0.0 )
187 : {
188 6 : dfArrowX2 = dfTargetX2;
189 6 : dfArrowY2 = dfArrowY1;
190 : }
191 :
192 : // special case if vec2 is horizontal.
193 12 : else if( dfVec1Y == 0.0 )
194 : {
195 6 : dfArrowX2 = dfArrowX1;
196 6 : dfArrowY2 = dfTargetY2;
197 : }
198 :
199 : else // General case for diagonal vectors.
200 : {
201 : // first convert vec1 + target2 into y = mx + b format: call this L1
202 :
203 6 : dfL1M = dfVec1Y / dfVec1X;
204 6 : dfL1B = dfTargetY2 - dfL1M * dfTargetX2;
205 :
206 : // convert vec2 + Arrow1 into y = mx + b format, call this L2
207 :
208 6 : dfL2M = dfVec2Y / dfVec2X;
209 6 : dfL2B = dfArrowY1 - dfL2M * dfArrowX1;
210 :
211 : // Compute intersection x = (b2-b1) / (m1-m2)
212 :
213 6 : dfArrowX2 = (dfL2B - dfL1B) / (dfL1M-dfL2M);
214 6 : dfArrowY2 = dfL2M * dfArrowX2 + dfL2B;
215 : }
216 :
217 : /* -------------------------------------------------------------------- */
218 : /* Compute the text angle. */
219 : /* -------------------------------------------------------------------- */
220 18 : dfAngle = atan2(dfVec2Y,dfVec2X) * 180.0 / PI;
221 :
222 : /* -------------------------------------------------------------------- */
223 : /* Rescale the direction vectors so we can use them in */
224 : /* constructing arrowheads. We want them to be about 3% of the */
225 : /* length of line on which the arrows will be drawn. */
226 : /* -------------------------------------------------------------------- */
227 : #define VECTOR_LEN(x,y) sqrt( (x)*(x) + (y)*(y) )
228 : #define POINT_DIST(x1,y1,x2,y2) VECTOR_LEN((x2-x1),(y2-y1))
229 :
230 18 : double dfBaselineLength = POINT_DIST(dfArrowX1,dfArrowY1,
231 : dfArrowX2,dfArrowY2);
232 18 : double dfTargetLength = dfBaselineLength * 0.03;
233 : double dfScaleFactor;
234 :
235 : // recompute vector 2 to ensure the direction is regular
236 18 : dfVec2X = (dfArrowX2 - dfArrowX1);
237 18 : dfVec2Y = (dfArrowY2 - dfArrowY1);
238 :
239 : // vector 1
240 18 : dfScaleFactor = dfTargetLength / VECTOR_LEN(dfVec1X,dfVec1Y);
241 18 : dfVec1X *= dfScaleFactor;
242 18 : dfVec1Y *= dfScaleFactor;
243 :
244 : // vector 2
245 18 : dfScaleFactor = dfTargetLength / VECTOR_LEN(dfVec2X,dfVec2Y);
246 18 : dfVec2X *= dfScaleFactor;
247 18 : dfVec2Y *= dfScaleFactor;
248 :
249 : /* -------------------------------------------------------------------- */
250 : /* Create geometries for the different components of the */
251 : /* dimension object. */
252 : /* -------------------------------------------------------------------- */
253 18 : OGRMultiLineString *poMLS = new OGRMultiLineString();
254 18 : OGRLineString oLine;
255 :
256 : // main arrow line between Arrow1 and Arrow2
257 18 : oLine.setPoint( 0, dfArrowX1, dfArrowY1 );
258 18 : oLine.setPoint( 1, dfArrowX2, dfArrowY2 );
259 18 : poMLS->addGeometry( &oLine );
260 :
261 : // dimension line from Target1 to Arrow1 with a small extension.
262 18 : oLine.setPoint( 0, dfTargetX1, dfTargetY1 );
263 18 : oLine.setPoint( 1, dfArrowX1 + dfVec1X, dfArrowY1 + dfVec1Y );
264 18 : poMLS->addGeometry( &oLine );
265 :
266 : // dimension line from Target2 to Arrow2 with a small extension.
267 18 : oLine.setPoint( 0, dfTargetX2, dfTargetY2 );
268 18 : oLine.setPoint( 1, dfArrowX2 + dfVec1X, dfArrowY2 + dfVec1Y );
269 18 : poMLS->addGeometry( &oLine );
270 :
271 : // add arrow1 arrow head.
272 :
273 18 : oLine.setPoint( 0, dfArrowX1, dfArrowY1 );
274 : oLine.setPoint( 1,
275 : dfArrowX1 + dfVec2X*3 + dfVec1X,
276 18 : dfArrowY1 + dfVec2Y*3 + dfVec1Y );
277 18 : poMLS->addGeometry( &oLine );
278 :
279 18 : oLine.setPoint( 0, dfArrowX1, dfArrowY1 );
280 : oLine.setPoint( 1,
281 : dfArrowX1 + dfVec2X*3 - dfVec1X,
282 18 : dfArrowY1 + dfVec2Y*3 - dfVec1Y );
283 18 : poMLS->addGeometry( &oLine );
284 :
285 : // add arrow2 arrow head.
286 :
287 18 : oLine.setPoint( 0, dfArrowX2, dfArrowY2 );
288 : oLine.setPoint( 1,
289 : dfArrowX2 - dfVec2X*3 + dfVec1X,
290 18 : dfArrowY2 - dfVec2Y*3 + dfVec1Y );
291 18 : poMLS->addGeometry( &oLine );
292 :
293 18 : oLine.setPoint( 0, dfArrowX2, dfArrowY2 );
294 : oLine.setPoint( 1,
295 : dfArrowX2 - dfVec2X*3 - dfVec1X,
296 18 : dfArrowY2 - dfVec2Y*3 - dfVec1Y );
297 18 : poMLS->addGeometry( &oLine );
298 :
299 18 : poFeature->SetGeometryDirectly( poMLS );
300 :
301 18 : PrepareLineStyle( poFeature );
302 :
303 : /* -------------------------------------------------------------------- */
304 : /* Prepare a new feature to serve as the dimension text label */
305 : /* feature. We will push it onto the layer as a pending */
306 : /* feature for the next feature read. */
307 : /* -------------------------------------------------------------------- */
308 :
309 : // a single space supresses labelling.
310 18 : if( osText == " " )
311 0 : return poFeature;
312 :
313 18 : OGRFeature *poLabelFeature = poFeature->Clone();
314 :
315 18 : poLabelFeature->SetGeometryDirectly( new OGRPoint( dfTextX, dfTextY ) );
316 :
317 : // Do we need to compute the dimension value?
318 18 : if( osText.size() == 0 )
319 : {
320 : FormatDimension( osText, POINT_DIST( dfArrowX1, dfArrowY1,
321 18 : dfArrowX2, dfArrowY2 ) );
322 : }
323 :
324 18 : CPLString osStyle;
325 : char szBuffer[64];
326 : char* pszComma;
327 :
328 18 : osStyle.Printf("LABEL(f:\"Arial\",t:\"%s\",p:5",osText.c_str());
329 :
330 18 : if( dfAngle != 0.0 )
331 : {
332 12 : snprintf(szBuffer, sizeof(szBuffer), "%.3g", dfAngle);
333 12 : pszComma = strchr(szBuffer, ',');
334 12 : if (pszComma)
335 0 : *pszComma = '.';
336 12 : osStyle += CPLString().Printf(",a:%s", szBuffer);
337 : }
338 :
339 18 : if( dfHeight != 0.0 )
340 : {
341 18 : snprintf(szBuffer, sizeof(szBuffer), "%.3g", dfHeight);
342 18 : pszComma = strchr(szBuffer, ',');
343 18 : if (pszComma)
344 0 : *pszComma = '.';
345 18 : osStyle += CPLString().Printf(",s:%sg", szBuffer);
346 : }
347 :
348 : // add color!
349 :
350 18 : osStyle += ")";
351 :
352 18 : poLabelFeature->SetStyleString( osStyle );
353 :
354 18 : apoPendingFeatures.push( poLabelFeature );
355 :
356 18 : return poFeature;
357 : }
358 :
359 : /************************************************************************/
360 : /* FormatDimension() */
361 : /* */
362 : /* Format a dimension number according to the current files */
363 : /* formatting conventions. */
364 : /************************************************************************/
365 :
366 18 : void OGRDXFLayer::FormatDimension( CPLString &osText, double dfValue )
367 :
368 : {
369 18 : int nPrecision = atoi(poDS->GetVariable("$LUPREC","4"));
370 : char szFormat[32];
371 : char szBuffer[64];
372 :
373 : // we could do a significantly more precise formatting if we want
374 : // to spend the effort. See QCAD's rs_dimlinear.cpp and related files
375 : // for example.
376 :
377 18 : sprintf(szFormat, "%%.%df", nPrecision );
378 18 : snprintf(szBuffer, sizeof(szBuffer), szFormat, dfValue);
379 18 : char* pszComma = strchr(szBuffer, ',');
380 18 : if (pszComma)
381 0 : *pszComma = '.';
382 18 : osText = szBuffer;
383 18 : }
|