refactor(draw): TRIM geometry helpers come from the kernel; drop a probe test

#1511 added an orthogonal ellipse projection and a nearest-end
arc_parameter to draw/modify/geom.rs, the latter shadowing the kernel
function of the same name. Both now live in opencadkernel 237827e and
are re-exported here, so every kernel caller gets them too.

test_coincident_line_and_arc_grip_move only printed values and asserted
nothing; removed.

Pins opencadkernel 237827e, opencadgraph fe36957.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-09-28 14:08:36 +03:00
1 parent 66ac8c4ec0
commit eac19348d5
4 files changed
+7 -170

No files matched your search

+1 -100
View File
@@ -15,106 +15,7 @@ use kernel::geom2d::{self, Ellipse};
/// Re-exported unchanged: these already speak in plain `f64`, so there is no
/// call-shape difference for this module to absorb.
pub use kernel::geom2d::{lerp, normalize_angle};
/// Where `angle` sits along the arc from `start` to `end`, as `0.0..=1.0`.
///
/// Unlike `kernel::geom2d::arc_parameter` which clamps any angle outside the arc to 1.0,
/// this clamps angles in the gap outside the arc to 0.0 or 1.0 depending on which endpoint
/// they are closer to. This prevents cursor jitter near `start` from snapping to 1.0.
pub fn arc_parameter(angle: f64, start: f64, end: f64) -> f64 {
let span = kernel::geom2d::arc_span(start, end);
if span < 1e-12 {
return 0.0;
}
let norm_angle = kernel::geom2d::normalize_angle(angle);
let norm_start = kernel::geom2d::normalize_angle(start);
let delta = (norm_angle - norm_start).rem_euclid(std::f64::consts::TAU);
if delta <= span {
(delta / span).clamp(0.0, 1.0)
} else {
let gap = std::f64::consts::TAU - span;
let past_end = delta - span;
if past_end <= gap * 0.5 {
1.0
} else {
0.0
}
}
}
/// Finds the closest parametric parameter `t` on an origin-centered ellipse
/// `(x/a)^2 + (y/b)^2 = 1` for an arbitrary query point `(u, v)` in local coordinates,
/// using David Eberly's exact orthogonal projection algorithm.
///
/// Unlike radial projection `(v/b).atan2(u/a)` which deviates by up to 20-40 degrees
/// when the query point is slightly off the ellipse perimeter (e.g. within screen pick box),
/// this finds the true orthogonal projection on the ellipse regardless of zoom level.
pub fn ellipse_closest_parameter(a: f64, b: f64, u: f64, v: f64) -> f64 {
if a < 1e-12 || b < 1e-12 {
return 0.0;
}
if (a - b).abs() < 1e-9 {
return v.atan2(u);
}
if a < b {
let t_swapped = ellipse_closest_parameter(b, a, v, u);
return std::f64::consts::FRAC_PI_2 - t_swapped;
}
let sign_x = if u >= 0.0 { 1.0 } else { -1.0 };
let sign_y = if v >= 0.0 { 1.0 } else { -1.0 };
let u_abs = u.abs();
let v_abs = v.abs();
if v_abs < 1e-12 {
let x = if u_abs < (a * a - b * b) / a {
a * a * u_abs / (a * a - b * b)
} else {
a
};
let y = b * (1.0 - (x / a).powi(2)).max(0.0).sqrt();
return (sign_y * y / b).atan2(sign_x * x / a);
}
if u_abs < 1e-12 {
return (sign_y * 1.0).atan2(0.0);
}
let a2 = a * a;
let b2 = b * b;
let mut t0 = -b2 + b * v_abs;
let mut t1 = -b2 + (a * u_abs).hypot(b * v_abs);
let mut t = t0;
for _ in 0..25 {
let t_a = t + a2;
let t_b = t + b2;
if t_a.abs() < 1e-12 || t_b.abs() < 1e-12 {
break;
}
let f = (a * u_abs / t_a).powi(2) + (b * v_abs / t_b).powi(2) - 1.0;
if f.abs() < 1e-12 {
break;
}
let df = -2.0 * (a2 * u_abs * u_abs / (t_a.powi(3)) + b2 * v_abs * v_abs / (t_b.powi(3)));
let t_next = t - f / df;
if t_next > t0 && t_next < t1 {
t = t_next;
} else {
if f > 0.0 {
t0 = t;
} else {
t1 = t;
}
t = 0.5 * (t0 + t1);
}
}
let x = a2 * u_abs / (t + a2);
let y = b2 * v_abs / (t + b2);
(sign_y * y / b).atan2(sign_x * x / a)
}
pub use kernel::geom2d::{arc_parameter, ellipse_closest_parameter, lerp, normalize_angle};
/// Preview geometry keeps the density the commands have always used.
const SEGMENTS_PER_RADIAN: f64 = geom2d::DEFAULT_SEGMENTS_PER_RADIAN;