| Crates.io | i_overlay |
| lib.rs | i_overlay |
| version | 4.2.2 |
| created_at | 2023-08-28 08:44:18.582108+00 |
| updated_at | 2026-01-25 15:04:51.273425+00 |
| description | Boolean Operations for 2D Polygons: Supports intersection, union, difference, xor, and self-intersections for all polygon varieties. |
| homepage | |
| repository | https://github.com/iShape-Rust/iOverlay |
| max_upload_size | |
| id | 956703 |
| size | 2,509,973 |
iOverlay is a high-performance polygon overlay engine for Rust. It solves robust boolean operations on complex polygons for GIS, CAD, and graphics workflows, built for developers who need reliable geometry at scale across integer and floating-point APIs.
iOverlay powers polygon boolean operations in geo.
iOverlay is optimized for large and complex inputs while preserving robust geometry semantics. The benchmark report compares iOverlay to other polygon overlay engines with a focus on throughput and performance.
See the detailed report: Performance Comparison
Add the following to your Cargo.toml:
[dependencies]
i_overlay = "^4.0"
Read full documentation
use i_overlay::core::fill_rule::FillRule;
use i_overlay::core::overlay_rule::OverlayRule;
use i_overlay::float::single::SingleFloatOverlay;
let subj = [[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0]];
let clip = [[2.0, 2.0], [6.0, 2.0], [6.0, 6.0], [2.0, 6.0]];
let result = subj.overlay(&clip, OverlayRule::Intersect, FillRule::EvenOdd);
println!("result: {:?}", result);
use i_overlay::core::fill_rule::FillRule;
use i_overlay::core::overlay_rule::OverlayRule;
use i_overlay::float::single::SingleFloatOverlay;
// Define the subject "O"
let subj = [
// main contour
vec![
[1.0, 0.0],
[4.0, 0.0],
[4.0, 5.0],
[1.0, 5.0], // the contour is auto closed!
],
// hole contour
vec![
[2.0, 1.0],
[2.0, 4.0],
[3.0, 4.0],
[3.0, 1.0], // the contour is auto closed!
],
];
// Define the clip "-"
let clip = [
// main contour
[0.0, 2.0],
[5.0, 2.0],
[5.0, 3.0],
[0.0, 3.0], // the contour is auto closed!
];
let result = subj.overlay(&clip, OverlayRule::Union, FillRule::EvenOdd);
println!("result: {:?}", result);
The result is a vec of shapes:
[
// first shape
[
// main contour (counterclockwise order)
[
[0.0, 3.0], [0.0, 2.0], [1.0, 2.0], [1.0, 0.0], [4.0, 0.0], [4.0, 2.0], [5.0, 2.0], [5.0, 3.0], [4.0, 3.0], [4.0, 5.0], [1.0, 5.0], [1.0, 3.0]
],
// first hole (clockwise order)
[
[2.0, 1.0], [2.0, 2.0], [3.0, 2.0], [3.0, 1.0]
],
// second hole (clockwise order)
[
[2.0, 3.0], [2.0, 4.0], [3.0, 4.0], [3.0, 3.0]
]
]
// ... other shapes if present
]
The overlay function returns a Vec<Shapes>:
Vec<Shape>: A collection of shapes.Shape: Represents a shape made up of:
Vec<Contour>: A list of contours.Contour: A sequence of points (Vec<P: FloatPointCompatible>) forming a closed contour.Note: By default, outer boundaries are counterclockwise and holes are clockwise—unless main_direction is set. More information about contours.
| A,B | A ∪ B | A ∩ B | A - B | B - A | A ⊕ B |
|---|---|---|---|---|---|
iOverlay allows users to define custom point types, as long as they implement the FloatPointCompatible trait.
use i_overlay::i_float::float::compatible::FloatPointCompatible;
use i_overlay::core::fill_rule::FillRule;
use i_overlay::core::overlay_rule::OverlayRule;
use i_overlay::float::single::SingleFloatOverlay;
#[derive(Clone, Copy, Debug)]
struct CustomPoint {
x: f32,
y: f32,
}
impl FloatPointCompatible<f32> for CustomPoint {
fn from_xy(x: f32, y: f32) -> Self {
Self { x, y }
}
fn x(&self) -> f32 {
self.x
}
fn y(&self) -> f32 {
self.y
}
}
let subj = [
CustomPoint { x: 0.0, y: 0.0 },
CustomPoint { x: 0.0, y: 3.0 },
CustomPoint { x: 3.0, y: 3.0 },
CustomPoint { x: 3.0, y: 0.0 },
];
let clip = [
CustomPoint { x: 1.0, y: 1.0 },
CustomPoint { x: 1.0, y: 2.0 },
CustomPoint { x: 2.0, y: 2.0 },
CustomPoint { x: 2.0, y: 1.0 },
];
let result = subj.overlay(&clip, OverlayRule::Difference, FillRule::EvenOdd);
println!("result: {:?}", result);
use i_overlay::core::fill_rule::FillRule;
use i_overlay::float::slice::FloatSlice;
let polygon = [
[1.0, 1.0],
[1.0, 4.0],
[4.0, 4.0],
[4.0, 1.0],
];
let slicing_line = [
[3.0, 5.0],
[2.0, 2.0],
[3.0, 3.0],
[2.0, 0.0],
];
let result = polygon.slice_by(&slicing_line, FillRule::NonZero);
println!("result: {:?}", result);
use i_overlay::core::fill_rule::FillRule;
use i_overlay::float::clip::FloatClip;
use i_overlay::string::clip::ClipRule;
let polygon = [
[1.0, 1.0],
[1.0, 4.0],
[4.0, 4.0],
[4.0, 1.0],
];
let string_line = [
[3.0, 5.0],
[2.0, 2.0],
[3.0, 3.0],
[2.0, 0.0],
];
let clip_rule = ClipRule { invert: false, boundary_included: false };
let result = string_line.clip_by(&polygon, FillRule::NonZero, clip_rule);
println!("result: {:?}", result);
use i_overlay::mesh::stroke::offset::StrokeOffset;
use i_overlay::mesh::style::{LineCap, LineJoin, StrokeStyle};
let path = [
[ 2.0, 1.0],
[ 5.0, 1.0],
[ 8.0, 4.0],
[11.0, 4.0],
[11.0, 1.0],
[ 8.0, 1.0],
[ 5.0, 4.0],
[ 2.0, 4.0],
];
let style = StrokeStyle::new(1.0)
.line_join(LineJoin::Miter(1.0))
.start_cap(LineCap::Round(0.1))
.end_cap(LineCap::Square);
let shapes = path.stroke(style, false);
println!("result: {:?}", shapes);
use i_overlay::mesh::outline::offset::OutlineOffset;
use i_overlay::mesh::style::{LineJoin, OutlineStyle};
let shape = vec![
vec![
[2.0, 1.0],
[4.0, 1.0],
[5.0, 2.0],
[13.0, 2.0],
[13.0, 3.0],
[12.0, 3.0],
[12.0, 4.0],
[11.0, 4.0],
[11.0, 3.0],
[10.0, 3.0],
[9.0, 4.0],
[8.0, 4.0],
[8.0, 3.0],
[5.0, 3.0],
[5.0, 4.0],
[4.0, 5.0],
[2.0, 5.0],
[1.0, 4.0],
[1.0, 2.0]
],
vec![
[2.0, 4.0],
[4.0, 4.0],
[4.0, 2.0],
[2.0, 2.0]
],
];
let style = OutlineStyle::new(0.2).line_join(LineJoin::Round(0.1));
let shapes = shape.outline(&style);
println!("shapes: {:?}", &shapes);
Note:
Offsetting a polygon works reliably only with valid polygons. Ensure that:
main_direction is set.If polygon validity cannot be guaranteed, it is recommended to apply the simplify_shape operation before offsetting.
More information on contour orientation.
Using LineJoin::Bevel with a large offset may produce visual artifacts.
| Butt | Square | Round | Custom |
|---|---|---|---|
| Bevel | Miter | Round |
|---|---|---|
FloatOverlay, SingleFloatOverlay, or FloatOverlayGraph?Use FloatOverlay when you perform repeated overlay operations:
let mut overlay = FloatOverlay::new();
loop {
overlay.clear();
overlay.add_source(shape);
let result = overlay.overlay(overlay_rule, fill_rule);
// ...
}
Use SingleFloatOverlay trait for one-shot operations.
Use FloatOverlayGraph if you need to extract multiple boolean results (e.g. union and intersection) from the same input geometry without recomputing.
Use the simplify operation:
let result = shapes.simplify(fill_rule);
It internally merges shapes efficiently and is typically faster and more robust than chaining many overlay() calls manually.
Use FixedScaleFloatOverlay or FloatOverlay::with_subj_and_clip_fixed_scale. The scale is
scale = 1.0 / grid_size.
use i_overlay::core::fill_rule::FillRule;
use i_overlay::core::overlay_rule::OverlayRule;
use i_overlay::float::scale::FixedScaleFloatOverlay;
let subj = vec![[0.0, 0.0], [0.0, 5.0], [5.0, 5.0], [5.0, 0.0]];
let clip = vec![[2.0, 2.0], [2.0, 4.0], [4.0, 4.0], [4.0, 2.0]];
let grid_size = 0.001;
let scale = 1.0 / grid_size;
let result = subj
.overlay_with_fixed_scale(&clip, OverlayRule::Difference, FillRule::EvenOdd, scale)
.expect("scale does not fit input bounds");
If you need more control, use FloatPointAdapter::with_scale and FloatOverlay::with_adapter.
Set the ocg flag in OverlayOptions.
use i_overlay::core::fill_rule::FillRule;
use i_overlay::core::overlay_rule::OverlayRule;
use i_overlay::float::overlay::{FloatOverlay, OverlayOptions};
// 0 1 2 3 4 5
// 5 ┌───────────────────┐
// │ │
// 4 │ ┌───────┐ │
// │ │ ░ ░ │ │ Two L-shaped holes share vertices at (2,2) and (3,3)
// 3 │ │ ┌───●───┐ │
// │ │ ░ │ │ ░ │ │ ░ = holes
// 2 │ └───●───┘ │ │
// │ │ ░ ░ │ │ The shared edge disconnects the interior
// 1 │ └───────┘ │
// │ │
// 0 └───────────────────┘
//
// OGC Simple Feature Specification (ISO 19125-1) states:
// "The interior of every Surface is a connected point set."
let subj = vec![vec![[0.0, 0.0], [5.0, 0.0], [5.0, 5.0], [0.0, 5.0]]];
let clip = vec![
vec![[1.0, 2.0], [1.0, 4.0], [3.0, 4.0], [3.0, 3.0], [2.0, 3.0], [2.0, 2.0]],
vec![[2.0, 1.0], [2.0, 2.0], [3.0, 2.0], [3.0, 3.0], [4.0, 3.0], [4.0, 1.0]],
];
let options = OverlayOptions::<f64>::ocg();
let mut overlay = FloatOverlay::with_subj_and_clip_custom(&subj, &clip, options, Default::default());
let result = overlay.overlay(OverlayRule::Difference, FillRule::EvenOdd);
assert_eq!(result.len(), 2);
This crate follows a pragmatic versioning approach:
PATCH updates (e.g., 1.8.1 → 1.8.2): Guaranteed to be backward-compatible, containing only bug fixes or small improvements.
MINOR updates (e.g., 1.8.0 → 1.9.0): Typically backward-compatible but may include changes to experimental or less commonly used APIs.
MAJOR updates (e.g., 1.x.x → 2.x.x): Reserved for significant breaking changes or major redesigns.
To minimize disruption, consider pinning dependencies when relying on specific versions.
Licensed under either of: