forked from 1b-agent/opencadstudio-upstream
fix(scene): keep low-LOD objects visible and selectable
At deep zoom-out, entities that projected to under ~5 px were dropped from the scene entirely (and text below 2 px-baseline followed the same path). The objects disappeared visually AND fell out of window / crossing selection, and any prior selection highlight stopped rendering once the zoom transition crossed the LOD threshold. #19. Two changes: 1. `tessellate_entity`: replace the sub-5-px `return vec![]` cull, the sub-2-px text-baseline `return vec![]`, and the empty-greek fall-out with a new `lod_stub_wire` helper. The stub is a 2-point AABB diagonal carrying the same `selected` flag, ACI, and AABB as the entity — so the entity stays visible as a 1-pixel speck, tracks its highlight colour across LOD changes, and remains hit-test'able. 2. `box_hit` / `poly_hit`: when a wire has no `points` but a finite `aabb` (greek text emits only fill_tris), fall back to the AABB rectangle as the hit-test shape. Defense in depth so future fill- only wires don't silently drop out of selection. Closes #19 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
1 parent
136e4581c0
commit
8355e5a278
2 files changed
+112
-12
No files matched your search
+39
-6
@@ -94,15 +94,31 @@ pub fn box_hit<'a>(
|
||||
wires
|
||||
.iter()
|
||||
.filter_map(|wire| {
|
||||
if wire.points.is_empty() {
|
||||
// Fallback: when wire has no line geometry (e.g. greek text emits
|
||||
// only fill_tris) treat the AABB rectangle as the hit-test shape
|
||||
// so low-LOD text stays selectable. See #19.
|
||||
let aabb_pts: Vec<[f32; 3]>;
|
||||
let pts: &[[f32; 3]] = if !wire.points.is_empty() {
|
||||
&wire.points
|
||||
} else if wire.aabb != WireModel::UNBOUNDED_AABB {
|
||||
let [ax, ay, bx, by] = wire.aabb;
|
||||
aabb_pts = vec![
|
||||
[ax, ay, 0.0],
|
||||
[bx, ay, 0.0],
|
||||
[bx, by, 0.0],
|
||||
[ax, by, 0.0],
|
||||
[ax, ay, 0.0],
|
||||
];
|
||||
&aabb_pts
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let mut hit = false;
|
||||
let mut all_inside = true;
|
||||
let mut prev: Option<Point> = None;
|
||||
|
||||
for &[px, py, pz] in &wire.points {
|
||||
for &[px, py, pz] in pts {
|
||||
if px.is_nan() {
|
||||
prev = None;
|
||||
continue;
|
||||
@@ -164,15 +180,32 @@ pub fn poly_hit<'a>(
|
||||
wires
|
||||
.iter()
|
||||
.filter_map(|wire| {
|
||||
if wire.points.is_empty() {
|
||||
// Same AABB fallback as `box_hit`: when a wire has no line
|
||||
// geometry (e.g. greek-LOD text emits only fill_tris) treat the
|
||||
// AABB rectangle as the hit-test shape so low-LOD text stays
|
||||
// selectable. See #19.
|
||||
let aabb_pts: Vec<[f32; 3]>;
|
||||
let pts: &[[f32; 3]] = if !wire.points.is_empty() {
|
||||
&wire.points
|
||||
} else if wire.aabb != WireModel::UNBOUNDED_AABB {
|
||||
let [ax, ay, bx, by] = wire.aabb;
|
||||
aabb_pts = vec![
|
||||
[ax, ay, 0.0],
|
||||
[bx, ay, 0.0],
|
||||
[bx, by, 0.0],
|
||||
[ax, by, 0.0],
|
||||
[ax, ay, 0.0],
|
||||
];
|
||||
&aabb_pts
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let mut hit = false;
|
||||
let mut all_inside = true;
|
||||
let mut prev: Option<Point> = None;
|
||||
|
||||
for &[px, py, pz] in &wire.points {
|
||||
for &[px, py, pz] in pts {
|
||||
if px.is_nan() {
|
||||
prev = None;
|
||||
continue;
|
||||
|
||||
Reference in new issue
Block a user