From 5f69d8c3f0d86ac0cbbba4854ea2bb9173f486fa Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Mon, 27 Apr 2026 18:21:40 +0300 Subject: [PATCH] fix: apply world_offset to hatch, solid, and wipeout boundary coordinates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hatch_model_from_dxf(), solid_hatch_model(), and wipeout_boundary_2d() were converting DXF world-space coordinates directly to f32 without subtracting world_offset. After the world_offset change moved all wire geometry to local space, hatches/solids/wipeouts rendered at the wrong position (displaced by the centroid offset, up to ~4M m for Turkish UTM). All three functions now accept world_offset and apply it to every boundary vertex before the f64→f32 cast. Co-Authored-By: Claude Sonnet 4.6 --- src/scene/mod.rs | 67 +++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/src/scene/mod.rs b/src/scene/mod.rs index a210b6d6d..bf0a2d646 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -1235,10 +1235,10 @@ impl Scene { pub fn add_entity(&mut self, mut entity: EntityType) -> Handle { let hatch_seed = if let EntityType::Hatch(dxf) = &entity { let color = self.render_style(&entity).0; - Self::hatch_model_from_dxf(dxf, color) + Self::hatch_model_from_dxf(dxf, color, self.world_offset) } else if let EntityType::Solid(solid) = &entity { let color = self.render_style(&entity).0; - Some(Self::solid_hatch_model(solid, color)) + Some(Self::solid_hatch_model(solid, color, self.world_offset)) } else { None }; @@ -1473,7 +1473,7 @@ impl Scene { continue; } let color = self.render_style(&EntityType::Hatch(dxf.clone())).0; - if let Some(mut model) = Self::hatch_model_from_dxf(&dxf, color) { + if let Some(mut model) = Self::hatch_model_from_dxf(&dxf, color, self.world_offset) { if selected { model.color = [0.15, 0.55, 1.00, model.color[3]]; } @@ -1537,7 +1537,7 @@ impl Scene { { continue; } - let boundary = Self::wipeout_boundary_2d(wo); + let boundary = Self::wipeout_boundary_2d(wo, self.world_offset); if boundary.len() >= 3 { let mut fill_color = bg_color; if self.selected.contains(&wo.common.handle) { @@ -1557,16 +1557,17 @@ impl Scene { } /// Compute the 2D (XY) boundary polygon for a Wipeout entity. - fn wipeout_boundary_2d(wo: &acadrust::entities::Wipeout) -> Vec<[f32; 2]> { + fn wipeout_boundary_2d(wo: &acadrust::entities::Wipeout, world_offset: [f64; 3]) -> Vec<[f32; 2]> { use acadrust::entities::WipeoutClipType; + let [wox, woy, _woz] = world_offset; let is_polygon = wo.clipping_enabled && wo.clip_boundary_vertices.len() >= 3 && matches!(wo.clip_type, WipeoutClipType::Polygonal); if is_polygon { - let ox = wo.insertion_point.x as f32; - let oy = wo.insertion_point.y as f32; + let ox = (wo.insertion_point.x - wox) as f32; + let oy = (wo.insertion_point.y - woy) as f32; wo.clip_boundary_vertices.iter().map(|v| { let wx = (wo.u_vector.x * v.x * wo.size.x + wo.v_vector.x * v.y * wo.size.y) as f32; let wy = (wo.u_vector.y * v.x * wo.size.x + wo.v_vector.y * v.y * wo.size.y) as f32; @@ -1574,8 +1575,8 @@ impl Scene { }).collect() } else { // Rectangular boundary from 4 corners. - let ox = wo.insertion_point.x as f32; - let oy = wo.insertion_point.y as f32; + let ox = (wo.insertion_point.x - wox) as f32; + let oy = (wo.insertion_point.y - woy) as f32; let oz = wo.insertion_point.z as f32; let ux = (wo.u_vector.x * wo.size.x) as f32; let uy = (wo.u_vector.y * wo.size.x) as f32; @@ -1591,7 +1592,8 @@ impl Scene { } } - fn hatch_model_from_dxf(dxf: &DxfHatch, color: [f32; 4]) -> Option { + fn hatch_model_from_dxf(dxf: &DxfHatch, color: [f32; 4], world_offset: [f64; 3]) -> Option { + let [ox, oy, _oz] = world_offset; let path = dxf .paths .iter() @@ -1615,10 +1617,10 @@ impl Scene { let v1 = &verts[(i + 1) % count]; let bulge = v0.z; if bulge.abs() < 1e-9 { - boundary.push([v0.x as f32, v0.y as f32]); + boundary.push([(v0.x - ox) as f32, (v0.y - oy) as f32]); } else { - let p0 = [v0.x as f32, v0.y as f32]; - let p1 = [v1.x as f32, v1.y as f32]; + let p0 = [(v0.x - ox) as f32, (v0.y - oy) as f32]; + let p1 = [(v1.x - ox) as f32, (v1.y - oy) as f32]; let angle = 4.0 * (bulge as f32).atan(); let dx = p1[0] - p0[0]; let dy = p1[1] - p0[1]; @@ -1656,12 +1658,12 @@ impl Scene { } } BoundaryEdge::Line(line) => { - boundary.push([line.start.x as f32, line.start.y as f32]); - boundary.push([line.end.x as f32, line.end.y as f32]); + boundary.push([(line.start.x - ox) as f32, (line.start.y - oy) as f32]); + boundary.push([(line.end.x - ox) as f32, (line.end.y - oy) as f32]); } BoundaryEdge::CircularArc(arc) => { - let cx = arc.center.x as f32; - let cy = arc.center.y as f32; + let cx = (arc.center.x - ox) as f32; + let cy = (arc.center.y - oy) as f32; let r = arc.radius as f32; let (sa, ea) = if arc.counter_clockwise { (arc.start_angle as f32, arc.end_angle as f32) @@ -1680,8 +1682,8 @@ impl Scene { } } BoundaryEdge::EllipticArc(ell) => { - let cx = ell.center.x as f32; - let cy = ell.center.y as f32; + let cx = (ell.center.x - ox) as f32; + let cy = (ell.center.y - oy) as f32; let maj_x = ell.major_axis_endpoint.x as f32; let maj_y = ell.major_axis_endpoint.y as f32; let r_maj = (maj_x * maj_x + maj_y * maj_y).sqrt(); @@ -1733,11 +1735,11 @@ impl Scene { for i in 0..=segs { let t = t0 + (t1 - t0) * (i as f64 / segs as f64); let p = bspl.subs(t); - boundary.push([p.x as f32, p.y as f32]); + boundary.push([(p.x - ox) as f32, (p.y - oy) as f32]); } } else { for cp in &spline.control_points { - boundary.push([cp.x as f32, cp.y as f32]); + boundary.push([(cp.x - ox) as f32, (cp.y - oy) as f32]); } } } @@ -1834,11 +1836,11 @@ impl Scene { let model = match &kind { EntityType::Hatch(dxf) => { let color = tessellate::aci_to_rgba(&dxf.common.color); - Self::hatch_model_from_dxf(dxf, color) + Self::hatch_model_from_dxf(dxf, color, self.world_offset) } EntityType::Solid(solid) => { let color = tessellate::aci_to_rgba(&solid.common.color); - Some(Self::solid_hatch_model(solid, color)) + Some(Self::solid_hatch_model(solid, color, self.world_offset)) } _ => None, }; @@ -1896,12 +1898,13 @@ impl Scene { /// Build a solid-fill HatchModel for a DXF Solid entity. /// DXF SOLID corners are in "Z-order": p0-p1 top, p2-p3 bottom. /// Visual quad is p0→p1→p3→p2 (closed). - fn solid_hatch_model(solid: &DxfSolid, color: [f32; 4]) -> HatchModel { + fn solid_hatch_model(solid: &DxfSolid, color: [f32; 4], world_offset: [f64; 3]) -> HatchModel { + let [ox, oy, _oz] = world_offset; 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], + [(solid.first_corner.x - ox) as f32, (solid.first_corner.y - oy) as f32], + [(solid.second_corner.x - ox) as f32, (solid.second_corner.y - oy) as f32], + [(solid.fourth_corner.x - ox) as f32, (solid.fourth_corner.y - oy) as f32], + [(solid.third_corner.x - ox) as f32, (solid.third_corner.y - oy) as f32], ]; HatchModel { boundary, @@ -2161,7 +2164,7 @@ impl Scene { if self.hatches.contains_key(&h) { let existing_color = self.hatches[&h].color; let new_model = if let Some(EntityType::Hatch(dxf)) = self.document.get_entity(h) { - Self::hatch_model_from_dxf(dxf, existing_color) + Self::hatch_model_from_dxf(dxf, existing_color, self.world_offset) } else { None }; @@ -2186,7 +2189,7 @@ impl Scene { if !h.is_null() { let new_model = if let Some(EntityType::Hatch(dxf)) = self.document.get_entity(h) { let color = tessellate::aci_to_rgba(&dxf.common.color); - Self::hatch_model_from_dxf(dxf, color) + Self::hatch_model_from_dxf(dxf, color, self.world_offset) } else { None }; @@ -2210,7 +2213,7 @@ impl Scene { match self.document.get_entity(handle) { Some(EntityType::Hatch(dxf)) => { let color = tessellate::aci_to_rgba(&dxf.common.color); - if let Some(model) = Self::hatch_model_from_dxf(dxf, color) { + if let Some(model) = Self::hatch_model_from_dxf(dxf, color, self.world_offset) { self.hatches.insert(handle, model); } else { self.hatches.remove(&handle); @@ -2218,7 +2221,7 @@ impl Scene { } Some(EntityType::Solid(solid)) => { let color = tessellate::aci_to_rgba(&solid.common.color); - self.hatches.insert(handle, Self::solid_hatch_model(solid, color)); + self.hatches.insert(handle, Self::solid_hatch_model(solid, color, self.world_offset)); } _ => {} }