1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
use super::helper::Float;
use super::segment_intersection::{intersection, LineIntersection};
use super::signed_area::signed_area;
use super::sweep_event::SweepEvent;
use std::cmp::Ordering;
use std::rc::Rc;

use super::helper;

pub fn compare_segments<F>(se1_l: &Rc<SweepEvent<F>>, se2_l: &Rc<SweepEvent<F>>) -> Ordering
where
    F: Float,
{
    debug_assert!(
        se1_l.is_left(),
        "compare_segments requires left-events, got a right-event."
    );
    debug_assert!(
        se2_l.is_left(),
        "compare_segments requires left-events, got a right-event."
    );
    debug_assert!(
        se1_l.get_other_event().is_some(),
        "missing right-event in compare_segments"
    );
    debug_assert!(
        se2_l.get_other_event().is_some(),
        "missing right-event in compare_segments"
    );

    if Rc::ptr_eq(&se1_l, &se2_l) {
        return Ordering::Equal;
    }

    // The main logic of compare segments is to check the orientation of the later/older
    // SweepEvent w.r.t. the segment of the earlier/newer one. The logic is easier to
    // express by swapping them here according to their temporal order. In case we have
    // to swap, the result function must be inverted accordingly.
    let (se_old_l, se_new_l, less_if) = if se1_l.is_before(&se2_l) {
        (se1_l, se2_l, helper::less_if as fn(bool) -> Ordering)
    } else {
        (se2_l, se1_l, helper::less_if_inversed as fn(bool) -> Ordering)
    };

    if let (Some(se_old_r), Some(se_new_r)) = (se_old_l.get_other_event(), se_new_l.get_other_event()) {
        let sa_l = signed_area(se_old_l.point, se_old_r.point, se_new_l.point);
        let sa_r = signed_area(se_old_l.point, se_old_r.point, se_new_r.point);
        if sa_l != 0. || sa_r != 0. {
            // Segments are not collinear

            // Left endpoints exactly identical? Use the right endpoint to sort
            if se_old_l.point == se_new_l.point {
                return less_if(se_old_l.is_below(se_new_r.point));
            }

            // Left endpoints identical in x, but different in y? Sort by y
            if se_old_l.point.x == se_new_l.point.x {
                return less_if(se_old_l.point.y < se_new_l.point.y);
            }

            // If `l` and `r` lie on the same side of the reference segment,
            // no intersection check is necessary.
            if (sa_l > 0.) == (sa_r > 0.) {
                return less_if(sa_l > 0.);
            }

            // If `l` lies on the reference segment, compare based on `r`.
            if sa_l == 0. {
                return less_if(sa_r > 0.);
            }

            // According to the signed-area values the segments cross. Verify if
            // we can get an intersection point whic is truely different from `l`.
            let inter = intersection(se_old_l.point, se_old_r.point, se_new_l.point, se_new_r.point);
            match inter {
                LineIntersection::None => return less_if(sa_l > 0.),
                LineIntersection::Point(p) => {
                    if p == se_new_l.point {
                        return less_if(sa_r > 0.);
                    } else {
                        return less_if(sa_l > 0.);
                    }
                }
                _ => {} // go into collinear logic below
            }
        }

        // Segments are collinear
        if se_old_l.is_subject == se_new_l.is_subject {
            if se_old_l.point == se_new_l.point {
                // Previously this was returning Ordering::Equal if the segments had identical
                // left and right endpoints. I think in order to properly support self-overlapping
                // segments we must return Ordering::Equal if and only if segments are the same
                // by identity (the Rc::ptr_eq above).
                less_if(se_old_l.contour_id < se_new_l.contour_id)
            } else {
                // Fallback to purely temporal-based comparison. Since `less_if` already
                // encodes "earlier-is-less" semantics, no comparison is needed.
                less_if(true)
            }
        } else {
            less_if(se_old_l.is_subject)
        }
    } else {
        debug_assert!(false, "Other events should always be defined in compare_segment.");
        less_if(true)
    }
}

#[cfg(test)]
mod test {
    use super::super::sweep_event::SweepEvent;
    use super::compare_segments;
    use crate::splay::SplaySet;
    use geo_types::Coordinate;
    use std::cmp::Ordering;
    use std::rc::{Rc, Weak};

    macro_rules! assert_ordering {
        ($se1:expr, $se2:expr, $ordering:expr) => {
            let inverse_ordering = match $ordering {
                Ordering::Less => Ordering::Greater,
                Ordering::Greater => Ordering::Less,
                _ => Ordering::Equal,
            };
            assert_eq!(
                compare_segments(&$se1, &$se2),
                $ordering,
                "Comparing se1/se2 with expected value {:?}",
                $ordering
            );
            assert_eq!(
                compare_segments(&$se2, &$se1),
                inverse_ordering,
                "Comparing se2/se1 with expected value {:?}",
                inverse_ordering
            );
        };
    }

    fn make_simple(
        contour_id: u32,
        x: f64,
        y: f64,
        other_x: f64,
        other_y: f64,
        is_subject: bool,
    ) -> (Rc<SweepEvent<f64>>, Rc<SweepEvent<f64>>) {
        let other = SweepEvent::new_rc(
            contour_id,
            Coordinate { x: other_x, y: other_y },
            false,
            Weak::new(),
            is_subject,
            true,
        );
        let event = SweepEvent::new_rc(
            contour_id,
            Coordinate { x, y },
            true,
            Rc::downgrade(&other),
            is_subject,
            true,
        );
        // Make sure test cases fulfill the invariant of left/right relationship.
        assert!(event.is_before(&other));

        (event, other)
    }

    #[test]
    fn not_collinear_shared_left_right_first() {
        let (se1, _other1) = make_simple(0, 0.0, 0.0, 1.0, 1.0, false);
        let (se2, _other2) = make_simple(0, 0.0, 0.0, 2.0, 3.0, false);

        let mut tree = SplaySet::new(compare_segments);

        tree.insert(se1);
        tree.insert(se2);

        let min_other = tree.min().unwrap().get_other_event().unwrap();
        let max_other = tree.max().unwrap().get_other_event().unwrap();

        assert_eq!(max_other.point, Coordinate { x: 2.0, y: 3.0 });
        assert_eq!(min_other.point, Coordinate { x: 1.0, y: 1.0 });
    }

    #[test]
    fn not_collinear_different_left_point_right_sort_y() {
        let (se1, _other1) = make_simple(0, 0.0, 1.0, 1.0, 1.0, false);
        let (se2, _other2) = make_simple(0, 0.0, 2.0, 2.0, 3.0, false);

        let mut tree = SplaySet::new(compare_segments);

        tree.insert(se1);
        tree.insert(se2);

        let min_other = tree.min().unwrap().get_other_event().unwrap();
        let max_other = tree.max().unwrap().get_other_event().unwrap();

        assert_eq!(min_other.point, Coordinate { x: 1.0, y: 1.0 });
        assert_eq!(max_other.point, Coordinate { x: 2.0, y: 3.0 });
    }

    #[test]
    fn not_collinear_order_in_sweep_line() {
        let (se1, _other1) = make_simple(0, 0.0, 1.0, 2.0, 1.0, false);
        let (se2, _other2) = make_simple(0, -1.0, 0.0, 2.0, 3.0, false);
        let (se3, _other3) = make_simple(0, 0.0, 1.0, 3.0, 4.0, false);
        let (se4, _other4) = make_simple(0, -1.0, 0.0, 3.0, 1.0, false);

        assert_eq!(se1.cmp(&se2), Ordering::Less);
        assert!(!se2.is_below(se1.point));
        assert!(se2.is_above(se1.point));

        assert_ordering!(se1, se2, Ordering::Less);

        assert_eq!(se3.cmp(&se4), Ordering::Less);
        assert!(!se4.is_above(se3.point));
    }

    #[test]
    fn not_collinear_first_point_is_below() {
        let (se2, _other2) = make_simple(0, 1.0, 1.0, 5.0, 1.0, false);
        let (se1, _other1) = make_simple(0, -1.0, 0.0, 2.0, 3.0, false);

        assert!(!se1.is_below(se2.point));
        assert_ordering!(se1, se2, Ordering::Greater);
    }

    #[test]
    fn collinear_segments() {
        let (se1, _other1) = make_simple(0, 1.0, 1.0, 5.0, 1.0, true);
        let (se2, _other2) = make_simple(0, 2.0, 01.0, 3.0, 1.0, false);

        assert_ne!(se1.is_subject, se2.is_subject);
        assert_ordering!(se1, se2, Ordering::Less);
    }

    #[test]
    fn collinear_shared_left_point() {
        {
            let (se1, _other2) = make_simple(1, 0.0, 1.0, 5.0, 1.0, false);
            let (se2, _other1) = make_simple(2, 0.0, 1.0, 3.0, 1.0, false);

            assert_eq!(se1.is_subject, se2.is_subject);
            assert_eq!(se1.point, se2.point);

            assert_ordering!(se1, se2, Ordering::Less);
        }
        {
            let (se1, _other2) = make_simple(2, 0.0, 1.0, 5.0, 1.0, false);
            let (se2, _other1) = make_simple(1, 0.0, 1.0, 3.0, 1.0, false);

            assert_ordering!(se1, se2, Ordering::Greater);
        }
    }

    #[test]
    fn collinear_same_polygon_different_left() {
        let (se1, _other2) = make_simple(0, 1.0, 1.0, 5.0, 1.0, true);
        let (se2, _other1) = make_simple(0, 2.0, 1.0, 3.0, 1.0, true);

        assert_eq!(se1.is_subject, se2.is_subject);
        assert_ne!(se1.point, se2.point);
        assert_ordering!(se1, se2, Ordering::Less);
    }

    #[test]
    fn t_shaped_cases() {
        // shape:  /
        //        /\
        let (se1, _other1) = make_simple(0, 0.0, 0.0, 1.0, 1.0, true);
        let (se2, _other2) = make_simple(0, 0.5, 0.5, 1.0, 0.0, true);
        assert_ordering!(se1, se2, Ordering::Greater);

        // shape: \/
        //         \
        let (se1, _other1) = make_simple(0, 0.0, 1.0, 1.0, 0.0, true);
        let (se2, _other2) = make_simple(0, 0.5, 0.5, 1.0, 1.0, true);
        assert_ordering!(se1, se2, Ordering::Less);

        // shape: T
        let (se1, _other1) = make_simple(0, 0.0, 1.0, 1.0, 1.0, true);
        let (se2, _other2) = make_simple(0, 0.5, 0.0, 0.5, 1.0, true);
        assert_ordering!(se1, se2, Ordering::Greater);

        // shape: T upside down
        let (se1, _other1) = make_simple(0, 0.0, 0.0, 1.0, 0.0, true);
        let (se2, _other2) = make_simple(0, 0.5, 0.0, 0.5, 1.0, true);
        assert_ordering!(se1, se2, Ordering::Less);
    }

    #[test]
    fn vertical_segment() {
        // vertical reference segment at x = 0, expanding from y = -1 to +1.
        let (se1, _other1) = make_simple(0, 0.0, -1.0, 0.0, 1.0, true);

        // "above" cases
        let (se2, _other2) = make_simple(0, -1.0, 1.0, 0.0, 1.0, true);
        assert_ordering!(se1, se2, Ordering::Less);
        let (se2, _other2) = make_simple(0, 0.0, 1.0, 1.0, 1.0, true);
        assert_ordering!(se1, se2, Ordering::Less);
        let (se2, _other2) = make_simple(0, -1.0, 2.0, 0.0, 2.0, true);
        assert_ordering!(se1, se2, Ordering::Less);
        let (se2, _other2) = make_simple(0, 0.0, 2.0, 1.0, 2.0, true);
        assert_ordering!(se1, se2, Ordering::Less);
        let (se2, _other2) = make_simple(0, 0.0, 1.0, 0.0, 2.0, true);
        assert_ordering!(se1, se2, Ordering::Less);

        // "below" cases
        let (se2, _other2) = make_simple(0, -1.0, -1.0, 0.0, -1.0, true);
        assert_ordering!(se1, se2, Ordering::Greater);
        let (se2, _other2) = make_simple(0, 0.0, -1.0, 1.0, -1.0, true);
        assert_ordering!(se1, se2, Ordering::Greater);
        let (se2, _other2) = make_simple(0, -1.0, -2.0, 0.0, -2.0, true);
        assert_ordering!(se1, se2, Ordering::Greater);
        let (se2, _other2) = make_simple(0, 0.0, -2.0, 1.0, -2.0, true);
        assert_ordering!(se1, se2, Ordering::Greater);
        let (se2, _other2) = make_simple(0, 0.0, -2.0, 0.0, -1.0, true);
        assert_ordering!(se1, se2, Ordering::Greater);

        // overlaps
        let (se2, _other2) = make_simple(0, 0.0, -0.5, 0.0, 0.5, true);
        assert_ordering!(se1, se2, Ordering::Less);
        // When left endpoints are identical, the ordering is no longer anti-symmetric.
        // TODO: Decide if this is a problem.
        // let (se2, _other2) = make_simple(0, 0.0, -1.0, 0.0, 0.0, true);
        // assert_ordering!(se1, se2, Ordering::Less); // fails because of its not anti-symmetric.
    }
}