From ca253c277728ffe480896bee6e0b83ed95fd1798 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Wed, 15 Jul 2026 22:55:51 +0300 Subject: [PATCH] fix(solid): keep SOLID fills placed on UTM-scale drawings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `solid_hatch_model` cast each corner from absolute WCS straight to f32 and left `world_origin` at [0, 0]. At UTM magnitudes (~1e6) f32's 24-bit mantissa resolves to ~0.06 units, so the quad snapped to a grid and the fill drifted off its own outline. Hold the corners in f64 until the AABB centre is known, then store each as a small f32 offset from it — the same precision-preserving anchor `hatch_model_from_dxf` already uses for real HATCH entities. All nine callers feed through this one function, and every reader of `boundary` already adds `world_origin` back, so they go from adding zero to adding the real anchor. Co-Authored-By: Claude Opus 4.8 --- src/scene/entity.rs | 55 ++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/src/scene/entity.rs b/src/scene/entity.rs index 34a21924a..edbf65459 100644 --- a/src/scene/entity.rs +++ b/src/scene/entity.rs @@ -1740,24 +1740,43 @@ impl Scene { /// DXF SOLID corners are in "Z-order": p0-p1 top, p2-p3 bottom. /// Visual quad is p0→p1→p3→p2 (closed). pub(super) fn solid_hatch_model(solid: &DxfSolid, color: [f32; 4]) -> HatchModel { - let boundary = vec![ - [ - (solid.first_corner.x) as f32, - (solid.first_corner.y) as f32, - ], - [ - (solid.second_corner.x) as f32, - (solid.second_corner.y) as f32, - ], - [ - (solid.fourth_corner.x) as f32, - (solid.fourth_corner.y) as f32, - ], - [ - (solid.third_corner.x) as f32, - (solid.third_corner.y) as f32, - ], + // Keep the corners in f64 until the AABB centre is known, then store + // each as a small f32 offset from it — same precision-preserving anchor + // `hatch_model_from_dxf` uses. Casting the absolute WCS corner straight + // to f32 costs ~0.06 units of resolution at UTM magnitudes (~1e6), so + // the quad snapped to a grid and the fill drifted off its outline. + let corners: [[f64; 2]; 4] = [ + [solid.first_corner.x, solid.first_corner.y], + [solid.second_corner.x, solid.second_corner.y], + [solid.fourth_corner.x, solid.fourth_corner.y], + [solid.third_corner.x, solid.third_corner.y], ]; + let mut min = [f64::INFINITY; 2]; + let mut max = [f64::NEG_INFINITY; 2]; + for c in &corners { + for i in 0..2 { + if c[i] < min[i] { + min[i] = c[i]; + } + if c[i] > max[i] { + max[i] = c[i]; + } + } + } + let world_origin = if min[0].is_finite() && min[1].is_finite() { + [(min[0] + max[0]) * 0.5, (min[1] + max[1]) * 0.5] + } else { + [0.0, 0.0] + }; + let boundary = corners + .iter() + .map(|c| { + [ + (c[0] - world_origin[0]) as f32, + (c[1] - world_origin[1]) as f32, + ] + }) + .collect(); HatchModel { boundary: std::sync::Arc::new(boundary), boundary_wcs: None, @@ -1766,7 +1785,7 @@ impl Scene { color, angle_offset: 0.0, scale: 1.0, - world_origin: [0.0; 2], + world_origin, vp_scissor: None, draw_depth: 0.0, }