geosets_rs/
geometric_operations.rs

1use ndarray::Array2;
2use thiserror::Error;
3
4#[derive(Debug, Error)]
5pub enum GeometryError {
6    #[error("Vertices must be a 2D array with shape (n, 2)")]
7    InvalidShape,
8}
9
10pub fn order_vertices_clockwise(vertices: Array2<f64>) -> Result<Array2<f64>, GeometryError> {
11    let n_vertices = vertices.nrows();
12
13    if vertices.dim().1 != 2 {
14        return Err(GeometryError::InvalidShape);
15    }
16
17    if n_vertices < 3 {
18        return Ok(vertices);
19    }
20
21    // Calculate centroid
22    let centroid_x = vertices.column(0).mean().unwrap();
23    let centroid_y = vertices.column(1).mean().unwrap();
24
25    // Create vector of (index, angle) pairs
26    let mut vertex_angles: Vec<(usize, f64)> = (0..n_vertices)
27        .map(|i| {
28            let x = vertices[[i, 0]] - centroid_x;
29            let y = vertices[[i, 1]] - centroid_y;
30            let angle = y.atan2(x);
31            (i, angle)
32        })
33        .collect();
34
35    // Sort by angle in descending order for clockwise ordering
36    vertex_angles.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
37
38    // Create new array with ordered vertices
39    let mut ordered_vertices = Array2::zeros((n_vertices, 2));
40    for (new_idx, (orig_idx, _)) in vertex_angles.iter().enumerate() {
41        ordered_vertices[[new_idx, 0]] = vertices[[*orig_idx, 0]];
42        ordered_vertices[[new_idx, 1]] = vertices[[*orig_idx, 1]];
43    }
44
45    Ok(ordered_vertices)
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51    use ndarray::array;
52
53    #[test]
54    fn test_order_vertices_clockwise() {
55        // Square vertices in random order
56        let vertices = array![
57            [1.0, 1.0], // top-right
58            [0.0, 0.0], // bottom-left
59            [1.0, 0.0], // bottom-right
60            [0.0, 1.0]  // top-left
61        ];
62
63        let ordered = order_vertices_clockwise(vertices).unwrap();
64
65        // Check that we have 4 vertices
66        assert_eq!(ordered.nrows(), 4);
67        assert_eq!(ordered.ncols(), 2);
68
69        // The exact order depends on the starting angle, but vertices should form a valid clockwise path
70        // We can verify by checking that consecutive vertices are connected properly
71        println!("Ordered vertices: {:?}", ordered);
72    }
73}