use euclid::{ approxeq::ApproxEq, default::{Rect, Size2D, Transform3D}, point2, point3, vec3, Angle, }; use plane_split::{Intersection, Line, LineProjection, NegativeHemisphereError, Plane, Polygon}; #[test] fn line_proj_bounds() { assert_eq!( (-5.0f64, 4.0), LineProjection { markers: [-5.0f64, 1.0, 4.0, 2.0] } .get_bounds() ); assert_eq!( (1f64, 4.0), LineProjection { markers: [4f64, 3.0, 2.0, 1.0] } .get_bounds() ); } #[test] fn valid() { let poly_a: Polygon = Polygon { points: [ point3(0.0, 0.0, 0.0), point3(1.0, 1.0, 1.0), point3(1.0, 1.0, 0.0), point3(0.0, 1.0, 1.0), ], plane: Plane { normal: vec3(0.0, 1.0, 0.0), offset: -1.0, }, anchor: 0, }; assert!(!poly_a.is_valid()); // points[0] is outside let poly_b: Polygon = Polygon { points: [ point3(0.0, 1.0, 0.0), point3(1.0, 1.0, 1.0), point3(1.0, 1.0, 0.0), point3(0.0, 1.0, 1.0), ], plane: Plane { normal: vec3(0.0, 1.0, 0.0), offset: -1.0, }, anchor: 0, }; assert!(!poly_b.is_valid()); // winding is incorrect let poly_c: Polygon = Polygon { points: [ point3(0.0, 0.0, 1.0), point3(1.0, 0.0, 1.0), point3(1.0, 1.0, 1.0), point3(0.0, 1.0, 1.0), ], plane: Plane { normal: vec3(0.0, 0.0, 1.0), offset: -1.0, }, anchor: 0, }; assert!(poly_c.is_valid()); } #[test] fn empty() { let poly = Polygon::from_points( [ point3(0.0, 0.0, 1.0), point3(0.0, 0.0, 1.0), point3(0.0, 0.00000001, 1.0), point3(1.0, 0.0, 0.0), ], 1usize, ); assert_eq!(None, poly); } fn test_transformed(rect: Rect, transform: Transform3D) { let poly = Polygon::from_transformed_rect(rect, transform, 0).unwrap(); assert!(poly.is_valid()); let inv_transform = transform.inverse().unwrap(); let poly2 = Polygon::from_transformed_rect_with_inverse(rect, &transform, &inv_transform, 0).unwrap(); assert_eq!(poly.points, poly2.points); assert!(poly.plane.offset.approx_eq(&poly2.plane.offset)); assert!(poly.plane.normal.dot(poly2.plane.normal).approx_eq(&1.0)); } #[test] fn from_transformed_rect() { let rect = Rect::new(point2(10.0, 10.0), Size2D::new(20.0, 30.0)); let transform = Transform3D::rotation(0.5f64.sqrt(), 0.0, 0.5f64.sqrt(), Angle::radians(5.0)) .pre_translate(vec3(0.0, 0.0, 10.0)); test_transformed(rect, transform); } #[test] fn from_transformed_rect_perspective() { let rect = Rect::new(point2(-10.0, -5.0), Size2D::new(20.0, 30.0)); let mut transform = Transform3D::perspective(400.0).pre_translate(vec3(0.0, 0.0, 100.0)); transform.m44 = 0.7; //for fun test_transformed(rect, transform); } #[test] fn untransform_point() { let poly: Polygon = Polygon { points: [ point3(0.0, 0.0, 0.0), point3(0.5, 1.0, 0.0), point3(1.5, 1.0, 0.0), point3(1.0, 0.0, 0.0), ], plane: Plane { normal: vec3(0.0, 1.0, 0.0), offset: 0.0, }, anchor: 0, }; assert_eq!(poly.untransform_point(poly.points[0]), point2(0.0, 0.0)); assert_eq!(poly.untransform_point(poly.points[1]), point2(1.0, 0.0)); assert_eq!(poly.untransform_point(poly.points[2]), point2(1.0, 1.0)); assert_eq!(poly.untransform_point(poly.points[3]), point2(0.0, 1.0)); } #[test] fn are_outside() { let plane = Plane { normal: vec3(0.0, 0.0, 1.0), offset: -1.0, }; assert!(plane.are_outside(&[point3(0.0, 0.0, 1.1), point3(1.0, 1.0, 2.0),])); assert!(plane.are_outside(&[point3(0.5, 0.5, 1.0),])); assert!(!plane.are_outside(&[point3(0.0, 0.0, 1.0), point3(0.0, 0.0, -1.0),])); } #[test] fn intersect() { let poly_a: Polygon = Polygon { points: [ point3(0.0, 0.0, 1.0), point3(1.0, 0.0, 1.0), point3(1.0, 1.0, 1.0), point3(0.0, 1.0, 1.0), ], plane: Plane { normal: vec3(0.0, 0.0, 1.0), offset: -1.0, }, anchor: 0, }; assert!(poly_a.is_valid()); let poly_b: Polygon = Polygon { points: [ point3(0.5, 0.0, 2.0), point3(0.5, 1.0, 2.0), point3(0.5, 1.0, 0.0), point3(0.5, 0.0, 0.0), ], plane: Plane { normal: vec3(1.0, 0.0, 0.0), offset: -0.5, }, anchor: 0, }; assert!(poly_b.is_valid()); let intersection = match poly_a.intersect(&poly_b) { Intersection::Inside(result) => result, _ => panic!("Bad intersection"), }; assert!(intersection.is_valid()); // confirm the origin is on both planes assert!(poly_a .plane .signed_distance_to(&intersection.origin) .approx_eq(&0.0)); assert!(poly_b .plane .signed_distance_to(&intersection.origin) .approx_eq(&0.0)); // confirm the direction is coplanar to both planes assert!(poly_a.plane.normal.dot(intersection.dir).approx_eq(&0.0)); assert!(poly_b.plane.normal.dot(intersection.dir).approx_eq(&0.0)); let poly_c: Polygon = Polygon { points: [ point3(0.0, -1.0, 2.0), point3(0.0, -1.0, 0.0), point3(0.0, 0.0, 0.0), point3(0.0, 0.0, 2.0), ], plane: Plane { normal: vec3(1.0, 0.0, 0.0), offset: 0.0, }, anchor: 0, }; assert!(poly_c.is_valid()); let poly_d: Polygon = Polygon { points: [ point3(0.0, 0.0, 0.5), point3(1.0, 0.0, 0.5), point3(1.0, 1.0, 0.5), point3(0.0, 1.0, 0.5), ], plane: Plane { normal: vec3(0.0, 0.0, 1.0), offset: -0.5, }, anchor: 0, }; assert!(poly_d.is_valid()); assert!(poly_a.intersect(&poly_c).is_outside()); assert!(poly_a.intersect(&poly_d).is_outside()); } fn test_cut(poly_base: &Polygon, extra_count: u8, line: Line) { assert!(line.is_valid()); let normal = poly_base.plane.normal.cross(line.dir).normalize(); let mut poly = poly_base.clone(); let (extra1, extra2) = poly.split_with_normal(&line, &normal); assert!(poly.is_valid() && poly_base.contains(&poly)); assert_eq!(extra_count > 0, extra1.is_some()); assert_eq!(extra_count > 1, extra2.is_some()); if let Some(extra) = extra1 { assert!(extra.is_valid() && poly_base.contains(&extra)); } if let Some(extra) = extra2 { assert!(extra.is_valid() && poly_base.contains(&extra)); } } #[test] fn split() { let poly: Polygon = Polygon { points: [ point3(0.0, 1.0, 0.0), point3(1.0, 1.0, 0.0), point3(1.0, 1.0, 1.0), point3(0.0, 1.0, 1.0), ], plane: Plane { normal: vec3(0.0, 1.0, 0.0), offset: -1.0, }, anchor: 0, }; // non-intersecting line test_cut( &poly, 0, Line { origin: point3(0.0, 1.0, 0.5), dir: vec3(0.0, 1.0, 0.0), }, ); // simple cut (diff=2) test_cut( &poly, 1, Line { origin: point3(0.0, 1.0, 0.5), dir: vec3(1.0, 0.0, 0.0), }, ); // complex cut (diff=1, wrapped) test_cut( &poly, 2, Line { origin: point3(0.0, 1.0, 0.5), dir: vec3(0.5f64.sqrt(), 0.0, -0.5f64.sqrt()), }, ); // complex cut (diff=1, non-wrapped) test_cut( &poly, 2, Line { origin: point3(0.5, 1.0, 0.0), dir: vec3(0.5f64.sqrt(), 0.0, 0.5f64.sqrt()), }, ); // complex cut (diff=3) test_cut( &poly, 2, Line { origin: point3(0.5, 1.0, 0.0), dir: vec3(-0.5f64.sqrt(), 0.0, 0.5f64.sqrt()), }, ); // perfect diagonal test_cut( &poly, 1, Line { origin: point3(0.0, 1.0, 0.0), dir: vec3(0.5f64.sqrt(), 0.0, 0.5f64.sqrt()), }, ); } #[test] fn plane_unnormalized() { let zero_vec = vec3(0.0000001, 0.0, 0.0); let mut plane: Result, _> = Plane::from_unnormalized(zero_vec, 1.0); assert_eq!(plane, Ok(None)); plane = Plane::from_unnormalized(zero_vec, 0.0); assert_eq!(plane, Err(NegativeHemisphereError)); plane = Plane::from_unnormalized(zero_vec, -0.5); assert_eq!(plane, Err(NegativeHemisphereError)); let plane = Plane::from_unnormalized(vec3(-3.0, 4.0, 0.0), 2.0) .unwrap() .unwrap(); let expected = Plane { normal: vec3(-3.0 / 5.0, 4.0 / 5.0, 0.0), offset: 2.0 / 5.0, }; assert!(plane.normal.approx_eq(&expected.normal)); assert!(plane.offset.approx_eq(&expected.offset)); }