[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).
This commit is contained in:
Ajinkya Dahale
2023-04-05 23:11:28 +05:30
parent 82f03593fd
commit 65b4dd10ae
7 changed files with 611 additions and 304 deletions

View File

@@ -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<GeomBSplineCurve*>(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<GeomBSplineCurve*>(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<GeomBSplineCurve*>(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<GeomBSplineCurve*>(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<GCS::BSpline*>(crv1);
if (crv1AsBSpline && crv1AsBSpline->flattenedknots.empty()) {
crv1AsBSpline->setupFlattenedKnots();
}
auto* crv2AsBSpline = dynamic_cast<GCS::BSpline*>(crv2);
if (crv2AsBSpline && crv2AsBSpline->flattenedknots.empty()) {
crv2AsBSpline->setupFlattenedKnots();
}
return GCSsys.calculateAngleViaParams(*crv1, *crv2, &param1, &param2);
}
Base::Vector3d Sketch::calculateNormalAtPoint(int geoIdCurve, double px, double py) const
{
geoIdCurve = checkGeoId(geoIdCurve);

View File

@@ -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;

View File

@@ -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<const Part::GeomCurve*>(this->getGeometry(GeoId1));
const Part::GeomCurve* p2 = dynamic_cast<const Part::GeomCurve*>(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<Part::GeomBSplineCurve>() ||
p2->is<Part::GeomBSplineCurve>()) {
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

View File

@@ -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);

View File

@@ -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,

View File

@@ -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();

View File

@@ -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<Constraint*>& cvals = Obj->Constraints.getValues();
std::vector<int> 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<int>(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<int>(PosId3),
GeoId1);
}
}
if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) {
Gui::cmdAppObjectArgs(
selection[0].getObject(),
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
GeoId3,
static_cast<int>(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<int>(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<int>(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<int>(PosId3),
GeoId1);
}
}
Gui::cmdAppObjectArgs(
selection[0].getObject(),
@@ -5757,6 +5812,8 @@ void CmdSketcherConstrainPerpendicular::activated(int iMsg)
GeoId2,
GeoId3,
static_cast<int>(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<SelIdPair>&
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<SelIdPair>&
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<int>(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<int>(PosId3),
GeoId1);
}
}
if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) {
Gui::cmdAppObjectArgs(
Obj,
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
GeoId3,
static_cast<int>(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<int>(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<int>(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<int>(PosId3),
GeoId1);
}
}
Gui::cmdAppObjectArgs(
Obj,
@@ -6322,6 +6376,8 @@ void CmdSketcherConstrainPerpendicular::applyConstraint(std::vector<SelIdPair>&
GeoId2,
GeoId3,
static_cast<int>(PosId3));
removeRedundantPointOnObject(Obj, GeoId1, GeoId2, GeoId3);
});
if (!safe) {
@@ -6335,7 +6391,7 @@ void CmdSketcherConstrainPerpendicular::applyConstraint(std::vector<SelIdPair>&
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<int>(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<int>(PosId3),
GeoId1);
}
}
if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) {
Gui::cmdAppObjectArgs(
selection[0].getObject(),
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
GeoId3,
static_cast<int>(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<int>(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<int>(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<int>(PosId3),
GeoId1);
}
}
Gui::cmdAppObjectArgs(
selection[0].getObject(),
@@ -6577,6 +6639,8 @@ void CmdSketcherConstrainTangent::activated(int iMsg)
GeoId2,
GeoId3,
static_cast<int>(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<int>(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<int>(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<SelIdPair>& 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<SelIdPair>& 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<int>(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<int>(PosId3),
GeoId1);
}
}
if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) {
Gui::cmdAppObjectArgs(
Obj,
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
GeoId3,
static_cast<int>(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<int>(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<int>(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<int>(PosId3),
GeoId1);
}
}
Gui::cmdAppObjectArgs(
Obj,
@@ -7195,6 +7248,8 @@ void CmdSketcherConstrainTangent::applyConstraint(std::vector<SelIdPair>& selSeq
GeoId2,
GeoId3,
static_cast<int>(PosId3));
removeRedundantPointOnObject(Obj, GeoId1, GeoId2, GeoId3);
});
if (!safe) {
@@ -7208,7 +7263,7 @@ void CmdSketcherConstrainTangent::applyConstraint(std::vector<SelIdPair>& 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<int>(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<int>(PosId3),
GeoId1);
}
}
if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) {
Gui::cmdAppObjectArgs(
selection[0].getObject(),
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
GeoId3,
static_cast<int>(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<int>(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<int>(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<int>(PosId3),
GeoId1);
}
}
// assuming point-on-curves have been solved, calculate the angle.
@@ -8604,6 +8665,8 @@ void CmdSketcherConstrainAngle::activated(int iMsg)
static_cast<int>(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<SelIdPair>& 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<int>(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<int>(PosId3),
GeoId1);
}
}
if (!IsPointAlreadyOnCurve(GeoId2, GeoId3, PosId3, Obj)) {
Gui::cmdAppObjectArgs(Obj,
"addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d))",
GeoId3,
static_cast<int>(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<int>(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<int>(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<int>(PosId3),
GeoId1);
}
}
// assuming point-on-curves have been solved, calculate the angle.
@@ -8826,6 +8898,8 @@ void CmdSketcherConstrainAngle::applyConstraint(std::vector<SelIdPair>& selSeq,
static_cast<int>(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<Sketcher::Constraint*>& ConStr = Obj->Constraints.getValues();
@@ -8838,7 +8912,7 @@ void CmdSketcherConstrainAngle::applyConstraint(std::vector<SelIdPair>& 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,