Skip to content

Commit

Permalink
Improve API (copied iterator, flat indices) (#7)
Browse files Browse the repository at this point in the history
* v0.2

* update utils3d

* Improve API

* Use f64 instead of [f64; 2] (similar to JS api)
  • Loading branch information
ciscorn committed Apr 1, 2024
1 parent afcb0f0 commit 3a889bb
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 75 deletions.
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
[package]
name = "earcut-rs"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
authors = ["Taku Fukada <naninunenor@gmail.com>", "MIERUNE Inc. <info@mierune.co.jp>"]
license-file = "LICENSE.txt"

[dependencies]
num-traits = "0.2.16"
num-traits = "0.2.18"

[dev-dependencies]
serde_json = { version = "1.0.107", features = ["float_roundtrip"] }
serde = { version = "1.0.188", features = ["derive"] }
serde_json = { version = "1.0.115", features = ["float_roundtrip"] }
serde = { version = "1.0.197", features = ["derive"] }
criterion = "0.5.1"

[[bench]]
Expand Down
1 change: 1 addition & 0 deletions LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
ISC License

Copyright (c) 2016, Mapbox
Copyright (c) 2023, Taku Fukada
Copyright (c) 2023, MIERUNE Inc.

Permission to use, copy, modify, and/or distribute this software for any purpose
Expand Down
27 changes: 16 additions & 11 deletions benches/benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@ use criterion::{criterion_group, criterion_main, Criterion};

use earcut_rs::Earcut;

fn load_fixture(name: &str) -> (Vec<[f64; 2]>, Vec<usize>) {
fn load_fixture(name: &str) -> (Vec<f64>, Vec<usize>) {
// load JSON
type Coords = Vec<Vec<[f64; 2]>>;
let s = fs::read_to_string("./tests/fixtures/".to_string() + name + ".json").unwrap();
let expected = serde_json::from_str::<Coords>(&s).unwrap();

// prepare input
let num_holes = expected.len();
let data: Vec<_> = expected.clone().into_iter().flatten().collect();
let data = expected
.clone()
.into_iter()
.flatten()
.flatten()
.collect::<Vec<_>>();
let hole_indices: Vec<_> = expected
.iter()
.map(|x| x.len())
Expand All @@ -33,64 +38,64 @@ fn bench(c: &mut Criterion) {
c.bench_function("water", |b| {
let (data, hole_indices) = load_fixture("water");
b.iter(|| {
earcut.earcut(&data, &hole_indices, &mut triangles);
earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles);
})
});

c.bench_function("building", |b| {
let (data, hole_indices) = load_fixture("building");
b.iter(|| {
earcut.earcut(&data, &hole_indices, &mut triangles);
earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles);
})
});

c.bench_function("water2", |b| {
let (data, hole_indices) = load_fixture("water2");
b.iter(|| {
earcut.earcut(&data, &hole_indices, &mut triangles);
earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles);
})
});

c.bench_function("water3", |b| {
let (data, hole_indices) = load_fixture("water3");
b.iter(|| {
earcut.earcut(&data, &hole_indices, &mut triangles);
earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles);
})
});

c.bench_function("water3b", |b| {
let (data, hole_indices) = load_fixture("water3b");
b.iter(|| {
earcut.earcut(&data, &hole_indices, &mut triangles);
earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles);
})
});

c.bench_function("water-huge", |b| {
let (data, hole_indices) = load_fixture("water-huge");
b.iter(|| {
earcut.earcut(&data, &hole_indices, &mut triangles);
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");
b.iter(|| {
earcut.earcut(&data, &hole_indices, &mut triangles);
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, &hole_indices, &mut triangles);
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, &hole_indices, &mut triangles);
earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles);
})
});
}
Expand Down
13 changes: 10 additions & 3 deletions examples/example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ fn load_fixture(name: &str, num_triangles: usize, expected_deviation: f64) {

// prepare input
let num_holes = expected.len();
let vertices: Vec<_> = expected.clone().into_iter().flatten().collect();
let vertices = expected
.clone()
.into_iter()
.flatten()
.flatten()
.collect::<Vec<_>>();
let hole_indices: Vec<_> = expected
.into_iter()
.map(|x| x.len() as u32)
Expand All @@ -25,13 +30,15 @@ fn load_fixture(name: &str, num_triangles: usize, expected_deviation: f64) {
let mut triangles = vec![];
let mut earcut = Earcut::new();
for _ in 0..500 {
earcut.earcut(&vertices, &hole_indices, &mut triangles);
earcut.earcut(vertices.iter().copied(), &hole_indices, &mut triangles);
}

// check
assert!(triangles.len() == num_triangles);
if !triangles.is_empty() {
assert!(deviation(&vertices, &hole_indices, &triangles) <= expected_deviation);
assert!(
deviation(vertices.iter().copied(), &hole_indices, &triangles) <= expected_deviation
);
}
}

Expand Down
35 changes: 18 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,14 @@ impl<T: Float> Earcut<T> {
self.nodes.reserve(capacity);
}

pub fn earcut<'a, N: Index>(
pub fn earcut<N: Index>(
&mut self,
data: impl IntoIterator<Item = &'a [T; 2]>,
data: impl IntoIterator<Item = T>,
hole_indices: &[N],
triangles_out: &mut Vec<[N; 3]>,
) where
T: 'a,
{
triangles_out: &mut Vec<N>,
) {
self.data.clear();
self.data.extend(data.into_iter().flatten());
self.data.extend(data);

triangles_out.clear();
self.reset(self.data.len() / 2 * 3 / 2);
Expand Down Expand Up @@ -253,7 +251,7 @@ impl<T: Float> Earcut<T> {
fn earcut_linked<N: Index>(
&mut self,
ear_i: usize,
triangles: &mut Vec<[N; 3]>,
triangles: &mut Vec<N>,
min_x: T,
min_y: T,
inv_size: T,
Expand Down Expand Up @@ -281,7 +279,7 @@ impl<T: Float> Earcut<T> {
};
if is_ear {
// cut off the triangle
triangles.push([
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),
Expand Down Expand Up @@ -445,7 +443,7 @@ impl<T: Float> Earcut<T> {
fn cure_local_intersections<N: Index>(
&mut self,
mut start_i: usize,
triangles: &mut Vec<[N; 3]>,
triangles: &mut Vec<N>,
) -> usize {
let mut p_i = start_i;
loop {
Expand All @@ -463,7 +461,7 @@ impl<T: Float> Earcut<T> {
&& self.locally_inside(a, b)
&& self.locally_inside(b, a)
{
triangles.push([
triangles.extend([
N::from_usize(a.i / 2),
N::from_usize(p.i / 2),
N::from_usize(b.i / 2),
Expand All @@ -486,7 +484,7 @@ impl<T: Float> Earcut<T> {
fn split_earcut<N: Index>(
&mut self,
start_i: usize,
triangles: &mut Vec<[N; 3]>,
triangles: &mut Vec<N>,
min_x: T,
min_y: T,
inv_size: T,
Expand Down Expand Up @@ -938,12 +936,12 @@ fn remove_node<T: Float>(nodes: &mut [Node<T>], p_i: usize) -> (usize, usize) {

/// return a percentage difference between the polygon area and its triangulation area;
/// used to verify correctness of triangulation
pub fn deviation<'a, T: Float + 'a, N: Index>(
data: impl IntoIterator<Item = &'a [T; 2]>,
pub fn deviation<T: Float, N: Index>(
data: impl IntoIterator<Item = T>,
hole_indices: &[N],
triangles: &[[N; 3]],
triangles: &[N],
) -> T {
let data = data.into_iter().copied().flatten().collect::<Vec<T>>();
let data = data.into_iter().collect::<Vec<T>>();

let has_holes = !hole_indices.is_empty();
let outer_len = match has_holes {
Expand All @@ -964,7 +962,10 @@ pub fn deviation<'a, T: Float + 'a, N: Index>(
}

let mut triangles_area = T::zero();
for [a, b, c] in triangles {
for [a, b, c] in triangles
.chunks_exact(3)
.map(|idxs| [idxs[0], idxs[1], idxs[2]])
{
let a = a.into_usize() * 2;
let b = b.into_usize() * 2;
let c = c.into_usize() * 2;
Expand Down
8 changes: 4 additions & 4 deletions tests/fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ fn test_fixture(name: &str, num_triangles: usize, expected_deviation: f64) {

// prepare input
let num_holes = expected.len();
let data: Vec<[f64; 2]> = expected.clone().into_iter().flatten().collect();
let data: Vec<f64> = expected.clone().into_iter().flatten().flatten().collect();
let hole_indices: Vec<_> = expected
.into_iter()
.map(|x| x.len() as u32)
Expand All @@ -24,12 +24,12 @@ fn test_fixture(name: &str, num_triangles: usize, expected_deviation: f64) {
// earcut
let mut triangles = vec![];
let mut earcut = Earcut::new();
earcut.earcut(&data, &hole_indices, &mut triangles);
earcut.earcut(data.iter().copied(), &hole_indices, &mut triangles);

// check
assert!(triangles.len() == num_triangles);
assert!(triangles.len() == num_triangles * 3);
if !triangles.is_empty() {
assert!(deviation(&data, &hole_indices, &triangles) <= expected_deviation);
assert!(deviation(data.iter().copied(), &hole_indices, &triangles) <= expected_deviation);
}
}

Expand Down
Loading

0 comments on commit 3a889bb

Please sign in to comment.