diff --git a/src/app/update/viewport.rs b/src/app/update/viewport.rs index a727351ca..bb84021ba 100644 --- a/src/app/update/viewport.rs +++ b/src/app/update/viewport.rs @@ -1753,7 +1753,7 @@ impl OpenCADStudio { .scene .document .get_entity(target.handle)?; - crate::scene::parametric_constraints::driven_ref_for_grip( + crate::scene::parametric_constraints::grip_solve_anchor_ref( entity, target.handle, target.grip_id, @@ -6141,11 +6141,9 @@ mod selection_preview_tests { current[1], before[1], "constrained neighbor must follow each grip frame" ); - let start_delta = current[0][0] - before[0][0]; - let end_delta = current[0][1] - before[0][1]; - assert!( - (start_delta + end_delta).length_squared() > 1.0e-8, - "the opposite endpoint must not mirror the dragged endpoint around a fixed midpoint" + assert_eq!( + current[0][0], before[0][0], + "the endpoint opposite the dragged grip must stay fixed" ); } let _ = app.on_viewport_left_release(); diff --git a/src/scene/parametric_constraints.rs b/src/scene/parametric_constraints.rs index 3d774584a..982f2d94f 100644 --- a/src/scene/parametric_constraints.rs +++ b/src/scene/parametric_constraints.rs @@ -83,17 +83,17 @@ impl ParametricRef { } } -/// Point reference driven by a native entity grip. Segment midpoint and -/// scalar radius grips deliberately return None because they do not identify -/// one stored point parameter. -pub(crate) fn driven_ref_for_grip( +/// Point reference temporarily pinned while solving a native grip edit. +/// A line endpoint anchors its opposite endpoint so the grabbed endpoint is +/// projected onto the constraint solution instead of moving the whole line. +pub(crate) fn grip_solve_anchor_ref( entity: &acadrust::EntityType, handle: Handle, grip_id: usize, ) -> Option { match entity { acadrust::EntityType::Line(_) if grip_id <= 1 => { - Some(ParametricRef::point(handle, grip_id as i32)) + Some(ParametricRef::point(handle, 1 - grip_id as i32)) } acadrust::EntityType::LwPolyline(polyline) if grip_id < polyline.vertices.len() => { Some(ParametricRef::point(handle, grip_id as i32)) @@ -1888,18 +1888,18 @@ mod tests { )); assert_eq!( - driven_ref_for_grip(&arc, handle, 0), + grip_solve_anchor_ref(&arc, handle, 0), Some(ParametricRef::center(handle)) ); assert_eq!( - driven_ref_for_grip(&arc, handle, 1), + grip_solve_anchor_ref(&arc, handle, 1), Some(ParametricRef::point(handle, 0)) ); assert_eq!( - driven_ref_for_grip(&arc, handle, 2), + grip_solve_anchor_ref(&arc, handle, 2), Some(ParametricRef::point(handle, 1)) ); - assert_eq!(driven_ref_for_grip(&arc, handle, 3), None); + assert_eq!(grip_solve_anchor_ref(&arc, handle, 3), None); } #[test] diff --git a/src/scene/parametric_solve.rs b/src/scene/parametric_solve.rs index 535faf3b1..1a99c817c 100644 --- a/src/scene/parametric_solve.rs +++ b/src/scene/parametric_solve.rs @@ -1942,10 +1942,10 @@ fn solve_scope( sys.add_constraint(Rc::new(Difference::new(el.center.y, el.focus1.y, dy))); } - // The first ordered Coincident selection is the anchor for this solve. - // Pin either its addressed point or every defining parameter of its whole - // curve temporarily; the relation remains persistent, while these pins do - // not enter the drawing's constraint graph. + // Temporarily make each requested anchor a fixed kernel input. Marking the + // geometry parameters as driven keeps them exact and out of the solver's + // free-variable list; an approximate equality equation would still allow + // visible drift after repeated grip frames. for reference in driven_refs { if !set .constraints @@ -1996,9 +1996,7 @@ fn solve_scope( } }; for parameter in params_to_pin { - let value = sys.store().get(parameter); - let target = sys.add_param(value, true); - sys.add_constraint(Rc::new(Equal::new(parameter, target, 1.0))); + sys.store_mut().set_driven(parameter, true); } }