forked from 1b-agent/opencadstudio-upstream
Fix ellipse solver drift: pin focus1 offset from center, not focus1 itself
An ocs_gcs Ellipse stores focus1 as an absolute point rather than an offset from center, so a constraint that pulls only on center (e.g. Concentric) left focus1's absolute coordinates untouched while center moved — changing the derived focus1-center vector that major_axis and minor_axis_ratio are computed from on write-back, so the ellipse visibly rotated and reshaped even though nothing asked it to. solve_scope now adds two Difference constraints per registered ellipse pinning focus1.x-center.x and focus1.y-center.y to their pre-solve values, so focus1 translates rigidly with center unless something actually constrains it. Fixed's Ellipse arm no longer re-pins focus1 directly since that's now redundant with these rules. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
d27eb625ca
commit
faf3473b2b
2 files changed
+109
-8
No files matched your search
@@ -947,6 +947,48 @@ fn a_tilted_ellipse_round_trips_through_the_solver_without_drifting() {
|
||||
assert!((ratio - 0.6).abs() < 1e-6, "minor/major ratio should not have drifted: {ratio}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn concentric_constraint_does_not_reshape_the_ellipse_when_its_center_actually_moves() {
|
||||
// Regression test for a real solver bug: `a_tilted_ellipse_round_trips_
|
||||
// through_the_solver_without_drifting` (above) uses a circle that's
|
||||
// ALREADY concentric with the ellipse, so Concentric's equations start
|
||||
// out satisfied and the ellipse's center never actually has to move —
|
||||
// it doesn't exercise the bug at all. Here the circle starts elsewhere,
|
||||
// forcing a real center move, which is exactly when `ocs_gcs::geo::
|
||||
// Ellipse`'s absolute `focus1` point used to get left behind: `center`
|
||||
// moved to meet the circle, `focus1` didn't (nothing else referenced
|
||||
// it), so the derived major-axis direction/length and minor/major
|
||||
// ratio changed even though only a center-to-center constraint was
|
||||
// ever applied. The fix ties `focus1`'s offset from `center` to its
|
||||
// seeded value, so it translates along with `center` instead.
|
||||
let mut scene = Scene::new();
|
||||
let ellipse = add_ellipse(&mut scene, 0.0, 0.0, (4.0, 0.0), 0.5);
|
||||
let circle = add_circle(&mut scene, 10.0, 7.0, 1.5);
|
||||
|
||||
scene.sketch_constraint_set_mut(SketchScope::ModelSpace).add(
|
||||
ConstraintKind::Concentric,
|
||||
vec![SketchRef::center(ellipse), SketchRef::center(circle)],
|
||||
None,
|
||||
);
|
||||
scene.bump_entities(&[(ellipse, ChangeKind::Modified), (circle, ChangeKind::Modified)]);
|
||||
|
||||
let (ellipse_center, major_axis, ratio) = ellipse_geom(&scene, ellipse);
|
||||
let (circle_center, _) = circle_geom(&scene, circle);
|
||||
|
||||
assert!(
|
||||
(ellipse_center.x - circle_center.x).abs() < 1e-6 && (ellipse_center.y - circle_center.y).abs() < 1e-6,
|
||||
"concentric constraint should still hold: ellipse={ellipse_center:?} circle={circle_center:?}"
|
||||
);
|
||||
// The whole point: the center was free to move (nothing else pinned
|
||||
// either entity), so it's not expected to still sit at (0,0) — but the
|
||||
// ellipse's *shape* must be exactly what it was before solving.
|
||||
assert!(
|
||||
(major_axis.x - 4.0).abs() < 1e-6 && (major_axis.y - 0.0).abs() < 1e-6,
|
||||
"major axis must not rotate or resize just because the center moved: {major_axis:?}"
|
||||
);
|
||||
assert!((ratio - 0.5).abs() < 1e-6, "minor/major ratio must not change just because the center moved: {ratio}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixed_constraint_holds_an_ellipse_in_place_despite_a_connected_edit() {
|
||||
let mut scene = Scene::new();
|
||||
|
||||
Reference in new issue
Block a user