galileo_types/cartesian/traits/
contour.rs1use std::cmp::Ordering;
2use std::fmt::Debug;
3
4use num_traits::{One, Zero};
5use serde::{Deserialize, Serialize};
6
7use crate::cartesian::traits::cartesian_point::CartesianPoint2d;
8use crate::contour::{ClosedContour, Contour};
9
10pub trait CartesianClosedContour {
13 type Point: CartesianPoint2d;
15
16 fn area_signed(&self) -> <Self::Point as CartesianPoint2d>::Num
18 where
19 Self: Sized;
20
21 fn winding(&self) -> Winding
23 where
24 Self: Sized;
25}
26
27impl<P, T> CartesianClosedContour for T
28where
29 P: CartesianPoint2d + Copy,
30 T: ClosedContour<Point = P>,
31{
32 type Point = P;
33
34 fn area_signed(&self) -> P::Num
35 where
36 Self: Sized,
37 {
38 let mut prev;
39 let mut iter = self.iter_points_closing();
40 if let Some(p) = iter.next() {
41 prev = p;
42 } else {
43 return P::Num::zero();
44 }
45
46 let mut aggr = P::Num::zero();
47
48 for p in iter {
49 aggr = aggr + prev.x() * p.y() - p.x() * prev.y();
50 prev = p;
51 }
52
53 aggr / (P::Num::one() + P::Num::one())
54 }
55
56 fn winding(&self) -> Winding
57 where
58 Self: Sized,
59 {
60 if self.area_signed() <= P::Num::zero() {
61 Winding::Clockwise
62 } else {
63 Winding::CounterClockwise
64 }
65 }
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Hash, Deserialize, Serialize)]
70pub enum Winding {
71 Clockwise,
73 CounterClockwise,
75}
76
77pub trait CartesianContour<P: CartesianPoint2d + Copy>: Contour<Point = P> {
79 fn distance_to_point_sq<Point>(&self, point: &Point) -> Option<P::Num>
81 where
82 Self: Sized,
83 Point: CartesianPoint2d<Num = P::Num>,
84 {
85 self.iter_segments()
86 .map(|v| v.distance_to_point_sq(point))
87 .min_by(move |a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal))
88 }
89}
90
91impl<T: Contour<Point = P>, P: CartesianPoint2d + Copy> CartesianContour<P> for T {}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use crate::cartesian::impls::Point2;
97 use crate::contour::Contour;
98 use crate::impls::ClosedContour;
99 use crate::segment::Segment;
100
101 #[test]
102 fn iter_points_closing() {
103 let contour =
104 crate::impls::Contour::open(vec![Point2::new(0.0, 0.0), Point2::new(1.0, 1.0)]);
105 assert_eq!(contour.iter_points_closing().count(), 2);
106 assert_eq!(
107 contour.iter_points_closing().last().unwrap(),
108 Point2::new(1.0, 1.0)
109 );
110
111 let contour = ClosedContour {
112 points: vec![Point2::new(0.0, 0.0), Point2::new(1.0, 1.0)],
113 };
114 assert_eq!(contour.iter_points_closing().count(), 3);
115 assert_eq!(
116 contour.iter_points_closing().last().unwrap(),
117 Point2::new(0.0, 0.0)
118 );
119 }
120
121 #[test]
122 fn iter_segments() {
123 let contour = crate::impls::Contour::open(vec![Point2::new(0.0, 0.0)]);
124 assert_eq!(contour.iter_segments().count(), 0);
125
126 let contour =
127 crate::impls::Contour::open(vec![Point2::new(0.0, 0.0), Point2::new(1.0, 1.0)]);
128 assert_eq!(contour.iter_segments().count(), 1);
129 assert_eq!(
130 contour.iter_segments().last().unwrap(),
131 Segment(Point2::new(0.0, 0.0), Point2::new(1.0, 1.0))
132 );
133
134 let contour = ClosedContour {
135 points: vec![Point2::new(0.0, 0.0), Point2::new(1.0, 1.0)],
136 };
137 assert_eq!(contour.iter_segments().count(), 2);
138 assert_eq!(
139 contour.iter_segments().last().unwrap(),
140 Segment(Point2::new(1.0, 1.0), Point2::new(0.0, 0.0))
141 );
142 }
143
144 #[test]
145 fn distance_to_point() {
146 let contour = ClosedContour {
147 points: vec![
148 Point2::new(0.0, 0.0),
149 Point2::new(1.0, 1.0),
150 Point2::new(1.0, 0.0),
151 ],
152 };
153
154 assert_eq!(
155 contour.distance_to_point_sq(&Point2::new(0.0, 0.0)),
156 Some(0.0)
157 );
158 assert_eq!(
159 contour.distance_to_point_sq(&Point2::new(0.5, 0.0)),
160 Some(0.0)
161 );
162 assert_eq!(
163 contour.distance_to_point_sq(&Point2::new(0.5, 0.5)),
164 Some(0.0)
165 );
166 assert_eq!(
167 contour.distance_to_point_sq(&Point2::new(0.0, 1.0)),
168 Some(0.5)
169 );
170 assert_eq!(
171 contour.distance_to_point_sq(&Point2::new(2.0, 2.0)),
172 Some(2.0)
173 );
174 assert_eq!(
175 contour.distance_to_point_sq(&Point2::new(-2.0, -2.0)),
176 Some(8.0)
177 );
178 }
179
180 #[test]
181 fn area() {
182 let contour = ClosedContour::new(vec![
183 Point2::new(0.0, 0.0),
184 Point2::new(0.0, 1.0),
185 Point2::new(1.0, 0.0),
186 ]);
187
188 assert_eq!(contour.area_signed(), -0.5);
189
190 let contour = ClosedContour::new(vec![
191 Point2::new(0.0, 0.0),
192 Point2::new(1.0, 0.0),
193 Point2::new(0.0, 1.0),
194 ]);
195
196 assert_eq!(contour.area_signed(), 0.5);
197 }
198
199 #[test]
200 fn winding() {
201 let contour = ClosedContour::new(vec![
202 Point2::new(0.0, 0.0),
203 Point2::new(0.0, 1.0),
204 Point2::new(1.0, 0.0),
205 ]);
206
207 assert_eq!(contour.winding(), Winding::Clockwise);
208
209 let contour = ClosedContour::new(vec![
210 Point2::new(0.0, 0.0),
211 Point2::new(1.0, 0.0),
212 Point2::new(0.0, 1.0),
213 ]);
214
215 assert_eq!(contour.winding(), Winding::CounterClockwise);
216 }
217}