#![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::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 From<(f64, f64)> for Point {
fn from((x, y): (f64, f64)) -> Self {
Point { x, y }
}
}
impl From<[f64; 2]> for Point {
fn from([x, y]: [f64; 2]) -> Self {
Point { x, y }
}
}
impl From<Point> for (f64, f64) {
fn from(pt: Point) -> Self {
(pt.x, pt.y)
}
}
impl From<Point> for [f64; 2] {
fn from(pt: Point) -> Self {
[pt.x, pt.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;
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.is_multiple_of(3) {
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, mut a: usize, points: &[Point], hull: &mut Hull) -> usize {
let mut ar;
loop {
let b = self.halfedges[a];
ar = prev_halfedge(a);
let illegal = b != EMPTY && {
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];
points[p0].in_circle(&points[pr], &points[pl], &points[p1])
};
if !illegal {
match hull.edge_stack.pop() {
Some(next) => a = next,
None => return ar,
}
continue;
}
let bl = prev_halfedge(b);
let p1 = self.triangles[bl];
let p0 = self.triangles[ar];
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);
hull.edge_stack.push(br);
}
}
}
struct Hull {
prev: Vec<usize>,
next: Vec<usize>,
tri: Vec<usize>,
hash: Vec<usize>,
start: usize,
center: Point,
edge_stack: Vec<usize>,
}
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,
edge_stack: Vec::new(),
};
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 {
if p.x < min_x {
min_x = p.x;
}
if p.y < min_y {
min_y = p.y;
}
if p.x > max_x {
max_x = p.x;
}
if p.y > max_y {
max_y = 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.total_cmp(&db));
}
fn handle_collinear_points(points: &[Point]) -> Triangulation {
let (x, y) = match points.first() {
Some(p) => (p.x, p.y),
None => return Triangulation::new(0),
};
let mut dist: Vec<_> = points
.iter()
.enumerate()
.map(|(i, p)| {
let d = p.x - x;
(i, if d == 0.0 { p.y - y } else { 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 Some((i0, i1, i2)) = find_seed_triangle(points) else {
return handle_collinear_points(points);
};
let n = points.len();
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);
let mut prev_kept: Option<&Point> = None;
for &(i, _) in &dists {
let p = &points[i];
if prev_kept.is_some_and(|q| p.nearly_equals(q)) {
continue;
}
prev_kept = Some(p);
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
}
#[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
}
}