#![no_std]
#![forbid(unsafe_code)]
#![deny(missing_docs)]
extern crate alloc;
pub mod earcut;
use alloc::vec::Vec;
use core::f64;
pub use earcut::{earcut, signed_area};
use libm::fabs;
pub use s2json::{GetXY, GetZ};
pub fn earclip<T: GetXY + GetZ>(
polygon: &[Vec<T>],
modulo: Option<f64>,
offset: Option<usize>,
) -> (Vec<f64>, Vec<usize>) {
let modulo = modulo.unwrap_or(f64::INFINITY);
let offset = offset.unwrap_or(0);
let (mut vertices, hole_indices, dim) = flatten(polygon); let mut indices = earcut(&vertices, &hole_indices, dim);
if modulo != f64::INFINITY {
tesselate(&mut vertices, &mut indices, modulo, dim);
}
for index in indices.iter_mut() {
*index += offset;
}
(vertices, indices)
}
pub fn earclip_float(
polygon: &[Vec<Vec<f64>>],
modulo: Option<f64>,
offset: Option<usize>,
) -> (Vec<f64>, Vec<usize>) {
let modulo = modulo.unwrap_or(f64::INFINITY);
let offset = offset.unwrap_or(0);
let (mut vertices, hole_indices, dim) = flatten_float(polygon); let mut indices = earcut(&vertices, &hole_indices, dim);
if modulo != f64::INFINITY {
tesselate(&mut vertices, &mut indices, modulo, 2);
}
indices = indices.into_iter().map(|index| index + offset).collect();
(vertices, indices)
}
pub fn tesselate(vertices: &mut Vec<f64>, indices: &mut Vec<usize>, modulo: f64, dim: usize) {
for axis in 0..dim {
let mut i: isize = 0;
while i < indices.len() as isize {
let i_index = i as usize;
let a = indices[i_index];
let b = indices[i_index + 1];
let c = indices[i_index + 2];
if let Some(new_triangle) =
split_if_necessary(a, b, c, vertices, indices, dim, axis, modulo)
{
indices[i_index] = new_triangle[0];
indices[i_index + 1] = new_triangle[1];
indices[i_index + 2] = new_triangle[2];
i -= 3;
}
i += 3;
}
}
}
#[allow(clippy::too_many_arguments)]
fn split_if_necessary(
i1: usize,
i2: usize,
i3: usize,
vertices: &mut Vec<f64>,
indices: &mut Vec<usize>,
dim: usize,
axis: usize,
modulo: f64,
) -> Option<[usize; 3]> {
let v1 = vertices[i1 * dim + axis];
let v2 = vertices[i2 * dim + axis];
let v3 = vertices[i3 * dim + axis];
if v1 < v2 && v1 < v3 {
let mod_point = v1 + modulo - mod2(v1, modulo);
if mod_point > v1 && mod_point <= v2 && mod_point <= v3 && v2 != mod_point {
return Some(split_right(
mod_point, i1, i2, i3, v1, v2, v3, vertices, indices, dim, axis, modulo,
));
}
} else if v1 > v2 && v1 > v3 {
let mut m2 = mod2(v1, modulo);
if m2 == 0. {
m2 = modulo;
}
let mod_point = v1 - m2;
if mod_point < v1 && mod_point >= v2 && mod_point >= v3 && v2 != mod_point {
return Some(split_left(
mod_point, i1, i2, i3, v1, v2, v3, vertices, indices, dim, axis, modulo,
));
}
}
if v2 < v1 && v2 < v3 {
let mod_point = v2 + modulo - mod2(v2, modulo);
if mod_point > v2
&& mod_point <= v3
&& mod_point <= v1
&& (v1 != mod_point || v3 != mod_point)
{
return Some(split_right(
mod_point, i2, i3, i1, v2, v3, v1, vertices, indices, dim, axis, modulo,
));
}
} else if v2 > v1 && v2 > v3 {
let mut m2 = mod2(v2, modulo);
if m2 == 0. {
m2 = modulo;
}
let mod_point = v2 - m2;
if mod_point < v2
&& mod_point >= v3
&& mod_point >= v1
&& (v1 != mod_point || v3 != mod_point)
{
return Some(split_left(
mod_point, i2, i3, i1, v2, v3, v1, vertices, indices, dim, axis, modulo,
));
}
}
if v3 < v1 && v3 < v2 {
let mod_point = v3 + modulo - mod2(v3, modulo);
if mod_point > v3
&& mod_point <= v1
&& mod_point <= v2
&& (v1 != mod_point || v2 != mod_point)
{
return Some(split_right(
mod_point, i3, i1, i2, v3, v1, v2, vertices, indices, dim, axis, modulo,
));
}
} else if v3 > v1 && v3 > v2 {
let mut m2 = mod2(v3, modulo);
if m2 == 0. {
m2 = modulo;
}
let mod_point = v3 - m2;
if mod_point < v3
&& mod_point >= v1
&& mod_point >= v2
&& (v1 != mod_point || v2 != mod_point)
{
return Some(split_left(
mod_point, i3, i1, i2, v3, v1, v2, vertices, indices, dim, axis, modulo,
));
}
}
None
}
#[allow(clippy::too_many_arguments)]
fn create_vertex(
split_point: f64,
i1: usize,
i2: usize,
v1: f64,
v2: f64,
vertices: &mut Vec<f64>,
dim: usize,
axis: usize,
) -> usize {
let index = vertices.len() / dim;
let travel_divisor = (v2 - v1) / (split_point - v1);
for i in 0..2 {
let va1 = vertices[i1 * dim + i];
let va2 = vertices[i2 * dim + i];
if i != axis {
vertices.push(va1 + (va2 - va1) / travel_divisor);
} else {
vertices.push(split_point);
}
}
index
}
#[allow(clippy::too_many_arguments)]
fn split_right(
mod_point: f64,
i1: usize,
i2: usize,
i3: usize,
v1: f64,
v2: f64,
v3: f64,
vertices: &mut Vec<f64>,
indices: &mut Vec<usize>,
dim: usize,
axis: usize,
modulo: f64,
) -> [usize; 3] {
let mut mod_point = mod_point;
let mut i12 = create_vertex(mod_point, i1, i2, v1, v2, vertices, dim, axis);
let mut i13 = create_vertex(mod_point, i1, i3, v1, v3, vertices, dim, axis);
indices.extend([i1, i12, i13]);
mod_point += modulo;
if v2 < v3 {
while mod_point < v2 {
indices.extend([i13, i12]);
i13 = create_vertex(mod_point, i1, i3, v1, v3, vertices, dim, axis);
indices.extend([i13, i13, i12]);
i12 = create_vertex(mod_point, i1, i2, v1, v2, vertices, dim, axis);
indices.push(i12);
mod_point += modulo;
}
indices.extend([i13, i12, i2]);
[i13, i2, i3]
} else {
while mod_point < v3 {
indices.extend([i13, i12]);
i13 = create_vertex(mod_point, i1, i3, v1, v3, vertices, dim, axis);
indices.extend([i13, i13, i12]);
i12 = create_vertex(mod_point, i1, i2, v1, v2, vertices, dim, axis);
indices.push(i12);
mod_point += modulo;
}
indices.extend([i13, i12, i3]);
[i3, i12, i2]
}
}
#[allow(clippy::too_many_arguments)]
fn split_left(
mod_point: f64,
i1: usize,
i2: usize,
i3: usize,
v1: f64,
v2: f64,
v3: f64,
vertices: &mut Vec<f64>,
indices: &mut Vec<usize>,
dim: usize,
axis: usize,
modulo: f64,
) -> [usize; 3] {
let mut mod_point = mod_point;
let mut i12 = create_vertex(mod_point, i1, i2, v1, v2, vertices, dim, axis);
let mut i13 = create_vertex(mod_point, i1, i3, v1, v3, vertices, dim, axis);
indices.extend([i1, i12, i13]);
mod_point -= modulo;
if v2 > v3 {
while mod_point > v2 {
indices.extend([i13, i12]);
i13 = create_vertex(mod_point, i1, i3, v1, v3, vertices, dim, axis);
indices.extend([i13, i13, i12]);
i12 = create_vertex(mod_point, i1, i2, v1, v2, vertices, dim, axis);
indices.push(i12);
mod_point -= modulo;
}
indices.extend([i13, i12, i2]);
[i13, i2, i3]
} else {
while mod_point > v3 {
indices.extend([i13, i12]);
i13 = create_vertex(mod_point, i1, i3, v1, v3, vertices, dim, axis);
indices.extend([i13, i13, i12]);
i12 = create_vertex(mod_point, i1, i2, v1, v2, vertices, dim, axis);
indices.push(i12);
mod_point -= modulo;
}
indices.extend([i13, i12, i3]);
[i3, i12, i2]
}
}
fn mod2(x: f64, n: f64) -> f64 {
((x % n) + n) % n
}
pub fn flatten<T: GetXY + GetZ>(data: &[Vec<T>]) -> (Vec<f64>, Vec<usize>, usize) {
let mut vertices = Vec::new();
let mut hole_indices = Vec::new();
let mut hole_index = 0;
let mut dim = 2; if !data.is_empty() && !data[0].is_empty() && data[0][0].z().is_some() {
dim = 3;
}
for (i, line) in data.iter().enumerate() {
for point in line {
vertices.push(point.x());
vertices.push(point.y());
if dim == 3 {
vertices.push(point.z().unwrap_or_default());
}
}
if i > 0 {
hole_index += data[i - 1].len();
hole_indices.push(hole_index);
}
}
(vertices, hole_indices, dim)
}
pub fn flatten_float(data: &[Vec<Vec<f64>>]) -> (Vec<f64>, Vec<usize>, usize) {
let mut vertices = Vec::new();
let mut hole_indices = Vec::new();
let mut hole_index = 0;
let mut dim = 2;
for (i, line) in data.iter().enumerate() {
for point in line {
vertices.push(point[0]);
vertices.push(point[1]);
if point.len() > 2 {
vertices.push(point[2]);
dim = 3; }
}
if i > 0 {
hole_index += data[i - 1].len();
hole_indices.push(hole_index);
}
}
(vertices, hole_indices, dim)
}
pub fn deviation(data: &[f64], hole_indices: &[usize], triangles: &[usize], dim: usize) -> f64 {
let has_holes = !hole_indices.is_empty();
let outer_len = if has_holes { hole_indices[0] * dim } else { data.len() };
let mut polygon_area = fabs(signed_area(data, 0, outer_len, dim));
if has_holes {
for i in 0..hole_indices.len() {
let start = hole_indices[i] * dim;
let end =
if i < hole_indices.len() - 1 { hole_indices[i + 1] * dim } else { data.len() };
polygon_area -= fabs(signed_area(data, start, end, dim));
}
}
let mut triangles_area = 0.;
let mut i = 0;
while i < triangles.len() {
let a = triangles[i] * dim;
let b = triangles[i + 1] * dim;
let c = triangles[i + 2] * dim;
triangles_area += fabs(
(data[a] - data[c]) * (data[b + 1] - data[a + 1])
- (data[a] - data[b]) * (data[c + 1] - data[a + 1]),
);
i += 3;
}
let zero = 0.;
if polygon_area == zero && triangles_area == zero {
zero
} else {
fabs((triangles_area - polygon_area) / polygon_area)
}
}