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(
|
handles.extend(
|
||||||
self.tabs[i].scene.mesh_box_hit(a, p, crossing, vp_mat, bounds),
|
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
|
// Box/lasso accumulates like individual picks
|
||||||
// (issue #83): a plain box adds to the current
|
// (issue #83): a plain box adds to the current
|
||||||
// selection, Shift+box removes the boxed
|
// selection, Shift+box removes the boxed
|
||||||
|
|
@ -3796,6 +3799,9 @@ impl OpenCADStudio {
|
||||||
handles.extend(
|
handles.extend(
|
||||||
self.tabs[i].scene.mesh_poly_hit(&poly_pts, crossing, vp_mat, bounds),
|
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.
|
// Selection filter: keep only allowed types.
|
||||||
handles.retain(|&h| self.tabs[i].scene.passes_selection_filter(h));
|
handles.retain(|&h| self.tabs[i].scene.passes_selection_filter(h));
|
||||||
// Accumulate like the box path (issue #83): plain
|
// Accumulate like the box path (issue #83): plain
|
||||||
|
|
@ -3935,6 +3941,9 @@ impl OpenCADStudio {
|
||||||
handles.extend(
|
handles.extend(
|
||||||
self.tabs[i].scene.mesh_box_hit(a, p, crossing, vp_mat, bounds),
|
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.
|
// Selection filter: keep only allowed types.
|
||||||
handles.retain(|&h| self.tabs[i].scene.passes_selection_filter(h));
|
handles.retain(|&h| self.tabs[i].scene.passes_selection_filter(h));
|
||||||
// Accumulate (issue #83): a plain box adds to the
|
// 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)
|
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`.
|
/// Tessellate all non-invisible entities owned by `block_handle`.
|
||||||
fn wires_for_block(&self, block_handle: Handle) -> Vec<WireModel> {
|
fn wires_for_block(&self, block_handle: Handle) -> Vec<WireModel> {
|
||||||
// Default culling is driven by the live `Scene::camera`. Multi-tile
|
// Default culling is driven by the live `Scene::camera`. Multi-tile
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue