Generate boustrophedon coverage waypoints for a polygon zone.
(polygon_wps: &[Waypoint], config: &CoverageConfig)
| 42 | |
| 43 | /// Generate boustrophedon coverage waypoints for a polygon zone. |
| 44 | pub fn generate(polygon_wps: &[Waypoint], config: &CoverageConfig) -> Result<CoverageResult, String> { |
| 45 | if polygon_wps.len() < 3 { |
| 46 | return Err("Polygon must have at least 3 vertices".into()); |
| 47 | } |
| 48 | if config.tool_width <= 0.0 { |
| 49 | return Err("Tool width must be positive".into()); |
| 50 | } |
| 51 | if !(0.0..=50.0).contains(&config.overlap_pct) { |
| 52 | return Err("Overlap must be between 0 and 50 percent".into()); |
| 53 | } |
| 54 | |
| 55 | // Build geo polygon from waypoints |
| 56 | let coords: Vec<Coord> = polygon_wps.iter().map(|w| Coord { x: w.x, y: w.y }).collect(); |
| 57 | let line_string = LineString::new(coords.clone()); |
| 58 | let polygon = Polygon::new(line_string, vec![]); |
| 59 | |
| 60 | // Determine sweep angle |
| 61 | let angle = config.swath_angle.unwrap_or_else(|| longest_edge_angle(&coords)); |
| 62 | |
| 63 | // Rotation transform: rotate polygon so sweep direction aligns with X axis |
| 64 | let cos_a = angle.cos(); |
| 65 | let sin_a = angle.sin(); |
| 66 | // Rotate by -angle to align sweep with X |
| 67 | let to_sweep = AffineTransform::new(cos_a, sin_a, 0.0, -sin_a, cos_a, 0.0); |
| 68 | // Inverse: rotate back by +angle |
| 69 | let from_sweep = AffineTransform::new(cos_a, -sin_a, 0.0, sin_a, cos_a, 0.0); |
| 70 | |
| 71 | let rotated = polygon.affine_transform(&to_sweep); |
| 72 | |
| 73 | let bbox = rotated |
| 74 | .bounding_rect() |
| 75 | .ok_or("Failed to compute bounding rectangle")?; |
| 76 | |
| 77 | let step = config.tool_width * (1.0 - config.overlap_pct / 100.0); |
| 78 | if step <= 0.0 { |
| 79 | return Err("Effective step size must be positive".into()); |
| 80 | } |
| 81 | |
| 82 | let y_min = bbox.min().y; |
| 83 | let y_max = bbox.max().y; |
| 84 | let x_min = bbox.min().x; |
| 85 | let x_max = bbox.max().x; |
| 86 | |
| 87 | // Small margin to extend sweep lines past polygon bounds |
| 88 | let x_margin = (x_max - x_min) * 0.01; |
| 89 | |
| 90 | // Generate sweep lines and intersect with rotated polygon |
| 91 | let mut sweep_segments: Vec<Vec<(f64, f64)>> = Vec::new(); |
| 92 | let mut y = y_min + step / 2.0; |
| 93 | |
| 94 | while y <= y_max { |
| 95 | let sweep_line = Line::new( |
| 96 | Coord { |
| 97 | x: x_min - x_margin, |
| 98 | y, |
| 99 | }, |
| 100 | Coord { |
| 101 | x: x_max + x_margin, |