From bf4eed0921d9ff94611269367ce0aa19e6102a42 Mon Sep 17 00:00:00 2001 From: Ajinkya Dahale Date: Thu, 20 Jun 2024 18:39:20 +0530 Subject: [PATCH] [Sketcher] Refactor `SketchObject::split()` --- src/Mod/Sketcher/App/SketchObject.cpp | 354 +++++++++++++------------- src/Mod/Sketcher/App/SketchObject.h | 8 + 2 files changed, 187 insertions(+), 175 deletions(-) diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 704d6d76be..04983d507c 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -4110,6 +4110,116 @@ int SketchObject::trim(int GeoId, const Base::Vector3d& point) return -1; } + +void SketchObject::deriveConstraintsForPieces(const int oldId, const std::vector newIds, const Constraint* con, std::vector& newConstraints) +{ + const Part::Geometry* geo = getGeometry(oldId); + std::vector newGeos; + for (auto& newId: newIds) + newGeos.push_back(getGeometry(newId)); + + int conId = con->First; + PointPos conPos = con->FirstPos; + if (conId == oldId) { + conId = con->Second; + conPos = con->SecondPos; + } + + bool transferToAll = false; + switch (con->Type) { + case Horizontal: + case Vertical: + case Parallel: { + transferToAll = geo->is(); + break; + } + case Tangent: + case Perpendicular: { + if (geo->is()) { + transferToAll = true; + break; + } + + const Part::Geometry* conGeo = getGeometry(conId); + if (!(conGeo && conGeo->isDerivedFrom())) { + break; + } + + // For now: just transfer to the first intersection + // TODO: Actually check that there was perpendicularity earlier + // TODO: Choose piece based on parameters ("values" of the constraint) + for (size_t i = 0; i < newIds.size(); ++i) { + std::vector> intersections; + bool intersects = static_cast(newGeos[i])->intersect( + static_cast(conGeo), intersections); + + if (intersects) { + Constraint* trans = con->copy(); + trans->substituteIndex(oldId, newIds[i]); + newConstraints.push_back(trans); + break; + } + } + + break; + } + case Distance: + case DistanceX: + case DistanceY: + case PointOnObject: { + if (con->FirstPos == PointPos::none && con->SecondPos == PointPos::none && newIds.size() > 1) { + Constraint* dist = con->copy(); + dist->First = newIds.front(); + dist->FirstPos = PointPos::start; + dist->Second = newIds.back(); + dist->SecondPos = PointPos::end; + newConstraints.push_back(dist); + break; + } + + + Base::Vector3d conPoint(getPoint(conId, conPos)); + double conParam; + static_cast(geo)->closestParameter(conPoint, conParam); + // Choose based on where the closest point lies + // If it's not there, just leave this constraint out + for (size_t i = 0; i < newIds.size(); ++i) { + double newGeoFirstParam = static_cast(newGeos[i])->getFirstParameter(); + double newGeoLastParam = static_cast(newGeos[i])->getLastParameter(); + if (newGeoFirstParam <= conParam && conParam <= newGeoLastParam) { + Constraint* trans = con->copy(); + trans->First = conId; + trans->FirstPos = conPos; + trans->Second = newIds[i]; + trans->SecondPos = PointPos::none; + newConstraints.push_back(trans); + break; + } + } + + break; + } + case Radius: + case Diameter: + case Equal: { + transferToAll = geo->is() + || geo->is(); + break; + } + default: + // Release other constraints + break; + } + + if (transferToAll) { + for (auto& newId : newIds) { + Constraint* trans = con->copy(); + trans->substituteIndex(oldId, newId); + newConstraints.push_back(trans); + } + } +} + int SketchObject::split(int GeoId, const Base::Vector3d& point) { // No need to check input data validity as this is an sketchobject managed operation @@ -4120,13 +4230,13 @@ int SketchObject::split(int GeoId, const Base::Vector3d& point) return -1; } + bool originalIsPeriodic = false; const Part::Geometry* geo = getGeometry(GeoId); std::vector newIds; std::vector newConstraints; Base::Vector3d startPoint, endPoint, splitPoint; double startParam, endParam, splitParam = 0.0; - unsigned int longestPart = 0; auto createGeosFromPeriodic = [&](const Part::GeomCurve* curve, auto getCurveWithLimitParams, @@ -4141,17 +4251,17 @@ int SketchObject::split(int GeoId, const Base::Vector3d& point) auto newCurve = getCurveWithLimitParams(curve, startParam, endParam); int newId(GeoEnum::GeoUndef); newId = addGeometry(std::move(newCurve));// after here newCurve is a shell - if (newId >= 0) { - newIds.push_back(newId); - setConstruction(newId, GeometryFacade::getConstruction(curve)); - exposeInternalGeometry(newId); - - // transfer any constraints - createAndTransferConstraints(GeoId, newId); - return true; + if (newId < 0) { + return false; } - return false; + newIds.push_back(newId); + setConstruction(newId, GeometryFacade::getConstruction(curve)); + exposeInternalGeometry(newId); + + // transfer any constraints + createAndTransferConstraints(GeoId, newId); + return true; }; auto createGeosFromNonPeriodic = [&](const Part::GeomBoundedCurve* curve, @@ -4170,41 +4280,39 @@ int SketchObject::split(int GeoId, const Base::Vector3d& point) || splitParam - startParam < Precision::PConfusion()) { THROWM(ValueError, "Split point is at one of the end points of the curve."); } - if (endParam - splitParam > splitParam - startParam) { - longestPart = 1; - } // create new curves auto newCurve = getCurveWithLimitParams(curve, startParam, splitParam); int newId(GeoEnum::GeoUndef); newId = addGeometry(std::move(newCurve)); - if (newId >= 0) { - newIds.push_back(newId); - setConstruction(newId, GeometryFacade::getConstruction(curve)); - exposeInternalGeometry(newId); - - // the "second" half - newCurve = getCurveWithLimitParams(curve, splitParam, endParam); - newId = addGeometry(std::move(newCurve)); - if (newId >= 0) { - newIds.push_back(newId); - setConstruction(newId, GeometryFacade::getConstruction(curve)); - exposeInternalGeometry(newId); - - // TODO: Certain transfers and new constraint can be directly made here. - // But this may reduce readability. - // apply appropriate constraints on the new points at split point and - // transfer constraints from start and end of original spline - createAndTransferConstraints(GeoId, newIds[0], newIds[1]); - return true; - } + if (newId < 0) { + return false; } + newIds.push_back(newId); + setConstruction(newId, GeometryFacade::getConstruction(curve)); + exposeInternalGeometry(newId); - return false; + // the "second" half + newCurve = getCurveWithLimitParams(curve, splitParam, endParam); + newId = addGeometry(std::move(newCurve)); + if (newId < 0) { + return false; + } + newIds.push_back(newId); + setConstruction(newId, GeometryFacade::getConstruction(curve)); + exposeInternalGeometry(newId); + + // TODO: Certain transfers and new constraint can be directly made here. + // But this may reduce readability. + // apply appropriate constraints on the new points at split point and + // transfer constraints from start and end of original spline + createAndTransferConstraints(GeoId, newIds[0], newIds[1]); + return true; }; bool ok = false; if (geo->is()) { + originalIsPeriodic = false; ok = createGeosFromNonPeriodic( static_cast(geo), [](const Part::GeomCurve* curve, double startParam, double endParam) { @@ -4227,6 +4335,7 @@ int SketchObject::split(int GeoId, const Base::Vector3d& point) }); } else if (geo->is()) { + originalIsPeriodic = true; ok = createGeosFromPeriodic( static_cast(geo), [](const Part::GeomCurve* curve, double startParam, double endParam) { @@ -4240,6 +4349,7 @@ int SketchObject::split(int GeoId, const Base::Vector3d& point) }); } else if (geo->is()) { + originalIsPeriodic = true; ok = createGeosFromPeriodic( static_cast(geo), [](const Part::GeomCurve* curve, double startParam, double endParam) { @@ -4253,6 +4363,7 @@ int SketchObject::split(int GeoId, const Base::Vector3d& point) }); } else if (geo->is()) { + originalIsPeriodic = false; ok = createGeosFromNonPeriodic( static_cast(geo), [](const Part::GeomCurve* curve, double startParam, double endParam) { @@ -4284,6 +4395,7 @@ int SketchObject::split(int GeoId, const Base::Vector3d& point) }); } else if (geo->isDerivedFrom(Part::GeomArcOfConic::getClassTypeId())) { + originalIsPeriodic = false; ok = createGeosFromNonPeriodic( static_cast(geo), [](const Part::GeomCurve* curve, double startParam, double endParam) { @@ -4314,7 +4426,8 @@ int SketchObject::split(int GeoId, const Base::Vector3d& point) const Part::GeomBSplineCurve* bsp = static_cast(geo); // what to do for periodic b-splines? - if (bsp->isPeriodic()) { + originalIsPeriodic = bsp->isPeriodic(); + if (originalIsPeriodic) { ok = createGeosFromPeriodic( static_cast(geo), [](const Part::GeomCurve* curve, double startParam, double endParam) { @@ -4354,154 +4467,45 @@ int SketchObject::split(int GeoId, const Base::Vector3d& point) } } - if (ok) { - std::vector oldConstraints; - getConstraintIndices(GeoId, oldConstraints); - - const auto& allConstraints = this->Constraints.getValues(); - - // keep constraints on internal geometries so they are deleted - // when the old curve is deleted - oldConstraints.erase(std::remove_if(oldConstraints.begin(), - oldConstraints.end(), - [=](const auto& i) { - return allConstraints[i]->Type == InternalAlignment; - }), - oldConstraints.end()); - - for (unsigned int i = 0; i < oldConstraints.size(); ++i) { - - Constraint* con = allConstraints[oldConstraints[i]]; - int conId = con->First; - PointPos conPos = con->FirstPos; - if (conId == GeoId) { - conId = con->Second; - conPos = con->SecondPos; - } - - bool transferToAll = false; - switch (con->Type) { - case Horizontal: - case Vertical: - case Parallel: { - transferToAll = geo->is(); - break; - } - case Tangent: - case Perpendicular: { - unsigned int initial = 0; - unsigned int limit = newIds.size(); - - if (geo->isDerivedFrom(Part::GeomArcOfConic::getClassTypeId())) { - const Part::Geometry* conGeo = getGeometry(conId); - - if (conGeo && conGeo->isDerivedFrom(Part::GeomCurve::getClassTypeId())) { - std::vector> intersections; - bool intersects[2]; - auto* geo1 = getGeometry(newIds[0]); - auto* geo2 = getGeometry(newIds[1]); - - intersects[0] = static_cast(geo1)->intersect( - static_cast(conGeo), intersections); - intersects[1] = static_cast(geo2)->intersect( - static_cast(conGeo), intersections); - - initial = longestPart; - if (intersects[0] != intersects[1]) { - initial = intersects[1] ? 1 : 0; - } - limit = initial + 1; - } - } - - for (unsigned int i = initial; i < limit; ++i) { - Constraint* trans = con->copy(); - trans->substituteIndex(GeoId, newIds[i]); - newConstraints.push_back(trans); - } - break; - } - case Distance: - case DistanceX: - case DistanceY: - case PointOnObject: { - if (con->FirstPos == PointPos::none && con->SecondPos == PointPos::none) { - Constraint* dist = con->copy(); - dist->First = newIds[0]; - dist->FirstPos = PointPos::start; - dist->Second = newIds[1]; - dist->SecondPos = PointPos::end; - newConstraints.push_back(dist); - } - else { - Constraint* trans = con->copy(); - trans->First = conId; - trans->FirstPos = conPos; - trans->SecondPos = PointPos::none; - - Base::Vector3d conPoint(getPoint(conId, conPos)); - int targetId = newIds[0]; - - // for non-periodic curves, see if second curve is more appropriate - if (geo->is()) { - Base::Vector3d projPoint( - conPoint.Perpendicular(startPoint, endPoint - startPoint)); - Base::Vector3d splitDir = splitPoint - startPoint; - if ((projPoint - startPoint) * splitDir > splitDir * splitDir) { - targetId = newIds[1]; - } - } - else if (geo->isDerivedFrom(Part::GeomArcOfConic::getClassTypeId())) { - double conParam; - static_cast(geo)->closestParameter( - conPoint, conParam); - if (conParam > splitParam) - targetId = newIds[1]; - } - trans->Second = targetId; - - newConstraints.push_back(trans); - } - break; - } - case Radius: - case Diameter: - case Equal: { - transferToAll = geo->is() - || geo->is(); - break; - } - default: - // Release other constraints - break; - } - - if (transferToAll) { - for (auto& newId : newIds) { - Constraint* trans = con->copy(); - trans->substituteIndex(GeoId, newId); - newConstraints.push_back(trans); - } - } + if (!ok) { + for (auto& cons : newConstraints) { + delete cons; } - if (noRecomputes) - solve(); - - delConstraints(oldConstraints); - addConstraints(newConstraints); + return -1; } + std::vector oldConstraints; + getConstraintIndices(GeoId, oldConstraints); + + const auto& allConstraints = this->Constraints.getValues(); + + // keep constraints on internal geometries so they are deleted + // when the old curve is deleted + oldConstraints.erase(std::remove_if(oldConstraints.begin(), + oldConstraints.end(), + [=](const auto& i) { + return allConstraints[i]->Type == InternalAlignment; + }), + oldConstraints.end()); + + for (const auto& oldConstrId: oldConstraints) { + Constraint* con = allConstraints[oldConstrId]; + deriveConstraintsForPieces(GeoId, newIds, con, newConstraints); + } + + if (noRecomputes) + solve(); + + delConstraints(oldConstraints); + addConstraints(newConstraints); + for (auto& cons : newConstraints) { delete cons; } - if (ok) { - delGeometry(GeoId); - return 0; - } - - return -1; + delGeometry(GeoId); + return 0; } int SketchObject::join(int geoId1, Sketcher::PointPos posId1, int geoId2, Sketcher::PointPos posId2, int continuity) diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index 3a462d5a82..5b278961fc 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -406,6 +406,14 @@ public: int trim(int geoId, const Base::Vector3d& point); /// extend a curve int extend(int geoId, double increment, PointPos endPoint); + /// Once smaller pieces have been created from a larger curve (by split or trim, say), derive + /// the constraint that will replace the given one (which is to be deleted). NOTE: Currently + /// assuming all constraints on the end points of the old curve have been transferred or + /// destroyed + void deriveConstraintsForPieces(const int oldId, + const std::vector newIds, + const Constraint* con, + std::vector& newConstraints); /// split a curve int split(int geoId, const Base::Vector3d& point); /*!