[Sketcher] Add insertBSplineKnot to SketcherObject

[Sketcher] Workaround for segfault on knot insertion
This commit is contained in:
Ajinkya Dahale
2021-12-28 21:10:41 -05:00
committed by abdullahtahiriyo
parent ff2e246a55
commit 07cad4ab64
4 changed files with 189 additions and 2 deletions

View File

@@ -1082,8 +1082,8 @@ int SketchObject::delGeometriesExclusiveList(const std::vector<int>& GeoIds)
for (const auto ptr : this->Constraints.getValues())
constraints.push_back(ptr->clone());
std::vector< Constraint* > filteredConstraints(0);
for (auto it = sGeoIds.rbegin(); it != sGeoIds.rend(); ++it) {
int GeoId = *it;
for (auto itGeo = sGeoIds.rbegin(); itGeo != sGeoIds.rend(); ++itGeo) {
int GeoId = *itGeo;
for (std::vector<Constraint*>::const_iterator it = constraints.begin();
it != constraints.end(); ++it) {
@@ -5925,6 +5925,160 @@ bool SketchObject::modifyBSplineKnotMultiplicity(int GeoId, int knotIndex, int m
return true;
}
bool SketchObject::insertBSplineKnot(int GeoId, double param, int multiplicity)
{
Base::StateLocker lock(managedoperation, true); // TODO: Check if this is still valid: no need to check input data validity as this is an sketchobject managed operation.
#if OCC_VERSION_HEX < 0x060900
THROWMT(Base::NotImplementedError, QT_TRANSLATE_NOOP("Exceptions", "This version of OCE/OCC does not support knot operation. You need 6.9.0 or higher."))
#endif
// handling unacceptable cases
if (GeoId < 0 || GeoId > getHighestCurveIndex())
THROWMT(Base::ValueError,QT_TRANSLATE_NOOP("Exceptions", "BSpline Geometry Index (GeoID) is out of bounds."));
if (multiplicity == 0)
THROWMT(Base::ValueError,QT_TRANSLATE_NOOP("Exceptions", "Knot cannot have zero multiplicity."));
const Part::Geometry *geo = getGeometry(GeoId);
if(geo->getTypeId() != Part::GeomBSplineCurve::getClassTypeId())
THROWMT(Base::TypeError,QT_TRANSLATE_NOOP("Exceptions", "The Geometry Index (GeoId) provided is not a B-spline curve."));
const Part::GeomBSplineCurve *bsp = static_cast<const Part::GeomBSplineCurve *>(geo);
int degree = bsp->getDegree();
double firstParam = bsp->getFirstParameter();
double lastParam = bsp->getLastParameter();
if (multiplicity > degree)
THROWMT(Base::ValueError,QT_TRANSLATE_NOOP("Exceptions", "Knot multiplicity cannot be higher than the degree of the BSpline."));
if (param > lastParam || param < firstParam)
THROWMT(Base::ValueError,QT_TRANSLATE_NOOP("Exceptions", "Knot cannot be inserted outside the BSpline parameter range."));
std::unique_ptr<Part::GeomBSplineCurve> bspline;
// run the command
try {
bspline.reset(static_cast<Part::GeomBSplineCurve *>(bsp->clone()));
bspline->insertKnot(param, multiplicity);
}
catch (const Base::Exception& e) {
Base::Console().Error("%s\n", e.what());
return false;
}
// once command is run update the internal geometries
std::vector<int> delGeoId;
std::vector<Base::Vector3d> poles = bsp->getPoles();
std::vector<Base::Vector3d> newpoles = bspline->getPoles();
std::vector<int> prevpole(bsp->countPoles());
for(int i = 0; i < int(poles.size()); i++)
prevpole[i] = -1;
int taken = 0;
for(int j = 0; j < int(poles.size()); j++){
for(int i = taken; i < int(newpoles.size()); i++){
if( newpoles[i] == poles[j] ) {
prevpole[j] = i;
taken++;
break;
}
}
}
// on fully removing a knot the knot geometry changes
std::vector<double> knots = bsp->getKnots();
std::vector<double> newknots = bspline->getKnots();
std::vector<int> prevknot(bsp->countKnots());
for(int i = 0; i < int(knots.size()); i++)
prevknot[i] = -1;
taken = 0;
for(int j = 0; j < int(knots.size()); j++){
for(int i = taken; i < int(newknots.size()); i++){
if( newknots[i] == knots[j] ) {
prevknot[j] = i;
taken++;
break;
}
}
}
const std::vector< Sketcher::Constraint * > &cvals = Constraints.getValues();
std::vector< Constraint * > newcVals(0);
// modify pole constraints
for (std::vector< Sketcher::Constraint * >::const_iterator it= cvals.begin(); it != cvals.end(); ++it) {
if((*it)->Type == Sketcher::InternalAlignment && (*it)->Second == GeoId)
{
if((*it)->AlignmentType == Sketcher::BSplineControlPoint) {
if (prevpole[(*it)->InternalAlignmentIndex]!=-1) {
assert(prevpole[(*it)->InternalAlignmentIndex] < bspline->countPoles());
Constraint * newConstr = (*it)->clone();
newConstr->InternalAlignmentIndex = prevpole[(*it)->InternalAlignmentIndex];
newcVals.push_back(newConstr);
}
else { // it is an internal alignment geometry that is no longer valid => delete it and the pole circle
delGeoId.push_back((*it)->First);
}
}
else if((*it)->AlignmentType == Sketcher::BSplineKnotPoint) {
if (prevknot[(*it)->InternalAlignmentIndex]!=-1) {
assert(prevknot[(*it)->InternalAlignmentIndex] < bspline->countKnots());
Constraint * newConstr = (*it)->clone();
newConstr->InternalAlignmentIndex = prevknot[(*it)->InternalAlignmentIndex];
newcVals.push_back(newConstr);
}
else { // it is an internal alignment geometry that is no longer valid => delete it and the knot point
delGeoId.push_back((*it)->First);
}
}
else { // it is a bspline geometry, but not a controlpoint or knot
newcVals.push_back(*it);
}
}
else {
newcVals.push_back(*it);
}
}
const std::vector< Part::Geometry * > &vals = getInternalGeometry();
std::vector< Part::Geometry * > newVals(vals);
newVals[GeoId] = bspline.release();
// Block acceptGeometry in OnChanged to avoid unnecessary checks and updates
{
Base::StateLocker lock(internaltransaction, true);
Geometry.setValues(std::move(newVals));
this->Constraints.setValues(std::move(newcVals));
}
// Trigger update now
// Update geometry indices and rebuild vertexindex now via onChanged, so that ViewProvider::UpdateData is triggered.
if (!delGeoId.empty()) {
// FIXME: Somehow this extra `Geometry.touch()` fixes a segfault.
// See https://forum.freecadweb.org/viewtopic.php?f=19&t=64962&sid=10272db50a635c633260517b14ecad37
Geometry.touch();
delGeometriesExclusiveList(delGeoId);
}
else {
Geometry.touch();
}
// handle this last return
return true;
}
int SketchObject::carbonCopy(App::DocumentObject * pObj, bool construction)
{
Base::StateLocker lock(managedoperation, true); // no need to check input data validity as this is an sketchobject managed operation.

View File

@@ -340,6 +340,15 @@ public:
*/
bool modifyBSplineKnotMultiplicity(int GeoId, int knotIndex, int multiplicityincr = 1);
/*!
\brief Inserts a knot in the BSpline at `param` with given `multiplicity`. If the knot already exists, its multiplicity is increased by `multiplicity`.
\param GeoId - the geometry of type bspline to increase the degree
\param param - the parameter value where the knot is to be placed
\param multiplicity - multiplicity of the inserted knot
\retval bool - returns true if the operation succeeded, or false if it did not succeed.
*/
bool insertBSplineKnot(int GeoId, double param, int multiplicity = 1);
/// retrieves for a Vertex number the corresponding GeoId and PosId
void getGeoVertexIndex(int VertexId, int &GeoId, PointPos &PosId) const;
int getHighestVertexIndex(void) const { return VertexId2GeoId.size() - 1; } // Most recently created

View File

@@ -287,6 +287,11 @@ If there is no such constraint an exception is raised.
<UserDocu>Increases or reduces the given BSpline knot multiplicity</UserDocu>
</Documentation>
</Methode>
<Methode Name="insertBSplineKnot">
<Documentation>
<UserDocu>Inserts a knot into the BSpline at the given param with given multiplicity. If the knot already exists, this increases the knot multiplicity by the given multiplicity.</UserDocu>
</Documentation>
</Methode>
<Methode Name="calculateAngleViaPoint">
<Documentation>
<UserDocu>

View File

@@ -1551,6 +1551,25 @@ PyObject* SketchObjectPy::modifyBSplineKnotMultiplicity(PyObject *args)
Py_Return;
}
PyObject* SketchObjectPy::insertBSplineKnot(PyObject *args)
{
int GeoId;
double knotParam;
int multiplicity = 1;
if (!PyArg_ParseTuple(args, "id|i", &GeoId, &knotParam, &multiplicity))
return 0;
if (this->getSketchObjectPtr()->insertBSplineKnot(GeoId, knotParam, multiplicity)==false) {
std::stringstream str;
str << "Knot insertion failed for: " << GeoId;
PyErr_SetString(PyExc_ValueError, str.str().c_str());
return 0;
}
Py_Return;
}
PyObject* SketchObjectPy::autoconstraint(PyObject *args)
{
double precision = Precision::Confusion() * 1000;