1 /******************************************************************************
2 *
3 * Project: DWG Translator
4 * Purpose: Implements translation support for DIMENSION elements as a part
5 * of the OGRDWGLayer class.
6 * Author: Frank Warmerdam, warmerdam@pobox.com
7 *
8 ******************************************************************************
9 * Copyright (c) 2011, Frank Warmerdam <warmerdam@pobox.com>
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included
19 * in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
28 ****************************************************************************/
29
30 #include "ogr_dwg.h"
31 #include "cpl_conv.h"
32
33 CPL_CVSID("$Id: ogrdwg_dimension.cpp 7e07230bbff24eb333608de4dbd460b7312839d0 2017-12-11 19:08:47Z Even Rouault $")
34
35 /************************************************************************/
36 /* TranslateDIMENSION() */
37 /************************************************************************/
38
TranslateDIMENSION(OdDbEntityPtr poEntity)39 OGRFeature *OGRDWGLayer::TranslateDIMENSION( OdDbEntityPtr poEntity )
40
41 {
42 OdDbDimensionPtr poDim = OdDbDimension::cast( poEntity );
43 OGRFeature *poFeature = new OGRFeature( poFeatureDefn );
44
45 double dfHeight = CPLAtof(poDS->GetVariable("$DIMTXT", "2.5"));
46 OdGePoint3d oTextPos, oTarget1, oTarget2, oArrow1;
47 CPLString osText;
48
49 TranslateGenericProperties( poFeature, poEntity );
50
51 /* -------------------------------------------------------------------- */
52 /* Generic Dimension stuff. */
53 /* -------------------------------------------------------------------- */
54 osText = (const char *) poDim->dimensionText();
55
56 oTextPos = poDim->textPosition();
57
58 /* -------------------------------------------------------------------- */
59 /* Specific based on the subtype. */
60 /* -------------------------------------------------------------------- */
61 OdRxClass *poClass = poEntity->isA();
62 const OdString osName = poClass->name();
63 const char *pszEntityClassName = (const char *) osName;
64
65 if( EQUAL(pszEntityClassName,"AcDbRotatedDimension") )
66 {
67 OdDbRotatedDimensionPtr poRDim = OdDbDimension::cast( poEntity );
68
69 oTarget2 = poRDim->xLine1Point();
70 oTarget1 = poRDim->xLine2Point();
71 oArrow1 = poRDim->dimLinePoint();
72 }
73
74 else if( EQUAL(pszEntityClassName,"AcDbAlignedDimension") )
75 {
76 OdDbAlignedDimensionPtr poADim = OdDbDimension::cast( poEntity );
77
78 oTarget2 = poADim->xLine1Point();
79 oTarget1 = poADim->xLine2Point();
80 oArrow1 = poADim->dimLinePoint();
81 }
82
83 /*************************************************************************
84
85 DIMENSION geometry layout
86
87 (11,21)(text center point)
88 | DimText |
89 (10,20) X<--------------------------------->X (Arrow2 - computed)
90 (Arrow1)| |
91 | |
92 | X (13,23) (Target2)
93 |
94 X (14,24) (Target1)
95
96 Given:
97 Locations Arrow1, Target1, and Target2 we need to compute Arrow2.
98
99 Steps:
100 1) Compute direction vector from Target1 to Arrow1 (Vec1).
101 2) Compute direction vector for arrow as perpendicular to Vec1 (call Vec2).
102 3) Compute Arrow2 location as intersection between line defined by
103 Vec2 and Arrow1 and line defined by Target2 and direction Vec1 (call Arrow2)
104
105 Then we can draw lines for the various components.
106
107 Note that Vec1 and Vec2 may be horizontal, vertical or on an angle but
108 the approach is as above in all these cases.
109
110 *************************************************************************/
111
112 /* -------------------------------------------------------------------- */
113 /* Step 1, compute direction vector between Target1 and Arrow1. */
114 /* -------------------------------------------------------------------- */
115 double dfVec1X, dfVec1Y;
116
117 dfVec1X = (oArrow1.x - oTarget1.x);
118 dfVec1Y = (oArrow1.y - oTarget1.y);
119
120 /* -------------------------------------------------------------------- */
121 /* Step 2, compute the direction vector from Arrow1 to Arrow2 */
122 /* as a perpendicular to Vec1. */
123 /* -------------------------------------------------------------------- */
124 double dfVec2X, dfVec2Y;
125
126 dfVec2X = dfVec1Y;
127 dfVec2Y = -dfVec1X;
128
129 /* -------------------------------------------------------------------- */
130 /* Step 3, compute intersection of line from target2 along */
131 /* direction vector 1, with the line through Arrow1 and */
132 /* direction vector 2. */
133 /* -------------------------------------------------------------------- */
134 double dfL1M, dfL1B, dfL2M, dfL2B;
135 double dfArrowX2, dfArrowY2;
136
137 // special case if vec1 is vertical.
138 if( dfVec1X == 0.0 )
139 {
140 dfArrowX2 = oTarget2.x;
141 dfArrowY2 = oArrow1.y;
142 }
143
144 // special case if vec2 is horizontal.
145 else if( dfVec1Y == 0.0 )
146 {
147 dfArrowX2 = oArrow1.x;
148 dfArrowY2 = oTarget2.y;
149 }
150
151 else // General case for diagonal vectors.
152 {
153 // first convert vec1 + target2 into y = mx + b format: call this L1
154
155 dfL1M = dfVec1Y / dfVec1X;
156 dfL1B = oTarget2.y - dfL1M * oTarget2.x;
157
158 // convert vec2 + Arrow1 into y = mx + b format, call this L2
159
160 dfL2M = dfVec2Y / dfVec2X;
161 dfL2B = oArrow1.y - dfL2M * oArrow1.x;
162
163 // Compute intersection x = (b2-b1) / (m1-m2)
164
165 dfArrowX2 = (dfL2B - dfL1B) / (dfL1M-dfL2M);
166 dfArrowY2 = dfL2M * dfArrowX2 + dfL2B;
167 }
168
169 /* -------------------------------------------------------------------- */
170 /* Compute the text angle. */
171 /* -------------------------------------------------------------------- */
172 double dfAngle = atan2(dfVec2Y,dfVec2X) * 180.0 / M_PI;
173
174 /* -------------------------------------------------------------------- */
175 /* Rescale the direction vectors so we can use them in */
176 /* constructing arrowheads. We want them to be about 3% of the */
177 /* length of line on which the arrows will be drawn. */
178 /* -------------------------------------------------------------------- */
179 #define VECTOR_LEN(x,y) sqrt( (x)*(x) + (y)*(y) )
180 #define POINT_DIST(x1,y1,x2,y2) VECTOR_LEN((x2-x1),(y2-y1))
181
182 double dfBaselineLength = POINT_DIST(oArrow1.x,oArrow1.y,
183 dfArrowX2,dfArrowY2);
184 double dfTargetLength = dfBaselineLength * 0.03;
185 double dfScaleFactor;
186
187 // recompute vector 2 to ensure the direction is regular
188 dfVec2X = (dfArrowX2 - oArrow1.x);
189 dfVec2Y = (dfArrowY2 - oArrow1.y);
190
191 // vector 1
192 dfScaleFactor = dfTargetLength / VECTOR_LEN(dfVec1X,dfVec1Y);
193 dfVec1X *= dfScaleFactor;
194 dfVec1Y *= dfScaleFactor;
195
196 // vector 2
197 dfScaleFactor = dfTargetLength / VECTOR_LEN(dfVec2X,dfVec2Y);
198 dfVec2X *= dfScaleFactor;
199 dfVec2Y *= dfScaleFactor;
200
201 /* -------------------------------------------------------------------- */
202 /* Create geometries for the different components of the */
203 /* dimension object. */
204 /* -------------------------------------------------------------------- */
205 OGRMultiLineString *poMLS = new OGRMultiLineString();
206 OGRLineString oLine;
207
208 // main arrow line between Arrow1 and Arrow2
209 oLine.setPoint( 0, oArrow1.x, oArrow1.y );
210 oLine.setPoint( 1, dfArrowX2, dfArrowY2 );
211 poMLS->addGeometry( &oLine );
212
213 // dimension line from Target1 to Arrow1 with a small extension.
214 oLine.setPoint( 0, oTarget1.x, oTarget1.y );
215 oLine.setPoint( 1, oArrow1.x + dfVec1X, oArrow1.y + dfVec1Y );
216 poMLS->addGeometry( &oLine );
217
218 // dimension line from Target2 to Arrow2 with a small extension.
219 oLine.setPoint( 0, oTarget2.x, oTarget2.y );
220 oLine.setPoint( 1, dfArrowX2 + dfVec1X, dfArrowY2 + dfVec1Y );
221 poMLS->addGeometry( &oLine );
222
223 // add arrow1 arrow head.
224
225 oLine.setPoint( 0, oArrow1.x, oArrow1.y );
226 oLine.setPoint( 1,
227 oArrow1.x + dfVec2X*3 + dfVec1X,
228 oArrow1.y + dfVec2Y*3 + dfVec1Y );
229 poMLS->addGeometry( &oLine );
230
231 oLine.setPoint( 0, oArrow1.x, oArrow1.y );
232 oLine.setPoint( 1,
233 oArrow1.x + dfVec2X*3 - dfVec1X,
234 oArrow1.y + dfVec2Y*3 - dfVec1Y );
235 poMLS->addGeometry( &oLine );
236
237 // add arrow2 arrow head.
238
239 oLine.setPoint( 0, dfArrowX2, dfArrowY2 );
240 oLine.setPoint( 1,
241 dfArrowX2 - dfVec2X*3 + dfVec1X,
242 dfArrowY2 - dfVec2Y*3 + dfVec1Y );
243 poMLS->addGeometry( &oLine );
244
245 oLine.setPoint( 0, dfArrowX2, dfArrowY2 );
246 oLine.setPoint( 1,
247 dfArrowX2 - dfVec2X*3 - dfVec1X,
248 dfArrowY2 - dfVec2Y*3 - dfVec1Y );
249 poMLS->addGeometry( &oLine );
250
251 poFeature->SetGeometryDirectly( poMLS );
252
253 PrepareLineStyle( poFeature );
254
255 /* -------------------------------------------------------------------- */
256 /* Is the layer disabled/hidden/frozen/off? */
257 /* -------------------------------------------------------------------- */
258 CPLString osLayer = poFeature->GetFieldAsString("Layer");
259
260 int bHidden =
261 EQUAL(poDS->LookupLayerProperty( osLayer, "Hidden" ), "1");
262
263 /* -------------------------------------------------------------------- */
264 /* Work out the color for this feature. */
265 /* -------------------------------------------------------------------- */
266 int nColor = 256;
267
268 if( oStyleProperties.count("Color") > 0 )
269 nColor = atoi(oStyleProperties["Color"]);
270
271 // Use layer color?
272 if( nColor < 1 || nColor > 255 )
273 {
274 const char *pszValue = poDS->LookupLayerProperty( osLayer, "Color" );
275 if( pszValue != nullptr )
276 nColor = atoi(pszValue);
277 }
278
279 if( nColor < 1 || nColor > 255 )
280 nColor = 8;
281
282 /* -------------------------------------------------------------------- */
283 /* Prepare a new feature to serve as the dimension text label */
284 /* feature. We will push it onto the layer as a pending */
285 /* feature for the next feature read. */
286 /* -------------------------------------------------------------------- */
287
288 // a single space suppresses labeling.
289 if( osText == " " )
290 return poFeature;
291
292 OGRFeature *poLabelFeature = poFeature->Clone();
293
294 poLabelFeature->SetGeometryDirectly( new OGRPoint( oTextPos.x, oTextPos.y ) );
295
296 // Do we need to compute the dimension value?
297 if( osText.empty() )
298 {
299 FormatDimension( osText, POINT_DIST( oArrow1.x, oArrow1.y,
300 dfArrowX2, dfArrowY2 ) );
301 }
302
303 CPLString osStyle;
304 char szBuffer[64];
305 char* pszComma = nullptr;
306
307 osStyle.Printf("LABEL(f:\"Arial\",t:\"%s\",p:5",osText.c_str());
308
309 if( dfAngle != 0.0 )
310 {
311 CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfAngle);
312 pszComma = strchr(szBuffer, ',');
313 if (pszComma)
314 *pszComma = '.';
315 osStyle += CPLString().Printf(",a:%s", szBuffer);
316 }
317
318 if( dfHeight != 0.0 )
319 {
320 CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfHeight);
321 pszComma = strchr(szBuffer, ',');
322 if (pszComma)
323 *pszComma = '.';
324 osStyle += CPLString().Printf(",s:%sg", szBuffer);
325 }
326
327 const unsigned char *pabyDWGColors = ACGetColorTable();
328
329 snprintf( szBuffer, sizeof(szBuffer), ",c:#%02x%02x%02x",
330 pabyDWGColors[nColor*3+0],
331 pabyDWGColors[nColor*3+1],
332 pabyDWGColors[nColor*3+2] );
333 osStyle += szBuffer;
334
335 if( bHidden )
336 osStyle += "00";
337
338 osStyle += ")";
339
340 poLabelFeature->SetStyleString( osStyle );
341
342 apoPendingFeatures.push( poLabelFeature );
343
344 return poFeature;
345 }
346
347 /************************************************************************/
348 /* FormatDimension() */
349 /* */
350 /* Format a dimension number according to the current files */
351 /* formatting conventions. */
352 /************************************************************************/
353
FormatDimension(CPLString & osText,double dfValue)354 void OGRDWGLayer::FormatDimension( CPLString &osText, double dfValue )
355
356 {
357 int nPrecision = atoi(poDS->GetVariable("$LUPREC","4"));
358 char szFormat[32];
359 char szBuffer[64];
360
361 // we could do a significantly more precise formatting if we want
362 // to spend the effort. See QCAD's rs_dimlinear.cpp and related files
363 // for example.
364
365 snprintf(szFormat, sizeof(szFormat), "%%.%df", nPrecision );
366 CPLsnprintf(szBuffer, sizeof(szBuffer), szFormat, dfValue);
367 char* pszComma = strchr(szBuffer, ',');
368 if (pszComma)
369 *pszComma = '.';
370 osText = szBuffer;
371 }
372