feat(pick): select frame-like entities by clicking inside them

Images, OLE frames, wipeouts, underlays, paper-space viewports and
drawing-view borders were selectable only on their outline. Each now
carries an interior pick surface (pick_tris over its rectangle, or the
triangulated clip polygon for a clipped underlay), ranked below edge
and fill hits so content drawn inside still wins the click. The
layout's own sheet ("overall") viewport is excluded — identified by
the Layout object's viewport link, since DWG files carry viewport
id = 0 and some center the sheet viewport off-origin — or it would
swallow every click over the real viewports beneath it. Drawing-view
borders draw nothing normally; they emit an invisible pick-only wire
whose rect outline appears while selected.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Fable 5 committed 2026-07-21 09:28:07 +03:00
1 parent 84adb71cee
commit e1a4ddef5e
6 files changed
+116 -10

No files matched your search

+58 -3
View File
@@ -624,9 +624,64 @@ pub(crate) fn tessellate_entity(
line_weight_px,
);
}
// Drawing-view borders are non-plotting aids: nothing is drawn.
if let EntityType::ViewBorder(_) = e {
return vec![];
// Drawing-view borders are non-plotting aids: nothing is drawn normally,
// but the view must still be click-selectable anywhere inside its
// rectangle — emit an invisible pick-only wire carrying the border rect as
// its interior pick surface. When selected, the rect outline is drawn so
// the selection has visible feedback.
if let EntityType::ViewBorder(b) = e {
let (x0, y0) = (b.min[0], b.min[1]);
let (x1, y1) = (b.max[0], b.max[1]);
if !(x1 > x0 && y1 > y0) {
return vec![];
}
let (pick_tris, pick_tris_low) =
super::tessellate::points_to_ds(crate::entities::common::quad_pick_tris(&[
[x0, y0, 0.0],
[x1, y0, 0.0],
[x1, y1, 0.0],
[x0, y1, 0.0],
]));
let points: Vec<[f32; 3]> = if sel {
vec![
[x0 as f32, y0 as f32, 0.0],
[x1 as f32, y0 as f32, 0.0],
[x1 as f32, y1 as f32, 0.0],
[x0 as f32, y1 as f32, 0.0],
[x0 as f32, y0 as f32, 0.0],
]
} else {
Vec::new()
};
return vec![WireModel {
taper_widths: Vec::new(),
world_width: 0.0,
fill_is_3d: false,
pick_tris,
pick_tris_low,
dash_from_start: false,
dash_align_end: None,
text_verts: Vec::new(),
name: h.value().to_string(),
points,
points_low: Vec::new(),
color: WireModel::SELECTED,
selected: sel,
aci: 0,
pattern_length: 0.0,
pattern: [0.0; 8],
line_weight_px: 1.0,
snap_pts: vec![],
tangent_geoms: vec![],
key_vertices: vec![
[x0, y0, 0.0],
[x1, y1, 0.0],
],
aabb: [x0 as f32, y0 as f32, x1 as f32, y1 as f32],
plinegen: true,
fill_tris: vec![],
fill_tris_low: Vec::new(),
}];
}
// ── Dimension baked-block fast path ─────────────────────────────────────
+31 -2
View File
@@ -1392,12 +1392,41 @@ pub fn tessellate(
// f64 for the WireModel's double-single-era snap buffer.
let snap_pts: Vec<(glam::DVec3, SnapHint)> =
snap_pts.into_iter().map(|(p, h)| (p.as_dvec3(), h)).collect();
// A paper-space viewport is a window, not a wireframe: give it an interior
// pick surface so a click anywhere inside the frame selects it. Ranked
// below edge and fill hits, so content drawn inside still wins the click.
// The sheet ("overall") viewport is the layout's own invisible camera
// frame covering the whole page — never pickable, or it would swallow
// every click over the real viewports beneath it. It is identified by the
// Layout object's viewport link (authoritative — DWG files carry id = 0
// and this file class centres the sheet viewport off-origin, so neither
// the id nor the geometry heuristic alone is reliable), with
// `is_content_viewport` as the fallback classifier.
let is_sheet_vp = |vp: &acadrust::entities::Viewport| {
let h = vp.common.handle;
document.objects.values().any(|obj| {
matches!(obj, acadrust::objects::ObjectType::Layout(l) if l.viewport == h)
}) || !crate::scene::Scene::is_content_viewport(vp)
};
let (pick_tris, pick_tris_low) = match entity {
EntityType::Viewport(vp) if !is_sheet_vp(vp) => {
let (cx, cy, cz) = (vp.center.x, vp.center.y, vp.center.z);
let (hw, hh) = (vp.width / 2.0, vp.height / 2.0);
points_to_ds(crate::entities::common::quad_pick_tris(&[
[cx - hw, cy - hh, cz],
[cx + hw, cy - hh, cz],
[cx + hw, cy + hh, cz],
[cx - hw, cy + hh, cz],
]))
}
_ => (Vec::new(), Vec::new()),
};
vec![WireModel {
taper_widths: Vec::new(),
world_width: 0.0,
fill_is_3d: false,
pick_tris: Vec::new(),
pick_tris_low: Vec::new(),
pick_tris,
pick_tris_low,
dash_from_start: false,
dash_align_end: None,
text_verts: Vec::new(),