From 65b4dd10ae53a85802283ec543e05c4e3ee0cc83 Mon Sep 17 00:00:00 2001 From: Ajinkya Dahale Date: Wed, 5 Apr 2023 23:11:28 +0530 Subject: [PATCH] [Sketcher] Expose general tangency with B-splines to Sketcher The following commits were squashed into this [Sketcher] Handle some corner cases in AngleViaPoint [Sketcher] Avoid redundant constraints with B-splines... When involving tangent, perpendicular and angle constraints. [Sketcher] Add pre-commit changes [Sketcher] Do not allow 2-selection tangent with B-spline Also... [Sketcher] Report error when using direct tangency with B-splines [Sketcher] Fix malformed constraint when B-spline is selected second To clarify, this means the second curve selected. The position of the point in selection order does not matter in angle-via-point. [Sketcher] Fix wrong number for B-Spline tangent on redundancy [Sketcher] Remove existing point-on-object in some redundant cases Particularly when point constrained on a B-spline is being used for tangent, perpendicular or angle via point with the same B-spline. [Sketcher] Fix direction issue with B-spline tangents. Without these changes the solver might try to "twist" the B-spline to make the angle between curves be 0 instead of PI (which may be closer to the initial shape). --- src/Mod/Sketcher/App/Sketch.cpp | 153 +++++- src/Mod/Sketcher/App/Sketch.h | 2 + src/Mod/Sketcher/App/SketchObject.cpp | 15 +- src/Mod/Sketcher/App/planegcs/GCS.cpp | 30 ++ src/Mod/Sketcher/App/planegcs/GCS.h | 15 + src/Mod/Sketcher/App/planegcs/Geo.cpp | 178 ++++--- src/Mod/Sketcher/Gui/CommandConstraints.cpp | 522 +++++++++++--------- 7 files changed, 611 insertions(+), 304 deletions(-) diff --git a/src/Mod/Sketcher/App/Sketch.cpp b/src/Mod/Sketcher/App/Sketch.cpp index 46d8ffd78e..df487b2b23 100644 --- a/src/Mod/Sketcher/App/Sketch.cpp +++ b/src/Mod/Sketcher/App/Sketch.cpp @@ -2868,6 +2868,11 @@ int Sketch::addTangentConstraint(int geoId1, int geoId2) GCSsys.addConstraintTangent(l, a, tag); return ConstraintsCounter; } + else if (Geoms[geoId2].type == BSpline) { + Base::Console().Error("Direct tangency constraint between line and B-spline is not " + "supported. Use tangent-via-point instead."); + return -1; + } } else if (Geoms[geoId1].type == Circle) { GCS::Circle& c = Circles[Geoms[geoId1].index]; @@ -2888,6 +2893,11 @@ int Sketch::addTangentConstraint(int geoId1, int geoId2) GCSsys.addConstraintTangent(c, a, tag); return ConstraintsCounter; } + else if (Geoms[geoId2].type == BSpline) { + Base::Console().Error("Direct tangency constraint between circle and B-spline is not " + "supported. Use tangent-via-point instead."); + return -1; + } } else if (Geoms[geoId1].type == Ellipse) { if (Geoms[geoId2].type == Circle) { @@ -2900,6 +2910,11 @@ int Sketch::addTangentConstraint(int geoId1, int geoId2) "supported. Use tangent-via-point instead."); return -1; } + else if (Geoms[geoId2].type == BSpline) { + Base::Console().Error("Direct tangency constraint between ellipse and B-spline is not " + "supported. Use tangent-via-point instead."); + return -1; + } } else if (Geoms[geoId1].type == Arc) { GCS::Arc& a = Arcs[Geoms[geoId1].index]; @@ -2920,6 +2935,16 @@ int Sketch::addTangentConstraint(int geoId1, int geoId2) GCSsys.addConstraintTangent(a, a2, tag); return ConstraintsCounter; } + else if (Geoms[geoId2].type == BSpline) { + Base::Console().Error("Direct tangency constraint between arc and B-spline is not " + "supported. Use tangent-via-point instead."); + return -1; + } + } + else if (Geoms[geoId1].type == BSpline) { + Base::Console().Error("Direct tangency constraint including B-splines is not " + "supported. Use tangent-via-point instead."); + return -1; } return -1; @@ -3041,7 +3066,6 @@ int Sketch::addAngleAtPointConstraint(int geoId1, ConstraintType cTyp, bool driving) { - if (!(cTyp == Angle || cTyp == Tangent || cTyp == Perpendicular)) { // assert(0);//none of the three types. Why are we here?? return -1; @@ -3146,19 +3170,116 @@ int Sketch::addAngleAtPointConstraint(int geoId1, } int tag = -1; + // FIXME: Perform construction of any parameters where this method is called instead of here if (e2c) { - // increases ConstraintsCounter - tag = Sketch::addPointOnObjectConstraint(geoId1, pos1, geoId2, driving); + if (Geoms[geoId2].type == BSpline) { + GCS::Point& p1 = Points[getPointId(geoId1, pos1)]; + auto* partBsp = static_cast(Geoms[geoId2].geo); + double uNear; + partBsp->closestParameter(Base::Vector3d(*p1.x, *p1.y, 0.0), uNear); + double* pointparam = new double(uNear); + Parameters.push_back(pointparam); + --ConstraintsCounter; // Do this just before point-on-object because ConstraintsCounter + // is increased again before being used + tag = addPointOnObjectConstraint(geoId1, + pos1, + geoId2, + pointparam, + driving); // increases ConstraintsCounter + GCSsys.addConstraintAngleViaPointAndParam(*crv2, + *crv1, + p, + pointparam, + angle, + tag, + driving); + } + else { + // increases ConstraintsCounter + tag = Sketch::addPointOnObjectConstraint(geoId1, + pos1, + geoId2, + driving); // increases ConstraintsCounter + GCSsys.addConstraintAngleViaPoint(*crv1, *crv2, p, angle, tag, driving); + } } if (e2e) { tag = ++ConstraintsCounter; GCSsys.addConstraintP2PCoincident(p, *p2, tag, driving); + GCSsys.addConstraintAngleViaPoint(*crv1, *crv2, p, angle, tag, driving); } if (avp) { tag = ++ConstraintsCounter; + if (Geoms[geoId1].type == BSpline || Geoms[geoId2].type == BSpline) { + if (Geoms[geoId1].type == BSpline && Geoms[geoId2].type == BSpline) { + GCS::Point& p3 = Points[getPointId(geoId3, pos3)]; + auto* partBsp = static_cast(Geoms[geoId1].geo); + double uNear; + partBsp->closestParameter(Base::Vector3d(*p3.x, *p3.y, 0.0), uNear); + double* pointparam1 = new double(uNear); + Parameters.push_back(pointparam1); + --ConstraintsCounter; // Do this just before point-on-object because + // ConstraintsCounter is increased again before being used + addPointOnObjectConstraint(geoId3, + pos3, + geoId1, + pointparam1, + driving); // increases ConstraintsCounter + partBsp = static_cast(Geoms[geoId2].geo); + partBsp->closestParameter(Base::Vector3d(*p3.x, *p3.y, 0.0), uNear); + double* pointparam2 = new double(uNear); + --ConstraintsCounter; // Do this just before point-on-object because + // ConstraintsCounter is increased again before being used + addPointOnObjectConstraint(geoId3, + pos3, + geoId2, + pointparam2, + driving); // increases ConstraintsCounter + Parameters.push_back(pointparam2); + GCSsys.addConstraintAngleViaPointAndTwoParams(*crv1, + *crv2, + p, + pointparam1, + pointparam2, + angle, + tag, + driving); + } + else { + if (Geoms[geoId1].type != BSpline) { + std::swap(geoId1, geoId2); + std::swap(crv1, crv2); + std::swap(pos1, pos2); + // FIXME: Confirm whether or not this is needed + // *angle = -*angle; + } + GCS::Point& p3 = Points[getPointId(geoId3, pos3)]; + auto* partBsp = static_cast(Geoms[geoId1].geo); + double uNear; + partBsp->closestParameter(Base::Vector3d(*p3.x, *p3.y, 0.0), uNear); + double* pointparam = new double(uNear); + Parameters.push_back(pointparam); + --ConstraintsCounter; // Do this just before point-on-object because + // ConstraintsCounter is increased again before being used + addPointOnObjectConstraint(geoId3, + pos3, + geoId1, + pointparam, + driving); // increases ConstraintsCounter + GCSsys.addConstraintAngleViaPointAndParam(*crv1, + *crv2, + p, + pointparam, + angle, + tag, + driving); + } + } + else { + GCSsys.addConstraintAngleViaPoint(*crv1, *crv2, p, angle, tag, driving); + } } - GCSsys.addConstraintAngleViaPoint(*crv1, *crv2, p, angle, tag, driving); return ConstraintsCounter; } @@ -4139,6 +4260,30 @@ double Sketch::calculateAngleViaPoint(int geoId1, int geoId2, double px, double return GCSsys.calculateAngleViaPoint(*crv1, *crv2, p); } +double Sketch::calculateAngleViaParams(int geoId1, int geoId2, double param1, double param2) +{ + geoId1 = checkGeoId(geoId1); + geoId2 = checkGeoId(geoId2); + + // check pointers + GCS::Curve* crv1 = getGCSCurveByGeoId(geoId1); + GCS::Curve* crv2 = getGCSCurveByGeoId(geoId2); + if (!crv1 || !crv2) { + throw Base::ValueError("calculateAngleViaPoint: getGCSCurveByGeoId returned NULL!"); + } + // FIXME: This should probably not be needed + auto* crv1AsBSpline = dynamic_cast(crv1); + if (crv1AsBSpline && crv1AsBSpline->flattenedknots.empty()) { + crv1AsBSpline->setupFlattenedKnots(); + } + auto* crv2AsBSpline = dynamic_cast(crv2); + if (crv2AsBSpline && crv2AsBSpline->flattenedknots.empty()) { + crv2AsBSpline->setupFlattenedKnots(); + } + + return GCSsys.calculateAngleViaParams(*crv1, *crv2, ¶m1, ¶m2); +} + Base::Vector3d Sketch::calculateNormalAtPoint(int geoIdCurve, double px, double py) const { geoIdCurve = checkGeoId(geoIdCurve); diff --git a/src/Mod/Sketcher/App/Sketch.h b/src/Mod/Sketcher/App/Sketch.h index 2014e81ec1..9035c7ae23 100644 --- a/src/Mod/Sketcher/App/Sketch.h +++ b/src/Mod/Sketcher/App/Sketch.h @@ -488,6 +488,8 @@ public: // value as the point approaches intersection of curves). double calculateAngleViaPoint(int geoId1, int geoId2, double px, double py); + double calculateAngleViaParams(int geoId1, int geoId2, double param1, double param2); + // This is to be used for rendering of angle-via-point constraint. Base::Vector3d calculateNormalAtPoint(int geoIdCurve, double px, double py) const; diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index fe6906223e..82a9455b5c 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -8578,13 +8578,24 @@ double SketchObject::calculateAngleViaPoint(int GeoId1, int GeoId2, double px, d // Temporary sketch based calculation. Slow, but guaranteed consistency with constraints. Sketcher::Sketch sk; - const Part::Geometry* p1 = this->getGeometry(GeoId1); - const Part::Geometry* p2 = this->getGeometry(GeoId2); + const Part::GeomCurve* p1 = dynamic_cast(this->getGeometry(GeoId1)); + const Part::GeomCurve* p2 = dynamic_cast(this->getGeometry(GeoId2)); if (p1 && p2) { + // TODO: Check if any of these are B-splines int i1 = sk.addGeometry(this->getGeometry(GeoId1)); int i2 = sk.addGeometry(this->getGeometry(GeoId2)); + if (p1->is() || + p2->is()) { + double p1ClosestParam, p2ClosestParam; + Base::Vector3d pt(px, py, 0); + p1->closestParameter(pt, p1ClosestParam); + p2->closestParameter(pt, p2ClosestParam); + + return sk.calculateAngleViaParams(i1, i2, p1ClosestParam, p2ClosestParam); + } + return sk.calculateAngleViaPoint(i1, i2, px, py); } else diff --git a/src/Mod/Sketcher/App/planegcs/GCS.cpp b/src/Mod/Sketcher/App/planegcs/GCS.cpp index b8d76d545c..c7e9a19d96 100644 --- a/src/Mod/Sketcher/App/planegcs/GCS.cpp +++ b/src/Mod/Sketcher/App/planegcs/GCS.cpp @@ -774,6 +774,36 @@ int System::addConstraintAngleViaPoint(Curve& crv1, return addConstraint(constr); } +int System::addConstraintAngleViaPointAndParam(Curve& crv1, + Curve& crv2, + Point& p, + double* cparam, + double* angle, + int tagId, + bool driving) +{ + Constraint* constr = new ConstraintAngleViaPointAndParam(crv1, crv2, p, cparam, angle); + constr->setTag(tagId); + constr->setDriving(driving); + return addConstraint(constr); +} + +int System::addConstraintAngleViaPointAndTwoParams(Curve& crv1, + Curve& crv2, + Point& p, + double* cparam1, + double* cparam2, + double* angle, + int tagId, + bool driving) +{ + Constraint* constr = + new ConstraintAngleViaPointAndTwoParams(crv1, crv2, p, cparam1, cparam2, angle); + constr->setTag(tagId); + constr->setDriving(driving); + return addConstraint(constr); +} + int System::addConstraintMidpointOnLine(Line& l1, Line& l2, int tagId, bool driving) { Constraint* constr = new ConstraintMidpointOnLine(l1, l2); diff --git a/src/Mod/Sketcher/App/planegcs/GCS.h b/src/Mod/Sketcher/App/planegcs/GCS.h index 1438157cec..68175e3e18 100644 --- a/src/Mod/Sketcher/App/planegcs/GCS.h +++ b/src/Mod/Sketcher/App/planegcs/GCS.h @@ -317,6 +317,21 @@ public: double* angle, int tagId = 0, bool driving = true); + int addConstraintAngleViaPointAndParam(Curve& crv1, + Curve& crv2, + Point& p, + double* cparam, + double* angle, + int tagId = 0, + bool driving = true); + int addConstraintAngleViaPointAndTwoParams(Curve& crv1, + Curve& crv2, + Point& p, + double* cparam1, + double* cparam2, + double* angle, + int tagId = 0, + bool driving = true); int addConstraintMidpointOnLine(Line& l1, Line& l2, int tagId = 0, bool driving = true); int addConstraintMidpointOnLine(Point& l1p1, Point& l1p2, diff --git a/src/Mod/Sketcher/App/planegcs/Geo.cpp b/src/Mod/Sketcher/App/planegcs/Geo.cpp index 46872cb2dd..e384274470 100644 --- a/src/Mod/Sketcher/App/planegcs/Geo.cpp +++ b/src/Mod/Sketcher/App/planegcs/Geo.cpp @@ -777,31 +777,43 @@ DeriVector2 BSpline::CalculateNormal(const double* param, const double* derivpar { // TODO: is there any advantage in making this a `static`? size_t startpole = 0; - for (size_t j = 1; j < mult.size() && *(knots[j]) <= *param; ++j) + for (size_t j = 1; j < mult.size() && *(knots[j]) <= *param; ++j) { startpole += mult[j]; - if (!periodic && startpole >= poles.size()) + } + if (!periodic && startpole >= poles.size()) { startpole = poles.size() - degree - 1; + } // double xsum = 0., xslopesum = 0.; // double ysum = 0., yslopesum = 0.; // double wsum = 0., wslopesum = 0.; - auto polexat = [&](size_t i) { return poles[(startpole + i) % poles.size()].x; }; - auto poleyat = [&](size_t i) { return poles[(startpole + i) % poles.size()].y; }; - auto weightat = [&](size_t i) { return weights[(startpole + i) % weights.size()]; }; + auto polexat = [&](size_t i) { + return poles[(startpole + i) % poles.size()].x; + }; + auto poleyat = [&](size_t i) { + return poles[(startpole + i) % poles.size()].y; + }; + auto weightat = [&](size_t i) { + return weights[(startpole + i) % weights.size()]; + }; size_t numpoints = degree + 1; // Tangent vector - // This should in principle be identical to error gradient wrt curve parameter in point-on-object + // This should in principle be identical to error gradient wrt curve parameter in + // point-on-object VEC_D d(numpoints); - for (size_t i = 0; i < numpoints; ++i) + for (size_t i = 0; i < numpoints; ++i) { d[i] = *polexat(i) * *weightat(i); + } double xsum = BSpline::splineValue(*param, startpole + degree, degree, d, flattenedknots); - for (size_t i = 0; i < numpoints; ++i) + for (size_t i = 0; i < numpoints; ++i) { d[i] = *poleyat(i) * *weightat(i); + } double ysum = BSpline::splineValue(*param, startpole + degree, degree, d, flattenedknots); - for (size_t i = 0; i < numpoints; ++i) + for (size_t i = 0; i < numpoints; ++i) { d[i] = *weightat(i); + } double wsum = BSpline::splineValue(*param, startpole + degree, degree, d, flattenedknots); d.resize(numpoints - 1); @@ -809,22 +821,22 @@ DeriVector2 BSpline::CalculateNormal(const double* param, const double* derivpar d[i - 1] = (*polexat(i) * *weightat(i) - *polexat(i - 1) * *weightat(i - 1)) / (flattenedknots[startpole + i + degree] - flattenedknots[startpole + i]); } - double xslopesum = degree * BSpline::splineValue( - *param, startpole + degree, degree - 1, d, flattenedknots); + double xslopesum = + degree * BSpline::splineValue(*param, startpole + degree, degree - 1, d, flattenedknots); for (size_t i = 1; i < numpoints; ++i) { d[i - 1] = (*poleyat(i) * *weightat(i) - *poleyat(i - 1) * *weightat(i - 1)) / (flattenedknots[startpole + i + degree] - flattenedknots[startpole + i]); } - double yslopesum = degree * BSpline::splineValue( - *param, startpole + degree, degree - 1, d, flattenedknots); + double yslopesum = + degree * BSpline::splineValue(*param, startpole + degree, degree - 1, d, flattenedknots); for (size_t i = 1; i < numpoints; ++i) { d[i - 1] = (*weightat(i) - *weightat(i - 1)) / (flattenedknots[startpole + i + degree] - flattenedknots[startpole + i]); } - double wslopesum = degree * BSpline::splineValue( - *param, startpole + degree, degree - 1, d, flattenedknots); + double wslopesum = + degree * BSpline::splineValue(*param, startpole + degree, degree - 1, d, flattenedknots); - DeriVector2 result(wsum*xslopesum - wslopesum*xsum, wsum*yslopesum - wslopesum*ysum); + DeriVector2 result(wsum * xslopesum - wslopesum * xsum, wsum * yslopesum - wslopesum * ysum); // get dx, dy of the normal as well bool dpfound = false; @@ -832,95 +844,113 @@ DeriVector2 BSpline::CalculateNormal(const double* param, const double* derivpar if (derivparam == polexat(i)) { VEC_D d(numpoints); d[i] = 1; - double factor = BSpline::splineValue(*param, startpole + degree, degree, d, flattenedknots); - VEC_D sd(numpoints-1); - if (i > 0) - sd[i-1] = 1.0 / (flattenedknots[startpole+i+degree] - flattenedknots[startpole+i]); - if (i < numpoints - 1) - sd[i] = -1.0 / (flattenedknots[startpole+i+1+degree] - flattenedknots[startpole+i+1]); - double slopefactor = BSpline::splineValue(*param, startpole + degree, degree-1, sd, flattenedknots); - result.dx = *weightat(i) * (wsum*slopefactor - wslopesum*factor); + double factor = + BSpline::splineValue(*param, startpole + degree, degree, d, flattenedknots); + VEC_D sd(numpoints - 1); + if (i > 0) { + sd[i - 1] = + 1.0 / (flattenedknots[startpole + i + degree] - flattenedknots[startpole + i]); + } + if (i < numpoints - 1) { + sd[i] = -1.0 + / (flattenedknots[startpole + i + 1 + degree] + - flattenedknots[startpole + i + 1]); + } + double slopefactor = + BSpline::splineValue(*param, startpole + degree, degree - 1, sd, flattenedknots); + result.dx = *weightat(i) * (wsum * slopefactor - wslopesum * factor); dpfound = true; break; } if (derivparam == poleyat(i)) { VEC_D d(numpoints); d[i] = 1; - double factor = BSpline::splineValue(*param, startpole + degree, degree, d, flattenedknots); - VEC_D sd(numpoints-1); - if (i > 0) - sd[i-1] = 1.0 / (flattenedknots[startpole+i+degree] - flattenedknots[startpole+i]); - if (i < numpoints - 1) - sd[i] = -1.0 / (flattenedknots[startpole+i+1+degree] - flattenedknots[startpole+i+1]); - double slopefactor = BSpline::splineValue(*param, startpole + degree, degree-1, sd, flattenedknots); - result.dy = *weightat(i) * (wsum*slopefactor - wslopesum*factor); + double factor = + BSpline::splineValue(*param, startpole + degree, degree, d, flattenedknots); + VEC_D sd(numpoints - 1); + if (i > 0) { + sd[i - 1] = + 1.0 / (flattenedknots[startpole + i + degree] - flattenedknots[startpole + i]); + } + if (i < numpoints - 1) { + sd[i] = -1.0 + / (flattenedknots[startpole + i + 1 + degree] + - flattenedknots[startpole + i + 1]); + } + double slopefactor = + BSpline::splineValue(*param, startpole + degree, degree - 1, sd, flattenedknots); + result.dy = *weightat(i) * (wsum * slopefactor - wslopesum * factor); dpfound = true; break; } if (derivparam == weightat(i)) { VEC_D d(numpoints); d[i] = 1; - double factor = BSpline::splineValue(*param, startpole + degree, degree, d, flattenedknots); - VEC_D sd(numpoints-1); - if (i > 0) - sd[i-1] = 1.0 / (flattenedknots[startpole+i+degree] - flattenedknots[startpole+i]); - if (i < numpoints - 1) - sd[i] = -1.0 / (flattenedknots[startpole+i+1+degree] - flattenedknots[startpole+i+1]); - double slopefactor = BSpline::splineValue(*param, startpole + degree, degree-1, sd, flattenedknots); - result.dx = degree * - (factor * (xslopesum - wslopesum*(*polexat(i))) - slopefactor * (xsum - wsum*(*polexat(i)))); - result.dy = degree * - (factor * (yslopesum - wslopesum*(*poleyat(i))) - slopefactor * (ysum - wsum*(*poleyat(i)))); + double factor = + BSpline::splineValue(*param, startpole + degree, degree, d, flattenedknots); + VEC_D sd(numpoints - 1); + if (i > 0) { + sd[i - 1] = + 1.0 / (flattenedknots[startpole + i + degree] - flattenedknots[startpole + i]); + } + if (i < numpoints - 1) { + sd[i] = -1.0 + / (flattenedknots[startpole + i + 1 + degree] + - flattenedknots[startpole + i + 1]); + } + double slopefactor = + BSpline::splineValue(*param, startpole + degree, degree - 1, sd, flattenedknots); + result.dx = degree + * (factor * (xslopesum - wslopesum * (*polexat(i))) + - slopefactor * (xsum - wsum * (*polexat(i)))); + result.dy = degree + * (factor * (yslopesum - wslopesum * (*poleyat(i))) + - slopefactor * (ysum - wsum * (*poleyat(i)))); dpfound = true; break; } } // the curve parameter being used by the constraint is not known to the geometry (there can be - // many tangent constraints on the same curve after all). Assume that this is the param provided. + // many tangent constraints on the same curve after all). Assume that this is the param + // provided. if (derivparam == param) { - VEC_D sd(numpoints-1), ssd(numpoints-2); + VEC_D sd(numpoints - 1), ssd(numpoints - 2); for (size_t i = 1; i < numpoints; ++i) { - sd[i-1] = - (*weightat(i) - *weightat(i-1)) / - (flattenedknots[startpole+i+degree] - flattenedknots[startpole+i]); + sd[i - 1] = (*weightat(i) - *weightat(i - 1)) + / (flattenedknots[startpole + i + degree] - flattenedknots[startpole + i]); } - for (size_t i = 1; i < numpoints-1; ++i) { - ssd[i-1] = - (sd[i] - sd[i-1]) / - (flattenedknots[startpole+i+degree] - flattenedknots[startpole+i]); + for (size_t i = 1; i < numpoints - 1; ++i) { + ssd[i - 1] = (sd[i] - sd[i - 1]) + / (flattenedknots[startpole + i + degree] - flattenedknots[startpole + i]); } - double wslopeslopesum = degree * (degree - 1) * - BSpline::splineValue(*param, startpole + degree, degree-2, ssd, flattenedknots); + double wslopeslopesum = degree * (degree - 1) + * BSpline::splineValue(*param, startpole + degree, degree - 2, ssd, flattenedknots); for (size_t i = 1; i < numpoints; ++i) { - sd[i-1] = - (*polexat(i) * *weightat(i) - *polexat(i-1) * *weightat(i-1)) / - (flattenedknots[startpole+i+degree] - flattenedknots[startpole+i]); + sd[i - 1] = (*polexat(i) * *weightat(i) - *polexat(i - 1) * *weightat(i - 1)) + / (flattenedknots[startpole + i + degree] - flattenedknots[startpole + i]); } - for (size_t i = 1; i < numpoints-1; ++i) { - ssd[i-1] = - (sd[i] - sd[i-1]) / - (flattenedknots[startpole+i+degree] - flattenedknots[startpole+i]); + for (size_t i = 1; i < numpoints - 1; ++i) { + ssd[i - 1] = (sd[i] - sd[i - 1]) + / (flattenedknots[startpole + i + degree] - flattenedknots[startpole + i]); } - double xslopeslopesum = degree * (degree - 1) * - BSpline::splineValue(*param, startpole + degree, degree-2, ssd, flattenedknots); + double xslopeslopesum = degree * (degree - 1) + * BSpline::splineValue(*param, startpole + degree, degree - 2, ssd, flattenedknots); for (size_t i = 1; i < numpoints; ++i) { - sd[i-1] = - (*poleyat(i) * *weightat(i) - *poleyat(i-1) * *weightat(i-1)) / - (flattenedknots[startpole+i+degree] - flattenedknots[startpole+i]); + sd[i - 1] = (*poleyat(i) * *weightat(i) - *poleyat(i - 1) * *weightat(i - 1)) + / (flattenedknots[startpole + i + degree] - flattenedknots[startpole + i]); } - for (size_t i = 1; i < numpoints-1; ++i) { - ssd[i-1] = - (sd[i] - sd[i-1]) / - (flattenedknots[startpole+i+degree] - flattenedknots[startpole+i]); + for (size_t i = 1; i < numpoints - 1; ++i) { + ssd[i - 1] = (sd[i] - sd[i - 1]) + / (flattenedknots[startpole + i + degree] - flattenedknots[startpole + i]); } - double yslopeslopesum = degree * (degree - 1) * - BSpline::splineValue(*param, startpole + degree, degree-2, ssd, flattenedknots); + double yslopeslopesum = degree * (degree - 1) + * BSpline::splineValue(*param, startpole + degree, degree - 2, ssd, flattenedknots); - result.dx = wsum*xslopeslopesum - wslopeslopesum*xsum; - result.dy = wsum*yslopeslopesum - wslopeslopesum*ysum; + result.dx = wsum * xslopeslopesum - wslopeslopesum * xsum; + result.dy = wsum * yslopeslopesum - wslopeslopesum * ysum; } return result.rotate90ccw(); diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 3e52a87070..73fae676e8 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -187,6 +187,54 @@ bool isGeoConcentricCompatible(const Part::Geometry* geo) return (isEllipse(*geo) || isArcOfEllipse(*geo) || isCircle(*geo) || isArcOfCircle(*geo)); } +// Removes point-on-object constraints made redundant with certain constraints +// under certain conditions. Currently, that happens only when the constraint is on +// a B-spline, for 3-selection tangent, perpendicular, and angle constraints. +// Returns true if constraints were removed. +// GeoId3 HAS to be the point, and the other two are the curves. +bool removeRedundantPointOnObject(SketchObject* Obj, int GeoId1, int GeoId2, int GeoId3) +{ + const std::vector& cvals = Obj->Constraints.getValues(); + + std::vector cidsToBeRemoved; + + int cid = 0; + for (auto it = cvals.begin(); it != cvals.end(); ++it, ++cid) { + if ((*it)->Type == Sketcher::PointOnObject && + (((*it)->First == GeoId3 && (*it)->Second == GeoId1) || + ((*it)->First == GeoId3 && (*it)->Second == GeoId2))) { + + // ONLY do this if it is a B-spline (or any other where point + // on object is implied). + const Part::Geometry* geom = Obj->getGeometry((*it)->Second); + if (isBSplineCurve(*geom)) + cidsToBeRemoved.push_back(cid); + } + } + + if (!cidsToBeRemoved.empty()) { + for (auto it = cidsToBeRemoved.rbegin(); it != cidsToBeRemoved.rend(); ++it) { + Gui::cmdAppObjectArgs(Obj, + "delConstraint(%d)", + *it);// remove the preexisting point on object constraint. + } + + // A substitution requires a solve() so that the autoremove redundants works when + // Autorecompute not active. However, delConstraint includes such solve() internally. So + // at this point it is already solved. + tryAutoRecomputeIfNotSolve(Obj); + + notifyConstraintSubstitutions(QObject::tr("One or two point on object constraint(s) was/were deleted, " + "since the latest constraint being applied internally applies point-on-object as well.")); + + // TODO: find way to get selection here, or clear elsewhere + // getSelection().clearSelection(); + return true; + } + + return false; +} + /// Makes an angle constraint between 2 lines void SketcherGui::makeAngleBetweenTwoLines(Sketcher::SketchObject* Obj, Gui::Command* cmd, @@ -232,8 +280,6 @@ void SketcherGui::makeAngleBetweenTwoLines(Sketcher::SketchObject* Obj, } } - - bool SketcherGui::calculateAngle(Sketcher::SketchObject* Obj, int& GeoId1, int& GeoId2, Sketcher::PointPos& PosId1, Sketcher::PointPos& PosId2, double& ActAngle) { const Part::Geometry* geom1 = Obj->getGeometry(GeoId1); @@ -312,7 +358,6 @@ bool SketcherGui::calculateAngle(Sketcher::SketchObject* Obj, int& GeoId1, int& return true; } - /// Makes a simple tangency constraint using extra point + tangent via point /// ellipse => an ellipse /// geom2 => any of an ellipse, an arc of ellipse, a circle, or an arc (of circle) @@ -5699,11 +5744,11 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) if (isVertex(GeoId1, PosId1)) { std::swap(GeoId1, GeoId2); std::swap(PosId1, PosId2); - }; + } if (isVertex(GeoId2, PosId2)) { std::swap(GeoId2, GeoId3); std::swap(PosId2, PosId3); - }; + } if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) { @@ -5720,35 +5765,45 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) bool safe = addConstraintSafely(Obj, [&]() { // add missing point-on-object constraints if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) { - Gui::cmdAppObjectArgs( - selection[0].getObject(), - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId1); - }; + const Part::Geometry *geom1 = Obj->getGeometry(GeoId1); + if (!(geom1 && isBSplineCurve(*geom1))) { + Gui::cmdAppObjectArgs( + selection[0].getObject(), + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId1); + } + } if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) { - Gui::cmdAppObjectArgs( - selection[0].getObject(), - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId2); - }; + const Part::Geometry *geom2 = Obj->getGeometry(GeoId2); + if (!(geom2 && isBSplineCurve(*geom2))) { + Gui::cmdAppObjectArgs( + selection[0].getObject(), + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId2); + } + } if (!IsPointAlreadyOnCurve( GeoId1, GeoId3, PosId3, - Obj)) {// FIXME: it's a good idea to add a check if the sketch is solved - Gui::cmdAppObjectArgs( - selection[0].getObject(), - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId1); - }; + Obj)) { + // FIXME: it's a good idea to add a check if the sketch is solved + const Part::Geometry *geom1 = Obj->getGeometry(GeoId1); + if (!(geom1 && isBSplineCurve(*geom1))) { + Gui::cmdAppObjectArgs( + selection[0].getObject(), + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId1); + } + } Gui::cmdAppObjectArgs( selection[0].getObject(), @@ -5757,6 +5812,8 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) GeoId2, GeoId3, static_cast(PosId3)); + + removeRedundantPointOnObject(Obj, GeoId1, GeoId2, GeoId3); }); if (!safe) { @@ -5770,7 +5827,7 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) getSelection().clearSelection(); return; - }; + } Gui::TranslatedUserWarning( Obj, @@ -5833,15 +5890,6 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) const Part::Geometry* geom2 = Obj->getGeometry(GeoId2); - if (geom2 && isBSplineCurve(*geom2)) { - // unsupported until normal to B-spline at any point implemented. - Gui::TranslatedUserWarning( - Obj, - QObject::tr("Wrong selection"), - QObject::tr("Perpendicular to B-spline edge currently unsupported.")); - return; - } - if (isBsplinePole(geom2)) { Gui::TranslatedUserWarning( Obj, @@ -5879,14 +5927,14 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg) return; } - if (isBSplineCurve(*geo1) || isBSplineCurve(*geo2)) { - // unsupported until tangent to B-spline at any point implemented. - Gui::TranslatedUserWarning( - Obj, - QObject::tr("Wrong selection"), - QObject::tr("Perpendicular to B-spline edge currently unsupported.")); - return; - } + // if (isBSplineCurve(*geo1) || isBSplineCurve(*geo2)) { + // // unsupported until tangent to B-spline at any point implemented. + // Gui::TranslatedUserWarning( + // Obj, + // QObject::tr("Wrong selection"), + // QObject::tr("Perpendicular to B-spline edge currently unsupported.")); + // return; + // } if (isLineSegment(*geo1)) { std::swap(GeoId1, GeoId2); @@ -6080,14 +6128,14 @@ void CmdSketcherConstrainPerpendicular::applyConstraint(std::vector& return; } - if (isBSplineCurve(*geo1) || isBSplineCurve(*geo2)) { - // unsupported until tangent to B-spline at any point implemented. - Gui::TranslatedUserWarning( - Obj, - QObject::tr("Wrong selection"), - QObject::tr("Perpendicular to B-spline edge currently unsupported.")); - return; - } + // if (isBSplineCurve(*geo1) || isBSplineCurve(*geo2)) { + // // unsupported until tangent to B-spline at any point implemented. + // Gui::TranslatedUserWarning( + // Obj, + // QObject::tr("Wrong selection"), + // QObject::tr("Perpendicular to B-spline edge currently unsupported.")); + // return; + // } if (isLineSegment(*geo1)) { std::swap(GeoId1, GeoId2); @@ -6285,35 +6333,41 @@ void CmdSketcherConstrainPerpendicular::applyConstraint(std::vector& bool safe = addConstraintSafely(Obj, [&]() { // add missing point-on-object constraints if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) { - Gui::cmdAppObjectArgs( - Obj, - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId1); - }; + const Part::Geometry *geom1 = Obj->getGeometry(GeoId1); + if (!(geom1 && isBSplineCurve(*geom1))) { + Gui::cmdAppObjectArgs( + Obj, + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId1); + } + } if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) { - Gui::cmdAppObjectArgs( - Obj, - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId2); - }; + const Part::Geometry *geom2 = Obj->getGeometry(GeoId2); + if (!(geom2 && isBSplineCurve(*geom2))) { + Gui::cmdAppObjectArgs( + Obj, + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId2); + } + } - if (!IsPointAlreadyOnCurve( - GeoId1, - GeoId3, - PosId3, - Obj)) {// FIXME: it's a good idea to add a check if the sketch is solved - Gui::cmdAppObjectArgs( - Obj, - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId1); - }; + if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) { + // FIXME: it's a good idea to add a check if the sketch is solved + const Part::Geometry *geom1 = Obj->getGeometry(GeoId1); + if (!(geom1 && isBSplineCurve(*geom1))) { + Gui::cmdAppObjectArgs( + Obj, + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId1); + } + } Gui::cmdAppObjectArgs( Obj, @@ -6322,6 +6376,8 @@ void CmdSketcherConstrainPerpendicular::applyConstraint(std::vector& GeoId2, GeoId3, static_cast(PosId3)); + + removeRedundantPointOnObject(Obj, GeoId1, GeoId2, GeoId3); }); if (!safe) { @@ -6335,7 +6391,7 @@ void CmdSketcherConstrainPerpendicular::applyConstraint(std::vector& getSelection().clearSelection(); return; - }; + } } // ====================================================================================== @@ -6519,11 +6575,11 @@ void CmdSketcherConstrainTangent::activated(int iMsg) if (isVertex(GeoId1, PosId1)) { std::swap(GeoId1, GeoId2); std::swap(PosId1, PosId2); - }; + } if (isVertex(GeoId2, PosId2)) { std::swap(GeoId2, GeoId3); std::swap(PosId2, PosId3); - }; + } if (isEdge(GeoId1, PosId1) && isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) { @@ -6540,35 +6596,41 @@ void CmdSketcherConstrainTangent::activated(int iMsg) bool safe = addConstraintSafely(Obj, [&]() { // add missing point-on-object constraints if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) { - Gui::cmdAppObjectArgs( - selection[0].getObject(), - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId1); - }; + const Part::Geometry *geom1 = Obj->getGeometry(GeoId1); + if (!(geom1 && isBSplineCurve(*geom1))) { + Gui::cmdAppObjectArgs( + selection[0].getObject(), + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId1); + } + } if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) { - Gui::cmdAppObjectArgs( - selection[0].getObject(), - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId2); - }; + const Part::Geometry *geom2 = Obj->getGeometry(GeoId2); + if (!(geom2 && isBSplineCurve(*geom2))) { + Gui::cmdAppObjectArgs( + selection[0].getObject(), + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId2); + } + } - if (!IsPointAlreadyOnCurve( - GeoId1, - GeoId3, - PosId3, - Obj)) {// FIXME: it's a good idea to add a check if the sketch is solved - Gui::cmdAppObjectArgs( - selection[0].getObject(), - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId1); - }; + if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) { + // FIXME: it's a good idea to add a check if the sketch is solved + const Part::Geometry *geom1 = Obj->getGeometry(GeoId1); + if (!(geom1 && isBSplineCurve(*geom1))) { + Gui::cmdAppObjectArgs( + selection[0].getObject(), + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId1); + } + } Gui::cmdAppObjectArgs( selection[0].getObject(), @@ -6577,6 +6639,8 @@ void CmdSketcherConstrainTangent::activated(int iMsg) GeoId2, GeoId3, static_cast(PosId3)); + + removeRedundantPointOnObject(Obj, GeoId1, GeoId2, GeoId3); }); if (!safe) { @@ -6590,7 +6654,7 @@ void CmdSketcherConstrainTangent::activated(int iMsg) getSelection().clearSelection(); return; - }; + } Gui::TranslatedUserWarning( Obj, @@ -6669,15 +6733,6 @@ void CmdSketcherConstrainTangent::activated(int iMsg) const Part::Geometry* geom2 = Obj->getGeometry(GeoId2); - if (geom2 && isBSplineCurve(*geom2)) { - // unsupported until tangent to B-spline at any point implemented. - Gui::TranslatedUserWarning( - Obj, - QObject::tr("Wrong selection"), - QObject::tr("Tangency to B-spline edge currently unsupported.")); - return; - } - if (isBsplinePole(geom2)) { Gui::TranslatedUserWarning( Obj, @@ -6686,16 +6741,18 @@ void CmdSketcherConstrainTangent::activated(int iMsg) return; } - openCommand(QT_TRANSLATE_NOOP("Command", "Add tangent constraint")); - Gui::cmdAppObjectArgs(selection[0].getObject(), - "addConstraint(Sketcher.Constraint('Tangent',%d,%d,%d))", - GeoId1, - static_cast(PosId1), - GeoId2); - commitCommand(); - tryAutoRecompute(Obj); + if (!substituteConstraintCombinations(Obj, GeoId1, GeoId2)) { + openCommand(QT_TRANSLATE_NOOP("Command", "Add tangent constraint")); + Gui::cmdAppObjectArgs(selection[0].getObject(), + "addConstraint(Sketcher.Constraint('Tangent',%d,%d,%d))", + GeoId1, + static_cast(PosId1), + GeoId2); + commitCommand(); + tryAutoRecompute(Obj); - getSelection().clearSelection(); + getSelection().clearSelection(); + } return; } else if (isEdge(GeoId1, PosId1) @@ -6704,15 +6761,6 @@ void CmdSketcherConstrainTangent::activated(int iMsg) const Part::Geometry* geom1 = Obj->getGeometry(GeoId1); const Part::Geometry* geom2 = Obj->getGeometry(GeoId2); - if (geom1 && geom2 && (isBSplineCurve(*geom1) || isBSplineCurve(*geom2))) { - // unsupported until tangent to B-spline at any point implemented. - Gui::TranslatedUserWarning( - Obj, - QObject::tr("Wrong selection"), - QObject::tr("Tangency to B-spline edge currently unsupported.")); - return; - } - if (isBsplinePole(geom1) || isBsplinePole(geom2)) { Gui::TranslatedUserWarning( Obj, @@ -6872,6 +6920,14 @@ void CmdSketcherConstrainTangent::activated(int iMsg) return; } } + else if (geom1 && geom2 && (isBSplineCurve(*geom1) || isBSplineCurve(*geom2))) { + Gui::TranslatedUserWarning( + Obj, + QObject::tr("Wrong selection"), + QObject::tr("Only tangent-via-point is supported with a B-spline.")); + getSelection().clearSelection(); + return; + } openCommand(QT_TRANSLATE_NOOP("Command", "Add tangent constraint")); Gui::cmdAppObjectArgs(selection[0].getObject(), @@ -6917,15 +6973,6 @@ void CmdSketcherConstrainTangent::applyConstraint(std::vector& selSeq const Part::Geometry* geom1 = Obj->getGeometry(GeoId1); const Part::Geometry* geom2 = Obj->getGeometry(GeoId2); - if (geom1 && geom2 && (isBSplineCurve(*geom1) || isBSplineCurve(*geom2))) { - // unsupported until tangent to B-spline at any point implemented. - Gui::TranslatedUserWarning( - Obj, - QObject::tr("Wrong selection"), - QObject::tr("Tangency to B-spline edge currently unsupported.")); - return; - } - if (isBsplinePole(geom1) || isBsplinePole(geom2)) { Gui::TranslatedUserWarning( Obj, @@ -7158,35 +7205,41 @@ void CmdSketcherConstrainTangent::applyConstraint(std::vector& selSeq bool safe = addConstraintSafely(Obj, [&]() { // add missing point-on-object constraints if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) { - Gui::cmdAppObjectArgs( - Obj, - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId1); - }; + const Part::Geometry *geom1 = Obj->getGeometry(GeoId1); + if (!(geom1 && isBSplineCurve(*geom1))) { + Gui::cmdAppObjectArgs( + Obj, + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId1); + } + } if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) { - Gui::cmdAppObjectArgs( - Obj, - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId2); - }; + const Part::Geometry *geom2 = Obj->getGeometry(GeoId2); + if (!(geom2 && isBSplineCurve(*geom2))) { + Gui::cmdAppObjectArgs( + Obj, + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId2); + } + } - if (!IsPointAlreadyOnCurve( - GeoId1, - GeoId3, - PosId3, - Obj)) {// FIXME: it's a good idea to add a check if the sketch is solved - Gui::cmdAppObjectArgs( - Obj, - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId1); - }; + if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) { + // FIXME: it's a good idea to add a check if the sketch is solved + const Part::Geometry *geom1 = Obj->getGeometry(GeoId1); + if (!(geom1 && isBSplineCurve(*geom1))) { + Gui::cmdAppObjectArgs( + Obj, + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId1); + } + } Gui::cmdAppObjectArgs( Obj, @@ -7195,6 +7248,8 @@ void CmdSketcherConstrainTangent::applyConstraint(std::vector& selSeq GeoId2, GeoId3, static_cast(PosId3)); + + removeRedundantPointOnObject(Obj, GeoId1, GeoId2, GeoId3); }); if (!safe) { @@ -7208,7 +7263,7 @@ void CmdSketcherConstrainTangent::applyConstraint(std::vector& selSeq getSelection().clearSelection(); return; - }; + } } // ====================================================================================== @@ -8530,11 +8585,11 @@ void CmdSketcherConstrainAngle::activated(int iMsg) if (isVertex(GeoId1, PosId1)) { std::swap(GeoId1, GeoId2); std::swap(PosId1, PosId2); - }; + } if (isVertex(GeoId2, PosId2)) { std::swap(GeoId2, GeoId3); std::swap(PosId2, PosId3); - }; + } bool bothexternal = areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2); @@ -8554,32 +8609,38 @@ void CmdSketcherConstrainAngle::activated(int iMsg) // add missing point-on-object constraints if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) { - Gui::cmdAppObjectArgs( - selection[0].getObject(), - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId1); + const Part::Geometry *geom1 = Obj->getGeometry(GeoId1); + if (!(geom1 && isBSplineCurve(*geom1))) { + Gui::cmdAppObjectArgs( + selection[0].getObject(), + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId1); + } } if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) { - Gui::cmdAppObjectArgs( - selection[0].getObject(), - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId2); + const Part::Geometry *geom2 = Obj->getGeometry(GeoId2); + if (!(geom2 && isBSplineCurve(*geom2))) { + Gui::cmdAppObjectArgs( + selection[0].getObject(), + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId2); + } } - if (!IsPointAlreadyOnCurve( - GeoId1, - GeoId3, - PosId3, - Obj)) {// FIXME: it's a good idea to add a check if the sketch is solved - Gui::cmdAppObjectArgs( - selection[0].getObject(), - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId1); + if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) { + // FIXME: it's a good idea to add a check if the sketch is solved + const Part::Geometry *geom1 = Obj->getGeometry(GeoId1); + if (!(geom1 && isBSplineCurve(*geom1))) { + Gui::cmdAppObjectArgs( + selection[0].getObject(), + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId1); + } } // assuming point-on-curves have been solved, calculate the angle. @@ -8604,6 +8665,8 @@ void CmdSketcherConstrainAngle::activated(int iMsg) static_cast(PosId3), ActAngle); + removeRedundantPointOnObject(Obj, GeoId1, GeoId2, GeoId3); + if (bothexternal || constraintCreationMode == Reference) {// it is a constraint on a external line, make it non-driving @@ -8620,7 +8683,7 @@ void CmdSketcherConstrainAngle::activated(int iMsg) } return; - }; + } } else if (SubNames.size() < 3) { @@ -8707,7 +8770,7 @@ void CmdSketcherConstrainAngle::activated(int iMsg) return; } } - }; + } Gui::TranslatedUserWarning( Obj, @@ -8783,26 +8846,35 @@ void CmdSketcherConstrainAngle::applyConstraint(std::vector& selSeq, // add missing point-on-object constraints if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) { - Gui::cmdAppObjectArgs(Obj, - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId1); + const Part::Geometry *geom1 = Obj->getGeometry(GeoId1); + if (!(geom1 && isBSplineCurve(*geom1))) { + Gui::cmdAppObjectArgs(Obj, + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId1); + } } if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) { - Gui::cmdAppObjectArgs(Obj, - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId2); + const Part::Geometry *geom2 = Obj->getGeometry(GeoId2); + if (!(geom2 && isBSplineCurve(*geom2))) { + Gui::cmdAppObjectArgs(Obj, + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId2); + } } if (!IsPointAlreadyOnCurve(GeoId1, GeoId3, PosId3, Obj)) { // FIXME: it's a good idea to add a check if the sketch is solved - Gui::cmdAppObjectArgs(Obj, - "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", - GeoId3, - static_cast(PosId3), - GeoId1); + const Part::Geometry *geom1 = Obj->getGeometry(GeoId1); + if (!(geom1 && isBSplineCurve(*geom1))) { + Gui::cmdAppObjectArgs(Obj, + "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))", + GeoId3, + static_cast(PosId3), + GeoId1); + } } // assuming point-on-curves have been solved, calculate the angle. @@ -8826,6 +8898,8 @@ void CmdSketcherConstrainAngle::applyConstraint(std::vector& selSeq, static_cast(PosId3), ActAngle); + removeRedundantPointOnObject(Obj, GeoId1, GeoId2, GeoId3); + if (bothexternal || constraintCreationMode == Reference) { // it is a constraint on a external line, make it non-driving const std::vector& ConStr = Obj->Constraints.getValues(); @@ -8838,7 +8912,7 @@ void CmdSketcherConstrainAngle::applyConstraint(std::vector& selSeq, } return; - }; + } } void CmdSketcherConstrainAngle::updateAction(int mode) @@ -9566,18 +9640,18 @@ void CmdSketcherConstrainSnellsLaw::activated(int iMsg) QObject::tr("Wrong selection"), QObject::tr("Incompatible geometry is selected.")); return; - }; + } const Part::Geometry* geo = Obj->getGeometry(GeoId3); - if (geo && isBSplineCurve(*geo)) { - // unsupported until normal to B-spline at any point implemented. - Gui::TranslatedUserWarning( - Obj, - QObject::tr("Wrong selection"), - QObject::tr("SnellsLaw on B-spline edge is currently unsupported.")); - return; - } + // if (geo && isBSplineCurve(*geo)) { + // // unsupported until normal to B-spline at any point implemented. + // Gui::TranslatedUserWarning( + // Obj, + // QObject::tr("Wrong selection"), + // QObject::tr("SnellsLaw on B-spline edge is currently unsupported.")); + // return; + // } if (isBsplinePole(geo)) { Gui::TranslatedUserWarning(Obj,