1use crate::cartesian::{CartesianPoint2d, Rect};
24use crate::geo::Projection;
25use crate::geometry::{CartesianGeometry2dSpecialization, Geom, Geometry, GeometrySpecialization};
26use crate::geometry_type::{CartesianSpace2d, ContourGeometryType, GeometryType};
27use crate::segment::Segment;
28
29pub trait Contour {
31 type Point;
33
34 fn is_closed(&self) -> bool;
38
39 fn iter_points(&self) -> impl Iterator<Item = Self::Point>;
45
46 fn iter_points_closing(&self) -> impl Iterator<Item = Self::Point>
48 where
49 Self::Point: Copy,
50 {
51 Box::new(ContourPointsIterator::new(
52 self.iter_points(),
53 self.is_closed(),
54 ))
55 }
56
57 fn iter_segments(&self) -> impl Iterator<Item = Segment<Self::Point>>
60 where
61 Self::Point: Copy,
62 {
63 ContourSegmentIterator::new(ContourPointsIterator::new(
64 self.iter_points(),
65 self.is_closed(),
66 ))
67 }
68
69 fn project_points<Proj>(
71 &self,
72 projection: &Proj,
73 ) -> Option<crate::impls::Contour<Proj::OutPoint>>
74 where
75 Proj: Projection<InPoint = Self::Point> + ?Sized,
76 {
77 Some(crate::impls::Contour::new(
78 self.iter_points()
79 .map(|p| projection.project(&p))
80 .collect::<Option<Vec<Proj::OutPoint>>>()?,
81 self.is_closed(),
82 ))
83 }
84}
85
86pub trait ClosedContour {
88 type Point;
90
91 fn iter_points(&self) -> impl Iterator<Item = Self::Point>;
97}
98
99impl<P, T: ClosedContour<Point = P>> Contour for T {
100 type Point = P;
101
102 fn is_closed(&self) -> bool {
103 true
104 }
105
106 fn iter_points(&self) -> impl Iterator<Item = Self::Point> {
107 self.iter_points()
108 }
109}
110
111#[derive(Debug, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Hash)]
113pub struct ContourPointsIterator<P, Iter>
114where
115 Iter: Iterator<Item = P>,
116{
117 points_iter: Iter,
118 is_closed: bool,
119 first_point: Option<P>,
120}
121
122impl<P, Iter> ContourPointsIterator<P, Iter>
123where
124 Iter: Iterator<Item = P>,
125{
126 fn new(points_iter: Iter, is_closed: bool) -> Self {
127 Self {
128 points_iter,
129 is_closed,
130 first_point: None,
131 }
132 }
133}
134
135impl<P, Iter> Iterator for ContourPointsIterator<P, Iter>
136where
137 P: Copy,
138 Iter: Iterator<Item = P>,
139{
140 type Item = P;
141
142 fn next(&mut self) -> Option<Self::Item> {
143 let next = self.points_iter.next();
144 if self.is_closed && self.first_point.is_none() {
145 self.first_point = next;
146 }
147
148 if next.is_none() {
149 self.first_point.take()
150 } else {
151 next
152 }
153 }
154}
155
156#[derive(Debug, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Hash)]
158pub struct ContourSegmentIterator<P, Iter>
159where
160 Iter: Iterator<Item = P>,
161{
162 points_iter: ContourPointsIterator<P, Iter>,
163 prev_point: Option<P>,
164}
165
166impl<P, Iter> ContourSegmentIterator<P, Iter>
167where
168 Iter: Iterator<Item = P>,
169{
170 fn new(points_iter: ContourPointsIterator<P, Iter>) -> Self {
171 Self {
172 points_iter,
173 prev_point: None,
174 }
175 }
176}
177
178impl<P, Iter> Iterator for ContourSegmentIterator<P, Iter>
179where
180 P: Copy,
181 Iter: Iterator<Item = P>,
182{
183 type Item = Segment<P>;
184
185 fn next(&mut self) -> Option<Self::Item> {
186 let next_point = self.points_iter.next()?;
187 let prev_point = self.prev_point.replace(next_point);
188
189 match prev_point {
190 Some(prev) => Some(Segment(prev, next_point)),
191 None => self.next(),
192 }
193 }
194}
195
196impl<C, Space> GeometrySpecialization<ContourGeometryType, Space> for C
197where
198 C: Contour + GeometryType<Type = ContourGeometryType, Space = Space>,
199{
200 type Point = C::Point;
201
202 fn project_spec<Proj>(&self, projection: &Proj) -> Option<Geom<Proj::OutPoint>>
203 where
204 Proj: Projection<InPoint = Self::Point> + ?Sized,
205 {
206 let points = self
207 .iter_points()
208 .map(|p| projection.project(&p))
209 .collect::<Option<Vec<Proj::OutPoint>>>()?;
210 Some(Geom::Contour(crate::impls::Contour::new(
211 points,
212 self.is_closed(),
213 )))
214 }
215}
216
217impl<P, C> CartesianGeometry2dSpecialization<P, ContourGeometryType> for C
218where
219 P: CartesianPoint2d + Copy,
220 C: Contour<Point = P>
221 + GeometryType<Type = ContourGeometryType, Space = CartesianSpace2d>
222 + Geometry<Point = P>,
223{
224 fn is_point_inside_spec<Other: CartesianPoint2d<Num = P::Num>>(
225 &self,
226 point: &Other,
227 tolerance: P::Num,
228 ) -> bool {
229 self.iter_segments()
230 .any(|segment| segment.distance_to_point_sq(point) <= tolerance * tolerance)
231 }
232
233 fn bounding_rectangle_spec(&self) -> Option<Rect<P::Num>> {
234 Rect::from_points(self.iter_points())
235 }
236}