Offset elliptical arcs towards the picked side (#1475)

Found while testing crates/engine/src/cmd/modify.rs:584-597 (offset_kind, EntityKind::Ellipse branch)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
NetterSilver
2026-10-19 02:32:52 -04:00
committed by GitHub
co-authored by Claude Haiku 5.5
parent bf6d9fd46a
commit 1738d3805e
2 changed files with 22 additions and 3 deletions
+7 -3
View File
@@ -586,10 +586,14 @@ pub(crate) fn offset_kind(k: &EntityKind, dist: f64, side: Vec2) -> Option<Entit
EntityKind::Ellipse(e) => {
// Approximate with a polyline offset of the tessellation.
let ge = cadcraft_geom::Ellipse { center: e.center.xy(), major: e.major.xy(), ratio: e.ratio, start: e.start, end: e.end };
// Inside = on the centre side of the full ellipse, even for an open arc.
let inside = {
let mut pts = Vec::new();
ge.tessellate(e.major.xy().len() * 1e-3, &mut pts);
cadcraft_geom::point_in_polygon(&pts, side)
let a = e.major.xy().len();
let b = a * e.ratio.abs();
let u = e.major.xy().normalized();
let d = side - e.center.xy();
let (x, y) = (d.dot(u) * b, d.dot(u.perp()) * a);
x * x + y * y < (a * b) * (a * b)
};
// offset_points shifts along the left normal, which points into a counter-clockwise ellipse.
let mut pts = Vec::new();
+15
View File
@@ -754,3 +754,18 @@ fn offset_polyline_arc_bulge_follows_joined_endpoints() {
assert!(near(arc.center, 10.0, 5.0), "arc centre drifted to {:?}", arc.center);
assert!((arc.radius - 4.0).abs() < 1e-6, "radius {}", arc.radius);
}
#[test]
fn offset_elliptical_arc_follows_picked_side() {
let max_x = |side: Value| {
let mut s = Session::new();
let e = h(&s.execute("ellipse", &json!({"center": [0, 0], "major": [10, 0], "ratio": 0.5, "start": 0, "end": 90})).unwrap());
s.execute("offset", &json!({"handle": e, "distance": 1, "side": side})).unwrap();
let p = poly(&last(&s));
p.vertices.iter().map(|v| v.p.x).fold(f64::MIN, f64::max)
};
let centre = max_x(json!([0, 0]));
assert!((centre - 9.0).abs() < 0.05, "centre side went outward: max x = {centre}");
let outer = max_x(json!([20, 20]));
assert!((outer - 11.0).abs() < 0.05, "outer side went inward: max x = {outer}");
}