Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_math/src/primitives/polygon.rs
6596 views
1
#[cfg(feature = "alloc")]
2
use {
3
super::{Measured2d, Triangle2d},
4
alloc::{collections::BTreeMap, vec::Vec},
5
core::cmp::Ordering,
6
};
7
8
use crate::Vec2;
9
10
#[derive(Debug, Clone, Copy)]
11
#[cfg(feature = "alloc")]
12
enum 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(Debug, Clone, Copy)]
25
struct SweepLineEvent {
26
segment: Segment,
27
/// Type of the vertex (left or right)
28
endpoint: Endpoint,
29
}
30
31
#[cfg(feature = "alloc")]
32
impl 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")]
42
impl PartialEq for SweepLineEvent {
43
fn eq(&self, other: &Self) -> bool {
44
self.position() == other.position()
45
}
46
}
47
48
#[cfg(feature = "alloc")]
49
impl Eq for SweepLineEvent {}
50
51
#[cfg(feature = "alloc")]
52
impl PartialOrd for SweepLineEvent {
53
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
54
Some(self.cmp(other))
55
}
56
}
57
58
#[cfg(feature = "alloc")]
59
impl 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")]
67
fn 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(Debug, Clone)]
74
struct EventQueue {
75
events: Vec<SweepLineEvent>,
76
}
77
#[cfg(feature = "alloc")]
78
impl 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(Debug, Clone, Copy)]
123
#[cfg(feature = "alloc")]
124
struct Segment {
125
edge_index: usize,
126
left: Vec2,
127
right: Vec2,
128
}
129
130
#[cfg(feature = "alloc")]
131
impl PartialEq for Segment {
132
fn eq(&self, other: &Self) -> bool {
133
self.edge_index == other.edge_index
134
}
135
}
136
137
#[cfg(feature = "alloc")]
138
impl Eq for Segment {}
139
140
#[cfg(feature = "alloc")]
141
impl PartialOrd for Segment {
142
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
143
Some(self.cmp(other))
144
}
145
}
146
147
#[cfg(feature = "alloc")]
148
impl 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(Debug, Clone, Copy)]
160
struct 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(Debug, Clone)]
170
#[cfg(feature = "alloc")]
171
struct SweepLine<'a> {
172
vertices: &'a [Vec2],
173
tree: BTreeMap<Segment, SegmentOrder>,
174
}
175
#[cfg(feature = "alloc")]
176
impl<'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(always)]
274
const 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")]
289
pub 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)]
325
mod 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
}
376
377