202 lines
8.4 KiB
Rust
202 lines
8.4 KiB
Rust
use lightningbeam_core::region_select::*;
|
|
use vello::kurbo::{BezPath, Point, Rect, Shape};
|
|
|
|
#[test]
|
|
fn debug_clip_rect_corner() {
|
|
// Rectangle from (0,0) to (200,200)
|
|
let mut subject = BezPath::new();
|
|
subject.move_to(Point::new(0.0, 0.0));
|
|
subject.line_to(Point::new(200.0, 0.0));
|
|
subject.line_to(Point::new(200.0, 200.0));
|
|
subject.line_to(Point::new(0.0, 200.0));
|
|
subject.close_path();
|
|
|
|
// Region: upper-right corner, from (100,0) to (300,100)
|
|
// This extends beyond the subject on the right and top
|
|
let region = rect_to_path(Rect::new(100.0, 0.0, 300.0, 100.0));
|
|
|
|
println!("Subject path: {:?}", subject);
|
|
println!("Region path: {:?}", region);
|
|
|
|
let result = clip_path_to_region(&subject, ®ion);
|
|
|
|
println!("Inside path: {:?}", result.inside);
|
|
println!("Outside path: {:?}", result.outside);
|
|
|
|
let inside_bb = result.inside.bounding_box();
|
|
println!("Inside bounding box: {:?}", inside_bb);
|
|
|
|
// Expected inside: a rectangle from (100,0) to (200,100)
|
|
assert!((inside_bb.x0 - 100.0).abs() < 2.0,
|
|
"inside x0 should be ~100, got {}", inside_bb.x0);
|
|
assert!((inside_bb.y0 - 0.0).abs() < 2.0,
|
|
"inside y0 should be ~0, got {}", inside_bb.y0);
|
|
assert!((inside_bb.x1 - 200.0).abs() < 2.0,
|
|
"inside x1 should be ~200, got {}", inside_bb.x1);
|
|
assert!((inside_bb.y1 - 100.0).abs() < 2.0,
|
|
"inside y1 should be ~100, got {}", inside_bb.y1);
|
|
|
|
// Verify the inside path has the right shape by checking it has ~5 elements
|
|
// (MoveTo, 3x LineTo, ClosePath) for a rectangle
|
|
let elem_count = result.inside.elements().len();
|
|
println!("Inside element count: {}", elem_count);
|
|
|
|
// Print each element
|
|
for (i, el) in result.inside.elements().iter().enumerate() {
|
|
println!(" inside[{}]: {:?}", i, el);
|
|
}
|
|
for (i, el) in result.outside.elements().iter().enumerate() {
|
|
println!(" outside[{}]: {:?}", i, el);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn debug_clip_partial_overlap() {
|
|
// When the region is fully contained inside the subject (no edge crossings),
|
|
// the path-clipping approach cannot split the subject since no path segments
|
|
// cross the region boundary. This is correct — the selection system handles
|
|
// this case by classifying the shape as "fully_inside" via hit_test, not
|
|
// via path clipping.
|
|
let mut subject = BezPath::new();
|
|
subject.move_to(Point::new(0.0, 0.0));
|
|
subject.line_to(Point::new(200.0, 0.0));
|
|
subject.line_to(Point::new(200.0, 200.0));
|
|
subject.line_to(Point::new(0.0, 200.0));
|
|
subject.close_path();
|
|
|
|
let region = rect_to_path(Rect::new(50.0, 50.0, 150.0, 150.0));
|
|
let result = clip_path_to_region(&subject, ®ion);
|
|
|
|
// No intersections found → entire subject classified as outside
|
|
assert!(result.inside.elements().is_empty(),
|
|
"No edge crossings → inside should be empty (handled by hit_test instead)");
|
|
assert!(!result.outside.elements().is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn debug_lasso_extending_beyond_subject() {
|
|
// Rectangle subject from (0,0) to (100,100)
|
|
let mut subject = BezPath::new();
|
|
subject.move_to(Point::new(0.0, 0.0));
|
|
subject.line_to(Point::new(100.0, 0.0));
|
|
subject.line_to(Point::new(100.0, 100.0));
|
|
subject.line_to(Point::new(0.0, 100.0));
|
|
subject.close_path();
|
|
|
|
// Lasso region that extends beyond the subject: a triangle
|
|
// covering (20,20) to (150,20) to (80,120)
|
|
// This extends beyond the right and bottom of the rectangle
|
|
let mut lasso = BezPath::new();
|
|
lasso.move_to(Point::new(20.0, 20.0));
|
|
lasso.line_to(Point::new(150.0, 20.0));
|
|
lasso.line_to(Point::new(80.0, 120.0));
|
|
lasso.close_path();
|
|
|
|
let result = clip_path_to_region(&subject, &lasso);
|
|
|
|
// The inside should be ONLY the intersection of the rectangle and lasso.
|
|
// It should NOT extend beyond the rectangle's bounds.
|
|
let inside_bb = result.inside.bounding_box();
|
|
println!("Lasso extending beyond: inside bb = {:?}", inside_bb);
|
|
for (i, el) in result.inside.elements().iter().enumerate() {
|
|
println!(" inside[{}]: {:?}", i, el);
|
|
}
|
|
|
|
// The inside must be contained within the subject rectangle
|
|
assert!(inside_bb.x0 >= -1.0, "inside x0={} should be >= 0", inside_bb.x0);
|
|
assert!(inside_bb.y0 >= -1.0, "inside y0={} should be >= 0", inside_bb.y0);
|
|
assert!(inside_bb.x1 <= 101.0, "inside x1={} should be <= 100", inside_bb.x1);
|
|
assert!(inside_bb.y1 <= 101.0, "inside y1={} should be <= 100", inside_bb.y1);
|
|
}
|
|
|
|
#[test]
|
|
fn debug_lasso_splits_remainder_into_two() {
|
|
// Rectangle subject from (0,0) to (200,200)
|
|
let mut subject = BezPath::new();
|
|
subject.move_to(Point::new(0.0, 0.0));
|
|
subject.line_to(Point::new(200.0, 0.0));
|
|
subject.line_to(Point::new(200.0, 200.0));
|
|
subject.line_to(Point::new(0.0, 200.0));
|
|
subject.close_path();
|
|
|
|
// Lasso that cuts across the rectangle, with vertices clearly NOT on
|
|
// the subject boundary. The lasso is a diamond that extends beyond
|
|
// the rect on top and right, splitting the remainder into two pieces:
|
|
// upper-left and lower-right.
|
|
let mut lasso = BezPath::new();
|
|
lasso.move_to(Point::new(-10.0, 100.0)); // left of rect
|
|
lasso.line_to(Point::new(100.0, -60.0)); // above rect
|
|
lasso.line_to(Point::new(260.0, 100.0)); // right of rect
|
|
lasso.line_to(Point::new(100.0, 210.0)); // below rect
|
|
lasso.close_path();
|
|
|
|
let result = clip_path_to_region(&subject, &lasso);
|
|
|
|
let inside_bb = result.inside.bounding_box();
|
|
println!("Lasso splits remainder: inside bb = {:?}", inside_bb);
|
|
for (i, el) in result.inside.elements().iter().enumerate() {
|
|
println!(" inside[{}]: {:?}", i, el);
|
|
}
|
|
|
|
// The inside must be contained within the subject rectangle bounds
|
|
assert!(inside_bb.x0 >= -1.0,
|
|
"inside x0={} should be >= 0 (must not extend left of subject)", inside_bb.x0);
|
|
assert!(inside_bb.y0 >= -1.0,
|
|
"inside y0={} should be >= 0 (must not extend above subject)", inside_bb.y0);
|
|
assert!(inside_bb.x1 <= 201.0,
|
|
"inside x1={} should be <= 200 (must not extend right of subject)", inside_bb.x1);
|
|
assert!(inside_bb.y1 <= 201.0,
|
|
"inside y1={} should be <= 200 (must not extend below subject)", inside_bb.y1);
|
|
|
|
// The outside (remainder) must also be within subject bounds
|
|
let outside_bb = result.outside.bounding_box();
|
|
println!("Lasso splits remainder: outside bb = {:?}", outside_bb);
|
|
for (i, el) in result.outside.elements().iter().enumerate() {
|
|
println!(" outside[{}]: {:?}", i, el);
|
|
}
|
|
assert!(outside_bb.x1 <= 201.0,
|
|
"outside x1={} must not extend right of subject (no lasso fill!)", outside_bb.x1);
|
|
assert!(outside_bb.y0 >= -1.0,
|
|
"outside y0={} must not extend above subject (no lasso fill!)", outside_bb.y0);
|
|
|
|
// The outside should have multiple separate sub-paths (multiple ClosePath elements)
|
|
// instead of one giant path that includes the lasso area
|
|
let close_count = result.outside.elements().iter()
|
|
.filter(|el| matches!(el, vello::kurbo::PathEl::ClosePath))
|
|
.count();
|
|
println!("Outside close_path count: {}", close_count);
|
|
assert!(close_count >= 2,
|
|
"Outside should be split into separate sub-paths, got {} ClosePaths", close_count);
|
|
}
|
|
|
|
#[test]
|
|
fn debug_clip_outside_shape_correct() {
|
|
// Test the exact scenario the user reported: outside shape should
|
|
// correctly include boundary walk points (no diagonal shortcuts)
|
|
let mut subject = BezPath::new();
|
|
subject.move_to(Point::new(0.0, 0.0));
|
|
subject.line_to(Point::new(200.0, 0.0));
|
|
subject.line_to(Point::new(200.0, 200.0));
|
|
subject.line_to(Point::new(0.0, 200.0));
|
|
subject.close_path();
|
|
|
|
let region = rect_to_path(Rect::new(100.0, 0.0, 300.0, 100.0));
|
|
let result = clip_path_to_region(&subject, ®ion);
|
|
|
|
// Outside should be an L-shape: (0,0)-(100,0)-(100,100)-(200,100)-(200,200)-(0,200)
|
|
let outside_bb = result.outside.bounding_box();
|
|
assert!((outside_bb.x0 - 0.0).abs() < 2.0);
|
|
assert!((outside_bb.y0 - 0.0).abs() < 2.0);
|
|
assert!((outside_bb.x1 - 200.0).abs() < 2.0);
|
|
assert!((outside_bb.y1 - 200.0).abs() < 2.0);
|
|
|
|
// Verify the path includes (200,100) — the critical boundary walk point
|
|
let has_200_100 = result.outside.elements().iter().any(|el| {
|
|
match *el {
|
|
vello::kurbo::PathEl::LineTo(p) => (p.x - 200.0).abs() < 1.0 && (p.y - 100.0).abs() < 1.0,
|
|
_ => false,
|
|
}
|
|
});
|
|
assert!(has_200_100, "Outside path must include point (200,100) from boundary walk");
|
|
}
|