| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include "rs_dimension.h" |
| |
|
| | #include <QRegularExpression> |
| |
|
| | #include "lc_align.h" |
| | #include "lc_dimarrowregistry.h" |
| | #include "lc_linemath.h" |
| | #include "muParser.h" |
| | #include "rs_arc.h" |
| | #include "rs_filterdxfrw.h" |
| | #include "rs_graphicview.h" |
| | #include "rs_information.h" |
| | #include "rs_line.h" |
| | #include "rs_math.h" |
| | #include "rs_painter.h" |
| | #include "rs_pen.h" |
| | #include "rs_settings.h" |
| | #include "rs_units.h" |
| |
|
| | class LC_ArrowHeadOpen; |
| |
|
| | namespace |
| | { |
| | |
| | |
| | |
| | QString evaluateFunction(const QString& expression, double dimValue) { |
| | |
| | if (expression.size() < 4) |
| | return expression; |
| | |
| | QString expr = expression.mid(1, expression.size() - 2); |
| | const QString variable("a"); |
| | expr.replace("<>", variable); |
| | try { |
| | mu::Parser p; |
| |
|
| | #ifdef _UNICODE |
| | p.DefineVar(variable.toStdWString(), &dimValue); |
| | p.SetExpr(expr.toStdWString()); |
| | #else |
| | p.DefineVar(variable.toStdString(), &dimValue); |
| | p.SetExpr(expr.toStdString()); |
| | #endif |
| |
|
| | double functionValue = p.Eval(); |
| | return QString::number(functionValue); |
| | } |
| | catch (...) { |
| | return expression; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | QString functionalText(const QString& dimText, double dimValue) { |
| | |
| | static const QRegularExpression re{R"({([^{}<>]*(<>)[^{}<>]*)*})"}; |
| | QRegularExpressionMatch match = re.match(dimText); |
| | if (!match.hasMatch()) |
| | return dimText; |
| |
|
| | QString ret = dimText; |
| | for (int i = 0; i <= match.lastCapturedIndex(); ++i) { |
| | QString captured = match.captured(i); |
| | QString functionValue = evaluateFunction(captured, dimValue); |
| | ret.replace(captured, functionValue); |
| | } |
| | return ret; |
| | } |
| | } |
| |
|
| | RS_DimensionData::RS_DimensionData(const RS_DimensionData& other): |
| | RS_Flags(other), |
| | definitionPoint(other.definitionPoint), |
| | middleOfText(other.middleOfText), |
| | valign(other.valign), |
| | halign(other.halign), |
| | lineSpacingStyle(other.lineSpacingStyle), |
| | lineSpacingFactor(other.lineSpacingFactor), |
| | text(other.text), |
| | style(other.style), |
| | angle(other.angle), |
| | horizontalAxisDirection(other.horizontalAxisDirection), |
| | autoText{other.autoText}, |
| | flipArrow1{other.flipArrow1}, |
| | flipArrow2{other.flipArrow2}{ |
| | if (other.m_dimStyleOverride != nullptr) { |
| | m_dimStyleOverride.reset(other.m_dimStyleOverride->getCopy()); |
| | } |
| | else { |
| | m_dimStyleOverride.reset(); |
| | } |
| | } |
| |
|
| | 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), |
| | horizontalAxisDirection(0.0), |
| | autoText{true}{ |
| | m_dimStyleOverride.reset(); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | 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, |
| | double hdir, |
| | bool autoTextLocation, |
| | LC_DimStyle* dimStyleOverride, |
| | bool flp1, bool flp2): |
| | definitionPoint(_definitionPoint) |
| | , middleOfText(_middleOfText) |
| | , valign(_valign) |
| | , halign(_halign) |
| | , lineSpacingStyle(_lineSpacingStyle) |
| | , lineSpacingFactor(_lineSpacingFactor) |
| | , text(_text) |
| | , style(_style) |
| | , angle(_angle) |
| | , horizontalAxisDirection(hdir) |
| | , autoText{autoTextLocation} |
| | ,flipArrow1{flp1} |
| | ,flipArrow2{flp2}{ |
| | m_dimStyleOverride.reset(dimStyleOverride); |
| | } |
| |
|
| | 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; |
| | } |
| |
|
| | |
| | |
| | |
| | RS_Dimension::RS_Dimension(RS_EntityContainer* parent, const RS_DimensionData& d) |
| | : RS_EntityContainer(parent) |
| | , m_dimGenericData(std::move(d)) { |
| | } |
| |
|
| | RS_Dimension::RS_Dimension(const RS_Dimension& entity) |
| | : RS_EntityContainer(entity, false) |
| | , m_dimGenericData(std::move(entity.getData())) { |
| | } |
| |
|
| | RS_Vector RS_Dimension::getNearestRef(const RS_Vector& coord, double* dist ) const { |
| | |
| | |
| | return RS_Entity::getNearestRef(coord, dist); |
| | } |
| |
|
| | RS_Vector RS_Dimension::getNearestSelectedRef(const RS_Vector& coord, double* dist ) const { |
| | |
| | |
| | return RS_Entity::getNearestSelectedRef(coord, dist); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | QString RS_Dimension::getLabel(bool resolve) { |
| | if (!resolve) { |
| | return m_dimGenericData.text; |
| | } |
| |
|
| | QString ret = ""; |
| |
|
| | |
| | if (m_dimGenericData.text == " ") { |
| | ret = ""; |
| | } |
| |
|
| | |
| | else if (m_dimGenericData.text == "") { |
| | ret = getMeasuredLabel(); |
| | } |
| |
|
| | |
| | else { |
| | QString measuredStr = getMeasuredLabel(); |
| | bool okay = false; |
| | double measured = measuredStr.toDouble(&okay); |
| |
|
| | |
| | |
| | |
| | if (okay) { |
| | ret = functionalText(m_dimGenericData.text, measured); |
| | } |
| | ret = ret.replace(QString("<>"), getMeasuredLabel()); |
| | } |
| |
|
| | return ret; |
| | } |
| |
|
| | |
| | |
| | |
| | void RS_Dimension::setLabel(const QString& l) { |
| | m_dimGenericData.text = l; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | 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; |
| |
|
| | |
| | |
| | for (RS_Entity* e : *c) { |
| | solutions_initial.push_back(RS_Information::getIntersection(l, e, false)); |
| | } |
| |
|
| | |
| | for (const RS_Vector& vp : solutions_initial) { |
| | for (RS_Entity* e : *c) { |
| | if (e->isConstruction(true) || e->isPointOnEntity(vp, tol)) { |
| | |
| | if (infiniteLine) { |
| | |
| | |
| | solutions_filtered.push_back(vp); |
| | break; |
| | } |
| | else if (l->isConstruction(true) || l->isPointOnEntity(vp, tol)) { |
| | solutions_filtered.push_back(vp); |
| | break; |
| | } |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | 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); |
| | } |
| |
|
| | RS_Pen RS_Dimension::getPenForText() { |
| | RS_Pen result(getTextColor(), RS2::WidthByBlock, RS2::SolidLine); |
| | return result; |
| | } |
| |
|
| | RS_Pen RS_Dimension::getPenExtensionLine(bool first) { |
| | RS2::LineType lineType = first? getExtensionLineTypeFirst() : getExtensionLineTypeSecond(); |
| | RS_Pen result(getExtensionLineColor(), getExtensionLineWidth(), lineType); |
| | return result; |
| | } |
| |
|
| | RS_Pen RS_Dimension::getPenDimensionLine() { |
| | RS_Pen result(getDimensionLineColor(), getDimensionLineWidth(), getDimensionLineType()); |
| | return result; |
| | } |
| |
|
| | RS_MText* RS_Dimension::createDimText(RS_Vector textPos, double textHeight, double textAngle) { |
| | RS_MTextData data = createDimTextData(textPos, textHeight, textAngle); |
| | return addDimText(data); |
| | } |
| |
|
| | RS_MText* RS_Dimension::addDimText(RS_MTextData &textData) { |
| | auto mtext = new RS_MText(this, textData); |
| | addDimComponentEntity(mtext, getPenForText()); |
| | mtext->update(); |
| | return mtext; |
| | } |
| |
|
| | RS_MTextData RS_Dimension::createDimTextData(RS_Vector textPos, double textHeight, double textAngle) { |
| | RS_MTextData textData = RS_MTextData(textPos, |
| | textHeight, 30.0, |
| | RS_MTextData::VAMiddle, |
| | RS_MTextData::HACenter, |
| | RS_MTextData::LeftToRight, |
| | RS_MTextData::Exact, |
| | 1.0, |
| | getLabel(), |
| | getTextStyle(), |
| | textAngle); |
| | return textData; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | void RS_Dimension::createHorizontalTextDimensionLine(const RS_Vector& p1, |
| | const RS_Vector& p2, bool showArrow1, bool showArrow2, |
| | bool showLine1, bool showLine2, |
| | bool forceAutoText) { |
| | double dimscale = getGeneralScale(); |
| | double dimtxt = getTextHeight() * dimscale; |
| | double dimgap = getDimensionLineGap() * dimscale; |
| | double dimOffset = getVerticalDistanceToDimLine() * dimscale; |
| |
|
| | bool drawFrameAroundText = std::signbit(dimgap); |
| | if (drawFrameAroundText) { |
| | dimgap = -dimgap; |
| | } |
| |
|
| | |
| | double distance = p1.distanceTo(p2); |
| | |
| | double arrowSize = getArrowSize() * dimscale; |
| |
|
| | RS_Pen dimensionLinePen = getPenDimensionLine(); |
| |
|
| | |
| | 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(dimensionLinePen); |
| | dimensionLine->setLayer(nullptr); |
| |
|
| | |
| |
|
| | RS_Vector textPos; |
| | double textAngle = 0.0; |
| | bool autoText = !m_dimGenericData.middleOfText.valid || forceAutoText; |
| |
|
| | if (autoText) { |
| | textPos = dimensionLine->getMiddlePoint(); |
| |
|
| | |
| | |
| | m_dimGenericData.middleOfText = textPos; |
| | } |
| | else { |
| | textPos = m_dimGenericData.middleOfText; |
| | } |
| |
|
| | auto* text = createDimText(textPos, dimtxt, textAngle); |
| |
|
| | |
| | double textIntersectionLength = 0.0; |
| | double w = text->getUsedTextWidth() / 2 + dimgap; |
| | double h = text->getUsedTextHeight() / 2 + dimgap; |
| |
|
| | |
| | |
| | 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); |
| | textIntersectionLength = sol1.get(0).distanceTo(sol1.get(1)); |
| |
|
| | |
| | bool outsideArrows = (textIntersectionLength + 3 * arrowSize) > distance; |
| |
|
| | |
| | |
| | double arrowAngle1 = outsideArrows ? dimensionLine->getAngle1() : dimensionLine->getAngle2(); |
| | double arrowAngle2 = outsideArrows ? dimensionLine->getAngle2() : dimensionLine->getAngle1(); |
| | const bool showArrows = arrowSize > 1e-6 * p1.distanceTo(p2); |
| | if (outsideArrows) { |
| | |
| | if (showArrows) { |
| | auto dir = RS_Vector::polar(arrowSize * 2, dimensionLine->getAngle1()); |
| |
|
| | dimensionLineOutside1 = new RS_Line{this, p1 - dir, p1}; |
| | dimensionLineOutside1->setPen(dimensionLinePen); |
| |
|
| | dimensionLineOutside2 = new RS_Line{this, p2 + dir, p2}; |
| | dimensionLineOutside2->setPen(dimensionLinePen); |
| | } |
| |
|
| | |
| | if (textIntersectionLength > distance && autoText) { |
| | double dist = (textIntersectionLength + distance) / 2.0 + arrowSize * 2; |
| | RS_Vector distH = RS_Vector::polar(dist, arrowAngle1); |
| | text->move(distH); |
| | textPos = text->getInsertionPoint(); |
| | m_dimGenericData.middleOfText = textPos; |
| | } |
| | } |
| | double dimtsz = getTickSize() * dimscale; |
| | LC_DimArrowRegistry dimArrowRegistry; |
| | auto arrowStyle = m_dimStyleTransient->arrowhead(); |
| | bool firstArrowIsObliqueOrArch = false; |
| | bool secondArrowIsObliqueOrArch = false; |
| |
|
| | if (dimtsz < 0.01) { |
| | |
| | if ( showArrows) { |
| | if (showArrow1) { |
| | auto firstArrowName = arrowStyle->obtainFirstArrowName(); |
| | auto arrow = dimArrowRegistry.createArrowBlock(this, firstArrowName, p1, arrowAngle1, arrowSize); |
| | addArrow(arrow.first, dimensionLinePen); |
| | firstArrowIsObliqueOrArch = dimArrowRegistry.isObliqueOrArchArrow(firstArrowName); |
| | } |
| |
|
| | if (showArrow2) { |
| | auto secondArrowName = arrowStyle->obtainSecondArrowName(); |
| | auto arrow = dimArrowRegistry.createArrowBlock(this, secondArrowName, p2, arrowAngle2, arrowSize); |
| | addArrow(arrow.first, dimensionLinePen); |
| | secondArrowIsObliqueOrArch = dimArrowRegistry.isObliqueOrArchArrow(secondArrowName); |
| | } |
| | } |
| | } |
| | else { |
| | if (showArrow1) { |
| | auto arrow = dimArrowRegistry.createArrowBlock(this, LC_DimArrowRegistry::ArrowInfo::ARROW_TYPE_OBLIQUE, p1, arrowAngle1, arrowSize); |
| | addArrow(arrow.first, dimensionLinePen); |
| | firstArrowIsObliqueOrArch = true; |
| | } |
| | if (showArrow2) { |
| | auto arrow = dimArrowRegistry.createArrowBlock(this, LC_DimArrowRegistry::ArrowInfo::ARROW_TYPE_OBLIQUE, p2, arrowAngle2, arrowSize); |
| | addArrow(arrow.first, dimensionLinePen); |
| | secondArrowIsObliqueOrArch = true; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | if (drawFrameAroundText) { |
| | addBoundsAroundText(dimgap, text); |
| | } |
| |
|
| | |
| | bool splitDimensionLine = false; |
| | |
| | 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) { |
| | |
| | splitDimensionLine = true; |
| | s1 = sol1.get(0); |
| | s2 = sol1.get(1); |
| | } |
| | else if (sol1.size() == 1) { |
| | |
| | splitDimensionLine = true; |
| | if (RS_Information::isPointInsideContour(p1, &c)) { |
| | |
| | s1 = p1; |
| | s2 = sol1.get(0); |
| | } |
| | else { |
| | |
| | s1 = sol1.get(0); |
| | s2 = p2; |
| | } |
| | } |
| | else { |
| | |
| | |
| | |
| | if (RS_Information::isPointInsideContour(p1, &c)) { |
| | splitDimensionLine = true; |
| | s1 = p1; |
| | s2 = p2; |
| | } |
| | } |
| |
|
| | if (splitDimensionLine) { |
| | if (showLine1) { |
| | dimensionLineInside1 = new RS_Line{this, p1, s1}; |
| | } |
| | if (showLine2) { |
| | dimensionLineInside2 = new RS_Line{this, s2, p2}; |
| | } |
| | } |
| |
|
| | |
| | if (outsideArrows && dimensionLineOutside1 != nullptr) { |
| | if (showLine1){ |
| | addDimComponentEntity(dimensionLineOutside1, dimensionLinePen); |
| | } |
| | else { |
| | delete dimensionLineOutside1; |
| | } |
| | if (showLine2) { |
| | addDimComponentEntity(dimensionLineOutside2, dimensionLinePen); |
| | } |
| | else { |
| | delete dimensionLineOutside2; |
| | } |
| | } |
| |
|
| | if (splitDimensionLine) { |
| | auto dimLine = m_dimStyleTransient->dimensionLine(); |
| | if (outsideArrows) { |
| | if (dimLine->drawPolicyForOutsideText() == LC_DimStyle::DimensionLine::DRAW_EVEN_IF_ARROWHEADS_ARE_OUTSIDE) { |
| | addDimComponentEntity(dimensionLineInside1, dimensionLinePen); |
| | addDimComponentEntity(dimensionLineInside2, dimensionLinePen); |
| | } |
| | else { |
| | delete dimensionLineInside1; |
| | delete dimensionLineInside2; |
| | } |
| | } |
| | else { |
| | addDimComponentEntity(dimensionLineInside1, dimensionLinePen); |
| | addDimComponentEntity(dimensionLineInside2, dimensionLinePen); |
| | } |
| | } |
| | else { |
| | addDimComponentEntity(dimensionLine, dimensionLinePen); |
| | } |
| | } |
| |
|
| | RS_VectorSolutions* RS_Dimension::determineTextAreaBounds(RS_MText* text, double dimGap){ |
| | auto* result = new RS_VectorSolutions(); |
| |
|
| | double w = text->getUsedTextWidth()/2 + dimGap; |
| | double h = text->getUsedTextHeight()/2 + dimGap; |
| |
|
| | result->push_back({-w, -h}); |
| | result->push_back({-w, h}); |
| | result->push_back({w, h}); |
| | result->push_back({w, -h}); |
| |
|
| | double ofsx = 0.0; |
| | double ofsy = 0.0; |
| |
|
| | switch (text->getHAlign()) { |
| | case RS_MTextData::HALeft : ofsx = w; break; |
| | case RS_MTextData::HARight : ofsx = -w; break; |
| | default:; |
| | } |
| |
|
| | switch (text->getVAlign()) { |
| | case RS_MTextData::VABottom : ofsy = h; break; |
| | case RS_MTextData::VATop : ofsy = -h; break; |
| | default:; |
| | } |
| |
|
| | result->move(RS_Vector(ofsx, ofsy, 0.0)); |
| | result->rotate(RS_Vector(0,0,0), text->getAngle()); |
| | result->move(text->getInsertionPoint()); |
| | return result; |
| | } |
| |
|
| | void RS_Dimension::addBoundsAroundText(double dimgap, RS_MText* text) { |
| | auto textBounds = determineTextAreaBounds(text, dimgap); |
| | addDimDimensionLine(textBounds->at(0), textBounds->at(1)); |
| | addDimDimensionLine(textBounds->at(1), textBounds->at(2)); |
| | addDimDimensionLine(textBounds->at(2), textBounds->at(3)); |
| | addDimDimensionLine(textBounds->at(3), textBounds->at(0)); |
| | delete textBounds; |
| | } |
| |
|
| | void RS_Dimension::addArrow(RS_Entity* arrow, RS_Pen &dimensionPen) { |
| | addDimComponentEntity(arrow, dimensionPen); |
| | if (arrow->rtti()==RS2::EntityInsert) { |
| | arrow->update(); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | void RS_Dimension::createAlignedTextDimensionLine(const RS_Vector& p1, |
| | const RS_Vector& p2, bool showArrow1, bool showArrow2, |
| | [[maybe_unused]]bool showLine1, [[maybe_unused]]bool showLine2, |
| | bool forceAutoText) { |
| | double dimscale = getGeneralScale(); |
| | double dimtxt = getTextHeight() * dimscale; |
| | double dimgap = getDimensionLineGap() * dimscale; |
| | double dimOffset = getVerticalDistanceToDimLine() * dimscale; |
| |
|
| | bool drawFrameAroundText = std::signbit(dimgap); |
| | if (drawFrameAroundText) { |
| | dimgap = -dimgap; |
| | } |
| |
|
| | |
| | double distance = p1.distanceTo(p2); |
| | |
| | double arrowSize = getArrowSize() * dimscale; |
| | double dimtsz = getTickSize() * dimscale; |
| | if (dimtsz > 0.01) { |
| | arrowSize = dimtsz; |
| | } |
| |
|
| | |
| | bool outsideArrows = (distance < arrowSize * 2.5); |
| |
|
| | |
| | double arrowAngle1, arrowAngle2; |
| | double dimAngle1 = p1.angleTo(p2); |
| |
|
| |
|
| | RS_Vector firstArrowOffsetVector = RS_Vector::polar(arrowSize, dimAngle1); |
| | RS_Vector dimP1 = p1 + firstArrowOffsetVector; |
| |
|
| | RS_Vector secondArrowOffsetVector = RS_Vector::polar(arrowSize, dimAngle1); |
| | RS_Vector dimP2 = p2 - secondArrowOffsetVector; |
| |
|
| | RS_Pen dimensionPen = getPenDimensionLine(); |
| |
|
| | |
| | auto* dimensionLine = addDimComponentLine(dimP1, dimP2, dimensionPen); |
| |
|
| | auto midPoint = (dimP1 + dimP2)*0.5; |
| |
|
| | |
| | RS_Vector textPos; |
| |
|
| | bool corrected = false; |
| | double textAngle = RS_Math::makeAngleReadable(dimAngle1, true, &corrected); |
| |
|
| | if (m_dimGenericData.middleOfText.valid && !forceAutoText) { |
| | textPos = m_dimGenericData.middleOfText; |
| | } |
| | else { |
| | textPos = midPoint; |
| |
|
| | |
| | |
| | double const a = corrected ? -M_PI_2 : M_PI_2; |
| | RS_Vector distV = RS_Vector::polar(dimOffset + dimtxt / 2.0, dimAngle1 + a); |
| |
|
| | |
| | textPos += distV; |
| |
|
| | |
| | |
| | m_dimGenericData.middleOfText = textPos; |
| | } |
| |
|
| | auto text = createDimText(textPos, dimtxt, textAngle); |
| |
|
| | |
| | RS_Vector distH; |
| | double usedTextWidth = text->getUsedTextWidth(); |
| | if (usedTextWidth > distance) { |
| | distH.setPolar(usedTextWidth / 2.0 + distance / 2.0 + dimgap, textAngle); |
| | text->move(distH); |
| | } |
| |
|
| | if (drawFrameAroundText) { |
| | addBoundsAroundText(dimgap, text); |
| | } |
| |
|
| | |
| | |
| | bool flipArrow1 = false; |
| | bool flipArrow2 = false; |
| |
|
| | if (outsideArrows) { |
| | arrowAngle1 = dimensionLine->getAngle1(); |
| | arrowAngle2 = dimensionLine->getAngle2(); |
| | RS_Vector dir = RS_Vector::polar(arrowSize * 2, arrowAngle2); |
| |
|
| | dimensionLine->setStartpoint(p1 + dir); |
| | dimensionLine->setEndpoint(p2 - dir); |
| | } |
| | else { |
| | flipArrow1 = isFlipArrow1(); |
| | flipArrow2 = isFlipArrow2(); |
| | if (flipArrow1) { |
| | arrowAngle1 = dimensionLine->getAngle1(); |
| | RS_Vector dir = RS_Vector::polar(arrowSize * 2, arrowAngle1); |
| | dimensionLine->setStartpoint(p1 - dir); |
| | } |
| | else { |
| | arrowAngle1 = dimensionLine->getAngle2(); |
| | } |
| | if (flipArrow2) { |
| | arrowAngle2 = dimensionLine->getAngle2(); |
| | RS_Vector dir = RS_Vector::polar(arrowSize * 2, arrowAngle2); |
| | dimensionLine->setEndpoint(p2 - dir); |
| | } |
| | else { |
| | arrowAngle2 = dimensionLine->getAngle1(); |
| | } |
| | } |
| |
|
| | LC_DimArrowRegistry dimArrowRegistry; |
| | auto arrowStyle = m_dimStyleTransient->arrowhead(); |
| | bool firstArrowIsObliqueOrArch = false; |
| | bool secondArrowIsObliqueOrArch = false; |
| | double dimLineOffsetFirst{0.0}; |
| | double dimLineOffsetSecond{0.0}; |
| | if (dimtsz < 0.01) { |
| | |
| | if (showArrow1) { |
| | auto firstArrowName = arrowStyle->obtainFirstArrowName(); |
| | auto arrow = dimArrowRegistry.createArrowBlock(this, firstArrowName, p1, arrowAngle1, arrowSize); |
| | addArrow(arrow.first, dimensionPen); |
| | firstArrowIsObliqueOrArch = dimArrowRegistry.isObliqueOrArchArrow(firstArrowName); |
| | RS_Vector firstArrowAdjustmentVector; |
| | if (flipArrow1) { |
| | dimLineOffsetFirst = arrow.second * arrowSize + 3*arrowSize; |
| | firstArrowAdjustmentVector = RS_Vector::polar(dimLineOffsetFirst, dimAngle1); |
| | } |
| | else { |
| | dimLineOffsetFirst = arrow.second * arrowSize; |
| | firstArrowAdjustmentVector = RS_Vector::polar(dimLineOffsetFirst, dimAngle1); |
| | } |
| | dimensionLine->setStartpoint(dimP1-firstArrowAdjustmentVector); |
| | } |
| |
|
| | if (showArrow2) { |
| | auto secondArrowName = arrowStyle->obtainSecondArrowName(); |
| | auto arrow = dimArrowRegistry.createArrowBlock(this, secondArrowName, p2, arrowAngle2, arrowSize); |
| | addArrow(arrow.first, dimensionPen); |
| | secondArrowIsObliqueOrArch = dimArrowRegistry.isObliqueOrArchArrow(secondArrowName); |
| | RS_Vector secondArrowAdjustmentVector; |
| | if (flipArrow2) { |
| | dimLineOffsetSecond = arrow.second * arrowSize + 3 * arrowSize; |
| | secondArrowAdjustmentVector = RS_Vector::polar(dimLineOffsetSecond, dimAngle1); |
| | } |
| | else { |
| | dimLineOffsetSecond = arrow.second * arrowSize; |
| | secondArrowAdjustmentVector = RS_Vector::polar(dimLineOffsetSecond, dimAngle1); |
| | } |
| | dimensionLine->setEndpoint(dimP2+secondArrowAdjustmentVector); |
| | } |
| | } |
| | else { |
| | if (showArrow1) { |
| | auto arrow = dimArrowRegistry.createArrowBlock(this, LC_DimArrowRegistry::ArrowInfo::ARROW_TYPE_OBLIQUE, p1, arrowAngle1, arrowSize); |
| | addArrow(arrow.first, dimensionPen); |
| | firstArrowIsObliqueOrArch = true; |
| | dimLineOffsetFirst = arrow.second * arrowSize; |
| | RS_Vector firstArrowAdjustmentVector = RS_Vector::polar(dimLineOffsetFirst, dimAngle1); |
| | dimensionLine->setEndpoint(dimP2-firstArrowAdjustmentVector); |
| | } |
| | if (showArrow2) { |
| | auto arrow = dimArrowRegistry.createArrowBlock(this, LC_DimArrowRegistry::ArrowInfo::ARROW_TYPE_OBLIQUE, p2, arrowAngle2, arrowSize); |
| | addArrow(arrow.first, dimensionPen); |
| | secondArrowIsObliqueOrArch = true; |
| | dimLineOffsetSecond = arrow.second * arrowSize; |
| | RS_Vector secondArrowAdjustmentVector = RS_Vector::polar(dimLineOffsetSecond, dimAngle1); |
| | dimensionLine->setEndpoint(dimP2+secondArrowAdjustmentVector); |
| | } |
| | } |
| |
|
| | |
| | RS_Vector extOffsetVector{false}; |
| | double obliqueExtent = m_dimStyleTransient->dimensionLine()->distanceBeyondExtLinesForObliqueStroke(); |
| | if (LC_LineMath::isMeaningful(obliqueExtent)) { |
| | obliqueExtent = obliqueExtent * dimscale; |
| | if (firstArrowIsObliqueOrArch) { |
| | extOffsetVector = RS_Vector::polar(obliqueExtent, dimAngle1); |
| | addDimComponentLine(p1, p1 - extOffsetVector, dimensionPen); |
| | } |
| |
|
| | if (secondArrowIsObliqueOrArch) { |
| | if (!extOffsetVector.valid) { |
| | extOffsetVector = RS_Vector::polar(obliqueExtent, dimAngle1); |
| | } |
| | addDimComponentLine(p2, p2 + extOffsetVector, dimensionPen); |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | void RS_Dimension::createDimensionLine(const RS_Vector& dimLineStart, const RS_Vector& dimLineEnd, |
| | bool showArrow1, bool showArrow2, bool showLine1, bool showLine2, bool forceAutoText) { |
| | if (getInsideHorizontalText()) { |
| | createHorizontalTextDimensionLine(dimLineStart, dimLineEnd, showArrow1, showArrow2, showLine1, showLine2, forceAutoText); |
| | } |
| | else { |
| | createAlignedTextDimensionLine(dimLineStart, dimLineEnd, showArrow1, showArrow2, showLine1,showLine2, forceAutoText); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | double RS_Dimension::getGeneralFactor() { |
| | return m_dimStyleTransient->scaling()->linearFactor(); |
| | |
| | } |
| |
|
| | |
| | |
| | |
| | double RS_Dimension::getGeneralScale() { |
| | return m_dimStyleTransient->scaling()->scale(); |
| | |
| | } |
| |
|
| | |
| | |
| | |
| | double RS_Dimension::getArrowSize() { |
| | return m_dimStyleTransient->arrowhead()->size(); |
| | |
| | } |
| |
|
| | |
| | |
| | |
| | double RS_Dimension::getTickSize() { |
| | return m_dimStyleTransient->arrowhead()->tickSize(); |
| | |
| | } |
| |
|
| | |
| | |
| | |
| | double RS_Dimension::getExtensionLineExtension() { |
| | return m_dimStyleTransient->extensionLine()->distanceBeyondDimLine(); |
| | |
| | } |
| |
|
| | |
| | |
| | |
| | double RS_Dimension::getExtensionLineOffset() { |
| | return m_dimStyleTransient->extensionLine()->distanceFromOriginPoint(); |
| |
|
| | |
| | } |
| |
|
| | |
| | |
| | |
| | double RS_Dimension::getDimensionLineGap() { |
| | return m_dimStyleTransient->dimensionLine()->lineGap(); |
| | |
| | } |
| |
|
| | double RS_Dimension::getVerticalDistanceToDimLine() { |
| | auto text = m_dimStyleTransient->text(); |
| | if (text->verticalPositioning() != LC_DimStyle::Text::CENTER_BETWEEN_EXT_LINES) { |
| | return text->verticalDistanceToDimLine(); |
| | } |
| | else { |
| | return m_dimStyleTransient->dimensionLine()->lineGap(); |
| | } |
| | |
| | } |
| |
|
| | |
| | |
| | |
| | double RS_Dimension::getTextHeight() { |
| | return m_dimStyleTransient->text()->height(); |
| | |
| | } |
| |
|
| | |
| | |
| | |
| | bool RS_Dimension::getInsideHorizontalText() { |
| | auto orientationPolicy = m_dimStyleTransient->text()->orientationInside(); |
| | return orientationPolicy == LC_DimStyle::Text::TextOrientationPolicy::DRAW_HORIZONTALLY; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | } |
| |
|
| | |
| | |
| | |
| | bool RS_Dimension::getFixedLengthOn() { |
| | return m_dimStyleTransient->extensionLine()->hasFixedLength(); |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | } |
| |
|
| | |
| | |
| | |
| | double RS_Dimension::getFixedLength() { |
| | return m_dimStyleTransient->extensionLine()->fixedLength(); |
| | |
| | } |
| |
|
| | |
| | |
| | |
| | RS2::LineWidth RS_Dimension::getExtensionLineWidth() { |
| | return m_dimStyleTransient->extensionLine()->lineWidth(); |
| | |
| | } |
| |
|
| | |
| | |
| | |
| | RS2::LineWidth RS_Dimension::getDimensionLineWidth() { |
| | return m_dimStyleTransient->dimensionLine()->lineWidth(); |
| | |
| | } |
| |
|
| | RS2::LineType RS_Dimension::getExtensionLineTypeFirst() { |
| | RS2::LineType result = m_dimStyleTransient->extensionLine()->lineTypeFirst(); |
| | return result; |
| | } |
| |
|
| | RS2::LineType RS_Dimension::getExtensionLineTypeSecond() { |
| | RS2::LineType result = m_dimStyleTransient->extensionLine()->lineTypeSecond(); |
| | return result; |
| | } |
| |
|
| | RS2::LineType RS_Dimension::getDimensionLineType() { |
| | RS2::LineType result = m_dimStyleTransient->dimensionLine()->lineType(); |
| | return result; |
| | } |
| |
|
| | |
| | |
| | |
| | RS_Color RS_Dimension::getDimensionLineColor() { |
| | |
| | return m_dimStyleTransient->dimensionLine()->color(); |
| |
|
| | |
| | } |
| |
|
| | |
| | |
| | |
| | RS_Color RS_Dimension::getExtensionLineColor() { |
| | |
| | return m_dimStyleTransient->extensionLine()->color(); |
| | |
| | } |
| |
|
| | |
| | |
| | |
| | RS_Color RS_Dimension::getTextColor() { |
| | return m_dimStyleTransient->text()->color(); |
| | |
| | } |
| |
|
| | |
| | |
| | |
| | QString RS_Dimension::getTextStyle() { |
| | return m_dimStyleTransient->text()->style(); |
| | |
| | } |
| |
|
| | RS2::LinearFormat RS_Dimension::getDimLinearFormat() { |
| | return m_dimStyleTransient->linearFormat()->format(); |
| | |
| | } |
| |
|
| | int RS_Dimension::getDimDecimalPlaces() { |
| | return m_dimStyleTransient->linearFormat()->decimalPlaces(); |
| | |
| | } |
| |
|
| | int RS_Dimension::getDimTrailingZerosSuppressionMode() { |
| | return m_dimStyleTransient->zerosSuppression()->linearRaw(); |
| | |
| | } |
| |
|
| | int RS_Dimension::getDimDecimalFormatSeparatorChar() { |
| | return m_dimStyleTransient->linearFormat()->decimalFormatSeparatorChar(); |
| | |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | 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; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | QString RS_Dimension::stripZerosAngle(QString angle, int zeros) { |
| | if (zeros == 0) { |
| | |
| | return angle; |
| | } |
| | if (zeros & 2 && (angle.contains(QString('.')) || angle.contains(QString(',')))) { |
| | int end = angle.size() - 1; |
| | QChar format = angle[end--]; |
| | while (end > 0 && angle[end] == QChar('0')) { |
| | |
| | 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; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | QString RS_Dimension::stripZerosLinear(QString linear, int zeros) { |
| | |
| | if (zeros == 1) { |
| | return linear; |
| | } |
| |
|
| | |
| | if (linear.size() <= 1) { |
| | return linear; |
| | } |
| |
|
| | |
| | if (zeros & 8 && (linear.contains(QString('.')) || linear.contains(QString(',')))) { |
| | |
| | int i = linear.size() - 1; |
| | |
| | while (i > 0 && linear[i] == QChar('0')) { |
| | i--; |
| | } |
| | |
| | if ((linear[i] == QChar('.') || linear[i] == QChar(',')) && i > 0) { |
| | i--; |
| | } |
| | |
| | linear = linear.remove(i + 1, linear.size() - i); |
| | } |
| | |
| | if (zeros & 4) { |
| | int i = 0; |
| | |
| | while (i < linear.size() - 1 && linear[i] == QChar('0')) { |
| | i++; |
| | } |
| | linear = linear.remove(0, i); |
| | } |
| | return linear; |
| | } |
| |
|
| | void RS_Dimension::move(const RS_Vector& offset) { |
| | m_dimGenericData.definitionPoint.move(offset); |
| | m_dimGenericData.middleOfText.move(offset); |
| | } |
| |
|
| | void RS_Dimension::rotate(const RS_Vector& center, double angle) { |
| | RS_Vector angleVector(angle); |
| | m_dimGenericData.definitionPoint.rotate(center, angleVector); |
| | m_dimGenericData.middleOfText.rotate(center, angleVector); |
| | m_dimGenericData.angle = RS_Math::correctAngle(m_dimGenericData.angle + angle); |
| | } |
| |
|
| | void RS_Dimension::rotate(const RS_Vector& center, const RS_Vector& angleVector) { |
| | m_dimGenericData.definitionPoint.rotate(center, angleVector); |
| | m_dimGenericData.middleOfText.rotate(center, angleVector); |
| | m_dimGenericData.angle = RS_Math::correctAngle(m_dimGenericData.angle + angleVector.angle()); |
| | } |
| |
|
| | void RS_Dimension::scale(const RS_Vector& center, const RS_Vector& factor) { |
| | m_dimGenericData.definitionPoint.scale(center, factor); |
| | m_dimGenericData.middleOfText.scale(center, factor); |
| | } |
| |
|
| | void RS_Dimension::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) { |
| | m_dimGenericData.definitionPoint.mirror(axisPoint1, axisPoint2); |
| | m_dimGenericData.middleOfText.mirror(axisPoint1, axisPoint2); |
| | } |
| |
|
| | void RS_Dimension::update() { |
| | clear(); |
| | if (isUndone()) { |
| | return; |
| | } |
| | resolveEffectiveDimStyleAndUpdateDim(); |
| | calculateBorders(); |
| | } |
| |
|
| | LC_DimStyle* RS_Dimension::getGlobalDimStyle() { |
| | auto dimStyleName = getStyle(); |
| | auto globalDimStyle = getGraphic()->getResolvedDimStyle(dimStyleName, rtti()); |
| | return globalDimStyle; |
| | } |
| |
|
| | LC_DimStyle* RS_Dimension::getEffectiveDimStyle() { |
| | auto dimStyleName = getStyle(); |
| | LC_DimStyle* result = getGraphic()->getEffectiveDimStyle(dimStyleName, rtti(), getDimStyleOverride()); |
| | return result; |
| | } |
| |
|
| | void RS_Dimension::resolveEffectiveDimStyleAndUpdateDim() { |
| | m_dimStyleTransient = getEffectiveDimStyle(); |
| | if (m_dimStyleTransient != nullptr) { |
| | doUpdateDim(); |
| | if (getDimStyleOverride() != nullptr) { |
| | delete m_dimStyleTransient; |
| | } |
| | } |
| | m_dimStyleTransient = nullptr; |
| | } |
| |
|
| | void RS_Dimension::updateDim(bool autoText) { |
| | m_dimGenericData.autoText = autoText; |
| | clear(); |
| | if (isUndone()) { |
| | return; |
| | } |
| | resolveEffectiveDimStyleAndUpdateDim(); |
| | } |
| |
|
| | void RS_Dimension::addDimComponentEntity(RS_Entity* en, const RS_Pen &pen) { |
| | if (en != nullptr) { |
| | en->setPen(pen); |
| | en->setLayer(nullptr); |
| | addEntity(en); |
| | } |
| | } |
| |
|
| | RS_Line* RS_Dimension::addDimExtensionLine(RS_Vector start, RS_Vector end, bool first) { |
| | return addDimComponentLine(start, end, getPenExtensionLine(first)); |
| | } |
| |
|
| | RS_Line* RS_Dimension::addDimDimensionLine(RS_Vector start, RS_Vector end) { |
| | return addDimComponentLine(start, end, getPenDimensionLine()); |
| | } |
| |
|
| | RS_Arc* RS_Dimension::addDimArc(RS_ArcData& arcData) { |
| | auto arc = new RS_Arc(this, arcData); |
| | RS_Pen pen = getPenDimensionLine(); |
| | addDimComponentEntity(arc, pen); |
| | return arc; |
| | } |
| |
|
| | RS_Line* RS_Dimension::addDimComponentLine(RS_Vector start, RS_Vector end, const RS_Pen &pen) { |
| | auto line = new RS_Line(this, {start, end}); |
| | line->setPen(pen); |
| | line->setLayer(nullptr); |
| | addEntity(line); |
| | return line; |
| | } |
| |
|
| | QString RS_Dimension::createLinearMeasuredLabel(double dist) { |
| | RS_Graphic* graphic = getGraphic(); |
| | QString ret; |
| | if (graphic) { |
| | |
| | int dimdec = getDimDecimalPlaces(); |
| | int dimzin = getDimTrailingZerosSuppressionMode(); |
| | |
| | RS2::LinearFormat format = getDimLinearFormat(); |
| |
|
| | ret = RS_Units::formatLinear(dist, getGraphicUnit(), format, dimdec); |
| | if (format == RS2::Decimal) { |
| | ret = stripZerosLinear(ret, dimzin); |
| | } |
| | |
| | if (format == RS2::Decimal || format == RS2::ArchitecturalMetric){ |
| | if (getDimDecimalFormatSeparatorChar() == 44) { |
| | ret.replace(QChar('.'), QChar(',')); |
| | } |
| | } |
| | } |
| | else { |
| | ret = QString("%1").arg(dist); |
| | } |
| | return ret; |
| | } |
| |
|
| |
|
| | double RS_Dimension::prepareLabelLinearDistance(double distance) { |
| | double dist = distance * getGeneralFactor(); |
| | if (!LC_GET_ONE_BOOL("Appearance", "UnitlessGrid", true)) { |
| | dist = RS_Units::convert(dist); |
| | } |
| | return dist; |
| | } |
| |
|