[Sketcher] Allow multiplicity change of B-Spline end knots

The first and last knots (which are often also poles) of a B-Spline are not
represented in sketcher as knots, but as end points of the curve. This creates a
problem when we want to change the multiplicities of those knots. As a
workaround, these points are treated differently.

NOTE: While it is technically possible to have end knots in a non-periodic
spline with degree <= degree of the curve, `Geom_BSplineCurve::RemoveKnot()`
fails for the first and last knots, so we are stuck with this. So, this commit
effectively only makes knot multiplicity change possible for periodic splines.
For non-periodic splines this commit just changes the confusing "no knot is
selected" message to an unhelpful "multiplicity modification failed".
This commit is contained in:
Ajinkya Dahale
2022-02-17 05:44:26 -05:00
committed by abdullahtahiriyo
parent 0a02853bb7
commit 61a7654094
3 changed files with 114 additions and 70 deletions

View File

@@ -91,6 +91,47 @@ void ShowRestoreInformationLayer(const char * visibleelementname)
hGrp->SetBool(visibleelementname, !status);
}
/// For a knot given by (GeoId, PosId) finds the B-Spline and the knot's
/// index within it (by OCC numbering).
/// Returns true if the entities are found, false otherwise.
/// If returns false, `splineGeoId` and `knotIndexOCC` have garbage values.
bool findBSplineAndKnotIndex(Sketcher::SketchObject* Obj,
int knotGeoId, Sketcher::PointPos knotPosId,
int& splineGeoId, int& knotIndexOCC)
{
for (auto const constraint : Obj->Constraints.getValues()) {
if (constraint->Type == Sketcher::InternalAlignment
&& constraint->First == knotGeoId
&& constraint->AlignmentType == Sketcher::BSplineKnotPoint)
{
splineGeoId = constraint->Second;
knotIndexOCC = constraint->InternalAlignmentIndex + 1;
return true; // we have already found our knot.
}
}
// TODO: what to do if multiple splines have the same first/last point?
const Part::Geometry *geo = Obj->getGeometry(knotGeoId);
if (geo->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()) {
splineGeoId = knotGeoId;
switch (knotPosId) {
case Sketcher::PointPos::start:
knotIndexOCC = 1;
return true;
case Sketcher::PointPos::end:
knotIndexOCC = static_cast<const Part::GeomBSplineCurve *>(geo)->countKnots();
return true;
default:
// If we reach here something went wrong.
// isBsplineKnotOrEndPoint (that we expect is run before) will
// only accept spline knotGeoID if knotPosId is start or end.
return false;
}
}
return false;
}
// Show/Hide B-spline degree
DEF_STD_CMD_A(CmdSketcherBSplineDegree)
@@ -637,55 +678,48 @@ void CmdSketcherIncreaseKnotMultiplicity::activated(int iMsg)
openCommand(QT_TRANSLATE_NOOP("Command", "Increase knot multiplicity"));
bool applied = false;
bool notaknot = true;
boost::uuids::uuid bsplinetag;
int GeoId;
Sketcher::PointPos PosId;
getIdsFromName(SubNames[0], Obj, GeoId, PosId);
if (isSimpleVertex(Obj, GeoId, PosId)) {
const std::vector<Sketcher::Constraint *> &vals = Obj->Constraints.getValues();
int splineGeoId;
int knotIndexOCC;
for (std::vector<Sketcher::Constraint *>::const_iterator it= vals.begin(); it != vals.end(); ++it) {
if ((*it)->Type == Sketcher::InternalAlignment
&& (*it)->First == GeoId
&& (*it)->AlignmentType == Sketcher::BSplineKnotPoint)
{
bsplinetag = Obj->getGeometry((*it)->Second)->getTag();
notaknot = false;
bool applied = false;
bool notaknot = !(isBsplineKnotOrEndPoint(Obj, GeoId, PosId) &&
findBSplineAndKnotIndex(Obj, GeoId, PosId, splineGeoId, knotIndexOCC));
boost::uuids::uuid bsplinetag;
try {
Gui::cmdAppObjectArgs(selection[0].getObject(),
"modifyBSplineKnotMultiplicity(%d, %d, %d) ",
(*it)->Second, (*it)->InternalAlignmentIndex + 1, 1);
applied = true;
if (!notaknot) {
bsplinetag = Obj->getGeometry(splineGeoId)->getTag();
// Warning: GeoId list might have changed
// as the consequence of deleting pole circles and
// particularly B-spline GeoID might have changed.
}
catch (const Base::CADKernelError& e) {
e.ReportException();
if (e.getTranslatable()) {
QMessageBox::warning(Gui::getMainWindow(),
QObject::tr("CAD Kernel Error"),
QObject::tr(e.getMessage().c_str()));
}
getSelection().clearSelection();
}
catch (const Base::Exception& e) {
e.ReportException();
if (e.getTranslatable()) {
QMessageBox::warning(Gui::getMainWindow(),
QObject::tr("Input Error"),
QObject::tr(e.getMessage().c_str()));
}
getSelection().clearSelection();
}
break; // we have already found our knot.
try {
Gui::cmdAppObjectArgs(selection[0].getObject(),
"modifyBSplineKnotMultiplicity(%d, %d, %d) ",
splineGeoId, knotIndexOCC, 1);
applied = true;
// Warning: GeoId list might have changed
// as the consequence of deleting pole circles and
// particularly B-spline GeoID might have changed.
}
catch (const Base::CADKernelError& e) {
e.ReportException();
if (e.getTranslatable()) {
QMessageBox::warning(Gui::getMainWindow(),
QObject::tr("CAD Kernel Error"),
QObject::tr(e.getMessage().c_str()));
}
getSelection().clearSelection();
}
catch (const Base::Exception& e) {
e.ReportException();
if (e.getTranslatable()) {
QMessageBox::warning(Gui::getMainWindow(),
QObject::tr("Input Error"),
QObject::tr(e.getMessage().c_str()));
}
getSelection().clearSelection();
}
}
@@ -791,42 +825,34 @@ void CmdSketcherDecreaseKnotMultiplicity::activated(int iMsg)
openCommand(QT_TRANSLATE_NOOP("Command", "Decrease knot multiplicity"));
bool applied = false;
bool notaknot = true;
boost::uuids::uuid bsplinetag;
int GeoId;
Sketcher::PointPos PosId;
getIdsFromName(SubNames[0], Obj, GeoId, PosId);
if (isSimpleVertex(Obj, GeoId, PosId))
{
const std::vector< Sketcher::Constraint * > &vals = Obj->Constraints.getValues();
int splineGeoId;
int knotIndexOCC;
for (std::vector< Sketcher::Constraint * >::const_iterator it= vals.begin(); it != vals.end(); ++it) {
if ((*it)->Type == Sketcher::InternalAlignment
&& (*it)->First == GeoId
&& (*it)->AlignmentType == Sketcher::BSplineKnotPoint)
{
bsplinetag = Obj->getGeometry((*it)->Second)->getTag();
notaknot = false;
bool applied = false;
bool notaknot = !(isBsplineKnotOrEndPoint(Obj, GeoId, PosId) &&
findBSplineAndKnotIndex(Obj, GeoId, PosId, splineGeoId, knotIndexOCC));
boost::uuids::uuid bsplinetag;
try {
Gui::cmdAppObjectArgs(selection[0].getObject(),
"modifyBSplineKnotMultiplicity(%d, %d, %d) ",
(*it)->Second, (*it)->InternalAlignmentIndex + 1, -1);
applied = true;
if (!notaknot) {
bsplinetag = Obj->getGeometry(splineGeoId)->getTag();
// Warning: GeoId list might have changed as the consequence of deleting pole circles and
// particularly B-spline GeoID might have changed.
}
catch (const Base::Exception& e) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Error"),
QObject::tr(getStrippedPythonExceptionString(e).c_str()));
getSelection().clearSelection();
}
break; // we have already found our knot.
}
try {
Gui::cmdAppObjectArgs(selection[0].getObject(),
"modifyBSplineKnotMultiplicity(%d, %d, %d) ",
splineGeoId, knotIndexOCC, -1);
applied = true;
// Warning: GeoId list might have changed as the consequence of deleting pole circles and
// particularly B-spline GeoID might have changed.
}
catch (const Base::Exception& e) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Error"),
QObject::tr(getStrippedPythonExceptionString(e).c_str()));
getSelection().clearSelection();
}
}

View File

@@ -221,6 +221,21 @@ bool SketcherGui::isBsplineKnot(const Sketcher::SketchObject* Obj, int GeoId)
return (gf && gf->getInternalType() == Sketcher::InternalType::BSplineKnotPoint);
}
bool SketcherGui::isBsplineKnotOrEndPoint(const Sketcher::SketchObject* Obj, int GeoId, Sketcher::PointPos PosId)
{
// check first using geometry facade
if (isBsplineKnot(Obj, GeoId))
return true;
const Part::Geometry *geo = Obj->getGeometry(GeoId);
// end points of B-Splines are also knots
if (geo->getTypeId() == Part::GeomBSplineCurve::getClassTypeId() &&
(PosId == Sketcher::PointPos::start || PosId == Sketcher::PointPos::end))
return true;
return false;
}
bool SketcherGui::IsPointAlreadyOnCurve(int GeoIdCurve, int GeoIdPoint, Sketcher::PointPos PosIdPoint, Sketcher::SketchObject* Obj)
{
//This func is a "smartness" behind three-element tangent-, perp.- and angle-via-point.

View File

@@ -66,7 +66,10 @@ bool inline isEdge(int GeoId, Sketcher::PointPos PosId);
bool isSimpleVertex(const Sketcher::SketchObject* Obj, int GeoId, Sketcher::PointPos PosId);
/// Checks if `GeoId` corresponds to a B-Spline knot
bool isBsplineKnot(const Sketcher::SketchObject* Obj, int GeoId);
/// Checks if the (`GeoId`, `PosId`) pair corresponds to a B-Spline knot, including first and last knots
bool isBsplineKnotOrEndPoint(const Sketcher::SketchObject* Obj, int GeoId, Sketcher::PointPos PosId);
bool IsPointAlreadyOnCurve(int GeoIdCurve, int GeoIdPoint, Sketcher::PointPos PosIdPoint, Sketcher::SketchObject* Obj);