From 39518f8dff9b3048aeb49db4a155a014bd46deb0 Mon Sep 17 00:00:00 2001 From: Tomas Pavlicek Date: Mon, 23 Sep 2019 09:27:57 +0200 Subject: [PATCH] Complete remake of drawing dimensions --- src/Mod/TechDraw/App/DrawUtil.cpp | 356 +++ src/Mod/TechDraw/App/DrawUtil.h | 46 + src/Mod/TechDraw/App/DrawViewDimension.cpp | 21 +- src/Mod/TechDraw/App/DrawViewDimension.h | 5 +- src/Mod/TechDraw/Gui/DlgPrefsTechDraw2.ui | 12 +- src/Mod/TechDraw/Gui/QGIView.cpp | 6 + src/Mod/TechDraw/Gui/QGIView.h | 1 + src/Mod/TechDraw/Gui/QGIViewDimension.cpp | 2506 +++++++++-------- src/Mod/TechDraw/Gui/QGIViewDimension.h | 112 +- .../TechDraw/Gui/ViewProviderDimension.cpp | 16 +- src/Mod/TechDraw/Gui/ViewProviderDimension.h | 22 +- 11 files changed, 1837 insertions(+), 1266 deletions(-) diff --git a/src/Mod/TechDraw/App/DrawUtil.cpp b/src/Mod/TechDraw/App/DrawUtil.cpp index 9e5401cf83..e00a334d3d 100644 --- a/src/Mod/TechDraw/App/DrawUtil.cpp +++ b/src/Mod/TechDraw/App/DrawUtil.cpp @@ -622,6 +622,362 @@ PyObject* DrawUtil::colorToPyTuple(App::Color color) return pTuple; } +// Supplementary mathematical functions +// ==================================== + +int DrawUtil::sgn(double x) +{ + return (x > +Precision::Confusion()) - (x < -Precision::Confusion()); +} + +double DrawUtil::sqr(double x) +{ + return x*x; +} + +void DrawUtil::angleNormalize(double &fi) +{ + while (fi <= -M_PI) { + fi += M_2PI; + } + while (fi > M_PI) { + fi -= M_2PI; + } +} + +double DrawUtil::angleComposition(double fi, double delta) +{ + fi += delta; + + angleNormalize(fi); + return fi; +} + +double DrawUtil::angleDifference(double fi1, double fi2, bool reflex) +{ + angleNormalize(fi1); + angleNormalize(fi2); + + fi1 -= fi2; + + if (((fi1 > +M_PI) || (fi1 <= -M_PI)) != reflex) { + fi1 += fi1 > 0.0 ? -M_2PI : +M_2PI; + } + + return fi1; +} + +// Interval marking functions +// ========================== + +unsigned int DrawUtil::intervalMerge(std::vector> &marking, + double boundary, bool wraps) +{ + // We will be returning the placement index instead of an iterator, because indices + // are still valid after we insert on higher positions, while iterators may be invalidated + // due to the insertion triggered reallocation + unsigned int i = 0; + bool last = false; + + if (wraps && marking.size() > 0) { + last = marking.back().second; + } + + while (i < marking.size()) { + if (marking[i].first == boundary) { + return i; + } + if (marking[i].first > boundary) { + break; + } + + last = marking[i].second; + ++i; + } + + if (!wraps && i >= marking.size()) { + last = false; + } + + marking.insert(marking.begin() + i, std::pair(boundary, last)); + return i; +} + +void DrawUtil::intervalMarkLinear(std::vector> &marking, + double start, double length, bool value) +{ + if (length == 0.0) { + return; + } + if (length < 0.0) { + length = -length; + start -= length; + } + + unsigned int startIndex = intervalMerge(marking, start, false); + unsigned int endIndex = intervalMerge(marking, start + length, false); + + while (startIndex < endIndex) { + marking[startIndex].second = value; + ++startIndex; + } +} + +void DrawUtil::intervalMarkCircular(std::vector> &marking, + double start, double length, bool value) +{ + if (length == 0.0) { + return; + } + if (length < 0.0) { + length = -length; + start -= length; + } + if (length > M_2PI) { + length = M_2PI; + } + + angleNormalize(start); + + double end = start + length; + if (end > M_PI) { + end -= M_2PI; + } + + // Just make sure the point is stored, its index is read last + intervalMerge(marking, end, true); + unsigned int startIndex = intervalMerge(marking, start, true); + unsigned int endIndex = intervalMerge(marking, end, true); + + do { + marking[startIndex].second = value; + ++startIndex; + startIndex %= marking.size(); + } + while (startIndex != endIndex); +} + +// Supplementary 2D analytic geometry functions +//============================================= + +int DrawUtil::findRootForValue(double Ax2, double Bxy, double Cy2, double Dx, double Ey, double F, + double value, bool findX, double roots[]) +{ + double qA = 0.0; + double qB = 0.0; + double qC = 0.0; + + if (findX) { + qA = Ax2; + qB = Bxy*value + Dx; + qC = Cy2*value*value + Ey*value + F; + } + else { + qA = Cy2; + qB = Bxy*value + Ey; + qC = Ax2*value*value + Dx*value + F; + } + + if (fabs(qA) < Precision::Confusion()) { + // No quadratic coefficient - the equation is linear + if (fabs(qB) < Precision::Confusion()) { + // Not even linear coefficient - test for zero + if (fabs(qC) > Precision::Confusion()) { + // This equation has no solution + return 0; + } + else { + // Signal infinite number of solutions by returning 2, but do not touch root variables + return 2; + } + } + else { + roots[0] = -qC/qB; + return 1; + } + } + else { + double qD = sqr(qB) - 4.0*qA*qC; + if (qD < -Precision::Confusion()) { + // Negative discriminant => no real roots + return 0; + } + else if (qD > +Precision::Confusion()) { + // Two distinctive roots + roots[0] = (-qB + sqrt(qD))*0.5/qA; + roots[1] = (-qB - sqrt(qD))*0.5/qA; + return 2; + } + else { + // Double root + roots[0] = -qB*0.5/qA; + return 1; + } + } +} + +bool DrawUtil::mergeBoundedPoint(const Base::Vector2d &point, const Base::BoundBox2d &boundary, + std::vector &storage) +{ + if (!boundary.Contains(point, Precision::Confusion())) { + return false; + } + + for (unsigned int i = 0; i < storage.size(); ++i) { + if (point.IsEqual(storage[i], Precision::Confusion())) { + return false; + } + } + + storage.push_back(point); + return true; +} + +void DrawUtil::findConicRectangleIntersections(double conicAx2, double conicBxy, double conicCy2, + double conicDx, double conicEy, double conicF, + const Base::BoundBox2d &rectangle, + std::vector &intersections) +{ + double roots[2]; + int rootCount; + + // Find intersections with rectangle left side line + roots[0] = rectangle.MinY; + roots[1] = rectangle.MaxY; + rootCount = findRootForValue(conicAx2, conicBxy, conicCy2, conicDx, conicEy, conicF, + rectangle.MinX, false, roots); + if (rootCount > 0) { + mergeBoundedPoint(Base::Vector2d(rectangle.MinX, roots[0]), rectangle, intersections); + } + if (rootCount > 1) { + mergeBoundedPoint(Base::Vector2d(rectangle.MinX, roots[1]), rectangle, intersections); + } + + // Find intersections with rectangle right side line + roots[0] = rectangle.MinY; + roots[1] = rectangle.MaxY; + rootCount = findRootForValue(conicAx2, conicBxy, conicCy2, conicDx, conicEy, conicF, + rectangle.MaxX, false, roots); + if (rootCount > 0) { + mergeBoundedPoint(Base::Vector2d(rectangle.MaxX, roots[0]), rectangle, intersections); + } + if (rootCount > 1) { + mergeBoundedPoint(Base::Vector2d(rectangle.MaxX, roots[1]), rectangle, intersections); + } + + // Find intersections with rectangle top side line + roots[0] = rectangle.MinX; + roots[1] = rectangle.MaxX; + rootCount = findRootForValue(conicAx2, conicBxy, conicCy2, conicDx, conicEy, conicF, + rectangle.MinY, true, roots); + if (rootCount > 0) { + mergeBoundedPoint(Base::Vector2d(roots[0], rectangle.MinY), rectangle, intersections); + } + if (rootCount > 1) { + mergeBoundedPoint(Base::Vector2d(roots[1], rectangle.MinY), rectangle, intersections); + } + + // Find intersections with rectangle top side line + roots[0] = rectangle.MinX; + roots[1] = rectangle.MaxX; + rootCount = findRootForValue(conicAx2, conicBxy, conicCy2, conicDx, conicEy, conicF, + rectangle.MaxY, true, roots); + if (rootCount > 0) { + mergeBoundedPoint(Base::Vector2d(roots[0], rectangle.MaxY), rectangle, intersections); + } + if (rootCount > 1) { + mergeBoundedPoint(Base::Vector2d(roots[1], rectangle.MaxY), rectangle, intersections); + } +} + +void DrawUtil::findLineRectangleIntersections(const Base::Vector2d &linePoint, double lineAngle, + const Base::BoundBox2d &rectangle, + std::vector &intersections) +{ + Base::Vector2d lineDirection(Base::Vector2d::FromPolar(1.0, lineAngle)); + findConicRectangleIntersections(0.0, 0.0, 0.0, +lineDirection.y, -lineDirection.x, + lineDirection.x*linePoint.y - lineDirection.y*linePoint.x, + rectangle, intersections); +} + +void DrawUtil::findCircleRectangleIntersections(const Base::Vector2d &circleCenter, double circleRadius, + const Base::BoundBox2d &rectangle, + std::vector &intersections) +{ + findConicRectangleIntersections(1.0, 0.0, 1.0, -2.0*circleCenter.x, -2.0*circleCenter.y, + sqr(circleCenter.x) + sqr(circleCenter.y) - sqr(circleRadius), + rectangle, intersections); +} + +void DrawUtil::findLineSegmentRectangleIntersections(const Base::Vector2d &linePoint, double lineAngle, + double segmentBasePosition, double segmentLength, + const Base::BoundBox2d &rectangle, + std::vector &intersections) +{ + findLineRectangleIntersections(linePoint, lineAngle, rectangle, intersections); + + if (segmentLength < 0.0) { + segmentLength = -segmentLength; + segmentBasePosition -= segmentLength; + } + + // Dispose the points on rectangle but not within the line segment boundaries + Base::Vector2d segmentDirection(Base::Vector2d::FromPolar(1.0, lineAngle)); + for (unsigned int i = 0; i < intersections.size(); ) { + double pointPosition = segmentDirection*(intersections[i] - linePoint); + + if (pointPosition < segmentBasePosition - Precision::Confusion() + || pointPosition > segmentBasePosition + segmentLength + Precision::Confusion()) { + intersections.erase(intersections.begin() + i); + } + else { + ++i; + } + } + + // Try to add the line segment end points + mergeBoundedPoint(linePoint + segmentBasePosition*segmentDirection, + rectangle, intersections); + mergeBoundedPoint(linePoint + (segmentBasePosition + segmentLength)*segmentDirection, + rectangle, intersections); +} + +void DrawUtil::findCircularArcRectangleIntersections(const Base::Vector2d &circleCenter, double circleRadius, + double arcBaseAngle, double arcRotation, + const Base::BoundBox2d &rectangle, + std::vector &intersections) +{ + findCircleRectangleIntersections(circleCenter, circleRadius, rectangle, intersections); + + if (arcRotation < 0.0) { + arcRotation = -arcRotation; + arcBaseAngle -= arcRotation; + if (arcBaseAngle <= -M_PI) { + arcBaseAngle += M_2PI; + } + } + + // Dispose the points on rectangle but not within the circular arc boundaries + for (unsigned int i = 0; i < intersections.size(); ) { + double pointAngle = (intersections[i] - circleCenter).Angle(); + if (pointAngle < arcBaseAngle - Precision::Confusion()) { + pointAngle += M_2PI; + } + + if (pointAngle > arcBaseAngle + arcRotation + Precision::Confusion()) { + intersections.erase(intersections.begin() + i); + } + else { + ++i; + } + } + + // Try to add the circular arc end points + mergeBoundedPoint(circleCenter + Base::Vector2d::FromPolar(circleRadius, arcBaseAngle), + rectangle, intersections); + mergeBoundedPoint(circleCenter + Base::Vector2d::FromPolar(circleRadius, arcBaseAngle + arcRotation), + rectangle, intersections); +} //============================ // various debugging routines. diff --git a/src/Mod/TechDraw/App/DrawUtil.h b/src/Mod/TechDraw/App/DrawUtil.h index 7ed6531b49..7ca4347e9d 100644 --- a/src/Mod/TechDraw/App/DrawUtil.h +++ b/src/Mod/TechDraw/App/DrawUtil.h @@ -49,6 +49,11 @@ #include "LineGroup.h" + +#ifndef M_2PI + #define M_2PI ((M_PI)*2.0) +#endif + #define VERTEXTOLERANCE (2.0 * Precision::Confusion()) namespace TechDraw @@ -103,6 +108,47 @@ class TechDrawExport DrawUtil { static App::Color pyTupleToColor(PyObject* pColor); static PyObject* colorToPyTuple(App::Color color); + // Supplementary mathematical functions + static int sgn(double x); + static double sqr(double x); + static void angleNormalize(double &fi); + static double angleComposition(double fi, double delta); + static double angleDifference(double fi1, double fi2, bool reflex = false); + + // Interval marking functions + static unsigned int intervalMerge(std::vector> &marking, + double boundary, bool wraps); + static void intervalMarkLinear(std::vector> &marking, + double start, double length, bool value); + static void intervalMarkCircular(std::vector> &marking, + double start, double length, bool value); + + // Supplementary 2D analytic geometry functions + static int findRootForValue(double Ax2, double Bxy, double Cy2, double Dx, double Ey, double F, + double value, bool findX, double roots[]); + static bool mergeBoundedPoint(const Base::Vector2d &point, const Base::BoundBox2d &boundary, + std::vector &storage); + + static void findConicRectangleIntersections(double conicAx2, double conicBxy, double conicCy2, + double conicDx, double conicEy, double conicF, + const Base::BoundBox2d &rectangle, + std::vector &intersections); + static void findLineRectangleIntersections(const Base::Vector2d &linePoint, double lineAngle, + const Base::BoundBox2d &rectangle, + std::vector &intersections); + static void findCircleRectangleIntersections(const Base::Vector2d &circleCenter, double circleRadius, + const Base::BoundBox2d &rectangle, + std::vector &intersections); + + static void findLineSegmentRectangleIntersections(const Base::Vector2d &linePoint, double lineAngle, + double segmentBasePosition, double segmentLength, + const Base::BoundBox2d &rectangle, + std::vector &intersections); + static void findCircularArcRectangleIntersections(const Base::Vector2d &circleCenter, double circleRadius, + double arcBaseAngle, double arcRotation, + const Base::BoundBox2d &rectangle, + std::vector &intersections); + //debugging routines static void dumpVertexes(const char* text, const TopoDS_Shape& s); static void dumpEdge(char* label, int i, TopoDS_Edge e); diff --git a/src/Mod/TechDraw/App/DrawViewDimension.cpp b/src/Mod/TechDraw/App/DrawViewDimension.cpp index cdfa1bde71..30ff00e003 100644 --- a/src/Mod/TechDraw/App/DrawViewDimension.cpp +++ b/src/Mod/TechDraw/App/DrawViewDimension.cpp @@ -97,6 +97,7 @@ DrawViewDimension::DrawViewDimension(void) References2D.setScope(App::LinkScope::Global); ADD_PROPERTY_TYPE(References3D,(0,0),"",(App::PropertyType)(App::Prop_None),"3D Geometry References"); References3D.setScope(App::LinkScope::Global); + ADD_PROPERTY_TYPE(FormatSpec,("") , "Format",(App::PropertyType)(App::Prop_None),"Dimension Format"); ADD_PROPERTY_TYPE(Arbitrary,(false) ,"Format",(App::PropertyType)(App::Prop_None),"Value overridden by user"); @@ -104,8 +105,10 @@ DrawViewDimension::DrawViewDimension(void) ADD_PROPERTY(Type,((long)0)); MeasureType.setEnums(MeasureTypeEnums); ADD_PROPERTY(MeasureType, ((long)1)); //Projected (or True) measurement + ADD_PROPERTY_TYPE(TheoreticalExact,(false),"",(App::PropertyType)(App::Prop_None),"Set for theoretical exact (basic) dimension"); ADD_PROPERTY_TYPE(OverTolerance ,(0.0),"",App::Prop_None,"+ Tolerance value"); ADD_PROPERTY_TYPE(UnderTolerance ,(0.0),"",App::Prop_None,"- Tolerance value"); + ADD_PROPERTY_TYPE(Inverted,(false),"",(App::PropertyType)(App::Prop_None),"The dimensional value is displayed inverted"); //hide the properties the user can't edit in the property editor // References2D.setStatus(App::Property::Hidden,true); @@ -509,7 +512,7 @@ App::DocumentObjectExecReturn *DrawViewDimension::execute(void) return DrawView::execute(); } -std::string DrawViewDimension::getFormatedValue(bool obtuse) +std::string DrawViewDimension::getFormatedValue() { // Base::Console().Message("DVD::getFormatedValue()\n"); std::string result; @@ -518,7 +521,7 @@ std::string DrawViewDimension::getFormatedValue(bool obtuse) } QString specStr = QString::fromUtf8(FormatSpec.getStrValue().data(),FormatSpec.getStrValue().size()); - double val = std::abs(getDimValue()); //internal units! + double val = getDimValue(); bool angularMeasure = false; Base::Quantity qVal; @@ -527,9 +530,6 @@ std::string DrawViewDimension::getFormatedValue(bool obtuse) (Type.isValue("Angle3Pt")) ) { angularMeasure = true; qVal.setUnit(Base::Unit::Angle); - if (obtuse) { - qVal.setValue(fabs(360.0 - val)); - } } else { qVal.setUnit(Base::Unit::Length); } @@ -712,6 +712,17 @@ double DrawViewDimension::getDimValue() result = legAngle; } } + + result = fabs(result); + if (Inverted.getValue()) { + if (Type.isValue("Angle") || Type.isValue("Angle3Pt")) { + result = 360 - result; + } + else { + result = -result; + } + } + return result; } diff --git a/src/Mod/TechDraw/App/DrawViewDimension.h b/src/Mod/TechDraw/App/DrawViewDimension.h index d281de5658..74e7345511 100644 --- a/src/Mod/TechDraw/App/DrawViewDimension.h +++ b/src/Mod/TechDraw/App/DrawViewDimension.h @@ -97,6 +97,9 @@ public: App::PropertyLinkSubList References2D; //Points to Projection SubFeatures App::PropertyLinkSubList References3D; //Points to 3D Geometry SubFeatures App::PropertyEnumeration Type; //DistanceX,DistanceY,Diameter, etc + + App::PropertyBool TheoreticalExact; + App::PropertyBool Inverted; App::PropertyString FormatSpec; App::PropertyBool Arbitrary; App::PropertyFloat OverTolerance; @@ -120,7 +123,7 @@ public: //return PyObject as DrawViewDimensionPy virtual PyObject *getPyObject(void); - virtual std::string getFormatedValue(bool obtuse = false); + virtual std::string getFormatedValue(); virtual double getDimValue(); DrawViewPart* getViewPart() const; virtual QRectF getRect() const { return QRectF(0,0,1,1);} //pretend dimensions always fit! diff --git a/src/Mod/TechDraw/Gui/DlgPrefsTechDraw2.ui b/src/Mod/TechDraw/Gui/DlgPrefsTechDraw2.ui index a7b1d5399f..967407285d 100644 --- a/src/Mod/TechDraw/Gui/DlgPrefsTechDraw2.ui +++ b/src/Mod/TechDraw/Gui/DlgPrefsTechDraw2.ui @@ -330,12 +330,7 @@ - ISO Levelled - - - - - ASME Regular + ISO Referencing @@ -343,6 +338,11 @@ ASME Inlined + + + ASME Referencing + + diff --git a/src/Mod/TechDraw/Gui/QGIView.cpp b/src/Mod/TechDraw/Gui/QGIView.cpp index 57b87887df..ddd2f55821 100644 --- a/src/Mod/TechDraw/Gui/QGIView.cpp +++ b/src/Mod/TechDraw/Gui/QGIView.cpp @@ -699,6 +699,12 @@ int QGIView::calculateFontPixelSize(double sizeInMillimetres) return (int) (Rez::guiX(sizeInMillimetres) + 0.5); } +int QGIView::calculateFontPixelWidth(const QFont &font) +{ + // Return the width of digit 0, most likely the most wide digit + return QFontMetrics(font).width(QChar::fromLatin1('0')); +} + const double QGIView::DefaultFontSizeInMM = 5.0; void QGIView::dumpRect(char* text, QRectF r) { diff --git a/src/Mod/TechDraw/Gui/QGIView.h b/src/Mod/TechDraw/Gui/QGIView.h index e24b3bdd44..41210a9fea 100644 --- a/src/Mod/TechDraw/Gui/QGIView.h +++ b/src/Mod/TechDraw/Gui/QGIView.h @@ -115,6 +115,7 @@ public: static Gui::ViewProvider* getViewProvider(App::DocumentObject* obj); static QGVPage* getGraphicsView(TechDraw::DrawView* dv); static int calculateFontPixelSize(double sizeInMillimetres); + static int calculateFontPixelWidth(const QFont &font); static const double DefaultFontSizeInMM; MDIViewPage* getMDIViewPage(void) const; diff --git a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp index 902b9e9cb0..a3568709d8 100644 --- a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp @@ -69,9 +69,6 @@ #include "ViewProviderDimension.h" #include "DrawGuiUtil.h" -#ifndef M_2PI - #define M_2PI ((M_PI) * 2.0) -#endif //TODO: hide the Qt coord system (+y down). @@ -102,6 +99,9 @@ QGIDatumLabel::QGIDatumLabel() m_ctrl = false; hasHover = false; + + m_isFramed = false; + m_lineWidth = Rez::guiX(0.5); } QVariant QGIDatumLabel::itemChange(GraphicsItemChange change, const QVariant &value) @@ -176,7 +176,9 @@ void QGIDatumLabel::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) QRectF QGIDatumLabel::boundingRect() const { - return childrenBoundingRect(); + QRectF result = childrenBoundingRect(); + result.adjust(-m_lineWidth*4.0, 0.0, 0.0, 0.0); + return result; } void QGIDatumLabel::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) @@ -186,7 +188,17 @@ void QGIDatumLabel::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt QStyleOptionGraphicsItem myOption(*option); myOption.state &= ~QStyle::State_Selected; - //painter->drawRect(boundingRect()); //good for debugging + if (m_isFramed) { + QPen prevPen = painter->pen(); + QPen framePen(prevPen); + + framePen.setWidthF(m_lineWidth); + framePen.setColor(m_dimText->defaultTextColor()); + + painter->setPen(framePen); + painter->drawRect(boundingRect()); + painter->setPen(prevPen); + } } void QGIDatumLabel::setPosFromCenter(const double &xCenter, const double &yCenter) @@ -311,8 +323,7 @@ void QGIDatumLabel::setColor(QColor c) //************************************************************** QGIViewDimension::QGIViewDimension() : hasHover(false), - m_lineWidth(0.0), - m_obtuse(false) + m_lineWidth(0.0) { setHandlesChildEvents(false); setFlag(QGraphicsItem::ItemIsMovable, false); @@ -325,6 +336,7 @@ QGIViewDimension::QGIViewDimension() : addToGroup(datumLabel); datumLabel->setColor(getNormalColor()); datumLabel->setPrettyNormal(); + dimLines = new QGIDimLines(); addToGroup(dimLines); aHead1 = new QGIArrow(); @@ -468,9 +480,8 @@ void QGIViewDimension::updateView(bool update) draw(); } -void QGIViewDimension::updateDim(bool obtuse) +void QGIViewDimension::updateDim() { - (void) obtuse; const auto dim( dynamic_cast(getViewObject()) ); if( dim == nullptr ) { return; @@ -480,7 +491,7 @@ void QGIViewDimension::updateDim(bool obtuse) return; } - QString labelText = QString::fromUtf8(dim->getFormatedValue(m_obtuse).c_str()); + QString labelText = QString::fromUtf8(dim->getFormatedValue().c_str()); QFont font = datumLabel->getFont(); font.setFamily(QString::fromUtf8(vp->Font.getValue())); @@ -491,6 +502,9 @@ void QGIViewDimension::updateDim(bool obtuse) datumLabel->setDimString(labelText); datumLabel->setTolString(); datumLabel->setPosFromCenter(datumLabel->X(),datumLabel->Y()); + + datumLabel->setFramed(dim->TheoreticalExact.getValue()); + datumLabel->setLineWidth(m_lineWidth); } //this is for formatting and finding centers, not display QString QGIViewDimension::getLabelText(void) @@ -531,9 +545,6 @@ void QGIViewDimension::draw() return; } - datumLabel->show(); - show(); - TechDraw::DrawViewDimension *dim = dynamic_cast(getViewObject()); if((!dim) || //nothing to draw, don't try (!dim->isDerivedFrom(TechDraw::DrawViewDimension::getClassTypeId())) || @@ -551,817 +562,48 @@ void QGIViewDimension::draw() } auto vp = static_cast(getViewProvider(getViewObject())); - if ( vp == nullptr ) { + if (vp == nullptr) { + datumLabel->show(); + show(); return; } - m_colNormal = getNormalColor(); - datumLabel->setColor(m_colNormal); - m_lineWidth = Rez::guiX(vp->LineWidth.getValue()); - float margin = Rez::guiX(5.f); - - QString labelText = getLabelText(); - Base::Vector3d lblCenter(datumLabel->X(), datumLabel->Y(), 0); //already Qt gui coords - - const char *dimType = dim->Type.getValueAsString(); + m_colNormal = getNormalColor(); + datumLabel->setColor(m_colNormal); + datumLabel->setRotation(0.0); datumLabel->show(); + + resetArrows(); show(); - if (strcmp(dimType, "Distance") == 0 || - strcmp(dimType, "DistanceX") == 0 || - strcmp(dimType, "DistanceY") == 0) { - Base::Vector3d stdUp(0.0,1.0,0.0); - Base::Vector3d stdLeft(-1.0,0.0,0.0); - pointPair pts = dim->getLinearPoints(); - Base::Vector3d startDist, endDist, midDist; //start/end/mid points of distance line - startDist = Rez::guiX(pts.first); - endDist = Rez::guiX(pts.second); - if (startDist.y < endDist.y) { //always measure bottom to top - Base::Vector3d temp = startDist; - startDist = endDist; - endDist = temp; + if (vp->RenderingExtent.getValue() > ViewProviderDimension::REND_EXTENT_NONE) { + // We are expected to draw something, not just display the value + const char *dimType = dim->Type.getValueAsString(); + + if (strcmp(dimType, "Distance") == 0 + || strcmp(dimType, "DistanceX") == 0 || strcmp(dimType, "DistanceY") == 0) { + drawDistance(dim, vp); } - - Base::Vector3d vecDist = (endDist - startDist); - - // +/- aligned method - // dimension text legible from bottom or right - // text outside arrows (not between) iso convention (asme convention) - // text to left of vertical dims - // text above horizontal dims - Base::Vector3d dirDist, normDist; //direction/normal vectors of distance line - Base::Vector3d dirExt; - Base::Vector3d dirDim, normDim; - Base::Vector3d dirIso; - dirDist = vecDist; - dirDist.Normalize(); - normDist = Base::Vector3d (-dirDist.y,dirDist.x, 0); //normal to distance direction - //toward dimension line??? how to tell? - if (strcmp(dimType, "Distance") == 0 ) { - //distance and dimension lines parallel - dirDim = dirDist; - normDim = normDist; - } else if (strcmp(dimType, "DistanceX") == 0 ) { - //distance and dimension lines not (necessarily) parallel - dirDim = Base::Vector3d ( ((endDist.x - startDist.x >= FLT_EPSILON) ? 1 : -1) , 0, 0); - normDim = Base::Vector3d (-dirDim.y,dirDim.x, 0); - } else if (strcmp(dimType, "DistanceY") == 0 ) { - //distance and dimension lines not (necessarily) parallel - dirDim = Base::Vector3d (0, 1, 0); - normDim = Base::Vector3d (-1, 0, 0); + else if (strcmp(dimType, "Diameter") == 0) { + drawDiameter(dim, vp); } - Base::Vector3d adjustDir = dirDim; //adjust line lengths for arrowheads - - //for ortho drawing extension lines are para to normDim, perp to dirDist - dirExt = normDim; //dirExt is para or anti-parallel to real extension direction - dirIso = normDim; - if (refObj->isIso()) { - //is this dimension an iso dimension? ie points +/-isoX,+/-isoY,+/-isoZ - dirIso = findIsoDir(dirDist); - dirExt = findIsoExt(dirIso); + else if (strcmp(dimType, "Radius") == 0) { + drawRadius(dim, vp); } - dirExt.Normalize(); - - // Get magnitude of angle between dimension line and horizontal - // to determine rotation of dimension text ("aligned" convention. alt is "unidirectional") - //note qt y axis is reversed! and angles are CW! - double textRotAngle = atan2(-dirDim.y,dirDim.x); - if (textRotAngle < 0.0) { - textRotAngle = 2 * M_PI + textRotAngle; //map to +ve textRotAngle + else if (strcmp(dimType, "Angle") == 0 || strcmp(dimType, "Angle3Pt") == 0) { + drawAngle(dim, vp); } - - //orient text right side up - double angleFiddle = M_PI / 10.0; // 18 => 10*, 12 => 15*, 10 => 18*, ... - if ((textRotAngle > M_PI_2 + angleFiddle) && // > 100CW - (textRotAngle <= M_PI)) { // < 180CW -> Q2 - textRotAngle += M_PI; // flip CW - } else if ((textRotAngle > M_PI) && // > 180CW - (textRotAngle <= 1.5*M_PI - angleFiddle)) { // < 260CW -> Q3 - textRotAngle -= M_PI; // flip CCW + else { + Base::Console().Error("QGIVD::draw - this DimensionType is unknown: %s\n", dimType); } - - Base::Vector3d textNorm = normDim; - if (strcmp(dimType, "DistanceX") == 0 ) { - textNorm = stdUp; //always above - } else if (strcmp(dimType, "DistanceY") == 0 ) { - textNorm = stdLeft; //left of dimLine - } else if (std::abs(dirDist.x) < FLT_EPSILON) { //this is horizontal dim line - textNorm = stdLeft; //left of dimLine - } else if (std::abs(dirDist.y) < FLT_EPSILON) { //this is vertical dim line - textNorm = stdLeft; //left of dimLine - } - - // +/- pos of startDist vs endDist for vert/horiz Dims - // distStartDelta sb zero for normal dims - float distStartDelta = vecDist.Dot(normDim); // component of distance vector in dim line direction - Base::Vector3d startDistAdj = startDist + normDim * distStartDelta; - - double textOffset = getDefaultTextVerticalOffset(); - - QPointF qFigure = boundingRect().center(); - Base::Vector3d figureCenter(qFigure.x(),qFigure.y(),0.0); - - QRectF mappedRect = mapRectFromItem(datumLabel, datumLabel->boundingRect()); - lblCenter = Base::Vector3d(mappedRect.center().x(), mappedRect.center().y(), 0.0); - - //find actual extension direction vector - Base::Vector3d startIntercept = DrawUtil::Intersect2d(startDist, dirExt, - lblCenter,dirDim); - Base::Vector3d dirExtActual = (startIntercept - startDist); - dirExtActual.Normalize(); - - midDist = (startDistAdj + endDist) / 2.0; //middle point of distance line - //fauxCenter is where the getDimText() would be if it was on the dimLine - Base::Vector3d fauxCenter; - if (strcmp(dimType, "Distance") == 0 ) { //oblique line - textRotAngle = -textRotAngle; // flip text 180* - fauxCenter = lblCenter + textOffset * dirExtActual; - double slope; - if (DrawUtil::fpCompare(dirDist.x, 0.0)) { - slope = std::numeric_limits::max(); //vertical line - } else { - slope = fabs(dirDist.y / dirDist.x); - } - double deg = atan(slope) * 180.0 / M_PI; - if (deg > (90.0 - (angleFiddle * 180.0/M_PI))) { //mostly vertical +/- 10* - //dim text reads bottom to top on left side of dim line - if (lblCenter.x > fauxCenter.x) { //label is to right of dimline - fauxCenter = lblCenter - textOffset*dirExtActual; //move dim line closer to figure - } - } else { //mostly horizontal - //dim text reads left to right above dim line - if (lblCenter.y > fauxCenter.y) { //label is below dimline - fauxCenter = lblCenter - textOffset*dirExtActual; //move dim line closer to figure - } - } - - } else if (strcmp(dimType, "DistanceX") == 0 ) { - fauxCenter = lblCenter + textOffset * textNorm; - } else if (strcmp(dimType, "DistanceY") == 0 ) { - textRotAngle = - textRotAngle; - fauxCenter = lblCenter - textOffset * textNorm; - } - - //intersection of extension lines and dimension line - startIntercept = DrawUtil::Intersect2d(startDist, dirExt, - fauxCenter,dirDim); - Base::Vector3d endIntercept = DrawUtil::Intersect2d(endDist, dirExt, - fauxCenter,dirDim); - - dirExtActual = (startIntercept - startDist); - dirExtActual.Normalize(); - - margin = Rez::guiX(2.f); - float scaler = 1.; - - Base::Vector3d extStartEnd = startIntercept + dirExtActual * (margin * scaler); - Base::Vector3d extEndEnd = endIntercept + dirExtActual * (margin * scaler); - - //case 1: inner placement: text between extensions & fits. arros point out from inside (default) - //case 2: inner placement2: text too big to fit. arrows point in from outside - //case 3: outer placement: text is outside extensions. arrows point in, 1 arrow tracks getDimText() - - Base::Vector3d dim1Tip = startIntercept; - Base::Vector3d dim1Tail = fauxCenter; - Base::Vector3d dim2Tip = endIntercept; - Base::Vector3d dim2Tail = fauxCenter; - Base::Vector3d a1Dir = -dirDim; - Base::Vector3d a2Dir = dirDim; - if (strcmp(dimType, "DistanceY") == 0 ) { - adjustDir = -adjustDir; - a1Dir = Base::Vector3d(0,1,0); - a2Dir = Base::Vector3d(0,-1,0); - } - - double dimSpan = (extEndEnd - extStartEnd).Length(); //distance between extension lines - double fauxToDim1 = (fauxCenter - dim1Tip).Length(); //label to arrow #1 - double fauxToDim2 = (fauxCenter - dim2Tip).Length(); //label to end #2 - double tailLength = Rez::guiX(10.f) * scaler; - - //case2 - innerPlacement && text > span - double lblWidth = datumLabel->boundingRect().width(); - if ((DrawUtil::isBetween(fauxCenter, dim1Tip, dim2Tip)) && - (lblWidth > dimSpan) ) { - adjustDir = -adjustDir; - if (strcmp(dimType, "DistanceY") == 0 ) { - a1Dir = Base::Vector3d(0,-1,0); - a2Dir = Base::Vector3d(0,1,0); - dim1Tail = dim1Tip + dirDim * tailLength; - dim2Tail = dim2Tip - dirDim * tailLength; - } else { - dim1Tail = dim1Tip - tailLength * dirDim; - dim2Tail = dim2Tip + tailLength * dirDim; - a1Dir = dirDim; - a2Dir = -dirDim; - } - } - - if (!DrawUtil::isBetween(fauxCenter, dim1Tip, dim2Tip)) { - //case3 - outerPlacement - adjustDir = -adjustDir; - if (strcmp(dimType, "DistanceY") == 0 ) { - a1Dir = Base::Vector3d(0,-1,0); - a2Dir = Base::Vector3d(0,1,0); - } else { - a1Dir = dirDim; - a2Dir = -dirDim; - } - - if (fauxToDim1 < fauxToDim2) { - dim1Tail = fauxCenter; - dim2Tail = dim2Tip - tailLength*a2Dir; - } else { - dim1Tail = dim1Tip - tailLength*a1Dir; - dim2Tail = fauxCenter; - } - } - - // Extension lines - QPainterPath path; - Base::Vector3d gap = startDist + dirExtActual * (margin * scaler); //space ext line a bit - path.moveTo(gap.x, gap.y); - path.lineTo(extStartEnd.x, extStartEnd.y); - - gap = endDist + dirExtActual * (margin * scaler); - path.moveTo(gap.x, gap.y); - path.lineTo(extEndEnd.x, extEndEnd.y); - - //Dimension lines (arrow shafts) - adjustDir.Normalize(); - double dimLineAdjust = Rez::guiX(QGIArrow::getOverlapAdjust(QGIArrow::getPrefArrowStyle(), - QGIArrow::getPrefArrowSize())); - Base::Vector3d adjustedTip = dim1Tip + adjustDir * dimLineAdjust; - path.moveTo(adjustedTip.x, adjustedTip.y); - path.lineTo(dim1Tail.x, dim1Tail.y); - - adjustedTip = dim2Tip - adjustDir * dimLineAdjust; - path.moveTo(adjustedTip.x, adjustedTip.y); - path.lineTo(dim2Tail.x, dim2Tail.y); - - dimLines->setPath(path); - - // Note Bounding Box size is not the same width or height as text (only used for finding center) - double bbX = datumLabel->boundingRect().width(); - double bbY = datumLabel->boundingRect().height(); - datumLabel->setTransformOriginPoint(bbX / 2, bbY /2); - double angleOption = 0.0; //put lblText angle adjustments here - datumLabel->setRotation((textRotAngle * 180 / M_PI) + angleOption); - if (strcmp(dimType, "DistanceY") == 0 ) { - datumLabel->setRotation(-90.0 + angleOption); - } - - aHead1->setDirMode(true); - aHead2->setDirMode(true); - - if (vp->FlipArrowheads.getValue()) { - aHead1->setDirection(a1Dir * -1.0); - aHead2->setDirection(a2Dir * -1.0); - } else { - aHead1->setDirection(a1Dir); - aHead2->setDirection(a2Dir); - } - - aHead1->setStyle(QGIArrow::getPrefArrowStyle()); - aHead1->setSize(QGIArrow::getPrefArrowSize()); - aHead1->draw(); - aHead2->setStyle(QGIArrow::getPrefArrowStyle()); - aHead2->setSize(QGIArrow::getPrefArrowSize()); - aHead2->draw(); - - aHead1->setPos(dim1Tip.x, dim1Tip.y); - aHead2->setPos(dim2Tip.x, dim2Tip.y); - - aHead1->setDirMode(false); - aHead2->setDirMode(false); - - } else if(strcmp(dimType, "Diameter") == 0) { - // terminology: Dimension Text, Dimension Line(s), Extension Lines, Arrowheads - Base::Vector3d arrow1Tip, arrow2Tip; - Base::Vector3d arrow1Tail, arrow2Tail; - Base::Vector3d arrow1Dir,arrow2Dir; - double radius; - Base::Vector3d pointOnCurve,curveCenter; - Base::Vector3d startExt1,endExt1,startExt2,endExt2; - - arcPoints pts = dim->getArcPoints(); - bool isArc = pts.isArc; - radius = Rez::guiX(pts.radius); - curveCenter = Rez::guiX(pts.center); - pointOnCurve = Rez::guiX(pts.onCurve.first); - - Base::Vector3d startDist,endDist,dirDim; - startDist = Rez::guiX(pts.onCurve.first); - endDist = Rez::guiX(pts.onCurve.second); - dirDim = endDist - startDist; - if (fabs(dirDim.Length()) < Precision::Confusion()) { - dirDim = Base::Vector3d(1.0,0.0,0.0); - } - dirDim.Normalize(); - Base::Vector3d adjustDir = dirDim; //adjust line lengths for arrowheads - Base::Vector3d fauxCenter = lblCenter; - - //default arrow endpoints - double dimLineAdjust = Rez::guiX(QGIArrow::getOverlapAdjust(QGIArrow::getPrefArrowStyle(), - QGIArrow::getPrefArrowSize())); - arrow1Tip = curveCenter - dirDim * radius; - Base::Vector3d adjustedTip1 = arrow1Tip - adjustDir * dimLineAdjust; - arrow2Tip = curveCenter + dirDim * radius; - Base::Vector3d adjustedTip2 = arrow2Tip + adjustDir * dimLineAdjust; - arrow1Tail = curveCenter; - arrow2Tail = curveCenter; - - double gapMargin = Rez::guiX(4.f); - margin = Rez::guiX(2.f); - float scaler = 1.; - double tip = (margin * scaler); - double gap = (gapMargin * scaler); //sb % of radius? - - //offset of dimLine from getDimText() - double horizOffset = getDefaultTextHorizontalOffset(lblCenter.x > curveCenter.x ? -1.0 : +1.0); - double vertOffset = getDefaultTextVerticalOffset(); - - bool outerPlacement = false; - if ((lblCenter-curveCenter).Length() > radius) { //label is outside circle - outerPlacement = true; - } - -// // Note Bounding Box size is not the same width or height as text (only used for finding center) - float bbX = datumLabel->boundingRect().width(); - float bbY = datumLabel->boundingRect().height(); - datumLabel->setTransformOriginPoint(bbX / 2, bbY /2); - - int posMode = NoSnap; - bool isLeader = false; - QPainterPath path; - - if(outerPlacement) { - Base::Vector3d v = (lblCenter - curveCenter); - double angle = atan2(v.y, v.x); - double tolerance = 15.0; //deg - - tolerance *= M_PI / 180; - if( (angle > -tolerance && angle < tolerance) || //angle = 0 or 180 (+/- 15) - (angle > (M_PI - tolerance) || angle < (-M_PI + tolerance)) ) { //dim line is Horizontal - posMode = HorizontalSnap; - } else if( (angle < ( M_PI / 2. + tolerance) && angle > ( M_PI / 2. - tolerance)) || //angle 90/270 - (angle < (-M_PI / 2. + tolerance) && angle > (-M_PI / 2. - tolerance)) ) { //Vertical - posMode = VerticalSnap; - } - -//NOTE: VerticalSnap and Horizontal snap are confusing names. -// VerticalSnap => dim line is horizontal, dim text is directly above/below center -// HorizontalSnap => dim line is vertical, dim text is directly left/right of center. - - if(posMode == VerticalSnap) { - QRectF mappedRect = mapRectFromItem(datumLabel, datumLabel->boundingRect()); - lblCenter = Base::Vector3d(mappedRect.center().x(), mappedRect.center().y(), 0.0); - if (lblCenter.y > curveCenter.y) { - fauxCenter = Base::Vector3d(lblCenter.x,lblCenter.y + vertOffset,lblCenter.z); - } else { - fauxCenter = Base::Vector3d(lblCenter.x,lblCenter.y + vertOffset,lblCenter.z); - tip = -tip; - gap = -gap; - } - - arrow1Tip.x = curveCenter.x - radius; //to left, on circle cl - arrow1Tip.y = fauxCenter.y; - arrow1Tail.x = curveCenter.x; - arrow1Tail.y = arrow1Tip.y; - arrow1Dir = (arrow1Tip - arrow1Tail).Normalize(); - - adjustedTip1.x = arrow1Tip.x + dimLineAdjust; - adjustedTip1.y = arrow1Tip.y; - path.moveTo(arrow1Tail.x, arrow1Tail.y); - path.lineTo(adjustedTip1.x, adjustedTip1.y); - - arrow2Tip.x = curveCenter.x + radius; - arrow2Tip.y = fauxCenter.y; - arrow2Tail.x = curveCenter.x; - arrow2Tail.y = arrow2Tip.y; - arrow2Dir = (arrow2Tip - arrow2Tail).Normalize(); - - adjustedTip2.x = arrow2Tip.x - dimLineAdjust; - adjustedTip2.y = arrow2Tip.y; - path.moveTo(arrow2Tail.x, arrow2Tail.y); - path.lineTo(adjustedTip2.x, adjustedTip2.y); - - startExt1.x = curveCenter.x - radius; - startExt1.y = curveCenter.y + gap; - endExt1.x = startExt1.x; - endExt1.y = fauxCenter.y + tip; - - startExt2.x = curveCenter.x + radius; - startExt2.y = curveCenter.y + gap; - endExt2.x = startExt2.x; - endExt2.y = fauxCenter.y + tip; - - path.moveTo(startExt1.x, startExt1.y); - path.lineTo(endExt1.x, endExt1.y); - - path.moveTo(startExt2.x, startExt2.y); - path.lineTo(endExt2.x, endExt2.y); - - datumLabel->setRotation(0.0); - - } else if(posMode == HorizontalSnap) { - QRectF mappedRect = mapRectFromItem(datumLabel, datumLabel->boundingRect()); - lblCenter = Base::Vector3d(mappedRect.center().x(), mappedRect.center().y(), 0.0); - - if (lblCenter.x > curveCenter.x) { //label right -// fauxCenter = Base::Vector3d(lblCenter.x - horizOffset,lblCenter.y,lblCenter.z); //uniform convention - fauxCenter = Base::Vector3d(lblCenter.x + vertOffset,lblCenter.y,lblCenter.z); //aligned convention - } else { //label left - tip = -tip; - gap = -gap; -// fauxCenter = Base::Vector3d(lblCenter.x + horizOffset,lblCenter.y,lblCenter.z); //uniform - fauxCenter = Base::Vector3d(lblCenter.x + vertOffset,lblCenter.y,lblCenter.z); //aligned - } - - arrow1Tip.x = fauxCenter.x; - arrow1Tip.y = curveCenter.y - radius; - arrow1Tail.x = arrow1Tip.x; - arrow1Tail.y = curveCenter.y; - arrow1Dir = (arrow1Tip - arrow1Tail).Normalize(); - - adjustedTip1.x = arrow1Tip.x; - adjustedTip1.y = arrow1Tip.y + dimLineAdjust; - path.moveTo(arrow1Tail.x, arrow1Tail.y); - path.lineTo(adjustedTip1.x, adjustedTip1.y); - - arrow2Tip.x = fauxCenter.x; - arrow2Tip.y = curveCenter.y + radius; - arrow2Tail.x = arrow2Tip.x; - arrow2Tail.y = curveCenter.y; - arrow2Dir = (arrow2Tip - arrow2Tail).Normalize(); - - adjustedTip2.x = arrow2Tip.x; - adjustedTip2.y = arrow2Tip.y - dimLineAdjust; - path.moveTo(arrow2Tail.x, arrow2Tail.y); - path.lineTo(adjustedTip2.x, adjustedTip2.y); - - startExt1.x = curveCenter.x + gap; - startExt1.y = curveCenter.y - radius; - endExt1.x = fauxCenter.x + tip; - endExt1.y = startExt1.y; - - startExt2.x = curveCenter.x + gap; - startExt2.y = curveCenter.y + radius; - endExt2.x = fauxCenter.x + tip; - endExt2.y = startExt2.y; - - path.moveTo(startExt1.x, startExt1.y); - path.lineTo(endExt1.x, endExt1.y); - - path.moveTo(startExt2.x, startExt2.y); - path.lineTo(endExt2.x, endExt2.y); - - datumLabel->setRotation(-90.0); //aligned convention -// datumLabel->setRotation(0.0); //unidirectional convention - - } else { //outer placement, NoSnap (leader type) - QRectF mappedRect = mapRectFromItem(datumLabel, datumLabel->boundingRect()); - lblCenter = Base::Vector3d(mappedRect.center().x(), mappedRect.center().y(), 0.0); - isLeader = true; - - arrow1Tail = lblCenter; - arrow1Tail.x += horizOffset; - - Base::Vector3d kinkPoint = arrow1Tail; - kinkPoint.x += (lblCenter.x < curveCenter.x) ? margin : - margin; - - arrow1Tip = curveCenter + (kinkPoint - curveCenter).Normalize() * radius; - adjustedTip1 = arrow1Tip + (kinkPoint - curveCenter).Normalize() * dimLineAdjust; - path.moveTo(arrow1Tail.x, arrow1Tail.y); - path.lineTo(kinkPoint.x, kinkPoint.y); - path.lineTo(adjustedTip1.x, adjustedTip1.y); - - arrow1Dir = (arrow1Tip - kinkPoint).Normalize(); - datumLabel->setRotation(0.); - } - } else { //NOT outerplacement ie dimLines are inside circle - double vertOffset = getDefaultTextVerticalOffset(); - - Base::Vector3d dirLabel = lblCenter - curveCenter; - if (dirLabel.Length() < Precision::Confusion()) { - dirLabel = Base::Vector3d(-1.0, 0.0, 0.0); - } - - double labelAngle = 0.0; - if (vertOffset < dirLabel.Length()) { - double lineShiftAngle = asin(vertOffset/dirLabel.Length()); - labelAngle = atan2(dirLabel.y, dirLabel.x); - - if (labelAngle > 0.25*M_PI) { - labelAngle -= M_PI + lineShiftAngle; - } - else if (labelAngle < -0.75*M_PI) { - labelAngle += M_PI - lineShiftAngle; - } - else { - labelAngle += lineShiftAngle; - } - } - - // Set the angle of the dimension text - datumLabel->setRotation(labelAngle*180/M_PI); - dirDim = Base::Vector3d(cos(labelAngle), sin(labelAngle), 0.0); - - arrow1Tip = curveCenter - dirDim * radius; - adjustedTip1 = arrow1Tip + dirDim * dimLineAdjust; - arrow1Tail = curveCenter; - arrow1Dir = (arrow1Tip - arrow1Tail).Normalize(); - - arrow2Tip = curveCenter + dirDim * radius; - adjustedTip2 = arrow2Tip - dirDim * dimLineAdjust; - arrow2Tail = curveCenter; - arrow2Dir = (arrow2Tip - arrow2Tail).Normalize(); - - path.moveTo(arrow1Tail.x, arrow1Tail.y); - path.lineTo(adjustedTip1.x, adjustedTip1.y); - - path.moveTo(arrow2Tail.x, arrow2Tail.y); - path.lineTo(adjustedTip2.x, adjustedTip2.y); - } - - aHead1->setStyle(QGIArrow::getPrefArrowStyle()); - aHead1->setSize(QGIArrow::getPrefArrowSize()); - - if (!isLeader) { - aHead2->setStyle(QGIArrow::getPrefArrowStyle()); - aHead2->setSize(QGIArrow::getPrefArrowSize()); - } - - //handle partial arc weird cases - //TODO: does anybody dimension Arcs with Diameter? doesn't Radius make more sense? - Base::Vector3d dLineStart; - Base::Vector3d kinkPoint; - margin = Rez::guiX(5.f); - double kinkLength = Rez::guiX(5.0); //sb % of horizontal dist(lblCenter,curveCenter)??? - QPainterPath arcPath; - if (isArc) { - arrow1Tail = lblCenter; - arrow1Tail.x += horizOffset; - Base::Vector3d kinkPoint = arrow1Tail; - kinkPoint.x += (lblCenter.x < curveCenter.x) ? margin : - margin; - if (lblCenter.x > curveCenter.x) { // label to right of vert c/l - dLineStart = Base::Vector3d(lblCenter.x + horizOffset,lblCenter.y,lblCenter.z); - kinkPoint = Base::Vector3d(dLineStart.x - kinkLength,dLineStart.y,dLineStart.z); - } else { - tip = -tip; - gap = -gap; - dLineStart = Base::Vector3d(lblCenter.x + horizOffset,lblCenter.y,lblCenter.z); - kinkPoint = Base::Vector3d(dLineStart.x + kinkLength,dLineStart.y,dLineStart.z); - } - dirDim = (kinkPoint - curveCenter).Normalize(); - pointOnCurve = curveCenter + (dirDim * radius); - pointOnCurve = Rez::guiX(pts.midArc); - adjustedTip1 = pointOnCurve + (dirDim * dimLineAdjust); - arcPath.moveTo(dLineStart.x,dLineStart.y); - arcPath.lineTo(kinkPoint.x,kinkPoint.y); - arcPath.lineTo(adjustedTip1.x,adjustedTip1.y); - arrow1Dir = (arrow1Tip - kinkPoint).Normalize(); - datumLabel->setRotation(0.); - } - dimLines->setPath(path); - - if (isArc) { - dimLines->setPath(arcPath); - aHead1->setPos(pointOnCurve.x, pointOnCurve.y); - aHead1->setDirMode(true); - aHead1->setDirection(arrow1Dir); - aHead1->draw(); - aHead1->show(); - aHead2->hide(); - } else if(outerPlacement) { - if(posMode > NoSnap) { // Horizontal or Vertical snap - aHead1->setPos(arrow1Tip.x, arrow1Tip.y); - aHead1->setDirMode(true); - aHead1->setDirection(arrow1Dir); - aHead1->draw(); - aHead1->show(); - aHead2->setPos(arrow2Tip.x, arrow2Tip.y); - aHead2->setDirMode(true); - aHead2->setDirection(arrow2Dir); - aHead2->draw(); - aHead2->show(); - } else { //leader stye - aHead1->setPos(arrow1Tip.x, arrow1Tip.y); - aHead1->setDirMode(true); - aHead1->setDirection(arrow1Dir); - aHead1->draw(); - aHead1->show(); - aHead2->hide(); //only 1 arrowhead for NoSnap + outerplacement (ie a leader) - } - } else { //inner placement - aHead1->setPos(arrow1Tip.x, arrow1Tip.y); - aHead1->setDirMode(true); - aHead1->setDirection(arrow1Dir); - aHead1->draw(); - aHead1->show(); - aHead2->setPos(arrow2Tip.x, arrow2Tip.y); - aHead2->setDirMode(true); - aHead2->setDirection(arrow2Dir); - aHead2->draw(); - aHead2->show(); - } - -// code for centerMark being attribute of Dim instead of View -// if (dim->CentreLines.getValue()) { -// curveCenterMark->setPos(curveCenter.x,curveCenter.y); -// centerMark->show(); -// dim->getViewPart()->addVertex(curveCenter,true); -// } - } else if(strcmp(dimType, "Radius") == 0) { - drawRadius(dim, vp); - } else if( (strcmp(dimType, "Angle") == 0) || - (strcmp(dimType, "Angle3Pt") == 0)) { - anglePoints pts = dim->getAnglePoints(); - Base::Vector3d X(1.0,0.0,0.0); - Base::Vector3d vertex = Rez::guiX(pts.vertex); - Base::Vector3d legEnd0 = Rez::guiX(pts.ends.first); - Base::Vector3d legEnd1 = Rez::guiX(pts.ends.second); - Base::Vector3d dir0 = legEnd0 - vertex; - Base::Vector3d dir1 = legEnd1 - vertex; - Base::Vector3d d0 = dir0; - d0.Normalize(); - Base::Vector3d d1 = dir1; - d1.Normalize(); - Base::Vector3d leg0 = dir0; - Base::Vector3d leg1 = dir1; - // Qt y coordinates are flipped - dir0.y *= -1.; - dir1.y *= -1.; - - double insideAngle = dir0.GetAngle(dir1); // [0,PI] - double outsideAngle = 2*M_PI - insideAngle; // [PI,2PI] - Base::Vector3d tempCross = d0.Cross(d1); - double insideDir = tempCross.z; - bool ccwInner = true; - if (insideDir > 0.0) { - ccwInner = false; - } - - //TODO: figure out the math for adjusting the arc so the tip doesn't overlap the arrowhead. -// double dimLineAdjust = Rez::guiX(QGIArrow::getOverlapAdjust(QGIArrow::getPrefArrowStyle(), -// QGIArrow::getPrefArrowSize())); - QRectF mappedRect = mapRectFromItem(datumLabel, datumLabel->boundingRect()); - lblCenter = Base::Vector3d(mappedRect.center().x(), mappedRect.center().y(), 0.0); - - Base::Vector3d labelVec = (lblCenter - vertex); //dir from label to vertex - - double textOffset = getDefaultTextVerticalOffset(); - double radius = labelVec.Length() - textOffset; - - QRectF arcRect(vertex.x - radius, vertex.y - radius, 2. * radius, 2. * radius); - Base::Vector3d ar0Pos = vertex + d0 * radius; -// Base::Vector3d adjustedTip0 = ar0Pos - d0 * dimLineAdjust; - Base::Vector3d ar1Pos = vertex + d1 * radius; -// Base::Vector3d adjustedTip1 = ar1Pos - d1 * dimLineAdjust; - - double startangle = atan2(dir0.y,dir0.x); - if (startangle < 0) { - startangle += 2.0 * M_PI; - } - double endangle = atan2(dir1.y,dir1.x); - if (endangle < 0) { - endangle += 2.0 * M_PI; - } - - Base::Vector3d startExt0 = legEnd0; - Base::Vector3d startExt1 = legEnd1; - // add an offset from the ends - double offsetFudge = 5.0; - startExt0 += d0 * offsetFudge; - startExt1 += d1 * offsetFudge; - - // Draw the path - QPainterPath path; - - // Only draw extension lines if outside arc - double extFudge = 5.0; - if(radius > (startExt0-vertex).Length()) { - path.moveTo(startExt0.x, startExt0.y); - Base::Vector3d endExt0 = ar0Pos + d0*Rez::guiX(extFudge); - path.lineTo(endExt0.x, endExt0.y); - } - - if(radius > (startExt1-vertex).Length()) { - path.moveTo(startExt1.x, startExt1.y); - Base::Vector3d endExt1 = ar1Pos + d1*Rez::guiX(extFudge); - path.lineTo(endExt1.x, endExt1.y); - } - - // Treat zero as positive to be consistent for horizontal lines - if(std::abs(startangle) < FLT_EPSILON) - startangle = 0; - - if(std::abs(endangle) < FLT_EPSILON) - endangle = 0; - - //https://stackoverflow.com/questions/13640931/how-to-determine-if-a-vector-is-between-two-other-vectors - bool isOutside = true; - if ( ((d0.Cross(labelVec)).Dot(d0.Cross(d1)) >= 0.0) && - ((d1.Cross(labelVec)).Dot(d1.Cross(d0)) >= 0.0) ) { - isOutside = false; - } - - //dim line (arc) calculation is very different here. - //TODO: make arc a bit shorter to not overlap arrowheads. ends +/- x degrees. - path.arcMoveTo(arcRect, startangle * 180 / M_PI); - double actualSweep = 0.0; - m_obtuse = false; - if(isOutside) { - m_obtuse = true; - if (ccwInner) { //inner is ccw so outer is cw and sweep is -ve - actualSweep = -outsideAngle; - } else { //inner is cw so outer is ccw and sweep is +ve - actualSweep = outsideAngle; - } - } else { - if (ccwInner) { //inner is ccw and sweep is +ve - actualSweep = insideAngle; - } else { //inner is cw and sweep is -ve - actualSweep = -insideAngle; - } - } - path.arcTo(arcRect, startangle * 180 / M_PI, actualSweep*180.0/M_PI); - - dimLines->setPath(path); - - //NOTE: arrowheads are dirMode(false) - aHead1->setFlipped(true); - aHead1->setStyle(QGIArrow::getPrefArrowStyle()); - aHead1->setSize(QGIArrow::getPrefArrowSize()); - aHead1->draw(); - aHead2->setStyle(QGIArrow::getPrefArrowStyle()); - aHead2->setSize(QGIArrow::getPrefArrowSize()); - aHead2->draw(); - - aHead1->setPos(ar0Pos.x,ar0Pos.y ); - aHead2->setPos(ar1Pos.x,ar1Pos.y ); - - Base::Vector3d norm1 = leg0; - Base::Vector3d norm2 = leg1; - Base::Vector3d avg = (norm1 + norm2) / 2.; //midline of legs - - norm1 = norm1.ProjectToLine(avg, norm1); - norm2 = norm2.ProjectToLine(avg, norm2); - - float ar0angle = atan2(-norm1.y, -norm1.x) * 180 / M_PI; - float ar1angle = atan2(norm2.y, norm2.x) * 180 / M_PI; - - if(isOutside) { - aHead1->setRotation(ar0angle + 180.); - aHead2->setRotation(ar1angle + 180.); - } else { - aHead1->setRotation(ar0angle); - aHead2->setRotation(ar1angle); - } - - // Set the angle of the dimension text - Base::Vector3d labelNorm(-labelVec.y, labelVec.x, 0.); - double lAngle = atan2(labelNorm.y, labelNorm.x); - - if (lAngle < 0.0) { - lAngle = 2 * M_PI + lAngle; //map to +ve lAngle - } - - //orient text right side up - double angleFiddle = M_PI / 10.0; // 18 => 10*, 12 => 15*, 10 => 18*, ... - if ((lAngle > M_PI_2 + angleFiddle) && // > 100CW - (lAngle <= M_PI)) { // < 180CW -> Q2 - lAngle += M_PI; // flip CW - } else if ((lAngle > M_PI) && // > 180CW - (lAngle <= 1.5*M_PI - angleFiddle)) { // < 260CW -> Q3 - lAngle -= M_PI; // flip CCW - } - - -// //if label is more/less vertical, make it vertical -// if (lAngle > M_PI_2+M_PI/12) { // label norm angle > 90 + 15 = 105 -// lAngle -= M_PI; // lAngle - 180 Flip -// } else if (lAngle <= -M_PI_2+M_PI/12) { // < -90 + 15 = - 85 -// lAngle += M_PI; // langle + 180 Flip -// } - - float bbX = datumLabel->boundingRect().width(); - float bbY = datumLabel->boundingRect().height(); - datumLabel->setTransformOriginPoint(bbX / 2., bbY /2.); - datumLabel->setRotation(lAngle * 180 / M_PI); - - } //endif Distance/Diameter/Radius/Angle - -//this is already handled in select() and hover() -// // redraw the Dimension and the parent View -// if (datumLabel->hasHover && !datumLabel->isSelected()) { -// setPrettyPre(); -// } else if (datumLabel->isSelected()) { -// setPrettySel(); -// } else { -// setPrettyNormal(); -// } + } + else { + // No dimension lines are drawn, the arrows are hidden + dimLines->setPath(QPainterPath()); + drawArrows(0, nullptr, nullptr, false); + } update(); if (parentItem()) { @@ -1372,28 +614,136 @@ void QGIViewDimension::draw() } } +double QGIViewDimension::getAnglePlacementFactor(double testAngle, double endAngle, double startRotation) +{ + if (startRotation > 0.0) { + startRotation = -startRotation; + endAngle -= startRotation; + if (endAngle > M_PI) { + endAngle -= M_2PI; + } + } + + if (testAngle > endAngle) { + testAngle -= M_2PI; + } + + if (testAngle >= endAngle + startRotation) { + return +1.0; + } + + testAngle += M_PI; + if (testAngle > endAngle) { + testAngle -= M_2PI; + } + + if (testAngle >= endAngle + startRotation) { + return -1.0; + } + + return 0.0; +} + +int QGIViewDimension::compareAngleStraightness(double straightAngle, double leftAngle, double rightAngle, + double leftStrikeFactor, double rightStrikeFactor) +{ + double leftDelta = DrawUtil::angleComposition(M_PI, straightAngle - leftAngle); + double rightDelta = DrawUtil::angleComposition(rightAngle, -straightAngle); + + if (fabs(leftDelta - rightDelta) <= Precision::Confusion()) { + return 0; + } + + if (leftStrikeFactor == rightStrikeFactor) { + double leftBend = fabs(leftDelta); + double rightBend = fabs(rightDelta); + + return DrawUtil::sgn(leftBend - rightBend); + } + + return rightStrikeFactor > leftStrikeFactor ? -1 : +1; +} + double QGIViewDimension::getIsoStandardLinePlacement(double labelAngle) { // According to ISO 129-1 Standard Figure 23, the bordering angle is 2/3 PI, resp. -1/3 PI - // As Qt Y axis points downwards, all signs are flipped - return labelAngle > +M_PI/3.0 || labelAngle < -2.0*M_PI/3.0 - ? -1.0 : +1.0; + return labelAngle < -M_PI/3.0 || labelAngle > +2.0*M_PI/3.0 + ? +1.0 : -1.0; } -double QGIViewDimension::computeLineAndLabelAngles(Base::Vector2d lineTarget, Base::Vector2d labelCenter, - double lineLabelDistance, double &lineAngle, double &labelAngle) +Base::Vector2d QGIViewDimension::getIsoRefOutsetPoint(const Base::BoundBox2d &labelRectangle, bool right) const +{ + return Base::Vector2d(right ? labelRectangle.MinX - getDefaultIsoReferenceLineOverhang() + : labelRectangle.MaxX + getDefaultIsoReferenceLineOverhang(), + labelRectangle.MinY - getDefaultIsoDimensionLineSpacing()); +} + +Base::Vector2d QGIViewDimension::getIsoRefJointPoint(const Base::BoundBox2d &labelRectangle, bool right) const +{ + return getIsoRefOutsetPoint(labelRectangle, !right); +} + +Base::Vector2d QGIViewDimension::getAsmeRefOutsetPoint(const Base::BoundBox2d &labelRectangle, bool right) const +{ + return Base::Vector2d(right ? labelRectangle.MaxX : labelRectangle.MinX, + labelRectangle.GetCenter().y); +} + +Base::Vector2d QGIViewDimension::getAsmeRefJointPoint(const Base::BoundBox2d &labelRectangle, bool right) const +{ + return Base::Vector2d(right ? labelRectangle.MaxX + getDefaultAsmeHorizontalLeaderLength() + : labelRectangle.MinX - getDefaultAsmeHorizontalLeaderLength(), + labelRectangle.GetCenter().y); +} + +Base::Vector2d QGIViewDimension::computePerpendicularIntersection(const Base::Vector2d &linePoint, + const Base::Vector2d &perpendicularPoint, double lineAngle) +{ + return linePoint + + Base::Vector2d::FromPolar((perpendicularPoint - linePoint)*Base::Vector2d::FromPolar(1.0, lineAngle), + lineAngle); +} + +Base::Vector2d QGIViewDimension::computeExtensionLinePoints(const Base::Vector2d &originPoint, + const Base::Vector2d &linePoint, double hintAngle, double overhangSize, + double gapSize, Base::Vector2d &startPoint) +{ + Base::Vector2d direction(linePoint - originPoint); + double rawLength = direction.Length(); + + if (rawLength <= Precision::Confusion()) { + direction = Base::Vector2d::FromPolar(1.0, hintAngle); + } + else { + direction = direction/rawLength; + } + + if (overhangSize > rawLength - gapSize) { + // The extension line would be smaller than extension line overhang, keep it at least so long + startPoint = linePoint - overhangSize*direction; + } + else { + startPoint = linePoint - (rawLength - gapSize)*direction; + } + + return linePoint + overhangSize*direction; +} + +double QGIViewDimension::computeLineAndLabelAngles(const Base::Vector2d &rotationCenter, + const Base::Vector2d &labelCenter, double lineLabelDistance, + double &lineAngle, double &labelAngle) { // By default horizontal line and no label rotation lineAngle = 0.0; labelAngle = 0.0; - Base::Vector2d rawDirection(labelCenter - lineTarget); + Base::Vector2d rawDirection(labelCenter - rotationCenter); double rawDistance = rawDirection.Length(); - if (rawDistance < Precision::Confusion()) { // Almost single point, can't tell + if (rawDistance <= Precision::Confusion()) { // Almost single point, can't tell return 0.0; } - double rawAngle = atan2(rawDirection.y, rawDirection.x); + double rawAngle = rawDirection.Angle(); lineAngle = rawAngle; // If we are too close to the line origin, no further adjustments @@ -1403,410 +753,1172 @@ double QGIViewDimension::computeLineAndLabelAngles(Base::Vector2d lineTarget, Ba // Rotate the line by angle between the label rectangle center and label bottom side center double devAngle = getIsoStandardLinePlacement(rawAngle)*asin(lineLabelDistance/rawDistance); - lineAngle = addAngles(lineAngle, devAngle); + lineAngle = DrawUtil::angleComposition(lineAngle, devAngle); - labelAngle = devAngle > 0.0 ? lineAngle : addAngles(lineAngle, M_PI); + labelAngle = devAngle < 0.0 ? lineAngle : DrawUtil::angleComposition(lineAngle, M_PI); return devAngle; } -bool QGIViewDimension::computeLineRectangleExitPoint(const QRectF &rectangle, Base::Vector2d targetPoint, - Base::Vector2d &exitPoint) { - if (targetPoint.x > rectangle.left() && targetPoint.x < rectangle.right() - && targetPoint.y > rectangle.top() && targetPoint.y < rectangle.bottom()) { - // Target point is inside the rectangle - no crossing at all +double QGIViewDimension::computeLineStrikeFactor(const Base::BoundBox2d &labelRectangle, + const Base::Vector2d &lineOrigin, double lineAngle, + const std::vector> &drawMarking) +{ + if (drawMarking.size() < 2) { + return 0.0; + } + + std::vector intersectionPoints; + unsigned int startIndex = 0; + unsigned int currentIndex = 1; + + while (currentIndex < drawMarking.size()) { + if (drawMarking[currentIndex].second != drawMarking[startIndex].second) { + if (drawMarking[startIndex].second) { + double segmentBase = drawMarking[startIndex].first; + double segmentLength = drawMarking[currentIndex].first - segmentBase; + + DrawUtil::findLineSegmentRectangleIntersections(lineOrigin, lineAngle, segmentBase, segmentLength, + labelRectangle, intersectionPoints); + } + + startIndex = currentIndex; + } + + ++currentIndex; + } + + return intersectionPoints.size() >= 2 ? 1.0 : 0.0; +} + +double QGIViewDimension::computeArcStrikeFactor(const Base::BoundBox2d &labelRectangle, + const Base::Vector2d &arcCenter, double arcRadius, + const std::vector> &drawMarking) +{ + if (drawMarking.size() < 1) { + return 0.0; + } + + unsigned int entryIndex = 0; + while (entryIndex < drawMarking.size() && drawMarking[entryIndex].second) { + ++entryIndex; + } + + std::vector intersectionPoints; + + if (entryIndex >= drawMarking.size()) { + DrawUtil::findCircleRectangleIntersections(arcCenter, arcRadius, labelRectangle, intersectionPoints); + } + else { + unsigned int startIndex = entryIndex; + unsigned int currentIndex = entryIndex; + do { + ++currentIndex; + currentIndex %= drawMarking.size(); + + if (drawMarking[currentIndex].second != drawMarking[startIndex].second) { + if (drawMarking[startIndex].second) { + double arcAngle = drawMarking[startIndex].first; + double arcRotation = drawMarking[currentIndex].first - arcAngle; + if (arcRotation < 0.0) { + arcRotation += M_2PI; + } + + DrawUtil::findCircularArcRectangleIntersections(arcCenter, arcRadius, arcAngle, arcRotation, + labelRectangle, intersectionPoints); + } + + startIndex = currentIndex; + } + } + while (currentIndex != entryIndex); + } + + return intersectionPoints.size() >= 2 ? 1.0 : 0.0; +} + +double QGIViewDimension::normalizeStartPosition(double &startPosition, double &lineAngle) +{ + if (startPosition > 0.0) { + startPosition = -startPosition; + lineAngle += M_PI; + return -1.0; + } + + return +1.0; +} + +double QGIViewDimension::normalizeStartRotation(double &startRotation) +{ + if (copysign(1.0, startRotation) > 0.0) { + startRotation = -startRotation; + return -1.0; + } + + return 1.0; +} + +bool QGIViewDimension::constructDimensionLine(const Base::Vector2d &targetPoint, double lineAngle, + double startPosition, double jointPosition, const Base::BoundBox2d &labelRectangle, + int arrowCount, int standardStyle, bool flipArrows, + std::vector> &outputMarking) const +{ + // The start position > 0 is not expected, the caller must handle this + if (startPosition > 0.0) { + Base::Console().Error("QGIVD::constructDimLine - Start Position must not be positive! Received: %f\n", + startPosition); return false; } - Base::Vector2d lineOrigin(rectangle.center().x(), rectangle.center().y()); - Base::Vector2d direction = targetPoint - lineOrigin; + double labelBorder = 0.0; + if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) { + labelBorder = labelRectangle.Width()*0.5 + getDefaultIsoReferenceLineOverhang(); + } + else if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) { + std::vector intersectionPoints; + DrawUtil::findLineRectangleIntersections(targetPoint, lineAngle, labelRectangle, intersectionPoints); - if (fabs(direction.y) >= Precision::Confusion()) { - // The line is not parallel with X axis - exitPoint.y = direction.y < 0 ? rectangle.top() : rectangle.bottom(); - exitPoint.x = lineOrigin.x + direction.x*(exitPoint.y - lineOrigin.y)/direction.y; - - if (exitPoint.x >= rectangle.left() && exitPoint.x <= rectangle.right()) { - return true; + if (intersectionPoints.size() >= 2) { + labelBorder = (intersectionPoints[0] - labelRectangle.GetCenter()).Length(); } } - if (fabs(direction.x) >= Precision::Confusion()) { - // The line is not parallel with Y axis - exitPoint.x = direction.x < 0 ? rectangle.left() : rectangle.right(); - exitPoint.y = lineOrigin.y + direction.y*(exitPoint.x - lineOrigin.x)/direction.x; + bool autoFlipArrows = false; + if (jointPosition + labelBorder > 0.0) { + // If label sticks out, extend the dimension line beyond the end point (0.0) + DrawUtil::intervalMarkLinear(outputMarking, 0.0, jointPosition + labelBorder, true); + autoFlipArrows = true; + } - if (exitPoint.y >= rectangle.top() && exitPoint.y <= rectangle.bottom()) { - return true; + if (jointPosition - labelBorder < startPosition) { + DrawUtil::intervalMarkLinear(outputMarking, startPosition, + jointPosition - labelBorder - startPosition, true); + + // For only one arrow and zero width line skip flipping, it already points correctly + if (arrowCount > 1 || startPosition < 0.0) { + autoFlipArrows = true; } } - return false; + flipArrows ^= autoFlipArrows; + if (!flipArrows + || (standardStyle != ViewProviderDimension::STD_STYLE_ASME_INLINED + && standardStyle != ViewProviderDimension::STD_STYLE_ASME_REFERENCING)) { + // If arrows point outside, or ASME standard is not followed, + // add the line part between start and end + DrawUtil::intervalMarkLinear(outputMarking, 0.0, startPosition, true); + } + + // For ASME Inlined, cut out the part of line occupied by the value + if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) { + DrawUtil::intervalMarkLinear(outputMarking, jointPosition - labelBorder, labelBorder*2.0, false); + } + + // Add the arrow tails - these are drawn always + double placementFactor = flipArrows ? +1.0 : -1.0; + DrawUtil::intervalMarkLinear(outputMarking, 0.0, placementFactor*getDefaultArrowTailLength(), true); + if (arrowCount > 1) { + DrawUtil::intervalMarkLinear(outputMarking, startPosition, + -placementFactor*getDefaultArrowTailLength(), true); + } + + return flipArrows; } -Base::Vector2d QGIViewDimension::computeLineOriginPoint(Base::Vector2d lineTarget, double projectedLabelDistance, - double lineAngle, double labelWidth, double direction) const +bool QGIViewDimension::constructDimensionArc(const Base::Vector2d &arcCenter, double arcRadius, double endAngle, + double startRotation, double handednessFactor, double jointRotation, + const Base::BoundBox2d &labelRectangle, int arrowCount, int standardStyle, bool flipArrows, + std::vector> &outputMarking) const { - return lineTarget + (projectedLabelDistance + direction*(0.5*labelWidth + getDefaultReferenceLineOverhang())) - *Base::Vector2d(cos(lineAngle), sin(lineAngle)); + // The start rotation > 0 is not expected, the caller must handle this + if (startRotation > 0.0) { + Base::Console().Error("QGIVD::constructDimArc - Start Rotation must not be positive! Received: %f\n", + startRotation); + return false; + } + + double startDelta = 0.0; + double endDelta = 0.0; + if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) { + double borderRadius = (labelRectangle.GetCenter() - arcCenter).Length(); + + if (borderRadius > arcRadius) { + borderRadius = arcRadius + getDefaultIsoDimensionLineSpacing(); + } + else if (borderRadius < arcRadius) { + borderRadius = arcRadius - getDefaultIsoDimensionLineSpacing(); + } + + // ISO oriented labels are symmetrical along their center axis + startDelta = atan((labelRectangle.Width()*0.5 + getDefaultIsoReferenceLineOverhang())/borderRadius); + endDelta = startDelta; + } + else if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) { + std::vector intersectionPoints; + + DrawUtil::findCircleRectangleIntersections(arcCenter, arcRadius, labelRectangle, intersectionPoints); + + // We do not want to handle other cases than 2 intersection points - if so, act as if there were none + if (intersectionPoints.size() == 2) { + double zeroAngle = (labelRectangle.GetCenter()- arcCenter).Angle(); + + startDelta = DrawUtil::angleDifference(zeroAngle, (intersectionPoints[0] - arcCenter).Angle()); + endDelta = DrawUtil::angleDifference(zeroAngle, (intersectionPoints[1] - arcCenter).Angle()); + + // End delta is the angle in the end point direction, start delta in the opposite + // Keep orientation and the computation sign in sync + if ((endDelta < 0.0) == (handednessFactor < 0.0)) { + std::swap(startDelta, endDelta); + } + + startDelta = fabs(startDelta); + endDelta = fabs(endDelta); + } + } + + bool autoFlipArrows = false; + if (jointRotation + endDelta > 0.0) { + // If label exceeds end angle ray, extend the dimension arc and flip arrows + DrawUtil::intervalMarkCircular(outputMarking, endAngle, handednessFactor*(jointRotation + endDelta), true); + autoFlipArrows = true; + } + + if (jointRotation - startDelta < startRotation) { + DrawUtil::intervalMarkCircular(outputMarking, endAngle + handednessFactor*startRotation, + handednessFactor*(jointRotation - startDelta - startRotation), true); + + // For only one arrow and zero width line skip flipping, it already points correctly + if (arrowCount > 1 || startRotation < 0.0) { + autoFlipArrows = true; + } + } + + flipArrows ^= autoFlipArrows; + if (!flipArrows + || (standardStyle != ViewProviderDimension::STD_STYLE_ASME_INLINED + && standardStyle != ViewProviderDimension::STD_STYLE_ASME_REFERENCING)) { + // If arrows point outside, or ASME standard is not followed, + // add the arc part between start and end + DrawUtil::intervalMarkCircular(outputMarking, endAngle, handednessFactor*startRotation, true); + } + + // For ASME Inlined, cut out the part of arc occupied by the value + if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) { + DrawUtil::intervalMarkCircular(outputMarking, endAngle + handednessFactor*(jointRotation - startDelta), + handednessFactor*(startDelta + endDelta), false); + } + + // Add the arrow tails - these are drawn always + double tailDelta = arcRadius >= Precision::Confusion() ? getDefaultArrowTailLength()/arcRadius : M_PI_4; + double placementFactor = flipArrows ? +1.0 : -1.0; + + DrawUtil::intervalMarkCircular(outputMarking, endAngle, + placementFactor*handednessFactor*tailDelta, true); + if (arrowCount > 1) { + DrawUtil::intervalMarkCircular(outputMarking, endAngle + handednessFactor*startRotation, + -placementFactor*handednessFactor*tailDelta, true); + } + + return flipArrows; } -Base::Vector2d QGIViewDimension::getIsoJointPoint(Base::Vector2d labelCenter, double width, double dir) const +void QGIViewDimension::resetArrows(void) const { - return Base::Vector2d(labelCenter.x + dir*(width*0.5 + getDefaultReferenceLineOverhang()), - labelCenter.y + getDefaultTextVerticalOffset()); + aHead1->setDirMode(true); + aHead1->setRotation(0.0); + aHead1->setFlipped(false); + + aHead2->setDirMode(true); + aHead2->setRotation(0.0); + aHead2->setFlipped(false); } -Base::Vector2d QGIViewDimension::getAsmeJointPoint(Base::Vector2d labelCenter, double width, double dir) const +void QGIViewDimension::drawArrows(int count, const Base::Vector2d positions[], double angles[], bool flipped) const { - return Base::Vector2d(labelCenter.x + dir*(width*0.5 + getDefaultHorizontalLeaderLength()), - labelCenter.y + TextOffsetFudge); + const int arrowCount = 2; + QGIArrow *arrows[arrowCount] = { aHead1, aHead2 }; + + for (int i = 0; i < arrowCount; ++i) { + QGIArrow *arrow = arrows[i]; + + if (positions && angles) { + arrow->setPos(toQtGui(positions[i])); + arrow->setDirection(toQtRad(angles[i])); + } + + if (i >= count) { + arrow->hide(); + continue; + } + + arrow->setStyle(QGIArrow::getPrefArrowStyle()); + arrow->setSize(QGIArrow::getPrefArrowSize()); + arrow->setFlipped(flipped); + + arrow->draw(); + arrow->show(); + } +} + +void QGIViewDimension::drawSingleLine(QPainterPath &painterPath, const Base::Vector2d &lineOrigin, double lineAngle, + double startPosition, double endPosition) const +{ + if (endPosition == startPosition) { + return; + } + + painterPath.moveTo(toQtGui(lineOrigin + Base::Vector2d::FromPolar(startPosition, lineAngle))); + painterPath.lineTo(toQtGui(lineOrigin + Base::Vector2d::FromPolar(endPosition, lineAngle))); +} + +void QGIViewDimension::drawMultiLine(QPainterPath &painterPath, const Base::Vector2d &lineOrigin, double lineAngle, + const std::vector> &drawMarking) const +{ + if (drawMarking.size() < 2) { + return; + } + + unsigned int startIndex = 0; + unsigned int currentIndex = 1; + while (currentIndex < drawMarking.size()) { + if (drawMarking[currentIndex].second != drawMarking[startIndex].second) { + if (drawMarking[startIndex].second) { + drawSingleLine(painterPath, lineOrigin, lineAngle, + drawMarking[startIndex].first, drawMarking[currentIndex].first); + } + + startIndex = currentIndex; + } + + ++currentIndex; + } +} + +void QGIViewDimension::drawSingleArc(QPainterPath &painterPath, const Base::Vector2d &arcCenter, double arcRadius, + double startAngle, double endAngle) const +{ + if (endAngle == startAngle) { + return; + } + if (endAngle < startAngle) { + endAngle += M_2PI; + } + + QRectF qtArcRectangle(toQtGui(Base::BoundBox2d(arcCenter.x - arcRadius, arcCenter.y - arcRadius, + arcCenter.x + arcRadius, arcCenter.y + arcRadius))); + + // In arc drawing are for some reason Qt's angles counterclockwise as in our computations... + painterPath.arcMoveTo(qtArcRectangle, toDeg(startAngle)); + painterPath.arcTo(qtArcRectangle, toDeg(startAngle), toDeg(endAngle - startAngle)); +} + +void QGIViewDimension::drawMultiArc(QPainterPath &painterPath, const Base::Vector2d &arcCenter, double arcRadius, + const std::vector> &drawMarking) const +{ + if (drawMarking.size() < 1) { + return; + } + + unsigned int entryIndex = 0; + while (entryIndex < drawMarking.size() && drawMarking[entryIndex].second) { + ++entryIndex; + } + + if (entryIndex >= drawMarking.size()) { + drawSingleArc(painterPath, arcCenter, arcRadius, 0, M_2PI); + return; + } + + unsigned int startIndex = entryIndex; + unsigned int currentIndex = entryIndex; + do { + ++currentIndex; + currentIndex %= drawMarking.size(); + + if (drawMarking[currentIndex].second != drawMarking[startIndex].second) { + if (drawMarking[startIndex].second) { + drawSingleArc(painterPath, arcCenter, arcRadius, + drawMarking[startIndex].first, drawMarking[currentIndex].first); + } + + startIndex = currentIndex; + } + } + while (currentIndex != entryIndex); +} + +void QGIViewDimension::drawDimensionLine(QPainterPath &painterPath, const Base::Vector2d &targetPoint, double lineAngle, + double startPosition, double jointPosition, const Base::BoundBox2d &labelRectangle, + int arrowCount, int standardStyle, bool flipArrows) const +{ + // Keep the convention start position <= 0 + jointPosition *= normalizeStartPosition(startPosition, lineAngle); + + std::vector> drawMarks; + flipArrows = constructDimensionLine(targetPoint, lineAngle, startPosition, jointPosition, + labelRectangle, arrowCount, standardStyle, flipArrows, + drawMarks); + + drawMultiLine(painterPath, targetPoint, lineAngle, drawMarks); + + Base::Vector2d arrowPositions[2]; + arrowPositions[0] = targetPoint; + arrowPositions[1] = targetPoint + Base::Vector2d::FromPolar(startPosition, lineAngle); + + double arrowAngles[2]; + arrowAngles[0] = lineAngle; + arrowAngles[1] = lineAngle + M_PI; + + drawArrows(arrowCount, arrowPositions, arrowAngles, flipArrows); +} + +void QGIViewDimension::drawDimensionArc(QPainterPath &painterPath, const Base::Vector2d &arcCenter, double arcRadius, + double endAngle, double startRotation, double jointAngle, + const Base::BoundBox2d &labelRectangle, + int arrowCount, int standardStyle, bool flipArrows) const +{ + // Keep the convention start rotation <= 0 + double handednessFactor = normalizeStartRotation(startRotation); + + // Split the rest of 2PI minus the angle and assign joint offset so > 0 is closer to end arc side + double jointRotation = handednessFactor*(jointAngle - endAngle); + if (fabs(jointRotation - startRotation*0.5) > M_PI) { + jointRotation += jointRotation < 0.0 ? +M_2PI : -M_2PI; + } + + std::vector> drawMarks; + flipArrows = constructDimensionArc(arcCenter, arcRadius, endAngle, startRotation, handednessFactor, jointRotation, + labelRectangle, arrowCount, standardStyle, flipArrows, + drawMarks); + + drawMultiArc(painterPath, arcCenter, arcRadius, drawMarks); + + Base::Vector2d arrowPositions[2]; + arrowPositions[0] = arcCenter + Base::Vector2d::FromPolar(arcRadius, endAngle); + arrowPositions[1] = arcCenter + Base::Vector2d::FromPolar(arcRadius, endAngle + handednessFactor*startRotation); + + double arrowAngles[2]; + arrowAngles[0] = endAngle + handednessFactor*M_PI_2; + arrowAngles[1] = endAngle + handednessFactor*(startRotation - M_PI_2); + + drawArrows(arrowCount, arrowPositions, arrowAngles, flipArrows); +} + +void QGIViewDimension::drawDistanceExecutive(const Base::Vector2d &startPoint, const Base::Vector2d &endPoint, + double lineAngle, const Base::BoundBox2d &labelRectangle, + int standardStyle, int renderExtent, bool flipArrows) const +{ + QPainterPath distancePath; + + Base::Vector2d labelCenter(labelRectangle.GetCenter()); + double labelAngle = 0.0; + + Base::Vector2d startCross; + Base::Vector2d endCross; + int arrowCount = renderExtent >= ViewProviderDimension::REND_EXTENT_NORMAL + || renderExtent == ViewProviderDimension::REND_EXTENT_CONFINED + ? 2 : 1; + + if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING + || standardStyle == ViewProviderDimension::STD_STYLE_ASME_REFERENCING) { + // The dimensional value text must stay horizontal + Base::Vector2d jointPoints[2]; + + if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING) { + jointPoints[0] = getIsoRefJointPoint(labelRectangle, false); + jointPoints[1] = getIsoRefJointPoint(labelRectangle, true); + } + else { + jointPoints[0] = getAsmeRefJointPoint(labelRectangle, false); + jointPoints[1] = getAsmeRefJointPoint(labelRectangle, true); + } + + // Find target points, i.e. points where the extension line intersects the dimension line + Base::Vector2d targetPoints[2]; + targetPoints[0] = computePerpendicularIntersection(jointPoints[0], endPoint, lineAngle); + targetPoints[1] = computePerpendicularIntersection(jointPoints[1], endPoint, lineAngle); + + // Compute and normalize (i.e. make < 0) the start position + Base::Vector2d lineDirection(Base::Vector2d::FromPolar(1.0, lineAngle)); + double startPosition = arrowCount > 1 ? lineDirection*(startPoint - targetPoints[0]) : 0.0; + lineDirection *= normalizeStartPosition(startPosition, lineAngle); + + // Find the positions where the reference line attaches to the dimension line + double jointPositions[2]; + jointPositions[0] = lineDirection*(jointPoints[0] - targetPoints[0]); + jointPositions[1] = lineDirection*(jointPoints[1] - targetPoints[1]); + + // Orient the leader line angle correctly towards the target point + double angles[2]; + angles[0] = jointPositions[0] > 0.0 ? DrawUtil::angleComposition(lineAngle, M_PI) : lineAngle; + angles[1] = jointPositions[1] > 0.0 ? DrawUtil::angleComposition(lineAngle, M_PI) : lineAngle; + + // Select the placement, where the label is not obscured by the leader line + // or (if both behave the same) the one that bends the reference line less + double strikeFactors[2]; + + std::vector> lineMarking; + constructDimensionLine(targetPoints[0], lineAngle, startPosition, jointPositions[0], + labelRectangle, arrowCount, standardStyle, flipArrows, lineMarking); + strikeFactors[0] = computeLineStrikeFactor(labelRectangle, targetPoints[0], lineAngle, lineMarking); + + lineMarking.clear(); + constructDimensionLine(targetPoints[1], lineAngle, startPosition, jointPositions[1], + labelRectangle, arrowCount, standardStyle, flipArrows, lineMarking); + strikeFactors[1] = computeLineStrikeFactor(labelRectangle, targetPoints[1], lineAngle, lineMarking); + + int selected = compareAngleStraightness(0.0, angles[0], angles[1], strikeFactors[0], strikeFactors[1]); + if (selected == 0) { + // Select the side closer, so the label is on the outer side of the dimension line + Base::Vector2d perpendicularDir(lineDirection.Perpendicular()); + if (fabs((jointPoints[0] - endPoint)*perpendicularDir) + > fabs((jointPoints[1] - endPoint)*perpendicularDir)) { + selected = 1; + } + } + else if (selected < 0) { + selected = 0; + } + + endCross = targetPoints[selected]; + startCross = targetPoints[selected] + Base::Vector2d::FromPolar(startPosition, lineAngle); + + drawDimensionLine(distancePath, endCross, lineAngle, startPosition, jointPositions[selected], + labelRectangle, arrowCount, standardStyle, flipArrows); + + Base::Vector2d outsetPoint(standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING + ? getIsoRefOutsetPoint(labelRectangle, selected == 1) + : getAsmeRefOutsetPoint(labelRectangle, selected == 1)); + + distancePath.moveTo(toQtGui(outsetPoint)); + distancePath.lineTo(toQtGui(jointPoints[selected])); + } + else if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) { + // We may rotate the label so no leader and reference lines are needed + double placementFactor = getIsoStandardLinePlacement(lineAngle); + labelAngle = placementFactor > 0.0 ? DrawUtil::angleComposition(lineAngle, M_PI) : lineAngle; + + // Find out the projection of label center on the line with given angle + Base::Vector2d labelProjection( + labelCenter + + Base::Vector2d::FromPolar( + placementFactor*(labelRectangle.Height()*0.5 + + getDefaultIsoDimensionLineSpacing()), + lineAngle + M_PI_2)); + + // Compute the dimensional line start and end crossings with (virtual) extension lines + startCross = computePerpendicularIntersection(labelProjection, startPoint, lineAngle); + endCross = computePerpendicularIntersection(labelProjection, endPoint, lineAngle); + + // Find linear coefficients of crossings + Base::Vector2d lineDirection(Base::Vector2d::FromPolar(1.0, lineAngle)); + double startPosition = arrowCount > 1 ? lineDirection*(startCross - endCross) : 0.0; + double labelPosition = lineDirection*(labelProjection - endCross); + + drawDimensionLine(distancePath, endCross, lineAngle, startPosition, labelPosition, + labelRectangle, arrowCount, standardStyle, flipArrows); + } + else if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) { + // Text must remain horizontal, but it may split the leader line + startCross = computePerpendicularIntersection(labelCenter, startPoint, lineAngle); + endCross = computePerpendicularIntersection(labelCenter, endPoint, lineAngle); + + // Find linear coefficients of crossings + Base::Vector2d lineDirection(Base::Vector2d::FromPolar(1.0, lineAngle)); + double startPosition = arrowCount > 1 ? lineDirection*(startCross - endCross) : 0.0; + double labelPosition = lineDirection*(labelCenter - endCross); + + drawDimensionLine(distancePath, endCross, lineAngle, startPosition, labelPosition, + labelRectangle, arrowCount, standardStyle, flipArrows); + } + else { + Base::Console().Error("QGIVD::drawDistanceExecutive - this Standard&Style is not supported: %d\n", + standardStyle); + arrowCount = 0; + } + + if (arrowCount > 0 && renderExtent >= ViewProviderDimension::REND_EXTENT_REDUCED) { + double gapSize = 0.0; + if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_REFERENCING + || standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) { + gapSize = getDefaultAsmeExtensionLineGap(); + } + + Base::Vector2d extensionOrigin; + Base::Vector2d extensionTarget(computeExtensionLinePoints(endPoint, endCross, lineAngle + M_PI_2, + getDefaultExtensionLineOverhang(), gapSize, extensionOrigin)); + + distancePath.moveTo(toQtGui(extensionOrigin)); + distancePath.lineTo(toQtGui(extensionTarget)); + + if (arrowCount > 1) { + extensionTarget = computeExtensionLinePoints(startPoint, startCross, lineAngle + M_PI_2, + getDefaultExtensionLineOverhang(), gapSize, extensionOrigin); + distancePath.moveTo(toQtGui(extensionOrigin)); + distancePath.lineTo(toQtGui(extensionTarget)); + } + } + + datumLabel->setTransformOriginPoint(datumLabel->boundingRect().center()); + datumLabel->setRotation(toQtDeg(labelAngle)); + + dimLines->setPath(distancePath); +} + +void QGIViewDimension::drawRadiusExecutive(const Base::Vector2d ¢erPoint, const Base::Vector2d &midPoint, + double radius, double endAngle, double startRotation, + const Base::BoundBox2d &labelRectangle, double centerOverhang, + int standardStyle, int renderExtent, bool flipArrow) const +{ + QPainterPath radiusPath; + + Base::Vector2d labelCenter(labelRectangle.GetCenter()); + double labelAngle = 0.0; + + if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING + || standardStyle == ViewProviderDimension::STD_STYLE_ASME_REFERENCING) { + // The dimensional value text must stay horizontal + Base::Vector2d jointDirections[2]; + + if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING) { + jointDirections[0] = getIsoRefJointPoint(labelRectangle, false) - centerPoint; + jointDirections[1] = getIsoRefJointPoint(labelRectangle, true) - centerPoint; + } + else { + jointDirections[0] = getAsmeRefJointPoint(labelRectangle, false) - centerPoint; + jointDirections[1] = getAsmeRefJointPoint(labelRectangle, true) - centerPoint; + } + + double lineAngles[2]; + lineAngles[0] = jointDirections[0].Angle(); + lineAngles[1] = jointDirections[1].Angle(); + + // Are there points on the arc, where line from center intersects it perpendicularly? + double angleFactors[2]; + angleFactors[0] = getAnglePlacementFactor(lineAngles[0], endAngle, startRotation); + angleFactors[1] = getAnglePlacementFactor(lineAngles[1], endAngle, startRotation); + + // Orient the leader line angle correctly towards the point on arc + if (angleFactors[0] < 0.0) lineAngles[0] = DrawUtil::angleComposition(lineAngles[0], M_PI); + if (angleFactors[1] < 0.0) lineAngles[1] = DrawUtil::angleComposition(lineAngles[1], M_PI); + + // Find the positions where the reference line attaches to the dimension line + double jointPositions[2]; + jointPositions[0] = angleFactors[0]*jointDirections[0].Length() - radius; + jointPositions[1] = angleFactors[1]*jointDirections[1].Length() - radius; + + Base::Vector2d targetPoints[2]; + targetPoints[0] = centerPoint + Base::Vector2d::FromPolar(radius, lineAngles[0]); + targetPoints[1] = centerPoint + Base::Vector2d::FromPolar(radius, lineAngles[1]); + + Base::Vector2d arcPoint; + int selected = 0; + if (angleFactors[0] || angleFactors[1]) { + // At least from one of the reference line sides can run the leader line + // perpendicularly to the arc, i.e. in direction to the center + if (angleFactors[0] && angleFactors[1]) { + // Both are acceptable, so choose the more convenient one + double strikeFactors[2] = { 0.0, 0.0 }; + + if (renderExtent >= ViewProviderDimension::REND_EXTENT_NORMAL) { + std::vector> lineMarking; + constructDimensionLine(targetPoints[0], lineAngles[0], -radius, jointPositions[0], + labelRectangle, 1, standardStyle, flipArrow, lineMarking); + strikeFactors[0] = computeLineStrikeFactor(labelRectangle, targetPoints[0], + lineAngles[0], lineMarking); + + lineMarking.clear(); + constructDimensionLine(targetPoints[1], lineAngles[1], -radius, jointPositions[1], + labelRectangle, 1, standardStyle, flipArrow, lineMarking); + strikeFactors[1] = computeLineStrikeFactor(labelRectangle, targetPoints[1], + lineAngles[1], lineMarking); + } + + if (compareAngleStraightness(0.0, + jointPositions[0] > 0.0 ? DrawUtil::angleComposition(lineAngles[0], M_PI) : lineAngles[0], + jointPositions[1] > 0.0 ? DrawUtil::angleComposition(lineAngles[1], M_PI) : lineAngles[1], + strikeFactors[0], strikeFactors[1]) > 0) { + selected = 1; + } + } + else if (angleFactors[1]) { + selected = 1; + } + + arcPoint = targetPoints[selected]; + } + else { // Both joint points lay outside the vertical angles + arcPoint = midPoint; + + if (labelCenter.x < arcPoint.x) { // Place the dimensional value left + selected = 1; + } + + Base::Vector2d lineDirection(arcPoint - centerPoint - jointDirections[selected]); + lineAngles[selected] = lineDirection.Angle(); + jointPositions[selected] = -lineDirection.Length(); + } + + drawDimensionLine(radiusPath, arcPoint, lineAngles[selected], + // If not reduced rendering and at least in one arc wedge, draw to center + (angleFactors[0] || angleFactors[1]) + && renderExtent >= ViewProviderDimension::REND_EXTENT_NORMAL + ? -radius - centerOverhang : 0.0, + jointPositions[selected], labelRectangle, 1, standardStyle, flipArrow); + + Base::Vector2d outsetPoint(standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING + ? getIsoRefOutsetPoint(labelRectangle, selected == 1) + : getAsmeRefOutsetPoint(labelRectangle, selected == 1)); + + radiusPath.moveTo(toQtGui(outsetPoint)); + radiusPath.lineTo(toQtGui(centerPoint + jointDirections[selected])); + } + else if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) { + // We may rotate the label so no reference line is needed + double lineAngle; + double devAngle = computeLineAndLabelAngles(centerPoint, labelCenter, + labelRectangle.Height()*0.5 + getDefaultIsoDimensionLineSpacing(), lineAngle, labelAngle); + + // Is there point on the arc, where line from center intersects it perpendicularly? + double angleFactor = getAnglePlacementFactor(lineAngle, endAngle, startRotation); + if (angleFactor < 0.0) { + lineAngle = DrawUtil::angleComposition(lineAngle, M_PI); + } + + Base::Vector2d arcPoint; + double labelPosition; + if (angleFactor) { + arcPoint = centerPoint + Base::Vector2d::FromPolar(radius, lineAngle); + + // Correct the label center distance projected on the leader line and subtract radius + labelPosition = angleFactor*cos(devAngle)*((labelCenter - centerPoint).Length()) - radius; + } + else { + // Leader line outside both arc wedges + arcPoint = midPoint; + + devAngle = computeLineAndLabelAngles(arcPoint, labelCenter, + labelRectangle.Height()*0.5 + getDefaultIsoDimensionLineSpacing(), lineAngle, labelAngle); + lineAngle = DrawUtil::angleComposition(lineAngle, M_PI); + + labelPosition = -cos(devAngle)*((labelCenter - arcPoint).Length()); + } + + drawDimensionLine(radiusPath, arcPoint, lineAngle, + // If not reduced rendering and at least in one arc wedge, draw to center + angleFactor && renderExtent >= ViewProviderDimension::REND_EXTENT_NORMAL + ? -radius - centerOverhang : 0.0, + labelPosition, labelRectangle, 1, standardStyle, flipArrow); + } + else if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) { + // Text must remain horizontal, but it may split the leader line + Base::Vector2d labelDirection(labelCenter - centerPoint); + double lineAngle = labelDirection.Angle(); + + // Is there point on the arc, where line from center intersects it perpendicularly? + double angleFactor = getAnglePlacementFactor(lineAngle, endAngle, startRotation); + if (angleFactor < 0) { + lineAngle = DrawUtil::angleComposition(lineAngle, M_PI); + } + + Base::Vector2d arcPoint; + double labelPosition; + if (angleFactor) { + arcPoint = centerPoint + Base::Vector2d::FromPolar(radius, lineAngle); + + labelPosition = angleFactor*labelDirection.Length() - radius; + } + else { + // Leader line outside both arc wedges + arcPoint = midPoint; + + labelDirection = arcPoint - labelCenter; + lineAngle = labelDirection.Angle(); + labelPosition = -labelDirection.Length(); + } + + drawDimensionLine(radiusPath, arcPoint, lineAngle, + // If not reduced rendering and at least in one arc wedge, draw to center + angleFactor && renderExtent >= ViewProviderDimension::REND_EXTENT_NORMAL + ? -radius - centerOverhang : 0.0, + labelPosition, labelRectangle, 1, standardStyle, flipArrow); + } + else { + Base::Console().Error("QGIVD::drawRadiusExecutive - this Standard&Style is not supported: %d\n", standardStyle); + } + + datumLabel->setTransformOriginPoint(datumLabel->boundingRect().center()); + datumLabel->setRotation(toQtDeg(labelAngle)); + + dimLines->setPath(radiusPath); +} + +void QGIViewDimension::drawDistance(TechDraw::DrawViewDimension *dimension, ViewProviderDimension *viewProvider) const +{ + QPainterPath distancePath; + + Base::BoundBox2d labelRectangle(fromQtGui(mapRectFromItem(datumLabel, datumLabel->boundingRect()))); + + pointPair linePoints = dimension->getLinearPoints(); + const char *dimensionType = dimension->Type.getValueAsString(); + + double lineAngle; + if (strcmp(dimensionType, "DistanceX") == 0) { + lineAngle = 0.0; + } + else if (strcmp(dimensionType, "DistanceY") == 0) { + lineAngle = M_PI_2; + } + else { + lineAngle = (fromQtApp(linePoints.second) - fromQtApp(linePoints.first)).Angle(); + } + + int standardStyle = viewProvider->StandardAndStyle.getValue(); + int renderExtent = viewProvider->RenderingExtent.getValue(); + bool flipArrows = viewProvider->FlipArrowheads.getValue(); + + drawDistanceExecutive(fromQtApp(linePoints.first), fromQtApp(linePoints.second), lineAngle, + labelRectangle, standardStyle, renderExtent, flipArrows); } void QGIViewDimension::drawRadius(TechDraw::DrawViewDimension *dimension, ViewProviderDimension *viewProvider) const { - // Preferred terminology according to ISO 129-1 for Radius: - // Dimensional Value, Leader Line, Reference Line, Terminator + Base::BoundBox2d labelRectangle(fromQtGui(mapRectFromItem(datumLabel, datumLabel->boundingRect()))); + arcPoints curvePoints = dimension->getArcPoints(); - QPainterPath radiusPath; - datumLabel->setRotation(0.0); - aHead1->setRotation(0.0); - aHead1->setFlipped(false); + double endAngle; + double startRotation; + if (curvePoints.isArc) { + endAngle = (fromQtApp(curvePoints.arcEnds.second) - fromQtApp(curvePoints.center)).Angle(); + startRotation = (fromQtApp(curvePoints.arcEnds.first) - fromQtApp(curvePoints.center)).Angle() + - endAngle; - QRectF mappedRect = mapRectFromItem(datumLabel, datumLabel->boundingRect()); - Base::Vector2d labelCenter = Base::Vector2d(mappedRect.center().x(), mappedRect.center().y()); + if (startRotation != 0.0 && ((startRotation > 0.0) != curvePoints.arcCW)) { + startRotation += curvePoints.arcCW ? +M_2PI : -M_2PI; + } + } + else { // A circle arc covers the whole plane + endAngle = M_PI; + startRotation = -M_2PI; + } + + drawRadiusExecutive(fromQtApp(curvePoints.center), fromQtApp(curvePoints.midArc), + curvePoints.radius, endAngle, startRotation, labelRectangle, 0.0, + viewProvider->StandardAndStyle.getValue(), viewProvider->RenderingExtent.getValue(), + viewProvider->FlipArrowheads.getValue()); +} + +void QGIViewDimension::drawDiameter(TechDraw::DrawViewDimension *dimension, ViewProviderDimension *viewProvider) const +{ + Base::BoundBox2d labelRectangle(fromQtGui(mapRectFromItem(datumLabel, datumLabel->boundingRect()))); + Base::Vector2d labelCenter(labelRectangle.GetCenter()); arcPoints curvePoints = dimension->getArcPoints(); - Base::Vector2d curveCenter = Rez::guiX(curvePoints.center, true); - double mappedRadius = Rez::guiX(curvePoints.radius); - double centerDistance = (labelCenter - curveCenter).Length(); - - double arcStartAngle; - double arcEndAngle; - bool arcClockwise; - if (curvePoints.isArc) { - arcStartAngle = atan2(curvePoints.arcEnds.first.y - curvePoints.center.y, - curvePoints.arcEnds.first.x - curvePoints.center.x); - arcEndAngle = atan2(curvePoints.arcEnds.second.y - curvePoints.center.y, - curvePoints.arcEnds.second.x - curvePoints.center.x); - arcClockwise = !curvePoints.arcCW; - } - else { // A circle arc covers the whole plane - arcStartAngle = -M_PI; - arcEndAngle = +M_PI; - arcClockwise = false; - } - - double labelAngle = 0.0; - Base::Vector2d arcPoint; - double lineAngle; + Base::Vector2d curveCenter(fromQtApp(curvePoints.center)); + double curveRadius = curvePoints.radius; int standardStyle = viewProvider->StandardAndStyle.getValue(); - if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_LEVELLED - || standardStyle == ViewProviderDimension::STD_STYLE_ASME_REGULAR) { - // The dimensional value text must stay horizontal - Base::Vector2d leftJoint, rightJoint; - if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_LEVELLED) { - leftJoint = getIsoJointPoint(labelCenter, mappedRect.width(), -1.0); - rightJoint = getIsoJointPoint(labelCenter, mappedRect.width(), +1.0); - } - else { - leftJoint = getAsmeJointPoint(labelCenter, mappedRect.width(), -1.0); - rightJoint = getAsmeJointPoint(labelCenter, mappedRect.width(), +1.0); - } + int renderExtent = viewProvider->RenderingExtent.getValue(); + bool flipArrows = viewProvider->FlipArrowheads.getValue(); - double leftAngle = atan2(leftJoint.y - curveCenter.y, leftJoint.x - curveCenter.x); - double rightAngle = atan2(rightJoint.y - curveCenter.y, rightJoint.x - curveCenter.x); + if (renderExtent == ViewProviderDimension::REND_EXTENT_NORMAL) { + // Draw diameter as one line crossing center touching two opposite circle points + QPainterPath diameterPath; + double labelAngle = 0.0; - int leftPosition = classifyPointToArcPosition((leftJoint - curveCenter).Length(), - leftAngle, mappedRadius, arcStartAngle, arcEndAngle, arcClockwise); - int rightPosition = classifyPointToArcPosition((rightJoint - curveCenter).Length(), - rightAngle, mappedRadius, arcStartAngle, arcEndAngle, arcClockwise); + if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING + || standardStyle == ViewProviderDimension::STD_STYLE_ASME_REFERENCING) { + // The dimensional value text must stay horizontal + Base::Vector2d jointDirections[2]; - Base::Vector2d originPoint; - Base::Vector2d jointPoint; - Base::Vector2d targetPoint; - if (leftPosition <= OPPOSITE_SECTOR || rightPosition <= OPPOSITE_SECTOR) { - // At least from one of the reference line sides can run the leader line - // perpendicularly to the arc, i.e. in direction to the center - if (leftPosition <= OPPOSITE_SECTOR && rightPosition <= OPPOSITE_SECTOR) { - // Both are acceptable, so choose the more convenient one - double leftBend = leftPosition == INNER_SECTOR ? M_PI - fabs(leftAngle) : fabs(leftAngle); - double rightBend = rightPosition == INNER_SECTOR ? fabs(rightAngle) : M_PI - fabs(rightAngle); - - // If right leader line bends less or does not cross the dimensional value, - // use it by marking left point as outlayer - if (leftBend <= M_PI_2 || rightBend <= M_PI_2 - || standardStyle == ViewProviderDimension::STD_STYLE_ASME_REGULAR) { - // Either at least one line does not cross the text, or it is an ASME connection - // (vertically centered), which behaves the same going up or down - if (rightBend < leftBend) { - leftPosition = COMPLEMENT_SECTOR; - } - } - else { // ISO connection, but crosses the value - try to find the one pointing down (if exists) - bool leftDown = leftPosition == INNER_SECTOR ? leftAngle > 0.0 : leftAngle < 0.0; - bool rightDown = rightPosition == INNER_SECTOR ? rightAngle > 0.0 : rightAngle < 0.0; - - if (leftDown == rightDown) { // Both lines go downwards or upwards - if (rightBend < leftBend) { - leftPosition = COMPLEMENT_SECTOR; - } - } - else if (rightDown) { - leftPosition = COMPLEMENT_SECTOR; - } - } - } - - int resultPosition; - if (leftPosition <= OPPOSITE_SECTOR) { - if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_REGULAR) { - originPoint = Base::Vector2d(labelCenter.x + getDefaultTextHorizontalOffset(-1.0), - labelCenter.y + TextOffsetFudge); - } - else { - originPoint = rightJoint; - } - - jointPoint = leftJoint; - lineAngle = leftAngle; - resultPosition = leftPosition; + if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING) { + jointDirections[0] = getIsoRefJointPoint(labelRectangle, false) - curveCenter; + jointDirections[1] = getIsoRefJointPoint(labelRectangle, true) - curveCenter; } else { - if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_REGULAR) { - originPoint = Base::Vector2d(labelCenter.x + getDefaultTextHorizontalOffset(+1.0), - labelCenter.y + TextOffsetFudge); - } - else { - originPoint = leftJoint; - } - - jointPoint = rightJoint; - lineAngle = rightAngle; - resultPosition = rightPosition; + jointDirections[0] = getAsmeRefJointPoint(labelRectangle, false) - curveCenter; + jointDirections[1] = getAsmeRefJointPoint(labelRectangle, true) - curveCenter; } - switch (resultPosition) { - case INNER_SECTOR: - arcPoint = curveCenter + mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle)); - targetPoint = arcPoint; - break; - case OUTER_SECTOR: - arcPoint = curveCenter + mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle)); - // If desired, extend the target point to the center - if (viewProvider->ExtendToCenter.getValue()) { - targetPoint = curveCenter; - if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_LEVELLED) { - aHead1->flip(); - } - } - else { - targetPoint = arcPoint; - aHead1->flip(); - } - break; - case OPPOSITE_SECTOR: - arcPoint = curveCenter - mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle)); - targetPoint = arcPoint; - aHead1->flip(); - break; + // Find the angles of lines from the center + double lineAngles[2]; + lineAngles[0] = jointDirections[0].Angle(); + lineAngles[1] = jointDirections[1].Angle(); + + Base::Vector2d targetPoints[2]; + targetPoints[0] = curveCenter + Base::Vector2d::FromPolar(curveRadius, lineAngles[0]); + targetPoints[1] = curveCenter + Base::Vector2d::FromPolar(curveRadius, lineAngles[1]); + + // Find the positions where the reference line attaches to the dimension line + double jointPositions[2]; + jointPositions[0] = jointDirections[0].Length() - curveRadius; + jointPositions[1] = jointDirections[1].Length() - curveRadius; + + // Select the placement, where the label is not obscured by the leader line + double strikeFactors[2]; + + std::vector> lineMarking; + constructDimensionLine(targetPoints[0], lineAngles[0], -curveRadius*2.0, jointPositions[0], + labelRectangle, 2, standardStyle, flipArrows, lineMarking); + strikeFactors[0] = computeLineStrikeFactor(labelRectangle, targetPoints[0], lineAngles[0], lineMarking); + + lineMarking.clear(); + constructDimensionLine(targetPoints[1], lineAngles[1], -curveRadius*2.0, jointPositions[1], + labelRectangle, 2, standardStyle, flipArrows, lineMarking); + strikeFactors[1] = computeLineStrikeFactor(labelRectangle, targetPoints[1], lineAngles[1], lineMarking); + + int selected = 0; + if (compareAngleStraightness(0.0, + jointPositions[0] > 0.0 ? DrawUtil::angleComposition(lineAngles[0], M_PI) : lineAngles[0], + jointPositions[1] > 0.0 ? DrawUtil::angleComposition(lineAngles[1], M_PI) : lineAngles[1], + strikeFactors[0], strikeFactors[1]) > 0) { + selected = 1; } + + drawDimensionLine(diameterPath, curveCenter + Base::Vector2d::FromPolar(curveRadius, lineAngles[selected]), + lineAngles[selected], -curveRadius*2.0, jointPositions[selected], + labelRectangle, 2, standardStyle, flipArrows); + + Base::Vector2d outsetPoint(standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING + ? getIsoRefOutsetPoint(labelRectangle, selected == 1) + : getAsmeRefOutsetPoint(labelRectangle, selected == 1)); + + diameterPath.moveTo(toQtGui(outsetPoint)); + diameterPath.lineTo(toQtGui(curveCenter + jointDirections[selected])); } - else { // Both joint points lay outside the vertical angles - arcPoint = Rez::guiX(curvePoints.midArc, true); + else if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) { + // We may rotate the label so no reference line is needed + double lineAngle; + double devAngle = computeLineAndLabelAngles(curveCenter, labelCenter, + labelRectangle.Height()*0.5 + getDefaultIsoDimensionLineSpacing(), + lineAngle, labelAngle); - if (labelCenter.x >= arcPoint.x) { // Place the dimensional value right - if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_REGULAR) { - originPoint = Base::Vector2d(mappedRect.left(), labelCenter.y); - } - else { - originPoint = rightJoint; - } - jointPoint = leftJoint; - } - else { // Place the dimensional value left - if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_REGULAR) { - originPoint = Base::Vector2d(mappedRect.right(), labelCenter.y); - } - else { - originPoint = leftJoint; - } - jointPoint = rightJoint; - } + // Correct the label center distance projected on the leader line and subtract radius + double labelPosition = cos(devAngle)*((labelCenter - curveCenter).Length()) - curveRadius; - targetPoint = arcPoint; - lineAngle = atan2(targetPoint.y - jointPoint.y, targetPoint.x - jointPoint.x); + drawDimensionLine(diameterPath, curveCenter + Base::Vector2d::FromPolar(curveRadius, lineAngle), lineAngle, + -curveRadius*2.0, labelPosition, labelRectangle, 2, standardStyle, flipArrows); + } + else if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) { + // Text must remain horizontal, but it may split the leader line + double lineAngle = (labelCenter - curveCenter).Angle(); + Base::Vector2d lineDirection(Base::Vector2d::FromPolar(1.0, lineAngle)); + + drawDimensionLine(diameterPath, curveCenter + Base::Vector2d::FromPolar(curveRadius, lineAngle), lineAngle, + -curveRadius*2.0, (labelCenter - curveCenter).Length() - curveRadius, + labelRectangle, 2, standardStyle, flipArrows); + } + else { + Base::Console().Error("QGIVD::drawRadius - this Standard&Style is not supported: %d\n", standardStyle); } - radiusPath.moveTo(originPoint.x, originPoint.y); - radiusPath.lineTo(jointPoint.x, jointPoint.y); - radiusPath.lineTo(targetPoint.x, targetPoint.y); + datumLabel->setTransformOriginPoint(datumLabel->boundingRect().center()); + datumLabel->setRotation(toQtDeg(labelAngle)); + + dimLines->setPath(diameterPath); + } + else if (renderExtent >= ViewProviderDimension::REND_EXTENT_EXPANDED) { + double lineAngle = (labelCenter - curveCenter).Angle(); + Base::Vector2d startPoint(curveCenter); + Base::Vector2d endPoint(curveCenter); + + if ((lineAngle >= M_PI_4 && lineAngle <= 3.0*M_PI_4) || (lineAngle <= -M_PI_4 && lineAngle >= -3.0*M_PI_4)) { + // Horizontal dimension line + startPoint.x -= curveRadius; + endPoint.x += curveRadius; + lineAngle = 0.0; + } + else { // Vertical dimension line + startPoint.y -= curveRadius; + endPoint.y += curveRadius; + lineAngle = M_PI_2; + } + +// lineAngle = DrawUtil::angleComposition((labelCenter - curveCenter).Angle(), +M_PI_2); +// startPoint = curveCenter - Base::Vector2d::FromPolar(curveRadius, lineAngle); +// endPoint = curveCenter + Base::Vector2d::FromPolar(curveRadius, lineAngle); + + drawDistanceExecutive(startPoint, endPoint, lineAngle, labelRectangle, + standardStyle, ViewProviderDimension::REND_EXTENT_NORMAL, flipArrows); + } + else if (renderExtent <= ViewProviderDimension::REND_EXTENT_REDUCED) { + renderExtent = renderExtent <= ViewProviderDimension::REND_EXTENT_CONFINED + ? ViewProviderDimension::REND_EXTENT_REDUCED + : ViewProviderDimension::REND_EXTENT_NORMAL; + + drawRadiusExecutive(curveCenter, Rez::guiX(curvePoints.midArc, true), + curveRadius, M_PI, -M_2PI, labelRectangle, getDefaultExtensionLineOverhang(), + standardStyle, renderExtent, flipArrows); + } +} + +void QGIViewDimension::drawAngle(TechDraw::DrawViewDimension *dimension, ViewProviderDimension *viewProvider) const +{ + QPainterPath anglePath; + + Base::BoundBox2d labelRectangle(fromQtGui(mapRectFromItem(datumLabel, datumLabel->boundingRect()))); + Base::Vector2d labelCenter(labelRectangle.GetCenter()); + double labelAngle = 0.0; + + anglePoints anglePoints = dimension->getAnglePoints(); + + Base::Vector2d angleVertex = fromQtApp(anglePoints.vertex); + Base::Vector2d startPoint = fromQtApp(anglePoints.ends.first); + Base::Vector2d endPoint = fromQtApp(anglePoints.ends.second); + + double endAngle = (endPoint - angleVertex).Angle(); + double startAngle = (startPoint - angleVertex).Angle(); + double arcRadius; + + int standardStyle = viewProvider->StandardAndStyle.getValue(); + int renderExtent = viewProvider->RenderingExtent.getValue(); + bool flipArrows = viewProvider->FlipArrowheads.getValue(); + + int arrowCount = renderExtent >= ViewProviderDimension::REND_EXTENT_NORMAL + || renderExtent == ViewProviderDimension::REND_EXTENT_CONFINED + ? 2 : 1; + + // Inverted dimensions display reflex angles (fi > PI), regular ones oblique angles (fi <= PI/2) + double startRotation = DrawUtil::angleDifference(startAngle, endAngle, dimension->Inverted.getValue()); + if (arrowCount < 2) { + // For single arrow, the effective angle span is 0, but still we need to know + // the angle orientation. Floating point positive/negative zero comes to rescue... + startRotation = copysign(0.0, startRotation); + } + + if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING + || standardStyle == ViewProviderDimension::STD_STYLE_ASME_REFERENCING) { + // The dimensional value text must stay horizontal + Base::Vector2d jointDirections[2]; + + if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING) { + jointDirections[0] = getIsoRefJointPoint(labelRectangle, false) - angleVertex; + jointDirections[1] = getIsoRefJointPoint(labelRectangle, true) - angleVertex; + } + else { + jointDirections[0] = getAsmeRefJointPoint(labelRectangle, false) - angleVertex; + jointDirections[1] = getAsmeRefJointPoint(labelRectangle, true) - angleVertex; + } + + // Get radiuses of the angle dimension arcs + double arcRadii[2]; + arcRadii[0] = jointDirections[0].Length(); + arcRadii[1] = jointDirections[1].Length(); + + // Compute the reference line joint angles + double jointAngles[2]; + jointAngles[0] = jointDirections[0].Angle(); + jointAngles[1] = jointDirections[1].Angle(); + + double handednessFactor = normalizeStartRotation(startRotation); + double jointRotations[2]; + jointRotations[0] = handednessFactor*(jointAngles[0] - endAngle); + jointRotations[1] = handednessFactor*(jointAngles[1] - endAngle); + + // Compare the offset with half of the rest of 2PI minus the angle and eventually fix the values + if (fabs(jointRotations[0] - startRotation*0.5) > M_PI) { + jointRotations[0] += jointRotations[0] < 0.0 ? +M_2PI : -M_2PI; + } + if (fabs(jointRotations[1] - startRotation*0.5) > M_PI) { + jointRotations[1] += jointRotations[1] < 0.0 ? +M_2PI : -M_2PI; + } + + // Compute the strike factors so we can choose the placement where value is not obscured by dimensional arc + double strikeFactors[2]; + + std::vector> arcMarking; + constructDimensionArc(angleVertex, arcRadii[0], endAngle, startRotation, handednessFactor, jointRotations[0], + labelRectangle, arrowCount, standardStyle, flipArrows, arcMarking); + strikeFactors[0] = computeArcStrikeFactor(labelRectangle, angleVertex, arcRadii[0], arcMarking); + + arcMarking.clear(); + constructDimensionArc(angleVertex, arcRadii[1], endAngle, startRotation, handednessFactor, jointRotations[1], + labelRectangle, arrowCount, standardStyle, flipArrows, arcMarking); + strikeFactors[1] = computeArcStrikeFactor(labelRectangle, angleVertex, arcRadii[1], arcMarking); + + int selected = 0; + if (compareAngleStraightness(0.0, + DrawUtil::angleComposition(jointAngles[0], + handednessFactor*jointRotations[0] > 0.0 ? -M_PI_2 : +M_PI_2), + DrawUtil::angleComposition(jointAngles[1], + handednessFactor*jointRotations[1] > 0.0 ? -M_PI_2 : +M_PI_2), + strikeFactors[0], strikeFactors[1]) > 0) { + selected = 1; + } + + arcRadius = arcRadii[selected]; + startRotation = copysign(startRotation, -handednessFactor); + + drawDimensionArc(anglePath, angleVertex, arcRadius, endAngle, startRotation, + jointAngles[selected], labelRectangle, arrowCount, standardStyle, flipArrows); + + Base::Vector2d outsetPoint(standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING + ? getIsoRefOutsetPoint(labelRectangle, selected == 1) + : getAsmeRefOutsetPoint(labelRectangle, selected == 1)); + + anglePath.moveTo(toQtGui(outsetPoint)); + anglePath.lineTo(toQtGui(angleVertex + jointDirections[selected])); } else if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) { - // We may rotate the label so no reference line is needed - double devAngle = computeLineAndLabelAngles(curveCenter, labelCenter, - getDefaultTextVerticalOffset(), lineAngle, labelAngle); - // Correct the label center distance projected on the leader line - centerDistance *= cos(devAngle); + // We may rotate the label so no leader and reference lines are needed + Base::Vector2d labelDirection(labelCenter - angleVertex); + double radiusAngle = labelDirection.Angle(); - Base::Vector2d originPoint; - Base::Vector2d targetPoint; - switch (classifyPointToArcPosition(centerDistance, lineAngle, mappedRadius, - arcStartAngle, arcEndAngle, arcClockwise)) { - case INNER_SECTOR: { - // The label is placed within the arc sector angle, there's always point - // on the arc where the leader line can cross it perpendicularly - arcPoint = curveCenter + mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle)); + labelAngle = DrawUtil::angleComposition(radiusAngle, M_PI_2); + double placementFactor = getIsoStandardLinePlacement(labelAngle); + labelAngle = placementFactor > 0.0 ? DrawUtil::angleComposition(labelAngle, M_PI) : labelAngle; - if (viewProvider->ExtendToCenter.getValue()) { // Start in the very center - originPoint = curveCenter; - } - else { // Start on the label side closer to the center - originPoint = computeLineOriginPoint(curveCenter, centerDistance, lineAngle, - mappedRect.width(), -1.0); - } - targetPoint = arcPoint; - break; - } - case OUTER_SECTOR: { - // Same situation as when on the inner side of sector - arcPoint = curveCenter + mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle)); - aHead1->flip(); - - originPoint = computeLineOriginPoint(curveCenter, centerDistance, lineAngle, - mappedRect.width(), +1.0); - // If leader line shall not be extended to the center, start on the arc projection - targetPoint = viewProvider->ExtendToCenter.getValue() ? curveCenter : arcPoint; - break; - } - case OPPOSITE_SECTOR: { - // If the label is placed within the vertically opposite angle of the arc sector, - // the leader line passing through the arc center can mark a point on the arc - arcPoint = curveCenter - mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle)); - aHead1->flip(); - - originPoint = computeLineOriginPoint(curveCenter, centerDistance, lineAngle, - mappedRect.width(), +1.0); - targetPoint = arcPoint; - break; - } - default: { - // Label outside both arc wedges - arcPoint = Rez::guiX(curvePoints.midArc, true); - aHead1->flip(); - devAngle = computeLineAndLabelAngles(arcPoint, labelCenter, - getDefaultTextVerticalOffset(), lineAngle, labelAngle); - centerDistance = (labelCenter - arcPoint).Length()*cos(devAngle); - - originPoint = computeLineOriginPoint(arcPoint, centerDistance, lineAngle, - mappedRect.width(), +1.0); - targetPoint = arcPoint; - break; - } + arcRadius = labelDirection.Length() + - placementFactor*(labelRectangle.Height()*0.5 + getDefaultIsoDimensionLineSpacing()); + if (arcRadius < 0.0) { + arcRadius = labelDirection.Length(); } - - // Draw only the leader line from start point to end point - radiusPath.moveTo(originPoint.x, originPoint.y); - radiusPath.lineTo(targetPoint.x, targetPoint.y); + + drawDimensionArc(anglePath, angleVertex, arcRadius, endAngle, startRotation, radiusAngle, + labelRectangle, arrowCount, standardStyle, flipArrows); } else if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) { // Text must remain horizontal, but it may split the leader line - Base::Vector2d lineDirection(labelCenter - curveCenter); - lineAngle = atan2(lineDirection.y, lineDirection.x); + Base::Vector2d labelDirection(labelCenter - angleVertex); + arcRadius = labelDirection.Length(); - Base::Vector2d exitPoint; - switch (classifyPointToArcPosition(centerDistance, lineAngle, mappedRadius, - arcStartAngle, arcEndAngle, arcClockwise)) { - case INNER_SECTOR: { - // The label is placed within the arc sector angle, there's always point - // on the arc where the leader line can cross it perpendicularly - arcPoint = curveCenter + mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle)); - - if (computeLineRectangleExitPoint(mappedRect, arcPoint, exitPoint)) { - radiusPath.moveTo(exitPoint.x, exitPoint.y); - radiusPath.lineTo(arcPoint.x, arcPoint.y); - } - - if (viewProvider->ExtendToCenter.getValue() - && computeLineRectangleExitPoint(mappedRect, curveCenter, exitPoint)) { - radiusPath.moveTo(exitPoint.x, exitPoint.y); - radiusPath.lineTo(curveCenter.x, curveCenter.y); - } - - break; - } - case OUTER_SECTOR: { - // Same situation as when on the inner side of sector - arcPoint = curveCenter + mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle)); - - Base::Vector2d targetPoint(viewProvider->ExtendToCenter.getValue() - ? curveCenter : arcPoint); - if (computeLineRectangleExitPoint(mappedRect, targetPoint, exitPoint)) { - radiusPath.moveTo(exitPoint.x, exitPoint.y); - radiusPath.lineTo(targetPoint.x, targetPoint.y); - } - - if (!viewProvider->ExtendToCenter.getValue()) { - aHead1->flip(); - } - - break; - } - case OPPOSITE_SECTOR: { - // If the label is placed within the vertically opposite angle of the arc sector, - // the leader line passing through the arc center can mark a point on the arc - arcPoint = curveCenter - mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle)); - - if (computeLineRectangleExitPoint(mappedRect, arcPoint, exitPoint)) { - radiusPath.moveTo(exitPoint.x, exitPoint.y); - radiusPath.lineTo(arcPoint.x, arcPoint.y); - } - - aHead1->flip(); - break; - } - default: { - // Label outside both arc wedges - arcPoint = Rez::guiX(curvePoints.midArc, true); - - lineDirection = labelCenter - arcPoint; - lineAngle = atan2(lineDirection.y, lineDirection.x); - - if (computeLineRectangleExitPoint(mappedRect, arcPoint, exitPoint)) { - radiusPath.moveTo(exitPoint.x, exitPoint.y); - radiusPath.lineTo(arcPoint.x, arcPoint.y); - } - - aHead1->flip(); - break; - } - } + drawDimensionArc(anglePath, angleVertex, arcRadius, endAngle, startRotation, labelDirection.Angle(), + labelRectangle, arrowCount, standardStyle, flipArrows); } else { - Base::Console().Error("QGIVD::drawRadius - this Standard&Style is not supported: %d\n", standardStyle); - return; + Base::Console().Error("QGIVD::drawAngle - this Standard&Style is not supported: %d\n", standardStyle); + arrowCount = 0; } - datumLabel->setTransformOriginPoint(datumLabel->boundingRect().width()*0.5, - datumLabel->boundingRect().height()*0.5); - datumLabel->setRotation(labelAngle*180.0/M_PI); + if (arrowCount > 0 && renderExtent >= ViewProviderDimension::REND_EXTENT_REDUCED) { + double gapSize = 0.0; + if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_REFERENCING + || standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) { + gapSize = getDefaultAsmeExtensionLineGap(); + } - dimLines->setPath(radiusPath); + Base::Vector2d extensionOrigin; + Base::Vector2d extensionTarget(computeExtensionLinePoints(endPoint, + angleVertex + Base::Vector2d::FromPolar(arcRadius, endAngle), endAngle, + getDefaultExtensionLineOverhang(), gapSize, extensionOrigin)); + anglePath.moveTo(toQtGui(extensionOrigin)); + anglePath.lineTo(toQtGui(extensionTarget)); - aHead1->setPos(arcPoint.x, arcPoint.y); - aHead1->setDirMode(true); - aHead1->setDirection(lineAngle); - if (viewProvider->FlipArrowheads.getValue()) { - aHead1->flip(); + if (arrowCount > 1) { + extensionTarget = computeExtensionLinePoints(startPoint, + angleVertex + Base::Vector2d::FromPolar(arcRadius, startAngle), + startAngle, getDefaultExtensionLineOverhang(), + gapSize, extensionOrigin); + anglePath.moveTo(toQtGui(extensionOrigin)); + anglePath.lineTo(toQtGui(extensionTarget)); + } } - aHead1->setStyle(QGIArrow::getPrefArrowStyle()); - aHead1->setSize(QGIArrow::getPrefArrowSize()); - aHead1->draw(); - aHead1->show(); - aHead2->hide(); + datumLabel->setTransformOriginPoint(datumLabel->boundingRect().center()); + datumLabel->setRotation(toQtDeg(labelAngle)); + + dimLines->setPath(anglePath); } QColor QGIViewDimension::getNormalColor() @@ -1818,7 +1930,7 @@ QColor QGIViewDimension::getNormalColor() m_colNormal = fcColor.asValue(); auto dim( dynamic_cast(getViewObject()) ); - if( dim == nullptr ) + if( dim == nullptr ) return m_colNormal; auto vp = static_cast(getViewProvider(getViewObject())); @@ -1910,81 +2022,41 @@ void QGIViewDimension::drawBorder(void) // Base::Console().Message("TRACE - QGIViewDimension::drawBorder - doing nothing!\n"); } -const double QGIViewDimension::TextOffsetFudge = 2.0; - -double QGIViewDimension::getDefaultTextHorizontalOffset(double direction) const +double QGIViewDimension::getDefaultExtensionLineOverhang() const { - return direction*(datumLabel->boundingRect().width()*0.5 + TextOffsetFudge*2.0); + // 8x Line Width according to ISO 129-1 Standard section 5.4, not specified by ASME Y14.5M + return Rez::appX(m_lineWidth*8.0); } -double QGIViewDimension::getDefaultTextVerticalOffset() const +double QGIViewDimension::getDefaultArrowTailLength() const { - TechDraw::DrawViewDimension *dim = dynamic_cast(getViewObject()); - ViewProviderDimension *vp = static_cast(getViewProvider(getViewObject())); - - double textMult = 1.0; - if (dim->hasTolerance()) { - textMult += datumLabel->getTolAdjust(); - } - - return textMult*Rez::guiX(vp->Fontsize.getValue()) + TextOffsetFudge; + // Arrow length shall be equal to font height and both ISO and ASME seem + // to have arrow tail twice the arrow length, so let's make it twice arrow size + return QGIArrow::getPrefArrowSize()*2.0; } -double QGIViewDimension::getDefaultReferenceLineOverhang() const +double QGIViewDimension::getDefaultIsoDimensionLineSpacing() const { - return 2.0*TextOffsetFudge; + // Not specified directly, but seems to be 2x Line Width according to ISO 129-1 Annex A + return Rez::appX(m_lineWidth*2.0); } -double QGIViewDimension::getDefaultHorizontalLeaderLength() const +double QGIViewDimension::getDefaultIsoReferenceLineOverhang() const { - QFontMetrics fontMetrics(datumLabel->getFont()); - - return 1.5*fontMetrics.width(QChar::fromLatin1('M')); + // Not specified directly but seems to be exactly Line Width according to ISO 129-1 Annex A + return Rez::appX(m_lineWidth*1.0); } -bool QGIViewDimension::angleWithinSector(double testAngle, double startAngle, double endAngle, bool clockwise) +double QGIViewDimension::getDefaultAsmeHorizontalLeaderLength() const { - if (clockwise) { - std::swap(startAngle, endAngle); - } - - if (endAngle < startAngle) { - endAngle += M_2PI; - } - - if (testAngle < startAngle) { - testAngle += M_2PI; - } - - return testAngle <= endAngle; + // Not specified by ASME Y14.5M, this is a best guess + return Rez::appX(m_lineWidth*12); } -double QGIViewDimension::addAngles(double angle1, double angle2) +double QGIViewDimension::getDefaultAsmeExtensionLineGap() const { - angle1 += angle2; - - if (angle2 >= 0.0) { - if (angle1 > +M_PI) angle1 -= M_2PI; - return angle1; - } - else { - if (angle1 < -M_PI) angle1 += M_2PI; - return angle1; - } -} - -int QGIViewDimension::classifyPointToArcPosition(double pointDistance, double pointAngle, - double radius, double startAngle, double endAngle, bool clockwise) -{ - if (angleWithinSector(pointAngle, startAngle, endAngle, clockwise)) { - return pointDistance > radius ? OUTER_SECTOR : INNER_SECTOR; - } - - if (angleWithinSector(addAngles(pointAngle, M_PI), startAngle, endAngle, clockwise)) { - return OPPOSITE_SECTOR; - } - - return COMPLEMENT_SECTOR; + // Not specified by ASME Y14.5M, this is a best guess + return Rez::appX(m_lineWidth*6.0); } //frame, border, caption are never shown in QGIVD, so shouldn't be in bRect diff --git a/src/Mod/TechDraw/Gui/QGIViewDimension.h b/src/Mod/TechDraw/Gui/QGIViewDimension.h index 5fa87b46fd..e97fa3202a 100644 --- a/src/Mod/TechDraw/Gui/QGIViewDimension.h +++ b/src/Mod/TechDraw/Gui/QGIViewDimension.h @@ -31,6 +31,7 @@ #include #include #include +#include "Rez.h" #include "QGIView.h" #include "QGCustomText.h" @@ -92,6 +93,11 @@ public: double getTolAdjust(void); bool hasHover; + bool isFramed(void) { return m_isFramed; } + void setFramed(bool framed) { m_isFramed = framed; } + + double getLineWidth(void) { return m_lineWidth; } + void setLineWidth(double lineWidth) { m_lineWidth = lineWidth; } Q_SIGNALS: void dragging(bool); @@ -115,6 +121,9 @@ protected: double posX; double posY; + + bool m_isFramed; + double m_lineWidth; private: }; @@ -152,23 +161,75 @@ public Q_SLOTS: void datumLabelDragFinished(void); void select(bool state); void hover(bool state); - void updateDim(bool obtuse = false); + void updateDim(); protected: - static double getIsoStandardLinePlacement(double labelAngle); - static double computeLineAndLabelAngles(Base::Vector2d lineTarget, Base::Vector2d labelCenter, - double lineLabelDistance, double &lineAngle, double &labelAngle); - static bool computeLineRectangleExitPoint(const QRectF &rectangle, Base::Vector2d targetPoint, - Base::Vector2d &exitPoint); + static double getAnglePlacementFactor(double testAngle, double endAngle, double startRotation); + static int compareAngleStraightness(double straightAngle, double leftAngle, double rightAngle, + double leftStrikeFactor, double rightStrikeFactor); - Base::Vector2d computeLineOriginPoint(Base::Vector2d lineTarget, double projectedLabelDistance, - double lineAngle, double labelWidth, double direction) const; - Base::Vector2d getIsoJointPoint(Base::Vector2d labelCenter, double width, double dir) const; - Base::Vector2d getAsmeJointPoint(Base::Vector2d labelCenter, double width, double dir) const; + static double getIsoStandardLinePlacement(double labelAngle); + Base::Vector2d getIsoRefOutsetPoint(const Base::BoundBox2d &labelRectangle, bool right) const; + Base::Vector2d getIsoRefJointPoint(const Base::BoundBox2d &labelRectangle, bool right) const; + Base::Vector2d getAsmeRefOutsetPoint(const Base::BoundBox2d &labelRectangle, bool right) const; + Base::Vector2d getAsmeRefJointPoint(const Base::BoundBox2d &labelRectangle, bool right) const; + + static Base::Vector2d computePerpendicularIntersection(const Base::Vector2d &linePoint, + const Base::Vector2d &perpendicularPoint, double lineAngle); + static Base::Vector2d computeExtensionLinePoints(const Base::Vector2d &originPoint, + const Base::Vector2d &linePoint, double hintAngle, + double overhangSize, double gapSize, Base::Vector2d &startPoint); + static double computeLineAndLabelAngles(const Base::Vector2d &rotationCenter, const Base::Vector2d &labelCenter, + double lineLabelDistance, double &lineAngle, double &labelAngle); + static double computeLineStrikeFactor(const Base::BoundBox2d &labelRectangle, const Base::Vector2d &lineOrigin, + double lineAngle, const std::vector> &drawMarking); + static double computeArcStrikeFactor(const Base::BoundBox2d &labelRectangle, const Base::Vector2d &arcCenter, + double arcRadius, const std::vector> &drawMarking); + + static double normalizeStartPosition(double &startPosition, double &lineAngle); + static double normalizeStartRotation(double &startRotation); + bool constructDimensionLine(const Base::Vector2d &targetPoint, double lineAngle, + double startPosition, double jointPosition, const Base::BoundBox2d &labelRectangle, + int arrowCount, int standardStyle, bool flipArrows, + std::vector> &outputMarking) const; + bool constructDimensionArc(const Base::Vector2d &arcCenter, double arcRadius, double endAngle, + double startRotation, double handednessFactor, double jointRotation, + const Base::BoundBox2d &labelRectangle, int arrowCount, int standardStyle, + bool flipArrows, std::vector> &outputMarking) const; void draw() override; + + void resetArrows(void) const; + void drawArrows(int count, const Base::Vector2d positions[], double angles[], bool flipped) const; + + void drawSingleLine(QPainterPath &painterPath, const Base::Vector2d &lineOrigin, double lineAngle, + double startPosition, double endPosition) const; + void drawMultiLine(QPainterPath &painterPath, const Base::Vector2d &lineOrigin, double lineAngle, + const std::vector> &drawMarking) const; + void drawSingleArc(QPainterPath &painterPath, const Base::Vector2d &arcCenter, double arcRadius, + double startAngle, double endAngle) const; + void drawMultiArc(QPainterPath &painterPath, const Base::Vector2d &arcCenter, double arcRadius, + const std::vector> &drawMarking) const; + + void drawDimensionLine(QPainterPath &painterPath, const Base::Vector2d &targetPoint, double lineAngle, + double startPosition, double jointPosition, const Base::BoundBox2d &labelRectangle, + int arrowCount, int standardStyle, bool flipArrows) const; + void drawDimensionArc(QPainterPath &painterPath, const Base::Vector2d &arcCenter, double arcRadius, + double endAngle, double startRotation, double jointAngle, + const Base::BoundBox2d &labelRectangle, int arrowCount, + int standardStyle, bool flipArrows) const; + + void drawDistanceExecutive(const Base::Vector2d &startPoint, const Base::Vector2d &endPoint, double lineAngle, + const Base::BoundBox2d &labelRectangle, int standardStyle, int renderExtent, bool flipArrows) const; + void drawRadiusExecutive(const Base::Vector2d ¢erPoint, const Base::Vector2d &midPoint, double radius, + double endAngle, double startRotation, const Base::BoundBox2d &labelRectangle, + double centerOverhang, int standardStyle, int renderExtent, bool flipArrow) const; + + void drawDistance(TechDraw::DrawViewDimension *dimension, ViewProviderDimension *viewProvider) const; void drawRadius(TechDraw::DrawViewDimension *dimension, ViewProviderDimension *viewProvider) const; + void drawDiameter(TechDraw::DrawViewDimension *dimension, ViewProviderDimension *viewProvider) const; + void drawAngle(TechDraw::DrawViewDimension *dimension, ViewProviderDimension *viewProvider) const; virtual QVariant itemChange( GraphicsItemChange change, const QVariant &value ) override; @@ -186,26 +247,29 @@ protected: QGIArrow* aHead2; //QGICMark* centerMark double m_lineWidth; - bool m_obtuse; private: - static const double TextOffsetFudge; + static inline Base::Vector2d fromQtApp(const Base::Vector3d &v) { return Base::Vector2d(v.x, -v.y); } + static inline Base::BoundBox2d fromQtGui(const QRectF &r) + { return Base::BoundBox2d(Rez::appX(r.left()), -Rez::appX(r.top()), + Rez::appX(r.right()), -Rez::appX(r.bottom())); } - double getDefaultTextHorizontalOffset(double direction) const; - double getDefaultTextVerticalOffset() const; - double getDefaultReferenceLineOverhang() const; - double getDefaultHorizontalLeaderLength() const; + static inline QPointF toQtGui(const Base::Vector2d &v) { return QPointF(Rez::guiX(v.x), -Rez::guiX(v.y)); } + static inline QRectF toQtGui(const Base::BoundBox2d &r) + { return QRectF(Rez::guiX(r.MinX), -Rez::guiX(r.MaxY), + Rez::guiX(r.Width()), Rez::guiX(r.Height())); } - static bool angleWithinSector(double testAngle, double startAngle, double endAngle, bool clockwise); - static double addAngles(double angle1, double angle2); + static inline double toDeg(double a) { return a*180/M_PI; } + static inline double toQtRad(double a) { return -a; } + static inline double toQtDeg(double a) { return -a*180.0/M_PI; } - static const int INNER_SECTOR = 0; - static const int OUTER_SECTOR = 1; - static const int OPPOSITE_SECTOR = 2; - static const int COMPLEMENT_SECTOR = 3; + double getDefaultExtensionLineOverhang() const; + double getDefaultArrowTailLength() const; + double getDefaultIsoDimensionLineSpacing() const; + double getDefaultIsoReferenceLineOverhang() const; + double getDefaultAsmeHorizontalLeaderLength() const; + double getDefaultAsmeExtensionLineGap() const; - static int classifyPointToArcPosition(double pointDistance, double pointAngle, - double radius, double startAngle, double endAngle, bool clockwise); }; } // namespace MDIViewPageGui diff --git a/src/Mod/TechDraw/Gui/ViewProviderDimension.cpp b/src/Mod/TechDraw/Gui/ViewProviderDimension.cpp index cf6beccc43..b8490e95ef 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderDimension.cpp +++ b/src/Mod/TechDraw/Gui/ViewProviderDimension.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) 2004 Jürgen Riegel * + * Copyright (c) 2004 Jürgen Riegel * * Copyright (c) 2012 Luke Parry * * * * This file is part of the FreeCAD CAx development system. * @@ -44,7 +44,10 @@ using namespace TechDrawGui; const char *ViewProviderDimension::StandardAndStyleEnums[]= - { "ISO Oriented", "ISO Levelled", "ASME Regular", "ASME Inlined", NULL }; + { "ISO Oriented", "ISO Referencing", "ASME Inlined", "ASME Referencing", NULL }; + +const char *ViewProviderDimension::RenderingExtentEnums[]= + { "None", "Minimal", "Confined", "Reduced", "Normal", "Expanded", NULL }; PROPERTY_SOURCE(TechDrawGui::ViewProviderDimension, TechDrawGui::ViewProviderDrawingView) @@ -83,11 +86,12 @@ ViewProviderDimension::ViewProviderDimension() ADD_PROPERTY_TYPE(Color,(fcColor),group,App::Prop_None,"The color of the Dimension"); int standardStyle = hGrp->GetInt("StandardAndStyle", STD_STYLE_ISO_ORIENTED); - ADD_PROPERTY_TYPE(StandardAndStyle, (standardStyle), group, App::Prop_None, "Specify the standard according to which this dimension is drawn"); + ADD_PROPERTY_TYPE(StandardAndStyle, (standardStyle), group, App::Prop_None, "Specifies the standard according to which this dimension is drawn"); StandardAndStyle.setEnums(StandardAndStyleEnums); - ADD_PROPERTY_TYPE(ExtendToCenter, (true), group, App::Prop_None,"Prolong the leader line right upto the center point"); - ADD_PROPERTY_TYPE(FlipArrowheads, (false), group, App::Prop_None,"Reverse the normal direction of arrowheads on dimline"); + ADD_PROPERTY_TYPE(RenderingExtent, (REND_EXTENT_NORMAL), group, App::Prop_None,"Select the rendering mode by space requirements"); + RenderingExtent.setEnums(RenderingExtentEnums); + ADD_PROPERTY_TYPE(FlipArrowheads, (false), group, App::Prop_None,"Reverts the usual direction of dimension line terminators"); } ViewProviderDimension::~ViewProviderDimension() @@ -139,7 +143,7 @@ void ViewProviderDimension::onChanged(const App::Property* p) (p == &Fontsize) || (p == &LineWidth) || (p == &StandardAndStyle) || - (p == &ExtendToCenter) || + (p == &RenderingExtent) || (p == &FlipArrowheads)) { QGIView* qgiv = getQView(); diff --git a/src/Mod/TechDraw/Gui/ViewProviderDimension.h b/src/Mod/TechDraw/Gui/ViewProviderDimension.h index 09d2897db6..3ff80e0d61 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderDimension.h +++ b/src/Mod/TechDraw/Gui/ViewProviderDimension.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) 2004 Jürgen Riegel * + * Copyright (c) 2004 Jürgen Riegel * * Copyright (c) 2012 Luke Parry * * * * This file is part of the FreeCAD CAx development system. * @@ -49,13 +49,20 @@ public: App::PropertyFloat LineWidth; App::PropertyColor Color; - static const int STD_STYLE_ISO_ORIENTED = 0; - static const int STD_STYLE_ISO_LEVELLED = 1; - static const int STD_STYLE_ASME_REGULAR = 2; - static const int STD_STYLE_ASME_INLINED = 3; - + static const int STD_STYLE_ISO_ORIENTED = 0; + static const int STD_STYLE_ISO_REFERENCING = 1; + static const int STD_STYLE_ASME_INLINED = 2; + static const int STD_STYLE_ASME_REFERENCING = 3; App::PropertyEnumeration StandardAndStyle; - App::PropertyBool ExtendToCenter; + + static const int REND_EXTENT_NONE = 0; + static const int REND_EXTENT_MINIMAL = 1; + static const int REND_EXTENT_CONFINED = 2; + static const int REND_EXTENT_REDUCED = 3; + static const int REND_EXTENT_NORMAL = 4; + static const int REND_EXTENT_EXPANDED = 5; + App::PropertyEnumeration RenderingExtent; + App::PropertyBool FlipArrowheads; virtual void attach(App::DocumentObject *); @@ -71,6 +78,7 @@ public: private: static const char *StandardAndStyleEnums[]; + static const char *RenderingExtentEnums[]; };