1use geometry_model::{
17 Box, Linestring, MultiLinestring, MultiPoint, MultiPolygon, Point, Polygon, Ring, Segment,
18};
19use geometry_trait::{Closure, Linestring as _, Polygon as _, Ring as _};
20
21#[inline]
26#[must_use]
27pub fn num_segments<G: NumSegments>(g: &G) -> usize {
28 g.num_segments()
29}
30
31#[doc(hidden)]
35pub trait NumSegments {
36 fn num_segments(&self) -> usize;
38}
39
40impl<T, const D: usize, Cs> NumSegments for Point<T, D, Cs>
41where
42 T: geometry_coords::CoordinateScalar,
43 Cs: geometry_cs::CoordinateSystem,
44{
45 fn num_segments(&self) -> usize {
46 0
47 }
48}
49
50impl<P: geometry_trait::Point> NumSegments for MultiPoint<P> {
51 fn num_segments(&self) -> usize {
52 0
53 }
54}
55
56impl<P: geometry_trait::Point> NumSegments for Segment<P> {
57 fn num_segments(&self) -> usize {
58 1
59 }
60}
61
62impl<P: geometry_trait::Point> NumSegments for Box<P> {
63 fn num_segments(&self) -> usize {
64 4
65 }
66}
67
68impl<P: geometry_trait::Point> NumSegments for Linestring<P> {
69 fn num_segments(&self) -> usize {
70 let n = self.points().count();
71 if n < 2 { 0 } else { n - 1 }
72 }
73}
74
75impl<P: geometry_trait::Point, const CW: bool, const CL: bool> NumSegments for Ring<P, CW, CL> {
76 fn num_segments(&self) -> usize {
77 let n = self.points().count();
78 if n <= 1 {
81 0
82 } else if matches!(self.closure(), Closure::Closed) {
83 n - 1
84 } else {
85 n
86 }
87 }
88}
89
90impl<P: geometry_trait::Point, const CW: bool, const CL: bool> NumSegments for Polygon<P, CW, CL> {
91 fn num_segments(&self) -> usize {
92 let mut n = NumSegments::num_segments(self.exterior());
93 for inner in self.interiors() {
94 n += NumSegments::num_segments(inner);
95 }
96 n
97 }
98}
99
100impl<L: geometry_trait::Linestring + NumSegments> NumSegments for MultiLinestring<L> {
101 fn num_segments(&self) -> usize {
102 self.0.iter().map(NumSegments::num_segments).sum()
103 }
104}
105
106impl<Pg: geometry_trait::Polygon + NumSegments> NumSegments for MultiPolygon<Pg> {
107 fn num_segments(&self) -> usize {
108 self.0.iter().map(NumSegments::num_segments).sum()
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::num_segments;
118 use geometry_cs::Cartesian;
119 use geometry_model::{
120 Box, Linestring, MultiPoint, Point2D, Polygon, Ring, Segment, linestring, polygon,
121 };
122
123 type Pt = Point2D<f64, Cartesian>;
124 type Ls = Linestring<Pt>;
125 type Poly = Polygon<Pt>;
126
127 #[test]
129 fn point_has_no_segments() {
130 assert_eq!(num_segments(&Pt::new(0.0, 0.0)), 0);
131 }
132
133 #[test]
135 fn multi_point_has_no_segments() {
136 let mp = MultiPoint(vec![Pt::new(0.0, 0.0), Pt::new(1.0, 1.0)]);
137 assert_eq!(num_segments(&mp), 0);
138 }
139
140 #[test]
142 fn segment_is_one() {
143 let s = Segment::new(Pt::new(0.0, 0.0), Pt::new(1.0, 1.0));
144 assert_eq!(num_segments(&s), 1);
145 }
146
147 #[test]
149 fn box_is_four() {
150 let b = Box::from_corners(Pt::new(0.0, 0.0), Pt::new(1.0, 1.0));
151 assert_eq!(num_segments(&b), 4);
152 }
153
154 #[test]
156 fn linestring_two_points_is_one_segment() {
157 let ls: Ls = linestring![(0.0, 0.0), (1.0, 1.0)];
158 assert_eq!(num_segments(&ls), 1);
159 }
160
161 #[test]
163 fn linestring_n_points_is_n_minus_one() {
164 let ls: Ls = linestring![(0.0, 0.0), (1.0, 1.0), (2.0, 2.0), (3.0, 3.0)];
165 assert_eq!(num_segments(&ls), 3);
166 }
167
168 #[test]
171 fn closed_ring_rectangle_has_4_edges() {
172 let mut r = Ring::<Pt>::new();
173 r.push(Pt::new(0.0, 0.0));
174 r.push(Pt::new(4.0, 0.0));
175 r.push(Pt::new(4.0, 3.0));
176 r.push(Pt::new(0.0, 3.0));
177 r.push(Pt::new(0.0, 0.0));
178 assert_eq!(num_segments(&r), 4);
179 }
180
181 #[test]
184 fn open_ring_rectangle_has_4_edges() {
185 let mut r = Ring::<Pt, true, false>::new();
186 r.push(Pt::new(0.0, 0.0));
187 r.push(Pt::new(4.0, 0.0));
188 r.push(Pt::new(4.0, 3.0));
189 r.push(Pt::new(0.0, 3.0));
190 assert_eq!(num_segments(&r), 4);
191 }
192
193 #[test]
197 fn polygon_with_one_hole_sums() {
198 let pg: Poly = polygon![
199 [(0.0, 0.0), (5.0, 0.0), (5.0, 5.0), (0.0, 5.0), (0.0, 0.0)],
200 [(1.0, 1.0), (2.0, 1.0), (1.0, 2.0), (1.0, 1.0)],
201 ];
202 assert_eq!(num_segments(&pg), 7);
203 }
204
205 #[test]
208 fn short_linestrings_have_no_segments() {
209 let empty: Ls = linestring![];
210 assert_eq!(num_segments(&empty), 0);
211 let single: Ls = linestring![(0.0, 0.0)];
212 assert_eq!(num_segments(&single), 0);
213 }
214
215 #[test]
217 fn multi_linestring_sums_members() {
218 let mls = geometry_model::MultiLinestring::<Ls>(vec![
219 linestring![(0.0, 0.0), (1.0, 1.0)], linestring![(2.0, 2.0), (3.0, 3.0), (4.0, 4.0), (5.0, 5.0)], ]);
222 assert_eq!(num_segments(&mls), 4);
223 }
224
225 #[test]
227 fn multi_polygon_sums_members() {
228 let plain: Poly = polygon![[(0.0, 0.0), (5.0, 0.0), (5.0, 5.0), (0.0, 5.0), (0.0, 0.0)]]; let holed: Poly = polygon![
230 [(0.0, 0.0), (5.0, 0.0), (5.0, 5.0), (0.0, 5.0), (0.0, 0.0)], [(1.0, 1.0), (2.0, 1.0), (1.0, 2.0), (1.0, 1.0)], ];
233 let mpg = geometry_model::MultiPolygon(vec![plain, holed]);
234 assert_eq!(num_segments(&mpg), 11);
235 }
236
237 #[test]
238 fn single_point_open_ring_has_no_segments() {
239 use geometry_model::Ring;
244 let open_one: Ring<Point2D<f64, Cartesian>, true, false> =
245 Ring::from_vec(vec![Point2D::new(0.0, 0.0)]);
246 assert_eq!(num_segments(&open_one), 0);
247 }
248}