@@ -2167,4 +2167,295 @@ mod tests {
21672167 assert_eq ! ( result[ 1 ] . a, point( 0.0 , 0.1 ) ) ; // t1
21682168 assert_eq ! ( result[ 1 ] . b, point( 0.6 , 0.1 ) ) ; // t2 = arc2.b
21692169 }
2170+
2171+ // ===== hull_seg_arc Tests =====
2172+
2173+ #[ test]
2174+ fn test_hull_seg_arc_tangent_from_seg_end ( ) {
2175+ // Segment to arc where tangent from seg.b to arc exists and is on the arc
2176+ // Horizontal segment, arc to the right and above
2177+ let seg = arcseg ( point ( 0.0 , 0.0 ) , point ( 2.0 , 0.0 ) ) ;
2178+ let arc_center = point ( 5.0 , 0.0 ) ;
2179+ let arc_radius = 2.0 ;
2180+ // Arc on right side of circle, top quadrant
2181+ let arc2 = arc ( point ( 7.0 , 0.0 ) , point ( 5.0 , 2.0 ) , arc_center, arc_radius) ;
2182+
2183+ let result = hull_seg_arc ( seg, arc2) ;
2184+
2185+ // Tangent connects to arc start, so: tangent line + full arc
2186+ assert_eq ! ( result. len( ) , 2 ) ;
2187+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2188+ assert_eq ! ( result[ 0 ] . a, point( 2.0 , 0.0 ) ) ; // seg.b
2189+ assert_eq ! ( result[ 0 ] . b, point( 7.0 , 0.0 ) ) ; // arc2.a
2190+ assert ! ( !result[ 1 ] . is_seg( ) ) ;
2191+ assert_eq ! ( result[ 1 ] . a, point( 7.0 , 0.0 ) ) ;
2192+ assert_eq ! ( result[ 1 ] . b, point( 5.0 , 2.0 ) ) ;
2193+ }
2194+
2195+ #[ test]
2196+ fn test_hull_seg_arc_tangent_point_on_arc ( ) {
2197+ // Segment with tangent point that lies exactly on the arc
2198+ let seg = arcseg ( point ( 0.0 , 0.0 ) , point ( 1.0 , 0.0 ) ) ;
2199+ // Circle at (5, 0) radius 2, arc from right to top
2200+ let arc2 = arc ( point ( 7.0 , 0.0 ) , point ( 5.0 , 2.0 ) , point ( 5.0 , 0.0 ) , 2.0 ) ;
2201+
2202+ let result = hull_seg_arc ( seg, arc2) ;
2203+
2204+ // Tangent connects to arc start
2205+ assert_eq ! ( result. len( ) , 2 ) ;
2206+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2207+ assert_eq ! ( result[ 0 ] . a, point( 1.0 , 0.0 ) ) ; // seg.b
2208+ assert_eq ! ( result[ 0 ] . b, point( 7.0 , 0.0 ) ) ; // arc2.a
2209+ assert ! ( !result[ 1 ] . is_seg( ) ) ;
2210+ assert_eq ! ( result[ 1 ] , arc2) ;
2211+ }
2212+
2213+ #[ test]
2214+ fn test_hull_seg_arc_no_tangent_connect_to_arc_start ( ) {
2215+ // Segment and arc where tangent computation finds valid tangent on arc
2216+ let seg = arcseg ( point ( 0.0 , 0.0 ) , point ( 1.0 , 1.0 ) ) ;
2217+ let arc2 = arc ( point ( 3.0 , 0.0 ) , point ( 4.0 , 1.0 ) , point ( 4.0 , 0.0 ) , 1.0 ) ;
2218+
2219+ let result = hull_seg_arc ( seg, arc2) ;
2220+
2221+ // Tangent connection produces 2 elements
2222+ assert_eq ! ( result. len( ) , 2 ) ;
2223+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2224+ assert ! ( !result[ 1 ] . is_seg( ) ) ;
2225+ }
2226+
2227+ #[ test]
2228+ fn test_hull_seg_arc_segment_below_arc ( ) {
2229+ // Horizontal segment below a convex arc
2230+ let seg = arcseg ( point ( 0.0 , 0.0 ) , point ( 2.0 , 0.0 ) ) ;
2231+ let arc2 = arc ( point ( 3.0 , 0.0 ) , point ( 3.0 , 2.0 ) , point ( 3.0 , 1.0 ) , 1.0 ) ;
2232+
2233+ let result = hull_seg_arc ( seg, arc2) ;
2234+
2235+ // Produces tangent or endpoint connection, 2 elements
2236+ assert_eq ! ( result. len( ) , 2 ) ;
2237+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2238+ assert ! ( !result[ 1 ] . is_seg( ) ) ;
2239+ }
2240+
2241+ #[ test]
2242+ fn test_hull_seg_arc_segment_to_left_of_arc ( ) {
2243+ // Segment to the left of arc
2244+ let seg = arcseg ( point ( 0.0 , 0.0 ) , point ( 0.0 , 2.0 ) ) ; // Vertical segment
2245+ let arc2 = arc ( point ( 3.0 , 0.0 ) , point ( 5.0 , 1.0 ) , point ( 4.0 , 0.0 ) , 1.414 ) ;
2246+
2247+ let result = hull_seg_arc ( seg, arc2) ;
2248+
2249+ // Tangent connection, 2 elements
2250+ assert_eq ! ( result. len( ) , 2 ) ;
2251+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2252+ assert ! ( !result[ 1 ] . is_seg( ) ) ;
2253+ }
2254+
2255+ #[ test]
2256+ fn test_hull_seg_arc_vertical_segment_horizontal_arc ( ) {
2257+ // Vertical segment connecting to horizontally oriented arc
2258+ let seg = arcseg ( point ( 0.0 , 0.0 ) , point ( 0.0 , 2.0 ) ) ;
2259+ let arc2 = arc ( point ( 2.0 , 3.0 ) , point ( 4.0 , 3.0 ) , point ( 3.0 , 3.0 ) , 1.0 ) ;
2260+
2261+ let result = hull_seg_arc ( seg, arc2) ;
2262+
2263+ // Tangent connection, 2 elements
2264+ assert_eq ! ( result. len( ) , 2 ) ;
2265+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2266+ assert ! ( !result[ 1 ] . is_seg( ) ) ;
2267+ }
2268+
2269+ #[ test]
2270+ fn test_hull_seg_arc_diagonal_segment_to_arc ( ) {
2271+ // Diagonal segment to arc
2272+ let seg = arcseg ( point ( 0.0 , 0.0 ) , point ( 2.0 , 1.0 ) ) ;
2273+ let arc2 = arc ( point ( 4.0 , 2.0 ) , point ( 5.0 , 3.0 ) , point ( 5.0 , 2.0 ) , 1.0 ) ;
2274+
2275+ let result = hull_seg_arc ( seg, arc2) ;
2276+
2277+ // Connection, 2 elements
2278+ assert_eq ! ( result. len( ) , 2 ) ;
2279+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2280+ assert ! ( !result[ 1 ] . is_seg( ) ) ;
2281+ }
2282+
2283+ #[ test]
2284+ fn test_hull_seg_arc_segment_far_from_arc ( ) {
2285+ // Segment far away from arc
2286+ let seg = arcseg ( point ( 0.0 , 0.0 ) , point ( 1.0 , 0.0 ) ) ;
2287+ let arc2 = arc ( point ( 10.0 , 5.0 ) , point ( 11.0 , 6.0 ) , point ( 11.0 , 5.0 ) , 1.0 ) ;
2288+
2289+ let result = hull_seg_arc ( seg, arc2) ;
2290+
2291+ // Tangent connection, 2 elements
2292+ assert_eq ! ( result. len( ) , 2 ) ;
2293+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2294+ assert ! ( !result[ 1 ] . is_seg( ) ) ;
2295+ }
2296+
2297+ #[ test]
2298+ fn test_hull_seg_arc_touching_segment_and_arc ( ) {
2299+ // Segment endpoint touches arc start, but tangent from seg.a to arc exists
2300+ let seg = arcseg ( point ( 0.0 , 0.0 ) , point ( 2.0 , 0.0 ) ) ;
2301+ let arc2 = arc ( point ( 2.0 , 0.0 ) , point ( 3.0 , 1.0 ) , point ( 3.0 , 0.0 ) , 1.0 ) ;
2302+
2303+ let result = hull_seg_arc ( seg, arc2) ;
2304+
2305+ // Tangent from seg.a found on arc, produces 2 elements
2306+ assert_eq ! ( result. len( ) , 2 ) ;
2307+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2308+ // Tangent from (0,0) to arc's circle
2309+ assert ! ( result[ 0 ] . a == seg. a) ;
2310+ assert ! ( !result[ 1 ] . is_seg( ) ) ;
2311+ }
2312+
2313+ #[ test]
2314+ fn test_hull_seg_arc_small_arc ( ) {
2315+ // Small arc to test numerical stability
2316+ let seg = arcseg ( point ( 0.0 , 0.0 ) , point ( 0.1 , 0.0 ) ) ;
2317+ let arc2 = arc ( point ( 0.3 , 0.0 ) , point ( 0.4 , 0.05 ) , point ( 0.4 , 0.0 ) , 0.05 ) ;
2318+
2319+ let result = hull_seg_arc ( seg, arc2) ;
2320+
2321+ // Should produce 2 elements
2322+ assert_eq ! ( result. len( ) , 2 ) ;
2323+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2324+ assert ! ( !result[ 1 ] . is_seg( ) ) ;
2325+ }
2326+
2327+ #[ test]
2328+ fn test_hull_seg_arc_large_arc ( ) {
2329+ // Large arc with segment
2330+ let seg = arcseg ( point ( 0.0 , 0.0 ) , point ( 5.0 , 0.0 ) ) ;
2331+ let arc2 = arc ( point ( 10.0 , 0.0 ) , point ( 10.0 , 10.0 ) , point ( 10.0 , 5.0 ) , 5.0 ) ;
2332+
2333+ let result = hull_seg_arc ( seg, arc2) ;
2334+
2335+ // Connection, 2 elements
2336+ assert_eq ! ( result. len( ) , 2 ) ;
2337+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2338+ assert ! ( !result[ 1 ] . is_seg( ) ) ;
2339+ }
2340+
2341+ #[ test]
2342+ fn test_hull_seg_arc_negative_coordinates ( ) {
2343+ // Segment and arc with negative coordinates
2344+ let seg = arcseg ( point ( -5.0 , -2.0 ) , point ( -3.0 , -2.0 ) ) ;
2345+ let arc2 = arc ( point ( -1.0 , -1.0 ) , point ( 0.0 , 0.0 ) , point ( 0.0 , -1.0 ) , 1.0 ) ;
2346+
2347+ let result = hull_seg_arc ( seg, arc2) ;
2348+
2349+ // Connection, 2 elements
2350+ assert_eq ! ( result. len( ) , 2 ) ;
2351+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2352+ assert ! ( !result[ 1 ] . is_seg( ) ) ;
2353+ }
2354+
2355+ #[ test]
2356+ fn test_hull_seg_arc_arc_below_segment ( ) {
2357+ // Arc positioned below the segment
2358+ let seg = arcseg ( point ( 0.0 , 5.0 ) , point ( 2.0 , 5.0 ) ) ;
2359+ let arc2 = arc ( point ( 3.0 , 2.0 ) , point ( 4.0 , 1.0 ) , point ( 4.0 , 2.0 ) , 1.0 ) ;
2360+
2361+ let result = hull_seg_arc ( seg, arc2) ;
2362+
2363+ // Connection, 2 elements
2364+ assert_eq ! ( result. len( ) , 2 ) ;
2365+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2366+ assert ! ( !result[ 1 ] . is_seg( ) ) ;
2367+ }
2368+
2369+ #[ test]
2370+ fn test_hull_seg_arc_semicircle ( ) {
2371+ // Segment to semicircular arc
2372+ let seg = arcseg ( point ( 0.0 , 0.0 ) , point ( 1.0 , 0.0 ) ) ;
2373+ let arc2 = arc ( point ( 3.0 , 0.0 ) , point ( 5.0 , 0.0 ) , point ( 4.0 , 0.0 ) , 1.0 ) ;
2374+
2375+ let result = hull_seg_arc ( seg, arc2) ;
2376+
2377+ // Tangent connection, 2 elements
2378+ assert_eq ! ( result. len( ) , 2 ) ;
2379+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2380+ assert ! ( !result[ 1 ] . is_seg( ) ) ;
2381+ }
2382+
2383+ #[ test]
2384+ fn test_hull_seg_arc_quarter_circle ( ) {
2385+ // Segment to quarter circle arc
2386+ let seg = arcseg ( point ( 0.0 , 0.0 ) , point ( 1.0 , 0.0 ) ) ;
2387+ // Quarter arc from right to top
2388+ let arc2 = arc ( point ( 3.0 , 0.0 ) , point ( 2.0 , 1.0 ) , point ( 2.0 , 0.0 ) , 1.0 ) ;
2389+
2390+ let result = hull_seg_arc ( seg, arc2) ;
2391+
2392+ // Connection from seg.b to arc start + full arc
2393+ assert_eq ! ( result. len( ) , 2 ) ;
2394+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2395+ assert_eq ! ( result[ 0 ] . a, point( 1.0 , 0.0 ) ) ; // seg.b
2396+ assert_eq ! ( result[ 0 ] . b, point( 3.0 , 0.0 ) ) ; // arc2.a
2397+ assert_eq ! ( result[ 1 ] , arc2) ;
2398+ }
2399+
2400+ #[ test]
2401+ fn test_hull_seg_arc_arc_wrapping_around ( ) {
2402+ // Arc that wraps significantly (large angular span)
2403+ let seg = arcseg ( point ( 0.0 , 0.0 ) , point ( 1.0 , 0.0 ) ) ;
2404+ // Large arc spanning more than 180 degrees
2405+ let arc2 = arc ( point ( 4.0 , 0.0 ) , point ( 2.0 , 0.0 ) , point ( 3.0 , 0.0 ) , 1.0 ) ;
2406+
2407+ let result = hull_seg_arc ( seg, arc2) ;
2408+
2409+ // Connection from seg.b to arc start + full arc
2410+ assert_eq ! ( result. len( ) , 2 ) ;
2411+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2412+ assert_eq ! ( result[ 0 ] . a, point( 1.0 , 0.0 ) ) ; // seg.b
2413+ assert_eq ! ( result[ 0 ] . b, point( 4.0 , 0.0 ) ) ; // arc2.a
2414+ assert_eq ! ( result[ 1 ] , arc2) ;
2415+ }
2416+
2417+ #[ test]
2418+ fn test_hull_seg_arc_parallel_segment_and_arc_chord ( ) {
2419+ // Segment parallel to arc's chord
2420+ let seg = arcseg ( point ( 0.0 , 0.0 ) , point ( 2.0 , 0.0 ) ) ;
2421+ let arc2 = arc ( point ( 3.0 , 1.0 ) , point ( 5.0 , 1.0 ) , point ( 4.0 , 1.0 ) , 1.0 ) ;
2422+
2423+ let result = hull_seg_arc ( seg, arc2) ;
2424+
2425+ // Connection, 2 elements
2426+ assert_eq ! ( result. len( ) , 2 ) ;
2427+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2428+ assert ! ( !result[ 1 ] . is_seg( ) ) ;
2429+ }
2430+
2431+ #[ test]
2432+ fn test_hull_seg_arc_perpendicular_configurations ( ) {
2433+ // Segment perpendicular to arc's general direction
2434+ let seg = arcseg ( point ( 0.0 , 0.0 ) , point ( 0.0 , 2.0 ) ) ; // Vertical
2435+ let arc2 = arc ( point ( 2.0 , 3.0 ) , point ( 4.0 , 3.0 ) , point ( 3.0 , 3.0 ) , 1.0 ) ; // Horizontal arc
2436+
2437+ let result = hull_seg_arc ( seg, arc2) ;
2438+
2439+ // Tangent connection, 2 elements
2440+ assert_eq ! ( result. len( ) , 2 ) ;
2441+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2442+ assert ! ( !result[ 1 ] . is_seg( ) ) ;
2443+ }
2444+
2445+ #[ test]
2446+ fn test_hull_seg_arc_segment_inside_arc_circle ( ) {
2447+ // Segment that's inside the circle's radius but arc doesn't cover it
2448+ let seg = arcseg ( point ( 0.0 , 0.0 ) , point ( 1.0 , 0.0 ) ) ;
2449+ // Circle center at (2, 0), radius 3, but arc only covers top portion
2450+ let arc2 = arc ( point ( 5.0 , 0.0 ) , point ( 2.0 , 3.0 ) , point ( 2.0 , 0.0 ) , 3.0 ) ;
2451+
2452+ let result = hull_seg_arc ( seg, arc2) ;
2453+
2454+ // Connection from seg.b to arc start + full arc
2455+ assert_eq ! ( result. len( ) , 2 ) ;
2456+ assert ! ( result[ 0 ] . is_seg( ) ) ;
2457+ assert_eq ! ( result[ 0 ] . a, point( 1.0 , 0.0 ) ) ; // seg.b
2458+ assert_eq ! ( result[ 0 ] . b, point( 5.0 , 0.0 ) ) ; // arc2.a
2459+ assert_eq ! ( result[ 1 ] , arc2) ;
2460+ }
21702461}
0 commit comments