fix(select): click the front-most solid across top-level and block bodies

Clicking a block whose body is a solid tried top-level solid meshes first
and block-internal ones only as a fallback, so a stray solid behind the
block always won and the block couldn't be picked. Merge both into one
depth-sorted hit-test (solid_click_hit) keyed by the solid's own handle or
the parent INSERT, so the front-most body wins regardless of kind.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-06-21 07:24:31 +03:00
1 parent d1395e8f3d
commit cd10aa0363
2 files changed
+48 -3

No files matched your search

+5 -3
View File
@@ -3880,8 +3880,10 @@ impl OpenCADStudio {
})
.or_else(|| {
// 3D solids: click anywhere on the shaded
// body, not just the thin projected edges.
self.tabs[i].scene.mesh_click_hit(p, vp_mat, bounds)
// body — top-level solids and block-internal
// ones together, front-most wins (a block in
// front of a solid resolves to the block).
self.tabs[i].scene.solid_click_hit(p, vp_mat, bounds)
});
// Selection filter: drop a pick whose type is excluded.
let hit =
@@ -4376,7 +4378,7 @@ impl OpenCADStudio {
bounds,
)
})
.or_else(|| self.tabs[i].scene.mesh_click_hit(p, view_proj, bounds));
.or_else(|| self.tabs[i].scene.solid_click_hit(p, view_proj, bounds));
self.tabs[i].scene.set_hover_highlight(hovered);
self.hover_dwell = None;
Task::none()
+43
View File
@@ -2310,6 +2310,49 @@ impl Scene {
pick::hit_test::mesh_poly_hit(poly, crossing, iter, view_proj, bounds)
}
/// Front-most solid under the cursor across BOTH top-level solid meshes
/// (keyed by their own handle) and block-internal solid instances (keyed
/// by the parent INSERT). Combining them in one depth-sorted test means a
/// block in front of a stray solid wins, instead of the solid always
/// taking priority by virtue of being tried first.
pub fn solid_click_hit(
&self,
cursor: iced::Point,
view_proj: glam::Mat4,
bounds: iced::Rectangle,
) -> Option<Handle> {
// Block-internal instances must be owned (transformed copies); keep
// them in a Vec and chain references alongside the top-level meshes so
// neither set is cloned wholesale.
let mut block_owned: Vec<(Handle, crate::scene::model::mesh_model::MeshModel)> = Vec::new();
if !self.block_meshes.is_empty() {
let layout_block = self.current_layout_block_handle();
let woff = self.world_offset;
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);
for set in sets {
if let Some(m) = set.lods.into_iter().next() {
block_owned.push((ins.common.handle, m));
}
}
}
}
let top = self
.meshes
.iter()
.filter_map(|(h, set)| set.lods.first().map(|m| (*h, m)));
let blk = block_owned.iter().map(|(h, m)| (*h, m));
pick::hit_test::mesh_click_hit(cursor, top.chain(blk), 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