Skip to main content

physdes/
algorithms.rs

1//! Additional geometric algorithms
2//!
3//! This module provides additional geometric algorithms beyond the basic operations.
4
5use crate::Polygon;
6
7/// Checks if a point is inside a polygon using the ray casting algorithm
8///
9/// # Arguments
10///
11/// * `point` - The point to check
12/// * `polygon` - The polygon to test against
13///
14/// # Returns
15///
16/// `true` if the point is inside the polygon, `false` otherwise
17///
18/// # Examples
19///
20/// ```
21/// use physdes::{Point, algorithms::point_in_polygon};
22/// use physdes::Polygon;
23///
24/// let points = vec![
25///     Point::new(0, 0),
26///     Point::new(10, 0),
27///     Point::new(10, 10),
28///     Point::new(0, 10),
29/// ];
30/// let polygon = Polygon::new(&points);
31///
32/// assert!(point_in_polygon(&Point::new(5, 5), &polygon));
33/// assert!(!point_in_polygon(&Point::new(15, 15), &polygon));
34/// ```
35pub fn point_in_polygon<T>(point: &crate::Point<T, T>, polygon: &Polygon<T>) -> bool
36where
37    T: Clone
38        + Copy
39        + PartialOrd
40        + std::ops::Sub<Output = T>
41        + std::ops::Mul<Output = T>
42        + std::ops::Div<Output = T>
43        + std::ops::Add<Output = T>
44        + num_traits::Zero
45        + num_traits::Num
46        + std::ops::AddAssign
47        + Ord,
48{
49    let vertices = polygon.get_vertices();
50    let n = vertices.len();
51
52    if n < 3 {
53        return false;
54    }
55
56    let mut inside = false;
57    let mut j = n - 1;
58
59    for i in 0..n {
60        let xi = vertices[i].xcoord;
61        let yi = vertices[i].ycoord;
62        let xj = vertices[j].xcoord;
63        let yj = vertices[j].ycoord;
64
65        let intersect = ((yi > point.ycoord) != (yj > point.ycoord))
66            && (point.xcoord < (xj - xi) * (point.ycoord - yi) / (yj - yi) + xi);
67
68        if intersect {
69            inside = !inside;
70        }
71
72        j = i;
73    }
74
75    inside
76}
77
78/// Computes the centroid (geometric center) of a polygon
79///
80/// # Arguments
81///
82/// * `polygon` - The polygon
83///
84/// # Returns
85///
86/// The centroid point of the polygon
87///
88/// # Examples
89///
90/// ```
91/// use physdes::{Point, algorithms::polygon_centroid};
92/// use physdes::Polygon;
93///
94/// let points = vec![
95///     Point::new(0, 0),
96///     Point::new(4, 0),
97///     Point::new(4, 3),
98/// ];
99/// let polygon = Polygon::new(&points);
100///
101/// let centroid = polygon_centroid(&polygon);
102/// // For a triangle, centroid is at the average of vertices
103/// ```
104pub fn polygon_centroid<T>(polygon: &Polygon<T>) -> crate::Point<T, T>
105where
106    T: Clone
107        + Copy
108        + std::ops::Add<Output = T>
109        + std::ops::Div<Output = T>
110        + From<i32>
111        + num_traits::Zero
112        + num_traits::Num
113        + std::ops::AddAssign
114        + Ord,
115{
116    let vertices = polygon.get_vertices();
117    let n = vertices.len();
118
119    if n == 0 {
120        return crate::Point::new(T::from(0), T::from(0));
121    }
122
123    let mut sum_x = T::from(0);
124    let mut sum_y = T::from(0);
125
126    for vertex in &vertices {
127        sum_x += vertex.xcoord;
128        sum_y += vertex.ycoord;
129    }
130
131    let n_t = T::from(n as i32);
132    crate::Point::new(sum_x / n_t, sum_y / n_t)
133}
134
135/// Computes the perimeter of a polygon
136///
137/// # Arguments
138///
139/// * `polygon` - The polygon
140///
141/// # Returns
142///
143/// The perimeter length
144///
145/// # Examples
146///
147/// ```
148/// use physdes::{Point, algorithms::polygon_perimeter};
149/// use physdes::Polygon;
150///
151/// let points = vec![
152///     Point::new(0, 0),
153///     Point::new(3, 0),
154///     Point::new(3, 4),
155///     Point::new(0, 4),
156/// ];
157/// let polygon = Polygon::new(&points);
158///
159/// let perimeter = polygon_perimeter(&polygon);
160/// assert_eq!(perimeter, 14); // 3 + 4 + 3 + 4
161/// ```
162pub fn polygon_perimeter<T>(polygon: &Polygon<T>) -> T
163where
164    T: Clone
165        + Copy
166        + std::ops::Add<Output = T>
167        + std::ops::Sub<Output = T>
168        + std::ops::Mul<Output = T>
169        + num_traits::Zero
170        + num_traits::Num
171        + std::ops::AddAssign
172        + Ord,
173{
174    let vertices = polygon.get_vertices();
175    let n = vertices.len();
176
177    if n < 2 {
178        return T::zero();
179    }
180
181    let mut perimeter = T::zero();
182
183    for i in 0..n {
184        let current = vertices[i];
185        let next = vertices[(i + 1) % n];
186
187        let dx = if current.xcoord > next.xcoord {
188            current.xcoord - next.xcoord
189        } else {
190            next.xcoord - current.xcoord
191        };
192
193        let dy = if current.ycoord > next.ycoord {
194            current.ycoord - next.ycoord
195        } else {
196            next.ycoord - current.ycoord
197        };
198
199        perimeter = perimeter + dx + dy;
200    }
201
202    perimeter
203}
204
205#[cfg(test)]
206mod tests {
207    use super::*;
208
209    #[test]
210    fn test_point_in_polygon() {
211        let points = vec![
212            crate::Point::new(0, 0),
213            crate::Point::new(10, 0),
214            crate::Point::new(10, 10),
215            crate::Point::new(0, 10),
216        ];
217        let polygon = Polygon::new(&points);
218
219        assert!(point_in_polygon(&crate::Point::new(5, 5), &polygon));
220        assert!(!point_in_polygon(&crate::Point::new(15, 15), &polygon));
221        assert!(!point_in_polygon(&crate::Point::new(-5, 5), &polygon));
222    }
223
224    #[test]
225    fn test_polygon_centroid() {
226        let points = vec![
227            crate::Point::new(0, 0),
228            crate::Point::new(3, 0),
229            crate::Point::new(3, 3),
230        ];
231        let polygon = Polygon::new(&points);
232
233        let centroid = polygon_centroid(&polygon);
234        assert_eq!(centroid.xcoord, 2);
235        assert_eq!(centroid.ycoord, 1);
236    }
237
238    #[test]
239    fn test_polygon_perimeter() {
240        let points = vec![
241            crate::Point::new(0, 0),
242            crate::Point::new(3, 0),
243            crate::Point::new(3, 4),
244            crate::Point::new(0, 4),
245        ];
246        let polygon = Polygon::new(&points);
247
248        let perimeter = polygon_perimeter(&polygon);
249        assert_eq!(perimeter, 14);
250    }
251}