From e78e137664cf376baf7f1d62c8eb2aff94c8f6ee Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Fri, 3 Jul 2026 23:29:18 +0300 Subject: [PATCH] fix(paper): keep model 3D solids off the paper-space sheet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The paper layout sheet pass reused the full model mesh set, so every top-level 3D solid piled onto the paper origin — the sheet camera works in paper coordinates, not model space. Guard `meshes` on `inst.paper_sheet` the same way wires/hatches/images already are: the sheet shows only paper-native content; the model's solids render inside the floating content viewports (model camera + per-viewport scissor). Also name 3DSOLID/REGION/BODY/SURFACE/MESH/SOLID/MULTILEADER in dxf_name (they fell through to "ENTITY"), and add a SELHANDLES diagnostic that reports the active space plus a per-type / per-block selection breakdown. Co-Authored-By: Claude Opus 4.8 --- src/app/commands/inquiry.rs | 49 +++++++++++++++++++++++++++++++++++++ src/entities/names.rs | 7 ++++++ src/scene/view/render.rs | 14 ++++++++++- 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/app/commands/inquiry.rs b/src/app/commands/inquiry.rs index d4f7326fe..548045271 100644 --- a/src/app/commands/inquiry.rs +++ b/src/app/commands/inquiry.rs @@ -115,6 +115,55 @@ impl OpenCADStudio { } } + // SELHANDLES — report the current selection for diagnostics: the + // active space (Model / which paper layout), a per-type and + // per-block breakdown, and the raw comma-separated hex handle list + // (so what renders on screen can be compared against the file). + "SELHANDLES" | "SELH" => { + let scene = &self.tabs[i].scene; + let selected = scene.selected_entities(); + if selected.is_empty() { + self.command_line + .push_error("SELHANDLES: no entities selected. Select entities first."); + } else { + use std::collections::BTreeMap; + let space = if scene.current_layout == "Model" { + "Model space".to_string() + } else { + format!("Paper space '{}'", scene.current_layout) + }; + let mut handles: Vec = Vec::with_capacity(selected.len()); + let mut type_counts: BTreeMap<&'static str, usize> = BTreeMap::new(); + let mut block_counts: BTreeMap = BTreeMap::new(); + for (h, e) in &selected { + handles.push(h.value()); + *type_counts + .entry(crate::entities::names::dxf_name(e)) + .or_default() += 1; + if let acadrust::EntityType::Insert(ins) = e { + *block_counts.entry(ins.block_name.clone()).or_default() += 1; + } + } + handles.sort_unstable(); + let types: Vec = + type_counts.iter().map(|(t, n)| format!("{t}×{n}")).collect(); + let list: Vec = handles.iter().map(|h| format!("{:X}", h)).collect(); + let mut msg = format!( + "SELHANDLES: {} selected in {}\n Types: {}", + handles.len(), + space, + types.join(", ") + ); + if !block_counts.is_empty() { + let blocks: Vec = + block_counts.iter().map(|(b, n)| format!("{b}×{n}")).collect(); + msg.push_str(&format!("\n Blocks: {}", blocks.join(", "))); + } + msg.push_str(&format!("\n Handles: {}", list.join(","))); + self.command_line.push_output(&msg); + } + } + // DBLIST — list data for every entity in the drawing (LIST over the // whole database rather than the current selection). "DBLIST" => { diff --git a/src/entities/names.rs b/src/entities/names.rs index 6cfd34b76..da50336f9 100644 --- a/src/entities/names.rs +++ b/src/entities/names.rs @@ -92,8 +92,15 @@ pub fn dxf_name(e: &EntityType) -> &'static str { EntityType::AttributeDefinition(_) => "ATTDEF", EntityType::AttributeEntity(_) => "ATTRIB", EntityType::Leader(_) => "LEADER", + EntityType::MultiLeader(_) => "MULTILEADER", EntityType::Tolerance(_) => "TOLERANCE", EntityType::Shape(_) => "SHAPE", + EntityType::Solid(_) => "SOLID", + EntityType::Solid3D(_) => "3DSOLID", + EntityType::Region(_) => "REGION", + EntityType::Body(_) => "BODY", + EntityType::Surface(_) => "SURFACE", + EntityType::Mesh(_) => "MESH", _ => "ENTITY", } } diff --git a/src/scene/view/render.rs b/src/scene/view/render.rs index 6f3381770..59676a50f 100644 --- a/src/scene/view/render.rs +++ b/src/scene/view/render.rs @@ -957,6 +957,18 @@ impl Scene { } else { self.images_arc() }; + // The paper sheet shows the layout's own 2-D content (fills, borders, + // annotation) — never the model's 3-D solids. Those are drawn inside + // the floating content viewports, whose model camera + per-viewport + // scissor place and clip them correctly. Feeding the model mesh set to + // the sheet piles every solid onto the paper origin, because the sheet + // camera works in paper coordinates, not model space — the same reason + // the sheet excludes model hatches and wires above. + let meshes = if inst.paper_sheet { + Arc::new(Vec::new()) + } else { + self.meshes_arc() + }; Some(ViewportData { wires: all_wires, @@ -966,7 +978,7 @@ impl Scene { hatches, wipeout_hatches, images, - meshes: self.meshes_arc(), + meshes, uniforms, cam_rotation: inst.camera.view_rotation_mat() * self.viewcube_ucs_mat(), compass_rotation: inst.camera.view_rotation_mat(),