Skip to main content

bevy_math/primitives/
polygon.rs

1#[cfg(feature = "alloc")]
2use {
3    super::{Measured2d, Triangle2d},
4    alloc::{collections::BTreeMap, vec::Vec},
5    core::cmp::Ordering,
6};
7
8use crate::Vec2;
9
10#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Endpoint {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                Endpoint::Left => "Left",
                Endpoint::Right => "Right",
            })
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for Endpoint {
    #[inline]
    fn clone(&self) -> Endpoint { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Endpoint { }Copy)]
11#[cfg(feature = "alloc")]
12enum Endpoint {
13    Left,
14    Right,
15}
16
17/// An event in the [`EventQueue`] is either the left or right vertex of an edge of the polygon.
18///
19/// Events are ordered so that any event `e1` which is to the left of another event `e2` is less than that event.
20/// If `e1.position().x == e2.position().x` the events are ordered from bottom to top.
21///
22/// This is the order expected by the [`SweepLine`].
23#[cfg(feature = "alloc")]
24#[derive(#[automatically_derived]
impl ::core::fmt::Debug for SweepLineEvent {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "SweepLineEvent", "segment", &self.segment, "endpoint",
            &&self.endpoint)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for SweepLineEvent {
    #[inline]
    fn clone(&self) -> SweepLineEvent {
        let _: ::core::clone::AssertParamIsClone<Segment>;
        let _: ::core::clone::AssertParamIsClone<Endpoint>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for SweepLineEvent { }Copy)]
25struct SweepLineEvent {
26    segment: Segment,
27    /// Type of the vertex (left or right)
28    endpoint: Endpoint,
29}
30
31#[cfg(feature = "alloc")]
32impl SweepLineEvent {
33    fn position(&self) -> Vec2 {
34        match self.endpoint {
35            Endpoint::Left => self.segment.left,
36            Endpoint::Right => self.segment.right,
37        }
38    }
39}
40
41#[cfg(feature = "alloc")]
42impl PartialEq for SweepLineEvent {
43    fn eq(&self, other: &Self) -> bool {
44        self.position() == other.position()
45    }
46}
47
48#[cfg(feature = "alloc")]
49impl Eq for SweepLineEvent {}
50
51#[cfg(feature = "alloc")]
52impl PartialOrd for SweepLineEvent {
53    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
54        Some(self.cmp(other))
55    }
56}
57
58#[cfg(feature = "alloc")]
59impl Ord for SweepLineEvent {
60    fn cmp(&self, other: &Self) -> Ordering {
61        xy_order(self.position(), other.position())
62    }
63}
64
65/// Orders 2D points according to the order expected by the sweep line and event queue from -X to +X and then -Y to Y.
66#[cfg(feature = "alloc")]
67fn xy_order(a: Vec2, b: Vec2) -> Ordering {
68    a.x.total_cmp(&b.x).then_with(|| a.y.total_cmp(&b.y))
69}
70
71/// The event queue holds an ordered list of all events the [`SweepLine`] will encounter when checking the current polygon.
72#[cfg(feature = "alloc")]
73#[derive(#[automatically_derived]
impl ::core::fmt::Debug for EventQueue {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f, "EventQueue",
            "events", &&self.events)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for EventQueue {
    #[inline]
    fn clone(&self) -> EventQueue {
        EventQueue { events: ::core::clone::Clone::clone(&self.events) }
    }
}Clone)]
74struct EventQueue {
75    events: Vec<SweepLineEvent>,
76}
77#[cfg(feature = "alloc")]
78impl EventQueue {
79    /// Initialize a new `EventQueue` with all events from the polygon represented by `vertices`.
80    ///
81    /// The events in the event queue will be ordered.
82    fn new(vertices: &[Vec2]) -> Self {
83        if vertices.is_empty() {
84            return Self { events: Vec::new() };
85        }
86
87        let mut events = Vec::with_capacity(vertices.len() * 2);
88        for i in 0..vertices.len() {
89            let v1 = vertices[i];
90            let v2 = *vertices.get(i + 1).unwrap_or(&vertices[0]);
91            let (left, right) = if xy_order(v1, v2) == Ordering::Less {
92                (v1, v2)
93            } else {
94                (v2, v1)
95            };
96
97            let segment = Segment {
98                edge_index: i,
99                left,
100                right,
101            };
102            events.push(SweepLineEvent {
103                segment,
104                endpoint: Endpoint::Left,
105            });
106            events.push(SweepLineEvent {
107                segment,
108                endpoint: Endpoint::Right,
109            });
110        }
111
112        events.sort();
113
114        Self { events }
115    }
116}
117
118/// Represents a segment or rather an edge of the polygon in the [`SweepLine`].
119///
120/// Segments are ordered from bottom to top based on their left vertices if possible.
121/// If their y values are identical, the segments are ordered based on the y values of their right vertices.
122#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Segment {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f, "Segment",
            "edge_index", &self.edge_index, "left", &self.left, "right",
            &&self.right)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for Segment {
    #[inline]
    fn clone(&self) -> Segment {
        let _: ::core::clone::AssertParamIsClone<usize>;
        let _: ::core::clone::AssertParamIsClone<Vec2>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Segment { }Copy)]
123#[cfg(feature = "alloc")]
124struct Segment {
125    edge_index: usize,
126    left: Vec2,
127    right: Vec2,
128}
129
130#[cfg(feature = "alloc")]
131impl PartialEq for Segment {
132    fn eq(&self, other: &Self) -> bool {
133        self.edge_index == other.edge_index
134    }
135}
136
137#[cfg(feature = "alloc")]
138impl Eq for Segment {}
139
140#[cfg(feature = "alloc")]
141impl PartialOrd for Segment {
142    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
143        Some(self.cmp(other))
144    }
145}
146
147#[cfg(feature = "alloc")]
148impl Ord for Segment {
149    fn cmp(&self, other: &Self) -> Ordering {
150        self.left
151            .y
152            .total_cmp(&other.left.y)
153            .then_with(|| self.right.y.total_cmp(&other.right.y))
154    }
155}
156
157/// Holds information about which segment is above and which is below a given [`Segment`]
158#[cfg(feature = "alloc")]
159#[derive(#[automatically_derived]
impl ::core::fmt::Debug for SegmentOrder {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "SegmentOrder",
            "above", &self.above, "below", &&self.below)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for SegmentOrder {
    #[inline]
    fn clone(&self) -> SegmentOrder {
        let _: ::core::clone::AssertParamIsClone<Option<usize>>;
        let _: ::core::clone::AssertParamIsClone<Option<usize>>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for SegmentOrder { }Copy)]
160struct SegmentOrder {
161    above: Option<usize>,
162    below: Option<usize>,
163}
164
165/// A sweep line allows for an efficient search for intersections between [segments](`Segment`).
166///
167/// It can be thought of as a vertical line sweeping from -X to +X across the polygon that keeps track of the order of the segments
168/// the sweep line is intersecting at any given moment.
169#[derive(#[automatically_derived]
impl<'a> ::core::fmt::Debug for SweepLine<'a> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "SweepLine",
            "vertices", &self.vertices, "tree", &&self.tree)
    }
}Debug, #[automatically_derived]
impl<'a> ::core::clone::Clone for SweepLine<'a> {
    #[inline]
    fn clone(&self) -> SweepLine<'a> {
        SweepLine {
            vertices: ::core::clone::Clone::clone(&self.vertices),
            tree: ::core::clone::Clone::clone(&self.tree),
        }
    }
}Clone)]
170#[cfg(feature = "alloc")]
171struct SweepLine<'a> {
172    vertices: &'a [Vec2],
173    tree: BTreeMap<Segment, SegmentOrder>,
174}
175#[cfg(feature = "alloc")]
176impl<'a> SweepLine<'a> {
177    const fn new(vertices: &'a [Vec2]) -> Self {
178        Self {
179            vertices,
180            tree: BTreeMap::new(),
181        }
182    }
183
184    /// Determine whether the given edges of the polygon intersect.
185    fn intersects(&self, edge1: Option<usize>, edge2: Option<usize>) -> bool {
186        let Some(edge1) = edge1 else {
187            return false;
188        };
189        let Some(edge2) = edge2 else {
190            return false;
191        };
192
193        // All adjacent edges intersect at their shared vertex
194        // but these intersections do not count so we ignore them here.
195        // Likewise a segment will always intersect itself / an identical edge.
196        if edge1 == edge2
197            || (edge1 + 1) % self.vertices.len() == edge2
198            || (edge2 + 1) % self.vertices.len() == edge1
199        {
200            return false;
201        }
202
203        let s11 = self.vertices[edge1];
204        let s12 = *self.vertices.get(edge1 + 1).unwrap_or(&self.vertices[0]);
205        let s21 = self.vertices[edge2];
206        let s22 = *self.vertices.get(edge2 + 1).unwrap_or(&self.vertices[0]);
207
208        // When both points of the second edge are on the same side of the first edge, no intersection is possible.
209        if point_side(s11, s12, s21) * point_side(s11, s12, s22) > 0.0 {
210            return false;
211        }
212        if point_side(s21, s22, s11) * point_side(s21, s22, s12) > 0.0 {
213            return false;
214        }
215
216        true
217    }
218
219    /// Add a new segment to the sweep line
220    fn add(&mut self, s: Segment) -> SegmentOrder {
221        let above = if let Some((next_s, next_ord)) = self.tree.range_mut(s..).next() {
222            next_ord.below.replace(s.edge_index);
223            Some(next_s.edge_index)
224        } else {
225            None
226        };
227        let below = if let Some((prev_s, prev_ord)) = self.tree.range_mut(..s).next_back() {
228            prev_ord.above.replace(s.edge_index);
229            Some(prev_s.edge_index)
230        } else {
231            None
232        };
233
234        let s_ord = SegmentOrder { above, below };
235        self.tree.insert(s, s_ord);
236        s_ord
237    }
238
239    /// Get the segment order for the given segment.
240    ///
241    /// If `s` has not been added to the [`SweepLine`] `None` will be returned.
242    fn find(&self, s: &Segment) -> Option<&SegmentOrder> {
243        self.tree.get(s)
244    }
245
246    /// Remove `s` from the [`SweepLine`].
247    fn remove(&mut self, s: &Segment) {
248        let Some(s_ord) = self.tree.get(s).copied() else {
249            return;
250        };
251
252        if let Some((_, above_ord)) = self.tree.range_mut(s..).next() {
253            above_ord.below = s_ord.below;
254        }
255        if let Some((_, below_ord)) = self.tree.range_mut(..s).next_back() {
256            below_ord.above = s_ord.above;
257        }
258
259        self.tree.remove(s);
260    }
261}
262
263/// Test what side of the line through `p1` and `p2` `q` is.
264///
265/// The result will be `0` if the `q` is on the segment, negative for one side and positive for the other.
266#[cfg_attr(
267    not(feature = "alloc"),
268    expect(
269        dead_code,
270        reason = "this function is only used with the alloc feature"
271    )
272)]
273#[inline]
274const fn point_side(p1: Vec2, p2: Vec2, q: Vec2) -> f32 {
275    (p2.x - p1.x) * (q.y - p1.y) - (q.x - p1.x) * (p2.y - p1.y)
276}
277
278/// Tests whether the `vertices` describe a simple polygon.
279/// The last vertex must not be equal to the first vertex.
280///
281/// A polygon is simple if it is not self intersecting and not self tangent.
282/// As such, no two edges of the polygon may cross each other and each vertex must not lie on another edge.
283///
284/// Any 'polygon' with less than three vertices is simple.
285///
286/// The algorithm used is the Shamos-Hoey algorithm, a version of the Bentley-Ottman algorithm adapted to only detect whether any intersections exist.
287/// This function will run in O(n * log n)
288#[cfg(feature = "alloc")]
289pub fn is_polygon_simple(vertices: &[Vec2]) -> bool {
290    if vertices.len() < 3 {
291        return true;
292    }
293    if vertices.len() == 3 {
294        return Triangle2d::new(vertices[0], vertices[1], vertices[2]).area() > 0.0;
295    }
296
297    let event_queue = EventQueue::new(vertices);
298    let mut sweep_line = SweepLine::new(vertices);
299
300    for e in event_queue.events {
301        match e.endpoint {
302            Endpoint::Left => {
303                let s = sweep_line.add(e.segment);
304                if sweep_line.intersects(Some(e.segment.edge_index), s.above)
305                    || sweep_line.intersects(Some(e.segment.edge_index), s.below)
306                {
307                    return false;
308                }
309            }
310            Endpoint::Right => {
311                if let Some(s) = sweep_line.find(&e.segment) {
312                    if sweep_line.intersects(s.above, s.below) {
313                        return false;
314                    }
315                    sweep_line.remove(&e.segment);
316                }
317            }
318        }
319    }
320
321    true
322}
323
324#[cfg(test)]
325mod tests {
326    use crate::{primitives::polygon::is_polygon_simple, Vec2};
327
328    #[test]
329    fn complex_polygon() {
330        // A square with one side punching through the opposite side.
331        let verts = [Vec2::ZERO, Vec2::X, Vec2::ONE, Vec2::Y, Vec2::new(2.0, 0.5)];
332        assert!(!is_polygon_simple(&verts));
333
334        // A square with a vertex from one side touching the opposite side.
335        let verts = [Vec2::ZERO, Vec2::X, Vec2::ONE, Vec2::Y, Vec2::new(1.0, 0.5)];
336        assert!(!is_polygon_simple(&verts));
337
338        // A square with one side touching the opposite side.
339        let verts = [
340            Vec2::ZERO,
341            Vec2::X,
342            Vec2::ONE,
343            Vec2::Y,
344            Vec2::new(1.0, 0.6),
345            Vec2::new(1.0, 0.4),
346        ];
347        assert!(!is_polygon_simple(&verts));
348
349        // Four points lying on a line
350        let verts = [Vec2::ONE, Vec2::new(3., 2.), Vec2::new(5., 3.), Vec2::NEG_X];
351        assert!(!is_polygon_simple(&verts));
352
353        // Three points lying on a line
354        let verts = [Vec2::ONE, Vec2::new(3., 2.), Vec2::NEG_X];
355        assert!(!is_polygon_simple(&verts));
356
357        // Two identical points and one other point
358        let verts = [Vec2::ONE, Vec2::ONE, Vec2::NEG_X];
359        assert!(!is_polygon_simple(&verts));
360
361        // Two triangles with one shared side
362        let verts = [Vec2::ZERO, Vec2::X, Vec2::Y, Vec2::ONE, Vec2::X, Vec2::Y];
363        assert!(!is_polygon_simple(&verts));
364    }
365
366    #[test]
367    fn simple_polygon() {
368        // A square
369        let verts = [Vec2::ZERO, Vec2::X, Vec2::ONE, Vec2::Y];
370        assert!(is_polygon_simple(&verts));
371
372        let verts = [];
373        assert!(is_polygon_simple(&verts));
374    }
375}