From 78e57295204cdae3b575312bf7bc7b7ca65b8f8c Mon Sep 17 00:00:00 2001 From: Ajinkya Dahale Date: Wed, 3 Dec 2025 11:03:13 +0530 Subject: [PATCH 1/2] Sketcher: Handle angle constraint on sketchobject changes Primarily trim, but also expected to affect split, extend etc. --- src/Mod/Sketcher/App/SketchObject.cpp | 63 ++++++++++++++++++++++----- src/Mod/Sketcher/App/SketchObject.h | 4 +- 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 6eeac89950..eddf23abd3 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -3417,7 +3417,7 @@ void createNewConstraintsForTrim( } // We have already transferred all constraints on endpoints to the new pieces. // If there is still any left, this means one of the remaining pieces was degenerate. - if (!con->involvesGeoIdAndPosId(GeoId, PointPos::none)) { + if (!(con->Type == Angle || con->involvesGeoIdAndPosId(GeoId, PointPos::none))) { continue; } // constraint has not yet been changed @@ -3693,8 +3693,7 @@ bool SketchObject::deriveConstraintsForPieces( case Vertical: case Parallel: { transferToAll = geo->is(); - break; - } + } break; case Tangent: case Perpendicular: { if (geo->is()) { @@ -3728,9 +3727,53 @@ bool SketchObject::deriveConstraintsForPieces( return true; } } + } break; + case Angle: { + const auto [thirdGeo, thirdPos] = con->getElement(2); + if (thirdGeo == oldId) { + // TODO: transfer to a coincident point, + // is it possible to do it somewhere else and avoid? + std::vector GeoIdList; + std::vector PosIdList; + getDirectlyCoincidentPoints(thirdGeo, thirdPos, GeoIdList, PosIdList); + if (GeoIdList.size() <= 1) { + // TODO: Even in this case we can add a point + return false; + } - break; - } + // TODO: transfer only to the curve that actually intersects + for (auto& newId : newIds) { + Constraint* trans = con->copy(); + trans->substituteIndexAndPos(GeoIdList[0], PosIdList[0], GeoIdList[1], PosIdList[1]); + trans->substituteIndex(oldId, newId); + newConstraints.push_back(trans); + } + + return true; + } + else if (thirdGeo != GeoEnum::GeoUndef) { + // Angle via point but the point won't change, can transfer to all or first + // TODO: transfer only to the curve that actually intersects + transferToAll = true; + break; + } + else if (std::ranges::any_of(newGeos, [](const Part::Geometry* geo) { + return !geo->is(); + })) { + // Angle without a specific point is only supported when _all_ geometries are lines. + // If the original was a line, we may reach this point, for example, when converting + // it to NURBS. + + // NOTE: We may decide to change this logic in the future. Follows + // `Sketch::addConstraint`. + return false; + } + else { + // Straight up angle, can transfer to all or first + transferToAll = true; + break; + } + } break; case Distance: case DistanceX: case DistanceY: @@ -3778,9 +3821,7 @@ bool SketchObject::deriveConstraintsForPieces( return true; } } - - break; - } + } break; case Radius: case Diameter: case Equal: { @@ -3794,7 +3835,7 @@ bool SketchObject::deriveConstraintsForPieces( newConstraints.push_back(trans); break; } - } + } break; default: // Release other constraints break; @@ -9493,7 +9534,7 @@ const std::map SketchObject::getAllCoincidentPoints(int void SketchObject::getDirectlyCoincidentPoints(int GeoId, PointPos PosId, std::vector& GeoIdList, - std::vector& PosIdList) + std::vector& PosIdList) const { const std::vector& constraints = this->Constraints.getValues(); @@ -9535,7 +9576,7 @@ void SketchObject::getDirectlyCoincidentPoints(int GeoId, PointPos PosId, } void SketchObject::getDirectlyCoincidentPoints(int VertexId, std::vector& GeoIdList, - std::vector& PosIdList) + std::vector& PosIdList) const { int GeoId; PointPos PosId; diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index f4135ec3c8..595b5e53d6 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -640,12 +640,12 @@ public: PointPos PosId, std::vector& GeoIdList, std::vector& PosIdList - ); + ) const; void getDirectlyCoincidentPoints( int VertexId, std::vector& GeoIdList, std::vector& PosIdList - ); + ) const; bool arePointsCoincident(int GeoId1, PointPos PosId1, int GeoId2, PointPos PosId2); /// returns a list of indices of all constraints involving given GeoId From 0800791a0f00c3e12a2cc4947c66cffb8e75bc3f Mon Sep 17 00:00:00 2001 From: Ajinkya Dahale Date: Fri, 12 Dec 2025 11:23:00 +0530 Subject: [PATCH 2/2] Sketcher: Only transfer angle to relevant segment on trim etc. --- src/Mod/Sketcher/App/SketchObject.cpp | 52 +++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index eddf23abd3..c560f2511e 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -3444,6 +3444,34 @@ void createNewConstraintsForTrim( } } +std::optional findPieceContainingPoint( + const SketchObject* obj, + const Part::Geometry* geo, + const Base::Vector3d& point, + const std::vector& newIds, + const std::vector& newGeos +) +{ + double conParam; + auto* geoAsCurve = static_cast(geo); + geoAsCurve->closestParameter(point, 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(); + // For periodic curves the point may need a full revolution + if ((newGeoFirstParam - conParam) > Precision::PApproximation() && obj->isClosedCurve(geo)) { + conParam += (geoAsCurve->getLastParameter() - geoAsCurve->getFirstParameter()); + } + if ((newGeoFirstParam - conParam) <= Precision::PApproximation() + && (conParam - newGeoLastParam) <= Precision::PApproximation()) { + return i; + } + } + return std::nullopt; +} + int SketchObject::trim(int GeoId, const Base::Vector3d& point) { if (!isGeoIdAllowedForTrim(this, GeoId)) { @@ -3741,20 +3769,30 @@ bool SketchObject::deriveConstraintsForPieces( return false; } - // TODO: transfer only to the curve that actually intersects - for (auto& newId : newIds) { + // transfer only to the curve that actually intersects + Base::Vector3d point(getPoint(thirdGeo, thirdPos)); + std::optional idx = findPieceContainingPoint(this, geo, point, newIds, newGeos); + + if (idx.has_value()) { Constraint* trans = con->copy(); trans->substituteIndexAndPos(GeoIdList[0], PosIdList[0], GeoIdList[1], PosIdList[1]); - trans->substituteIndex(oldId, newId); + trans->substituteIndex(oldId, newIds[idx.value()]); newConstraints.push_back(trans); + return true; } - - return true; } else if (thirdGeo != GeoEnum::GeoUndef) { // Angle via point but the point won't change, can transfer to all or first - // TODO: transfer only to the curve that actually intersects - transferToAll = true; + // transfer only to the curve that actually intersects + Base::Vector3d point(getPoint(thirdGeo, thirdPos)); + std::optional idx = findPieceContainingPoint(this, geo, point, newIds, newGeos); + + if (idx.has_value()) { + Constraint* trans = con->copy(); + trans->substituteIndex(oldId, newIds[idx.value()]); + newConstraints.push_back(trans); + return true; + } break; } else if (std::ranges::any_of(newGeos, [](const Part::Geometry* geo) {