Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions iTriangle/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "i_triangle"
version = "0.42.0"
version = "0.43.0"
edition = "2021"
authors = ["Nail Sharipov <nailxsharipov@gmail.com>"]
description = "Polygon Triangulation Library: Efficient Delaunay Triangulation for Complex Shapes."
Expand All @@ -24,9 +24,9 @@ serde = { version = "^1.0", default-features = false, features = ["derive"], opt
#i_tree = { path = "../../iTree" }
#i_key_sort = { path = "../../iKeySort/iKeySort" }

i_overlay = "~5.0.0"
i_overlay = "^6.0.0"
i_tree = "~0.18.0"
i_key_sort = "~0.10.1"
i_key_sort = "~0.10.3"



Expand Down
82 changes: 80 additions & 2 deletions iTriangle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ iTriangle is a high-performance 2D polygon triangulation library for Rust. It so
- [Quick Start](#quick-start)
- [Documentation](#documentation)
- [Examples](#examples)
- [Integer API](#integer-api)
- [Performance](#performance)
- [Gallery](#gallery)
- [Contributing](#contributing)
Expand Down Expand Up @@ -73,7 +74,7 @@ Add to your `Cargo.toml`:

```toml
[dependencies]
i_triangle = "0.36"
i_triangle = "0.43"
```

Minimal example:
Expand Down Expand Up @@ -183,7 +184,13 @@ println!("centroids: {:?}", centroids);
If you need to triangulate many shapes, it is more efficient to use `Triangulator`.

```rust
let contours = random_contours(100);
use i_triangle::float::triangulation::Triangulation;
use i_triangle::float::triangulator::Triangulator;

let contours = vec![
vec![[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0]],
vec![[5.0, 0.0], [9.0, 0.0], [9.0, 4.0], [5.0, 4.0]],
];

let mut triangulator = Triangulator::<u32>::default();

Expand All @@ -204,6 +211,77 @@ for contour in contours.iter() {
}
```

## Integer API

The integer API is useful when your coordinates are already quantized or when you want direct control over the robust integer core. It avoids float-to-int adapter setup and returns integer points unchanged.

Use `IntPoint` with `IntContour`, `IntShape`, or `IntShapes`-compatible containers:

```rust
use i_triangle::int::triangulatable::IntTriangulatable;
use i_triangle::i_overlay::i_float::int::point::IntPoint;

let contour = vec![
IntPoint::new(0, 0),
IntPoint::new(10, 0),
IntPoint::new(10, 10),
IntPoint::new(0, 10),
];

let triangulation = contour.triangulate().into_triangulation::<u16>();

assert_eq!(triangulation.points.len(), 4);
assert_eq!(triangulation.indices.len(), 6);
```

For repeated triangulation, use `IntTriangulator` and reuse its internal buffers:

```rust
use i_triangle::int::triangulation::IntTriangulation;
use i_triangle::int::triangulator::IntTriangulator;
use i_triangle::i_overlay::i_float::int::point::IntPoint;

let contours = vec![
vec![
IntPoint::new(0, 0),
IntPoint::new(10, 0),
IntPoint::new(10, 10),
IntPoint::new(0, 10),
],
vec![
IntPoint::new(20, 0),
IntPoint::new(30, 0),
IntPoint::new(30, 10),
IntPoint::new(20, 10),
],
];

let mut triangulator = IntTriangulator::<u32>::default();
let mut output = IntTriangulation::<u32>::default();

for contour in &contours {
triangulator.triangulate_contour_into(contour.clone(), &mut output);
assert!(!output.indices.is_empty());
}
```

If your integer contours are already valid and correctly oriented, the unchecked API skips validation:

```rust
use i_triangle::int::unchecked::IntUncheckedTriangulatable;
use i_triangle::i_overlay::i_float::int::point::IntPoint;

let contour = vec![
IntPoint::new(0, 0),
IntPoint::new(10, 0),
IntPoint::new(10, 10),
IntPoint::new(0, 10),
];

let triangulation = contour.uncheck_triangulate().into_triangulation::<u16>();
assert_eq!(triangulation.indices.len(), 6);
```

## Performance

Benchmarks and interactive demos are available here:
Expand Down
5 changes: 2 additions & 3 deletions iTriangle/src/float/centroid_net.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use crate::float::delaunay::Delaunay;
use alloc::vec::Vec;
use i_overlay::i_float::float::compatible::FloatPointCompatible;
use i_overlay::i_float::float::number::FloatNumber;
use i_overlay::i_shape::base::data::Contour;
use i_overlay::i_shape::float::adapter::ShapeToFloat;

impl<P: FloatPointCompatible<T>, T: FloatNumber> Delaunay<P, T> {
impl<P: FloatPointCompatible> Delaunay<P> {
#[inline]
pub fn to_centroid_net(&self, min_area: T) -> Vec<Contour<P>> {
pub fn to_centroid_net(&self, min_area: P::Scalar) -> Vec<Contour<P>> {
let int_area = self.adapter.sqr_float_to_int(min_area);
self.delaunay.centroid_net(int_area).to_float(&self.adapter)
}
Expand Down
11 changes: 5 additions & 6 deletions iTriangle/src/float/circumcenter.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
use crate::float::delaunay::Delaunay;
use i_overlay::i_float::float::compatible::FloatPointCompatible;
use i_overlay::i_float::float::number::FloatNumber;

impl<P: FloatPointCompatible<T>, T: FloatNumber> Delaunay<P, T> {
impl<P: FloatPointCompatible> Delaunay<P> {
#[inline]
pub fn refine_with_circumcenters(mut self, min_area: T) -> Self {
pub fn refine_with_circumcenters(mut self, min_area: P::Scalar) -> Self {
self.refine_with_circumcenters_mut(min_area);
self
}

#[inline]
pub fn refine_with_circumcenters_by_obtuse_angle(mut self, min_area: T) -> Self {
pub fn refine_with_circumcenters_by_obtuse_angle(mut self, min_area: P::Scalar) -> Self {
self.refine_with_circumcenters_by_obtuse_angle_mut(min_area);
self
}

#[inline]
pub fn refine_with_circumcenters_mut(&mut self, min_area: T) {
pub fn refine_with_circumcenters_mut(&mut self, min_area: P::Scalar) {
let int_area = self.adapter.sqr_float_to_int(min_area);
self.delaunay.refine_with_circumcenters_mut(int_area);
}

#[inline]
pub fn refine_with_circumcenters_by_obtuse_angle_mut(&mut self, min_area: T) {
pub fn refine_with_circumcenters_by_obtuse_angle_mut(&mut self, min_area: P::Scalar) {
let int_area = self.adapter.sqr_float_to_int(min_area);
self.delaunay
.refine_with_circumcenters_by_obtuse_angle_mut(int_area);
Expand Down
3 changes: 1 addition & 2 deletions iTriangle/src/float/convex.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use crate::float::delaunay::Delaunay;
use alloc::vec::Vec;
use i_overlay::i_float::float::compatible::FloatPointCompatible;
use i_overlay::i_float::float::number::FloatNumber;
use i_overlay::i_shape::base::data::Contour;
use i_overlay::i_shape::float::adapter::ShapeToFloat;

impl<P: FloatPointCompatible<T>, T: FloatNumber> Delaunay<P, T> {
impl<P: FloatPointCompatible> Delaunay<P> {
/// Groups triangles into non-overlapping convex polygons in counter-clockwise order.
///
/// Returns a list of float-based [`Contour<P>`]s.
Expand Down
49 changes: 24 additions & 25 deletions iTriangle/src/float/custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use crate::int::triangulation::RawIntTriangulation;
use crate::int::validation::Validation;
use i_overlay::i_float::adapter::FloatPointAdapter;
use i_overlay::i_float::float::compatible::FloatPointCompatible;
use i_overlay::i_float::float::number::FloatNumber;
use i_overlay::i_float::float::rect::FloatRect;
use i_overlay::i_shape::base::data::{Contour, Shape};
use i_overlay::i_shape::float::adapter::{PathToInt, ShapeToInt, ShapesToInt};
Expand All @@ -13,28 +12,28 @@ use i_overlay::i_shape::float::rect::RectInit;
/// A trait for triangulating float geometry with user-defined validation rules.
///
/// Accepts a custom [`Validation`] object for tuning fill rule, min area, etc.
pub trait CustomTriangulatable<P: FloatPointCompatible<T>, T: FloatNumber> {
pub trait CustomTriangulatable<P: FloatPointCompatible> {
/// Performs triangulation using the specified [`Validation`] settings.
fn custom_triangulate(&self, validation: Validation) -> RawTriangulation<P, T>;
fn custom_triangulate(&self, validation: Validation) -> RawTriangulation<P>;

/// Performs triangulation with Steiner points and a custom [`Validation`] config.
fn custom_triangulate_with_steiner_points(
&self,
points: &[P],
validation: Validation,
) -> RawTriangulation<P, T>;
) -> RawTriangulation<P>;
}

impl<P: FloatPointCompatible<T>, T: FloatNumber> CustomTriangulatable<P, T> for Contour<P> {
fn custom_triangulate(&self, validation: Validation) -> RawTriangulation<P, T> {
impl<P: FloatPointCompatible> CustomTriangulatable<P> for Contour<P> {
fn custom_triangulate(&self, validation: Validation) -> RawTriangulation<P> {
if let Some(rect) = FloatRect::with_path(self) {
let adapter = FloatPointAdapter::<P, T>::new(rect);
let adapter = FloatPointAdapter::<P>::new(rect);
let raw = self.to_int(&adapter).custom_triangulate(validation);
RawTriangulation { raw, adapter }
} else {
RawTriangulation {
raw: RawIntTriangulation::default(),
adapter: FloatPointAdapter::<P, T>::new(FloatRect::zero()),
adapter: FloatPointAdapter::<P>::new(FloatRect::zero()),
}
}
}
Expand All @@ -43,9 +42,9 @@ impl<P: FloatPointCompatible<T>, T: FloatNumber> CustomTriangulatable<P, T> for
&self,
points: &[P],
validation: Validation,
) -> RawTriangulation<P, T> {
) -> RawTriangulation<P> {
if let Some(rect) = FloatRect::with_path(self) {
let adapter = FloatPointAdapter::<P, T>::new(rect);
let adapter = FloatPointAdapter::<P>::new(rect);
let float_points = points.to_int(&adapter);
let raw = self
.to_int(&adapter)
Expand All @@ -54,22 +53,22 @@ impl<P: FloatPointCompatible<T>, T: FloatNumber> CustomTriangulatable<P, T> for
} else {
RawTriangulation {
raw: RawIntTriangulation::default(),
adapter: FloatPointAdapter::<P, T>::new(FloatRect::zero()),
adapter: FloatPointAdapter::<P>::new(FloatRect::zero()),
}
}
}
}

impl<P: FloatPointCompatible<T>, T: FloatNumber> CustomTriangulatable<P, T> for [Contour<P>] {
fn custom_triangulate(&self, validation: Validation) -> RawTriangulation<P, T> {
impl<P: FloatPointCompatible> CustomTriangulatable<P> for [Contour<P>] {
fn custom_triangulate(&self, validation: Validation) -> RawTriangulation<P> {
if let Some(rect) = FloatRect::with_paths(self) {
let adapter = FloatPointAdapter::<P, T>::new(rect);
let adapter = FloatPointAdapter::<P>::new(rect);
let raw = self.to_int(&adapter).custom_triangulate(validation);
RawTriangulation { raw, adapter }
} else {
RawTriangulation {
raw: RawIntTriangulation::default(),
adapter: FloatPointAdapter::<P, T>::new(FloatRect::zero()),
adapter: FloatPointAdapter::<P>::new(FloatRect::zero()),
}
}
}
Expand All @@ -78,9 +77,9 @@ impl<P: FloatPointCompatible<T>, T: FloatNumber> CustomTriangulatable<P, T> for
&self,
points: &[P],
validation: Validation,
) -> RawTriangulation<P, T> {
) -> RawTriangulation<P> {
if let Some(rect) = FloatRect::with_paths(self) {
let adapter = FloatPointAdapter::<P, T>::new(rect);
let adapter = FloatPointAdapter::<P>::new(rect);
let float_points = points.to_int(&adapter);
let raw = self
.to_int(&adapter)
Expand All @@ -89,22 +88,22 @@ impl<P: FloatPointCompatible<T>, T: FloatNumber> CustomTriangulatable<P, T> for
} else {
RawTriangulation {
raw: RawIntTriangulation::default(),
adapter: FloatPointAdapter::<P, T>::new(FloatRect::zero()),
adapter: FloatPointAdapter::<P>::new(FloatRect::zero()),
}
}
}
}

impl<P: FloatPointCompatible<T>, T: FloatNumber> CustomTriangulatable<P, T> for [Shape<P>] {
fn custom_triangulate(&self, validation: Validation) -> RawTriangulation<P, T> {
impl<P: FloatPointCompatible> CustomTriangulatable<P> for [Shape<P>] {
fn custom_triangulate(&self, validation: Validation) -> RawTriangulation<P> {
if let Some(rect) = FloatRect::with_list_of_paths(self) {
let adapter = FloatPointAdapter::<P, T>::new(rect);
let adapter = FloatPointAdapter::<P>::new(rect);
let raw = self.to_int(&adapter).custom_triangulate(validation);
RawTriangulation { raw, adapter }
} else {
RawTriangulation {
raw: RawIntTriangulation::default(),
adapter: FloatPointAdapter::<P, T>::new(FloatRect::zero()),
adapter: FloatPointAdapter::<P>::new(FloatRect::zero()),
}
}
}
Expand All @@ -113,9 +112,9 @@ impl<P: FloatPointCompatible<T>, T: FloatNumber> CustomTriangulatable<P, T> for
&self,
points: &[P],
validation: Validation,
) -> RawTriangulation<P, T> {
) -> RawTriangulation<P> {
if let Some(rect) = FloatRect::with_list_of_paths(self) {
let adapter = FloatPointAdapter::<P, T>::new(rect);
let adapter = FloatPointAdapter::<P>::new(rect);
let float_points = points.to_int(&adapter);
let raw = self
.to_int(&adapter)
Expand All @@ -124,7 +123,7 @@ impl<P: FloatPointCompatible<T>, T: FloatNumber> CustomTriangulatable<P, T> for
} else {
RawTriangulation {
raw: RawIntTriangulation::default(),
adapter: FloatPointAdapter::<P, T>::new(FloatRect::zero()),
adapter: FloatPointAdapter::<P>::new(FloatRect::zero()),
}
}
}
Expand Down
11 changes: 5 additions & 6 deletions iTriangle/src/float/delaunay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,28 @@ use crate::int::triangulation::IndexType;
use alloc::vec::Vec;
use i_overlay::i_float::adapter::FloatPointAdapter;
use i_overlay::i_float::float::compatible::FloatPointCompatible;
use i_overlay::i_float::float::number::FloatNumber;
use i_overlay::i_shape::float::adapter::PathToFloat;

/// A Delaunay-refined triangle mesh with float-mapped geometry.
///
/// Produced from [`Triangulation::into_delaunay`] by applying edge flips
/// to satisfy the Delaunay condition.
pub struct Delaunay<P: FloatPointCompatible<T>, T: FloatNumber> {
pub struct Delaunay<P: FloatPointCompatible> {
pub(super) delaunay: IntDelaunay,
pub(super) adapter: FloatPointAdapter<P, T>,
pub(super) adapter: FloatPointAdapter<P>,
}

impl<P: FloatPointCompatible<T>, T: FloatNumber> RawTriangulation<P, T> {
impl<P: FloatPointCompatible> RawTriangulation<P> {
#[inline]
pub fn into_delaunay(self) -> Delaunay<P, T> {
pub fn into_delaunay(self) -> Delaunay<P> {
Delaunay {
delaunay: self.raw.into_delaunay(),
adapter: self.adapter,
}
}
}

impl<P: FloatPointCompatible<T>, T: FloatNumber> Delaunay<P, T> {
impl<P: FloatPointCompatible> Delaunay<P> {
/// Returns the float-mapped vertex positions in the triangulation.
#[inline]
pub fn points(&self) -> Vec<P> {
Expand Down
Loading
Loading