mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-30 05:46:50 -06:00
fix(select): include solid interiors in crossing-window selection (#401)
* fix(select): include solid interiors in crossing-window selection * test(select): keep solid crossing regressions in an isolated module
This commit is contained in:
@@ -193,8 +193,21 @@ pub(crate) fn in_window(d: &Drawing, e: &Entity, known: Option<Bounds2>, bx: &Bo
|
||||
pl.windows(2).any(|w| w.first().zip(w.get(1)).is_some_and(|(a, b)| seg_hits_box(*a, *b, bx)))
|
||||
}
|
||||
});
|
||||
// A crossing box entirely inside a filled/text object also selects it.
|
||||
hit || (eb.contains_box(bx) && matches!(e.kind, EntityKind::Text(_) | EntityKind::MText(_) | EntityKind::Insert(_)))
|
||||
// A crossing box entirely inside a text/block bounding rectangle also selects it.
|
||||
if hit || (eb.contains_box(bx) && matches!(e.kind, EntityKind::Text(_) | EntityKind::MText(_) | EntityKind::Insert(_))) {
|
||||
return true;
|
||||
}
|
||||
// SOLID/TRACE are filled polygons. A crossing window wholly within the
|
||||
// fill must select them even when it does not touch their outline. Check
|
||||
// the polygon, not just its bounding rectangle (which can cover empty
|
||||
// space around triangular solids).
|
||||
if matches!(e.kind, EntityKind::Solid(_) | EntityKind::Trace(_)) && eb.contains_box(bx) {
|
||||
return e.kind.prims().iter().any(|prim| match prim {
|
||||
Prim::Fill(poly) => bx.corners().iter().all(|p| cadcraft_geom::point_in_polygon(poly, *p)),
|
||||
_ => false,
|
||||
});
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Fence selection: entities crossed by the fence polyline.
|
||||
@@ -255,3 +268,41 @@ mod tests {
|
||||
assert_eq!(fence_hits(EntityKind::Ray(line), [(5.0, 1.0), (5.0, 5.0)]), 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod solid_interior_tests {
|
||||
use super::*;
|
||||
|
||||
fn face(kind: bool, triangular: bool) -> Entity {
|
||||
let a = Vec2::new(0.0, 0.0).to3(0.0);
|
||||
let b = Vec2::new(10.0, 0.0).to3(0.0);
|
||||
let c = Vec2::new(0.0, 10.0).to3(0.0);
|
||||
let d = if triangular { c } else { Vec2::new(10.0, 10.0).to3(0.0) };
|
||||
let solid = cadcraft_doc::Solid { corners: [a, b, c, d] };
|
||||
Entity::new(Handle(256), if kind { EntityKind::Solid(solid) } else { EntityKind::Trace(solid) })
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crossing_window_inside_solid_and_trace_selects_fill() {
|
||||
let inside = Bounds2::new(Vec2::new(4.0, 4.0), Vec2::new(6.0, 6.0));
|
||||
for solid in [true, false] {
|
||||
let mut d = Drawing::default();
|
||||
let e = face(solid, false);
|
||||
assert!(in_window(&d, &e, None, &inside, true, 0.01));
|
||||
assert!(!in_window(&d, &e, None, &inside, false, 0.01));
|
||||
d.model.push(e);
|
||||
assert_eq!(select_window(&d, &Space::Model, inside, true), vec![Handle(256)]);
|
||||
assert!(select_window(&d, &Space::Model, inside, false).is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crossing_window_in_triangular_bbox_but_outside_fill_does_not_select() {
|
||||
let d = Drawing::default();
|
||||
let e = face(true, true);
|
||||
let empty = Bounds2::new(Vec2::new(7.0, 7.0), Vec2::new(8.0, 8.0));
|
||||
assert!(!in_window(&d, &e, None, &empty, true, 0.01));
|
||||
let inside = Bounds2::new(Vec2::new(3.0, 3.0), Vec2::new(4.0, 4.0));
|
||||
assert!(in_window(&d, &e, None, &inside, true, 0.01));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user