From 7065bfa9357640e446bc43389b32677ea6a18772 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Wed, 29 Apr 2026 21:38:48 +0300 Subject: [PATCH] fix: do not apply world_offset to paper-space entities Paper-space entities (wires, hatches, wipeouts) live in sheet coordinates (~mm scale) and do not need the model-space centring shift. world_offset was incorrectly subtracted from their coordinates, displacing all paper-space geometry away from the paper sheet. Co-Authored-By: Claude Sonnet 4.6 --- src/scene/mod.rs | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/scene/mod.rs b/src/scene/mod.rs index bf0a2d646..0e77551e0 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -618,7 +618,9 @@ impl Scene { let doc = &self.document; let sel = &self.selected; let avp = self.active_viewport; - let woff = self.world_offset; + // Paper-space entities live in sheet coordinates (mm), not model-world + // coordinates, so world_offset must not be subtracted from them. + let woff = if self.current_layout == "Model" { self.world_offset } else { [0.0; 3] }; let mut wires: Vec = visible .into_par_iter() .flat_map(|e| tessellate_entity(doc, sel, avp, woff, e)) @@ -1233,12 +1235,13 @@ impl Scene { // ── Entity management ───────────────────────────────────────────────── pub fn add_entity(&mut self, mut entity: EntityType) -> Handle { + let hatch_offset = if self.current_layout == "Model" { self.world_offset } else { [0.0; 3] }; let hatch_seed = if let EntityType::Hatch(dxf) = &entity { let color = self.render_style(&entity).0; - Self::hatch_model_from_dxf(dxf, color, self.world_offset) + Self::hatch_model_from_dxf(dxf, color, hatch_offset) } else if let EntityType::Solid(solid) = &entity { let color = self.render_style(&entity).0; - Some(Self::solid_hatch_model(solid, color, self.world_offset)) + Some(Self::solid_hatch_model(solid, color, hatch_offset)) } else { None }; @@ -1401,6 +1404,7 @@ impl Scene { fn synced_hatch_models(&self) -> Vec { let layout_block = self.current_layout_block_handle(); + let hatch_offset = if self.current_layout == "Model" { self.world_offset } else { [0.0; 3] }; let layer_hidden = |layer: &str| { self.document @@ -1473,7 +1477,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, self.world_offset) { + if let Some(mut model) = Self::hatch_model_from_dxf(&dxf, color, hatch_offset) { if selected { model.color = [0.15, 0.55, 1.00, model.color[3]]; } @@ -1519,11 +1523,14 @@ impl Scene { /// Wipeout fill models — rendered in a separate pass AFTER wires so that /// wipeouts correctly mask everything below them in the draw order. pub(super) fn wipeout_models(&self) -> Vec { - let bg_color: [f32; 4] = if self.current_layout == "Model" { - self.bg_color - } else { + let is_paper = self.current_layout != "Model"; + let bg_color: [f32; 4] = if is_paper { self.paper_bg_color + } else { + self.bg_color }; + // Paper-space entities are already in small coordinates — don't shift them. + let world_offset = if is_paper { [0.0; 3] } else { self.world_offset }; let mut models = Vec::new(); for entity in self.document.entities() { let EntityType::Wipeout(wo) = entity else { continue }; @@ -1537,7 +1544,7 @@ impl Scene { { continue; } - let boundary = Self::wipeout_boundary_2d(wo, self.world_offset); + let boundary = Self::wipeout_boundary_2d(wo, world_offset); if boundary.len() >= 3 { let mut fill_color = bg_color; if self.selected.contains(&wo.common.handle) { @@ -1822,6 +1829,8 @@ impl Scene { pub fn populate_hatches_from_document(&mut self) { self.hatches.clear(); + let model_block = self.model_space_block_handle(); + let entries: Vec<(Handle, EntityType)> = self .document .entities() @@ -1833,14 +1842,18 @@ impl Scene { .collect(); for (handle, kind) in entries { + // Paper-space entities live in sheet coordinates — world_offset must not + // be applied to them. Only model-space entities need the shift. + let owner = kind.common().owner_handle; + let offset = if owner == model_block { self.world_offset } else { [0.0; 3] }; let model = match &kind { EntityType::Hatch(dxf) => { let color = tessellate::aci_to_rgba(&dxf.common.color); - Self::hatch_model_from_dxf(dxf, color, self.world_offset) + Self::hatch_model_from_dxf(dxf, color, offset) } EntityType::Solid(solid) => { let color = tessellate::aci_to_rgba(&solid.common.color); - Some(Self::solid_hatch_model(solid, color, self.world_offset)) + Some(Self::solid_hatch_model(solid, color, offset)) } _ => None, }; @@ -2157,6 +2170,7 @@ impl Scene { // ── Modify (transform / copy) ───────────────────────────────────────── pub fn transform_entities(&mut self, handles: &[Handle], t: &EntityTransform) { + let hatch_offset = if self.current_layout == "Model" { self.world_offset } else { [0.0; 3] }; for &h in handles { if let Some(entity) = self.document.get_entity_mut(h) { dispatch::apply_transform(entity, t); @@ -2164,7 +2178,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.world_offset) + Self::hatch_model_from_dxf(dxf, existing_color, hatch_offset) } else { None }; @@ -2177,6 +2191,7 @@ impl Scene { } pub fn copy_entities(&mut self, handles: &[Handle], t: &EntityTransform) -> Vec { + let hatch_offset = if self.current_layout == "Model" { self.world_offset } else { [0.0; 3] }; let clones: Vec = handles .iter() .filter_map(|&h| self.document.get_entity(h).cloned()) @@ -2189,7 +2204,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.world_offset) + Self::hatch_model_from_dxf(dxf, color, hatch_offset) } else { None }; @@ -2210,10 +2225,11 @@ impl Scene { dispatch::apply_grip(entity, grip_id, apply); } // Rebuild GPU hatch/solid model when a boundary vertex or corner moves. + let hatch_offset = if self.current_layout == "Model" { self.world_offset } else { [0.0; 3] }; 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, self.world_offset) { + if let Some(model) = Self::hatch_model_from_dxf(dxf, color, hatch_offset) { self.hatches.insert(handle, model); } else { self.hatches.remove(&handle); @@ -2221,7 +2237,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.world_offset)); + self.hatches.insert(handle, Self::solid_hatch_model(solid, color, hatch_offset)); } _ => {} }