diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 74aa65c000..f4a4d57215 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 @@ -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)) { @@ -3693,8 +3721,7 @@ bool SketchObject::deriveConstraintsForPieces( case Vertical: case Parallel: { transferToAll = geo->is(); - break; - } + } break; case Tangent: case Perpendicular: { if (geo->is()) { @@ -3728,9 +3755,63 @@ 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; - } + // 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, newIds[idx.value()]); + 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 + // 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) { + 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 +3859,7 @@ bool SketchObject::deriveConstraintsForPieces( return true; } } - - break; - } + } break; case Radius: case Diameter: case Equal: { @@ -3794,7 +3873,7 @@ bool SketchObject::deriveConstraintsForPieces( newConstraints.push_back(trans); break; } - } + } break; default: // Release other constraints break; @@ -9493,7 +9572,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 +9614,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 d1223f2e75..b4a0a724ca 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 true if the sketch has 1 or more block constraint