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 4 : OGRFeature *OGRDXFLayer::TranslateDIMENSION()
45 :
46 : {
47 : char szLineBuf[257];
48 4 : int nCode, nDimType = 0;
49 4 : OGRFeature *poFeature = new OGRFeature( poFeatureDefn );
50 4 : double dfArrowX1 = 0.0, dfArrowY1 = 0.0, dfArrowZ1 = 0.0;
51 4 : double dfTargetX1 = 0.0, dfTargetY1 = 0.0, dfTargetZ1 = 0.0;
52 4 : double dfTargetX2 = 0.0, dfTargetY2 = 0.0, dfTargetZ2 = 0.0;
53 4 : double dfTextX = 0.0, dfTextY = 0.0, dfTextZ = 0.0;
54 4 : double dfAngle = 0.0;
55 4 : double dfHeight = CPLAtof(poDS->GetVariable("$DIMTXT", "2.5"));
56 :
57 4 : CPLString osText;
58 :
59 120 : while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 )
60 : {
61 112 : switch( nCode )
62 : {
63 : case 10:
64 4 : dfArrowX1 = CPLAtof(szLineBuf);
65 4 : break;
66 :
67 : case 20:
68 4 : dfArrowY1 = CPLAtof(szLineBuf);
69 4 : break;
70 :
71 : case 30:
72 4 : dfArrowZ1 = CPLAtof(szLineBuf);
73 4 : break;
74 :
75 : case 11:
76 4 : dfTextX = CPLAtof(szLineBuf);
77 4 : break;
78 :
79 : case 21:
80 4 : dfTextY = CPLAtof(szLineBuf);
81 4 : break;
82 :
83 : case 31:
84 4 : dfTextZ = CPLAtof(szLineBuf);
85 4 : break;
86 :
87 : case 13:
88 4 : dfTargetX2 = CPLAtof(szLineBuf);
89 4 : break;
90 :
91 : case 23:
92 4 : dfTargetY2 = CPLAtof(szLineBuf);
93 4 : break;
94 :
95 : case 33:
96 4 : dfTargetZ2 = CPLAtof(szLineBuf);
97 4 : break;
98 :
99 : case 14:
100 4 : dfTargetX1 = CPLAtof(szLineBuf);
101 4 : break;
102 :
103 : case 24:
104 4 : dfTargetY1 = CPLAtof(szLineBuf);
105 4 : break;
106 :
107 : case 34:
108 4 : dfTargetZ1 = CPLAtof(szLineBuf);
109 4 : break;
110 :
111 : case 70:
112 4 : nDimType = atoi(szLineBuf);
113 4 : break;
114 :
115 : case 1:
116 4 : osText = szLineBuf;
117 4 : break;
118 :
119 : default:
120 56 : TranslateGenericProperty( poFeature, nCode, szLineBuf );
121 : break;
122 : }
123 : }
124 :
125 4 : if( nCode == 0 )
126 4 : 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 4 : dfVec1X = (dfArrowX1 - dfTargetX1);
166 4 : 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 4 : dfVec2X = dfVec1Y;
175 4 : 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 4 : if( dfVec1X == 0.0 )
187 : {
188 1 : dfArrowX2 = dfTargetX2;
189 1 : dfArrowY2 = dfArrowY1;
190 : }
191 :
192 : // special case if vec2 is horizontal.
193 3 : else if( dfVec1Y == 0.0 )
194 : {
195 1 : dfArrowX2 = dfArrowX1;
196 1 : 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 2 : dfL1M = dfVec1Y / dfVec1X;
204 2 : dfL1B = dfTargetY2 - dfL1M * dfTargetX2;
205 :
206 : // convert vec2 + Arrow1 into y = mx + b format, call this L2
207 :
208 2 : dfL2M = dfVec2Y / dfVec2X;
209 2 : dfL2B = dfArrowY1 - dfL2M * dfArrowX1;
210 :
211 : // Compute intersection x = (b2-b1) / (m1-m2)
212 :
213 2 : dfArrowX2 = (dfL2B - dfL1B) / (dfL1M-dfL2M);
214 2 : dfArrowY2 = dfL2M * dfArrowX2 + dfL2B;
215 : }
216 :
217 : /* -------------------------------------------------------------------- */
218 : /* Compute the text angle. */
219 : /* -------------------------------------------------------------------- */
220 4 : 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 4 : double dfBaselineLength = POINT_DIST(dfArrowX1,dfArrowY1,
231 : dfArrowX2,dfArrowY2);
232 4 : double dfTargetLength = dfBaselineLength * 0.03;
233 : double dfScaleFactor;
234 :
235 : // recompute vector 2 to ensure the direction is regular
236 4 : dfVec2X = (dfArrowX2 - dfArrowX1);
237 4 : dfVec2Y = (dfArrowY2 - dfArrowY1);
238 :
239 : // vector 1
240 4 : dfScaleFactor = dfTargetLength / VECTOR_LEN(dfVec1X,dfVec1Y);
241 4 : dfVec1X *= dfScaleFactor;
242 4 : dfVec1Y *= dfScaleFactor;
243 :
244 : // vector 2
245 4 : dfScaleFactor = dfTargetLength / VECTOR_LEN(dfVec2X,dfVec2Y);
246 4 : dfVec2X *= dfScaleFactor;
247 4 : dfVec2Y *= dfScaleFactor;
248 :
249 : /* -------------------------------------------------------------------- */
250 : /* Create geometries for the different components of the */
251 : /* dimension object. */
252 : /* -------------------------------------------------------------------- */
253 4 : OGRMultiLineString *poMLS = new OGRMultiLineString();
254 4 : OGRLineString oLine;
255 :
256 : // main arrow line between Arrow1 and Arrow2
257 4 : oLine.setPoint( 0, dfArrowX1, dfArrowY1 );
258 4 : oLine.setPoint( 1, dfArrowX2, dfArrowY2 );
259 4 : poMLS->addGeometry( &oLine );
260 :
261 : // dimension line from Target1 to Arrow1 with a small extension.
262 4 : oLine.setPoint( 0, dfTargetX1, dfTargetY1 );
263 4 : oLine.setPoint( 1, dfArrowX1 + dfVec1X, dfArrowY1 + dfVec1Y );
264 4 : poMLS->addGeometry( &oLine );
265 :
266 : // dimension line from Target2 to Arrow2 with a small extension.
267 4 : oLine.setPoint( 0, dfTargetX2, dfTargetY2 );
268 4 : oLine.setPoint( 1, dfArrowX2 + dfVec1X, dfArrowY2 + dfVec1Y );
269 4 : poMLS->addGeometry( &oLine );
270 :
271 : // add arrow1 arrow head.
272 :
273 4 : oLine.setPoint( 0, dfArrowX1, dfArrowY1 );
274 : oLine.setPoint( 1,
275 : dfArrowX1 + dfVec2X*3 + dfVec1X,
276 4 : dfArrowY1 + dfVec2Y*3 + dfVec1Y );
277 4 : poMLS->addGeometry( &oLine );
278 :
279 4 : oLine.setPoint( 0, dfArrowX1, dfArrowY1 );
280 : oLine.setPoint( 1,
281 : dfArrowX1 + dfVec2X*3 - dfVec1X,
282 4 : dfArrowY1 + dfVec2Y*3 - dfVec1Y );
283 4 : poMLS->addGeometry( &oLine );
284 :
285 : // add arrow2 arrow head.
286 :
287 4 : oLine.setPoint( 0, dfArrowX2, dfArrowY2 );
288 : oLine.setPoint( 1,
289 : dfArrowX2 - dfVec2X*3 + dfVec1X,
290 4 : dfArrowY2 - dfVec2Y*3 + dfVec1Y );
291 4 : poMLS->addGeometry( &oLine );
292 :
293 4 : oLine.setPoint( 0, dfArrowX2, dfArrowY2 );
294 : oLine.setPoint( 1,
295 : dfArrowX2 - dfVec2X*3 - dfVec1X,
296 4 : dfArrowY2 - dfVec2Y*3 - dfVec1Y );
297 4 : poMLS->addGeometry( &oLine );
298 :
299 4 : poFeature->SetGeometryDirectly( poMLS );
300 :
301 4 : 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 4 : if( osText == " " )
311 0 : return poFeature;
312 :
313 4 : OGRFeature *poLabelFeature = poFeature->Clone();
314 :
315 4 : poLabelFeature->SetGeometryDirectly( new OGRPoint( dfTextX, dfTextY ) );
316 :
317 : // Do we need to compute the dimension value?
318 4 : if( osText.size() == 0 )
319 : {
320 4 : FormatDimension( osText, POINT_DIST( dfArrowX1, dfArrowY1,
321 : dfArrowX2, dfArrowY2 ) );
322 : }
323 :
324 4 : CPLString osStyle;
325 : char szBuffer[64];
326 : char* pszComma;
327 :
328 4 : osStyle.Printf("LABEL(f:\"Arial\",t:\"%s\",p:5",osText.c_str());
329 :
330 4 : if( dfAngle != 0.0 )
331 : {
332 3 : snprintf(szBuffer, sizeof(szBuffer), "%.3g", dfAngle);
333 3 : pszComma = strchr(szBuffer, ',');
334 3 : if (pszComma)
335 0 : *pszComma = '.';
336 3 : osStyle += CPLString().Printf(",a:%s", szBuffer);
337 : }
338 :
339 4 : if( dfHeight != 0.0 )
340 : {
341 4 : snprintf(szBuffer, sizeof(szBuffer), "%.3g", dfHeight);
342 4 : pszComma = strchr(szBuffer, ',');
343 4 : if (pszComma)
344 0 : *pszComma = '.';
345 4 : osStyle += CPLString().Printf(",s:%sg", szBuffer);
346 : }
347 :
348 : // add color!
349 :
350 4 : osStyle += ")";
351 :
352 4 : poLabelFeature->SetStyleString( osStyle );
353 :
354 4 : apoPendingFeatures.push( poLabelFeature );
355 :
356 4 : return poFeature;
357 : }
358 :
359 : /************************************************************************/
360 : /* FormatDimension() */
361 : /* */
362 : /* Format a dimension number according to the current files */
363 : /* formatting conventions. */
364 : /************************************************************************/
365 :
366 4 : void OGRDXFLayer::FormatDimension( CPLString &osText, double dfValue )
367 :
368 : {
369 4 : 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 4 : sprintf(szFormat, "%%.%df", nPrecision );
378 4 : snprintf(szBuffer, sizeof(szBuffer), szFormat, dfValue);
379 4 : char* pszComma = strchr(szBuffer, ',');
380 4 : if (pszComma)
381 0 : *pszComma = '.';
382 4 : osText = szBuffer;
383 4 : }
|