#![no_std]
#![allow(clippy::many_single_char_names)]
#[cfg(feature = "std")]
extern crate std;
#[macro_use]
extern crate alloc;
use alloc::vec::Vec;
use core::{cmp::Ordering, f64, fmt};
use robust::orient2d;
pub const EPSILON: f64 = f64::EPSILON * 2.0;
#[derive(Clone, PartialEq, Default)]
pub struct Point {
pub x: f64,
pub y: f64,
}
impl fmt::Debug for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}, {}]", self.x, self.y)
}
}
impl From<&Point> for robust::Coord<f64> {
fn from(p: &Point) -> robust::Coord<f64> {
robust::Coord::<f64> { x: p.x, y: p.y }
}
}
impl Point {
fn dist2(&self, p: &Self) -> f64 {
let dx = self.x - p.x;
let dy = self.y - p.y;
dx * dx + dy * dy
}
fn orient(&self, q: &Self, r: &Self) -> f64 {
orient2d(self.into(), q.into(), r.into())
}
fn circumdelta(&self, b: &Self, c: &Self) -> (f64, f64) {
let dx = b.x - self.x;
let dy = b.y - self.y;
let ex = c.x - self.x;
let ey = c.y - self.y;
let bl = dx * dx + dy * dy;
let cl = ex * ex + ey * ey;
let d = 0.5 / (dx * ey - dy * ex);
let x = (ey * bl - dy * cl) * d;
let y = (dx * cl - ex * bl) * d;
(x, y)
}
fn circumradius2(&self, b: &Self, c: &Self) -> f64 {
let (x, y) = self.circumdelta(b, c);
x * x + y * y
}
fn circumcenter(&self, b: &Self, c: &Self) -> Self {
let (x, y) = self.circumdelta(b, c);
Self {
x: self.x + x,
y: self.y + y,
}
}
fn in_circle(&self, b: &Self, c: &Self, p: &Self) -> bool {
let dx = self.x - p.x;
let dy = self.y - p.y;
let ex = b.x - p.x;
let ey = b.y - p.y;
let fx = c.x - p.x;
let fy = c.y - p.y;
let ap = dx * dx + dy * dy;
let bp = ex * ex + ey * ey;
let cp = fx * fx + fy * fy;
dx * (ey * cp - bp * fy) - dy * (ex * cp - bp * fx) + ap * (ex * fy - ey * fx) < 0.0
}
fn nearly_equals(&self, p: &Self) -> bool {
f64_abs(self.x - p.x) <= EPSILON && f64_abs(self.y - p.y) <= EPSILON
}
}
pub const EMPTY: usize = usize::max_value();
pub fn next_halfedge(i: usize) -> usize {
if i % 3 == 2 {
i - 2
} else {
i + 1
}
}
pub fn prev_halfedge(i: usize) -> usize {
if i % 3 == 0 {
i + 2
} else {
i - 1
}
}
#[derive(Debug, Clone)]
pub struct Triangulation {
pub triangles: Vec<usize>,
pub halfedges: Vec<usize>,
pub hull: Vec<usize>,
}
impl Triangulation {
fn new(n: usize) -> Self {
let max_triangles = if n > 2 { 2 * n - 5 } else { 0 };
Self {
triangles: Vec::with_capacity(max_triangles * 3),
halfedges: Vec::with_capacity(max_triangles * 3),
hull: Vec::new(),
}
}
pub fn len(&self) -> usize {
self.triangles.len() / 3
}
pub fn is_empty(&self) -> bool {
self.triangles.is_empty()
}
fn add_triangle(
&mut self,
i0: usize,
i1: usize,
i2: usize,
a: usize,
b: usize,
c: usize,
) -> usize {
let t = self.triangles.len();
self.triangles.push(i0);
self.triangles.push(i1);
self.triangles.push(i2);
self.halfedges.push(a);
self.halfedges.push(b);
self.halfedges.push(c);
if a != EMPTY {
self.halfedges[a] = t;
}
if b != EMPTY {
self.halfedges[b] = t + 1;
}
if c != EMPTY {
self.halfedges[c] = t + 2;
}
t
}
fn legalize(&mut self, a: usize, points: &[Point], hull: &mut Hull) -> usize {
let b = self.halfedges[a];
let ar = prev_halfedge(a);
if b == EMPTY {
return ar;
}
let al = next_halfedge(a);
let bl = prev_halfedge(b);
let p0 = self.triangles[ar];
let pr = self.triangles[a];
let pl = self.triangles[al];
let p1 = self.triangles[bl];
let illegal = points[p0].in_circle(&points[pr], &points[pl], &points[p1]);
if illegal {
self.triangles[a] = p1;
self.triangles[b] = p0;
let hbl = self.halfedges[bl];
let har = self.halfedges[ar];
if hbl == EMPTY {
let mut e = hull.start;
loop {
if hull.tri[e] == bl {
hull.tri[e] = a;
break;
}
e = hull.prev[e];
if e == hull.start {
break;
}
}
}
self.halfedges[a] = hbl;
self.halfedges[b] = har;
self.halfedges[ar] = bl;
if hbl != EMPTY {
self.halfedges[hbl] = a;
}
if har != EMPTY {
self.halfedges[har] = b;
}
if bl != EMPTY {
self.halfedges[bl] = ar;
}
let br = next_halfedge(b);
self.legalize(a, points, hull);
return self.legalize(br, points, hull);
}
ar
}
}
struct Hull {
prev: Vec<usize>,
next: Vec<usize>,
tri: Vec<usize>,
hash: Vec<usize>,
start: usize,
center: Point,
}
impl Hull {
fn new(n: usize, center: Point, i0: usize, i1: usize, i2: usize, points: &[Point]) -> Self {
let hash_len = f64_sqrt(n as f64) as usize;
let mut hull = Self {
prev: vec![0; n], next: vec![0; n], tri: vec![0; n], hash: vec![EMPTY; hash_len], start: i0,
center,
};
hull.next[i0] = i1;
hull.prev[i2] = i1;
hull.next[i1] = i2;
hull.prev[i0] = i2;
hull.next[i2] = i0;
hull.prev[i1] = i0;
hull.tri[i0] = 0;
hull.tri[i1] = 1;
hull.tri[i2] = 2;
hull.hash_edge(&points[i0], i0);
hull.hash_edge(&points[i1], i1);
hull.hash_edge(&points[i2], i2);
hull
}
fn hash_key(&self, p: &Point) -> usize {
let dx = p.x - self.center.x;
let dy = p.y - self.center.y;
let p = dx / (f64_abs(dx) + f64_abs(dy));
let a = (if dy > 0.0 { 3.0 - p } else { 1.0 + p }) / 4.0;
let len = self.hash.len();
(f64_floor((len as f64) * a) as usize) % len
}
fn hash_edge(&mut self, p: &Point, i: usize) {
let key = self.hash_key(p);
self.hash[key] = i;
}
fn find_visible_edge(&self, p: &Point, points: &[Point]) -> (usize, bool) {
let mut start: usize = 0;
let key = self.hash_key(p);
let len = self.hash.len();
for j in 0..len {
start = self.hash[(key + j) % len];
if start != EMPTY && self.next[start] != EMPTY {
break;
}
}
start = self.prev[start];
let mut e = start;
while p.orient(&points[e], &points[self.next[e]]) <= 0. {
e = self.next[e];
if e == start {
return (EMPTY, false);
}
}
(e, e == start)
}
}
fn calc_bbox_center(points: &[Point]) -> Point {
let mut min_x = f64::INFINITY;
let mut min_y = f64::INFINITY;
let mut max_x = f64::NEG_INFINITY;
let mut max_y = f64::NEG_INFINITY;
for p in points.iter() {
min_x = min_x.min(p.x);
min_y = min_y.min(p.y);
max_x = max_x.max(p.x);
max_y = max_y.max(p.y);
}
Point {
x: (min_x + max_x) / 2.0,
y: (min_y + max_y) / 2.0,
}
}
fn find_closest_point(points: &[Point], p0: &Point) -> Option<usize> {
let mut min_dist = f64::INFINITY;
let mut k: usize = 0;
for (i, p) in points.iter().enumerate() {
let d = p0.dist2(p);
if d > 0.0 && d < min_dist {
k = i;
min_dist = d;
}
}
if min_dist == f64::INFINITY {
None
} else {
Some(k)
}
}
fn find_seed_triangle(points: &[Point]) -> Option<(usize, usize, usize)> {
let bbox_center = calc_bbox_center(points);
let i0 = find_closest_point(points, &bbox_center)?;
let p0 = &points[i0];
let i1 = find_closest_point(points, p0)?;
let p1 = &points[i1];
let mut min_radius = f64::INFINITY;
let mut i2: usize = 0;
for (i, p) in points.iter().enumerate() {
if i == i0 || i == i1 {
continue;
}
let r = p0.circumradius2(p1, p);
if r < min_radius {
i2 = i;
min_radius = r;
}
}
if min_radius == f64::INFINITY {
None
} else {
Some(if p0.orient(p1, &points[i2]) > 0. {
(i0, i2, i1)
} else {
(i0, i1, i2)
})
}
}
fn sortf(f: &mut [(usize, f64)]) {
f.sort_unstable_by(|&(_, da), &(_, db)| da.partial_cmp(&db).unwrap_or(Ordering::Equal));
}
fn handle_collinear_points(points: &[Point]) -> Triangulation {
let Point { x, y } = points.first().cloned().unwrap_or_default();
let mut dist: Vec<_> = points
.iter()
.enumerate()
.map(|(i, p)| {
let mut d = p.x - x;
if d == 0.0 {
d = p.y - y;
}
(i, d)
})
.collect();
sortf(&mut dist);
let mut triangulation = Triangulation::new(0);
let mut d0 = f64::NEG_INFINITY;
for (i, distance) in dist {
if distance > d0 {
triangulation.hull.push(i);
d0 = distance;
}
}
triangulation
}
pub fn triangulate(points: &[Point]) -> Triangulation {
let seed_triangle = find_seed_triangle(points);
if seed_triangle.is_none() {
return handle_collinear_points(points);
}
let n = points.len();
let (i0, i1, i2) =
seed_triangle.expect("At this stage, points are guaranteed to yeild a seed triangle");
let center = points[i0].circumcenter(&points[i1], &points[i2]);
let mut triangulation = Triangulation::new(n);
triangulation.add_triangle(i0, i1, i2, EMPTY, EMPTY, EMPTY);
let mut dists: Vec<_> = points
.iter()
.enumerate()
.map(|(i, point)| (i, center.dist2(point)))
.collect();
sortf(&mut dists);
let mut hull = Hull::new(n, center, i0, i1, i2, points);
for (k, &(i, _)) in dists.iter().enumerate() {
let p = &points[i];
if k > 0 && p.nearly_equals(&points[dists[k - 1].0]) {
continue;
}
if i == i0 || i == i1 || i == i2 {
continue;
}
let (mut e, walk_back) = hull.find_visible_edge(p, points);
if e == EMPTY {
continue; }
let t = triangulation.add_triangle(e, i, hull.next[e], EMPTY, EMPTY, hull.tri[e]);
hull.tri[i] = triangulation.legalize(t + 2, points, &mut hull);
hull.tri[e] = t;
let mut n = hull.next[e];
loop {
let q = hull.next[n];
if p.orient(&points[n], &points[q]) <= 0. {
break;
}
let t = triangulation.add_triangle(n, i, q, hull.tri[i], EMPTY, hull.tri[n]);
hull.tri[i] = triangulation.legalize(t + 2, points, &mut hull);
hull.next[n] = EMPTY; n = q;
}
if walk_back {
loop {
let q = hull.prev[e];
if p.orient(&points[q], &points[e]) <= 0. {
break;
}
let t = triangulation.add_triangle(q, i, e, EMPTY, hull.tri[e], hull.tri[q]);
triangulation.legalize(t + 2, points, &mut hull);
hull.tri[q] = t;
hull.next[e] = EMPTY; e = q;
}
}
hull.prev[i] = e;
hull.next[i] = n;
hull.prev[n] = i;
hull.next[e] = i;
hull.start = e;
hull.hash_edge(p, i);
hull.hash_edge(&points[e], e);
}
let mut e = hull.start;
loop {
triangulation.hull.push(e);
e = hull.next[e];
if e == hull.start {
break;
}
}
triangulation.triangles.shrink_to_fit();
triangulation.halfedges.shrink_to_fit();
triangulation
}
#[cfg(feature = "std")]
#[inline]
fn f64_abs(f: f64) -> f64 {
f.abs()
}
#[cfg(not(feature = "std"))]
#[inline]
fn f64_abs(f: f64) -> f64 {
const SIGN_BIT: u64 = 1 << 63;
f64::from_bits(f64::to_bits(f) & !SIGN_BIT)
}
#[cfg(feature = "std")]
#[inline]
fn f64_floor(f: f64) -> f64 {
f.floor()
}
#[cfg(not(feature = "std"))]
#[inline]
fn f64_floor(f: f64) -> f64 {
let mut res = (f as i64) as f64;
if res > f {
res -= 1.0;
}
res as f64
}
#[cfg(feature = "std")]
#[inline]
fn f64_sqrt(f: f64) -> f64 {
f.sqrt()
}
#[cfg(not(feature = "std"))]
#[inline]
fn f64_sqrt(f: f64) -> f64 {
if f < 2.0 {
return f;
};
let sc = f64_sqrt(f / 4.0) * 2.0;
let lc = sc + 1.0;
if lc * lc > f {
sc
} else {
lc
}
}