diff --git a/Cargo.toml b/Cargo.toml index d38e1a8..9a7cd81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "earcut" -version = "0.3.5" +version = "0.3.6" edition = "2021" description = "A Rust port of the Earcut polygon triangulation library" authors = ["Taku Fukada ", "MIERUNE Inc. "] license = "ISC" repository = "https://github.com/MIERUNE/earcut-rs" -categories = ["graphics", "science"] +categories = ["graphics", "science", "no-std"] [dependencies] num-traits = "0.2" diff --git a/benches/benchmark.rs b/benches/benchmark.rs index a7743d0..c4188dc 100644 --- a/benches/benchmark.rs +++ b/benches/benchmark.rs @@ -35,8 +35,8 @@ fn bench(c: &mut Criterion) { let mut earcut = Earcut::new(); let mut triangles = Vec::new(); - c.bench_function("water", |b| { - let (data, hole_indices) = load_fixture("water"); + c.bench_function("bad-hole", |b| { + let (data, hole_indices) = load_fixture("bad-hole"); b.iter(|| { earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles); }) @@ -49,6 +49,34 @@ fn bench(c: &mut Criterion) { }) }); + c.bench_function("degenerate", |b| { + let (data, hole_indices) = load_fixture("degenerate"); + b.iter(|| { + earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles); + }) + }); + + c.bench_function("dude", |b| { + let (data, hole_indices) = load_fixture("dude"); + b.iter(|| { + earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles); + }) + }); + + c.bench_function("empty-square", |b| { + let (data, hole_indices) = load_fixture("empty-square"); + b.iter(|| { + earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles); + }) + }); + + c.bench_function("water", |b| { + let (data, hole_indices) = load_fixture("water"); + b.iter(|| { + earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles); + }) + }); + c.bench_function("water2", |b| { let (data, hole_indices) = load_fixture("water2"); b.iter(|| { @@ -70,34 +98,41 @@ fn bench(c: &mut Criterion) { }) }); - c.bench_function("water-huge", |b| { - let (data, hole_indices) = load_fixture("water-huge"); + c.bench_function("water4", |b| { + let (data, hole_indices) = load_fixture("water4"); b.iter(|| { earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles); - assert_eq!(triangles.len(), 5177 * 3) }) }); - c.bench_function("water-huge2", |b| { - let (data, hole_indices) = load_fixture("water-huge2"); + c.bench_function("water-huge", |b| { + let (data, hole_indices) = load_fixture("water-huge"); b.iter(|| { earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles); + assert_eq!(triangles.len(), 5177 * 3) }) }); - c.bench_function("rain", |b| { - let (data, hole_indices) = load_fixture("rain"); + c.bench_function("water-huge2", |b| { + let (data, hole_indices) = load_fixture("water-huge2"); b.iter(|| { earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles); }) }); - c.bench_function("hilbert", |b| { - let (data, hole_indices) = load_fixture("hilbert"); - b.iter(|| { - earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles); - }) - }); + // c.bench_function("rain", |b| { + // let (data, hole_indices) = load_fixture("rain"); + // b.iter(|| { + // earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles); + // }) + // }); + + // c.bench_function("hilbert", |b| { + // let (data, hole_indices) = load_fixture("hilbert"); + // b.iter(|| { + // earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles); + // }) + // }); } criterion_group!(benches, bench); diff --git a/examples/example.rs b/examples/example.rs index fac05de..a2aac7e 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -34,7 +34,7 @@ fn load_fixture(name: &str, num_triangles: usize, expected_deviation: f64) { } // check - assert!(triangles.len() == num_triangles); + assert!(triangles.len() == num_triangles * 3); if !triangles.is_empty() { assert!( deviation(vertices.iter().copied(), &hole_indices, &triangles) <= expected_deviation diff --git a/src/lib.rs b/src/lib.rs index 675d90c..66b1296 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,45 +42,45 @@ impl Index for usize { macro_rules! node { ($self:ident.$nodes:ident, $index:expr) => { - unsafe { $self.$nodes.get_unchecked($index) } + unsafe { $self.$nodes.get_unchecked($index as usize) } }; ($nodes:ident, $index:expr) => { - unsafe { $nodes.get_unchecked($index) } + unsafe { $nodes.get_unchecked($index as usize) } }; } macro_rules! node_mut { ($self:ident.$nodes:ident, $index:expr) => { - unsafe { $self.$nodes.get_unchecked_mut($index) } + unsafe { $self.$nodes.get_unchecked_mut($index as usize) } }; ($nodes:ident, $index:expr) => { - unsafe { $nodes.get_unchecked_mut($index) } + unsafe { $nodes.get_unchecked_mut($index as usize) } }; } struct Node { /// vertex index in coordinates array - i: usize, + i: u32, + /// z-order curve value + z: i32, /// vertex coordinates x x: T, /// vertex coordinates y y: T, /// previous vertex nodes in a polygon ring - prev_i: usize, + prev_i: u32, /// next vertex nodes in a polygon ring - next_i: usize, - /// z-order curve value - z: u32, + next_i: u32, /// previous nodes in z-order - prev_z_i: Option, + prev_z_i: Option, /// next nodes in z-order - next_z_i: Option, + next_z_i: Option, /// indicates whether this is a steiner point steiner: bool, } impl Node { - fn new(i: usize, x: T, y: T) -> Self { + fn new(i: u32, x: T, y: T) -> Self { Self { i, x, @@ -99,7 +99,7 @@ impl Node { pub struct Earcut { data: Vec, nodes: Vec>, - queue: Vec, + queue: Vec, } impl Default for Earcut { @@ -134,27 +134,26 @@ impl Earcut { hole_indices: &[N], triangles_out: &mut Vec, ) { + // initialize self.data.clear(); self.data.extend(data); - triangles_out.clear(); self.reset(self.data.len() / 2 * 3 / 2); - let has_holes = !hole_indices.is_empty(); let outer_len: usize = if has_holes { hole_indices[0].into_usize() * 2 } else { self.data.len() }; - let Some(mut outer_node_i) = self.linked_list(0, outer_len, true) else { + + // create nodes + let Some(mut outer_node_i) = self.linked_list(0, outer_len as u32, true) else { return; }; - let outer_node = node!(self.nodes, outer_node_i); if outer_node.next_i == outer_node.prev_i { return; } - if has_holes { outer_node_i = self.eliminate_holes(hole_indices, outer_node_i); } @@ -165,7 +164,7 @@ impl Earcut { // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox if self.data.len() > 80 * 2 { - let max_x = self.data[0..outer_len] + let max_x = self.data[2..outer_len] .iter() .step_by(2) .fold(self.data[0], |a, b| T::max(a, *b)); @@ -189,34 +188,36 @@ impl Earcut { } } - self.earcut_linked(outer_node_i, triangles_out, min_x, min_y, inv_size, 0); + earcut_linked( + &mut self.nodes, + outer_node_i, + triangles_out, + min_x, + min_y, + inv_size, + Pass::P0, + ); } /// create a circular doubly linked list from polygon points in the specified winding order - fn linked_list(&mut self, start: usize, end: usize, clockwise: bool) -> Option { - let mut last_i: Option = None; + fn linked_list(&mut self, start: u32, end: u32, clockwise: bool) -> Option { + let mut last_i: Option = None; if start >= end { return None; } + let iter = self.data[start as usize..end as usize] + .chunks_exact(2) + .enumerate(); if clockwise == (signed_area(&self.data, start, end) > T::zero()) { - for (i, (x, y)) in self.data[start..end - 1] - .iter() - .step_by(2) - .zip(self.data[(start + 1)..end].iter().step_by(2)) - .enumerate() - { - last_i = Some(insert_node(&mut self.nodes, start + i * 2, *x, *y, last_i)); + for (i, v) in iter { + let idx = start + i as u32 * 2; + last_i = Some(insert_node(&mut self.nodes, idx, v[0], v[1], last_i)); } } else { - for (i, (x, y)) in self.data[start..end - 1] - .iter() - .step_by(2) - .zip(self.data[(start + 1)..end].iter().step_by(2)) - .enumerate() - .rev() - { - last_i = Some(insert_node(&mut self.nodes, start + i * 2, *x, *y, last_i)); + for (i, v) in iter.rev() { + let idx = start + i as u32 * 2; + last_i = Some(insert_node(&mut self.nodes, idx, v[0], v[1], last_i)); } }; @@ -231,689 +232,684 @@ impl Earcut { last_i } - /// eliminate colinear or duplicate points - fn filter_points(&mut self, start_i: usize, end_i: Option) -> usize { - let mut end_i = end_i.unwrap_or(start_i); - - let mut p_i = start_i; - loop { - let p = node!(self.nodes, p_i); - let p_next = node!(self.nodes, p.next_i); - if !p.steiner - && (equals(p, p_next) || area(node!(self.nodes, p.prev_i), p, p_next).is_zero()) - { - let (prev_i, next_i) = remove_node(&mut self.nodes, p_i); - (p_i, end_i) = (prev_i, prev_i); - if p_i == next_i { - return end_i; - } + /// link every hole into the outer loop, producing a single-ring polygon without holes + fn eliminate_holes(&mut self, hole_indices: &[N], mut outer_node_i: u32) -> u32 { + self.queue.clear(); + for (i, hi) in hole_indices.iter().enumerate() { + let start = (*hi).into_usize() * 2; + let end = if i < hole_indices.len() - 1 { + hole_indices[i + 1].into_usize() * 2 } else { - p_i = p.next_i; - if p_i == end_i { - return end_i; - } + self.data.len() }; + if let Some(list_i) = self.linked_list(start as u32, end as u32, false) { + let list = &mut node_mut!(self.nodes, list_i); + if list_i == list.next_i { + list.steiner = true; + } + self.queue.push(get_leftmost(&self.nodes, list_i)) + } } - } - /// main ear slicing loop which triangulates a polygon (given as a linked list) - #[allow(clippy::too_many_arguments)] - fn earcut_linked( - &mut self, - ear_i: usize, - triangles: &mut Vec, - min_x: T, - min_y: T, - inv_size: T, - pass: usize, - ) { - let mut ear_i = ear_i; + self.queue.sort_unstable_by(|a, b| { + node!(self.nodes, *a) + .x + .partial_cmp(&node!(self.nodes, *b).x) + .unwrap_or(core::cmp::Ordering::Equal) + }); - // interlink polygon nodes in z-order - if pass == 0 && inv_size != T::zero() { - self.index_curve(ear_i, min_x, min_y, inv_size); + // process holes from left to right + for &q in &self.queue { + outer_node_i = eliminate_hole(&mut self.nodes, q, outer_node_i); } - let mut stop_i = ear_i; + outer_node_i + } +} - // iterate through ears, slicing them one by one - while node!(self.nodes, ear_i).prev_i != node!(self.nodes, ear_i).next_i { - let ear = node!(self.nodes, ear_i); - let prev_i = ear.prev_i; - let next_i = ear.next_i; +#[derive(Clone, Copy, PartialEq, Eq)] +enum Pass { + P0 = 0, + P1 = 1, + P2 = 2, +} - let is_ear = if inv_size != T::zero() { - self.is_ear_hashed(ear_i, min_x, min_y, inv_size) - } else { - self.is_ear(ear_i) - }; - if is_ear { - // cut off the triangle - triangles.extend([ - N::from_usize(node!(self.nodes, prev_i).i / 2), - N::from_usize(ear.i / 2), - N::from_usize(node!(self.nodes, next_i).i / 2), - ]); +/// main ear slicing loop which triangulates a polygon (given as a linked list) +#[allow(clippy::too_many_arguments)] +fn earcut_linked( + nodes: &mut Vec>, + ear_i: u32, + triangles: &mut Vec, + min_x: T, + min_y: T, + inv_size: T, + pass: Pass, +) { + let mut ear_i = ear_i; + + // interlink polygon nodes in z-order + if pass == Pass::P0 && inv_size != T::zero() { + index_curve(nodes, ear_i, min_x, min_y, inv_size); + } - remove_node(&mut self.nodes, ear_i); + let mut stop_i = ear_i; - // skipping the next vertex leads to less sliver triangles - let next_next_i = node!(self.nodes, next_i).next_i; - (ear_i, stop_i) = (next_next_i, next_next_i); + // iterate through ears, slicing them one by one + while node!(nodes, ear_i).prev_i != node!(nodes, ear_i).next_i { + let ear = node!(nodes, ear_i); + let prev_i = ear.prev_i; + let next_i = ear.next_i; - continue; - } + let is_ear = if inv_size != T::zero() { + is_ear_hashed(nodes, ear_i, min_x, min_y, inv_size) + } else { + is_ear(nodes, ear_i) + }; + if is_ear { + // cut off the triangle + triangles.extend([ + N::from_usize(node!(nodes, prev_i).i as usize / 2), + N::from_usize(ear.i as usize / 2), + N::from_usize(node!(nodes, next_i).i as usize / 2), + ]); - ear_i = next_i; - - // if we looped through the whole remaining polygon and can't find any more ears - if ear_i == stop_i { - if pass == 0 { - // try filtering points and slicing again - ear_i = self.filter_points(ear_i, None); - self.earcut_linked(ear_i, triangles, min_x, min_y, inv_size, 1); - } else if pass == 1 { - // if this didn't work, try curing all small self-intersections locally - let filtered = self.filter_points(ear_i, None); - ear_i = self.cure_local_intersections(filtered, triangles); - self.earcut_linked(ear_i, triangles, min_x, min_y, inv_size, 2); - } else if pass == 2 { - // as a last resort, try splitting the remaining polygon into two - self.split_earcut(ear_i, triangles, min_x, min_y, inv_size); - } - return; - } - } - } + remove_node(nodes, ear_i); - /// check whether a polygon node forms a valid ear with adjacent nodes - fn is_ear(&self, ear_i: usize) -> bool { - let b = node!(self.nodes, ear_i); - let a = node!(self.nodes, b.prev_i); - let c = node!(self.nodes, b.next_i); + // skipping the next vertex leads to less sliver triangles + let next_next_i = node!(nodes, next_i).next_i; + (ear_i, stop_i) = (next_next_i, next_next_i); - if area(a, b, c) >= T::zero() { - // reflex, can't be an ear - return false; + continue; } - // now make sure we don't have other points inside the potential ear - - // triangle bbox - let x0 = a.x.min(b.x.min(c.x)); - let y0 = a.y.min(b.y.min(c.y)); - let x1 = a.x.max(b.x.max(c.x)); - let y1 = a.y.max(b.y.max(c.y)); - - let mut p = node!(self.nodes, c.next_i); - let mut p_prev = node!(self.nodes, p.prev_i); - while !ptr::eq(p, a) { - let p_next = node!(self.nodes, p.next_i); - if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1) - && point_in_triangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) - && area(p_prev, p, p_next) >= T::zero() - { - return false; + ear_i = next_i; + + // if we looped through the whole remaining polygon and can't find any more ears + if ear_i == stop_i { + if pass == Pass::P0 { + // try filtering points and slicing again + ear_i = filter_points(nodes, ear_i, None); + earcut_linked(nodes, ear_i, triangles, min_x, min_y, inv_size, Pass::P1); + } else if pass == Pass::P1 { + // if this didn't work, try curing all small self-intersections locally + let filtered = filter_points(nodes, ear_i, None); + ear_i = cure_local_intersections(nodes, filtered, triangles); + earcut_linked(nodes, ear_i, triangles, min_x, min_y, inv_size, Pass::P2); + } else if pass == Pass::P2 { + // as a last resort, try splitting the remaining polygon into two + split_earcut(nodes, ear_i, triangles, min_x, min_y, inv_size); } - (p_prev, p) = (p, p_next); + return; } - true } +} + +/// check whether a polygon node forms a valid ear with adjacent nodes +fn is_ear(nodes: &[Node], ear_i: u32) -> bool { + let b = node!(nodes, ear_i); + let a = node!(nodes, b.prev_i); + let c = node!(nodes, b.next_i); - fn is_ear_hashed(&self, ear_i: usize, min_x: T, min_y: T, inv_size: T) -> bool { - let b = node!(self.nodes, ear_i); - let a = node!(self.nodes, b.prev_i); - let c = node!(self.nodes, b.next_i); + if area(a, b, c) >= T::zero() { + // reflex, can't be an ear + return false; + } - if area(a, b, c) >= T::zero() { - // reflex, can't be an ear + // now make sure we don't have other points inside the potential ear + + // triangle bbox + let x0 = a.x.min(b.x.min(c.x)); + let y0 = a.y.min(b.y.min(c.y)); + let x1 = a.x.max(b.x.max(c.x)); + let y1 = a.y.max(b.y.max(c.y)); + + let mut p = node!(nodes, c.next_i); + let mut p_prev = node!(nodes, p.prev_i); + while !ptr::eq(p, a) { + let p_next = node!(nodes, p.next_i); + if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1) + && point_in_triangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) + && area(p_prev, p, p_next) >= T::zero() + { return false; } + (p_prev, p) = (p, p_next); + } + true +} - // triangle bbox - let x0 = a.x.min(b.x.min(c.x)); - let y0 = a.y.min(b.y.min(c.y)); - let x1 = a.x.max(b.x.max(c.x)); - let y1 = a.y.max(b.y.max(c.y)); - - // z-order range for the current triangle bbox; - let min_z = z_order(x0, y0, min_x, min_y, inv_size); - let max_z = z_order(x1, y1, min_x, min_y, inv_size); - - let ear = node!(self.nodes, ear_i); - let mut o_p = ear.prev_z_i.map(|i| node!(self.nodes, i)); - let mut o_n = ear.next_z_i.map(|i| node!(self.nodes, i)); - - let ear_prev = node!(self.nodes, ear.prev_i); - let ear_next = node!(self.nodes, ear.next_i); +fn is_ear_hashed(nodes: &[Node], ear_i: u32, min_x: T, min_y: T, inv_size: T) -> bool { + let b = node!(nodes, ear_i); + let a = node!(nodes, b.prev_i); + let c = node!(nodes, b.next_i); - // look for points inside the triangle in both directions - loop { - let Some(p) = o_p else { break }; - if p.z < min_z { - break; - }; - let Some(n) = o_n else { break }; - if n.z > max_z { - break; - }; - - if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1) - && (!ptr::eq(p, a) && !ptr::eq(p, c)) - && point_in_triangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) - && area(node!(self.nodes, p.prev_i), p, node!(self.nodes, p.next_i)) >= T::zero() - { - return false; - } - o_p = p.prev_z_i.map(|i| node!(self.nodes, i)); + if area(a, b, c) >= T::zero() { + // reflex, can't be an ear + return false; + } - if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1) - && (!ptr::eq(n, a) && !ptr::eq(n, c)) - && point_in_triangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) - && area(node!(self.nodes, n.prev_i), n, node!(self.nodes, n.next_i)) >= T::zero() - { - return false; - } - o_n = p.next_z_i.map(|i| node!(self.nodes, i)); - } + // triangle bbox + let x0 = a.x.min(b.x.min(c.x)); + let y0 = a.y.min(b.y.min(c.y)); + let x1 = a.x.max(b.x.max(c.x)); + let y1 = a.y.max(b.y.max(c.y)); + + // z-order range for the current triangle bbox; + let min_z = z_order(x0, y0, min_x, min_y, inv_size); + let max_z = z_order(x1, y1, min_x, min_y, inv_size); + + let ear = node!(nodes, ear_i); + let mut o_p = ear.prev_z_i.map(|i| node!(nodes, i as usize)); + let mut o_n = ear.next_z_i.map(|i| node!(nodes, i)); + + // NOTE: Disabled this part because it rather degrades performance. + // + // look for points inside the triangle in both directions + loop { + let Some(p) = o_p else { break }; + if p.z < min_z { + break; + }; + let Some(n) = o_n else { break }; + if n.z > max_z { + break; + }; - // look for remaining points in decreasing z-order - while let Some(p) = o_p { - if p.z < min_z { - break; - }; - if (!ptr::eq(p, ear_prev) && !ptr::eq(p, ear_next)) - && point_in_triangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) - && area(node!(self.nodes, p.prev_i), p, node!(self.nodes, p.next_i)) >= T::zero() - { - return false; - } - o_p = p.prev_z_i.map(|i| node!(self.nodes, i)); + if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1) + && (!ptr::eq(p, a) && !ptr::eq(p, c)) + && point_in_triangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) + && area(node!(nodes, p.prev_i), p, node!(nodes, p.next_i)) >= T::zero() + { + return false; } + o_p = p.prev_z_i.map(|i| node!(nodes, i)); - // look for remaining points in increasing z-order - while let Some(n) = o_n { - if n.z > max_z { - break; - }; - if (!ptr::eq(n, ear_prev) && !ptr::eq(n, ear_next)) - && point_in_triangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) - && area(node!(self.nodes, n.prev_i), n, node!(self.nodes, n.next_i)) >= T::zero() - { - return false; - } - o_n = n.next_z_i.map(|i| node!(self.nodes, i)); + if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1) + && (!ptr::eq(n, a) && !ptr::eq(n, c)) + && point_in_triangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) + && area(node!(nodes, n.prev_i), n, node!(nodes, n.next_i)) >= T::zero() + { + return false; } - - true + o_n = n.next_z_i.map(|i| node!(nodes, i)); } - /// go through all polygon nodes and cure small local self-intersections - fn cure_local_intersections( - &mut self, - mut start_i: usize, - triangles: &mut Vec, - ) -> usize { - let mut p_i = start_i; - loop { - let p_prev_i = node!(self.nodes, p_i).prev_i; - let p_next_i = node!(self.nodes, p_i).next_i; - let a_i = p_prev_i; - let p_next = node!(self.nodes, p_next_i); - let b_i = p_next.next_i; - let a = node!(self.nodes, a_i); - let b = node!(self.nodes, b_i); - let p = node!(self.nodes, p_i); - - if !equals(a, b) - && self.intersects(a, p, p_next, b) - && self.locally_inside(a, b) - && self.locally_inside(b, a) - { - triangles.extend([ - N::from_usize(a.i / 2), - N::from_usize(p.i / 2), - N::from_usize(b.i / 2), - ]); - - remove_node(&mut self.nodes, p_i); - remove_node(&mut self.nodes, p_next_i); - - (p_i, start_i) = (b_i, b_i); - } - - p_i = node!(self.nodes, p_i).next_i; - if p_i == start_i { - return self.filter_points(p_i, None); - } + // look for remaining points in decreasing z-order + while let Some(p) = o_p { + if p.z < min_z { + break; + }; + if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1) + && (!ptr::eq(p, a) && !ptr::eq(p, c)) + && point_in_triangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) + && area(node!(nodes, p.prev_i), p, node!(nodes, p.next_i)) >= T::zero() + { + return false; } + o_p = p.prev_z_i.map(|i| node!(nodes, i)); } - /// try splitting polygon into two and triangulate them independently - fn split_earcut( - &mut self, - start_i: usize, - triangles: &mut Vec, - min_x: T, - min_y: T, - inv_size: T, - ) { - // look for a valid diagonal that divides the polygon into two - let mut a_i = start_i; - loop { - let ai = node!(self.nodes, a_i).i; - let a_prev_i = node!(self.nodes, a_i).prev_i; - let a_next_i = node!(self.nodes, a_i).next_i; - let mut b_i = (node!(self.nodes, a_next_i)).next_i; - - while b_i != a_prev_i { - let b = node!(self.nodes, b_i); - if ai != b.i && self.is_valid_diagonal(node!(self.nodes, a_i), b) { - // split the polygon in two by the diagonal - let mut c_i = self.split_polygon(a_i, b_i); - - // filter colinear points around the cuts - a_i = self.filter_points(a_i, Some(node!(self.nodes, a_i).next_i)); - c_i = self.filter_points(c_i, Some(node!(self.nodes, c_i).next_i)); - - // run earcut on each half - self.earcut_linked(a_i, triangles, min_x, min_y, inv_size, 0); - self.earcut_linked(c_i, triangles, min_x, min_y, inv_size, 0); - return; - } - b_i = b.next_i; - } - - a_i = node!(self.nodes, a_i).next_i; - if a_i == start_i { - return; - } + // look for remaining points in increasing z-order + while let Some(n) = o_n { + if n.z > max_z { + break; + }; + if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1) + && (!ptr::eq(n, a) && !ptr::eq(n, c)) + && point_in_triangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) + && area(node!(nodes, n.prev_i), n, node!(nodes, n.next_i)) >= T::zero() + { + return false; } + o_n = n.next_z_i.map(|i| node!(nodes, i)); } - /// link every hole into the outer loop, producing a single-ring polygon without holes - fn eliminate_holes(&mut self, hole_indices: &[N], mut outer_node_i: usize) -> usize { - self.queue.clear(); - let len = hole_indices.len(); - for (i, hi) in hole_indices.iter().enumerate() { - let start = (*hi).into_usize() * 2; - let end = if i < len - 1 { - hole_indices[i + 1].into_usize() * 2 - } else { - self.data.len() - }; - if let Some(list_i) = self.linked_list(start, end, false) { - let list = &mut node_mut!(self.nodes, list_i); - if list_i == list.next_i { - list.steiner = true; - } - self.queue.push(self.get_leftmost(list_i)) - } - } - - self.queue.sort_unstable_by(|a, b| { - node!(self.nodes, *a) - .x - .partial_cmp(&node!(self.nodes, *b).x) - .unwrap_or(core::cmp::Ordering::Equal) - }); + true +} - // process holes from left to right - for i in 0..self.queue.len() { - outer_node_i = self.eliminate_hole(self.queue[i], outer_node_i); +/// go through all polygon nodes and cure small local self-intersections +fn cure_local_intersections( + nodes: &mut [Node], + mut start_i: u32, + triangles: &mut Vec, +) -> u32 { + let mut p_i = start_i; + loop { + let p = node!(nodes, p_i); + let p_next_i = p.next_i; + let p_next = node!(nodes, p_next_i); + let b_i = p_next.next_i; + let a = node!(nodes, p.prev_i); + let b = node!(nodes, b_i); + + if !equals(a, b) + && intersects(a, p, p_next, b) + && locally_inside(nodes, a, b) + && locally_inside(nodes, b, a) + { + triangles.extend([ + N::from_usize(a.i as usize / 2), + N::from_usize(p.i as usize / 2), + N::from_usize(b.i as usize / 2), + ]); + + remove_node(nodes, p_i); + remove_node(nodes, p_next_i); + + (p_i, start_i) = (b_i, b_i); } - outer_node_i - } - - /// find a bridge between vertices that connects hole with an outer ring and and link it - fn eliminate_hole(&mut self, hole_i: usize, outer_node_i: usize) -> usize { - let Some(bridge_i) = self.find_hole_bridge(node!(self.nodes, hole_i), outer_node_i) else { - return outer_node_i; - }; - let bridge_reverse_i = self.split_polygon(bridge_i, hole_i); - - // filter collinear points around the cuts - self.filter_points( - bridge_reverse_i, - Some(node!(self.nodes, bridge_reverse_i).next_i), - ); - self.filter_points(bridge_i, Some(node!(self.nodes, bridge_i).next_i)) + p_i = node!(nodes, p_i).next_i; + if p_i == start_i { + return filter_points(nodes, p_i, None); + } } +} - /// dimavid Eberly's algorithm for finding a bridge between hole and outer polygon - fn find_hole_bridge(&self, hole: &Node, outer_node_i: usize) -> Option { - let mut p_i = outer_node_i; - let mut qx = T::neg_infinity(); - let mut m_i: Option = None; - - // find a segment intersected by a ray from the hole's leftmost point to the left; - // segment's endpoint with lesser x will be potential connection point - loop { - let p = node!(self.nodes, p_i); - let p_next_i = p.next_i; - let p_next = node!(self.nodes, p_next_i); - if hole.y <= p.y && hole.y >= p_next.y && p_next.y != p.y { - let x = p.x + (hole.y - p.y) * (p_next.x - p.x) / (p_next.y - p.y); - if x <= hole.x && x > qx { - qx = x; - m_i = Some(if p.x < p_next.x { p_i } else { p_next_i }); - if x == hole.x { - // hole touches outer segment; pick leftmost endpoint - return m_i; - } - } - } - p_i = p_next_i; - if p_i == outer_node_i { - break; +/// try splitting polygon into two and triangulate them independently +fn split_earcut( + nodes: &mut Vec>, + start_i: u32, + triangles: &mut Vec, + min_x: T, + min_y: T, + inv_size: T, +) { + // look for a valid diagonal that divides the polygon into two + let mut ai = start_i; + loop { + let a = node!(nodes, ai); + let a_i = a.i; + let a_prev_i = a.prev_i; + let a_next_i = a.next_i; + let mut bi = (node!(nodes, a_next_i)).next_i; + + while bi != a_prev_i { + let b = node!(nodes, bi); + if a_i != b.i && is_valid_diagonal(nodes, a, b) { + // split the polygon in two by the diagonal + let mut ci = split_polygon(nodes, ai, bi); + + // filter colinear points around the cuts + let end_i = Some(node!(nodes, ai).next_i); + ai = filter_points(nodes, ai, end_i); + let end_i = Some(node!(nodes, ci).next_i); + ci = filter_points(nodes, ci, end_i); + + // run earcut on each half + earcut_linked(nodes, ai, triangles, min_x, min_y, inv_size, Pass::P0); + earcut_linked(nodes, ci, triangles, min_x, min_y, inv_size, Pass::P0); + return; } + bi = b.next_i; } - let mut m_i = m_i?; - - // look for points inside the triangle of hole point, segment intersection and endpoint; - // if there are no points found, we have a valid connection; - // otherwise choose the point of the minimum angle with the ray as connection point - - let stop_i = m_i; - let Node { x: mx, y: my, .. } = *node!(self.nodes, m_i); // must copy - let mut tan_min = T::infinity(); - - p_i = m_i; - let mut p = node!(self.nodes, p_i); - let mut m = p; - - loop { - if (hole.x >= p.x && p.x >= mx && hole.x != p.x) - && point_in_triangle( - if hole.y < my { hole.x } else { qx }, - hole.y, - mx, - my, - if hole.y < my { qx } else { hole.x }, - hole.y, - p.x, - p.y, - ) - { - let tan = (hole.y - p.y).abs() / (hole.x - p.x); - if self.locally_inside(p, hole) - && (tan < tan_min - || (tan == tan_min - && (p.x > m.x || p.x == m.x && self.sector_contains_sector(m, p)))) - { - (m_i, m) = (p_i, p); - tan_min = tan; - } - } - - p_i = p.next_i; - if p_i == stop_i { - return Some(m_i); - } - p = node!(self.nodes, p_i); + ai = node!(nodes, ai).next_i; + if ai == start_i { + return; } } +} - /// whether sector in vertex m contains sector in vertex p in the same coordinates - fn sector_contains_sector(&self, m: &Node, p: &Node) -> bool { - area(node!(self.nodes, m.prev_i), m, node!(self.nodes, p.prev_i)) < T::zero() - && area(node!(self.nodes, p.next_i), m, node!(self.nodes, m.next_i)) < T::zero() - } - - /// interlink polygon nodes in z-order - fn index_curve(&mut self, start_i: usize, min_x: T, min_y: T, inv_size: T) { - let mut p_i = start_i; +/// interlink polygon nodes in z-order +fn index_curve(nodes: &mut [Node], start_i: u32, min_x: T, min_y: T, inv_size: T) { + let mut p_i = start_i; - loop { - let p = node_mut!(self.nodes, p_i); - if p.z == 0 { - p.z = z_order(p.x, p.y, min_x, min_y, inv_size); - } - p.prev_z_i = Some(p.prev_i); - p.next_z_i = Some(p.next_i); - p_i = p.next_i; - if p_i == start_i { - break; - } + loop { + let p = node_mut!(nodes, p_i); + if p.z == 0 { + p.z = z_order(p.x, p.y, min_x, min_y, inv_size); + } + p.prev_z_i = Some(p.prev_i); + p.next_z_i = Some(p.next_i); + p_i = p.next_i; + if p_i == start_i { + break; } - - let p_prev_z_i = node!(self.nodes, p_i).prev_z_i.unwrap(); - node_mut!(self.nodes, p_prev_z_i).next_z_i = None; - node_mut!(self.nodes, p_i).prev_z_i = None; - self.sort_linked(p_i); } - /// Simon Tatham's linked list merge sort algorithm - /// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html - fn sort_linked(&mut self, list_i: usize) { - let mut in_size: usize = 1; - let mut list_i = Some(list_i); - - loop { - let mut p_i = list_i; - list_i = None; - let mut tail_i: Option = None; - let mut num_merges = 0; - - while let Some(pp) = p_i { - num_merges += 1; - let mut q_i = node!(self.nodes, pp).next_z_i; - let mut p_size: usize = 1; - for _ in 1..in_size { - if let Some(i) = q_i { - p_size += 1; - q_i = node!(self.nodes, i).next_z_i; - } else { - q_i = None; - break; - } + let p_prev_z_i = node!(nodes, p_i).prev_z_i.unwrap(); + node_mut!(nodes, p_prev_z_i).next_z_i = None; + node_mut!(nodes, p_i).prev_z_i = None; + sort_linked(nodes, p_i); +} + +/// Simon Tatham's linked list merge sort algorithm +/// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html +fn sort_linked(nodes: &mut [Node], list_i: u32) { + let mut in_size: usize = 1; + let mut list_i = Some(list_i); + + loop { + let mut p_i = list_i; + list_i = None; + let mut tail_i: Option = None; + let mut num_merges = 0; + + while let Some(pp) = p_i { + num_merges += 1; + let mut q_i = node!(nodes, pp).next_z_i; + let mut p_size: u32 = 1; + for _ in 1..in_size { + if let Some(i) = q_i { + p_size += 1; + q_i = node!(nodes, i).next_z_i; + } else { + q_i = None; + break; } - let mut q_size = in_size; - - while p_size > 0 || (q_size > 0 && q_i.is_some()) { - let (e_i, e) = if p_size == 0 { - q_size -= 1; - let e_i = q_i.unwrap(); - let e = node_mut!(self.nodes, e_i); - q_i = e.next_z_i; - (e_i, e) - } else if q_size == 0 { - p_size -= 1; - let e_i = p_i.unwrap(); - let e = node_mut!(self.nodes, e_i); - p_i = e.next_z_i; - (e_i, e) - } else { - let p_i_s = p_i.unwrap(); - if let Some(q_i_s) = q_i { - if node!(self.nodes, p_i_s).z <= node!(self.nodes, q_i_s).z { - p_size -= 1; - let e_i = p_i_s; - let e = node_mut!(self.nodes, p_i_s); - p_i = e.next_z_i; - (e_i, e) - } else { - q_size -= 1; - let e_i = q_i_s; - let e = node_mut!(self.nodes, q_i_s); - q_i = e.next_z_i; - (e_i, e) - } - } else { + } + let mut q_size = in_size; + + while p_size > 0 || (q_size > 0 && q_i.is_some()) { + let (e_i, e) = if p_size == 0 { + q_size -= 1; + let e_i = q_i.unwrap(); + let e = node_mut!(nodes, e_i); + q_i = e.next_z_i; + (e_i, e) + } else if q_size == 0 { + p_size -= 1; + let e_i = p_i.unwrap(); + let e = node_mut!(nodes, e_i); + p_i = e.next_z_i; + (e_i, e) + } else { + let p_i_s = p_i.unwrap(); + if let Some(q_i_s) = q_i { + if node!(nodes, p_i_s).z <= node!(nodes, q_i_s).z { p_size -= 1; - let e_i = p_i_s; - let e = node_mut!(self.nodes, e_i); + let e = node_mut!(nodes, p_i_s); p_i = e.next_z_i; - (e_i, e) + (p_i_s, e) + } else { + q_size -= 1; + let e = node_mut!(nodes, q_i_s); + q_i = e.next_z_i; + (q_i_s, e) } - }; - - e.prev_z_i = tail_i; - - if let Some(tail_i) = tail_i { - node_mut!(self.nodes, tail_i).next_z_i = Some(e_i); } else { - list_i = Some(e_i); + p_size -= 1; + let e = node_mut!(nodes, p_i_s); + p_i = e.next_z_i; + (p_i_s, e) } - tail_i = Some(e_i); - } + }; - p_i = q_i; - } + e.prev_z_i = tail_i; - node_mut!(self.nodes, tail_i.unwrap()).next_z_i = None; - if num_merges <= 1 { - break; + if let Some(tail_i) = tail_i { + node_mut!(nodes, tail_i).next_z_i = Some(e_i); + } else { + list_i = Some(e_i); + } + tail_i = Some(e_i); } - in_size *= 2; + + p_i = q_i; + } + + node_mut!(nodes, tail_i.unwrap()).next_z_i = None; + if num_merges <= 1 { + break; } + in_size *= 2; } +} - /// find the leftmost node of a polygon ring - fn get_leftmost(&self, start_i: usize) -> usize { - let mut p_i = start_i; - let mut leftmost_i = start_i; - let mut p = node!(self.nodes, p_i); - let mut leftmost = p; +/// find the leftmost node of a polygon ring +fn get_leftmost(nodes: &[Node], start_i: u32) -> u32 { + let mut p_i = start_i; + let mut leftmost_i = start_i; + let mut p = node!(nodes, p_i); + let mut leftmost = p; - loop { - if p.x < leftmost.x || (p.x == leftmost.x && p.y < leftmost.y) { - (leftmost_i, leftmost) = (p_i, p); - } - p_i = p.next_i; - if p_i == start_i { - return leftmost_i; - } - p = node!(self.nodes, p_i); + loop { + if p.x < leftmost.x || (p.x == leftmost.x && p.y < leftmost.y) { + (leftmost_i, leftmost) = (p_i, p); + } + p_i = p.next_i; + if p_i == start_i { + return leftmost_i; } + p = node!(nodes, p_i); } +} - /// check if a diagonal between two polygon nodes is valid (lies in polygon interior) - fn is_valid_diagonal(&self, a: &Node, b: &Node) -> bool { - let a_next = node!(self.nodes, a.next_i); - let a_prev = node!(self.nodes, a.prev_i); - let b_next = node!(self.nodes, b.next_i); - let b_prev = node!(self.nodes, b.prev_i); - // dones't intersect other edges - (a_next.i != b.i && a_prev.i != b.i && !self.intersects_polygon(a, b)) +/// check if a diagonal between two polygon nodes is valid (lies in polygon interior) +fn is_valid_diagonal(nodes: &[Node], a: &Node, b: &Node) -> bool { + let a_next = node!(nodes, a.next_i); + let a_prev = node!(nodes, a.prev_i); + let b_next = node!(nodes, b.next_i); + let b_prev = node!(nodes, b.prev_i); + // dones't intersect other edges + (a_next.i != b.i && a_prev.i != b.i && !intersects_polygon(nodes, a, b)) // locally visible - && ((self.locally_inside(a, b) && self.locally_inside(b, a) && self.middle_inside(a, b)) + && ((locally_inside(nodes, a, b) && locally_inside(nodes, b, a) && middle_inside(nodes, a, b)) // does not create opposite-facing sectors && (area(a_prev, a, b_prev) != T::zero() || area(a, b_prev, b) != T::zero()) // special zero-length case || equals(a, b) && area(a_prev, a, a_next) > T::zero() && area(b_prev, b, b_next) > T::zero()) - } +} + +/// check if two segments intersect +fn intersects(p1: &Node, q1: &Node, p2: &Node, q2: &Node) -> bool { + let o1 = sign(area(p1, q1, p2)); + let o2 = sign(area(p1, q1, q2)); + let o3 = sign(area(p2, q2, p1)); + let o4 = sign(area(p2, q2, q1)); + (o1 != o2 && o3 != o4) // general case + || (o1 == 0 && on_segment(p1, p2, q1)) // p1, q1 and p2 are collinear and p2 lies on p1q1 + || (o2 == 0 && on_segment(p1, q2, q1)) // p1, q1 and q2 are collinear and q2 lies on p1q1 + || (o3 == 0 && on_segment(p2, p1, q2)) // p2, q2 and p1 are collinear and p1 lies on p2q2 + || (o4 == 0 && on_segment(p2, q1, q2)) // p2, q2 and q1 are collinear and q1 lies on p2q2 +} - /// check if two segments intersect - fn intersects(&self, p1: &Node, q1: &Node, p2: &Node, q2: &Node) -> bool { - let o1 = sign(area(p1, q1, p2)); - let o2 = sign(area(p1, q1, q2)); - let o3 = sign(area(p2, q2, p1)); - let o4 = sign(area(p2, q2, q1)); - (o1 != o2 && o3 != o4) // general case - || (o1 == 0 && on_segment(p1, p2, q1)) // p1, q1 and p2 are collinear and p2 lies on p1q1 - || (o2 == 0 && on_segment(p1, q2, q1)) // p1, q1 and q2 are collinear and q2 lies on p1q1 - || (o3 == 0 && on_segment(p2, p1, q2)) // p2, q2 and p1 are collinear and p1 lies on p2q2 - || (o4 == 0 && on_segment(p2, q1, q2)) // p2, q2 and q1 are collinear and q1 lies on p2q2 +/// check if a polygon diagonal intersects any polygon segments +fn intersects_polygon(nodes: &[Node], a: &Node, b: &Node) -> bool { + let mut p = a; + loop { + let p_next = node!(nodes, p.next_i); + if (p.i != a.i && p.i != b.i && p_next.i != a.i && p_next.i != b.i) + && intersects(p, p_next, a, b) + { + return true; + } + p = p_next; + if ptr::eq(p, a) { + return false; + } } +} - /// check if a polygon diagonal intersects any polygon segments - fn intersects_polygon(&self, a: &Node, b: &Node) -> bool { - let mut p = a; - loop { - let p_next = node!(self.nodes, p.next_i); - if (p.i != a.i && p.i != b.i && p_next.i != a.i && p_next.i != b.i) - && self.intersects(p, p_next, a, b) - { - return true; - } - p = p_next; - if ptr::eq(p, a) { - return false; - } +/// check if the middle point of a polygon diagonal is inside the polygon +fn middle_inside(nodes: &[Node], a: &Node, b: &Node) -> bool { + let mut p = a; + let mut inside = false; + let two = T::one() + T::one(); + let (px, py) = ((a.x + b.x) / two, (a.y + b.y) / two); + loop { + let p_next = node!(nodes, p.next_i); + inside ^= (p.y > py) != (p_next.y > py) + && p_next.y != p.y + && (px < (p_next.x - p.x) * (py - p.y) / (p_next.y - p.y) + p.x); + p = p_next; + if ptr::eq(p, a) { + return inside; } } +} - /// check if a polygon diagonal is locally inside the polygon - fn locally_inside(&self, a: &Node, b: &Node) -> bool { - let a_prev = node!(self.nodes, a.prev_i); - let a_next = node!(self.nodes, a.next_i); - if area(a_prev, a, a_next) < T::zero() { - area(a, b, a_next) >= T::zero() && area(a, a_prev, b) >= T::zero() - } else { - area(a, b, a_prev) < T::zero() || area(a, a_next, b) < T::zero() +/// find a bridge between vertices that connects hole with an outer ring and and link it +fn eliminate_hole(nodes: &mut Vec>, hole_i: u32, outer_node_i: u32) -> u32 { + let Some(bridge_i) = find_hole_bridge(nodes, node!(nodes, hole_i), outer_node_i) else { + return outer_node_i; + }; + let bridge_reverse_i = split_polygon(nodes, bridge_i, hole_i); + + // filter collinear points around the cuts + let end_i = Some(node!(nodes, bridge_reverse_i).next_i); + filter_points(nodes, bridge_reverse_i, end_i); + let end_i = Some(node!(nodes, bridge_i).next_i); + filter_points(nodes, bridge_i, end_i) +} + +/// check if a polygon diagonal is locally inside the polygon +fn locally_inside(nodes: &[Node], a: &Node, b: &Node) -> bool { + let a_prev = node!(nodes, a.prev_i); + let a_next = node!(nodes, a.next_i); + if area(a_prev, a, a_next) < T::zero() { + area(a, b, a_next) >= T::zero() && area(a, a_prev, b) >= T::zero() + } else { + area(a, b, a_prev) < T::zero() || area(a, a_next, b) < T::zero() + } +} + +/// dimavid Eberly's algorithm for finding a bridge between hole and outer polygon +fn find_hole_bridge(nodes: &[Node], hole: &Node, outer_node_i: u32) -> Option { + let mut p_i = outer_node_i; + let mut qx = T::neg_infinity(); + let mut m_i: Option = None; + + // find a segment intersected by a ray from the hole's leftmost point to the left; + // segment's endpoint with lesser x will be potential connection point + loop { + let p = node!(nodes, p_i); + let p_next_i = p.next_i; + let p_next = node!(nodes, p_next_i); + if hole.y <= p.y && hole.y >= p_next.y && p_next.y != p.y { + let x = p.x + (hole.y - p.y) * (p_next.x - p.x) / (p_next.y - p.y); + if x <= hole.x && x > qx { + qx = x; + m_i = Some(if p.x < p_next.x { p_i } else { p_next_i }); + if x == hole.x { + // hole touches outer segment; pick leftmost endpoint + return m_i; + } + } + } + p_i = p_next_i; + if p_i == outer_node_i { + break; } } - /// check if the middle point of a polygon diagonal is inside the polygon - fn middle_inside(&self, a: &Node, b: &Node) -> bool { - let mut p = a; - let mut inside = false; - let two = T::one() + T::one(); - let (px, py) = ((a.x + b.x) / two, (a.y + b.y) / two); - loop { - let p_next = node!(self.nodes, p.next_i); - inside ^= (p.y > py) != (p_next.y > py) - && p_next.y != p.y - && (px < (p_next.x - p.x) * (py - p.y) / (p_next.y - p.y) + p.x); - p = p_next; - if ptr::eq(p, a) { - return inside; + let mut m_i = m_i?; + + // look for points inside the triangle of hole point, segment intersection and endpoint; + // if there are no points found, we have a valid connection; + // otherwise choose the point of the minimum angle with the ray as connection point + + let stop_i = m_i; + let Node { x: mx, y: my, .. } = *node!(nodes, m_i); // must copy + let mut tan_min = T::infinity(); + + p_i = m_i; + let mut p = node!(nodes, p_i); + let mut m = p; + + loop { + if (hole.x >= p.x && p.x >= mx && hole.x != p.x) + && point_in_triangle( + if hole.y < my { hole.x } else { qx }, + hole.y, + mx, + my, + if hole.y < my { qx } else { hole.x }, + hole.y, + p.x, + p.y, + ) + { + let tan = (hole.y - p.y).abs() / (hole.x - p.x); + if locally_inside(nodes, p, hole) + && (tan < tan_min + || (tan == tan_min + && (p.x > m.x || p.x == m.x && sector_contains_sector(nodes, m, p)))) + { + (m_i, m) = (p_i, p); + tan_min = tan; } } + + p_i = p.next_i; + if p_i == stop_i { + return Some(m_i); + } + p = node!(nodes, p_i); } +} - /// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; - /// if one belongs to the outer ring and another to a hole, it merges it into a single ring - fn split_polygon(&mut self, a_i: usize, b_i: usize) -> usize { - let a2_i = self.nodes.len(); - let b2_i = a2_i + 1; - - let a = node_mut!(self.nodes, a_i); - let mut a2 = Node::new(a.i, a.x, a.y); - let an_i = a.next_i; - a.next_i = b_i; - a2.prev_i = b2_i; - a2.next_i = an_i; - - let b = node_mut!(self.nodes, b_i); - let mut b2 = Node::new(b.i, b.x, b.y); - let bp_i = b.prev_i; - b.prev_i = a_i; - b2.next_i = a2_i; - b2.prev_i = bp_i; - - node_mut!(self.nodes, an_i).prev_i = a2_i; - node_mut!(self.nodes, bp_i).next_i = b2_i; - - self.nodes.push(a2); - self.nodes.push(b2); - - b2_i +/// whether sector in vertex m contains sector in vertex p in the same coordinates +fn sector_contains_sector(nodes: &[Node], m: &Node, p: &Node) -> bool { + area(node!(nodes, m.prev_i), m, node!(nodes, p.prev_i)) < T::zero() + && area(node!(nodes, p.next_i), m, node!(nodes, m.next_i)) < T::zero() +} + +/// eliminate colinear or duplicate points +fn filter_points(nodes: &mut [Node], start_i: u32, end_i: Option) -> u32 { + let mut end_i = end_i.unwrap_or(start_i); + + let mut p_i = start_i; + loop { + let p = node!(nodes, p_i); + let p_next = node!(nodes, p.next_i); + if !p.steiner && (equals(p, p_next) || area(node!(nodes, p.prev_i), p, p_next).is_zero()) { + let (prev_i, next_i) = remove_node(nodes, p_i); + (p_i, end_i) = (prev_i, prev_i); + if p_i == next_i { + return end_i; + } + } else { + p_i = p.next_i; + if p_i == end_i { + return end_i; + } + }; } } +/// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; +/// if one belongs to the outer ring and another to a hole, it merges it into a single ring +fn split_polygon(nodes: &mut Vec>, a_i: u32, b_i: u32) -> u32 { + let a2_i = nodes.len() as u32; + let b2_i = a2_i + 1; + + let a = node_mut!(nodes, a_i); + let mut a2 = Node::new(a.i, a.x, a.y); + let an_i = a.next_i; + a.next_i = b_i; + a2.prev_i = b2_i; + a2.next_i = an_i; + node_mut!(nodes, an_i).prev_i = a2_i; + + let b = node_mut!(nodes, b_i); + let mut b2 = Node::new(b.i, b.x, b.y); + let bp_i = b.prev_i; + b.prev_i = a_i; + b2.next_i = a2_i; + b2.prev_i = bp_i; + node_mut!(nodes, bp_i).next_i = b2_i; + + nodes.extend([a2, b2]); + + b2_i +} + /// create a node and optionally link it with previous one (in a circular doubly linked list) -fn insert_node( - nodes: &mut Vec>, - i: usize, - x: T, - y: T, - last: Option, -) -> usize { +fn insert_node(nodes: &mut Vec>, i: u32, x: T, y: T, last: Option) -> u32 { let mut p = Node::new(i, x, y); - let p_i = nodes.len(); + let p_i = nodes.len() as u32; match last { Some(last_i) => { - let last_next_i = node!(nodes, last_i).next_i; + let last = node_mut!(nodes, last_i); + let last_next_i = last.next_i; + last.next_i = p_i; p.next_i = last_next_i; p.prev_i = last_i; node_mut!(nodes, last_next_i).prev_i = p_i; - node_mut!(nodes, last_i).next_i = p_i; } None => { (p.prev_i, p.next_i) = (p_i, p_i); @@ -923,14 +919,16 @@ fn insert_node( p_i } -fn remove_node(nodes: &mut [Node], p_i: usize) -> (usize, usize) { +fn remove_node(nodes: &mut [Node], p_i: u32) -> (u32, u32) { let p = node!(nodes, p_i); let p_next_i = p.next_i; let p_prev_i = p.prev_i; let p_next_z_i = p.next_z_i; let p_prev_z_i = p.prev_z_i; + node_mut!(nodes, p_next_i).prev_i = p_prev_i; node_mut!(nodes, p_prev_i).next_i = p_next_i; + if let Some(prev_z_i) = p_prev_z_i { node_mut!(nodes, prev_z_i).next_z_i = p_next_z_i; } @@ -953,16 +951,16 @@ pub fn deviation( let outer_len = match has_holes { true => hole_indices[0].into_usize() * 2, false => data.len(), - }; + } as u32; let mut polygon_area = signed_area(&data, 0, outer_len).abs(); if has_holes { for i in 0..hole_indices.len() { - let start = hole_indices[i].into_usize() * 2; + let start = hole_indices[i].into_usize() as u32 * 2; let end = if i < hole_indices.len() - 1 { hole_indices[i + 1].into_usize() * 2 } else { data.len() - }; + } as u32; polygon_area = polygon_area - signed_area(&data, start, end).abs(); } } @@ -988,30 +986,28 @@ pub fn deviation( } /// check if a point lies within a convex triangle -fn signed_area(data: &[T], start: usize, end: usize) -> T { +fn signed_area(data: &[T], start: u32, end: u32) -> T { if start == end { return T::zero(); } let j = if end > 2 { end - 2 } else { 0 }; - let mut bx = data[j]; - let mut by = data[j + 1]; + let mut bx = data[j as usize]; + let mut by = data[(j + 1) as usize]; + let mut sum = T::zero(); - for (ax, ay) in data[start..end] - .iter() - .step_by(2) - .zip(data[start + 1..end].iter().step_by(2)) - { - sum = sum + (bx - *ax) * (*ay + by); - (bx, by) = (*ax, *ay); + for a in data[start as usize..end as usize].chunks_exact(2) { + let (ax, ay) = (a[0], a[1]); + sum = sum + (bx - ax) * (ay + by); + (bx, by) = (ax, ay); } sum } /// z-order of a point given coords and inverse of the longer side of data bbox -fn z_order(x: T, y: T, min_x: T, min_y: T, inv_size: T) -> u32 { +fn z_order(x: T, y: T, min_x: T, min_y: T, inv_size: T) -> i32 { // coords are transformed into non-negative 15-bit integer range - let mut x = ((x - min_x) * inv_size).to_u32().unwrap(); - let mut y = ((y - min_y) * inv_size).to_u32().unwrap(); + let mut x = ((x - min_x) * inv_size).to_i32().unwrap(); + let mut y = ((y - min_y) * inv_size).to_i32().unwrap(); x = (x | (x << 8)) & 0x00FF00FF; x = (x | (x << 4)) & 0x0F0F0F0F; x = (x | (x << 2)) & 0x33333333;