use crate::clipper::*;
use crate::core::{area, Path64, PathD, Paths64, Point, Point64, Rect64, RectD};
use crate::engine::ClipType;
use crate::engine_public::{PolyTree64, PolyTreeD};
use crate::offset::{EndType, JoinType};
use crate::FillRule;
fn square_64(cx: i64, cy: i64, size: i64) -> Path64 {
vec![
Point64::new(cx - size, cy - size),
Point64::new(cx + size, cy - size),
Point64::new(cx + size, cy + size),
Point64::new(cx - size, cy + size),
]
}
fn square_d(cx: f64, cy: f64, size: f64) -> PathD {
vec![
Point::<f64>::new(cx - size, cy - size),
Point::<f64>::new(cx + size, cy - size),
Point::<f64>::new(cx + size, cy + size),
Point::<f64>::new(cx - size, cy + size),
]
}
#[test]
fn test_boolean_op_64_intersection() {
let subjects = vec![square_64(0, 0, 100)];
let clips = vec![square_64(50, 50, 100)];
let result = boolean_op_64(ClipType::Intersection, FillRule::NonZero, &subjects, &clips);
assert!(!result.is_empty(), "Intersection should produce output");
let result_area: f64 = result.iter().map(|p| area(p).abs()).sum();
assert!(result_area > 0.0);
assert!(
(result_area - 22500.0).abs() < 500.0,
"Expected ~22500, got {}",
result_area
);
}
#[test]
fn test_boolean_op_64_union() {
let subjects = vec![square_64(0, 0, 50)];
let clips = vec![square_64(50, 0, 50)];
let result = boolean_op_64(ClipType::Union, FillRule::NonZero, &subjects, &clips);
assert!(!result.is_empty());
let result_area: f64 = result.iter().map(|p| area(p).abs()).sum();
assert!(result_area > 5000.0);
}
#[test]
fn test_boolean_op_tree_64() {
let subjects = vec![square_64(0, 0, 100)];
let clips = vec![square_64(0, 0, 50)]; let mut tree = PolyTree64::new();
boolean_op_tree_64(
ClipType::Difference,
FillRule::NonZero,
&subjects,
&clips,
&mut tree,
);
let root = tree.root();
assert!(root.count() > 0 || tree.nodes.len() > 1);
}
#[test]
fn test_boolean_op_d_intersection() {
let subjects = vec![square_d(0.0, 0.0, 100.0)];
let clips = vec![square_d(50.0, 50.0, 100.0)];
let result = boolean_op_d(
ClipType::Intersection,
FillRule::NonZero,
&subjects,
&clips,
2,
);
assert!(!result.is_empty());
}
#[test]
fn test_intersect_64() {
let subjects = vec![square_64(0, 0, 100)];
let clips = vec![square_64(50, 50, 100)];
let result = intersect_64(&subjects, &clips, FillRule::NonZero);
assert!(!result.is_empty());
}
#[test]
fn test_intersect_d() {
let subjects = vec![square_d(0.0, 0.0, 100.0)];
let clips = vec![square_d(50.0, 50.0, 100.0)];
let result = intersect_d(&subjects, &clips, FillRule::NonZero, 2);
assert!(!result.is_empty());
}
#[test]
fn test_union_64() {
let subjects = vec![square_64(0, 0, 100)];
let clips = vec![square_64(50, 0, 100)];
let result = union_64(&subjects, &clips, FillRule::NonZero);
assert!(!result.is_empty());
}
#[test]
fn test_union_d() {
let subjects = vec![square_d(0.0, 0.0, 100.0)];
let clips = vec![square_d(50.0, 0.0, 100.0)];
let result = union_d(&subjects, &clips, FillRule::NonZero, 2);
assert!(!result.is_empty());
}
#[test]
fn test_union_subjects_64() {
let subjects = vec![square_64(0, 0, 100), square_64(50, 0, 100)];
let result = union_subjects_64(&subjects, FillRule::NonZero);
assert!(!result.is_empty());
}
#[test]
fn test_union_subjects_d() {
let subjects = vec![square_d(0.0, 0.0, 100.0), square_d(50.0, 0.0, 100.0)];
let result = union_subjects_d(&subjects, FillRule::NonZero, 2);
assert!(!result.is_empty());
}
#[test]
fn test_difference_64() {
let subjects = vec![square_64(0, 0, 100)];
let clips = vec![square_64(50, 50, 100)];
let result = difference_64(&subjects, &clips, FillRule::NonZero);
assert!(!result.is_empty());
let result_area: f64 = result.iter().map(|p| area(p).abs()).sum();
let original = area(&subjects[0]).abs();
assert!(result_area < original, "Difference should reduce area");
}
#[test]
fn test_difference_d() {
let subjects = vec![square_d(0.0, 0.0, 100.0)];
let clips = vec![square_d(50.0, 50.0, 100.0)];
let result = difference_d(&subjects, &clips, FillRule::NonZero, 2);
assert!(!result.is_empty());
}
#[test]
fn test_xor_64() {
let subjects = vec![square_64(0, 0, 100)];
let clips = vec![square_64(50, 50, 100)];
let result = xor_64(&subjects, &clips, FillRule::NonZero);
assert!(!result.is_empty());
}
#[test]
fn test_xor_d() {
let subjects = vec![square_d(0.0, 0.0, 100.0)];
let clips = vec![square_d(50.0, 50.0, 100.0)];
let result = xor_d(&subjects, &clips, FillRule::NonZero, 2);
assert!(!result.is_empty());
}
#[test]
fn test_inflate_paths_64() {
let paths = vec![square_64(0, 0, 100)];
let result = inflate_paths_64(&paths, 10.0, JoinType::Miter, EndType::Polygon, 2.0, 0.0);
assert!(!result.is_empty());
let result_area: f64 = result.iter().map(|p| area(p).abs()).sum();
let original_area = area(&paths[0]).abs();
assert!(result_area > original_area);
}
#[test]
fn test_inflate_paths_64_zero_delta() {
let paths = vec![square_64(0, 0, 100)];
let result = inflate_paths_64(&paths, 0.0, JoinType::Miter, EndType::Polygon, 2.0, 0.0);
assert_eq!(result.len(), paths.len());
}
#[test]
fn test_inflate_paths_d() {
let paths = vec![square_d(0.0, 0.0, 100.0)];
let result = inflate_paths_d(&paths, 10.0, JoinType::Miter, EndType::Polygon, 2.0, 2, 0.0);
assert!(!result.is_empty());
}
#[test]
fn test_translate_path_64() {
let path = square_64(0, 0, 100);
let translated = translate_path(&path, 50i64, 50i64);
assert_eq!(translated.len(), path.len());
for (orig, trans) in path.iter().zip(translated.iter()) {
assert_eq!(trans.x, orig.x + 50);
assert_eq!(trans.y, orig.y + 50);
}
}
#[test]
fn test_translate_path_d() {
let path = square_d(0.0, 0.0, 100.0);
let translated = translate_path(&path, 50.0f64, 50.0f64);
assert_eq!(translated.len(), path.len());
for (orig, trans) in path.iter().zip(translated.iter()) {
assert!((trans.x - (orig.x + 50.0)).abs() < 1e-10);
assert!((trans.y - (orig.y + 50.0)).abs() < 1e-10);
}
}
#[test]
fn test_translate_paths() {
let paths = vec![square_64(0, 0, 100), square_64(200, 0, 50)];
let translated = translate_paths(&paths, 10i64, 20i64);
assert_eq!(translated.len(), 2);
assert_eq!(translated[0][0].x, paths[0][0].x + 10);
assert_eq!(translated[0][0].y, paths[0][0].y + 20);
}
#[test]
fn test_rect_clip_64() {
let rect = Rect64::new(-50, -50, 50, 50);
let paths = vec![square_64(0, 0, 100)];
let result = rect_clip_64(&rect, &paths);
assert!(!result.is_empty());
let result_area: f64 = result.iter().map(|p| area(p).abs()).sum();
assert!(
(result_area - 10000.0).abs() < 500.0,
"Expected ~10000, got {}",
result_area
);
}
#[test]
fn test_rect_clip_path_64() {
let rect = Rect64::new(-50, -50, 50, 50);
let path = square_64(0, 0, 100);
let result = rect_clip_path_64(&rect, &path);
assert!(!result.is_empty());
}
#[test]
fn test_rect_clip_empty() {
let rect = Rect64::new(0, 0, 0, 0); let paths = vec![square_64(0, 0, 100)];
let result = rect_clip_64(&rect, &paths);
assert!(result.is_empty());
}
#[test]
fn test_rect_clip_lines_64() {
let rect = Rect64::new(-50, -50, 50, 50);
let lines = vec![vec![Point64::new(-200, 0), Point64::new(200, 0)]];
let result = rect_clip_lines_64(&rect, &lines);
assert!(!result.is_empty());
}
#[test]
fn test_rect_clip_line_64() {
let rect = Rect64::new(-50, -50, 50, 50);
let line = vec![Point64::new(-200, 0), Point64::new(200, 0)];
let result = rect_clip_line_64(&rect, &line);
assert!(!result.is_empty());
}
#[test]
fn test_make_path64() {
let path = make_path64(&[0, 0, 100, 0, 100, 100, 0, 100]);
assert_eq!(path.len(), 4);
assert_eq!(path[0], Point64::new(0, 0));
assert_eq!(path[1], Point64::new(100, 0));
assert_eq!(path[2], Point64::new(100, 100));
assert_eq!(path[3], Point64::new(0, 100));
}
#[test]
fn test_make_path64_odd_count() {
let path = make_path64(&[0, 0, 100, 0, 100]);
assert_eq!(path.len(), 2);
}
#[test]
fn test_make_path_d() {
let path = make_path_d(&[0.0, 0.0, 100.5, 0.0, 100.5, 100.5, 0.0, 100.5]);
assert_eq!(path.len(), 4);
assert!((path[0].x - 0.0).abs() < 1e-10);
assert!((path[2].x - 100.5).abs() < 1e-10);
}
#[test]
fn test_trim_collinear_closed() {
let path = vec![
Point64::new(0, 0),
Point64::new(50, 0), Point64::new(100, 0),
Point64::new(100, 100),
Point64::new(0, 100),
];
let result = trim_collinear_64(&path, false);
assert_eq!(result.len(), 4, "Collinear point should be removed");
}
#[test]
fn test_trim_collinear_open() {
let path = vec![
Point64::new(0, 0),
Point64::new(50, 0), Point64::new(100, 0),
];
let result = trim_collinear_64(&path, true);
assert_eq!(result.len(), 2, "Open collinear should reduce to endpoints");
}
#[test]
fn test_trim_collinear_too_short() {
let path = vec![Point64::new(0, 0), Point64::new(100, 0)];
let result = trim_collinear_64(&path, false);
assert!(result.is_empty(), "Closed path with < 3 points is empty");
let result_open = trim_collinear_64(&path, true);
assert_eq!(result_open.len(), 2, "Open 2-point path stays");
}
#[test]
fn test_trim_collinear_d() {
let path = vec![
Point::<f64>::new(0.0, 0.0),
Point::<f64>::new(50.0, 0.0),
Point::<f64>::new(100.0, 0.0),
Point::<f64>::new(100.0, 100.0),
Point::<f64>::new(0.0, 100.0),
];
let result = trim_collinear_d(&path, 2, false);
assert_eq!(result.len(), 4);
}
#[test]
fn test_distance_basic() {
let p1 = Point64::new(0, 0);
let p2 = Point64::new(3, 4);
assert!((distance(p1, p2) - 5.0).abs() < 1e-10);
}
#[test]
fn test_distance_same_point() {
let p = Point64::new(42, 42);
assert!((distance(p, p) - 0.0).abs() < 1e-10);
}
#[test]
fn test_path_length_open() {
let path = vec![
Point64::new(0, 0),
Point64::new(100, 0),
Point64::new(100, 100),
];
let len = path_length(&path, false);
assert!((len - 200.0).abs() < 1e-10);
}
#[test]
fn test_path_length_closed() {
let path = vec![
Point64::new(0, 0),
Point64::new(100, 0),
Point64::new(100, 100),
Point64::new(0, 100),
];
let len = path_length(&path, true);
assert!((len - 400.0).abs() < 1e-10);
}
#[test]
fn test_path_length_too_short() {
let path: Path64 = vec![Point64::new(0, 0)];
assert_eq!(path_length(&path, false), 0.0);
}
#[test]
fn test_near_collinear_true() {
let p1 = Point64::new(0, 0);
let p2 = Point64::new(50, 1); let p3 = Point64::new(100, 0);
assert!(near_collinear(p1, p2, p3, 0.01));
}
#[test]
fn test_near_collinear_false() {
let p1 = Point64::new(0, 0);
let p2 = Point64::new(50, 50); let p3 = Point64::new(100, 0);
assert!(!near_collinear(p1, p2, p3, 0.01));
}
#[test]
fn test_simplify_path_basic() {
let path = vec![
Point64::new(0, 0),
Point64::new(10, 1), Point64::new(20, 0),
Point64::new(30, 1), Point64::new(40, 0),
];
let result = simplify_path(&path, 5.0, false);
assert!(result.len() <= path.len());
}
#[test]
fn test_simplify_path_short() {
let path = vec![
Point64::new(0, 0),
Point64::new(50, 0),
Point64::new(100, 0),
];
let result = simplify_path(&path, 5.0, false);
assert_eq!(result.len(), 3);
}
#[test]
fn test_simplify_paths() {
let paths = vec![
vec![
Point64::new(0, 0),
Point64::new(10, 1),
Point64::new(20, 0),
Point64::new(20, 20),
Point64::new(0, 20),
],
vec![
Point64::new(100, 100),
Point64::new(110, 101),
Point64::new(120, 100),
Point64::new(120, 120),
Point64::new(100, 120),
],
];
let result = simplify_paths(&paths, 5.0, true);
assert_eq!(result.len(), 2);
}
#[test]
fn test_poly_tree_to_paths64_empty() {
let tree = PolyTree64::new();
let result = poly_tree_to_paths64(&tree);
assert!(result.is_empty());
}
#[test]
fn test_poly_tree_to_paths64() {
let mut tree = PolyTree64::new();
let outer = square_64(0, 0, 100);
tree.add_child(0, outer.clone());
let result = poly_tree_to_paths64(&tree);
assert_eq!(result.len(), 1);
assert_eq!(result[0].len(), outer.len());
}
#[test]
fn test_poly_tree_to_paths_d_empty() {
let tree = PolyTreeD::new();
let result = poly_tree_to_paths_d(&tree);
assert!(result.is_empty());
}
#[test]
fn test_ramer_douglas_peucker_short_path() {
let path = vec![
Point64::new(0, 0),
Point64::new(50, 0),
Point64::new(100, 0),
Point64::new(100, 100),
];
let result = ramer_douglas_peucker(&path, 5.0);
assert_eq!(result.len(), 4);
}
#[test]
fn test_ramer_douglas_peucker_simplifies() {
let path = vec![
Point64::new(0, 0),
Point64::new(25, 1), Point64::new(50, 0),
Point64::new(75, 1), Point64::new(100, 0),
];
let result = ramer_douglas_peucker(&path, 5.0);
assert!(
result.len() < path.len(),
"RDP should simplify, got {} points",
result.len()
);
assert_eq!(result[0], path[0]);
assert_eq!(result[result.len() - 1], path[path.len() - 1]);
}
#[test]
fn test_ramer_douglas_peucker_paths() {
let paths = vec![
vec![
Point64::new(0, 0),
Point64::new(25, 1),
Point64::new(50, 0),
Point64::new(75, 1),
Point64::new(100, 0),
],
vec![
Point64::new(0, 0),
Point64::new(0, 50),
Point64::new(0, 100),
Point64::new(50, 100),
Point64::new(100, 100),
],
];
let result = ramer_douglas_peucker_paths(&paths, 5.0);
assert_eq!(result.len(), 2);
}
#[test]
fn test_boolean_op_empty_subjects() {
let subjects = Paths64::new();
let clips = vec![square_64(0, 0, 100)];
let result = boolean_op_64(ClipType::Intersection, FillRule::NonZero, &subjects, &clips);
assert!(result.is_empty());
}
#[test]
fn test_boolean_op_empty_clips() {
let subjects = vec![square_64(0, 0, 100)];
let clips = Paths64::new();
let result = boolean_op_64(ClipType::Union, FillRule::NonZero, &subjects, &clips);
let _ = result;
}
#[test]
fn test_rect_clip_d() {
let rect = RectD::new(-50.0, -50.0, 50.0, 50.0);
let paths = vec![square_d(0.0, 0.0, 100.0)];
let result = rect_clip_d(&rect, &paths, 2);
assert!(!result.is_empty());
}
#[test]
fn test_rect_clip_lines_d() {
let rect = RectD::new(-50.0, -50.0, 50.0, 50.0);
let lines = vec![vec![
Point::<f64>::new(-200.0, 0.0),
Point::<f64>::new(200.0, 0.0),
]];
let result = rect_clip_lines_d(&rect, &lines, 2);
assert!(!result.is_empty());
}
#[test]
fn test_check_polytree_fully_contains_children_empty() {
let tree = PolyTree64::new();
assert!(check_polytree_fully_contains_children(&tree));
}