Match Fix geometric constraint behavior

1) FXCONSTRAINT (and the new GCFIX name) prompts "Select point or [Object]
   <Object>:" — a pick fixes that constraint point; Enter or O asks for one
   line, polyline segment, circle, arc, ellipse or spline; misses and
   unsupported objects report the reference messages and re-prompt.
2) Point, block, text, MText, attribute and table insertion points are
   addressable constraint points.
3) Re-applying Fixed to the same point or object reports that the constraint
   already exists instead of adding a duplicate.
4) A persistent Fixed holds its pre-edit geometry through MOVE, ROTATE,
   SCALE, STRETCH, grip and Properties edits: fixed points stay, free points
   of a partly fixed entity follow the edit, and size retention skips fixed
   entities.
5) Object mode on a polyline fixes the picked segment (natively persisted)
   instead of the whole polyline; arc segments pin endpoints, center and
   radius.
6) Ribbon label "Fix" with a padlock icon.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
ramox81andClaude Fable 5.1 committed 2026-09-18 15:38:33 +03:00
1 parent 2b38f798c0
commit f998b59193
9 files changed
+483 -206

No files matched your search

+28
View File
@@ -587,7 +587,29 @@ impl ParametricConstraintSet {
///
/// Solver-side registration reads raw entity fields directly. This helper is
/// for UI-side consumers that need the current world-space position.
/// The single addressable point (marker `0`) of a point-like entity: the
/// node, insertion, or table origin the solver registers as its
/// `EntityGeom::Point` / text baseline start. `None` for curve entities,
/// whose marker `0` is a `source_points()` endpoint.
pub(crate) fn insertion_point(entity: &acadrust::EntityType) -> Option<Vector3> {
match entity {
acadrust::EntityType::Point(point) => Some(point.location),
acadrust::EntityType::Insert(insert) => Some(insert.insert_point),
acadrust::EntityType::Text(text) => Some(text.insertion_point),
acadrust::EntityType::MText(text) => Some(text.insertion_point),
acadrust::EntityType::AttributeDefinition(attribute) => Some(attribute.insertion_point),
acadrust::EntityType::AttributeEntity(attribute) => Some(attribute.insertion_point),
acadrust::EntityType::Table(table) => Some(table.insertion_point),
_ => None,
}
}
pub(crate) fn resolve_point(entity: &acadrust::EntityType, marker: i32) -> Option<Vector3> {
if marker == 0 {
if let Some(point) = insertion_point(entity) {
return Some(point);
}
}
if marker == -3 {
return match entity {
acadrust::EntityType::Circle(circle) => Some(circle.center_wcs()),
@@ -645,6 +667,12 @@ pub(crate) fn parametric_point_candidates(
.enumerate()
.map(|(marker, point)| (marker as i32, point))
.collect();
// A node / insertion point is a constraint point too (the reference
// fixes a POINT via NODe and TEXT via INSert), and these entities have
// no `source_points()` to collide with marker `0`.
if let Some(point) = insertion_point(entity) {
points.push((0, point));
}
match entity {
acadrust::EntityType::Circle(circle) => points.push((-3, circle.center_wcs())),
acadrust::EntityType::Arc(arc) => points.push((-3, arc.center_wcs())),