forked from 1b-agent/opencadstudio-upstream
fix(paper): keep model 3D solids off the paper-space sheet
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 <noreply@anthropic.com>
This commit is contained in:
1 parent
313a0eacfd
commit
e78e137664
3 files changed
+69
-1
No files matched your search
@@ -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<u64> = Vec::with_capacity(selected.len());
|
||||||
|
let mut type_counts: BTreeMap<&'static str, usize> = BTreeMap::new();
|
||||||
|
let mut block_counts: BTreeMap<String, usize> = 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<String> =
|
||||||
|
type_counts.iter().map(|(t, n)| format!("{t}×{n}")).collect();
|
||||||
|
let list: Vec<String> = 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<String> =
|
||||||
|
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
|
// DBLIST — list data for every entity in the drawing (LIST over the
|
||||||
// whole database rather than the current selection).
|
// whole database rather than the current selection).
|
||||||
"DBLIST" => {
|
"DBLIST" => {
|
||||||
|
|||||||
@@ -92,8 +92,15 @@ pub fn dxf_name(e: &EntityType) -> &'static str {
|
|||||||
EntityType::AttributeDefinition(_) => "ATTDEF",
|
EntityType::AttributeDefinition(_) => "ATTDEF",
|
||||||
EntityType::AttributeEntity(_) => "ATTRIB",
|
EntityType::AttributeEntity(_) => "ATTRIB",
|
||||||
EntityType::Leader(_) => "LEADER",
|
EntityType::Leader(_) => "LEADER",
|
||||||
|
EntityType::MultiLeader(_) => "MULTILEADER",
|
||||||
EntityType::Tolerance(_) => "TOLERANCE",
|
EntityType::Tolerance(_) => "TOLERANCE",
|
||||||
EntityType::Shape(_) => "SHAPE",
|
EntityType::Shape(_) => "SHAPE",
|
||||||
|
EntityType::Solid(_) => "SOLID",
|
||||||
|
EntityType::Solid3D(_) => "3DSOLID",
|
||||||
|
EntityType::Region(_) => "REGION",
|
||||||
|
EntityType::Body(_) => "BODY",
|
||||||
|
EntityType::Surface(_) => "SURFACE",
|
||||||
|
EntityType::Mesh(_) => "MESH",
|
||||||
_ => "ENTITY",
|
_ => "ENTITY",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -957,6 +957,18 @@ impl Scene {
|
|||||||
} else {
|
} else {
|
||||||
self.images_arc()
|
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 {
|
Some(ViewportData {
|
||||||
wires: all_wires,
|
wires: all_wires,
|
||||||
@@ -966,7 +978,7 @@ impl Scene {
|
|||||||
hatches,
|
hatches,
|
||||||
wipeout_hatches,
|
wipeout_hatches,
|
||||||
images,
|
images,
|
||||||
meshes: self.meshes_arc(),
|
meshes,
|
||||||
uniforms,
|
uniforms,
|
||||||
cam_rotation: inst.camera.view_rotation_mat() * self.viewcube_ucs_mat(),
|
cam_rotation: inst.camera.view_rotation_mat() * self.viewcube_ucs_mat(),
|
||||||
compass_rotation: inst.camera.view_rotation_mat(),
|
compass_rotation: inst.camera.view_rotation_mat(),
|
||||||
|
|||||||
Reference in new issue
Block a user