@@ -535,4 +535,190 @@ mod tests {
535535 assert ! ( point. x != 0.0 || point. y != 0.0 || nfp. len( ) > 1 ) ;
536536 }
537537 }
538+
539+ #[ test]
540+ fn test_nfp_collinear_points ( ) {
541+ // Collinear points should still form a valid polygon (degenerate triangle)
542+ let triangle_a = vec ! [
543+ Point :: new( 0.0 , 0.0 ) ,
544+ Point :: new( 1.0 , 0.0 ) ,
545+ Point :: new( 0.5 , 0.0 ) , // Collinear with others
546+ ] ;
547+
548+ let triangle_b = vec ! [
549+ Point :: new( 0.0 , 0.0 ) ,
550+ Point :: new( 1.0 , 1.0 ) ,
551+ Point :: new( 0.0 , 1.0 ) ,
552+ ] ;
553+
554+ let result = NFP :: nfp ( & triangle_a, & triangle_b) ;
555+ // Should complete without panicking (even if degenerate)
556+ assert ! ( result. is_ok( ) ) ;
557+ }
558+
559+ #[ test]
560+ fn test_nfp_very_small_polygon ( ) {
561+ // Minimal triangles (smallest valid polygon)
562+ let tiny_a = vec ! [
563+ Point :: new( 0.0 , 0.0 ) ,
564+ Point :: new( 0.001 , 0.0 ) ,
565+ Point :: new( 0.0005 , 0.001 ) ,
566+ ] ;
567+
568+ let tiny_b = vec ! [
569+ Point :: new( 0.0 , 0.0 ) ,
570+ Point :: new( 0.002 , 0.0 ) ,
571+ Point :: new( 0.001 , 0.002 ) ,
572+ ] ;
573+
574+ let result = NFP :: nfp ( & tiny_a, & tiny_b) ;
575+ assert ! ( result. is_ok( ) ) ;
576+ let nfp = result. unwrap ( ) ;
577+ assert ! ( !nfp. is_empty( ) ) ;
578+ }
579+
580+ #[ test]
581+ fn test_nfp_negative_coordinates ( ) {
582+ // Polygons in negative coordinate space
583+ let triangle_a = vec ! [
584+ Point :: new( -2.0 , -2.0 ) ,
585+ Point :: new( -1.0 , -2.0 ) ,
586+ Point :: new( -1.5 , -1.0 ) ,
587+ ] ;
588+
589+ let triangle_b = vec ! [
590+ Point :: new( -3.0 , -3.0 ) ,
591+ Point :: new( -1.0 , -3.0 ) ,
592+ Point :: new( -2.0 , -1.0 ) ,
593+ ] ;
594+
595+ let result = NFP :: nfp ( & triangle_a, & triangle_b) ;
596+ assert ! ( result. is_ok( ) ) ;
597+ let nfp = result. unwrap ( ) ;
598+ assert ! ( !nfp. is_empty( ) ) ;
599+ }
600+
601+ #[ test]
602+ fn test_nfp_mixed_coordinate_signs ( ) {
603+ // Polygons spanning multiple quadrants
604+ let triangle_a = vec ! [
605+ Point :: new( -1.0 , -1.0 ) ,
606+ Point :: new( 1.0 , -1.0 ) ,
607+ Point :: new( 0.0 , 1.0 ) ,
608+ ] ;
609+
610+ let triangle_b = vec ! [
611+ Point :: new( -0.5 , -0.5 ) ,
612+ Point :: new( 0.5 , -0.5 ) ,
613+ Point :: new( 0.0 , 0.5 ) ,
614+ ] ;
615+
616+ let result = NFP :: nfp ( & triangle_a, & triangle_b) ;
617+ assert ! ( result. is_ok( ) ) ;
618+ let nfp = result. unwrap ( ) ;
619+ assert ! ( !nfp. is_empty( ) ) ;
620+ assert ! ( polygon:: is_ccw( & nfp) ) ;
621+ }
622+
623+ #[ test]
624+ fn test_nfp_identical_polygons ( ) {
625+ // Two identical triangles
626+ let triangle = vec ! [
627+ Point :: new( 0.0 , 0.0 ) ,
628+ Point :: new( 1.0 , 0.0 ) ,
629+ Point :: new( 0.5 , 1.0 ) ,
630+ ] ;
631+
632+ let result = NFP :: nfp ( & triangle, & triangle) ;
633+ assert ! ( result. is_ok( ) ) ;
634+ let nfp = result. unwrap ( ) ;
635+ assert ! ( !nfp. is_empty( ) ) ;
636+ }
637+
638+ #[ test]
639+ fn test_nfp_very_thin_rectangle ( ) {
640+ // Very thin elongated rectangle
641+ let thin_rect_a = vec ! [
642+ Point :: new( 0.0 , 0.0 ) ,
643+ Point :: new( 10.0 , 0.0 ) ,
644+ Point :: new( 10.0 , 0.01 ) ,
645+ Point :: new( 0.0 , 0.01 ) ,
646+ ] ;
647+
648+ let square = vec ! [
649+ Point :: new( 0.0 , 0.0 ) ,
650+ Point :: new( 1.0 , 0.0 ) ,
651+ Point :: new( 1.0 , 1.0 ) ,
652+ Point :: new( 0.0 , 1.0 ) ,
653+ ] ;
654+
655+ let result = NFP :: nfp ( & thin_rect_a, & square) ;
656+ assert ! ( result. is_ok( ) ) ;
657+ let nfp = result. unwrap ( ) ;
658+ assert ! ( !nfp. is_empty( ) ) ;
659+ }
660+
661+ #[ test]
662+ fn test_nfp_large_scale_coordinates ( ) {
663+ // Polygons with very large coordinates
664+ let large_a = vec ! [
665+ Point :: new( 1000.0 , 1000.0 ) ,
666+ Point :: new( 1100.0 , 1000.0 ) ,
667+ Point :: new( 1050.0 , 1100.0 ) ,
668+ ] ;
669+
670+ let large_b = vec ! [
671+ Point :: new( 2000.0 , 2000.0 ) ,
672+ Point :: new( 2050.0 , 2000.0 ) ,
673+ Point :: new( 2025.0 , 2050.0 ) ,
674+ ] ;
675+
676+ let result = NFP :: nfp ( & large_a, & large_b) ;
677+ assert ! ( result. is_ok( ) ) ;
678+ let nfp = result. unwrap ( ) ;
679+ assert ! ( !nfp. is_empty( ) ) ;
680+ }
681+
682+ #[ test]
683+ fn test_nfp_right_angle_triangle ( ) {
684+ // Right-angled triangle (standard form)
685+ let right_a = vec ! [
686+ Point :: new( 0.0 , 0.0 ) ,
687+ Point :: new( 3.0 , 0.0 ) ,
688+ Point :: new( 0.0 , 4.0 ) ,
689+ ] ;
690+
691+ let right_b = vec ! [
692+ Point :: new( 0.0 , 0.0 ) ,
693+ Point :: new( 1.0 , 0.0 ) ,
694+ Point :: new( 0.0 , 1.0 ) ,
695+ ] ;
696+
697+ let result = NFP :: nfp ( & right_a, & right_b) ;
698+ assert ! ( result. is_ok( ) ) ;
699+ let nfp = result. unwrap ( ) ;
700+ assert ! ( !nfp. is_empty( ) ) ;
701+ assert ! ( polygon:: is_ccw( & nfp) ) ;
702+ }
703+
704+ #[ test]
705+ fn test_nfp_polygon_with_zero_area ( ) {
706+ // Three points where area rounds to near-zero (but technically valid)
707+ // This tests robustness - should handle near-degenerate cases
708+ let point_a = vec ! [
709+ Point :: new( 0.0 , 0.0 ) ,
710+ Point :: new( 1.0 , 0.0 ) ,
711+ Point :: new( 1.0 + 1e-15 , 1e-15 ) , // Extremely close to collinear
712+ ] ;
713+
714+ let point_b = vec ! [
715+ Point :: new( 0.0 , 0.0 ) ,
716+ Point :: new( 0.5 , 0.0 ) ,
717+ Point :: new( 0.25 , 0.5 ) ,
718+ ] ;
719+
720+ let result = NFP :: nfp ( & point_a, & point_b) ;
721+ // Should complete without panicking
722+ assert ! ( result. is_ok( ) ) ;
723+ }
538724}
0 commit comments