#[cfg(feature = "alloc")]
use {
super::{Measured2d, Triangle2d},
alloc::{collections::BTreeMap, vec::Vec},
core::cmp::Ordering,
};
use crate::Vec2;
#[derive(Debug, Clone, Copy)]
#[cfg(feature = "alloc")]
enum Endpoint {
Left,
Right,
}
#[cfg(feature = "alloc")]
#[derive(Debug, Clone, Copy)]
struct SweepLineEvent {
segment: Segment,
endpoint: Endpoint,
}
#[cfg(feature = "alloc")]
impl SweepLineEvent {
fn position(&self) -> Vec2 {
match self.endpoint {
Endpoint::Left => self.segment.left,
Endpoint::Right => self.segment.right,
}
}
}
#[cfg(feature = "alloc")]
impl PartialEq for SweepLineEvent {
fn eq(&self, other: &Self) -> bool {
self.position() == other.position()
}
}
#[cfg(feature = "alloc")]
impl Eq for SweepLineEvent {}
#[cfg(feature = "alloc")]
impl PartialOrd for SweepLineEvent {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[cfg(feature = "alloc")]
impl Ord for SweepLineEvent {
fn cmp(&self, other: &Self) -> Ordering {
xy_order(self.position(), other.position())
}
}
#[cfg(feature = "alloc")]
fn xy_order(a: Vec2, b: Vec2) -> Ordering {
a.x.total_cmp(&b.x).then_with(|| a.y.total_cmp(&b.y))
}
#[cfg(feature = "alloc")]
#[derive(Debug, Clone)]
struct EventQueue {
events: Vec<SweepLineEvent>,
}
#[cfg(feature = "alloc")]
impl EventQueue {
fn new(vertices: &[Vec2]) -> Self {
if vertices.is_empty() {
return Self { events: Vec::new() };
}
let mut events = Vec::with_capacity(vertices.len() * 2);
for i in 0..vertices.len() {
let v1 = vertices[i];
let v2 = *vertices.get(i + 1).unwrap_or(&vertices[0]);
let (left, right) = if xy_order(v1, v2) == Ordering::Less {
(v1, v2)
} else {
(v2, v1)
};
let segment = Segment {
edge_index: i,
left,
right,
};
events.push(SweepLineEvent {
segment,
endpoint: Endpoint::Left,
});
events.push(SweepLineEvent {
segment,
endpoint: Endpoint::Right,
});
}
events.sort();
Self { events }
}
}
#[derive(Debug, Clone, Copy)]
#[cfg(feature = "alloc")]
struct Segment {
edge_index: usize,
left: Vec2,
right: Vec2,
}
#[cfg(feature = "alloc")]
impl PartialEq for Segment {
fn eq(&self, other: &Self) -> bool {
self.edge_index == other.edge_index
}
}
#[cfg(feature = "alloc")]
impl Eq for Segment {}
#[cfg(feature = "alloc")]
impl PartialOrd for Segment {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[cfg(feature = "alloc")]
impl Ord for Segment {
fn cmp(&self, other: &Self) -> Ordering {
self.left
.y
.total_cmp(&other.left.y)
.then_with(|| self.right.y.total_cmp(&other.right.y))
}
}
#[cfg(feature = "alloc")]
#[derive(Debug, Clone, Copy)]
struct SegmentOrder {
above: Option<usize>,
below: Option<usize>,
}
#[derive(Debug, Clone)]
#[cfg(feature = "alloc")]
struct SweepLine<'a> {
vertices: &'a [Vec2],
tree: BTreeMap<Segment, SegmentOrder>,
}
#[cfg(feature = "alloc")]
impl<'a> SweepLine<'a> {
const fn new(vertices: &'a [Vec2]) -> Self {
Self {
vertices,
tree: BTreeMap::new(),
}
}
fn intersects(&self, edge1: Option<usize>, edge2: Option<usize>) -> bool {
let Some(edge1) = edge1 else {
return false;
};
let Some(edge2) = edge2 else {
return false;
};
if edge1 == edge2
|| (edge1 + 1) % self.vertices.len() == edge2
|| (edge2 + 1) % self.vertices.len() == edge1
{
return false;
}
let s11 = self.vertices[edge1];
let s12 = *self.vertices.get(edge1 + 1).unwrap_or(&self.vertices[0]);
let s21 = self.vertices[edge2];
let s22 = *self.vertices.get(edge2 + 1).unwrap_or(&self.vertices[0]);
if point_side(s11, s12, s21) * point_side(s11, s12, s22) > 0.0 {
return false;
}
if point_side(s21, s22, s11) * point_side(s21, s22, s12) > 0.0 {
return false;
}
true
}
fn add(&mut self, s: Segment) -> SegmentOrder {
let above = if let Some((next_s, next_ord)) = self.tree.range_mut(s..).next() {
next_ord.below.replace(s.edge_index);
Some(next_s.edge_index)
} else {
None
};
let below = if let Some((prev_s, prev_ord)) = self.tree.range_mut(..s).next_back() {
prev_ord.above.replace(s.edge_index);
Some(prev_s.edge_index)
} else {
None
};
let s_ord = SegmentOrder { above, below };
self.tree.insert(s, s_ord);
s_ord
}
fn find(&self, s: &Segment) -> Option<&SegmentOrder> {
self.tree.get(s)
}
fn remove(&mut self, s: &Segment) {
let Some(s_ord) = self.tree.get(s).copied() else {
return;
};
if let Some((_, above_ord)) = self.tree.range_mut(s..).next() {
above_ord.below = s_ord.below;
}
if let Some((_, below_ord)) = self.tree.range_mut(..s).next_back() {
below_ord.above = s_ord.above;
}
self.tree.remove(s);
}
}
#[cfg_attr(
not(feature = "alloc"),
expect(
dead_code,
reason = "this function is only used with the alloc feature"
)
)]
#[inline]
const fn point_side(p1: Vec2, p2: Vec2, q: Vec2) -> f32 {
(p2.x - p1.x) * (q.y - p1.y) - (q.x - p1.x) * (p2.y - p1.y)
}
#[cfg(feature = "alloc")]
pub fn is_polygon_simple(vertices: &[Vec2]) -> bool {
if vertices.len() < 3 {
return true;
}
if vertices.len() == 3 {
return Triangle2d::new(vertices[0], vertices[1], vertices[2]).area() > 0.0;
}
let event_queue = EventQueue::new(vertices);
let mut sweep_line = SweepLine::new(vertices);
for e in event_queue.events {
match e.endpoint {
Endpoint::Left => {
let s = sweep_line.add(e.segment);
if sweep_line.intersects(Some(e.segment.edge_index), s.above)
|| sweep_line.intersects(Some(e.segment.edge_index), s.below)
{
return false;
}
}
Endpoint::Right => {
if let Some(s) = sweep_line.find(&e.segment) {
if sweep_line.intersects(s.above, s.below) {
return false;
}
sweep_line.remove(&e.segment);
}
}
}
}
true
}
#[cfg(test)]
mod tests {
use crate::{primitives::polygon::is_polygon_simple, Vec2};
#[test]
fn complex_polygon() {
let verts = [Vec2::ZERO, Vec2::X, Vec2::ONE, Vec2::Y, Vec2::new(2.0, 0.5)];
assert!(!is_polygon_simple(&verts));
let verts = [Vec2::ZERO, Vec2::X, Vec2::ONE, Vec2::Y, Vec2::new(1.0, 0.5)];
assert!(!is_polygon_simple(&verts));
let verts = [
Vec2::ZERO,
Vec2::X,
Vec2::ONE,
Vec2::Y,
Vec2::new(1.0, 0.6),
Vec2::new(1.0, 0.4),
];
assert!(!is_polygon_simple(&verts));
let verts = [Vec2::ONE, Vec2::new(3., 2.), Vec2::new(5., 3.), Vec2::NEG_X];
assert!(!is_polygon_simple(&verts));
let verts = [Vec2::ONE, Vec2::new(3., 2.), Vec2::NEG_X];
assert!(!is_polygon_simple(&verts));
let verts = [Vec2::ONE, Vec2::ONE, Vec2::NEG_X];
assert!(!is_polygon_simple(&verts));
let verts = [Vec2::ZERO, Vec2::X, Vec2::Y, Vec2::ONE, Vec2::X, Vec2::Y];
assert!(!is_polygon_simple(&verts));
}
#[test]
fn simple_polygon() {
let verts = [Vec2::ZERO, Vec2::X, Vec2::ONE, Vec2::Y];
assert!(is_polygon_simple(&verts));
let verts = [];
assert!(is_polygon_simple(&verts));
}
}