Skip to main content

geometry_algorithm/
for_each.rs

1//! Geometry visitors — `for_each_point` and `for_each_segment`.
2//!
3//! Mirror `boost::geometry::for_each_point` and
4//! `boost::geometry::for_each_segment` from
5//! `boost/geometry/algorithms/for_each.hpp`. Both pass each visited
6//! element to a user closure by `&` (read-only). Boost also exposes
7//! mutating variants; we ship the read-only forms — mutation is the
8//! KC1 `PointMut` concern, not the visitor's job.
9
10use geometry_model::{Linestring, MultiLinestring, MultiPoint, MultiPolygon, Point, Polygon, Ring};
11use geometry_trait::{
12    Closure, Linestring as LinestringTrait, MultiPoint as MultiPointTrait, Polygon as PolygonTrait,
13    Ring as RingTrait,
14};
15
16/// Apply `f` to every stored point of `g`, recursively.
17///
18/// Mirrors `boost::geometry::for_each_point(g, f)` from
19/// `boost/geometry/algorithms/for_each.hpp`. The closure is invoked
20/// once per *stored* point — a closed ring visits its closing vertex
21/// once (it appears once in storage).
22pub fn for_each_point<G, F>(g: &G, mut f: F)
23where
24    G: ForEachPoint,
25    F: FnMut(&G::Point),
26{
27    g.for_each_point(&mut f);
28}
29
30/// Per-kind point-visitor dispatch.
31#[doc(hidden)]
32pub trait ForEachPoint {
33    type Point: geometry_trait::Point;
34    fn for_each_point<F: FnMut(&Self::Point)>(&self, f: &mut F);
35}
36
37impl<T, const D: usize, Cs> ForEachPoint for Point<T, D, Cs>
38where
39    T: geometry_coords::CoordinateScalar,
40    Cs: geometry_cs::CoordinateSystem,
41{
42    type Point = Self;
43    fn for_each_point<F: FnMut(&Self::Point)>(&self, f: &mut F) {
44        f(self);
45    }
46}
47
48impl<P: geometry_trait::Point> ForEachPoint for Linestring<P> {
49    type Point = P;
50    fn for_each_point<F: FnMut(&P)>(&self, f: &mut F) {
51        for p in self.points() {
52            f(p);
53        }
54    }
55}
56
57impl<P: geometry_trait::Point, const CW: bool, const CL: bool> ForEachPoint for Ring<P, CW, CL> {
58    type Point = P;
59    fn for_each_point<F: FnMut(&P)>(&self, f: &mut F) {
60        for p in self.points() {
61            f(p);
62        }
63    }
64}
65
66impl<P: geometry_trait::Point, const CW: bool, const CL: bool> ForEachPoint for Polygon<P, CW, CL> {
67    type Point = P;
68    fn for_each_point<F: FnMut(&P)>(&self, f: &mut F) {
69        self.exterior().for_each_point(f);
70        for inner in self.interiors() {
71            inner.for_each_point(f);
72        }
73    }
74}
75
76impl<P: geometry_trait::Point> ForEachPoint for MultiPoint<P> {
77    type Point = P;
78    fn for_each_point<F: FnMut(&P)>(&self, f: &mut F) {
79        for p in self.points() {
80            f(p);
81        }
82    }
83}
84
85impl<L> ForEachPoint for MultiLinestring<L>
86where
87    L: LinestringTrait + ForEachPoint<Point = <L as geometry_trait::Geometry>::Point>,
88{
89    type Point = <L as geometry_trait::Geometry>::Point;
90    fn for_each_point<F: FnMut(&Self::Point)>(&self, f: &mut F) {
91        for l in &self.0 {
92            l.for_each_point(f);
93        }
94    }
95}
96
97impl<Pg> ForEachPoint for MultiPolygon<Pg>
98where
99    Pg: PolygonTrait + ForEachPoint<Point = <Pg as geometry_trait::Geometry>::Point>,
100{
101    type Point = <Pg as geometry_trait::Geometry>::Point;
102    fn for_each_point<F: FnMut(&Self::Point)>(&self, f: &mut F) {
103        for p in &self.0 {
104            p.for_each_point(f);
105        }
106    }
107}
108
109/// Apply `f` to every segment of `g` as a `(&start, &end)` pair,
110/// recursively.
111///
112/// Mirrors `boost::geometry::for_each_segment(g, f)` from
113/// `boost/geometry/algorithms/for_each.hpp`. A closed ring visits its
114/// closing edge via the repeated last-vertex already in storage; an
115/// open ring emits the implicit `(last, first)` closing edge.
116pub fn for_each_segment<G, F>(g: &G, mut f: F)
117where
118    G: ForEachSegment,
119    F: FnMut(&G::Point, &G::Point),
120{
121    g.for_each_segment(&mut f);
122}
123
124/// Per-kind segment-visitor dispatch.
125#[doc(hidden)]
126pub trait ForEachSegment {
127    type Point: geometry_trait::Point;
128    fn for_each_segment<F: FnMut(&Self::Point, &Self::Point)>(&self, f: &mut F);
129}
130
131impl<P: geometry_trait::Point> ForEachSegment for Linestring<P> {
132    type Point = P;
133    fn for_each_segment<F: FnMut(&P, &P)>(&self, f: &mut F) {
134        let pts: alloc::vec::Vec<&P> = self.points().collect();
135        for w in pts.windows(2) {
136            f(w[0], w[1]);
137        }
138    }
139}
140
141impl<P: geometry_trait::Point, const CW: bool, const CL: bool> ForEachSegment for Ring<P, CW, CL> {
142    type Point = P;
143    fn for_each_segment<F: FnMut(&P, &P)>(&self, f: &mut F) {
144        let pts: alloc::vec::Vec<&P> = self.points().collect();
145        if pts.len() < 2 {
146            return;
147        }
148        for w in pts.windows(2) {
149            f(w[0], w[1]);
150        }
151        // Open ring: add the implicit closing edge. A closed ring
152        // already stores its closing vertex, so the windowed walk
153        // emitted the closing edge.
154        if matches!(self.closure(), Closure::Open) {
155            f(pts[pts.len() - 1], pts[0]);
156        }
157    }
158}
159
160impl<P: geometry_trait::Point, const CW: bool, const CL: bool> ForEachSegment
161    for Polygon<P, CW, CL>
162{
163    type Point = P;
164    fn for_each_segment<F: FnMut(&P, &P)>(&self, f: &mut F) {
165        self.exterior().for_each_segment(f);
166        for inner in self.interiors() {
167            inner.for_each_segment(f);
168        }
169    }
170}
171
172impl<L> ForEachSegment for MultiLinestring<L>
173where
174    L: LinestringTrait + ForEachSegment<Point = <L as geometry_trait::Geometry>::Point>,
175{
176    type Point = <L as geometry_trait::Geometry>::Point;
177    fn for_each_segment<F: FnMut(&Self::Point, &Self::Point)>(&self, f: &mut F) {
178        for l in &self.0 {
179            l.for_each_segment(f);
180        }
181    }
182}
183
184impl<Pg> ForEachSegment for MultiPolygon<Pg>
185where
186    Pg: PolygonTrait + ForEachSegment<Point = <Pg as geometry_trait::Geometry>::Point>,
187{
188    type Point = <Pg as geometry_trait::Geometry>::Point;
189    fn for_each_segment<F: FnMut(&Self::Point, &Self::Point)>(&self, f: &mut F) {
190        for p in &self.0 {
191            p.for_each_segment(f);
192        }
193    }
194}
195
196#[cfg(test)]
197#[allow(clippy::float_cmp, reason = "Visited coordinates are exact literals.")]
198mod tests {
199    //! Reference from `boost/geometry/test/algorithms/for_each.cpp`.
200
201    use super::{for_each_point, for_each_segment};
202    use geometry_cs::Cartesian;
203    use geometry_model::{Linestring, MultiPoint, Point2D, Polygon, linestring, polygon};
204    use geometry_trait::Point as _;
205
206    type Pt = Point2D<f64, Cartesian>;
207
208    #[test]
209    fn point_visits_once() {
210        let p = Pt::new(3.0, 4.0);
211        let mut sum = 0.0;
212        for_each_point(&p, |q| sum += q.get::<0>() + q.get::<1>());
213        assert_eq!(sum, 7.0);
214    }
215
216    #[test]
217    fn linestring_visits_in_order() {
218        let ls: Linestring<Pt> = linestring![(0.0, 0.0), (3.0, 4.0), (4.0, 3.0)];
219        let mut xs = alloc::vec::Vec::new();
220        for_each_point(&ls, |p| xs.push(p.get::<0>()));
221        assert_eq!(xs, vec![0.0, 3.0, 4.0]);
222    }
223
224    #[test]
225    fn polygon_visits_outer_then_inner() {
226        let pg: Polygon<Pt> = polygon![
227            [(0.0, 0.0), (5.0, 0.0), (5.0, 5.0), (0.0, 5.0), (0.0, 0.0)],
228            [(1.0, 1.0), (2.0, 1.0), (2.0, 2.0), (1.0, 1.0)]
229        ];
230        let mut count = 0;
231        for_each_point(&pg, |_| count += 1);
232        assert_eq!(count, 5 + 4); // outer + inner stored points
233    }
234
235    #[test]
236    fn multipoint_visits_each() {
237        let mp = MultiPoint(vec![Pt::new(0.0, 0.0), Pt::new(1.0, 1.0)]);
238        let mut count = 0;
239        for_each_point(&mp, |_| count += 1);
240        assert_eq!(count, 2);
241    }
242
243    #[test]
244    fn linestring_segments_are_consecutive_pairs() {
245        let ls: Linestring<Pt> = linestring![(0.0, 0.0), (3.0, 0.0), (3.0, 4.0)];
246        let mut edges = alloc::vec::Vec::new();
247        for_each_segment(&ls, |a, b| edges.push((a.get::<0>(), b.get::<0>())));
248        assert_eq!(edges, vec![(0.0, 3.0), (3.0, 3.0)]);
249    }
250
251    #[test]
252    fn closed_ring_segment_count_equals_edges() {
253        // A closed 5-point square ring stores its closing vertex, so a
254        // windowed walk emits 4 edges.
255        let pg: Polygon<Pt> =
256            polygon![[(0.0, 0.0), (2.0, 0.0), (2.0, 2.0), (0.0, 2.0), (0.0, 0.0)]];
257        let mut count = 0;
258        for_each_segment(&pg, |_, _| count += 1);
259        assert_eq!(count, 4);
260    }
261
262    use geometry_model::{MultiLinestring, MultiPolygon, Ring};
263
264    /// A `MultiLinestring` visits every point of every member in order.
265    #[test]
266    fn multilinestring_visits_all_member_points() {
267        let mls: MultiLinestring<Linestring<Pt>> = MultiLinestring(vec![
268            linestring![(0.0, 0.0), (1.0, 1.0)],
269            linestring![(2.0, 2.0), (3.0, 3.0), (4.0, 4.0)],
270        ]);
271        let mut xs = alloc::vec::Vec::new();
272        for_each_point(&mls, |p| xs.push(p.get::<0>()));
273        assert_eq!(xs, vec![0.0, 1.0, 2.0, 3.0, 4.0]);
274    }
275
276    /// A `MultiPolygon` visits every stored point across its members.
277    #[test]
278    fn multipolygon_visits_all_member_points() {
279        let member: Polygon<Pt> = polygon![[(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]];
280        let mpg: MultiPolygon<Polygon<Pt>> = MultiPolygon(vec![member.clone(), member]);
281        let mut count = 0;
282        for_each_point(&mpg, |_| count += 1);
283        assert_eq!(count, 8); // two members × 4 stored points
284    }
285
286    /// An *open* ring emits an extra implicit `(last, first)` closing
287    /// edge that a closed ring does not.
288    #[test]
289    fn open_ring_emits_implicit_closing_edge() {
290        // 4 distinct vertices, open (CL = false): 3 windowed edges + the
291        // implicit closing edge = 4.
292        let ring: Ring<Pt, true, false> = Ring::from_vec(vec![
293            Pt::new(0.0, 0.0),
294            Pt::new(2.0, 0.0),
295            Pt::new(2.0, 2.0),
296            Pt::new(0.0, 2.0),
297        ]);
298        let mut edges = alloc::vec::Vec::new();
299        for_each_segment(&ring, |a, b| {
300            edges.push(((a.get::<0>(), a.get::<1>()), (b.get::<0>(), b.get::<1>())));
301        });
302        assert_eq!(edges.len(), 4);
303        // The last edge closes the ring: (0,2) -> (0,0).
304        assert_eq!(*edges.last().unwrap(), ((0.0, 2.0), (0.0, 0.0)));
305    }
306
307    /// A ring with fewer than two vertices emits no segments (the length
308    /// guard).
309    #[test]
310    fn degenerate_ring_emits_no_segments() {
311        let ring: Ring<Pt, true, false> = Ring::from_vec(vec![Pt::new(1.0, 1.0)]);
312        let mut count = 0;
313        for_each_segment(&ring, |_, _| count += 1);
314        assert_eq!(count, 0);
315    }
316
317    /// A `MultiLinestring` visits the segments of every member.
318    #[test]
319    fn multilinestring_visits_all_member_segments() {
320        let mls: MultiLinestring<Linestring<Pt>> = MultiLinestring(vec![
321            linestring![(0.0, 0.0), (1.0, 1.0)],             // 1 edge
322            linestring![(2.0, 2.0), (3.0, 3.0), (4.0, 4.0)], // 2 edges
323        ]);
324        let mut count = 0;
325        for_each_segment(&mls, |_, _| count += 1);
326        assert_eq!(count, 3);
327    }
328
329    /// A `MultiPolygon` visits the segments of every member polygon.
330    #[test]
331    fn multipolygon_visits_all_member_segments() {
332        let member: Polygon<Pt> =
333            polygon![[(0.0, 0.0), (2.0, 0.0), (2.0, 2.0), (0.0, 2.0), (0.0, 0.0)]]; // 4 edges
334        let mpg: MultiPolygon<Polygon<Pt>> = MultiPolygon(vec![member.clone(), member]);
335        let mut count = 0;
336        for_each_segment(&mpg, |_, _| count += 1);
337        assert_eq!(count, 8); // two members × 4 edges
338    }
339}