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