From 75f2a1d69ac5510cd2c21146d265ce0fb6865d30 Mon Sep 17 00:00:00 2001 From: Ajinkya Dahale Date: Wed, 21 Sep 2022 20:26:56 +0530 Subject: [PATCH] [Sketcher] Use tangent at B-spline knot constraint Also squashes: [Sketcher] Make tangent-at-knot with just knot and line [Sketcher] Disallow tangent at C0 knot If passed on to planegcs can cause segmentation fault. [Sketcher] (Re-)Support tangent at B-spline end-knots New code had introduced problems for non-periodic spline end-points, and periodic spline "end-points" were not supported anyway. (here end-points mean star/end points) --- src/Mod/Sketcher/App/Sketch.cpp | 42 +++++++++++++++++++++ src/Mod/Sketcher/App/SketchObject.cpp | 5 ++- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 39 ++++++++++++++++--- src/Mod/Sketcher/Gui/Utils.cpp | 16 +++++++- 4 files changed, 94 insertions(+), 8 deletions(-) diff --git a/src/Mod/Sketcher/App/Sketch.cpp b/src/Mod/Sketcher/App/Sketch.cpp index 2b3d3d11ac..86a92c2d51 100644 --- a/src/Mod/Sketcher/App/Sketch.cpp +++ b/src/Mod/Sketcher/App/Sketch.cpp @@ -2454,6 +2454,48 @@ int Sketch::addAngleAtPointConstraint( if(avp) geoId3 = checkGeoId(geoId3); + if ((Geoms[geoId1].type == BSpline && Geoms[geoId2].type == Line) || + (Geoms[geoId1].type == Line && Geoms[geoId2].type == BSpline)) { + if (cTyp == Tangent) { + if (Geoms[geoId1].type == Line && Geoms[geoId2].type == BSpline) { + std::swap(geoId1, geoId2); + std::swap(pos1, pos2); + } + GCS::BSpline &b = BSplines[Geoms[geoId1].index]; + GCS::Line &l = Lines[Geoms[geoId2].index]; + size_t knotindex = b.knots.size(); + if (avp) { + auto knotIt = std::find(b.knotpointGeoids.begin(), + b.knotpointGeoids.end(), geoId3); + knotindex = + std::distance(b.knotpointGeoids.begin(), knotIt); + } + else { + knotindex = (pos1 == PointPos::start) ? 0 : (b.knots.size() - 1); + } + if (knotindex >= b.knots.size()) + return -1; + + if (b.mult[knotindex] >= b.degree) { + // Leave handling of start/end of non-periodic B-splines to legacy code + if (b.periodic || + (pos1 != PointPos::start && pos1 != PointPos::end)) { + Base::Console().Error("addAngleAtPointConstraint: cannot set constraint when B-spline slope is discontinuous at knot!\n"); + return -1; + } + } + else { + int tag; + if(e2c) + tag = Sketch::addPointOnObjectConstraint(geoId1, pos1, geoId2, driving);//increases ConstraintsCounter + else + tag = ++ConstraintsCounter; + GCSsys.addConstraintTangentAtBSplineKnot(b, l, knotindex, tag, driving); + return ConstraintsCounter; + } + } + } + if (Geoms[geoId1].type == Point || Geoms[geoId2].type == Point){ Base::Console().Error("addAngleAtPointConstraint: one of the curves is a point!\n"); diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 7258de3c30..b3a839b72d 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -8492,7 +8492,7 @@ int SketchObject::port_reversedExternalArcs(bool justAnalyze) /// false - fail (this indicates an error, or that a constraint locking isn't supported). bool SketchObject::AutoLockTangencyAndPerpty(Constraint *cstr, bool bForce, bool bLock) { - try{ + try { //assert ( cstr->Type == Tangent || cstr->Type == Perpendicular); if(cstr->getValue() != 0.0 && ! bForce) /*tangency type already set. If not bForce - don't touch.*/ return true; @@ -8536,7 +8536,8 @@ bool SketchObject::AutoLockTangencyAndPerpty(Constraint *cstr, bool bForce, bool cstr->setValue(angleDesire + angleOffset); //external tangency. The angle stored is offset by Pi/2 so that a value of 0.0 is invalid and treated as "undecided". } } - } catch (Base::Exception& e){ + } + catch (Base::Exception& e){ //failure to determine tangency type is not a big deal, so a warning. Base::Console().Warning("Error in AutoLockTangency. %s \n", e.what()); return false; diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 4b3d717a9a..ec3e50a336 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -4198,7 +4198,8 @@ void CmdSketcherConstrainTangent::activated(int iMsg) }; strError = QObject::tr("With 3 objects, there must be 2 curves and 1 point.", "tangent constraint"); - } else if (SubNames.size() == 2) { + } + else if (SubNames.size() == 2) { if (isVertex(GeoId1,PosId1) && isVertex(GeoId2,PosId2)) { // endpoint-to-endpoint tangency @@ -4218,16 +4219,44 @@ void CmdSketcherConstrainTangent::activated(int iMsg) return; } else if ((isVertex(GeoId1,PosId1) && isEdge(GeoId2,PosId2)) || - (isEdge(GeoId1,PosId1) && isVertex(GeoId2,PosId2))) { // endpoint-to-curve tangency + (isEdge(GeoId1,PosId1) && isVertex(GeoId2,PosId2))) { // endpoint-to-curve/knot-to-curve tangency if (isVertex(GeoId2,PosId2)) { std::swap(GeoId1,GeoId2); std::swap(PosId1,PosId2); } if (isSimpleVertex(Obj, GeoId1, PosId1)) { - QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), - QObject::tr("Cannot add a tangency constraint at an unconnected point!")); - return; + if (isBsplineKnot(Obj, GeoId1)) { + // find the B-spline and treat as TangentViaPoint + openCommand(QT_TRANSLATE_NOOP("Command", "Add tangent constraint")); + const std::vector &constraints = Obj->Constraints.getValues(); + for (const auto& constraint: constraints) { + // TODO: wrap around with try-catch + if (constraint->Type == Sketcher::ConstraintType::InternalAlignment && + constraint->First == GeoId1 && + constraint->AlignmentType == Sketcher::InternalAlignmentType::BSplineKnotPoint) { + int GeoId3 = constraint->Second; + // TODO: ensure C1 continuity at point + if(! IsPointAlreadyOnCurve(GeoId2, GeoId1, PosId1, Obj)){ + Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ", + GeoId1,static_cast(PosId1),GeoId2); + } + + Gui::cmdAppObjectArgs(selection[0].getObject(), "addConstraint(Sketcher.Constraint('TangentViaPoint',%d,%d,%d,%d)) ", + GeoId3,GeoId2,GeoId1,static_cast(PosId1)); + commitCommand(); + tryAutoRecompute(Obj); + + getSelection().clearSelection(); + return; + } + } + } + else { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), + QObject::tr("Cannot add a tangency constraint at an unconnected point!")); + return; + } } const Part::Geometry *geom2 = Obj->getGeometry(GeoId2); diff --git a/src/Mod/Sketcher/Gui/Utils.cpp b/src/Mod/Sketcher/Gui/Utils.cpp index 9be430a592..c7220f9c3f 100644 --- a/src/Mod/Sketcher/Gui/Utils.cpp +++ b/src/Mod/Sketcher/Gui/Utils.cpp @@ -226,8 +226,22 @@ bool SketcherGui::IsPointAlreadyOnCurve(int GeoIdCurve, int GeoIdPoint, Sketcher //Simple geometric test seems to be the best, because a point can be // constrained to a curve in a number of ways (e.g. it is an endpoint of an // arc, or is coincident to endpoint of an arc, or it is an endpoint of an - // ellipse's majopr diameter line). Testing all those possibilities is way + // ellipse's major diameter line). Testing all those possibilities is way // too much trouble, IMO(DeepSOIC). + // One exception: check for knots on their B-splines, at least until point on B-spline is implemented. (Ajinkya) + if (isBsplineKnot(Obj, GeoIdPoint)) { + const Part::Geometry *geoCurve = Obj->getGeometry(GeoIdCurve); + if (geoCurve->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()) { + const std::vector &constraints = Obj->Constraints.getValues(); + for (const auto& constraint: constraints) { + if (constraint->Type == Sketcher::ConstraintType::InternalAlignment && + constraint->First == GeoIdPoint && + constraint->Second == GeoIdCurve) + return true; + } + } + } + Base::Vector3d p = Obj->getPoint(GeoIdPoint, PosIdPoint); return Obj->isPointOnCurve(GeoIdCurve, p.x, p.y); }