From 46c73062fffb7e4cc27ae9d95ff7b4688c8a151f Mon Sep 17 00:00:00 2001 From: Tomas Pavlicek Date: Wed, 21 Aug 2019 21:43:08 +0200 Subject: [PATCH] TechDraw: Radius drawing refactored - ASME support --- src/Base/Tools2D.h | 11 + src/Mod/TechDraw/Gui/DlgPrefsTechDraw2.ui | 42 +- src/Mod/TechDraw/Gui/DlgPrefsTechDraw2Imp.cpp | 2 + src/Mod/TechDraw/Gui/QGIViewDimension.cpp | 585 +++++++++--------- src/Mod/TechDraw/Gui/QGIViewDimension.h | 36 +- src/Mod/TechDraw/Gui/Rez.cpp | 6 + src/Mod/TechDraw/Gui/Rez.h | 1 + .../TechDraw/Gui/ViewProviderDimension.cpp | 19 +- src/Mod/TechDraw/Gui/ViewProviderDimension.h | 27 +- 9 files changed, 413 insertions(+), 316 deletions(-) diff --git a/src/Base/Tools2D.h b/src/Base/Tools2D.h index cae01f290d..0ce86aa937 100644 --- a/src/Base/Tools2D.h +++ b/src/Base/Tools2D.h @@ -65,6 +65,7 @@ public: inline bool operator== (const Vector2d &rclVct) const; inline Vector2d operator+ (const Vector2d &rclVct) const; inline Vector2d operator- (const Vector2d &rclVct) const; + inline Vector2d operator* (double c) const; inline Vector2d operator/ (double c) const; inline void Set (double fPX, double fPY); @@ -238,6 +239,16 @@ inline double Vector2d::operator* (const Vector2d &rclVct) const return (x * rclVct.x) + (y * rclVct.y); } +inline Vector2d operator* (double c, const Vector2d &rclVct) +{ + return Vector2d(c * rclVct.x, c * rclVct.y); +} + +inline Vector2d Vector2d::operator* (double c) const +{ + return Vector2d(c * x, c * y); +} + inline Vector2d Vector2d::operator/ (double c) const { return Vector2d(x / c, y / c); diff --git a/src/Mod/TechDraw/Gui/DlgPrefsTechDraw2.ui b/src/Mod/TechDraw/Gui/DlgPrefsTechDraw2.ui index 6eca55cea9..a7b1d5399f 100644 --- a/src/Mod/TechDraw/Gui/DlgPrefsTechDraw2.ui +++ b/src/Mod/TechDraw/Gui/DlgPrefsTechDraw2.ui @@ -1,4 +1,4 @@ - + TechDrawGui::DlgPrefsTechDraw2Imp @@ -305,6 +305,46 @@ + + + + Dimensioning Standard and Style + + + + + + + Preferred standard and style of drawing dimensional values + + + StandardAndStyle + + + /Mod/TechDraw/Dimensions + + + + ISO Oriented + + + + + ISO Levelled + + + + + ASME Regular + + + + + ASME Inlined + + + + diff --git a/src/Mod/TechDraw/Gui/DlgPrefsTechDraw2Imp.cpp b/src/Mod/TechDraw/Gui/DlgPrefsTechDraw2Imp.cpp index 751b4436a7..73b73dbab8 100644 --- a/src/Mod/TechDraw/Gui/DlgPrefsTechDraw2Imp.cpp +++ b/src/Mod/TechDraw/Gui/DlgPrefsTechDraw2Imp.cpp @@ -62,6 +62,7 @@ void DlgPrefsTechDraw2Imp::saveSettings() leLineGroup->onSave(); pdsb_VertexScale->onSave(); pcb_VertexColor->onSave(); + pcbStandardAndStyle->onSave(); } void DlgPrefsTechDraw2Imp::loadSettings() @@ -83,6 +84,7 @@ void DlgPrefsTechDraw2Imp::loadSettings() leLineGroup->onRestore(); pdsb_VertexScale->onRestore(); pcb_VertexColor->onRestore(); + pcbStandardAndStyle->onRestore(); } /** diff --git a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp index 51f50fc954..902b9e9cb0 100644 --- a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp @@ -70,8 +70,7 @@ #include "DrawGuiUtil.h" #ifndef M_2PI -// #define M_2PI 6.283185307179586476925287 - #define M_2PI (M_PI * 2.0) + #define M_2PI ((M_PI) * 2.0) #endif //TODO: hide the Qt coord system (+y down). @@ -880,7 +879,7 @@ void QGIViewDimension::draw() double gap = (gapMargin * scaler); //sb % of radius? //offset of dimLine from getDimText() - double horizOffset = getDefaultTextHorizontalOffset(lblCenter.x > curveCenter.x); + double horizOffset = getDefaultTextHorizontalOffset(lblCenter.x > curveCenter.x ? -1.0 : +1.0); double vertOffset = getDefaultTextVerticalOffset(); bool outerPlacement = false; @@ -1175,13 +1174,7 @@ void QGIViewDimension::draw() // dim->getViewPart()->addVertex(curveCenter,true); // } } else if(strcmp(dimType, "Radius") == 0) { - if (prefRadiusAligned() == 0) { //ISO - drawRadiusAligned(dim, vp); - } else if (prefRadiusAligned() == 1) { //ASME - drawRadiusUniform(dim, vp); - } else { - Base::Console().Error("QGIVD::draw - bad radiusAligned pref: %d\n", prefRadiusAligned()); - } + drawRadius(dim, vp); } else if( (strcmp(dimType, "Angle") == 0) || (strcmp(dimType, "Angle3Pt") == 0)) { anglePoints pts = dim->getAnglePoints(); @@ -1379,28 +1372,22 @@ void QGIViewDimension::draw() } } -int QGIViewDimension::classifyPointToArcPosition(double pointDistance, double pointAngle, - double radius, double startAngle, double endAngle, bool clockwise) const +double QGIViewDimension::getIsoStandardLinePlacement(double labelAngle) { - 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; + // 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; } -double QGIViewDimension::computeLineAndLabelAngles(Base::Vector3d lineTarget, Base::Vector3d labelCenter, - double lineLabelDistance, double &lineAngle, double &labelAngle) const +double QGIViewDimension::computeLineAndLabelAngles(Base::Vector2d lineTarget, Base::Vector2d labelCenter, + double lineLabelDistance, double &lineAngle, double &labelAngle) { // By default horizontal line and no label rotation lineAngle = 0.0; labelAngle = 0.0; - Base::Vector3d rawDirection(labelCenter - lineTarget); + Base::Vector2d rawDirection(labelCenter - lineTarget); double rawDistance = rawDirection.Length(); if (rawDistance < Precision::Confusion()) { // Almost single point, can't tell return 0.0; @@ -1415,7 +1402,7 @@ double QGIViewDimension::computeLineAndLabelAngles(Base::Vector3d lineTarget, Ba } // Rotate the line by angle between the label rectangle center and label bottom side center - double devAngle = getStandardLinePlacement(rawAngle)*asin(lineLabelDistance/rawDistance); + double devAngle = getIsoStandardLinePlacement(rawAngle)*asin(lineLabelDistance/rawDistance); lineAngle = addAngles(lineAngle, devAngle); labelAngle = devAngle > 0.0 ? lineAngle : addAngles(lineAngle, M_PI); @@ -1423,30 +1410,75 @@ double QGIViewDimension::computeLineAndLabelAngles(Base::Vector3d lineTarget, Ba return devAngle; } -Base::Vector3d QGIViewDimension::computeLineOriginPoint(Base::Vector3d lineTarget, double projectedLabelDistance, +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 + return false; + } + + Base::Vector2d lineOrigin(rectangle.center().x(), rectangle.center().y()); + Base::Vector2d direction = targetPoint - lineOrigin; + + 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 (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; + + if (exitPoint.y >= rectangle.top() && exitPoint.y <= rectangle.bottom()) { + return true; + } + } + + return false; +} + +Base::Vector2d QGIViewDimension::computeLineOriginPoint(Base::Vector2d lineTarget, double projectedLabelDistance, double lineAngle, double labelWidth, double direction) const { return lineTarget + (projectedLabelDistance + direction*(0.5*labelWidth + getDefaultReferenceLineOverhang())) - *Base::Vector3d(cos(lineAngle), sin(lineAngle), 0.0); + *Base::Vector2d(cos(lineAngle), sin(lineAngle)); } -//draw a Radius dimension using "aligned" convention (ISO) -void QGIViewDimension::drawRadiusAligned(TechDraw::DrawViewDimension *dimension, - ViewProviderDimension *viewProvider) const +Base::Vector2d QGIViewDimension::getIsoJointPoint(Base::Vector2d labelCenter, double width, double dir) const +{ + return Base::Vector2d(labelCenter.x + dir*(width*0.5 + getDefaultReferenceLineOverhang()), + labelCenter.y + getDefaultTextVerticalOffset()); +} + +Base::Vector2d QGIViewDimension::getAsmeJointPoint(Base::Vector2d labelCenter, double width, double dir) const +{ + return Base::Vector2d(labelCenter.x + dir*(width*0.5 + getDefaultHorizontalLeaderLength()), + labelCenter.y + TextOffsetFudge); +} + +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 QPainterPath radiusPath; + datumLabel->setRotation(0.0); + aHead1->setRotation(0.0); aHead1->setFlipped(false); - datumLabel->setRotation(0.0); QRectF mappedRect = mapRectFromItem(datumLabel, datumLabel->boundingRect()); - Base::Vector3d labelCenter = Base::Vector3d(mappedRect.center().x(), mappedRect.center().y(), 0.0); + Base::Vector2d labelCenter = Base::Vector2d(mappedRect.center().x(), mappedRect.center().y()); arcPoints curvePoints = dimension->getArcPoints(); - Base::Vector3d curveCenter = Rez::guiX(curvePoints.center); + Base::Vector2d curveCenter = Rez::guiX(curvePoints.center, true); double mappedRadius = Rez::guiX(curvePoints.radius); double centerDistance = (labelCenter - curveCenter).Length(); @@ -1467,24 +1499,166 @@ void QGIViewDimension::drawRadiusAligned(TechDraw::DrawViewDimension *dimension, } double labelAngle = 0.0; - Base::Vector3d arcPoint; + Base::Vector2d arcPoint; double lineAngle; - //TODO: does this violate "Aligned" convention? - if (viewProvider->TiltText.getValue()) { // We may rotate the label so no reference line is needed + 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); + } + + double leftAngle = atan2(leftJoint.y - curveCenter.y, leftJoint.x - curveCenter.x); + double rightAngle = atan2(rightJoint.y - curveCenter.y, rightJoint.x - curveCenter.x); + + int leftPosition = classifyPointToArcPosition((leftJoint - curveCenter).Length(), + leftAngle, mappedRadius, arcStartAngle, arcEndAngle, arcClockwise); + int rightPosition = classifyPointToArcPosition((rightJoint - curveCenter).Length(), + rightAngle, mappedRadius, arcStartAngle, arcEndAngle, arcClockwise); + + 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; + } + 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; + } + + 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; + } + } + else { // Both joint points lay outside the vertical angles + arcPoint = Rez::guiX(curvePoints.midArc, true); + + 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; + } + + targetPoint = arcPoint; + lineAngle = atan2(targetPoint.y - jointPoint.y, targetPoint.x - jointPoint.x); + } + + radiusPath.moveTo(originPoint.x, originPoint.y); + radiusPath.lineTo(jointPoint.x, jointPoint.y); + radiusPath.lineTo(targetPoint.x, targetPoint.y); + } + 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); - Base::Vector3d originPoint; - Base::Vector3d targetPoint; + 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::Vector3d(cos(lineAngle), sin(lineAngle), 0.0); + arcPoint = curveCenter + mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle)); if (viewProvider->ExtendToCenter.getValue()) { // Start in the very center originPoint = curveCenter; @@ -1498,7 +1672,7 @@ void QGIViewDimension::drawRadiusAligned(TechDraw::DrawViewDimension *dimension, } case OUTER_SECTOR: { // Same situation as when on the inner side of sector - arcPoint = curveCenter + mappedRadius*Base::Vector3d(cos(lineAngle), sin(lineAngle), 0.0); + arcPoint = curveCenter + mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle)); aHead1->flip(); originPoint = computeLineOriginPoint(curveCenter, centerDistance, lineAngle, @@ -1510,7 +1684,7 @@ void QGIViewDimension::drawRadiusAligned(TechDraw::DrawViewDimension *dimension, 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::Vector3d(cos(lineAngle), sin(lineAngle), 0.0); + arcPoint = curveCenter - mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle)); aHead1->flip(); originPoint = computeLineOriginPoint(curveCenter, centerDistance, lineAngle, @@ -1520,7 +1694,7 @@ void QGIViewDimension::drawRadiusAligned(TechDraw::DrawViewDimension *dimension, } default: { // Label outside both arc wedges - arcPoint = Rez::guiX(curvePoints.midArc); + arcPoint = Rez::guiX(curvePoints.midArc, true); aHead1->flip(); devAngle = computeLineAndLabelAngles(arcPoint, labelCenter, getDefaultTextVerticalOffset(), lineAngle, labelAngle); @@ -1537,104 +1711,82 @@ void QGIViewDimension::drawRadiusAligned(TechDraw::DrawViewDimension *dimension, radiusPath.moveTo(originPoint.x, originPoint.y); radiusPath.lineTo(targetPoint.x, targetPoint.y); } - else { // The dimensional value text must stay horizontal - Base::Vector3d leftJoint(mappedRect.left() - getDefaultReferenceLineOverhang(), - labelCenter.y + getDefaultTextVerticalOffset(), 0.0); - Base::Vector3d rightJoint(mappedRect.right() + getDefaultReferenceLineOverhang(), - labelCenter.y + getDefaultTextVerticalOffset(), 0.0); + 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); - double leftAngle = atan2(leftJoint.y - curveCenter.y, leftJoint.x - curveCenter.x); - double rightAngle = atan2(rightJoint.y - curveCenter.y, rightJoint.x - curveCenter.x); + 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)); - int leftPosition = classifyPointToArcPosition((leftJoint - curveCenter).Length(), - leftAngle, mappedRadius, arcStartAngle, arcEndAngle, arcClockwise); - int rightPosition = classifyPointToArcPosition((rightJoint - curveCenter).Length(), - rightAngle, mappedRadius, arcStartAngle, arcEndAngle, arcClockwise); - - Base::Vector3d originPoint; - Base::Vector3d jointPoint; - Base::Vector3d 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) { // At least one line is not crossing the text - if (rightBend < leftBend) { - leftPosition = COMPLEMENT_SECTOR; - } + if (computeLineRectangleExitPoint(mappedRect, arcPoint, exitPoint)) { + radiusPath.moveTo(exitPoint.x, exitPoint.y); + radiusPath.lineTo(arcPoint.x, arcPoint.y); } - else { - 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; - } + if (viewProvider->ExtendToCenter.getValue() + && computeLineRectangleExitPoint(mappedRect, curveCenter, exitPoint)) { + radiusPath.moveTo(exitPoint.x, exitPoint.y); + radiusPath.lineTo(curveCenter.x, curveCenter.y); } - } - int resultPosition; - if (leftPosition >= OPPOSITE_SECTOR) { - originPoint = rightJoint; - jointPoint = leftJoint; - lineAngle = leftAngle; - resultPosition = leftPosition; - } - else { - originPoint = leftJoint; - jointPoint = rightJoint; - lineAngle = rightAngle; - resultPosition = rightPosition; + break; } + case OUTER_SECTOR: { + // Same situation as when on the inner side of sector + arcPoint = curveCenter + mappedRadius*Base::Vector2d(cos(lineAngle), sin(lineAngle)); - switch (resultPosition) { - case INNER_SECTOR: - arcPoint = curveCenter + mappedRadius*Base::Vector3d(cos(lineAngle), sin(lineAngle), 0.0); - targetPoint = arcPoint; - break; - case OUTER_SECTOR: - arcPoint = curveCenter + mappedRadius*Base::Vector3d(cos(lineAngle), sin(lineAngle), 0.0); - // If desired, extend the target point to the center - targetPoint = viewProvider->ExtendToCenter.getValue() ? curveCenter : arcPoint; + 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: - arcPoint = curveCenter - mappedRadius*Base::Vector3d(cos(lineAngle), sin(lineAngle), 0.0); - targetPoint = arcPoint; - aHead1->flip(); - break; + } + + 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; } } - else { // Both joint points lay outside the vertical angles - arcPoint = Rez::guiX(curvePoints.midArc); - - if (labelCenter.x >= arcPoint.x) { // Place the dimensional value right - originPoint = rightJoint; - jointPoint = leftJoint; - } - else { // Place the dimensional value left - originPoint = leftJoint; - jointPoint = rightJoint; - } - - targetPoint = arcPoint; - lineAngle = atan2(targetPoint.y - jointPoint.y, targetPoint.x - jointPoint.x); - } - - radiusPath.moveTo(originPoint.x, originPoint.y); - radiusPath.lineTo(jointPoint.x, jointPoint.y); - radiusPath.lineTo(targetPoint.x, targetPoint.y); + } + else { + Base::Console().Error("QGIVD::drawRadius - this Standard&Style is not supported: %d\n", standardStyle); + return; } datumLabel->setTransformOriginPoint(datumLabel->boundingRect().width()*0.5, @@ -1644,7 +1796,6 @@ void QGIViewDimension::drawRadiusAligned(TechDraw::DrawViewDimension *dimension, dimLines->setPath(radiusPath); aHead1->setPos(arcPoint.x, arcPoint.y); - aHead1->setRotation(0.0); aHead1->setDirMode(true); aHead1->setDirection(lineAngle); if (viewProvider->FlipArrowheads.getValue()) { @@ -1658,141 +1809,6 @@ void QGIViewDimension::drawRadiusAligned(TechDraw::DrawViewDimension *dimension, aHead2->hide(); } -//Draw a Radius dimension using "uniform" alignment convention (Ansi/Asme) -void QGIViewDimension::drawRadiusUniform(TechDraw::DrawViewDimension* dim, - TechDrawGui::ViewProviderDimension* viewProvider) const -{ - // preferred terminology: Dimension Text, Dimension Line(s), Extension Lines, Arrowheads - // radius gets 1 dimension line from the dimension text to a point on the curve - aHead1->setFlipped(false); - - Base::Vector3d pointOnCurve,curveCenter; - double radius; - arcPoints pts = dim->getArcPoints(); - bool isArc = pts.isArc; - radius = Rez::guiX(pts.radius); - curveCenter = Rez::guiX(pts.center); - pointOnCurve = Rez::guiX(pts.onCurve.first); - QRectF mappedRect = mapRectFromItem(datumLabel, datumLabel->boundingRect()); - Base::Vector3d lblCenter = Base::Vector3d(mappedRect.center().x(), mappedRect.center().y(), 0.0); - - // 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); - datumLabel->setRotation(0.0); //label is always right side up & horizontal - - //if inside the arc (len(DimLine < radius)) arrow goes from center to edge away from label - //if outside the arc arrow kinks, then goes to edge nearest label - bool outerPlacement = false; - if ((lblCenter - curveCenter).Length() > radius) { //label is outside circle - outerPlacement = true; - } - - Base::Vector3d dirDimLine = (lblCenter - curveCenter).Normalize(); - if (fabs(dirDimLine.Length()) < (Precision::Confusion())) { - dirDimLine = Base::Vector3d(-1.0,0.0,0.0); - } -// Base::Vector3d adjustDir = dirDimLine; //adjust line lengths for arrowheads - double dimLineAdjust = Rez::guiX(QGIArrow::getOverlapAdjust(QGIArrow::getPrefArrowStyle(), - QGIArrow::getPrefArrowSize())); - - Base::Vector3d dLineStart; - Base::Vector3d dLineEnd; //?? radius draws line from text to curve?? (diam is curve to text!) - Base::Vector3d kinkPoint; - double margin = Rez::guiX(5.f); //space around label - double kinkLength = Rez::guiX(5.0); //sb % of horizontal dist(lblCenter,curveCenter)??? - if (outerPlacement) { - double offset = getDefaultTextHorizontalOffset(lblCenter.x > curveCenter.x); - dLineStart.y = lblCenter.y; - dLineStart.x = lblCenter.x + offset; //start at right or left of label - kinkLength = (lblCenter.x < curveCenter.x) ? kinkLength : -kinkLength; - kinkPoint.y = dLineStart.y; - kinkPoint.x = dLineStart.x + kinkLength; - pointOnCurve = curveCenter + (kinkPoint - curveCenter).Normalize() * radius; - dLineEnd = pointOnCurve + (kinkPoint - curveCenter).Normalize() * dimLineAdjust; - if ((kinkPoint - curveCenter).Length() < radius) { - dirDimLine = (curveCenter - kinkPoint).Normalize(); - } else { - dirDimLine = (kinkPoint - curveCenter).Normalize(); - } - } else { - dLineStart = curveCenter - dirDimLine * margin; //just beyond centerpoint - pointOnCurve = curveCenter - dirDimLine * radius; - dLineEnd = pointOnCurve + dirDimLine * dimLineAdjust; - kinkPoint = dLineStart; //no kink - } - - //handle partial arc weird cases - if (isArc) { - Base::Vector3d midPt = Rez::guiX(pts.midArc); - Base::Vector3d startPt = Rez::guiX(pts.arcEnds.first); - Base::Vector3d endPt = Rez::guiX(pts.arcEnds.second); - if (outerPlacement && - !dim->leaderIntersectsArc(Rez::appX(curveCenter),Rez::appX(kinkPoint))) { //keep pathological case within arc - pointOnCurve = midPt; - } else if (!outerPlacement) { - if ((midPt - lblCenter).Length() > (midPt - curveCenter).Length()) { //label is farther than center - dirDimLine = dirDimLine * -1; - } - dLineStart = curveCenter + dirDimLine * margin; - pointOnCurve = curveCenter + dirDimLine * radius; - dLineEnd = pointOnCurve - dirDimLine * dimLineAdjust; - kinkPoint = dLineStart; - if (!dim->leaderIntersectsArc(Rez::appX(dLineStart),Rez::appX(pointOnCurve))) { //keep pathological case within arc - if ((pointOnCurve - endPt).Length() < (pointOnCurve - startPt).Length()) { - if (!pts.arcCW ) { - pointOnCurve = endPt; - } else { - pointOnCurve = startPt; - } - } else { - if (!pts.arcCW ) { - pointOnCurve = startPt; - } else { - pointOnCurve = endPt; - } - } - dLineStart = curveCenter + (pointOnCurve - curveCenter).Normalize() * margin; - dLineEnd = pointOnCurve - dirDimLine * dimLineAdjust; - kinkPoint = dLineStart; - } - } - } - - QPainterPath dLinePath; //radius dimension line path - dLinePath.moveTo(dLineStart.x, dLineStart.y); - dLinePath.lineTo(kinkPoint.x, kinkPoint.y); - dLinePath.lineTo(dLineEnd.x, dLineEnd.y); - - dimLines->setPath(dLinePath); - - Base::Vector3d ar1Pos = pointOnCurve; - Base::Vector3d dirArrowLine = (pointOnCurve - kinkPoint).Normalize(); - float arAngle = atan2(dirArrowLine.y, dirArrowLine.x) * 180 / M_PI; - - //NOTE: in this case aHead1->dirMode is false and Qt rotation is used to point arrowhead - aHead1->setPos(ar1Pos.x, ar1Pos.y); - aHead1->setDirMode(false); - aHead1->setRotation(arAngle); - if (viewProvider->FlipArrowheads.getValue()) { - aHead1->flip(); - } - aHead1->setStyle(QGIArrow::getPrefArrowStyle()); - aHead1->setSize(QGIArrow::getPrefArrowSize()); - aHead1->draw(); - aHead1->show(); - - aHead2->hide(); - -// code for showCenterMark as attribute of Dimension instead of View -// if (dim->CentreLines.getValue()) { -// curveCenterMark->setPos(curveCenter.x,curveCenter.y); -// centerMark->show(); -// dim->getViewPart()->addVertex(curveCenter,true); -// } -} - QColor QGIViewDimension::getNormalColor() { Base::Reference hGrp = App::GetApplication().GetUserParameter() @@ -1896,12 +1912,9 @@ void QGIViewDimension::drawBorder(void) const double QGIViewDimension::TextOffsetFudge = 2.0; -double QGIViewDimension::getDefaultTextHorizontalOffset(bool toLeft) const +double QGIViewDimension::getDefaultTextHorizontalOffset(double direction) const { - double offset = datumLabel->boundingRect().width()*0.5 + TextOffsetFudge*2.0; - -// return toLeft ? -offset - TextOffsetFudge*5.0: offset; - return toLeft ? -offset : offset; + return direction*(datumLabel->boundingRect().width()*0.5 + TextOffsetFudge*2.0); } double QGIViewDimension::getDefaultTextVerticalOffset() const @@ -1922,12 +1935,11 @@ double QGIViewDimension::getDefaultReferenceLineOverhang() const return 2.0*TextOffsetFudge; } -double QGIViewDimension::getStandardLinePlacement(double labelAngle) +double QGIViewDimension::getDefaultHorizontalLeaderLength() const { - // 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; + QFontMetrics fontMetrics(datumLabel->getFont()); + + return 1.5*fontMetrics.width(QChar::fromLatin1('M')); } bool QGIViewDimension::angleWithinSector(double testAngle, double startAngle, double endAngle, bool clockwise) @@ -1961,6 +1973,20 @@ double QGIViewDimension::addAngles(double angle1, double angle2) } } +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; +} + //frame, border, caption are never shown in QGIVD, so shouldn't be in bRect QRectF QGIViewDimension::boundingRect() const { @@ -2010,13 +2036,4 @@ void QGIViewDimension::setPens(void) aHead2->setWidth(m_lineWidth); } -int QGIViewDimension::prefRadiusAligned(void) -{ - int result = 0; - Base::Reference hGrp = App::GetApplication().GetUserParameter() - .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/Standards"); - result = hGrp->GetInt("RadiusAligned", 0); //default to ISO - return result; -} - #include diff --git a/src/Mod/TechDraw/Gui/QGIViewDimension.h b/src/Mod/TechDraw/Gui/QGIViewDimension.h index beafe7feea..5fa87b46fd 100644 --- a/src/Mod/TechDraw/Gui/QGIViewDimension.h +++ b/src/Mod/TechDraw/Gui/QGIViewDimension.h @@ -156,23 +156,19 @@ public Q_SLOTS: protected: - static const int INNER_SECTOR = 3; - static const int OUTER_SECTOR = 2; - static const int OPPOSITE_SECTOR = 1; - static const int COMPLEMENT_SECTOR = 0; + 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); - int classifyPointToArcPosition(double pointDistance, double pointAngle, - double radius, double startAngle, double endAngle, bool clockwise) const; - double computeLineAndLabelAngles(Base::Vector3d lineTarget, Base::Vector3d labelCenter, - double lineLabelDistance, double &lineAngle, double &labelAngle) const; - Base::Vector3d computeLineOriginPoint(Base::Vector3d lineTarget, double projectedLabelDistance, + 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; void draw() override; - void drawRadiusAligned(TechDraw::DrawViewDimension *dimension, - ViewProviderDimension *viewProvider) const; - void drawRadiusUniform(TechDraw::DrawViewDimension *dimension, - ViewProviderDimension *viewProvider) const; + void drawRadius(TechDraw::DrawViewDimension *dimension, ViewProviderDimension *viewProvider) const; virtual QVariant itemChange( GraphicsItemChange change, const QVariant &value ) override; @@ -182,8 +178,6 @@ protected: Base::Vector3d findIsoExt(Base::Vector3d isoDir); QString getPrecision(void); - int prefRadiusAligned(void); - protected: bool hasHover; QGIDatumLabel* datumLabel; //dimension text @@ -197,13 +191,21 @@ protected: private: static const double TextOffsetFudge; - double getDefaultTextHorizontalOffset(bool toLeft) const; + double getDefaultTextHorizontalOffset(double direction) const; double getDefaultTextVerticalOffset() const; double getDefaultReferenceLineOverhang() const; + double getDefaultHorizontalLeaderLength() const; - static double getStandardLinePlacement(double labelAngle); static bool angleWithinSector(double testAngle, double startAngle, double endAngle, bool clockwise); static double addAngles(double angle1, double angle2); + + static const int INNER_SECTOR = 0; + static const int OUTER_SECTOR = 1; + static const int OPPOSITE_SECTOR = 2; + static const int COMPLEMENT_SECTOR = 3; + + static int classifyPointToArcPosition(double pointDistance, double pointAngle, + double radius, double startAngle, double endAngle, bool clockwise); }; } // namespace MDIViewPageGui diff --git a/src/Mod/TechDraw/Gui/Rez.cpp b/src/Mod/TechDraw/Gui/Rez.cpp index 2ca2df25be..e75c0ca02e 100644 --- a/src/Mod/TechDraw/Gui/Rez.cpp +++ b/src/Mod/TechDraw/Gui/Rez.cpp @@ -61,6 +61,12 @@ Base::Vector3d Rez::guiX(Base::Vector3d v) return result; } +Base::Vector2d Rez::guiX(Base::Vector3d v, bool planar) +{ + Q_UNUSED(planar); + return Base::Vector2d(guiX(v.x), guiX(v.y)); +} + //turn Gui side value to App side value double Rez::appX(double x) { diff --git a/src/Mod/TechDraw/Gui/Rez.h b/src/Mod/TechDraw/Gui/Rez.h index 6d8e59f0e2..f35e48a76c 100644 --- a/src/Mod/TechDraw/Gui/Rez.h +++ b/src/Mod/TechDraw/Gui/Rez.h @@ -42,6 +42,7 @@ public: //turn App side value to Gui side value static double guiX(double x); static Base::Vector3d guiX(Base::Vector3d v); + static Base::Vector2d guiX(Base::Vector3d v, bool planar); //turn Gui side value to App side value static double appX(double x); static Base::Vector3d appX(Base::Vector3d v); diff --git a/src/Mod/TechDraw/Gui/ViewProviderDimension.cpp b/src/Mod/TechDraw/Gui/ViewProviderDimension.cpp index 6fd65c7576..cf6beccc43 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderDimension.cpp +++ b/src/Mod/TechDraw/Gui/ViewProviderDimension.cpp @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** * Copyright (c) 2004 Jürgen Riegel * * Copyright (c) 2012 Luke Parry * * * @@ -43,6 +43,9 @@ using namespace TechDrawGui; +const char *ViewProviderDimension::StandardAndStyleEnums[]= + { "ISO Oriented", "ISO Levelled", "ASME Regular", "ASME Inlined", NULL }; + PROPERTY_SOURCE(TechDrawGui::ViewProviderDimension, TechDrawGui::ViewProviderDrawingView) //************************************************************************** @@ -79,9 +82,12 @@ ViewProviderDimension::ViewProviderDimension() fcColor.setPackedValue(hGrp->GetUnsigned("Color", 0x00000000)); ADD_PROPERTY_TYPE(Color,(fcColor),group,App::Prop_None,"The color of the Dimension"); - ADD_PROPERTY_TYPE(FlipArrowheads, (false), group, App::Prop_None,"Reverse the normal direction of arrowheads on dimline"); - ADD_PROPERTY_TYPE(TiltText, (true), group, App::Prop_None,"Rotate the text label so it is parallel with dimline"); + 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"); + 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"); } ViewProviderDimension::~ViewProviderDimension() @@ -132,9 +138,10 @@ void ViewProviderDimension::onChanged(const App::Property* p) if ((p == &Font) || (p == &Fontsize) || (p == &LineWidth) || - (p == &FlipArrowheads) || - (p == &TiltText) || - (p == &ExtendToCenter)) { + (p == &StandardAndStyle) || + (p == &ExtendToCenter) || + (p == &FlipArrowheads)) + { QGIView* qgiv = getQView(); if (qgiv) { qgiv->updateView(true); diff --git a/src/Mod/TechDraw/Gui/ViewProviderDimension.h b/src/Mod/TechDraw/Gui/ViewProviderDimension.h index 9daf9c6b43..09d2897db6 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderDimension.h +++ b/src/Mod/TechDraw/Gui/ViewProviderDimension.h @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** * Copyright (c) 2004 Jürgen Riegel * * Copyright (c) 2012 Luke Parry * * * @@ -44,13 +44,19 @@ public: /// destructor virtual ~ViewProviderDimension(); - App::PropertyFont Font; - App::PropertyLength Fontsize; - App::PropertyFloat LineWidth; - App::PropertyColor Color; - App::PropertyBool FlipArrowheads; - App::PropertyBool TiltText; - App::PropertyBool ExtendToCenter; + App::PropertyFont Font; + App::PropertyLength Fontsize; + 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; + + App::PropertyEnumeration StandardAndStyle; + App::PropertyBool ExtendToCenter; + App::PropertyBool FlipArrowheads; virtual void attach(App::DocumentObject *); virtual void setDisplayMode(const char* ModeName); @@ -61,6 +67,11 @@ public: virtual void onChanged(const App::Property* p); virtual TechDraw::DrawViewDimension* getViewObject() const; + +private: + + static const char *StandardAndStyleEnums[]; + }; } // namespace TechDrawGui