1use super::*;
2use crate::GeoFloat;
3use std::{cmp::Ordering, fmt::Debug};
4
5#[derive(Clone)]
7pub(super) struct Segment<C: Cross> {
8 pub(super) geom: LineOrPoint<C::Scalar>,
9 pub(super) cross: C,
10 pub(super) first_segment: bool,
11 pub(super) left_event_done: bool,
12 pub(super) overlapping: Option<IMSegment<C>>,
13 pub(super) is_overlapping: bool,
14}
15
16impl<C: Cross> Segment<C> {
17 pub fn new(cross: C, geom: Option<LineOrPoint<C::Scalar>>) -> Self {
18 let first_segment = geom.is_none();
19 let geom = geom.unwrap_or_else(|| cross.line());
20 Self {
21 geom,
22 cross,
23 first_segment,
24 left_event_done: false,
25 overlapping: None,
26 is_overlapping: false,
27 }
28 }
29
30 pub fn adjust_for_intersection(
37 &mut self,
38 intersection: LineOrPoint<C::Scalar>,
39 ) -> SplitSegments<C::Scalar> {
40 use SplitSegments::*;
41
42 debug_assert!(self.geom.is_line());
44 let (p, q) = self.geom.end_points();
45
46 if !intersection.is_line() {
47 let r = intersection.left();
49 debug_assert!(
50 p <= r,
51 "intersection point was not ordered within the line: {p:?} <= {r:?} <=> {q:?}",
52 );
53 if p == r || q == r {
54 Unchanged { overlap: false }
57 } else {
58 self.geom = (p, r).into();
61 SplitOnce {
63 overlap: None,
64 right: (r, q).into(),
65 }
66 }
67 } else {
68 let (r1, r2) = intersection.end_points();
69 debug_assert!(
70 p <= r1 && r2 <= q,
71 "overlapping segment was not ordered within the line!"
72 );
73 if p == r1 {
74 if r2 == q {
75 Unchanged { overlap: true }
77 } else {
78 self.geom = (p, r2).into();
79 SplitOnce {
81 overlap: Some(false),
82 right: (r2, q).into(),
83 }
84 }
85 } else if r2 == q {
86 self.geom = (p, r1).into();
87 SplitOnce {
89 overlap: Some(true),
90 right: (r1, q).into(),
91 }
92 } else {
93 self.geom = (p, r1).into();
94 SplitTwice {
96 right: (r2, q).into(),
97 }
98 }
99 }
100 }
101}
102
103impl<C: Cross> Debug for Segment<C> {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 write!(
107 f,
108 "Segment{{ {geom:?}\n\tof {c:?}\n\t{first} [{has}/{ovl}] }}",
109 c = self.cross,
110 geom = self.geom,
111 first = if self.first_segment { "[1st]" } else { "" },
112 has = if self.overlapping.is_some() {
113 "HAS"
114 } else {
115 "NON"
116 },
117 ovl = if self.is_overlapping { "OVL" } else { "NON" },
118 )
119 }
120}
121
122impl<C: Cross> PartialEq for Segment<C> {
126 #[inline]
127 fn eq(&self, other: &Self) -> bool {
128 self.partial_cmp(other) == Some(Ordering::Equal)
129 }
130}
131
132impl<C: Cross> PartialOrd for Segment<C> {
136 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
137 self.geom.partial_cmp(&other.geom)
138 }
139}
140
141#[derive(Debug)]
144pub(super) enum SplitSegments<T: GeoFloat> {
145 Unchanged {
146 overlap: bool,
147 },
148 SplitOnce {
149 overlap: Option<bool>,
150 right: LineOrPoint<T>,
151 },
152 SplitTwice {
153 right: LineOrPoint<T>,
154 },
155}
156
157#[cfg(test)]
158mod tests {
159
160 use super::*;
161
162 impl<T: GeoFloat> PartialEq for SplitSegments<T> {
163 fn eq(&self, other: &Self) -> bool {
164 match (self, other) {
165 (
166 Self::Unchanged { overlap: l_overlap },
167 Self::Unchanged { overlap: r_overlap },
168 ) => l_overlap == r_overlap,
169 (
170 Self::SplitOnce {
171 overlap: l_overlap,
172 right: l_right,
173 },
174 Self::SplitOnce {
175 overlap: r_overlap,
176 right: r_right,
177 },
178 ) => l_overlap == r_overlap && l_right.coords_equal(r_right),
179 (Self::SplitTwice { right: l_right }, Self::SplitTwice { right: r_right }) => {
180 l_right.coords_equal(r_right)
181 }
182 _ => false,
183 }
184 }
185 }
186
187 #[test]
188 fn test_split() {
189 let lines: Vec<_> = vec![
190 LineOrPoint::from(((0., 0.).into(), (10., 10.).into())),
191 ((10.0, 0.).into(), (0., 10.).into()).into(),
192 ((0., 0.).into(), (0., 10.).into()).into(),
193 ((0., 0.).into(), (5., 5.).into()).into(),
194 ((10., 10.).into(), (5., 5.).into()).into(),
195 ]
196 .into_iter()
197 .map(|lp| Segment::new(lp, None))
198 .collect();
199
200 struct TestCase {
201 a: usize,
202 b: usize,
203 isec: Option<LineOrPoint<f64>>,
204 split: Option<SplitSegments<f64>>,
205 }
206
207 impl TestCase {
208 fn assert_equality(&self, lines: &[Segment<LineOrPoint<f64>>]) {
209 let isec = lines[self.a]
210 .geom
211 .intersect_line_ordered(&lines[self.b].geom);
212 assert_eq!(isec, self.isec);
213
214 if isec.is_none() {
215 return;
216 }
217 let isec = isec.unwrap();
218 let mut copy_seg = lines[self.a].clone();
219 let split = copy_seg.adjust_for_intersection(isec);
220 assert_eq!(&split, self.split.as_ref().unwrap(),)
221 }
222 }
223
224 let test_cases = vec![
225 TestCase {
226 a: 0,
227 b: 0,
228 isec: Some(lines[0].geom),
229 split: Some(SplitSegments::Unchanged { overlap: true }),
230 },
231 TestCase {
232 a: 0,
233 b: 1,
234 isec: Some(LineOrPoint::from(SweepPoint::from((5., 5.)))),
235 split: Some(SplitSegments::SplitOnce {
236 overlap: None,
237 right: LineOrPoint::from(((5., 5.).into(), (10., 10.).into())),
238 }),
239 },
240 TestCase {
241 a: 0,
242 b: 2,
243 isec: Some(LineOrPoint::from(SweepPoint::from((0., 0.)))),
244 split: Some(SplitSegments::Unchanged { overlap: false }),
245 },
246 TestCase {
247 a: 0,
248 b: 3,
249 isec: Some(LineOrPoint::from(((0., 0.).into(), (5., 5.).into()))),
250 split: Some(SplitSegments::SplitOnce {
251 overlap: Some(false),
252 right: LineOrPoint::from(((5., 5.).into(), (10., 10.).into())),
253 }),
254 },
255 TestCase {
256 a: 0,
257 b: 4,
258 isec: Some(LineOrPoint::from(((5., 5.).into(), (10., 10.).into()))),
259 split: Some(SplitSegments::SplitOnce {
260 overlap: Some(true),
261 right: LineOrPoint::from(((5., 5.).into(), (10., 10.).into())),
262 }),
263 },
264 ];
265
266 test_cases.iter().for_each(|t| t.assert_equality(&lines));
267 }
268}