mod api;
mod connect;
mod dirty_regions;
mod geometry;
mod output;
mod priority_queue;
mod region;
mod sweep;
#[cfg(test)]
mod tests;
pub use api::TessellatorApi;
use geometry::{check_orientation, compute_normal, dot, is_valid_coord, long_axis};
use crate::dict::Dict;
use crate::geom::{vert_eq, Real};
use crate::mesh::{Mesh, VertIdx, E_HEAD, INVALID, V_HEAD};
use crate::sweep::ActiveRegion;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum WindingRule {
Odd,
NonZero,
Positive,
Negative,
AbsGeqTwo,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ElementType {
Polygons,
ConnectedPolygons,
BoundaryContours,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum TessOption {
ConstrainedDelaunayTriangulation,
ReverseContours,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum TessStatus {
Ok,
OutOfMemory,
InvalidInput,
}
pub const TESS_UNDEF: u32 = u32::MAX;
const MAX_VALID_COORD: Real = (1u64 << 50) as Real;
const MIN_VALID_COORD: Real = -MAX_VALID_COORD;
type RegionIdx = u32;
pub struct Tessellator {
mesh: Option<Mesh>,
pub status: TessStatus,
normal: [Real; 3],
s_unit: [Real; 3],
t_unit: [Real; 3],
bmin: [Real; 2],
bmax: [Real; 2],
process_cdt: bool,
reverse_contours: bool,
winding_rule: WindingRule,
dict: Dict,
intersection_verts: Vec<VertIdx>,
next_isect_handle: i32,
event: VertIdx,
event_s: Real,
event_t: Real,
regions: Vec<Option<ActiveRegion>>,
region_free: Vec<RegionIdx>,
pub out_vertices: Vec<Real>,
pub out_vertex_indices: Vec<u32>,
pub out_elements: Vec<u32>,
pub out_edge_flags: Vec<u8>,
pub out_vertex_count: usize,
pub out_element_count: usize,
vertex_index_counter: u32,
sorted_events: Vec<VertIdx>,
sorted_event_pos: usize,
sweep_event_num: u32,
trace_enabled: bool,
}
impl Tessellator {
pub fn new() -> Self {
Tessellator {
mesh: None,
status: TessStatus::Ok,
normal: [0.0; 3],
s_unit: [0.0; 3],
t_unit: [0.0; 3],
bmin: [0.0; 2],
bmax: [0.0; 2],
process_cdt: false,
reverse_contours: false,
winding_rule: WindingRule::Odd,
dict: Dict::new(),
intersection_verts: Vec::new(),
next_isect_handle: 0,
event: INVALID,
event_s: 0.0,
event_t: 0.0,
regions: Vec::new(),
region_free: Vec::new(),
out_vertices: Vec::new(),
out_vertex_indices: Vec::new(),
out_elements: Vec::new(),
out_edge_flags: Vec::new(),
out_vertex_count: 0,
out_element_count: 0,
vertex_index_counter: 0,
sorted_events: Vec::new(),
sorted_event_pos: 0,
sweep_event_num: 0,
trace_enabled: std::env::var("TESS_TRACE").is_ok(),
}
}
pub fn set_option(&mut self, option: TessOption, value: bool) {
match option {
TessOption::ConstrainedDelaunayTriangulation => self.process_cdt = value,
TessOption::ReverseContours => self.reverse_contours = value,
}
}
pub fn add_contour(&mut self, size: usize, vertices: &[Real]) {
if self.status != TessStatus::Ok {
return;
}
let size = size.min(3).max(2);
let count = vertices.len() / size;
if self.mesh.is_none() {
self.mesh = Some(Mesh::new());
}
let mut e = INVALID;
for i in 0..count {
let cx = vertices[i * size];
let cy = vertices[i * size + 1];
let cz = if size > 2 {
vertices[i * size + 2]
} else {
0.0
};
if !is_valid_coord(cx) || !is_valid_coord(cy) || (size > 2 && !is_valid_coord(cz)) {
self.status = TessStatus::InvalidInput;
return;
}
let mesh = self.mesh.as_mut().unwrap();
if e == INVALID {
let new_e = match mesh.make_edge() {
Some(v) => v,
None => {
self.status = TessStatus::OutOfMemory;
return;
}
};
e = new_e;
if !mesh.splice(e, e ^ 1) {
self.status = TessStatus::OutOfMemory;
return;
}
} else {
if mesh.split_edge(e).is_none() {
self.status = TessStatus::OutOfMemory;
return;
}
e = mesh.edges[e as usize].lnext;
}
let org = mesh.edges[e as usize].org;
mesh.verts[org as usize].coords[0] = cx;
mesh.verts[org as usize].coords[1] = cy;
mesh.verts[org as usize].coords[2] = cz;
mesh.verts[org as usize].idx = self.vertex_index_counter;
self.vertex_index_counter += 1;
let w = if self.reverse_contours { -1 } else { 1 };
mesh.edges[e as usize].winding = w;
mesh.edges[(e ^ 1) as usize].winding = -w;
}
}
pub fn tessellate(
&mut self,
winding_rule: WindingRule,
element_type: ElementType,
poly_size: usize,
vertex_size: usize,
normal: Option<[Real; 3]>,
) -> bool {
if self.status != TessStatus::Ok {
return false;
}
self.winding_rule = winding_rule;
self.out_vertices.clear();
self.out_vertex_indices.clear();
self.out_elements.clear();
self.out_edge_flags.clear();
self.out_vertex_count = 0;
self.out_element_count = 0;
self.normal = normal.unwrap_or([0.0, 0.0, 0.0]);
if self.mesh.is_none() {
self.mesh = Some(Mesh::new());
}
if !self.project_polygon() {
self.status = TessStatus::OutOfMemory;
return false;
}
if !self.compute_interior() {
if self.status == TessStatus::Ok {
self.status = TessStatus::OutOfMemory;
}
return false;
}
let vertex_size = vertex_size.min(3).max(2);
if element_type == ElementType::BoundaryContours {
self.output_contours(vertex_size);
} else {
self.output_polymesh(element_type, poly_size, vertex_size);
}
self.mesh = None;
self.status == TessStatus::Ok
}
pub fn vertex_count(&self) -> usize {
self.out_vertex_count
}
pub fn element_count(&self) -> usize {
self.out_element_count
}
pub fn vertices(&self) -> &[Real] {
&self.out_vertices
}
pub fn vertex_indices(&self) -> &[u32] {
&self.out_vertex_indices
}
pub fn elements(&self) -> &[u32] {
&self.out_elements
}
pub fn edge_flags(&self) -> &[u8] {
&self.out_edge_flags
}
pub fn get_status(&self) -> TessStatus {
self.status
}
fn project_polygon(&mut self) -> bool {
let mut norm = self.normal;
let mut computed_normal = false;
if norm[0] == 0.0 && norm[1] == 0.0 && norm[2] == 0.0 {
if let Some(ref m) = self.mesh {
compute_normal(m, &mut norm);
}
computed_normal = true;
}
let i = long_axis(&norm);
self.s_unit = [0.0; 3];
self.t_unit = [0.0; 3];
self.s_unit[(i + 1) % 3] = 1.0;
self.t_unit[(i + 2) % 3] = if norm[i] > 0.0 { 1.0 } else { -1.0 };
let su = self.s_unit;
let tu = self.t_unit;
if let Some(ref mut mesh) = self.mesh {
let mut v = mesh.verts[V_HEAD as usize].next;
while v != V_HEAD {
let c = mesh.verts[v as usize].coords;
mesh.verts[v as usize].s = dot(&c, &su);
mesh.verts[v as usize].t = dot(&c, &tu);
v = mesh.verts[v as usize].next;
}
if computed_normal {
check_orientation(mesh);
}
let mut first = true;
let mut v = mesh.verts[V_HEAD as usize].next;
while v != V_HEAD {
let vs = mesh.verts[v as usize].s;
let vt = mesh.verts[v as usize].t;
if first {
self.bmin = [vs, vt];
self.bmax = [vs, vt];
first = false;
} else {
if vs < self.bmin[0] {
self.bmin[0] = vs;
}
if vs > self.bmax[0] {
self.bmax[0] = vs;
}
if vt < self.bmin[1] {
self.bmin[1] = vt;
}
if vt > self.bmax[1] {
self.bmax[1] = vt;
}
}
v = mesh.verts[v as usize].next;
}
}
true
}
fn compute_interior(&mut self) -> bool {
self.sweep_event_num = 0;
if !self.remove_degenerate_edges() {
return false;
}
if !self.init_priority_queue() {
return false;
}
if !self.init_edge_dict() {
return false;
}
loop {
if self.pq_is_empty() {
break;
}
let v = self.pq_extract_min();
if v == INVALID {
break;
}
loop {
if self.pq_is_empty() {
break;
}
let next_v = self.pq_minimum();
if next_v == INVALID {
break;
}
let (v_s, v_t) = {
let mesh = self.mesh.as_ref().unwrap();
(mesh.verts[v as usize].s, mesh.verts[v as usize].t)
};
let (nv_s, nv_t) = {
let mesh = self.mesh.as_ref().unwrap();
(mesh.verts[next_v as usize].s, mesh.verts[next_v as usize].t)
};
if !vert_eq(v_s, v_t, nv_s, nv_t) {
break;
}
let next_v = self.pq_extract_min();
let an1 = self.mesh.as_ref().unwrap().verts[v as usize].an_edge;
let an2 = self.mesh.as_ref().unwrap().verts[next_v as usize].an_edge;
if an1 != INVALID && an2 != INVALID {
if !self.mesh.as_mut().unwrap().splice(an1, an2) {
return false;
}
}
}
self.event = v;
let (v_s, v_t) = {
let m = self.mesh.as_ref().unwrap();
(m.verts[v as usize].s, m.verts[v as usize].t)
};
self.event_s = v_s;
self.event_t = v_t;
if !self.sweep_event(v) {
return false;
}
}
self.done_edge_dict();
let trace = self.trace_enabled;
if let Some(ref mut mesh) = self.mesh {
if trace {
let mut inside = 0u32;
let mut outside = 0u32;
let mut f = mesh.faces[crate::mesh::F_HEAD as usize].next;
while f != crate::mesh::F_HEAD {
let an = mesh.faces[f as usize].an_edge;
let mut edge_count = 0u32;
if an != INVALID {
let mut e = an;
loop {
edge_count += 1;
e = mesh.edges[e as usize].lnext;
if e == an { break; }
if edge_count > 10000 { break; }
}
}
if mesh.faces[f as usize].inside {
inside += 1;
eprintln!("R FACE inside edges={}", edge_count);
} else {
outside += 1;
}
f = mesh.faces[f as usize].next;
}
eprintln!("R FACES inside={} outside={}", inside, outside);
}
if !mesh.tessellate_interior() {
return false;
}
if self.process_cdt {
mesh.refine_delaunay();
}
}
true
}
fn remove_degenerate_edges(&mut self) -> bool {
let mesh = match self.mesh.as_mut() {
Some(m) => m,
None => return true,
};
let mut e = mesh.edges[E_HEAD as usize].next;
while e != E_HEAD {
let mut e_next = mesh.edges[e as usize].next;
let mut e_lnext = mesh.edges[e as usize].lnext;
let org = mesh.edges[e as usize].org;
let dst = mesh.dst(e);
let valid = org != INVALID
&& dst != INVALID
&& (org as usize) < mesh.verts.len()
&& (dst as usize) < mesh.verts.len();
if valid {
let (os, ot) = (mesh.verts[org as usize].s, mesh.verts[org as usize].t);
let (ds, dt) = (mesh.verts[dst as usize].s, mesh.verts[dst as usize].t);
if vert_eq(os, ot, ds, dt) && mesh.edges[e_lnext as usize].lnext != e {
mesh.splice(e_lnext, e);
if !mesh.delete_edge(e) {
return false;
}
e = e_lnext;
e_lnext = mesh.edges[e as usize].lnext;
}
}
let e_lnext_lnext = mesh.edges[e_lnext as usize].lnext;
if e_lnext_lnext == e {
if e_lnext != e {
if e_lnext == e_next || e_lnext == (e_next ^ 1) {
e_next = mesh.edges[e_next as usize].next;
}
let w1 = mesh.edges[e_lnext as usize].winding;
let w2 = mesh.edges[(e_lnext ^ 1) as usize].winding;
mesh.edges[e as usize].winding += w1;
mesh.edges[(e ^ 1) as usize].winding += w2;
if !mesh.delete_edge(e_lnext) {
return false;
}
}
if e == e_next || e == (e_next ^ 1) {
e_next = mesh.edges[e_next as usize].next;
}
if !mesh.delete_edge(e) {
return false;
}
}
e = e_next;
}
true
}
}