From 14b5067e25cbd045a235ea3d67533c69cd4d152d Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Thu, 11 Jun 2026 00:41:16 +0300 Subject: [PATCH] =?UTF-8?q?fix(hatch):=20mirrored-block=20arcs=20=E2=80=94?= =?UTF-8?q?=20bump=20acadrust,=20document=20angle=20convention?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hatches inside mirrored INSERTs (x_scale = -1, e.g. the *U blocks in xref_Plan-Sade) drew some boundary arcs swept the wrong way, covering huge complementary regions — in the pattern clip before EXPLODE, and in both pattern and boundary wires after (the exploded entity bakes the broken angles). Root cause was in acadrust's transform_hatch, not the renderer: DXF stores CW (ccw=false) boundary-arc angles MIRRORED (verified against AutoCAD output by endpoint continuity — mirrored interpretation gives Δ=0.0 against adjacent edges), and the transform stored geometric angles after flipping the direction flag. The legacy (TAU-θ) sampling flip in arc_signed_span is exactly right for file data and stays; its comment now records the verification so the convention isn't "fixed" away again. Co-Authored-By: Claude Opus 4.8 --- Cargo.lock | 2 +- src/scene/tess_util.rs | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 5f7f9561..0ade86b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,7 +56,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "acadrust" version = "0.3.4" -source = "git+https://github.com/HakanSeven12/acadrust?branch=main#db110124facbab3fbb4d947871638d73dc8b8a3a" +source = "git+https://github.com/HakanSeven12/acadrust?branch=main#ddd6888b261cb104fcf3acd6e62dc197f5d4e769" dependencies = [ "ahash", "anyhow", diff --git a/src/scene/tess_util.rs b/src/scene/tess_util.rs index c4bd703e..b2a2154f 100644 --- a/src/scene/tess_util.rs +++ b/src/scene/tess_util.rs @@ -31,6 +31,15 @@ pub type FallbackGeometry = ( /// semantics are preserved on real files. (Wrap-through-2π is a known /// edge case in that convention; do not "fix" it without a wider audit /// of how upstream writers emit CW boundary arcs.) +/// +/// VERIFIED against real AutoCAD output (KSR-039 DWG, CW arc edges with +/// line neighbours): the stored angles of a `ccw = false` arc edge are +/// MIRRORED — the true point is `center + r·(cos(TAU-θ), sin(TAU-θ))`. +/// Sampling the mirrored parameter directly (this function's flip) gives +/// exact endpoint continuity with the adjacent edges (Δ = 0.000000), +/// while interpreting them as true angles lands tens of units away. Any +/// code that *produces* CW boundary arcs (mirror transforms, explode) +/// must therefore store mirrored angles, not geometric ones. pub fn arc_signed_span(start: f64, end: f64, ccw: bool) -> (f64, f64) { const TAU: f64 = std::f64::consts::TAU; let (sa, ea) = if ccw { (start, end) } else { (TAU - start, TAU - end) };