From a3efad9cafc15054800e00c9d3ad02692e51fd7a Mon Sep 17 00:00:00 2001 From: Hakan Date: Tue, 12 May 2026 17:47:54 +0300 Subject: [PATCH] fix(hatch): align EllipticArc angle logic with CircularArc Use reflection (TAU - sa, TAU - ea) for CW arcs and span.abs() for segment count, consistent with the CircularArc pattern in both hatch boundary (mod.rs) and tessellate (tessellate.rs). Co-Authored-By: Claude Sonnet 4.6 --- src/scene/mod.rs | 20 ++++++++------------ src/scene/tessellate.rs | 20 ++++++++++---------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/scene/mod.rs b/src/scene/mod.rs index b8ac34c0c..5e9147fb1 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -2175,13 +2175,14 @@ impl Scene { let ccw = arc.counter_clockwise; let (sa, ea) = if ccw { (sa, ea) } else { (TAU - sa, TAU - ea) }; let span = ea - sa; - let segs = ((span / TAU) * 32.0).ceil().max(4.0) as u32; + let segs = ((span.abs() / TAU) * 32.0).ceil().max(4.0) as u32; for i in 0..=segs { let t = sa + span * (i as f64 / segs as f64); boundary.push(to_xy(cx + r * t.cos(), cy + r * t.sin())); } } BoundaryEdge::EllipticArc(ell) => { + const TAU: f32 = std::f32::consts::TAU; let cx = ell.center.x; let cy = ell.center.y; let maj_x = ell.major_axis_endpoint.x as f32; @@ -2189,17 +2190,12 @@ impl Scene { let r_maj = (maj_x * maj_x + maj_y * maj_y).sqrt(); let r_min = r_maj * ell.minor_axis_ratio as f32; let rot = maj_y.atan2(maj_x); - let (sa, ea) = if ell.counter_clockwise { - (ell.start_angle as f32, ell.end_angle as f32) - } else { - (ell.end_angle as f32, ell.start_angle as f32) - }; - let mut end = ea; - if end < sa { - end += std::f32::consts::TAU; - } - let span = end - sa; - let segs = ((span / std::f32::consts::TAU) * 32.0).ceil().max(4.0) as u32; + let ccw = ell.counter_clockwise; + let sa = ell.start_angle as f32; + let ea = ell.end_angle as f32; + let (sa, ea) = if ccw { (sa, ea) } else { (TAU - sa, TAU - ea) }; + let span = ea - sa; + let segs = ((span.abs() / TAU) * 32.0).ceil().max(4.0) as u32; for i in 0..=segs { let t = sa + span * (i as f32 / segs as f32); let lx = r_maj * t.cos(); diff --git a/src/scene/tessellate.rs b/src/scene/tessellate.rs index baf87b27d..d22521d0a 100644 --- a/src/scene/tessellate.rs +++ b/src/scene/tessellate.rs @@ -872,7 +872,7 @@ fn legacy_geometry(entity: &EntityType, world_offset: [f64; 3]) -> Geometry { let ccw = arc.counter_clockwise; let (sa, ea) = if ccw { (sa, ea) } else { (TAU - sa, TAU - ea) }; let span = ea - sa; - let segs = ((span / TAU) * 32.0).ceil().max(4.0) as u32; + let segs = ((span.abs() / TAU) * 32.0).ceil().max(4.0) as u32; if !pts.is_empty() { pts.push([f32::NAN; 3]); } @@ -899,18 +899,18 @@ fn legacy_geometry(entity: &EntityType, world_offset: [f64; 3]) -> Geometry { let r_min = r_maj * ell.minor_axis_ratio as f32; let rot = (ell.major_axis_endpoint.y as f32) .atan2(ell.major_axis_endpoint.x as f32); - let (sa, ea) = if ell.counter_clockwise { - (ell.start_angle as f32, ell.end_angle as f32) + const TAU: f32 = std::f32::consts::TAU; + let ccw = ell.counter_clockwise; + let sa = ell.start_angle as f32; + let ea = ell.end_angle as f32; + let (sa, ea) = if ccw { + (sa, ea) } else { - (ell.end_angle as f32, ell.start_angle as f32) - }; - let span = if ea > sa { - ea - sa - } else { - ea - sa + std::f32::consts::TAU + (TAU - sa, TAU - ea) }; + let span = ea - sa; let segs = - ((span / std::f32::consts::TAU) * 32.0).ceil().max(4.0) as u32; + ((span.abs() / TAU) * 32.0).ceil().max(4.0) as u32; if !pts.is_empty() { pts.push([f32::NAN; 3]); }