1 /****************************************************************************
2 **
3 ** This file is part of the LibreCAD project, a 2D CAD program
4 **
5 ** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
6 ** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
7 **
8 **
9 ** This file may be distributed and/or modified under the terms of the
10 ** GNU General Public License version 2 as published by the Free Software
11 ** Foundation and appearing in the file gpl-2.0.txt included in the
12 ** packaging of this file.
13 **
14 ** This program is distributed in the hope that it will be useful,
15 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ** GNU General Public License for more details.
18 **
19 ** You should have received a copy of the GNU General Public License
20 ** along with this program; if not, write to the Free Software
21 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 **
23 ** This copyright notice MUST APPEAR in all copies of the script!
24 **
25 **********************************************************************/
26 #include<iostream>
27 #include<cmath>
28 #include<string>
29 #include "rs_information.h"
30 #include "rs_line.h"
31 #include "rs_dimension.h"
32 #include "rs_solid.h"
33 #include "rs_units.h"
34 #include "rs_math.h"
35 #include "rs_filterdxfrw.h" //for int <-> rs_color conversion
36 #include "rs_debug.h"
37 
RS_DimensionData()38 RS_DimensionData::RS_DimensionData():
39 	definitionPoint(false),
40 	middleOfText(false),
41 	valign(RS_MTextData::VABottom),
42 	halign(RS_MTextData::HALeft),
43 	lineSpacingStyle(RS_MTextData::Exact),
44 	lineSpacingFactor(0.0),
45 	text(""),
46 	style(""),
47 	angle(0.0)
48 {}
49 
50 /**
51  * Constructor with initialisation.
52  *
53  * @param definitionPoint Definition point.
54  * @param middleOfText Middle point of dimension text.
55  * @param valign Vertical alignment.
56  * @param halign Horizontal alignment.
57  * @param lineSpacingStyle Line spacing style.
58  * @param lineSpacingFactor Line spacing factor.
59  * @param text Text string entered explicitly by user or null
60  *         or "<>" for the actual measurement or " " (one blank space).
61  *         for suppressing the text.
62  * @param style Dimension style name.
63  * @param angle Rotation angle of dimension text away from
64  *         default orientation.
65  */
RS_DimensionData(const RS_Vector & _definitionPoint,const RS_Vector & _middleOfText,RS_MTextData::VAlign _valign,RS_MTextData::HAlign _halign,RS_MTextData::MTextLineSpacingStyle _lineSpacingStyle,double _lineSpacingFactor,QString _text,QString _style,double _angle)66 RS_DimensionData::RS_DimensionData(const RS_Vector& _definitionPoint,
67 				 const RS_Vector& _middleOfText,
68 				 RS_MTextData::VAlign _valign,
69 				 RS_MTextData::HAlign _halign,
70 				 RS_MTextData::MTextLineSpacingStyle _lineSpacingStyle,
71 				 double _lineSpacingFactor,
72 				 QString _text,
73 				 QString _style,
74 				 double _angle):
75 	definitionPoint(_definitionPoint)
76 	,middleOfText(_middleOfText)
77 	,valign(_valign)
78 	,halign(_halign)
79 	,lineSpacingStyle(_lineSpacingStyle)
80 	,lineSpacingFactor(_lineSpacingFactor)
81 	,text(_text)
82 	,style(_style)
83 	,angle(_angle)
84 {
85 }
86 
operator <<(std::ostream & os,const RS_DimensionData & dd)87 std::ostream& operator << (std::ostream& os,
88 						   const RS_DimensionData& dd) {
89 	os << "("
90 	   << dd.definitionPoint<<','
91 	   <<dd.middleOfText<<','
92 	  <<dd.valign<<','
93 	 <<dd.halign<<','
94 	<<dd.lineSpacingStyle<<','
95 	<<dd.lineSpacingFactor<<','
96 	<<dd.text.toLatin1().data() <<','
97 	<<dd.style.toLatin1().data()<<','
98 	<<dd.angle
99 	<< ")";
100 	return os;
101 }
102 
103 /**
104  * Constructor.
105  */
RS_Dimension(RS_EntityContainer * parent,const RS_DimensionData & d)106 RS_Dimension::RS_Dimension(RS_EntityContainer* parent,
107                            const RS_DimensionData& d)
108         : RS_EntityContainer(parent), data(d) {
109 }
110 
getNearestRef(const RS_Vector & coord,double * dist) const111 RS_Vector RS_Dimension::getNearestRef( const RS_Vector& coord,
112                                        double* dist /*= nullptr*/) const
113 {
114     // override the RS_EntityContainer method
115     // use RS_Entity instead for refpoint dragging
116     return RS_Entity::getNearestRef( coord, dist);
117 }
118 
getNearestSelectedRef(const RS_Vector & coord,double * dist) const119 RS_Vector RS_Dimension::getNearestSelectedRef( const RS_Vector& coord,
120                                                double* dist /*= nullptr*/) const
121 {
122     // override the RS_EntityContainer method
123     // use RS_Entity instead for refpoint dragging
124     return RS_Entity::getNearestSelectedRef( coord, dist);
125 }
126 
127 
128 /**
129  * @return Dimension text. Either a text the user defined or
130  *         the measured text.
131  *
132  * @param resolve false: return plain value. true: return measured
133  *      label if appropriate.
134  * @see getMeasuredLabel
135  */
getLabel(bool resolve)136 QString RS_Dimension::getLabel(bool resolve) {
137         if (!resolve) {
138                 return data.text;
139         }
140 
141     QString ret="";
142 
143     // One space suppresses the text:
144     if (data.text==" ") {
145         ret = "";
146     }
147 
148     // No text prints actual measurement:
149     else if (data.text=="") {
150         ret = getMeasuredLabel();
151     }
152 
153     // Others print the text (<> is replaced by the measurement)
154     else {
155         ret = data.text;
156         ret = ret.replace(QString("<>"), getMeasuredLabel());
157     }
158 
159     return ret;
160 }
161 
162 
163 /**
164  * Sets a new text for the label.
165  */
setLabel(const QString & l)166 void RS_Dimension::setLabel(const QString& l) {
167         data.text = l;
168 }
169 
170 
171 /**
172  * Find intersections between a line and an EntityContainer.  Solutions are
173  * sorted along the line before returning.
174  *
175  * @param infiniteLine Treat the line as infinitely long in both directions.
176  */
getIntersectionsLineContainer(const RS_Line * l,const RS_EntityContainer * c,bool infiniteLine)177 RS_VectorSolutions RS_Dimension::getIntersectionsLineContainer(
178         const RS_Line* l, const RS_EntityContainer* c, bool infiniteLine)
179 {
180     RS_VectorSolutions solutions_initial;
181     RS_VectorSolutions solutions_filtered;
182     const double tol = 1.0e-4;
183 
184     // Find all intersections, including those beyond limits of container
185     // entities.
186     for(RS_Entity* e: *c) {
187         solutions_initial.push_back(
188             RS_Information::getIntersection(l, e, false)
189         );
190     }
191 
192     // Filter solutions based on whether they are actually on any entities.
193     for(const RS_Vector& vp: solutions_initial){
194         for(RS_Entity* e: *c) {
195             if (e->isConstruction(true) || e->isPointOnEntity(vp, tol)) {
196                 // the intersection is at least on the container, now check the line:
197                 if (infiniteLine) {
198                     // The line is treated as infinitely long so we don't need to
199                     // check if the intersection is on the line.
200                     solutions_filtered.push_back(vp);
201                     break;
202                 } else if (l->isConstruction(true) || l->isPointOnEntity(vp, tol)) {
203                     solutions_filtered.push_back(vp);
204                     break;
205                 }
206             }
207         }
208     }
209 
210     /**
211      * We cannot sort the solutions in place because getVector() returns a
212      * const vector, so first construct a copy:
213      */
214     std::vector<RS_Vector> solutions_sorted(solutions_filtered.getVector());
215     std::sort(solutions_sorted.begin(), solutions_sorted.end(),
216               [l](const RS_Vector& lhs, const RS_Vector& rhs)
217         {
218             return l->getProjectionValueAlongLine(lhs)
219                    < l->getProjectionValueAlongLine(rhs);
220         });
221 
222     return RS_VectorSolutions(solutions_sorted);
223 }
224 
225 
226 /**
227  * Creates a horizontal-text dimensioning line (line with one, two or no arrows
228  * and "inside horizontal" text).
229  *
230  * @param forceAutoText Automatically reposition the text label.
231  */
updateCreateHorizontalTextDimensionLine(const RS_Vector & p1,const RS_Vector & p2,bool arrow1,bool arrow2,bool forceAutoText)232 void RS_Dimension::updateCreateHorizontalTextDimensionLine(const RS_Vector& p1,
233         const RS_Vector& p2, bool arrow1, bool arrow2, bool forceAutoText) {
234     // general scale (DIMSCALE)
235     double dimscale = getGeneralScale();
236     // text height (DIMTXT)
237     double dimtxt = getTextHeight()*dimscale;
238     // text distance to line (DIMGAP)
239     double dimgap = getDimensionLineGap()*dimscale;
240 
241     // length of dimension line:
242     double distance = p1.distanceTo(p2);
243     // arrow size:
244     double arrowSize = getArrowSize()*dimscale;
245 
246     // arrow angles:
247     double arrowAngle1, arrowAngle2;
248 
249     RS_Pen pen(getDimensionLineColor(),
250            getDimensionLineWidth(),
251            RS2::LineByBlock);
252 
253     // Create dimension line:
254     RS_Line* dimensionLine {new RS_Line{this, p1, p2}};
255     RS_Line* dimensionLineInside1 {nullptr};
256     RS_Line* dimensionLineInside2 {nullptr};
257     RS_Line* dimensionLineOutside1 {nullptr};
258     RS_Line* dimensionLineOutside2 {nullptr};
259     dimensionLine->setPen(pen);
260     dimensionLine->setLayer(nullptr);
261 
262     // Text label:
263     RS_MTextData textData;
264     RS_Vector textPos;
265     double textAngle = 0.0;
266     bool autoText = !data.middleOfText.valid || forceAutoText;
267 
268     if (autoText) {
269         textPos = dimensionLine->getMiddlePoint();
270 
271         //// the next update should still be able to adjust this
272         ////   auto text position. leave it invalid
273         data.middleOfText = textPos;
274     } else {
275         textPos = data.middleOfText;
276     }
277 
278     textData = RS_MTextData(textPos,
279                             dimtxt, 30.0,
280                             RS_MTextData::VAMiddle,
281                             RS_MTextData::HACenter,
282                             RS_MTextData::LeftToRight,
283                             RS_MTextData::Exact,
284                             1.0,
285                             getLabel(),
286                             getTextStyle(),
287                             textAngle);
288 
289     RS_MText* text = new RS_MText(this, textData);
290     text->setPen(RS_Pen(getTextColor(), RS2::WidthByBlock, RS2::SolidLine));
291     text->setLayer(nullptr);
292 
293     // evaluate intersection between dim line and text
294     double textIntersectionLength = 0.0;
295     double w = text->getUsedTextWidth()/2+dimgap;
296     double h = text->getUsedTextHeight()/2+dimgap;
297     // textCorner variables correspond to the corners of the text bounding box
298     // if the text were to be positioned in the center of the dimensionLine.
299     RS_Vector textCorner1 = dimensionLine->getMiddlePoint() - RS_Vector{w, h};
300     RS_Vector textCorner2 = dimensionLine->getMiddlePoint() + RS_Vector{w, h};
301     RS_EntityContainer c;
302     c.addRectangle(textCorner1, textCorner2);
303     RS_VectorSolutions sol1 = getIntersectionsLineContainer(
304         dimensionLine, &c,
305         true  // treat line as infinitely long in both directions
306         );
307     textIntersectionLength = sol1.get(0).distanceTo(sol1.get(1));
308 
309     // determine if we should use outside arrows
310     bool outsideArrows = (textIntersectionLength+3*arrowSize) > distance;
311 
312     // add arrows
313     if (outsideArrows==false) {
314         arrowAngle1 = dimensionLine->getAngle2();
315         arrowAngle2 = dimensionLine->getAngle1();
316     } else {
317         arrowAngle1 = dimensionLine->getAngle1();
318         arrowAngle2 = dimensionLine->getAngle2();
319 
320         // extend dimension line outside arrows
321         RS_Vector dir = RS_Vector::polar(arrowSize*2, dimensionLine->getAngle1());
322         dimensionLineOutside1 = new RS_Line{this, p1 - dir, p1};
323         dimensionLineOutside2 = new RS_Line{this, p2 + dir, p2};
324 
325         // move text to the side if it won't fit either
326         RS_Vector distH;
327         if (textIntersectionLength>distance && autoText) {
328             distH.setPolar(textIntersectionLength/2.0+arrowSize*2+distance/2.0,
329                            arrowAngle1);
330             text->move(distH);
331             textPos = text->getInsertionPoint();
332             data.middleOfText = textPos;
333         }
334     }
335     double dimtsz=getTickSize()*dimscale;
336     bool displayArrows = dimtsz < 0.01;
337     if(displayArrows) {
338         //display arrow
339         // Arrows:
340         RS_SolidData sd;
341         RS_Solid* arrow;
342 
343         if (arrow1) {
344             // arrow 1
345             arrow = new RS_Solid(this, sd);
346             arrow->shapeArrow(p1,
347                               arrowAngle1,
348                               arrowSize);
349             arrow->setPen(pen);
350             arrow->setLayer(nullptr);
351             addEntity(arrow);
352         }
353 
354         if (arrow2) {
355             // arrow 2:
356             arrow = new RS_Solid(this, sd);
357             arrow->shapeArrow(p2,
358                               arrowAngle2,
359                               arrowSize);
360             arrow->setPen(pen);
361             arrow->setLayer(nullptr);
362             addEntity(arrow);
363         }
364     } else {
365         //display ticks
366         // Arrows:
367 
368         RS_Line* tick;
369         RS_Vector tickVector = RS_Vector::polar(dimtsz,arrowAngle1 + M_PI*0.25); //tick is 45 degree away
370 
371         if (arrow1) {
372             // tick 1
373             tick = new RS_Line(this, p1-tickVector, p1+tickVector);
374             tick->setPen(pen);
375             tick->setLayer(nullptr);
376             addEntity(tick);
377         }
378 
379         if (arrow2) {
380             // tick 2:
381             tick = new RS_Line(this, p2-tickVector, p2+tickVector);
382             tick->setPen(pen);
383             tick->setLayer(nullptr);
384             addEntity(tick);
385         }
386     }
387 
388     // calculate split dimension lines
389     bool splitDimensionLine = false;
390     if (!outsideArrows) {
391         w = text->getUsedTextWidth()/2+dimgap;
392         h = text->getUsedTextHeight()/2+dimgap;
393         RS_Vector s1 = text->getInsertionPoint() - RS_Vector{w, h};
394         RS_Vector s2 = text->getInsertionPoint() + RS_Vector{w, h};
395         c = RS_EntityContainer();
396         c.addRectangle(s1, s2);
397         sol1 = getIntersectionsLineContainer(dimensionLine, &c);
398         if (sol1.size()>1) {
399             // the text bounding box intersects dimensionLine on two sides
400             splitDimensionLine = true;
401             s1 = sol1.get(0);
402             s2 = sol1.get(1);
403         } else if (sol1.size()==1) {
404             // the text bounding box intersects dimensionLine on one side
405             splitDimensionLine = true;
406             if (RS_Information::isPointInsideContour(p1, &c)) {
407                 // the dimension line begins inside the text bounds
408                 s1 = p1;
409                 s2 = sol1.get(0);
410             } else {
411                 // the dimension line ends inside the text bounds
412                 s1 = sol1.get(0);
413                 s2 = p2;
414             }
415         } else {
416             // the text bounding box does not intersect with dimensionLine, but we
417             // should still check if dimensionLine endpoints are completely inside
418             // the bounding box.
419             if (RS_Information::isPointInsideContour(p1, &c)) {
420                 splitDimensionLine = true;
421                 s1 = p1;
422                 s2 = p2;
423             }
424         }
425 
426         if (splitDimensionLine) {
427             dimensionLineInside1 = new RS_Line{this, p1, s1};
428             dimensionLineInside2 = new RS_Line{this, s2, p2};
429         }
430     }
431 
432     // finally, add the dimension line(s) and text to the drawing
433     if (outsideArrows && dimensionLineOutside1) {
434         addEntity(dimensionLineOutside1);
435         addEntity(dimensionLineOutside2);
436     } else if (splitDimensionLine && dimensionLineInside1) {
437         addEntity(dimensionLineInside1);
438         addEntity(dimensionLineInside2);
439     } else {
440         addEntity(dimensionLine);
441     }
442 
443     addEntity(text);
444 }
445 
446 
447 /**
448  * Creates an aligned-text dimensioning line (line with one, two or no arrows
449  * and aligned text).
450  *
451  * @param forceAutoText Automatically reposition the text label.
452  */
updateCreateAlignedTextDimensionLine(const RS_Vector & p1,const RS_Vector & p2,bool arrow1,bool arrow2,bool forceAutoText)453 void RS_Dimension::updateCreateAlignedTextDimensionLine(const RS_Vector& p1,
454         const RS_Vector& p2, bool arrow1, bool arrow2, bool forceAutoText) {
455     // general scale (DIMSCALE)
456     double dimscale = getGeneralScale();
457     // text height (DIMTXT)
458     double dimtxt = getTextHeight()*dimscale;
459     // text distance to line (DIMGAP)
460     double dimgap = getDimensionLineGap()*dimscale;
461 
462     // length of dimension line:
463     double distance = p1.distanceTo(p2);
464     // arrow size:
465     double arrowSize = getArrowSize()*dimscale;
466 
467     // do we have to put the arrows outside of the line?
468     bool outsideArrows = (distance<arrowSize*2.5);
469 
470     // arrow angles:
471     double arrowAngle1, arrowAngle2;
472 
473     RS_Pen pen(getDimensionLineColor(),
474            getDimensionLineWidth(),
475            RS2::LineByBlock);
476 
477     // Create dimension line:
478     RS_Line* dimensionLine = new RS_Line{this, p1, p2};
479     dimensionLine->setPen(pen);
480     dimensionLine->setLayer(nullptr);
481     addEntity(dimensionLine);
482 
483     // Text label:
484     RS_MTextData textData;
485     RS_Vector textPos;
486     double dimAngle1 = dimensionLine->getAngle1();
487     bool corrected = false;
488     double textAngle = RS_Math::makeAngleReadable(dimAngle1, true, &corrected);
489 
490     if (data.middleOfText.valid && !forceAutoText) {
491         textPos = data.middleOfText;
492     } else {
493         textPos = dimensionLine->getMiddlePoint();
494 
495         // rotate text so it's readable from the bottom or right (ISO)
496         // quadrant 1 & 4
497         double const a = corrected?-M_PI_2:M_PI_2;
498         RS_Vector distV = RS_Vector::polar(dimgap + dimtxt/2.0, dimAngle1+a);
499 
500         // move text away from dimension line:
501         textPos+=distV;
502 
503         //// the next update should still be able to adjust this
504         ////   auto text position. leave it invalid
505         data.middleOfText = textPos;
506     }
507 
508     textData = RS_MTextData(textPos,
509                             dimtxt, 30.0,
510                             RS_MTextData::VAMiddle,
511                             RS_MTextData::HACenter,
512                             RS_MTextData::LeftToRight,
513                             RS_MTextData::Exact,
514                             1.0,
515                             getLabel(),
516                             getTextStyle(),
517                             textAngle);
518 
519     RS_MText* text = new RS_MText(this, textData);
520     text->setPen(RS_Pen(getTextColor(), RS2::WidthByBlock, RS2::SolidLine));
521     text->setLayer(nullptr);
522 
523     // move text to the side:
524     RS_Vector distH;
525     if (text->getUsedTextWidth()>distance) {
526         distH.setPolar(text->getUsedTextWidth()/2.0
527                        +distance/2.0+dimgap, textAngle);
528         text->move(distH);
529     }
530 
531     addEntity(text);
532 
533     // add arrows
534     if (outsideArrows==false) {
535         arrowAngle1 = dimensionLine->getAngle2();
536         arrowAngle2 = dimensionLine->getAngle1();
537     } else {
538         arrowAngle1 = dimensionLine->getAngle1();
539         arrowAngle2 = dimensionLine->getAngle2();
540 
541         // extend dimension line outside arrows
542         RS_Vector dir = RS_Vector::polar(arrowSize*2, arrowAngle2);
543         dimensionLine->setStartpoint(p1 + dir);
544         dimensionLine->setEndpoint(p2 - dir);
545     }
546     double dimtsz=getTickSize()*dimscale;
547     if(dimtsz < 0.01) {
548         //display arrow
549         // Arrows:
550         RS_SolidData sd;
551         RS_Solid* arrow;
552 
553         if (arrow1) {
554             // arrow 1
555             arrow = new RS_Solid(this, sd);
556             arrow->shapeArrow(p1,
557                               arrowAngle1,
558                               arrowSize);
559             arrow->setPen(pen);
560             arrow->setLayer(nullptr);
561             addEntity(arrow);
562         }
563 
564         if (arrow2) {
565             // arrow 2:
566             arrow = new RS_Solid(this, sd);
567             arrow->shapeArrow(p2,
568                               arrowAngle2,
569                               arrowSize);
570             arrow->setPen(pen);
571             arrow->setLayer(nullptr);
572             addEntity(arrow);
573         }
574     }else{
575         //display ticks
576         // Arrows:
577 
578         RS_Line* tick;
579         RS_Vector tickVector = RS_Vector::polar(dimtsz,arrowAngle1 + M_PI*0.25); //tick is 45 degree away
580 
581         if (arrow1) {
582             // tick 1
583             tick = new RS_Line(this, p1-tickVector, p1+tickVector);
584             tick->setPen(pen);
585             tick->setLayer(nullptr);
586             addEntity(tick);
587         }
588 
589         if (arrow2) {
590             // tick 2:
591             tick = new RS_Line(this, p2-tickVector, p2+tickVector);
592             tick->setPen(pen);
593             tick->setLayer(nullptr);
594             addEntity(tick);
595         }
596     }
597 }
598 
599 
600 /**
601  * Creates a dimensioning line (line with one, two or no arrows and a text).
602  *
603  * @param forceAutoText Automatically reposition the text label.
604  */
updateCreateDimensionLine(const RS_Vector & p1,const RS_Vector & p2,bool arrow1,bool arrow2,bool forceAutoText)605 void RS_Dimension::updateCreateDimensionLine(const RS_Vector& p1,
606         const RS_Vector& p2, bool arrow1, bool arrow2, bool forceAutoText) {
607     if (getInsideHorizontalText())
608         updateCreateHorizontalTextDimensionLine(p1, p2, arrow1, arrow2, forceAutoText);
609     else
610         updateCreateAlignedTextDimensionLine(p1, p2, arrow1, arrow2, forceAutoText);
611 }
612 
613 
614 /**
615  * @return general factor for linear dimensions.
616  */
getGeneralFactor()617 double RS_Dimension::getGeneralFactor() {
618     return getGraphicVariable("$DIMLFAC", 1.0, 40);
619 }
620 
621 /**
622  * @return general scale for dimensions.
623  */
getGeneralScale()624 double RS_Dimension::getGeneralScale() {
625     return getGraphicVariable("$DIMSCALE", 1.0, 40);
626 }
627 
628 /**
629  * @return arrow size in drawing units.
630  */
getArrowSize()631 double RS_Dimension::getArrowSize() {
632     return getGraphicVariable("$DIMASZ", 2.5, 40);
633 }
634 
635 /**
636  * @return tick size in drawing units.
637  */
getTickSize()638 double RS_Dimension::getTickSize() {
639     return getGraphicVariable("$DIMTSZ", 0., 40);
640 }
641 
642 
643 /**
644  * @return extension line overlength in drawing units.
645  */
getExtensionLineExtension()646 double RS_Dimension::getExtensionLineExtension() {
647     return getGraphicVariable("$DIMEXE", 1.25, 40);
648 }
649 
650 
651 
652 /**
653  * @return extension line offset from entities in drawing units.
654  */
getExtensionLineOffset()655 double RS_Dimension::getExtensionLineOffset() {
656     return getGraphicVariable("$DIMEXO", 0.625, 40);
657 }
658 
659 
660 
661 /**
662  * @return extension line gap to text in drawing units.
663  */
getDimensionLineGap()664 double RS_Dimension::getDimensionLineGap() {
665     return getGraphicVariable("$DIMGAP", 0.625, 40);
666 }
667 
668 
669 
670 /**
671  * @return Dimension labels text height.
672  */
getTextHeight()673 double RS_Dimension::getTextHeight() {
674     return getGraphicVariable("$DIMTXT", 2.5, 40);
675 }
676 
677 
678 /**
679  * @return Dimension labels alignment text true= horizontal, false= aligned.
680  */
getInsideHorizontalText()681 bool RS_Dimension::getInsideHorizontalText() {
682     int v = getGraphicVariableInt("$DIMTIH", 1);
683     if (v>0) {
684         addGraphicVariable("$DIMTIH", 1, 70);
685         getGraphicVariableInt("$DIMTIH", 1);
686 		return true;
687     }
688 	return false;
689 }
690 
691 
692 /**
693  * @return Dimension fixed length for extension lines true= fixed, false= not fixed.
694  */
getFixedLengthOn()695 bool RS_Dimension::getFixedLengthOn() {
696     int v = getGraphicVariableInt("$DIMFXLON", 0);
697     if (v == 1) {
698         addGraphicVariable("$DIMFXLON", 1, 70);
699         getGraphicVariableInt("$DIMFXLON", 0);
700 		return true;
701     }
702 	return false;
703 }
704 
705 /**
706  * @return Dimension fixed length for extension lines.
707  */
getFixedLength()708 double RS_Dimension::getFixedLength() {
709     return getGraphicVariable("$DIMFXL", 1.0, 40);
710 }
711 
712 
713 /**
714  * @return extension line Width.
715  */
getExtensionLineWidth()716 RS2::LineWidth RS_Dimension::getExtensionLineWidth() {
717     return RS2::intToLineWidth( getGraphicVariableInt("$DIMLWE", -2) ); //default -2 (RS2::WidthByBlock)
718 }
719 
720 
721 /**
722  * @return dimension line Width.
723  */
getDimensionLineWidth()724 RS2::LineWidth RS_Dimension::getDimensionLineWidth() {
725     return RS2::intToLineWidth( getGraphicVariableInt("$DIMLWD", -2) ); //default -2 (RS2::WidthByBlock)
726 }
727 
728 /**
729  * @return dimension line Color.
730  */
getDimensionLineColor()731 RS_Color RS_Dimension::getDimensionLineColor() {
732     return RS_FilterDXFRW::numberToColor(getGraphicVariableInt("$DIMCLRD", 0));
733 }
734 
735 
736 /**
737  * @return extension line Color.
738  */
getExtensionLineColor()739 RS_Color RS_Dimension::getExtensionLineColor() {
740     return RS_FilterDXFRW::numberToColor(getGraphicVariableInt("$DIMCLRE", 0));
741 }
742 
743 
744 /**
745  * @return dimension text Color.
746  */
getTextColor()747 RS_Color RS_Dimension::getTextColor() {
748     return RS_FilterDXFRW::numberToColor(getGraphicVariableInt("$DIMCLRT", 0));
749 }
750 
751 
752 /**
753  * @return text style for dimensions.
754  */
getTextStyle()755 QString RS_Dimension::getTextStyle() {
756     return getGraphicVariableString("$DIMTXSTY", "standard");
757 }
758 
759 
760 /**
761  * @return the given graphic variable or the default value given in mm
762  * converted to the graphic unit.
763  * If the variable is not found it is added with the given default
764  * value converted to the local unit.
765  */
getGraphicVariable(const QString & key,double defMM,int code)766 double RS_Dimension::getGraphicVariable(const QString& key, double defMM,
767                                         int code) {
768 
769     double v = getGraphicVariableDouble(key, RS_MINDOUBLE);
770     if (v<=RS_MINDOUBLE) {
771         addGraphicVariable(
772             key,
773             RS_Units::convert(defMM, RS2::Millimeter, getGraphicUnit()),
774             code);
775         v = getGraphicVariableDouble(key, 1.0);
776     }
777 
778     return v;
779 }
780 
781 /**
782  * Removes zeros from angle string.
783  *
784  * @param angle The string representing angle.
785  * @param zeros Zeros suppression (0 none, 1 suppress leading, 2 suppress trailing, 3 both)
786  * Decimal separator are '.'
787  *
788  * @ret String with the formatted angle.
789  */
790 
stripZerosAngle(QString angle,int zeros)791 QString RS_Dimension::stripZerosAngle(QString angle, int zeros){
792     if (zeros == 0) //do nothing
793         return angle;
794     if (zeros & 2 && (angle.contains(QString('.')) || angle.contains(QString(',')))) {
795         int end = angle.size() - 1;
796         QChar format = angle[end--];  //stores & skip format char
797         while (end > 0 && angle[end] == QChar('0')) // locate first 0 from end
798             end--;
799         if (angle[end] == QChar('.'))
800             end--;
801         angle.truncate(end+1);
802         angle.append(format);
803     }
804     if (zeros & 1){
805 		if (angle[0] == QChar('0') && angle[1] == QChar('.'))
806         angle = angle.remove(0, 1);
807     }
808     return angle;
809 }
810 
811 /**
812  * Removes zeros from linear string.
813  *
814  * @param linear The string representing linear measure.
815  * @param zeros Zeros suppression (see dimzin)
816  *
817  * @ret String with the formatted linear measure.
818  */
819 
stripZerosLinear(QString linear,int zeros)820 QString RS_Dimension::stripZerosLinear(QString linear, int zeros){
821 
822     //do nothing
823     if (zeros == 1)
824         return linear;
825 
826     // return at least 1 character in string
827     int ls = linear.size();
828     if (ls <= 1) {
829         return linear;
830     }
831 
832     // if removing of trailing zeroes is needed
833     if (zeros & 8 && (linear.contains(QString('.')) || linear.contains(QString(',')))) {
834         // search index
835         int i = ls - 1;
836         // locate first 0 in row from right
837         while (i > 0 && linear[i] == QChar('0')) {
838             i--;
839         }
840         // strip decimal point
841         if ((linear[i] == QChar('.') || linear[i] == QChar(',')) && i > 0)
842             i--;
843         // strip zeros. Leave at least one character at the beginning
844         linear = linear.remove(i+1, ls-i);
845     }
846     // if removing of initial zeroes is needed
847     if (zeros & 4) {
848         int i = 0;
849         // locate last 0 in row from left
850         while (i < ls-1 && linear[i] == QChar('0')) {
851             i++;
852         }
853         linear = linear.remove(0, i);
854     }
855     return linear;
856 }
857 
858 
move(const RS_Vector & offset)859 void RS_Dimension::move(const RS_Vector& offset) {
860 	data.definitionPoint.move(offset);
861     data.middleOfText.move(offset);
862 }
863 
864 
865 
rotate(const RS_Vector & center,const double & angle)866 void RS_Dimension::rotate(const RS_Vector& center, const double& angle) {
867     RS_Vector angleVector(angle);
868 	data.definitionPoint.rotate(center, angleVector);
869     data.middleOfText.rotate(center, angleVector);
870     data.angle = RS_Math::correctAngle(data.angle+angle);
871 }
872 
rotate(const RS_Vector & center,const RS_Vector & angleVector)873 void RS_Dimension::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
874 	data.definitionPoint.rotate(center, angleVector);
875     data.middleOfText.rotate(center, angleVector);
876     data.angle = RS_Math::correctAngle(data.angle+angleVector.angle());
877 }
878 
879 
scale(const RS_Vector & center,const RS_Vector & factor)880 void RS_Dimension::scale(const RS_Vector& center, const RS_Vector& factor) {
881 	data.definitionPoint.scale(center, factor);
882     data.middleOfText.scale(center, factor);
883 }
884 
885 
886 
mirror(const RS_Vector & axisPoint1,const RS_Vector & axisPoint2)887 void RS_Dimension::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
888 	data.definitionPoint.mirror(axisPoint1, axisPoint2);
889     data.middleOfText.mirror(axisPoint1, axisPoint2);
890 }
891 
892 // EOF
893