use super::*;
#[test]
fn test_compute_signed_area_ccw() {
let contour = vec![
Point2::new(0.0, 0.0),
Point2::new(1.0, 0.0),
Point2::new(1.0, 1.0),
Point2::new(0.0, 1.0),
];
let area = compute_signed_area(&contour);
assert!((area - 1.0).abs() < EPSILON_2D);
}
#[test]
fn test_compute_signed_area_cw() {
let contour = vec![
Point2::new(0.0, 0.0),
Point2::new(0.0, 1.0),
Point2::new(1.0, 1.0),
Point2::new(1.0, 0.0),
];
let area = compute_signed_area(&contour);
assert!((area + 1.0).abs() < EPSILON_2D);
}
#[test]
fn test_ensure_ccw() {
let cw = vec![
Point2::new(0.0, 0.0),
Point2::new(0.0, 1.0),
Point2::new(1.0, 1.0),
Point2::new(1.0, 0.0),
];
let ccw = ensure_ccw(&cw);
assert!(compute_signed_area(&ccw) > 0.0);
}
#[test]
fn test_subtract_2d_simple() {
let profile = Profile2D::new(vec![
Point2::new(0.0, 0.0),
Point2::new(10.0, 0.0),
Point2::new(10.0, 10.0),
Point2::new(0.0, 10.0),
]);
let void_contour = vec![
Point2::new(4.0, 4.0),
Point2::new(6.0, 4.0),
Point2::new(6.0, 6.0),
Point2::new(4.0, 6.0),
];
let result = subtract_2d(&profile, &void_contour).unwrap();
assert_eq!(result.holes.len(), 1);
assert_eq!(result.outer.len(), 4);
}
#[test]
fn test_subtract_multiple_2d() {
let profile = Profile2D::new(vec![
Point2::new(0.0, 0.0),
Point2::new(10.0, 0.0),
Point2::new(10.0, 10.0),
Point2::new(0.0, 10.0),
]);
let voids = vec![
vec![
Point2::new(2.0, 2.0),
Point2::new(3.0, 2.0),
Point2::new(3.0, 3.0),
Point2::new(2.0, 3.0),
],
vec![
Point2::new(7.0, 7.0),
Point2::new(8.0, 7.0),
Point2::new(8.0, 8.0),
Point2::new(7.0, 8.0),
],
];
let result = subtract_multiple_2d(&profile, &voids).unwrap();
assert_eq!(result.holes.len(), 2);
}
#[test]
fn test_subtract_counted_interior_single_shape() {
let profile = Profile2D::new(vec![
Point2::new(0.0, 0.0),
Point2::new(10.0, 0.0),
Point2::new(10.0, 10.0),
Point2::new(0.0, 10.0),
]);
let voids = vec![
vec![
Point2::new(2.0, 2.0),
Point2::new(3.0, 2.0),
Point2::new(3.0, 3.0),
Point2::new(2.0, 3.0),
],
vec![
Point2::new(7.0, 7.0),
Point2::new(8.0, 7.0),
Point2::new(8.0, 8.0),
Point2::new(7.0, 8.0),
],
];
let (res, shapes) = subtract_multiple_2d_counted(&profile, &voids).unwrap();
assert_eq!(shapes, 1, "interior voids keep one connected shape");
assert_eq!(res.holes.len(), 2);
}
#[test]
fn test_subtract_counted_splitting_void_multi_shape() {
let profile = Profile2D::new(vec![
Point2::new(0.0, 0.0),
Point2::new(10.0, 0.0),
Point2::new(10.0, 10.0),
Point2::new(0.0, 10.0),
]);
let slot = vec![
Point2::new(-1.0, 4.5),
Point2::new(11.0, 4.5),
Point2::new(11.0, 5.5),
Point2::new(-1.0, 5.5),
];
let (_res, shapes) = subtract_multiple_2d_counted(&profile, &[slot]).unwrap();
assert_eq!(
shapes, 2,
"a full-width slot splits the profile into two shapes"
);
}
#[test]
fn test_subtract_counted_subthreshold_sliver_still_multi_shape() {
let scale = 0.02_f64;
let profile = Profile2D::new(vec![
Point2::new(0.0, 0.0),
Point2::new(scale, 0.0),
Point2::new(scale, scale),
Point2::new(0.0, scale),
]);
let top_h = scale * 1e-8;
let b_lo = scale * 0.45;
let b_hi = scale - top_h;
let band = vec![
Point2::new(-scale, b_lo),
Point2::new(scale * 2.0, b_lo),
Point2::new(scale * 2.0, b_hi),
Point2::new(-scale, b_hi),
];
let (_res, shapes) =
subtract_multiple_2d_counted(&profile, std::slice::from_ref(&band)).unwrap();
assert_eq!(
shapes, 2,
"a sub-threshold sliver must still be counted, forcing the exact-kernel defer"
);
let subject = profile_to_paths(&profile);
let clip = ccw_paths([band.as_slice()]);
let result = subject.overlay(&clip, OverlayRule::Difference, FillRule::NonZero);
let mut areas: Vec<f64> = result
.iter()
.filter_map(|s| {
s.first().map(|o| {
let ring: Vec<Point2<f64>> = o.iter().map(|p| Point2::new(p[0], p[1])).collect();
compute_signed_area(&ring).abs()
})
})
.collect();
areas.sort_by(|a, b| a.partial_cmp(b).unwrap());
assert_eq!(areas.len(), 2, "the split yields exactly two output shapes");
assert!(
areas[0] <= MIN_AREA_THRESHOLD,
"smaller shape area {} must be <= MIN_AREA_THRESHOLD {} (the blind spot the fix closes)",
areas[0],
MIN_AREA_THRESHOLD
);
let above_threshold = areas
.iter()
.filter(|a| **a > MIN_AREA_THRESHOLD)
.count();
assert_eq!(
above_threshold, 1,
"old area-filtered count would have seen only 1 shape and proceeded"
);
}
#[test]
fn test_point_in_contour() {
let contour = vec![
Point2::new(0.0, 0.0),
Point2::new(10.0, 0.0),
Point2::new(10.0, 10.0),
Point2::new(0.0, 10.0),
];
assert!(point_in_contour(&Point2::new(5.0, 5.0), &contour));
assert!(!point_in_contour(&Point2::new(15.0, 5.0), &contour));
assert!(!point_in_contour(&Point2::new(-1.0, 5.0), &contour));
}
#[test]
fn test_is_valid_contour() {
let valid = vec![
Point2::new(0.0, 0.0),
Point2::new(1.0, 0.0),
Point2::new(1.0, 1.0),
Point2::new(0.0, 1.0),
];
assert!(is_valid_contour(&valid));
let degenerate = vec![
Point2::new(0.0, 0.0),
Point2::new(1.0, 0.0),
Point2::new(2.0, 0.0),
];
assert!(!is_valid_contour(°enerate));
let too_few = vec![Point2::new(0.0, 0.0), Point2::new(1.0, 0.0)];
assert!(!is_valid_contour(&too_few));
}
fn net_area(p: &Profile2D) -> f64 {
compute_signed_area(&p.outer).abs()
- p.holes
.iter()
.map(|h| compute_signed_area(h).abs())
.sum::<f64>()
}
fn overlapping_void_pair() -> Vec<Vec<Point2<f64>>> {
vec![
vec![
Point2::new(2.0, 2.0),
Point2::new(6.0, 2.0),
Point2::new(6.0, 6.0),
Point2::new(2.0, 6.0),
],
vec![
Point2::new(4.0, 4.0),
Point2::new(8.0, 4.0),
Point2::new(8.0, 8.0),
Point2::new(4.0, 8.0),
],
]
}
fn plate_10x10() -> Profile2D {
Profile2D::new(vec![
Point2::new(0.0, 0.0),
Point2::new(10.0, 0.0),
Point2::new(10.0, 10.0),
Point2::new(0.0, 10.0),
])
}
fn assert_single_merged_hole(result: &Profile2D, case: &str) {
assert_eq!(result.holes.len(), 1, "{case}: the two footprints merge into ONE hole");
let hole_area = compute_signed_area(&result.holes[0]).abs();
assert!(
(hole_area - 28.0).abs() < 1e-6,
"{case}: merged hole must be the UNION (28.0), got {hole_area} (24.0 = symmetric difference)"
);
let net = net_area(result);
assert!(
(net - 72.0).abs() < 1e-6,
"{case}: net material must be 100 - 28 = 72.0, got {net} (76.0 = the phantom pillar)"
);
}
#[test]
fn overlapping_voids_merge_instead_of_cancelling() {
let profile = plate_10x10();
let voids = overlapping_void_pair();
let result = subtract_multiple_2d(&profile, &voids).unwrap();
assert_single_merged_hole(&result, "two CCW footprints");
let (counted, shapes) = subtract_multiple_2d_counted(&profile, &voids).unwrap();
assert_eq!(
shapes, 1,
"overlapping interior voids keep one connected shape"
);
assert!((net_area(&counted) - 72.0).abs() < 1e-6);
}
#[test]
fn overlapping_voids_merge_when_one_footprint_is_mirrored() {
let profile = plate_10x10();
let mut voids = overlapping_void_pair();
voids[1].reverse(); assert!(
compute_signed_area(&voids[1]) < 0.0,
"fixture precondition: the second footprint really is CW"
);
let result = subtract_multiple_2d(&profile, &voids).unwrap();
assert_single_merged_hole(&result, "one footprint mirrored CW");
}
#[test]
fn mixed_route_compat_preserves_established_overlap_parity() {
let profile = plate_10x10();
let voids = overlapping_void_pair();
let (result, shapes) =
subtract_multiple_2d_counted_mixed_compat(&profile, &voids).unwrap();
assert_eq!(shapes, 1);
assert!(
(net_area(&result) - 76.0).abs() < 1e-6,
"mixed routing keeps its established input mesh until #4617"
);
}
#[test]
fn subtract_2d_single_void_overlapping_an_existing_hole_merges() {
let mut profile = plate_10x10();
profile.holes.push(ensure_cw(&overlapping_void_pair()[0]));
assert!((net_area(&profile) - 84.0).abs() < 1e-6);
let result = subtract_2d(&profile, &overlapping_void_pair()[1]).unwrap();
assert_single_merged_hole(&result, "void over an existing hole");
}