fix(select): box/lasso-select blocks whose body is a solid
Box/lasso only tested top-level solid meshes, so a block whose visible body is a solid (its mesh lives in the per-INSERT instanced cache, and it has no wires to catch) was never picked up by a window/crossing/lasso. Add block_mesh_box_hit / block_mesh_poly_hit that test each INSERT's instanced block solid meshes and resolve a hit to the parent INSERT, and wire them into the selection paths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
ae3b305f95
commit
ae9940929c
2 changed files with 100 additions and 0 deletions
|
|
@ -3742,6 +3742,9 @@ impl OpenCADStudio {
|
|||
handles.extend(
|
||||
self.tabs[i].scene.mesh_box_hit(a, p, crossing, vp_mat, bounds),
|
||||
);
|
||||
handles.extend(self.tabs[i].scene.block_mesh_box_hit(
|
||||
a, p, crossing, vp_mat, bounds,
|
||||
));
|
||||
// Box/lasso accumulates like individual picks
|
||||
// (issue #83): a plain box adds to the current
|
||||
// selection, Shift+box removes the boxed
|
||||
|
|
@ -3796,6 +3799,9 @@ impl OpenCADStudio {
|
|||
handles.extend(
|
||||
self.tabs[i].scene.mesh_poly_hit(&poly_pts, crossing, vp_mat, bounds),
|
||||
);
|
||||
handles.extend(self.tabs[i].scene.block_mesh_poly_hit(
|
||||
&poly_pts, crossing, vp_mat, bounds,
|
||||
));
|
||||
// Selection filter: keep only allowed types.
|
||||
handles.retain(|&h| self.tabs[i].scene.passes_selection_filter(h));
|
||||
// Accumulate like the box path (issue #83): plain
|
||||
|
|
@ -3935,6 +3941,9 @@ impl OpenCADStudio {
|
|||
handles.extend(
|
||||
self.tabs[i].scene.mesh_box_hit(a, p, crossing, vp_mat, bounds),
|
||||
);
|
||||
handles.extend(self.tabs[i].scene.block_mesh_box_hit(
|
||||
a, p, crossing, vp_mat, bounds,
|
||||
));
|
||||
// Selection filter: keep only allowed types.
|
||||
handles.retain(|&h| self.tabs[i].scene.passes_selection_filter(h));
|
||||
// Accumulate (issue #83): a plain box adds to the
|
||||
|
|
|
|||
|
|
@ -2310,6 +2310,97 @@ impl Scene {
|
|||
pick::hit_test::mesh_poly_hit(poly, crossing, iter, view_proj, bounds)
|
||||
}
|
||||
|
||||
/// Parent INSERT handles whose block-internal solid meshes fall in a
|
||||
/// rectangular selection box. A block whose visible body is a solid has
|
||||
/// no wires to catch, so box/lasso selection must test its instanced
|
||||
/// meshes too.
|
||||
pub fn block_mesh_box_hit(
|
||||
&self,
|
||||
a: iced::Point,
|
||||
b: iced::Point,
|
||||
crossing: bool,
|
||||
view_proj: glam::Mat4,
|
||||
bounds: iced::Rectangle,
|
||||
) -> Vec<Handle> {
|
||||
if self.block_meshes.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
let layout_block = self.current_layout_block_handle();
|
||||
let woff = self.world_offset;
|
||||
let mut out = Vec::new();
|
||||
for e in self.document.entities() {
|
||||
if e.common().owner_handle != layout_block {
|
||||
continue;
|
||||
}
|
||||
let EntityType::Insert(ins) = e else { continue };
|
||||
if !self.mesh_entity_visible(ins.common.handle) {
|
||||
continue;
|
||||
}
|
||||
let mut sets = Vec::new();
|
||||
self.expand_block_meshes(&ins.block_name, &ins.get_transform(), 0, woff, &mut sets);
|
||||
let hit = sets.iter().any(|set| {
|
||||
set.lods.first().map_or(false, |m| {
|
||||
!pick::hit_test::mesh_box_hit(
|
||||
a,
|
||||
b,
|
||||
crossing,
|
||||
std::iter::once((ins.common.handle, m)),
|
||||
view_proj,
|
||||
bounds,
|
||||
)
|
||||
.is_empty()
|
||||
})
|
||||
});
|
||||
if hit {
|
||||
out.push(ins.common.handle);
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Parent INSERT handles whose block-internal solid meshes fall in a lasso.
|
||||
pub fn block_mesh_poly_hit(
|
||||
&self,
|
||||
poly: &[iced::Point],
|
||||
crossing: bool,
|
||||
view_proj: glam::Mat4,
|
||||
bounds: iced::Rectangle,
|
||||
) -> Vec<Handle> {
|
||||
if self.block_meshes.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
let layout_block = self.current_layout_block_handle();
|
||||
let woff = self.world_offset;
|
||||
let mut out = Vec::new();
|
||||
for e in self.document.entities() {
|
||||
if e.common().owner_handle != layout_block {
|
||||
continue;
|
||||
}
|
||||
let EntityType::Insert(ins) = e else { continue };
|
||||
if !self.mesh_entity_visible(ins.common.handle) {
|
||||
continue;
|
||||
}
|
||||
let mut sets = Vec::new();
|
||||
self.expand_block_meshes(&ins.block_name, &ins.get_transform(), 0, woff, &mut sets);
|
||||
let hit = sets.iter().any(|set| {
|
||||
set.lods.first().map_or(false, |m| {
|
||||
!pick::hit_test::mesh_poly_hit(
|
||||
poly,
|
||||
crossing,
|
||||
std::iter::once((ins.common.handle, m)),
|
||||
view_proj,
|
||||
bounds,
|
||||
)
|
||||
.is_empty()
|
||||
})
|
||||
});
|
||||
if hit {
|
||||
out.push(ins.common.handle);
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Tessellate all non-invisible entities owned by `block_handle`.
|
||||
fn wires_for_block(&self, block_handle: Handle) -> Vec<WireModel> {
|
||||
// Default culling is driven by the live `Scene::camera`. Multi-tile
|
||||
|
|
|
|||
Loading…
Reference in a new issue