diff --git a/crates/engine/src/cmd/modify.rs b/crates/engine/src/cmd/modify.rs index b1b29059..324d9b28 100644 --- a/crates/engine/src/cmd/modify.rs +++ b/crates/engine/src/cmd/modify.rs @@ -586,10 +586,14 @@ pub(crate) fn offset_kind(k: &EntityKind, dist: f64, side: Vec2) -> Option { // 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(); diff --git a/crates/engine/src/cmd/modify2_tests.rs b/crates/engine/src/cmd/modify2_tests.rs index 836e5c01..7c8f9262 100644 --- a/crates/engine/src/cmd/modify2_tests.rs +++ b/crates/engine/src/cmd/modify2_tests.rs @@ -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}"); +}