Constraints: an arc solved with a negative radius is stored turned half a turn, so its points stay put (fixes #703) (#772)

This commit is contained in:
Marco Arnulfo authored and GitHub committed 2026-10-13 04:34:51 -04:00
1 parent b2ba585251
commit 6772150bc4
2 files changed
+31 -2

No files matched your search

+5 -2
View File
@@ -500,11 +500,14 @@ impl Model {
}
(EntityKind::Arc(a), Shape::Arc) => {
if let [cx, cy, r, s0, s1] = new {
// A negative radius puts every point opposite its angle: turning both
// ends half a turn keeps the same points with a positive radius.
let turn = if *r < 0.0 { std::f64::consts::PI } else { 0.0 };
a.center.x = *cx;
a.center.y = *cy;
a.radius = r.abs().max(1e-12);
a.start = cadcraft_geom::norm_angle(*s0);
a.end = cadcraft_geom::norm_angle(*s1);
a.start = cadcraft_geom::norm_angle(*s0 + turn);
a.end = cadcraft_geom::norm_angle(*s1 + turn);
}
}
(EntityKind::LwPolyline(p), Shape::Poly { n, .. }) if p.vertices.len() == n => {
+26
View File
@@ -463,3 +463,29 @@ fn set_parameter_rejects_out_of_domain_dimension() {
set_parameter(&mut d, "rad1", "2", &opts).unwrap();
assert!(all_satisfied(&d));
}
/// The solver may meet a coincidence with a negative arc radius; the stored arc must still put
/// the referenced point where the solver did (issue #703), through `add` and through `solve`.
#[test]
fn arc_solved_with_a_negative_radius_keeps_its_points() {
use std::f64::consts::FRAC_PI_2;
// (arc centre, radius, start, end, point): a semicircle with the point behind it, and the
// asymmetric case from the issue.
let cases = [((0.0, 0.0), 1.0, -FRAC_PI_2, FRAC_PI_2, (-5.0, 0.0)), ((6.694, -0.686), 0.62075, 3.4824, 1.1766, (-7.562, 4.946))];
for (c, r, a0, a1, p) in cases {
for through_solve in [false, true] {
let mut d = Drawing::default();
let a = arc(&mut d, c, r, a0, a1);
let q = add_e(&mut d, EntityKind::Point(DPoint { p: v(p.0, p.1), angle: 0.0 }));
let k = con(ConstraintKind::Coincident, &[s(a, Sub::Mid), w(q)]);
if through_solve {
d.constraints.push(Constraint { id: 1, ..k });
solve(&mut d, &SolveOptions::default()).unwrap();
} else {
ok(&mut d, k);
}
assert!(all_satisfied(&d), "{c:?} solve {through_solve}: {:?}", residuals(&d));
assert!(circ(&d, a).1 > 0.0);
}
}
}