1 /****************************************************************************
2 **
3 ** This file is part of the LibreCAD project, a 2D CAD program
4 **
5 ** Copyright (C) 2018 A. Stebich (librecad@mail.lordofbikes.de)
6 ** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
7 ** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
8 **
9 **
10 ** This file may be distributed and/or modified under the terms of the
11 ** GNU General Public License version 2 as published by the Free Software
12 ** Foundation and appearing in the file gpl-2.0.txt included in the
13 ** packaging of this file.
14 **
15 ** This program is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ** GNU General Public License for more details.
19 **
20 ** You should have received a copy of the GNU General Public License
21 ** along with this program; if not, write to the Free Software
22 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 **
24 ** This copyright notice MUST APPEAR in all copies of the script!
25 **
26 **********************************************************************/
27
28 #include<iostream>
29 #include<cmath>
30 #include "rs_dimangular.h"
31 #include "rs_math.h"
32
33 #include "rs_constructionline.h"
34 #include "rs_arc.h"
35 #include "rs_line.h"
36 #include "rs_graphic.h"
37 #include "rs_information.h"
38 #include "rs_solid.h"
39 #include "rs_mtext.h"
40 #include "rs_debug.h"
41
RS_DimAngularData()42 RS_DimAngularData::RS_DimAngularData():
43 definitionPoint1( false),
44 definitionPoint2( false),
45 definitionPoint3( false),
46 definitionPoint4( false)
47 {
48 }
49
RS_DimAngularData(const RS_DimAngularData & ed)50 RS_DimAngularData::RS_DimAngularData(const RS_DimAngularData &ed):
51 definitionPoint1( ed.definitionPoint1),
52 definitionPoint2( ed.definitionPoint2),
53 definitionPoint3( ed.definitionPoint3),
54 definitionPoint4( ed.definitionPoint4)
55 {
56 }
57
58 /**
59 * Constructor with initialisation.
60 *
61 * @param definitionPoint Definition point of the angular dimension.
62 * @param leader Leader length.
63 */
RS_DimAngularData(const RS_Vector & _definitionPoint1,const RS_Vector & _definitionPoint2,const RS_Vector & _definitionPoint3,const RS_Vector & _definitionPoint4)64 RS_DimAngularData::RS_DimAngularData(const RS_Vector& _definitionPoint1,
65 const RS_Vector& _definitionPoint2,
66 const RS_Vector& _definitionPoint3,
67 const RS_Vector& _definitionPoint4):
68 definitionPoint1( _definitionPoint1),
69 definitionPoint2( _definitionPoint2),
70 definitionPoint3( _definitionPoint3),
71 definitionPoint4( _definitionPoint4)
72 {
73 }
74
75 /**
76 * Constructor with initialisation.
77 *
78 * @param dimscale general scale (DIMSCALE)
79 * @param dimexo distance from entities (DIMEXO)
80 * @param dimexe extension line extension (DIMEXE)
81 * @param dimtxt text height (DIMTXT)
82 * @param dimgap text distance to line (DIMGAP)
83 * @param arrowSize arrow length
84 */
LC_DimAngularVars(const double _dimscale,const double _dimexo,const double _dimexe,const double _dimtxt,const double _dimgap,const double _arrowSize)85 LC_DimAngularVars::LC_DimAngularVars(const double _dimscale,
86 const double _dimexo,
87 const double _dimexe,
88 const double _dimtxt,
89 const double _dimgap,
90 const double _arrowSize) :
91 dimscale( _dimscale),
92 dimexo( _dimexo * _dimscale),
93 dimexe( _dimexe * _dimscale),
94 dimtxt( _dimtxt * _dimscale),
95 dimgap( _dimgap * _dimscale),
96 arrowSize( _arrowSize * _dimscale)
97 {
98 }
99
LC_DimAngularVars(const LC_DimAngularVars & av)100 LC_DimAngularVars::LC_DimAngularVars(const LC_DimAngularVars& av) :
101 dimscale( av.dimscale),
102 dimexo( av.dimexo),
103 dimexe( av.dimexe),
104 dimtxt( av.dimtxt),
105 dimgap( av.dimgap),
106 arrowSize( av.arrowSize)
107 {
108 }
109
110 /**
111 * Constructor.
112 *
113 * @para parent Parent Entity Container.
114 * @para d Common dimension geometrical data.
115 * @para ed Extended geometrical data for angular dimension.
116 */
RS_DimAngular(RS_EntityContainer * parent,const RS_DimensionData & d,const RS_DimAngularData & ed)117 RS_DimAngular::RS_DimAngular(RS_EntityContainer* parent,
118 const RS_DimensionData& d,
119 const RS_DimAngularData& ed) :
120 RS_Dimension( parent, d),
121 edata( ed)
122 {
123 calcDimension();
124 calculateBorders();
125 }
126
clone() const127 RS_Entity* RS_DimAngular::clone() const
128 {
129 RS_DimAngular *d {new RS_DimAngular(*this)};
130
131 d->setOwner( isOwner());
132 d->initId();
133 d->detach();
134
135 return d;
136 }
137
138 /**
139 * @return Automatically created label for the default
140 * measurement of this dimension.
141 */
getMeasuredLabel()142 QString RS_DimAngular::getMeasuredLabel()
143 {
144 int dimaunit {getGraphicVariableInt( QStringLiteral( "$DIMAUNIT"), 0)};
145 int dimadec {getGraphicVariableInt( QStringLiteral( "$DIMADEC"), 0)};
146 int dimazin {getGraphicVariableInt( QStringLiteral( "$DIMAZIN"), 0)};
147 RS2::AngleFormat format {RS_Units::numberToAngleFormat( dimaunit)};
148 QString strLabel( RS_Units::formatAngle( dimAngle, format, dimadec));
149
150 if (RS2::DegreesMinutesSeconds != format
151 && RS2::Surveyors != format) {
152 strLabel = stripZerosAngle( strLabel, dimazin);
153 }
154
155 //verify if units are decimal and comma separator
156 if (RS2::DegreesMinutesSeconds != dimaunit) {
157 if (',' == getGraphicVariableInt( QStringLiteral( "$DIMDSEP"), 0)) {
158 strLabel.replace( QChar('.'), QChar(','));
159 }
160 }
161
162 return strLabel;
163 }
164
165 /**
166 * @return Center of the measured dimension.
167 */
getCenter() const168 RS_Vector RS_DimAngular::getCenter() const
169 {
170 return dimCenter;
171 }
172
173 /**
174 * @brief Add an extension line if necessary
175 *
176 * @param dimLine dimension definition line including extension offset
177 * @param dimPoint point where the arc meets the definition line
178 * @param dirStart unit vector defining the lines starting point direction
179 * @param dirEnd unit vector defining the lines ending point direction
180 * @param av DXF variables with offset and extension line length
181 * @param pen pen to draw the extension line
182 */
extensionLine(const RS_ConstructionLine & dimLine,const RS_Vector & dimPoint,const RS_Vector & dirStart,const RS_Vector & dirEnd,const LC_DimAngularVars & av,const RS_Pen & pen)183 void RS_DimAngular::extensionLine(const RS_ConstructionLine& dimLine,
184 const RS_Vector& dimPoint,
185 const RS_Vector& dirStart,
186 const RS_Vector& dirEnd,
187 const LC_DimAngularVars& av,
188 const RS_Pen& pen)
189 {
190 double diffLine {RS_Vector::posInLine( dimLine.getStartpoint(), dimLine.getEndpoint(), dimPoint)};
191 double diffCenter {RS_Vector::posInLine( dimLine.getStartpoint(), dimCenter, dimPoint)};
192
193 if( 0.0 <= diffLine && 1.0 >= diffLine) {
194 // dimension ends on entity, nothing to extend
195 return;
196 }
197
198 if( 0.0 > diffLine && 0.0 > diffCenter) {
199 RS_Line* line {new RS_Line( this,
200 dimLine.getStartpoint(),
201 dimPoint - dirStart * av.exe())};
202
203 line->setPen( pen);
204 line->setLayer( nullptr);
205 addEntity( line);
206 }
207 else if( 1.0 < diffLine && 0.0 < diffCenter) {
208 RS_Line* line {new RS_Line( this,
209 dimLine.getEndpoint(),
210 dimPoint - dirEnd * av.exe())};
211
212 line->setPen( pen);
213 line->setLayer( nullptr);
214 addEntity( line);
215 }
216 else if( 0.0 > diffLine && 1.0 < diffCenter) {
217 RS_Line* line {new RS_Line( this,
218 dimCenter - dirStart * av.exo(),
219 dimPoint + dirEnd * av.exe())};
220
221 line->setPen( pen);
222 line->setLayer( nullptr);
223 addEntity( line);
224 }
225 }
226
227 /**
228 * @brief Add an arrow to the dimension arc
229 *
230 * @param point arc endpoint, the arrow tip
231 * @param angle the angle from center to the arc endpoint
232 * @param direction this holds the sign for the arrow endpoint direction
233 * @param outsideArrow when the arc becomes too small, arrows are placed outside
234 * @param av DXF variables with offset and extension line length
235 * @param pen pen to draw the extension line
236 */
arrow(const RS_Vector & point,const double angle,const double direction,const bool outsideArrows,const LC_DimAngularVars & av,const RS_Pen & pen)237 void RS_DimAngular::arrow(const RS_Vector& point,
238 const double angle,
239 const double direction,
240 const bool outsideArrows,
241 const LC_DimAngularVars& av,
242 const RS_Pen& pen)
243 {
244 double arrowAngle {0.0};
245
246 if (outsideArrows) {
247 // for outside arrows use tangent angle on endpoints
248 // because for small radius the arrows looked inclined
249 arrowAngle = angle + std::copysign( M_PI_2, direction);
250 }
251 else {
252 // compute the angle from center to the endpoint of the arrow on the arc
253 double endAngle {0.0};
254 if (RS_TOLERANCE_ANGLE < dimRadius) {
255 endAngle = av.arrow() / dimRadius;
256 }
257
258 // compute the endpoint of the arrow on the arc
259 RS_Vector arrowEnd;
260 arrowEnd.setPolar( dimRadius, angle + std::copysign( endAngle, direction));
261 arrowEnd += dimCenter;
262 arrowAngle = arrowEnd.angleTo( point);
263 }
264
265 RS_SolidData sd;
266 RS_Solid* arrow;
267
268 arrow = new RS_Solid( this, sd);
269 arrow->shapeArrow( point, arrowAngle, av.arrow());
270 arrow->setPen( pen);
271 arrow->setLayer( nullptr);
272 addEntity( arrow);
273
274 }
275
276 /**
277 * Updates the sub entities of this dimension. Called when the
278 * dimension or the position, alignment, .. changes.
279 *
280 * @param autoText Automatically reposition the text label
281 */
updateDim(bool autoText)282 void RS_DimAngular::updateDim(bool autoText /*= false*/)
283 {
284 Q_UNUSED( autoText)
285 RS_DEBUG->print("RS_DimAngular::update");
286
287 clear();
288
289 if (isUndone()) {
290 return;
291 }
292
293 if ( ! dimCenter.valid) {
294 return;
295 }
296
297 LC_DimAngularVars av( getGeneralScale(),
298 getExtensionLineOffset(),
299 getExtensionLineExtension(),
300 getTextHeight(),
301 getDimensionLineGap(),
302 getArrowSize());
303
304 // create new lines with offsets for extension lines
305 RS_ConstructionLine line1( nullptr,
306 RS_ConstructionLineData( dimLine1.getStartpoint() - dimDir1s * av.exo(),
307 dimLine1.getEndpoint() - dimDir1e * av.exo()));
308 RS_ConstructionLine line2( nullptr,
309 RS_ConstructionLineData( dimLine2.getStartpoint() - dimDir2s * av.exo(),
310 dimLine2.getEndpoint() - dimDir2e * av.exo()));
311
312 RS_Vector p1 {dimCenter + dimDir1e * dimRadius};
313 RS_Vector p2 {dimCenter + dimDir2e * dimRadius};
314 RS_Pen pen( getExtensionLineColor(), getExtensionLineWidth(), RS2::LineByBlock);
315
316 extensionLine( line1, p1, dimDir1s, dimDir1e, av, pen);
317 extensionLine( line2, p2, dimDir2s, dimDir2e, av, pen);
318
319 // Create dimension line (arc)
320 RS_Arc* arc {new RS_Arc( this, RS_ArcData( dimCenter, dimRadius, dimAngleL1, dimAngleL2, false))};
321 pen.setWidth( getDimensionLineWidth());
322 pen.setColor( getDimensionLineColor());
323 arc->setPen( pen);
324 arc->setLayer( nullptr);
325 addEntity( arc);
326
327 // do we have to put the arrows outside of the arc?
328 bool outsideArrows {arc->getLength() < 3.0 * av.arrow()};
329
330 arrow( p1, dimAngleL1, +1.0, outsideArrows, av, pen);
331 arrow( p2, dimAngleL2, -1.0, outsideArrows, av, pen);
332
333 // text label
334 RS_MTextData textData;
335 RS_Vector textPos {arc->getMiddlePoint()};
336
337 RS_Vector distV;
338 double textAngle {0.0};
339 double angle1 {textPos.angleTo( dimCenter) - M_PI_2};
340
341 // rotate text so it's readable from the bottom or right (ISO)
342 // quadrant 1 & 4
343 if (angle1 > M_PI_2 * 3.0 + 0.001
344 || angle1 < M_PI_2 + 0.001) {
345 distV.setPolar( av.gap(), angle1 + M_PI_2);
346 textAngle = angle1;
347 }
348 // quadrant 2 & 3
349 else {
350 distV.setPolar( av.gap(), angle1 - M_PI_2);
351 textAngle = angle1 + M_PI;
352 }
353
354 // move text away from dimension line:
355 textPos += distV;
356
357 textData = RS_MTextData( textPos,
358 av.txt(), 30.0,
359 RS_MTextData::VABottom,
360 RS_MTextData::HACenter,
361 RS_MTextData::LeftToRight,
362 RS_MTextData::Exact,
363 1.0,
364 getLabel(),
365 getTextStyle(),
366 textAngle);
367
368 RS_MText* text {new RS_MText( this, textData)};
369
370 // move text to the side:
371 text->setPen( RS_Pen( getTextColor(), RS2::WidthByBlock, RS2::SolidLine));
372 text->setLayer( nullptr);
373 addEntity( text);
374
375 calculateBorders();
376 }
377
update()378 void RS_DimAngular::update()
379 {
380 calcDimension();
381 RS_Dimension::update();
382 }
383
move(const RS_Vector & offset)384 void RS_DimAngular::move(const RS_Vector& offset)
385 {
386 RS_Dimension::move( offset);
387
388 edata.definitionPoint1.move( offset);
389 edata.definitionPoint2.move( offset);
390 edata.definitionPoint3.move( offset);
391 edata.definitionPoint4.move( offset);
392 update();
393 }
394
rotate(const RS_Vector & center,const double & angle)395 void RS_DimAngular::rotate(const RS_Vector& center, const double& angle)
396 {
397 rotate( center, RS_Vector( angle));
398 }
399
rotate(const RS_Vector & center,const RS_Vector & angleVector)400 void RS_DimAngular::rotate(const RS_Vector& center, const RS_Vector& angleVector)
401 {
402 RS_Dimension::rotate( center, angleVector);
403
404 edata.definitionPoint1.rotate( center, angleVector);
405 edata.definitionPoint2.rotate( center, angleVector);
406 edata.definitionPoint3.rotate( center, angleVector);
407 edata.definitionPoint4.rotate( center, angleVector);
408 update();
409 }
410
scale(const RS_Vector & center,const RS_Vector & factor)411 void RS_DimAngular::scale(const RS_Vector& center, const RS_Vector& factor)
412 {
413 RS_Dimension::scale( center, factor);
414
415 edata.definitionPoint1.scale( center, factor);
416 edata.definitionPoint2.scale( center, factor);
417 edata.definitionPoint3.scale( center, factor);
418 edata.definitionPoint4.scale( center, factor);
419 update();
420 }
421
mirror(const RS_Vector & axisPoint1,const RS_Vector & axisPoint2)422 void RS_DimAngular::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2)
423 {
424 RS_Dimension::mirror( axisPoint1, axisPoint2);
425
426 edata.definitionPoint1.mirror( axisPoint1, axisPoint2);
427 edata.definitionPoint2.mirror( axisPoint1, axisPoint2);
428 edata.definitionPoint3.mirror( axisPoint1, axisPoint2);
429 edata.definitionPoint4.mirror( axisPoint1, axisPoint2);
430 update();
431 }
432
433 /**
434 * @brief Compute all static values for dimension.
435 *
436 * From DXF reference the lines are P2-P1 and P-P3.
437 * The dimension is drawn from line1 (P2-P1) to line2 (P-P3) in CCW direction.
438 */
calcDimension(void)439 void RS_DimAngular::calcDimension(void)
440 {
441 // get unit vectors for definition points
442 dimDir1s = RS_Vector::polar( 1.0, RS_Math::correctAngle( edata.definitionPoint2.angleTo( edata.definitionPoint1)));
443 dimDir1e = RS_Vector::polar( 1.0, RS_Math::correctAngle( edata.definitionPoint1.angleTo( edata.definitionPoint2)));
444 dimDir2s = RS_Vector::polar( 1.0, RS_Math::correctAngle( data.definitionPoint.angleTo( edata.definitionPoint3)));
445 dimDir2e = RS_Vector::polar( 1.0, RS_Math::correctAngle( edata.definitionPoint3.angleTo( data.definitionPoint)));
446
447 // create the two dimension definition lines
448 dimLine1 = RS_ConstructionLine( nullptr,
449 RS_ConstructionLineData( edata.definitionPoint2,
450 edata.definitionPoint1));
451 dimLine2 = RS_ConstructionLine( nullptr,
452 RS_ConstructionLineData( data.definitionPoint,
453 edata.definitionPoint3));
454
455 RS_VectorSolutions vs {RS_Information::getIntersection( &dimLine1, &dimLine2, false)};
456 dimCenter = vs.get(0);
457 dimRadius = dimCenter.distanceTo( edata.definitionPoint4);
458 dimDirRad = RS_Vector::polar( 1.0, RS_Math::correctAngle( dimCenter.angleTo( edata.definitionPoint4)));
459
460 fixDimension();
461
462 dimAngleL1 = dimLine1.getDirection2();
463 dimAngleL2 = dimLine2.getDirection2();
464
465 dimAngle = RS_Math::correctAngle( dimLine2.getDirection1() - dimLine1.getDirection1());
466 }
467
468 /**
469 * @brief check the dimension and fix non conform values from foreign CAD systems
470 *
471 * check if the radius definition point is on the arc,
472 * from line1 to line2 in counter clockwise direction
473 * LibreCAD takes care on correct orientation and line order in RS_ActionDimAngular
474 * but angular dimensions, created in other CAD software, may fail and must be fixed here
475 */
fixDimension(void)476 void RS_DimAngular::fixDimension(void)
477 {
478 if( ! RS_Math::isAngleBetween( dimDirRad.angle(), dimDir2s.angle(), dimDir1s.angle(), false)) {
479 double distance0 {data.definitionPoint.distanceTo( dimCenter)};
480 double distance1 {edata.definitionPoint1.distanceTo( dimCenter)};
481 double distance2 {edata.definitionPoint2.distanceTo( dimCenter)};
482 double distance3 {edata.definitionPoint3.distanceTo( dimCenter)};
483 double angle0 {0.0};
484 double angle1 {0.0};
485 double angle2 {0.0};
486 double angle3 {0.0};
487 if( RS_TOLERANCE >= distance0) {
488 angle3 = (edata.definitionPoint3 - dimCenter).angle();
489 angle0 = angle3;
490 }
491 else if( RS_TOLERANCE >= distance3) {
492 angle0 = (data.definitionPoint - dimCenter).angle();
493 angle3 = angle0;
494 }
495 else {
496 angle0 = (data.definitionPoint - dimCenter).angle();
497 angle3 = (edata.definitionPoint3 - dimCenter).angle();
498 }
499
500 if( RS_TOLERANCE >= distance1) {
501 angle2 = (edata.definitionPoint2- dimCenter).angle();
502 angle1 = angle2;
503 }
504 else if( RS_TOLERANCE >= distance2) {
505 angle1 = (edata.definitionPoint1 - dimCenter).angle();
506 angle2 = angle1;
507 }
508 else {
509 angle1 = (edata.definitionPoint1 - dimCenter).angle();
510 angle2 = (edata.definitionPoint2 - dimCenter).angle();
511 }
512
513 if( angle2 == angle1
514 && distance2 < distance1
515 && angle0 == angle3
516 && distance0 < distance3) {
517 // revert both lines
518 dimLine1 = RS_ConstructionLine( nullptr,
519 RS_ConstructionLineData( dimLine1.getEndpoint(),
520 dimLine1.getStartpoint()));
521 dimLine2 = RS_ConstructionLine( nullptr,
522 RS_ConstructionLineData( dimLine2.getEndpoint(),
523 dimLine2.getStartpoint()));
524
525 // and their unit vectors
526 RS_Vector swapDir {dimDir1s};
527 dimDir1s = dimDir1e;
528 dimDir1e = swapDir;
529
530 swapDir = dimDir2s;
531 dimDir2s = dimDir2e;
532 dimDir2e = swapDir;
533 }
534
535 // check again, as the previous revert may have made this condition false
536 if( ! RS_Math::isAngleBetween( dimDirRad.angle(), dimDir2s.angle(), dimDir1s.angle(), false)) {
537 // swap the lines
538 RS_ConstructionLine swapLine {dimLine1};
539 dimLine1 = dimLine2;
540 dimLine2 = swapLine;
541
542 // and their unit vectors
543 RS_Vector swapDir {dimDir1s};
544 dimDir1s = dimDir2s;
545 dimDir2s = swapDir;
546
547 swapDir = dimDir1e;
548 dimDir1e = dimDir2e;
549 dimDir2e = swapDir;
550 }
551 }
552 }
553
554 /**
555 * Dumps the point's data to stdout.
556 */
operator <<(std::ostream & os,const RS_DimAngular & d)557 std::ostream& operator << (std::ostream& os, const RS_DimAngular& d)
558 {
559 os << " DimAngular: "
560 << d.getData() << std::endl
561 << d.getEData() << std::endl;
562
563 return os;
564 }
565
operator <<(std::ostream & os,const RS_DimAngularData & dd)566 std::ostream& operator << (std::ostream& os, const RS_DimAngularData& dd)
567 {
568 os << "(" << dd.definitionPoint1
569 << "," << dd.definitionPoint2
570 << "," << dd.definitionPoint3
571 << "," << dd.definitionPoint3
572 << ")";
573
574 return os;
575 }
576