forked from 1b-agent/opencadstudio-upstream
feat(model): SECTIONCULL erases what lies outside a section
SECTIONCULL keeps only what the selected section object (or the live section of the space) shows: solids, surfaces and closed meshes are cut to it with the same planes the live section uses, open meshes are clipped face by face, and every other object wholly outside it is erased. Objects across the boundary that cannot be cut stay whole. One undo step. The section's bounding planes are gathered once (section_planes) and shared by the live section and the cull. Refs #1407 Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
adbcac8a02
commit
d4b4fc37e1
28 files changed
+450
-77
No files matched your search
@@ -2148,6 +2148,11 @@ impl OpenCADStudio {
|
||||
self.tabs[i].active_cmd = Some(Box::new(subtract));
|
||||
}
|
||||
|
||||
// SECTIONCULL — erase what lies outside a section object.
|
||||
"SECTIONCULL" => {
|
||||
return Some(self.section_cull());
|
||||
}
|
||||
|
||||
// INTERFERE — non-destructive intersect: solid from the overlap.
|
||||
"INTERFERE" => {
|
||||
return Some(self.solid_interfere());
|
||||
|
||||
@@ -621,6 +621,7 @@ inventory::submit!(crate::command::CommandRegistration {
|
||||
"SL",
|
||||
"INTERFERE",
|
||||
"INF",
|
||||
"SECTIONCULL",
|
||||
"PRESSPULL",
|
||||
"THICKEN",
|
||||
"3DROTATE",
|
||||
|
||||
@@ -225,6 +225,8 @@ fn subtract_bodies(
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
inventory::submit!(crate::command::CommandRegistration { names: &["SECTIONCULL"] });
|
||||
|
||||
fn entity_with_boolean_body(mut source: EntityType, body: &Body) -> Option<EntityType> {
|
||||
let document = crate::scene::convert::acis_export::solid_to_sat(body)?;
|
||||
let wires = solid_model::edge_wires(body);
|
||||
@@ -2073,6 +2075,74 @@ impl super::OpenCADStudio {
|
||||
self.solid_slice(&handles, plane, keep_point)
|
||||
}
|
||||
|
||||
/// SECTIONCULL — keep only what lies inside the selected section object
|
||||
/// (or the live section): bodies are cut to it, open meshes clipped, and
|
||||
/// everything wholly outside it erased. One undo step.
|
||||
pub(super) fn section_cull(&mut self) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
let Some(section) = self.tabs[i].scene.cull_section() else {
|
||||
self.command_line.push_error(
|
||||
crate::t!("SECTIONCULL: select a section object or turn on a live section.")
|
||||
.as_ref(),
|
||||
);
|
||||
return Task::none();
|
||||
};
|
||||
let Some(cull) = self.tabs[i].scene.section_cull(section) else {
|
||||
return Task::none();
|
||||
};
|
||||
let mut cut = Vec::with_capacity(cull.cut.len());
|
||||
for (handle, body) in cull.cut {
|
||||
let scene = &self.tabs[i].scene;
|
||||
let entity = scene
|
||||
.document
|
||||
.get_entity(handle)
|
||||
.cloned()
|
||||
.and_then(|source| entity_with_boolean_body(source, &body));
|
||||
let display = scene
|
||||
.prepare_solid_model_display(handle, &body)
|
||||
.filter(|display| display.0.complete);
|
||||
let (Some(entity), Some(display)) = (entity, display) else {
|
||||
self.command_line.push_error(
|
||||
crate::t!(
|
||||
"SECTIONCULL: a cut object could not be prepared; nothing was changed."
|
||||
)
|
||||
.as_ref(),
|
||||
);
|
||||
return Task::none();
|
||||
};
|
||||
cut.push((handle, entity, body, display));
|
||||
}
|
||||
let changed = cut.len() + cull.clipped.len() + cull.outside.len();
|
||||
if changed == 0 {
|
||||
self.command_line
|
||||
.push_output(crate::t!("SECTIONCULL: nothing lies outside the section.").as_ref());
|
||||
return Task::none();
|
||||
}
|
||||
self.push_undo_snapshot(i, "SECTIONCULL");
|
||||
let scene = &mut self.tabs[i].scene;
|
||||
for (handle, entity, body, display) in cut {
|
||||
scene.delete_solid_history(handle);
|
||||
scene.update_entity(entity);
|
||||
scene.register_prepared_solid_model(handle, body, display);
|
||||
}
|
||||
for mesh in cull.clipped {
|
||||
scene.update_entity(mesh);
|
||||
}
|
||||
scene.erase_entities(&cull.outside);
|
||||
scene.deselect_all();
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
self.command_line.push_output(
|
||||
crate::t!(
|
||||
"SECTIONCULL: erased %{erased} object(s) and cut %{cut} across the section.",
|
||||
erased = cull.outside.len(),
|
||||
cut = changed - cull.outside.len()
|
||||
)
|
||||
.as_ref(),
|
||||
);
|
||||
Task::none()
|
||||
}
|
||||
|
||||
/// INTERFERE — create a solid from the overlap of the two selected solids,
|
||||
/// leaving the originals in place (a non-destructive boolean intersect).
|
||||
pub(super) fn solid_interfere(&mut self) -> Task<Message> {
|
||||
|
||||
Reference in new issue
Block a user