geometry_algorithm/
reverse.rs1use geometry_model::{Linestring, MultiLinestring, MultiPolygon, Polygon, Ring};
21use geometry_trait::{Linestring as LinestringTrait, Polygon as PolygonTrait};
22
23pub fn reverse<G: Reverse>(g: &mut G) {
28 g.reverse();
29}
30
31#[doc(hidden)]
35pub trait Reverse {
36 fn reverse(&mut self);
37}
38
39impl<P: geometry_trait::Point> Reverse for Linestring<P> {
40 fn reverse(&mut self) {
41 self.0.reverse();
42 }
43}
44
45impl<P: geometry_trait::Point, const CW: bool, const CL: bool> Reverse for Ring<P, CW, CL> {
46 fn reverse(&mut self) {
47 self.0.reverse();
48 }
49}
50
51impl<P: geometry_trait::Point, const CW: bool, const CL: bool> Reverse for Polygon<P, CW, CL> {
52 fn reverse(&mut self) {
53 self.outer.reverse();
54 for inner in &mut self.inners {
55 inner.reverse();
56 }
57 }
58}
59
60impl<L: Reverse + LinestringTrait> Reverse for MultiLinestring<L> {
61 fn reverse(&mut self) {
62 for l in &mut self.0 {
63 l.reverse();
64 }
65 }
66}
67
68impl<Pg: Reverse + PolygonTrait> Reverse for MultiPolygon<Pg> {
69 fn reverse(&mut self) {
70 for p in &mut self.0 {
71 p.reverse();
72 }
73 }
74}
75
76#[cfg(test)]
77#[allow(clippy::float_cmp, reason = "Reversed coordinates are exact literals.")]
78mod tests {
79 use super::reverse;
85 use geometry_cs::Cartesian;
86 use geometry_model::{Point2D, linestring, polygon};
87 use geometry_trait::{Linestring as _, Point as _, Polygon as _, Ring as _};
88
89 type P = Point2D<f64, Cartesian>;
90
91 #[test]
92 fn reverse_linestring_flips_order() {
93 let mut ls: geometry_model::Linestring<P> = linestring![(0.0, 0.0), (1.0, 1.0), (2.0, 2.0)];
94 reverse(&mut ls);
95 let xs: Vec<f64> = ls.points().map(geometry_trait::Point::get::<0>).collect();
96 assert_eq!(xs, vec![2.0, 1.0, 0.0]);
97 }
98
99 #[test]
100 fn reverse_polygon_flips_outer_and_inner() {
101 let mut pg: geometry_model::Polygon<P> = polygon![
102 [(0.0, 0.0), (0.0, 4.0), (4.0, 4.0), (4.0, 0.0), (0.0, 0.0)],
103 [(1.0, 1.0), (1.0, 2.0), (2.0, 2.0), (2.0, 1.0), (1.0, 1.0)]
104 ];
105 reverse(&mut pg);
106 let outer_first_x = pg.exterior().points().next().unwrap().get::<0>();
107 let outer_second_x = pg.exterior().points().nth(1).unwrap().get::<0>();
111 assert_eq!(outer_first_x, 0.0);
112 assert_eq!(outer_second_x, 4.0); assert_eq!(pg.interiors().count(), 1);
114 }
115
116 #[test]
119 fn reverse_multi_linestring_flips_each_member_in_place() {
120 let mut mls: geometry_model::MultiLinestring<geometry_model::Linestring<P>> =
121 geometry_model::MultiLinestring(alloc::vec![
122 linestring![(0.0, 0.0), (1.0, 0.0)],
123 linestring![(5.0, 0.0), (6.0, 0.0), (7.0, 0.0)],
124 ]);
125 reverse(&mut mls);
126 let member_xs: Vec<Vec<f64>> = mls
127 .0
128 .iter()
129 .map(|l| l.points().map(geometry_trait::Point::get::<0>).collect())
130 .collect();
131 assert_eq!(member_xs, vec![vec![1.0, 0.0], vec![7.0, 6.0, 5.0]]);
133 }
134
135 #[test]
138 fn reverse_multi_polygon_flips_each_member_in_place() {
139 let a: geometry_model::Polygon<P> =
140 polygon![[(0.0, 0.0), (0.0, 4.0), (4.0, 4.0), (0.0, 0.0)]];
141 let b: geometry_model::Polygon<P> =
142 polygon![[(10.0, 0.0), (10.0, 4.0), (14.0, 4.0), (10.0, 0.0)]];
143 let mut mpg: geometry_model::MultiPolygon<geometry_model::Polygon<P>> =
144 geometry_model::MultiPolygon(alloc::vec![a, b]);
145 reverse(&mut mpg);
146 let firsts: Vec<f64> = mpg
148 .0
149 .iter()
150 .map(|pg| pg.exterior().points().next().unwrap().get::<0>())
151 .collect();
152 assert_eq!(firsts, vec![0.0, 10.0]);
153 let seconds: Vec<f64> = mpg
155 .0
156 .iter()
157 .map(|pg| pg.exterior().points().nth(1).unwrap().get::<0>())
158 .collect();
159 assert_eq!(seconds, vec![4.0, 14.0]);
160 }
161}