1use crate::config::{CutDirectionPreference, CuttingConfig};
8use crate::contour::{ContourType, CutContour};
9use crate::cost::{closest_point_on_polygon, point_distance};
10use crate::result::CutDirection;
11
12#[derive(Debug, Clone)]
14pub struct PierceSelection {
15 pub point: (f64, f64),
17 pub vertex_index: usize,
19 pub direction: CutDirection,
21 pub end_point: (f64, f64),
23}
24
25pub fn select_pierce(
30 contour: &CutContour,
31 from_point: (f64, f64),
32 config: &CuttingConfig,
33) -> PierceSelection {
34 let (closest, vertex_idx, _t) = closest_point_on_polygon(&contour.vertices, from_point)
35 .unwrap_or((contour.vertices[0], 0, 0.0));
36
37 let direction = determine_cut_direction(contour.contour_type, config);
38
39 PierceSelection {
40 point: closest,
41 vertex_index: vertex_idx,
42 direction,
43 end_point: closest, }
45}
46
47fn determine_cut_direction(contour_type: ContourType, config: &CuttingConfig) -> CutDirection {
49 let pref = match contour_type {
50 ContourType::Exterior => config.exterior_direction,
51 ContourType::Interior => config.interior_direction,
52 };
53
54 match pref {
55 CutDirectionPreference::Ccw => CutDirection::Ccw,
56 CutDirectionPreference::Cw => CutDirection::Cw,
57 CutDirectionPreference::Auto => match contour_type {
58 ContourType::Exterior => CutDirection::Ccw,
59 ContourType::Interior => CutDirection::Cw,
60 },
61 }
62}
63
64pub fn rapid_distance_to_contour(from_point: (f64, f64), contour: &CutContour) -> f64 {
66 match closest_point_on_polygon(&contour.vertices, from_point) {
67 Some((closest, _, _)) => point_distance(from_point, closest),
68 None => f64::MAX,
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 fn make_square_contour(id: usize, cx: f64, cy: f64) -> CutContour {
77 CutContour {
78 id,
79 geometry_id: "test".to_string(),
80 instance: 0,
81 contour_type: ContourType::Exterior,
82 vertices: vec![
83 (cx - 5.0, cy - 5.0),
84 (cx + 5.0, cy - 5.0),
85 (cx + 5.0, cy + 5.0),
86 (cx - 5.0, cy + 5.0),
87 ],
88 perimeter: 40.0,
89 centroid: (cx, cy),
90 }
91 }
92
93 #[test]
94 fn test_select_pierce_closest_point() {
95 let contour = make_square_contour(0, 50.0, 50.0);
96 let config = CuttingConfig::default();
97 let sel = select_pierce(&contour, (50.0, 0.0), &config);
98
99 assert!((sel.point.0 - 50.0).abs() < 0.1);
101 assert!((sel.point.1 - 45.0).abs() < 0.1);
102 }
103
104 #[test]
105 fn test_auto_direction_exterior_ccw() {
106 let contour = CutContour {
107 id: 0,
108 geometry_id: "test".to_string(),
109 instance: 0,
110 contour_type: ContourType::Exterior,
111 vertices: vec![(0.0, 0.0), (10.0, 0.0), (10.0, 10.0), (0.0, 10.0)],
112 perimeter: 40.0,
113 centroid: (5.0, 5.0),
114 };
115 let config = CuttingConfig::default();
116 let sel = select_pierce(&contour, (0.0, 0.0), &config);
117 assert_eq!(sel.direction, CutDirection::Ccw);
118 }
119
120 #[test]
121 fn test_auto_direction_interior_cw() {
122 let contour = CutContour {
123 id: 0,
124 geometry_id: "test".to_string(),
125 instance: 0,
126 contour_type: ContourType::Interior,
127 vertices: vec![(2.0, 2.0), (8.0, 2.0), (8.0, 8.0), (2.0, 8.0)],
128 perimeter: 24.0,
129 centroid: (5.0, 5.0),
130 };
131 let config = CuttingConfig::default();
132 let sel = select_pierce(&contour, (0.0, 0.0), &config);
133 assert_eq!(sel.direction, CutDirection::Cw);
134 }
135
136 #[test]
137 fn test_rapid_distance() {
138 let contour = make_square_contour(0, 50.0, 50.0);
139 let dist = rapid_distance_to_contour((0.0, 0.0), &contour);
140 assert!(dist > 60.0 && dist < 70.0);
143 }
144
145 #[test]
146 fn test_pierce_end_point_equals_pierce_point() {
147 let contour = make_square_contour(0, 50.0, 50.0);
148 let config = CuttingConfig::default();
149 let sel = select_pierce(&contour, (0.0, 0.0), &config);
150 assert_eq!(sel.point, sel.end_point);
151 }
152
153 #[test]
154 fn test_forced_cw_exterior() {
155 let contour = CutContour {
156 id: 0,
157 geometry_id: "test".to_string(),
158 instance: 0,
159 contour_type: ContourType::Exterior,
160 vertices: vec![(0.0, 0.0), (10.0, 0.0), (10.0, 10.0), (0.0, 10.0)],
161 perimeter: 40.0,
162 centroid: (5.0, 5.0),
163 };
164 let config = CuttingConfig {
165 exterior_direction: CutDirectionPreference::Cw,
166 ..CuttingConfig::default()
167 };
168 let sel = select_pierce(&contour, (0.0, 0.0), &config);
169 assert_eq!(sel.direction, CutDirection::Cw);
170 }
171}