diff --git a/src/Gui/SoDatumLabel.cpp b/src/Gui/SoDatumLabel.cpp index c019674f65..2e2b44b3c4 100644 --- a/src/Gui/SoDatumLabel.cpp +++ b/src/Gui/SoDatumLabel.cpp @@ -91,6 +91,8 @@ SoDatumLabel::SoDatumLabel() SO_NODE_ADD_FIELD(param1, (0.f)); SO_NODE_ADD_FIELD(param2, (0.f)); + SO_NODE_ADD_FIELD(param4, (0.f)); + SO_NODE_ADD_FIELD(param5, (0.f)); useAntialiasing = true; @@ -994,6 +996,13 @@ void SoDatumLabel::GLRender(SoGLRenderAction * action) SbVec3f p2 = points[1]; SbVec3f dir = (p2-p1); + SbVec3f center = p1; + double radius = (p2 - p1).length(); + if (this->datumtype.getValue() == DIAMETER) { + center = (p1 + p2) / 2; + radius = radius / 2; + } + dir.normalize(); SbVec3f normal (-dir[1],dir[0],0); @@ -1053,7 +1062,22 @@ void SoDatumLabel::GLRender(SoGLRenderAction * action) glVertex2f(ar1_1[0], ar1_1[1]); glVertex2f(ar2_1[0], ar2_1[1]); glEnd(); + } + // Draw arc helper if needed + float startangle = this->param3.getValue(); + float range = this->param4.getValue(); + if (range != 0.0) { + int countSegments = std::max(6, abs(int(50.0 * range / (2 * M_PI)))); + double segment = range / (countSegments - 1); + + glBegin(GL_LINE_STRIP); + for (int i = 0; i < countSegments; i++) { + double theta = startangle + segment * i; + SbVec3f v1 = center + SbVec3f(radius * cos(theta), radius * sin(theta), 0); + glVertex2f(v1[0], v1[1]); + } + glEnd(); } } @@ -1061,11 +1085,17 @@ void SoDatumLabel::GLRender(SoGLRenderAction * action) // Only the angle intersection point is needed SbVec3f p0 = points[0]; + float margin = this->imgHeight / 4.0; + // Load the Parameters float length = this->param1.getValue(); float startangle = this->param2.getValue(); float range = this->param3.getValue(); float endangle = startangle + range; + float endLineLength1 = std::max(this->param4.getValue(), margin); + float endLineLength2 = std::max(this->param5.getValue(), margin); + float endLineLength12 = std::max(- this->param4.getValue(), margin); + float endLineLength22 = std::max(- this->param5.getValue(), margin); float r = 2*length; @@ -1089,7 +1119,6 @@ void SoDatumLabel::GLRender(SoGLRenderAction * action) textOffset = p0 + v0 * r; - float margin = this->imgHeight / 4.0; // Draw glBegin(GL_LINE_STRIP); @@ -1113,10 +1142,10 @@ void SoDatumLabel::GLRender(SoGLRenderAction * action) SbVec3f v1(cos(startangle),sin(startangle),0); SbVec3f v2(cos(endangle),sin(endangle),0); - SbVec3f pnt1 = p0+(r-margin)*v1; - SbVec3f pnt2 = p0+(r+margin)*v1; - SbVec3f pnt3 = p0+(r-margin)*v2; - SbVec3f pnt4 = p0+(r+margin)*v2; + SbVec3f pnt1 = p0 + (r - endLineLength1) * v1; + SbVec3f pnt2 = p0 + (r + endLineLength12) * v1; + SbVec3f pnt3 = p0 + (r - endLineLength2) * v2; + SbVec3f pnt4 = p0 + (r + endLineLength22) * v2; glBegin(GL_LINES); glVertex2f(pnt1[0],pnt1[1]); diff --git a/src/Gui/SoDatumLabel.h b/src/Gui/SoDatumLabel.h index 7f4700555c..077069236a 100644 --- a/src/Gui/SoDatumLabel.h +++ b/src/Gui/SoDatumLabel.h @@ -75,6 +75,8 @@ public: SoSFFloat param1; SoSFFloat param2; SoSFFloat param3; + SoSFFloat param4; + SoSFFloat param5; SoMFVec3f pnts; SoSFVec3f norm; SoSFImage image; diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index fca1a0abf9..a3c35d1c82 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -664,7 +664,12 @@ void SketchObject::reverseAngleConstraintToSupplementary(Constraint* constr, int { std::swap(constr->First, constr->Second); std::swap(constr->FirstPos, constr->SecondPos); - constr->FirstPos = (constr->FirstPos == Sketcher::PointPos::start) ? Sketcher::PointPos::end : Sketcher::PointPos::start; + if (constr->FirstPos == constr->SecondPos) { + constr->FirstPos = (constr->FirstPos == Sketcher::PointPos::start) ? Sketcher::PointPos::end : Sketcher::PointPos::start; + } + else { + constr->SecondPos = (constr->SecondPos == Sketcher::PointPos::start) ? Sketcher::PointPos::end : Sketcher::PointPos::start; + } // Edit the expression if any, else modify constraint value directly if (constraintHasExpression(constNum)) { @@ -677,6 +682,12 @@ void SketchObject::reverseAngleConstraintToSupplementary(Constraint* constr, int } } +void SketchObject::inverseAngleConstraint(Constraint* constr) +{ + constr->FirstPos = (constr->FirstPos == Sketcher::PointPos::start) ? Sketcher::PointPos::end : Sketcher::PointPos::start; + constr->SecondPos = (constr->SecondPos == Sketcher::PointPos::start) ? Sketcher::PointPos::end : Sketcher::PointPos::start; +} + bool SketchObject::constraintHasExpression(int constNum) const { App::ObjectIdentifier path = Constraints.createPath(constNum); diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index f153ce3646..5e78fe4f16 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -271,6 +271,7 @@ public: /// Change an angle constraint to its supplementary angle. void reverseAngleConstraintToSupplementary(Constraint* constr, int constNum); + void inverseAngleConstraint(Constraint* constr); /// Modify an angle constraint expression string to its supplementary angle static std::string reverseAngleConstraintExpression(std::string expression); diff --git a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp index 68b5bc2b8c..c8eb0a17d5 100644 --- a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp +++ b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp @@ -1178,7 +1178,10 @@ Restart: || Constr->Second == GeoEnum::GeoUndef); SbVec3f p0; + double distance = Constr->LabelDistance; double startangle, range; + double endLineLength1 = 0.0; + double endLineLength2 = 0.0; if (Constr->Second != GeoEnum::GeoUndef) { Base::Vector3d dir1, dir2; if (Constr->Third == GeoEnum::GeoUndef) { // angle between two lines @@ -1186,46 +1189,48 @@ Restart: geolistfacade.getGeometryFromGeoId(Constr->First); const Part::Geometry* geo2 = geolistfacade.getGeometryFromGeoId(Constr->Second); - if (geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId() - || geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId()) { + if (!isLineSegment(*geo1) || !isLineSegment(*geo2)) { break; } - const Part::GeomLineSegment* lineSeg1 = - static_cast(geo1); - const Part::GeomLineSegment* lineSeg2 = - static_cast(geo2); + auto* line1 = static_cast(geo1); + auto* line2 = static_cast(geo2); bool flip1 = (Constr->FirstPos == PointPos::end); bool flip2 = (Constr->SecondPos == PointPos::end); dir1 = (flip1 ? -1. : 1.) - * (lineSeg1->getEndPoint() - lineSeg1->getStartPoint()); + * (line1->getEndPoint() - line1->getStartPoint()).Normalize(); dir2 = (flip2 ? -1. : 1.) - * (lineSeg2->getEndPoint() - lineSeg2->getStartPoint()); + * (line2->getEndPoint() - line2->getStartPoint()).Normalize(); Base::Vector3d pnt1 = - flip1 ? lineSeg1->getEndPoint() : lineSeg1->getStartPoint(); + flip1 ? line1->getEndPoint() : line1->getStartPoint(); Base::Vector3d pnt2 = - flip2 ? lineSeg2->getEndPoint() : lineSeg2->getStartPoint(); + flip2 ? line2->getEndPoint() : line2->getStartPoint(); + Base::Vector3d pnt12 = + flip1 ? line1->getStartPoint() : line1->getEndPoint(); + Base::Vector3d pnt22 = + flip2 ? line2->getStartPoint() : line2->getEndPoint(); // line-line intersection + Base::Vector3d intersection; { double det = dir1.x * dir2.y - dir1.y * dir2.x; if ((det > 0 ? det : -det) < 1e-10) { // lines are coincident (or parallel) and in this case the // center of the point pairs with the shortest distance is used Base::Vector3d p1[2], p2[2]; - p1[0] = lineSeg1->getStartPoint(); - p1[1] = lineSeg1->getEndPoint(); - p2[0] = lineSeg2->getStartPoint(); - p2[1] = lineSeg2->getEndPoint(); + p1[0] = line1->getStartPoint(); + p1[1] = line1->getEndPoint(); + p2[0] = line2->getStartPoint(); + p2[1] = line2->getEndPoint(); double length = DBL_MAX; for (int i = 0; i <= 1; i++) { for (int j = 0; j <= 1; j++) { double tmp = (p2[j] - p1[i]).Length(); if (tmp < length) { length = tmp; - p0.setValue((p2[j].x + p1[i].x) / 2, - (p2[j].y + p1[i].y) / 2, - 0); + double x = (p2[j].x + p1[i].x) / 2; + double y = (p2[j].y + p1[i].y) / 2; + intersection = Base::Vector3d(x, y, 0.); } } } @@ -1235,12 +1240,24 @@ Restart: double c2 = dir2.y * pnt2.x - dir2.x * pnt2.y; double x = (dir1.x * c2 - dir2.x * c1) / det; double y = (dir1.y * c2 - dir2.y * c1) / det; - p0 = SbVec3f(x, y, 0); + intersection = Base::Vector3d(x, y, 0.); } } + p0.setValue(intersection.x, intersection.y, 0.); range = Constr->getValue(); // WYSIWYG startangle = atan2(dir1.y, dir1.x); + Base::Vector3d vl1 = dir1 * 2 * distance - (pnt1 - intersection); + Base::Vector3d vl2 = dir2 * 2 * distance - (pnt2 - intersection); + Base::Vector3d vl12 = dir1 * 2 * distance - (pnt12 - intersection); + Base::Vector3d vl22 = dir2 * 2 * distance - (pnt22 - intersection); + + endLineLength1 = vl12.Dot(dir1) > 0 ? vl12.Length() + : vl1.Dot(dir1) < 0 ? -vl1.Length() + : 0.0; + endLineLength2 = vl22.Dot(dir2) > 0 ? vl22.Length() + : vl2.Dot(dir2) < 0 ? -vl2.Length() + : 0.0; } else { // angle-via-point Base::Vector3d p = @@ -1297,9 +1314,11 @@ Restart: asciiText->string = SbString(getPresentationString(Constr).toUtf8().constData()); asciiText->datumtype = SoDatumLabel::ANGLE; - asciiText->param1 = Constr->LabelDistance; + asciiText->param1 = distance; asciiText->param2 = startangle; asciiText->param3 = range; + asciiText->param4 = endLineLength1; + asciiText->param5 = endLineLength2; asciiText->pnts.setNum(2); SbVec3f* verts = asciiText->pnts.startEditing(); @@ -1313,27 +1332,42 @@ Restart: assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); Base::Vector3d pnt1(0., 0., 0.), pnt2(0., 0., 0.); + double helperStartAngle = 0.; + double helperRange = 0.; + if (Constr->First != GeoEnum::GeoUndef) { const Part::Geometry* geo = geolistfacade.getGeometryFromGeoId(Constr->First); if (geo->is()) { - const Part::GeomArcOfCircle* arc = - static_cast(geo); + auto* arc = static_cast(geo); double radius = arc->getRadius(); double angle = (double)Constr->LabelPosition; + double startAngle, endAngle; + arc->getRange(startAngle, endAngle, /*emulateCCW=*/true); if (angle == 10) { - double startangle, endangle; - arc->getRange(startangle, endangle, /*emulateCCW=*/true); - angle = (startangle + endangle) / 2; + angle = (startAngle + endAngle) / 2; + } + if (!(angle > startAngle && angle < endAngle)) { + if (angle < startAngle + && startAngle - angle < angle + 2 * M_PI - endAngle) { + helperStartAngle = angle; + helperRange = startAngle - angle; + } + else { + if (angle < endAngle) { + angle += 2 * M_PI; + } + helperStartAngle = endAngle; + helperRange = angle - endAngle; + } } Base::Vector3d center = arc->getCenter(); pnt1 = center - radius * Base::Vector3d(cos(angle), sin(angle), 0.); pnt2 = center + radius * Base::Vector3d(cos(angle), sin(angle), 0.); } else if (geo->is()) { - const Part::GeomCircle* circle = - static_cast(geo); + auto* circle = static_cast(geo); double radius = circle->getRadius(); double angle = (double)Constr->LabelPosition; if (angle == 10) { @@ -1364,6 +1398,8 @@ Restart: asciiText->datumtype = SoDatumLabel::DIAMETER; asciiText->param1 = Constr->LabelDistance; asciiText->param2 = Constr->LabelPosition; + asciiText->param3 = helperStartAngle; + asciiText->param4 = helperRange; asciiText->pnts.setNum(2); SbVec3f* verts = asciiText->pnts.startEditing(); @@ -1378,27 +1414,41 @@ Restart: assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); Base::Vector3d pnt1(0., 0., 0.), pnt2(0., 0., 0.); + double helperStartAngle = 0.; + double helperRange = 0.; if (Constr->First != GeoEnum::GeoUndef) { const Part::Geometry* geo = geolistfacade.getGeometryFromGeoId(Constr->First); if (geo->is()) { - const Part::GeomArcOfCircle* arc = - static_cast(geo); + auto* arc = static_cast(geo); double radius = arc->getRadius(); double angle = (double)Constr->LabelPosition; + double startAngle, endAngle; + arc->getRange(startAngle, endAngle, /*emulateCCW=*/true); if (angle == 10) { - double startangle, endangle; - arc->getRange(startangle, endangle, /*emulateCCW=*/true); - angle = (startangle + endangle) / 2; + angle = (startAngle + endAngle) / 2; + } + if (!(angle > startAngle && angle < endAngle)) { + if (angle < startAngle + && startAngle - angle < angle + 2 * M_PI - endAngle) { + helperStartAngle = angle; + helperRange = startAngle - angle; + } + else { + if (angle < endAngle) { + angle += 2 * M_PI; + } + helperStartAngle = endAngle; + helperRange = angle - endAngle; + } } pnt1 = arc->getCenter(); pnt2 = pnt1 + radius * Base::Vector3d(cos(angle), sin(angle), 0.); } else if (geo->is()) { - const Part::GeomCircle* circle = - static_cast(geo); + auto* circle = static_cast(geo); auto gf = GeometryFacade::getFacade(geo); double radius; @@ -1439,6 +1489,8 @@ Restart: asciiText->datumtype = SoDatumLabel::RADIUS; asciiText->param1 = Constr->LabelDistance; asciiText->param2 = Constr->LabelPosition; + asciiText->param3 = helperStartAngle; + asciiText->param4 = helperRange; asciiText->pnts.setNum(2); SbVec3f* verts = asciiText->pnts.startEditing(); diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index f9716bc12b..abb44f2ec2 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -1918,8 +1918,12 @@ void ViewProviderSketch::moveAngleConstraint(int constNum, const Base::Vector2d& sign2 = isLeftOfLine(p21, p22, ap3); } + bool inverse = !(sign1 == sign3 && sign2 == sign4); + if (inverse) { + obj->inverseAngleConstraint(constr); + } + p0 = Base::Vector3d(intersection.x, intersection.y, 0.); - factor *= (sign1 == sign3 && sign2 == sign4) ? 1. : -1.; } else {// angle-via-point Base::Vector3d p = getSolvedSketch().getPoint(constr->Third, constr->ThirdPos);