Skip to content

Commit

Permalink
mapbox/earcut v3.0.1 (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
ciscorn authored Feb 18, 2025
1 parent 3898cc0 commit 3a96d4d
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 13 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "earcut"
version = "0.4.2"
version = "0.4.3"
edition = "2021"
description = "A Rust port of the Earcut polygon triangulation library"
authors = ["Taku Fukada <naninunenor@gmail.com>", "MIERUNE Inc. <info@mierune.co.jp>"]
Expand Down
46 changes: 39 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,28 @@ impl<T: Float> Earcut<T> {
}
}

self.queue
.sort_by(|(_a, ax), (_b, bx)| ax.partial_cmp(bx).unwrap_or(Ordering::Equal));
self.queue.sort_by(|(ai, ax), (bi, bx)| {
// compareXYSlope
match ax.partial_cmp(bx) {
Some(Ordering::Equal) => {}
Some(ordering) => return ordering,
None => return Ordering::Equal,
}
// when the left-most point of 2 holes meet at a vertex, sort the holes counterclockwise so that when we find
// the bridge to the outer shell is always the point that they meet at.
let a = node!(self.nodes, ai);
let b = node!(self.nodes, bi);
match a.xy[1].partial_cmp(&b.xy[1]) {
Some(Ordering::Equal) => {}
Some(ordering) => return ordering,
None => return Ordering::Equal,
};
let a_slope = (node!(self.nodes, a.next_i).xy[1] - a.xy[1])
/ (node!(self.nodes, a.next_i).xy[0] - a.xy[0]);
let b_slope = (node!(self.nodes, b.next_i).xy[1] - b.xy[1])
/ (node!(self.nodes, b.next_i).xy[0] - b.xy[0]);
a_slope.partial_cmp(&b_slope).unwrap_or(Ordering::Equal)
});

// process holes from left to right
for &(q, _) in &self.queue {
Expand Down Expand Up @@ -406,7 +426,7 @@ fn is_ear<'a, T: Float>(
while !ptr::eq(p, a) {
let p_next = node!(nodes, p.next_i);
if (p.xy[0] >= x0 && p.xy[0] <= x1 && p.xy[1] >= y0 && p.xy[1] <= y1)
&& point_in_triangle(a.xy, b.xy, c.xy, p.xy)
&& point_in_triangle_except_first(a.xy, b.xy, c.xy, p.xy)
&& area(p_prev, p, p_next) >= T::zero()
{
return (false, a, c);
Expand Down Expand Up @@ -465,7 +485,7 @@ fn is_ear_hashed<'a, T: Float>(
& (p.xy[1] >= xy_min[1])
& (p.xy[1] <= xy_max[1]))
&& (!ptr::eq(p, a) && !ptr::eq(p, c))
&& point_in_triangle(a.xy, b.xy, c.xy, p.xy)
&& point_in_triangle_except_first(a.xy, b.xy, c.xy, p.xy)
&& area(node!(nodes, p.prev_i), p, node!(nodes, p.next_i)) >= T::zero()
{
return (false, a, c);
Expand All @@ -477,7 +497,7 @@ fn is_ear_hashed<'a, T: Float>(
& (n.xy[1] >= xy_min[1])
& (n.xy[1] <= xy_max[1]))
&& (!ptr::eq(n, a) && !ptr::eq(n, c))
&& point_in_triangle(a.xy, b.xy, c.xy, n.xy)
&& point_in_triangle_except_first(a.xy, b.xy, c.xy, n.xy)
&& area(node!(nodes, n.prev_i), n, node!(nodes, n.next_i)) >= T::zero()
{
return (false, a, c);
Expand All @@ -495,7 +515,7 @@ fn is_ear_hashed<'a, T: Float>(
& (p.xy[1] >= xy_min[1])
& (p.xy[1] <= xy_max[1]))
&& (!ptr::eq(p, a) && !ptr::eq(p, c))
&& point_in_triangle(a.xy, b.xy, c.xy, p.xy)
&& point_in_triangle_except_first(a.xy, b.xy, c.xy, p.xy)
&& area(node!(nodes, p.prev_i), p, node!(nodes, p.next_i)) >= T::zero()
{
return (false, a, c);
Expand All @@ -513,7 +533,7 @@ fn is_ear_hashed<'a, T: Float>(
& (n.xy[1] >= xy_min[1])
& (n.xy[1] <= xy_max[1]))
&& (!ptr::eq(n, a) && !ptr::eq(n, c))
&& point_in_triangle(a.xy, b.xy, c.xy, n.xy)
&& point_in_triangle_except_first(a.xy, b.xy, c.xy, n.xy)
&& area(node!(nodes, n.prev_i), n, node!(nodes, n.next_i)) >= T::zero()
{
return (false, a, c);
Expand Down Expand Up @@ -862,9 +882,16 @@ fn find_hole_bridge<T: Float>(

// 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
// unless they intersect at a vertex, then choose the vertex
let mut p = node!(nodes, p_i);
if equals(hole, p) {
return Some(p_i);
}
loop {
let p_next = node!(nodes, p.next_i);
if equals(hole, p_next) {
return Some(p.next_i);
}
if hole.xy[1] <= p.xy[1] && hole.xy[1] >= p_next.xy[1] && p_next.xy[1] != p.xy[1] {
let x = p.xy[0]
+ (hole.xy[1] - p.xy[1]) * (p_next.xy[0] - p.xy[0]) / (p_next.xy[1] - p.xy[1]);
Expand Down Expand Up @@ -1135,6 +1162,11 @@ fn point_in_triangle<T: Float>(a: [T; 2], b: [T; 2], c: [T; 2], p: [T; 2]) -> bo
&& ((b[0] - p[0]) * (c[1] - p[1]) >= (c[0] - p[0]) * (b[1] - p[1]))
}

#[allow(clippy::too_many_arguments)]
fn point_in_triangle_except_first<T: Float>(a: [T; 2], b: [T; 2], c: [T; 2], p: [T; 2]) -> bool {
!(a[0] == p[0] && a[1] == p[1]) && point_in_triangle(a, b, c, p)
}

/// signed area of a triangle
fn area<T: Float>(p: &Node<T>, q: &Node<T>, r: &Node<T>) -> T {
(q.xy[1] - p.xy[1]) * (r.xy[0] - q.xy[0]) - (q.xy[0] - p.xy[0]) * (r.xy[1] - q.xy[1])
Expand Down
35 changes: 30 additions & 5 deletions tests/fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@ fn fixture_water4() {

#[test]
fn fixture_water_huge1() {
test_fixture("water-huge", 5177, 0.0011);
test_fixture("water-huge", 5176, 0.0011);
}

#[test]
fn fixture_water_huge2() {
test_fixture("water-huge2", 4462, 0.0028);
test_fixture("water-huge2", 4462, 0.004);
}

#[test]
Expand Down Expand Up @@ -148,6 +148,31 @@ fn fixture_touching_holes() {
test_fixture("touching-holes", 57, 0.0);
}

#[test]
fn fixture_touching_holes2() {
test_fixture("touching-holes2", 10, 0.0);
}

#[test]
fn fixture_touching_holes3() {
test_fixture("touching-holes3", 82, 0.0);
}

#[test]
fn fixture_touching_holes4() {
test_fixture("touching-holes4", 55, 0.0);
}

#[test]
fn fixture_touching_holes5() {
test_fixture("touching-holes5", 133, 0.0);
}

#[test]
fn fixture_touching_holes6() {
test_fixture("touching-holes6", 3098, 0.0);
}

#[test]
fn fixture_hole_touching_outer() {
test_fixture("hole-touching-outer", 77, 0.0);
Expand Down Expand Up @@ -200,12 +225,12 @@ fn fixture_issue107() {

#[test]
fn fixture_issue111() {
test_fixture("issue111", 19, 0.0);
test_fixture("issue111", 18, 0.0);
}

#[test]
fn fixture_collinear_boxy() {
test_fixture("boxy", 57, 0.0);
test_fixture("boxy", 58, 0.0);
}

#[test]
Expand Down Expand Up @@ -235,7 +260,7 @@ fn fixture_touching3() {

#[test]
fn fixture_touching4() {
test_fixture("touching4", 20, 0.0);
test_fixture("touching4", 19, 0.0);
}

#[test]
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/touching-holes2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[[[0,0],[20,0],[20,25],[0,25],[0,0]],[[3,3],[2,12],[9,15],[3,3]],[[9,21],[2,12],[7,22],[9,21]]]
1 change: 1 addition & 0 deletions tests/fixtures/touching-holes3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[[[0,0],[20,0],[20,25],[0,25],[0,0]],[[2,12],[4,23],[5,23],[2,12]],[[2,12],[6,23],[7,23],[2,12]],[[2,12],[8,23],[9,23],[2,12]],[[2,12],[10,23],[11,23],[2,12]],[[2,12],[12,23],[13,23],[2,12]],[[2,12],[14,23],[15,23],[2,12]],[[2,12],[16,23],[17,23],[2,12]],[[2,12],[18,23],[18,22],[2,12]],[[2,12],[18,21],[18,20],[2,12]],[[2,12],[18,19],[18,18],[2,12]],[[2,12],[18,17],[18,16],[2,12]],[[2,12],[18,15],[18,14],[2,12]],[[2,12],[18,13],[18,12],[2,12]],[[2,12],[18,11],[18,10],[2,12]],[[2,12],[18,9],[18,8],[2,12]],[[2,12],[18,7],[18,6],[2,12]],[[2,12],[18,5],[18,4],[2,12]],[[2,12],[18,3],[18,2],[2,12]],[[2,12],[18,1],[17,1],[2,12]],[[2,12],[16,1],[15,1],[2,12]],[[2,12],[14,1],[13,1],[2,12]],[[2,12],[12,1],[11,1],[2,12]],[[2,12],[10,1],[9,1],[2,12]],[[2,12],[8,1],[7,1],[2,12]],[[2,12],[6,1],[5,1],[2,12]],[[2,12],[4,1],[3,1],[2,12]]]
1 change: 1 addition & 0 deletions tests/fixtures/touching-holes4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[[[-20,0],[20,0],[20,25],[-20,25],[-20,0]],[[2,12],[-1,23],[0,23],[2,12]],[[2,12],[-3,23],[-2,23],[2,12]],[[2,12],[-5,23],[-4,23],[2,12]],[[2,12],[-7,23],[-6,23],[2,12]],[[2,12],[-9,23],[-8,23],[2,12]],[[2,12],[-11,23],[-10,23],[2,12]],[[2,12],[-13,23],[-12,23],[2,12]],[[2,12],[-14,22],[-14,23],[2,12]],[[2,12],[-14,20],[-14,21],[2,12]],[[2,12],[-14,18],[-14,19],[2,12]],[[2,12],[-14,16],[-14,17],[2,12]],[[2,12],[-14,14],[-14,15],[2,12]],[[2,12],[-14,12],[-14,13],[2,12]],[[2,12],[-14,10],[-14,11],[2,12]],[[2,12],[-14,8],[-14,9],[2,12]],[[2,12],[-14,6],[-14,7],[2,12]],[[2,12],[-14,4],[-14,5],[2,12]],[[2,12],[-14,2],[-14,3],[2,12]],[[2,12],[-13,1],[-14,1],[2,12]],[[2,12],[-11,1],[-12,1],[2,12]],[[2,12],[-9,1],[-10,1],[2,12]],[[2,12],[-7,1],[-8,1],[2,12]],[[2,12],[-5,1],[-6,1],[2,12]],[[2,12],[-3,1],[-4,1],[2,12]],[[2,12],[-1,1],[-2,1],[2,12]],[[2,12],[1,1],[0,1],[2,12]]]
1 change: 1 addition & 0 deletions tests/fixtures/touching-holes5.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[[[-20,0],[20,0],[20,25],[-20,25],[-20,0]],[[2,12],[4,23],[5,23],[2,12]],[[2,12],[6,23],[7,23],[2,12]],[[2,12],[8,23],[9,23],[2,12]],[[2,12],[10,23],[11,23],[2,12]],[[2,12],[12,23],[13,23],[2,12]],[[2,12],[14,23],[15,23],[2,12]],[[2,12],[16,23],[17,23],[2,12]],[[2,12],[18,23],[18,22],[2,12]],[[2,12],[18,21],[18,20],[2,12]],[[2,12],[18,19],[18,18],[2,12]],[[2,12],[18,17],[18,16],[2,12]],[[2,12],[18,15],[18,14],[2,12]],[[2,12],[18,13],[18,12],[2,12]],[[2,12],[18,11],[18,10],[2,12]],[[2,12],[18,9],[18,8],[2,12]],[[2,12],[18,7],[18,6],[2,12]],[[2,12],[18,5],[18,4],[2,12]],[[2,12],[18,3],[18,2],[2,12]],[[2,12],[18,1],[17,1],[2,12]],[[2,12],[16,1],[15,1],[2,12]],[[2,12],[14,1],[13,1],[2,12]],[[2,12],[12,1],[11,1],[2,12]],[[2,12],[10,1],[9,1],[2,12]],[[2,12],[8,1],[7,1],[2,12]],[[2,12],[6,1],[5,1],[2,12]],[[2,12],[4,1],[3,1],[2,12]],[[2,12],[-1,23],[0,23],[2,12]],[[2,12],[-3,23],[-2,23],[2,12]],[[2,12],[-5,23],[-4,23],[2,12]],[[2,12],[-7,23],[-6,23],[2,12]],[[2,12],[-9,23],[-8,23],[2,12]],[[2,12],[-11,23],[-10,23],[2,12]],[[2,12],[-13,23],[-12,23],[2,12]],[[2,12],[-14,22],[-14,23],[2,12]],[[2,12],[-14,20],[-14,21],[2,12]],[[2,12],[-14,18],[-14,19],[2,12]],[[2,12],[-14,16],[-14,17],[2,12]],[[2,12],[-14,14],[-14,15],[2,12]],[[2,12],[-14,12],[-14,13],[2,12]],[[2,12],[-14,10],[-14,11],[2,12]],[[2,12],[-14,8],[-14,9],[2,12]],[[2,12],[-14,6],[-14,7],[2,12]],[[2,12],[-14,4],[-14,5],[2,12]],[[2,12],[-14,2],[-14,3],[2,12]],[[2,12],[-13,1],[-14,1],[2,12]],[[2,12],[-11,1],[-12,1],[2,12]],[[2,12],[-9,1],[-10,1],[2,12]],[[2,12],[-7,1],[-8,1],[2,12]],[[2,12],[-5,1],[-6,1],[2,12]],[[2,12],[-3,1],[-4,1],[2,12]],[[2,12],[-1,1],[-2,1],[2,12]],[[2,12],[1,1],[0,1],[2,12]]]
1 change: 1 addition & 0 deletions tests/fixtures/touching-holes6.json

Large diffs are not rendered by default.

0 comments on commit 3a96d4d

Please sign in to comment.