893 lines
26 KiB
C++
893 lines
26 KiB
C++
/****************************************************************************
|
|
**
|
|
** This file is part of the LibreCAD project, a 2D CAD program
|
|
**
|
|
** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
|
|
** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
|
|
**
|
|
**
|
|
** This file may be distributed and/or modified under the terms of the
|
|
** GNU General Public License version 2 as published by the Free Software
|
|
** Foundation and appearing in the file gpl-2.0.txt included in the
|
|
** packaging of this file.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program; if not, write to the Free Software
|
|
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
**
|
|
** This copyright notice MUST APPEAR in all copies of the script!
|
|
**
|
|
**********************************************************************/
|
|
#include<iostream>
|
|
#include<cmath>
|
|
#include<string>
|
|
#include "rs_information.h"
|
|
#include "rs_line.h"
|
|
#include "rs_dimension.h"
|
|
#include "rs_solid.h"
|
|
#include "rs_units.h"
|
|
#include "rs_math.h"
|
|
#include "rs_filterdxfrw.h" //for int <-> rs_color conversion
|
|
#include "rs_debug.h"
|
|
|
|
RS_DimensionData::RS_DimensionData():
|
|
definitionPoint(false),
|
|
middleOfText(false),
|
|
valign(RS_MTextData::VABottom),
|
|
halign(RS_MTextData::HALeft),
|
|
lineSpacingStyle(RS_MTextData::Exact),
|
|
lineSpacingFactor(0.0),
|
|
text(""),
|
|
style(""),
|
|
angle(0.0)
|
|
{}
|
|
|
|
/**
|
|
* Constructor with initialisation.
|
|
*
|
|
* @param definitionPoint Definition point.
|
|
* @param middleOfText Middle point of dimension text.
|
|
* @param valign Vertical alignment.
|
|
* @param halign Horizontal alignment.
|
|
* @param lineSpacingStyle Line spacing style.
|
|
* @param lineSpacingFactor Line spacing factor.
|
|
* @param text Text string entered explicitly by user or null
|
|
* or "<>" for the actual measurement or " " (one blank space).
|
|
* for suppressing the text.
|
|
* @param style Dimension style name.
|
|
* @param angle Rotation angle of dimension text away from
|
|
* default orientation.
|
|
*/
|
|
RS_DimensionData::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):
|
|
definitionPoint(_definitionPoint)
|
|
,middleOfText(_middleOfText)
|
|
,valign(_valign)
|
|
,halign(_halign)
|
|
,lineSpacingStyle(_lineSpacingStyle)
|
|
,lineSpacingFactor(_lineSpacingFactor)
|
|
,text(_text)
|
|
,style(_style)
|
|
,angle(_angle)
|
|
{
|
|
}
|
|
|
|
std::ostream& operator << (std::ostream& os,
|
|
const RS_DimensionData& dd) {
|
|
os << "("
|
|
<< dd.definitionPoint<<','
|
|
<<dd.middleOfText<<','
|
|
<<dd.valign<<','
|
|
<<dd.halign<<','
|
|
<<dd.lineSpacingStyle<<','
|
|
<<dd.lineSpacingFactor<<','
|
|
<<dd.text.toLatin1().data() <<','
|
|
<<dd.style.toLatin1().data()<<','
|
|
<<dd.angle
|
|
<< ")";
|
|
return os;
|
|
}
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
RS_Dimension::RS_Dimension(RS_EntityContainer* parent,
|
|
const RS_DimensionData& d)
|
|
: RS_EntityContainer(parent), data(d) {
|
|
}
|
|
|
|
RS_Vector RS_Dimension::getNearestRef( const RS_Vector& coord,
|
|
double* dist /*= nullptr*/) const
|
|
{
|
|
// override the RS_EntityContainer method
|
|
// use RS_Entity instead for refpoint dragging
|
|
return RS_Entity::getNearestRef( coord, dist);
|
|
}
|
|
|
|
RS_Vector RS_Dimension::getNearestSelectedRef( const RS_Vector& coord,
|
|
double* dist /*= nullptr*/) const
|
|
{
|
|
// override the RS_EntityContainer method
|
|
// use RS_Entity instead for refpoint dragging
|
|
return RS_Entity::getNearestSelectedRef( coord, dist);
|
|
}
|
|
|
|
|
|
/**
|
|
* @return Dimension text. Either a text the user defined or
|
|
* the measured text.
|
|
*
|
|
* @param resolve false: return plain value. true: return measured
|
|
* label if appropriate.
|
|
* @see getMeasuredLabel
|
|
*/
|
|
QString RS_Dimension::getLabel(bool resolve) {
|
|
if (!resolve) {
|
|
return data.text;
|
|
}
|
|
|
|
QString ret="";
|
|
|
|
// One space suppresses the text:
|
|
if (data.text==" ") {
|
|
ret = "";
|
|
}
|
|
|
|
// No text prints actual measurement:
|
|
else if (data.text=="") {
|
|
ret = getMeasuredLabel();
|
|
}
|
|
|
|
// Others print the text (<> is replaced by the measurement)
|
|
else {
|
|
ret = data.text;
|
|
ret = ret.replace(QString("<>"), getMeasuredLabel());
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets a new text for the label.
|
|
*/
|
|
void RS_Dimension::setLabel(const QString& l) {
|
|
data.text = l;
|
|
}
|
|
|
|
|
|
/**
|
|
* Find intersections between a line and an EntityContainer. Solutions are
|
|
* sorted along the line before returning.
|
|
*
|
|
* @param infiniteLine Treat the line as infinitely long in both directions.
|
|
*/
|
|
RS_VectorSolutions RS_Dimension::getIntersectionsLineContainer(
|
|
const RS_Line* l, const RS_EntityContainer* c, bool infiniteLine)
|
|
{
|
|
RS_VectorSolutions solutions_initial;
|
|
RS_VectorSolutions solutions_filtered;
|
|
const double tol = 1.0e-4;
|
|
|
|
// Find all intersections, including those beyond limits of container
|
|
// entities.
|
|
for(RS_Entity* e: *c) {
|
|
solutions_initial.push_back(
|
|
RS_Information::getIntersection(l, e, false)
|
|
);
|
|
}
|
|
|
|
// Filter solutions based on whether they are actually on any entities.
|
|
for(const RS_Vector& vp: solutions_initial){
|
|
for(RS_Entity* e: *c) {
|
|
if (e->isConstruction(true) || e->isPointOnEntity(vp, tol)) {
|
|
// the intersection is at least on the container, now check the line:
|
|
if (infiniteLine) {
|
|
// The line is treated as infinitely long so we don't need to
|
|
// check if the intersection is on the line.
|
|
solutions_filtered.push_back(vp);
|
|
break;
|
|
} else if (l->isConstruction(true) || l->isPointOnEntity(vp, tol)) {
|
|
solutions_filtered.push_back(vp);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* We cannot sort the solutions in place because getVector() returns a
|
|
* const vector, so first construct a copy:
|
|
*/
|
|
std::vector<RS_Vector> solutions_sorted(solutions_filtered.getVector());
|
|
std::sort(solutions_sorted.begin(), solutions_sorted.end(),
|
|
[l](const RS_Vector& lhs, const RS_Vector& rhs)
|
|
{
|
|
return l->getProjectionValueAlongLine(lhs)
|
|
< l->getProjectionValueAlongLine(rhs);
|
|
});
|
|
|
|
return RS_VectorSolutions(solutions_sorted);
|
|
}
|
|
|
|
|
|
/**
|
|
* Creates a horizontal-text dimensioning line (line with one, two or no arrows
|
|
* and "inside horizontal" text).
|
|
*
|
|
* @param forceAutoText Automatically reposition the text label.
|
|
*/
|
|
void RS_Dimension::updateCreateHorizontalTextDimensionLine(const RS_Vector& p1,
|
|
const RS_Vector& p2, bool arrow1, bool arrow2, bool forceAutoText) {
|
|
// general scale (DIMSCALE)
|
|
double dimscale = getGeneralScale();
|
|
// text height (DIMTXT)
|
|
double dimtxt = getTextHeight()*dimscale;
|
|
// text distance to line (DIMGAP)
|
|
double dimgap = getDimensionLineGap()*dimscale;
|
|
|
|
// length of dimension line:
|
|
double distance = p1.distanceTo(p2);
|
|
// arrow size:
|
|
double arrowSize = getArrowSize()*dimscale;
|
|
|
|
// arrow angles:
|
|
double arrowAngle1, arrowAngle2;
|
|
|
|
RS_Pen pen(getDimensionLineColor(),
|
|
getDimensionLineWidth(),
|
|
RS2::LineByBlock);
|
|
|
|
// Create dimension line:
|
|
RS_Line* dimensionLine {new RS_Line{this, p1, p2}};
|
|
RS_Line* dimensionLineInside1 {nullptr};
|
|
RS_Line* dimensionLineInside2 {nullptr};
|
|
RS_Line* dimensionLineOutside1 {nullptr};
|
|
RS_Line* dimensionLineOutside2 {nullptr};
|
|
dimensionLine->setPen(pen);
|
|
dimensionLine->setLayer(nullptr);
|
|
|
|
// Text label:
|
|
RS_MTextData textData;
|
|
RS_Vector textPos;
|
|
double textAngle = 0.0;
|
|
bool autoText = !data.middleOfText.valid || forceAutoText;
|
|
|
|
if (autoText) {
|
|
textPos = dimensionLine->getMiddlePoint();
|
|
|
|
//// the next update should still be able to adjust this
|
|
//// auto text position. leave it invalid
|
|
data.middleOfText = textPos;
|
|
} else {
|
|
textPos = data.middleOfText;
|
|
}
|
|
|
|
textData = RS_MTextData(textPos,
|
|
dimtxt, 30.0,
|
|
RS_MTextData::VAMiddle,
|
|
RS_MTextData::HACenter,
|
|
RS_MTextData::LeftToRight,
|
|
RS_MTextData::Exact,
|
|
1.0,
|
|
getLabel(),
|
|
getTextStyle(),
|
|
textAngle);
|
|
|
|
RS_MText* text = new RS_MText(this, textData);
|
|
text->setPen(RS_Pen(getTextColor(), RS2::WidthByBlock, RS2::SolidLine));
|
|
text->setLayer(nullptr);
|
|
|
|
// evaluate intersection between dim line and text
|
|
double textIntersectionLength = 0.0;
|
|
double w = text->getUsedTextWidth()/2+dimgap;
|
|
double h = text->getUsedTextHeight()/2+dimgap;
|
|
// textCorner variables correspond to the corners of the text bounding box
|
|
// if the text were to be positioned in the center of the dimensionLine.
|
|
RS_Vector textCorner1 = dimensionLine->getMiddlePoint() - RS_Vector{w, h};
|
|
RS_Vector textCorner2 = dimensionLine->getMiddlePoint() + RS_Vector{w, h};
|
|
RS_EntityContainer c;
|
|
c.addRectangle(textCorner1, textCorner2);
|
|
RS_VectorSolutions sol1 = getIntersectionsLineContainer(
|
|
dimensionLine, &c,
|
|
true // treat line as infinitely long in both directions
|
|
);
|
|
textIntersectionLength = sol1.get(0).distanceTo(sol1.get(1));
|
|
|
|
// determine if we should use outside arrows
|
|
bool outsideArrows = (textIntersectionLength+3*arrowSize) > distance;
|
|
|
|
// add arrows
|
|
if (outsideArrows==false) {
|
|
arrowAngle1 = dimensionLine->getAngle2();
|
|
arrowAngle2 = dimensionLine->getAngle1();
|
|
} else {
|
|
arrowAngle1 = dimensionLine->getAngle1();
|
|
arrowAngle2 = dimensionLine->getAngle2();
|
|
|
|
// extend dimension line outside arrows
|
|
RS_Vector dir = RS_Vector::polar(arrowSize*2, dimensionLine->getAngle1());
|
|
dimensionLineOutside1 = new RS_Line{this, p1 - dir, p1};
|
|
dimensionLineOutside2 = new RS_Line{this, p2 + dir, p2};
|
|
|
|
// move text to the side if it won't fit either
|
|
RS_Vector distH;
|
|
if (textIntersectionLength>distance && autoText) {
|
|
distH.setPolar(textIntersectionLength/2.0+arrowSize*2+distance/2.0,
|
|
arrowAngle1);
|
|
text->move(distH);
|
|
textPos = text->getInsertionPoint();
|
|
data.middleOfText = textPos;
|
|
}
|
|
}
|
|
double dimtsz=getTickSize()*dimscale;
|
|
bool displayArrows = dimtsz < 0.01;
|
|
if(displayArrows) {
|
|
//display arrow
|
|
// Arrows:
|
|
RS_SolidData sd;
|
|
RS_Solid* arrow;
|
|
|
|
if (arrow1) {
|
|
// arrow 1
|
|
arrow = new RS_Solid(this, sd);
|
|
arrow->shapeArrow(p1,
|
|
arrowAngle1,
|
|
arrowSize);
|
|
arrow->setPen(pen);
|
|
arrow->setLayer(nullptr);
|
|
addEntity(arrow);
|
|
}
|
|
|
|
if (arrow2) {
|
|
// arrow 2:
|
|
arrow = new RS_Solid(this, sd);
|
|
arrow->shapeArrow(p2,
|
|
arrowAngle2,
|
|
arrowSize);
|
|
arrow->setPen(pen);
|
|
arrow->setLayer(nullptr);
|
|
addEntity(arrow);
|
|
}
|
|
} else {
|
|
//display ticks
|
|
// Arrows:
|
|
|
|
RS_Line* tick;
|
|
RS_Vector tickVector = RS_Vector::polar(dimtsz,arrowAngle1 + M_PI*0.25); //tick is 45 degree away
|
|
|
|
if (arrow1) {
|
|
// tick 1
|
|
tick = new RS_Line(this, p1-tickVector, p1+tickVector);
|
|
tick->setPen(pen);
|
|
tick->setLayer(nullptr);
|
|
addEntity(tick);
|
|
}
|
|
|
|
if (arrow2) {
|
|
// tick 2:
|
|
tick = new RS_Line(this, p2-tickVector, p2+tickVector);
|
|
tick->setPen(pen);
|
|
tick->setLayer(nullptr);
|
|
addEntity(tick);
|
|
}
|
|
}
|
|
|
|
// calculate split dimension lines
|
|
bool splitDimensionLine = false;
|
|
if (!outsideArrows) {
|
|
w = text->getUsedTextWidth()/2+dimgap;
|
|
h = text->getUsedTextHeight()/2+dimgap;
|
|
RS_Vector s1 = text->getInsertionPoint() - RS_Vector{w, h};
|
|
RS_Vector s2 = text->getInsertionPoint() + RS_Vector{w, h};
|
|
c = RS_EntityContainer();
|
|
c.addRectangle(s1, s2);
|
|
sol1 = getIntersectionsLineContainer(dimensionLine, &c);
|
|
if (sol1.size()>1) {
|
|
// the text bounding box intersects dimensionLine on two sides
|
|
splitDimensionLine = true;
|
|
s1 = sol1.get(0);
|
|
s2 = sol1.get(1);
|
|
} else if (sol1.size()==1) {
|
|
// the text bounding box intersects dimensionLine on one side
|
|
splitDimensionLine = true;
|
|
if (RS_Information::isPointInsideContour(p1, &c)) {
|
|
// the dimension line begins inside the text bounds
|
|
s1 = p1;
|
|
s2 = sol1.get(0);
|
|
} else {
|
|
// the dimension line ends inside the text bounds
|
|
s1 = sol1.get(0);
|
|
s2 = p2;
|
|
}
|
|
} else {
|
|
// the text bounding box does not intersect with dimensionLine, but we
|
|
// should still check if dimensionLine endpoints are completely inside
|
|
// the bounding box.
|
|
if (RS_Information::isPointInsideContour(p1, &c)) {
|
|
splitDimensionLine = true;
|
|
s1 = p1;
|
|
s2 = p2;
|
|
}
|
|
}
|
|
|
|
if (splitDimensionLine) {
|
|
dimensionLineInside1 = new RS_Line{this, p1, s1};
|
|
dimensionLineInside2 = new RS_Line{this, s2, p2};
|
|
}
|
|
}
|
|
|
|
// finally, add the dimension line(s) and text to the drawing
|
|
if (outsideArrows && dimensionLineOutside1) {
|
|
addEntity(dimensionLineOutside1);
|
|
addEntity(dimensionLineOutside2);
|
|
} else if (splitDimensionLine && dimensionLineInside1) {
|
|
addEntity(dimensionLineInside1);
|
|
addEntity(dimensionLineInside2);
|
|
} else {
|
|
addEntity(dimensionLine);
|
|
}
|
|
|
|
addEntity(text);
|
|
}
|
|
|
|
|
|
/**
|
|
* Creates an aligned-text dimensioning line (line with one, two or no arrows
|
|
* and aligned text).
|
|
*
|
|
* @param forceAutoText Automatically reposition the text label.
|
|
*/
|
|
void RS_Dimension::updateCreateAlignedTextDimensionLine(const RS_Vector& p1,
|
|
const RS_Vector& p2, bool arrow1, bool arrow2, bool forceAutoText) {
|
|
// general scale (DIMSCALE)
|
|
double dimscale = getGeneralScale();
|
|
// text height (DIMTXT)
|
|
double dimtxt = getTextHeight()*dimscale;
|
|
// text distance to line (DIMGAP)
|
|
double dimgap = getDimensionLineGap()*dimscale;
|
|
|
|
// length of dimension line:
|
|
double distance = p1.distanceTo(p2);
|
|
// arrow size:
|
|
double arrowSize = getArrowSize()*dimscale;
|
|
|
|
// do we have to put the arrows outside of the line?
|
|
bool outsideArrows = (distance<arrowSize*2.5);
|
|
|
|
// arrow angles:
|
|
double arrowAngle1, arrowAngle2;
|
|
|
|
RS_Pen pen(getDimensionLineColor(),
|
|
getDimensionLineWidth(),
|
|
RS2::LineByBlock);
|
|
|
|
// Create dimension line:
|
|
RS_Line* dimensionLine = new RS_Line{this, p1, p2};
|
|
dimensionLine->setPen(pen);
|
|
dimensionLine->setLayer(nullptr);
|
|
addEntity(dimensionLine);
|
|
|
|
// Text label:
|
|
RS_MTextData textData;
|
|
RS_Vector textPos;
|
|
double dimAngle1 = dimensionLine->getAngle1();
|
|
bool corrected = false;
|
|
double textAngle = RS_Math::makeAngleReadable(dimAngle1, true, &corrected);
|
|
|
|
if (data.middleOfText.valid && !forceAutoText) {
|
|
textPos = data.middleOfText;
|
|
} else {
|
|
textPos = dimensionLine->getMiddlePoint();
|
|
|
|
// rotate text so it's readable from the bottom or right (ISO)
|
|
// quadrant 1 & 4
|
|
double const a = corrected?-M_PI_2:M_PI_2;
|
|
RS_Vector distV = RS_Vector::polar(dimgap + dimtxt/2.0, dimAngle1+a);
|
|
|
|
// move text away from dimension line:
|
|
textPos+=distV;
|
|
|
|
//// the next update should still be able to adjust this
|
|
//// auto text position. leave it invalid
|
|
data.middleOfText = textPos;
|
|
}
|
|
|
|
textData = RS_MTextData(textPos,
|
|
dimtxt, 30.0,
|
|
RS_MTextData::VAMiddle,
|
|
RS_MTextData::HACenter,
|
|
RS_MTextData::LeftToRight,
|
|
RS_MTextData::Exact,
|
|
1.0,
|
|
getLabel(),
|
|
getTextStyle(),
|
|
textAngle);
|
|
|
|
RS_MText* text = new RS_MText(this, textData);
|
|
text->setPen(RS_Pen(getTextColor(), RS2::WidthByBlock, RS2::SolidLine));
|
|
text->setLayer(nullptr);
|
|
|
|
// move text to the side:
|
|
RS_Vector distH;
|
|
if (text->getUsedTextWidth()>distance) {
|
|
distH.setPolar(text->getUsedTextWidth()/2.0
|
|
+distance/2.0+dimgap, textAngle);
|
|
text->move(distH);
|
|
}
|
|
|
|
addEntity(text);
|
|
|
|
// add arrows
|
|
if (outsideArrows==false) {
|
|
arrowAngle1 = dimensionLine->getAngle2();
|
|
arrowAngle2 = dimensionLine->getAngle1();
|
|
} else {
|
|
arrowAngle1 = dimensionLine->getAngle1();
|
|
arrowAngle2 = dimensionLine->getAngle2();
|
|
|
|
// extend dimension line outside arrows
|
|
RS_Vector dir = RS_Vector::polar(arrowSize*2, arrowAngle2);
|
|
dimensionLine->setStartpoint(p1 + dir);
|
|
dimensionLine->setEndpoint(p2 - dir);
|
|
}
|
|
double dimtsz=getTickSize()*dimscale;
|
|
if(dimtsz < 0.01) {
|
|
//display arrow
|
|
// Arrows:
|
|
RS_SolidData sd;
|
|
RS_Solid* arrow;
|
|
|
|
if (arrow1) {
|
|
// arrow 1
|
|
arrow = new RS_Solid(this, sd);
|
|
arrow->shapeArrow(p1,
|
|
arrowAngle1,
|
|
arrowSize);
|
|
arrow->setPen(pen);
|
|
arrow->setLayer(nullptr);
|
|
addEntity(arrow);
|
|
}
|
|
|
|
if (arrow2) {
|
|
// arrow 2:
|
|
arrow = new RS_Solid(this, sd);
|
|
arrow->shapeArrow(p2,
|
|
arrowAngle2,
|
|
arrowSize);
|
|
arrow->setPen(pen);
|
|
arrow->setLayer(nullptr);
|
|
addEntity(arrow);
|
|
}
|
|
}else{
|
|
//display ticks
|
|
// Arrows:
|
|
|
|
RS_Line* tick;
|
|
RS_Vector tickVector = RS_Vector::polar(dimtsz,arrowAngle1 + M_PI*0.25); //tick is 45 degree away
|
|
|
|
if (arrow1) {
|
|
// tick 1
|
|
tick = new RS_Line(this, p1-tickVector, p1+tickVector);
|
|
tick->setPen(pen);
|
|
tick->setLayer(nullptr);
|
|
addEntity(tick);
|
|
}
|
|
|
|
if (arrow2) {
|
|
// tick 2:
|
|
tick = new RS_Line(this, p2-tickVector, p2+tickVector);
|
|
tick->setPen(pen);
|
|
tick->setLayer(nullptr);
|
|
addEntity(tick);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Creates a dimensioning line (line with one, two or no arrows and a text).
|
|
*
|
|
* @param forceAutoText Automatically reposition the text label.
|
|
*/
|
|
void RS_Dimension::updateCreateDimensionLine(const RS_Vector& p1,
|
|
const RS_Vector& p2, bool arrow1, bool arrow2, bool forceAutoText) {
|
|
if (getInsideHorizontalText())
|
|
updateCreateHorizontalTextDimensionLine(p1, p2, arrow1, arrow2, forceAutoText);
|
|
else
|
|
updateCreateAlignedTextDimensionLine(p1, p2, arrow1, arrow2, forceAutoText);
|
|
}
|
|
|
|
|
|
/**
|
|
* @return general factor for linear dimensions.
|
|
*/
|
|
double RS_Dimension::getGeneralFactor() {
|
|
return getGraphicVariable("$DIMLFAC", 1.0, 40);
|
|
}
|
|
|
|
/**
|
|
* @return general scale for dimensions.
|
|
*/
|
|
double RS_Dimension::getGeneralScale() {
|
|
return getGraphicVariable("$DIMSCALE", 1.0, 40);
|
|
}
|
|
|
|
/**
|
|
* @return arrow size in drawing units.
|
|
*/
|
|
double RS_Dimension::getArrowSize() {
|
|
return getGraphicVariable("$DIMASZ", 2.5, 40);
|
|
}
|
|
|
|
/**
|
|
* @return tick size in drawing units.
|
|
*/
|
|
double RS_Dimension::getTickSize() {
|
|
return getGraphicVariable("$DIMTSZ", 0., 40);
|
|
}
|
|
|
|
|
|
/**
|
|
* @return extension line overlength in drawing units.
|
|
*/
|
|
double RS_Dimension::getExtensionLineExtension() {
|
|
return getGraphicVariable("$DIMEXE", 1.25, 40);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @return extension line offset from entities in drawing units.
|
|
*/
|
|
double RS_Dimension::getExtensionLineOffset() {
|
|
return getGraphicVariable("$DIMEXO", 0.625, 40);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @return extension line gap to text in drawing units.
|
|
*/
|
|
double RS_Dimension::getDimensionLineGap() {
|
|
return getGraphicVariable("$DIMGAP", 0.625, 40);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @return Dimension labels text height.
|
|
*/
|
|
double RS_Dimension::getTextHeight() {
|
|
return getGraphicVariable("$DIMTXT", 2.5, 40);
|
|
}
|
|
|
|
|
|
/**
|
|
* @return Dimension labels alignment text true= horizontal, false= aligned.
|
|
*/
|
|
bool RS_Dimension::getInsideHorizontalText() {
|
|
int v = getGraphicVariableInt("$DIMTIH", 1);
|
|
if (v>0) {
|
|
addGraphicVariable("$DIMTIH", 1, 70);
|
|
getGraphicVariableInt("$DIMTIH", 1);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* @return Dimension fixed length for extension lines true= fixed, false= not fixed.
|
|
*/
|
|
bool RS_Dimension::getFixedLengthOn() {
|
|
int v = getGraphicVariableInt("$DIMFXLON", 0);
|
|
if (v == 1) {
|
|
addGraphicVariable("$DIMFXLON", 1, 70);
|
|
getGraphicVariableInt("$DIMFXLON", 0);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @return Dimension fixed length for extension lines.
|
|
*/
|
|
double RS_Dimension::getFixedLength() {
|
|
return getGraphicVariable("$DIMFXL", 1.0, 40);
|
|
}
|
|
|
|
|
|
/**
|
|
* @return extension line Width.
|
|
*/
|
|
RS2::LineWidth RS_Dimension::getExtensionLineWidth() {
|
|
return RS2::intToLineWidth( getGraphicVariableInt("$DIMLWE", -2) ); //default -2 (RS2::WidthByBlock)
|
|
}
|
|
|
|
|
|
/**
|
|
* @return dimension line Width.
|
|
*/
|
|
RS2::LineWidth RS_Dimension::getDimensionLineWidth() {
|
|
return RS2::intToLineWidth( getGraphicVariableInt("$DIMLWD", -2) ); //default -2 (RS2::WidthByBlock)
|
|
}
|
|
|
|
/**
|
|
* @return dimension line Color.
|
|
*/
|
|
RS_Color RS_Dimension::getDimensionLineColor() {
|
|
return RS_FilterDXFRW::numberToColor(getGraphicVariableInt("$DIMCLRD", 0));
|
|
}
|
|
|
|
|
|
/**
|
|
* @return extension line Color.
|
|
*/
|
|
RS_Color RS_Dimension::getExtensionLineColor() {
|
|
return RS_FilterDXFRW::numberToColor(getGraphicVariableInt("$DIMCLRE", 0));
|
|
}
|
|
|
|
|
|
/**
|
|
* @return dimension text Color.
|
|
*/
|
|
RS_Color RS_Dimension::getTextColor() {
|
|
return RS_FilterDXFRW::numberToColor(getGraphicVariableInt("$DIMCLRT", 0));
|
|
}
|
|
|
|
|
|
/**
|
|
* @return text style for dimensions.
|
|
*/
|
|
QString RS_Dimension::getTextStyle() {
|
|
return getGraphicVariableString("$DIMTXSTY", "standard");
|
|
}
|
|
|
|
|
|
/**
|
|
* @return the given graphic variable or the default value given in mm
|
|
* converted to the graphic unit.
|
|
* If the variable is not found it is added with the given default
|
|
* value converted to the local unit.
|
|
*/
|
|
double RS_Dimension::getGraphicVariable(const QString& key, double defMM,
|
|
int code) {
|
|
|
|
double v = getGraphicVariableDouble(key, RS_MINDOUBLE);
|
|
if (v<=RS_MINDOUBLE) {
|
|
addGraphicVariable(
|
|
key,
|
|
RS_Units::convert(defMM, RS2::Millimeter, getGraphicUnit()),
|
|
code);
|
|
v = getGraphicVariableDouble(key, 1.0);
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
/**
|
|
* Removes zeros from angle string.
|
|
*
|
|
* @param angle The string representing angle.
|
|
* @param zeros Zeros suppression (0 none, 1 suppress leading, 2 suppress trailing, 3 both)
|
|
* Decimal separator are '.'
|
|
*
|
|
* @ret String with the formatted angle.
|
|
*/
|
|
|
|
QString RS_Dimension::stripZerosAngle(QString angle, int zeros){
|
|
if (zeros == 0) //do nothing
|
|
return angle;
|
|
if (zeros & 2 && (angle.contains(QString('.')) || angle.contains(QString(',')))) {
|
|
int end = angle.size() - 1;
|
|
QChar format = angle[end--]; //stores & skip format char
|
|
while (end > 0 && angle[end] == QChar('0')) // locate first 0 from end
|
|
end--;
|
|
if (angle[end] == QChar('.'))
|
|
end--;
|
|
angle.truncate(end+1);
|
|
angle.append(format);
|
|
}
|
|
if (zeros & 1){
|
|
if (angle[0] == QChar('0') && angle[1] == QChar('.'))
|
|
angle = angle.remove(0, 1);
|
|
}
|
|
return angle;
|
|
}
|
|
|
|
/**
|
|
* Removes zeros from linear string.
|
|
*
|
|
* @param linear The string representing linear measure.
|
|
* @param zeros Zeros suppression (see dimzin)
|
|
*
|
|
* @ret String with the formatted linear measure.
|
|
*/
|
|
|
|
QString RS_Dimension::stripZerosLinear(QString linear, int zeros){
|
|
|
|
//do nothing
|
|
if (zeros == 1)
|
|
return linear;
|
|
|
|
// return at least 1 character in string
|
|
int ls = linear.size();
|
|
if (ls <= 1) {
|
|
return linear;
|
|
}
|
|
|
|
// if removing of trailing zeroes is needed
|
|
if (zeros & 8 && (linear.contains(QString('.')) || linear.contains(QString(',')))) {
|
|
// search index
|
|
int i = ls - 1;
|
|
// locate first 0 in row from right
|
|
while (i > 0 && linear[i] == QChar('0')) {
|
|
i--;
|
|
}
|
|
// strip decimal point
|
|
if ((linear[i] == QChar('.') || linear[i] == QChar(',')) && i > 0)
|
|
i--;
|
|
// strip zeros. Leave at least one character at the beginning
|
|
linear = linear.remove(i+1, ls-i);
|
|
}
|
|
// if removing of initial zeroes is needed
|
|
if (zeros & 4) {
|
|
int i = 0;
|
|
// locate last 0 in row from left
|
|
while (i < ls-1 && linear[i] == QChar('0')) {
|
|
i++;
|
|
}
|
|
linear = linear.remove(0, i);
|
|
}
|
|
return linear;
|
|
}
|
|
|
|
|
|
void RS_Dimension::move(const RS_Vector& offset) {
|
|
data.definitionPoint.move(offset);
|
|
data.middleOfText.move(offset);
|
|
}
|
|
|
|
|
|
|
|
void RS_Dimension::rotate(const RS_Vector& center, const double& angle) {
|
|
RS_Vector angleVector(angle);
|
|
data.definitionPoint.rotate(center, angleVector);
|
|
data.middleOfText.rotate(center, angleVector);
|
|
data.angle = RS_Math::correctAngle(data.angle+angle);
|
|
}
|
|
|
|
void RS_Dimension::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
|
|
data.definitionPoint.rotate(center, angleVector);
|
|
data.middleOfText.rotate(center, angleVector);
|
|
data.angle = RS_Math::correctAngle(data.angle+angleVector.angle());
|
|
}
|
|
|
|
|
|
void RS_Dimension::scale(const RS_Vector& center, const RS_Vector& factor) {
|
|
data.definitionPoint.scale(center, factor);
|
|
data.middleOfText.scale(center, factor);
|
|
}
|
|
|
|
|
|
|
|
void RS_Dimension::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
|
|
data.definitionPoint.mirror(axisPoint1, axisPoint2);
|
|
data.middleOfText.mirror(axisPoint1, axisPoint2);
|
|
}
|
|
|
|
// EOF
|