const ORIENT_EPS: f64 = 1e-9;
fn orientation(a: (f64, f64), b: (f64, f64), c: (f64, f64)) -> i32 {
let cross = (b.0 - a.0) * (c.1 - a.1) - (b.1 - a.1) * (c.0 - a.0);
if cross > ORIENT_EPS {
1
} else if cross < -ORIENT_EPS {
-1
} else {
0
}
}
fn on_segment(a: (f64, f64), b: (f64, f64), p: (f64, f64)) -> bool {
p.0 >= a.0.min(b.0) - ORIENT_EPS
&& p.0 <= a.0.max(b.0) + ORIENT_EPS
&& p.1 >= a.1.min(b.1) - ORIENT_EPS
&& p.1 <= a.1.max(b.1) + ORIENT_EPS
}
pub(crate) fn segments_intersect(
p1: (f64, f64),
p2: (f64, f64),
p3: (f64, f64),
p4: (f64, f64),
) -> bool {
let d1 = orientation(p3, p4, p1);
let d2 = orientation(p3, p4, p2);
let d3 = orientation(p1, p2, p3);
let d4 = orientation(p1, p2, p4);
if ((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0)) {
return true;
}
(d1 == 0 && on_segment(p3, p4, p1))
|| (d2 == 0 && on_segment(p3, p4, p2))
|| (d3 == 0 && on_segment(p1, p2, p3))
|| (d4 == 0 && on_segment(p1, p2, p4))
}
pub(crate) fn is_simple_polygon(vertices: &[(f64, f64)]) -> bool {
let n = vertices.len();
if n < 3 {
return false;
}
for i in 0..n {
let a1 = vertices[i];
let a2 = vertices[(i + 1) % n];
for j in (i + 1)..n {
if j == i + 1 || (i == 0 && j == n - 1) {
continue;
}
let b1 = vertices[j];
let b2 = vertices[(j + 1) % n];
if segments_intersect(a1, a2, b1, b2) {
return false;
}
}
}
true
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn crossing_segments_intersect() {
assert!(segments_intersect(
(0.0, 0.0),
(10.0, 10.0),
(0.0, 10.0),
(10.0, 0.0)
));
}
#[test]
fn disjoint_segments_do_not_intersect() {
assert!(!segments_intersect(
(0.0, 0.0),
(1.0, 0.0),
(0.0, 5.0),
(1.0, 5.0)
));
}
#[test]
fn square_is_simple() {
let sq = [(0.0, 0.0), (10.0, 0.0), (10.0, 10.0), (0.0, 10.0)];
assert!(is_simple_polygon(&sq));
}
#[test]
fn bowtie_is_not_simple() {
let bowtie = [(0.0, 0.0), (100.0, 100.0), (100.0, 0.0), (0.0, 100.0)];
assert!(!is_simple_polygon(&bowtie));
}
#[test]
fn concave_l_shape_is_simple() {
let l = [
(0.0, 0.0),
(20.0, 0.0),
(20.0, 10.0),
(10.0, 10.0),
(10.0, 20.0),
(0.0, 20.0),
];
assert!(is_simple_polygon(&l));
}
}