From 39397bbd254aa746cb12b8e663348c54ba4addcd Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Fri, 3 Jul 2026 11:37:36 +0300 Subject: [PATCH 01/13] rename to_wide --- iOverlay/src/mesh/stroke/offset.rs | 4 ++-- iOverlay/src/split/cross_solver.rs | 16 ++++++++-------- iOverlay/src/split/grid_layout.rs | 6 +++--- iOverlay/src/string/split.rs | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/iOverlay/src/mesh/stroke/offset.rs b/iOverlay/src/mesh/stroke/offset.rs index 150e5253..baed7c39 100644 --- a/iOverlay/src/mesh/stroke/offset.rs +++ b/iOverlay/src/mesh/stroke/offset.rs @@ -507,7 +507,7 @@ where is_closed_path: bool, options: OverlayOptions, ) -> Shapes

{ - let ir = self.adapter.round_len_to_int(self.r).wide().unsigned_abs(); + let ir = self.adapter.round_len_to_int(self.r).to_wide().unsigned_abs(); if ir <= I::WideUInt::ONE { // offset is too small return vec![]; @@ -548,7 +548,7 @@ where options: OverlayOptions, output: &mut FloatFlatContoursBuffer

, ) { - let ir = self.adapter.round_len_to_int(self.r).wide().unsigned_abs(); + let ir = self.adapter.round_len_to_int(self.r).to_wide().unsigned_abs(); if ir <= I::WideUInt::ONE { // offset is too small output.clear_and_reserve(0, 0); diff --git a/iOverlay/src/split/cross_solver.rs b/iOverlay/src/split/cross_solver.rs index 993f221d..c93cffcc 100644 --- a/iOverlay/src/split/cross_solver.rs +++ b/iOverlay/src/split/cross_solver.rs @@ -249,18 +249,18 @@ impl CrossSolver { // move a0.x to 0 // move all by a0.x - let a0x = target.a.x.wide(); - let a0y = target.a.y.wide(); + let a0x = target.a.x.to_wide(); + let a0y = target.a.y.to_wide(); - let a1x = target.b.x.wide() - a0x; - let b0x = other.a.x.wide() - a0x; - let b1x = other.b.x.wide() - a0x; + let a1x = target.b.x.to_wide() - a0x; + let b0x = other.a.x.to_wide() - a0x; + let b1x = other.b.x.to_wide() - a0x; // move a0.y to 0 // move all by a0.y - let a1y = target.b.y.wide() - a0y; - let b0y = other.a.y.wide() - a0y; - let b1y = other.b.y.wide() - a0y; + let a1y = target.b.y.to_wide() - a0y; + let b0y = other.a.y.to_wide() - a0y; + let b1y = other.b.y.to_wide() - a0y; let dy_b = b0y - b1y; let dx_b = b0x - b1x; diff --git a/iOverlay/src/split/grid_layout.rs b/iOverlay/src/split/grid_layout.rs index f56f25f0..2a90eaab 100644 --- a/iOverlay/src/split/grid_layout.rs +++ b/iOverlay/src/split/grid_layout.rs @@ -112,7 +112,7 @@ impl FragmentBuffer { let is_inc = s.a.y <= s.b.y; let width = (s.b.x - s.a.x).to_uint(); - let height = (s.b.y.wide() - s.a.y.wide()).unsigned_abs(); + let height = (s.b.y.to_wide() - s.a.y.to_wide()).unsigned_abs(); let log = (width * height).ilog2(); let p = I::WideUInt::LAST_BIT_INDEX - log; @@ -257,7 +257,7 @@ pub(super) struct GridLayout { impl GridLayout { #[inline] pub(super) fn index(&self, x: I) -> usize { - ((x.wide() - self.min_x.wide()) >> self.power).to_usize() + ((x.to_wide() - self.min_x.to_wide()) >> self.power).to_usize() } #[inline] @@ -285,7 +285,7 @@ impl GridLayout { } fn with_min_max(min_x: I, max_x: I, max_power: u32) -> Option { - let dx = max_x.wide() - min_x.wide(); + let dx = max_x.to_wide() - min_x.to_wide(); if dx < I::Wide::FOUR { return None; } diff --git a/iOverlay/src/string/split.rs b/iOverlay/src/string/split.rs index 9484b161..782e5ba0 100644 --- a/iOverlay/src/string/split.rs +++ b/iOverlay/src/string/split.rs @@ -154,8 +154,8 @@ impl BinStore { #[inline] fn bin_index(&self, p: IntPoint) -> usize { - let x = p.x.wide().wrapping_mul(I::Wide::from_usize(31)); - let y = p.y.wide().wrapping_mul(I::Wide::from_usize(17)); + let x = p.x.to_wide().wrapping_mul(I::Wide::from_usize(31)); + let y = p.y.to_wide().wrapping_mul(I::Wide::from_usize(17)); let hash = x.wrapping_add(y); (hash & I::Wide::from_usize(self.mask as usize)).to_usize() } From d64858a29aa236b6486970db3860d1f6d1a35c64 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Fri, 3 Jul 2026 11:38:00 +0300 Subject: [PATCH 02/13] local path --- iOverlay/Cargo.toml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/iOverlay/Cargo.toml b/iOverlay/Cargo.toml index 5dce7d66..ce825483 100644 --- a/iOverlay/Cargo.toml +++ b/iOverlay/Cargo.toml @@ -12,13 +12,13 @@ readme = "README.md" categories = ["algorithms", "graphics", "science::geo", "mathematics", "no-std"] [dependencies] -i_float = { version = "^3.0.0" } -i_shape = { version = "^3.0.0" } +#i_float = { version = "^3.0.0" } +#i_shape = { version = "^3.0.0" } i_tree = { version = "^0.19.0" } i_key_sort = { version = "^0.10.3" } -#i_float = { path = "../../iFloat"} -#i_shape = { path = "../../iShape"} +i_float = { path = "../../iFloat"} +i_shape = { path = "../../iShape"} #i_tree = { path = "../../iTree" } #i_key_sort = { path = "../../iKeySort" } @@ -36,7 +36,7 @@ allow_multithreading = ["dep:rayon", "i_key_sort/allow_multithreading"] serde = { version = "^1.0", features = ["derive"] } serde_json = "^1.0" rand = { version = "~0.10", features = ["alloc"] } -#i_float = { path = "../../iFloat", features = ["serde"] } -#i_shape = { path = "../../iShape", features = ["serde"] } -i_float = { version = "^3.0.0", features = ["serde"] } -i_shape = { version = "^3.0.0", features = ["serde"] } +i_float = { path = "../../iFloat", features = ["serde"] } +i_shape = { path = "../../iShape", features = ["serde"] } +#i_float = { version = "^3.0.0", features = ["serde"] } +#i_shape = { version = "^3.0.0", features = ["serde"] } From 52aeae3a68f7a6245144d9d450040b578643c835 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Tue, 21 Jul 2026 14:47:49 +0300 Subject: [PATCH 03/13] open public api --- iOverlay/src/core/mod.rs | 1 + iOverlay/src/core/v_segment.rs | 1 + iOverlay/src/geom/v_segment.rs | 13 +++++++++---- 3 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 iOverlay/src/core/v_segment.rs diff --git a/iOverlay/src/core/mod.rs b/iOverlay/src/core/mod.rs index dff945b8..107ab834 100644 --- a/iOverlay/src/core/mod.rs +++ b/iOverlay/src/core/mod.rs @@ -13,3 +13,4 @@ pub mod predicate; pub mod relate; pub mod simplify; pub mod solver; +pub mod v_segment; diff --git a/iOverlay/src/core/v_segment.rs b/iOverlay/src/core/v_segment.rs new file mode 100644 index 00000000..d7328b66 --- /dev/null +++ b/iOverlay/src/core/v_segment.rs @@ -0,0 +1 @@ +pub use crate::geom::v_segment::VSegment; diff --git a/iOverlay/src/geom/v_segment.rs b/iOverlay/src/geom/v_segment.rs index f8449d9a..154e1546 100644 --- a/iOverlay/src/geom/v_segment.rs +++ b/iOverlay/src/geom/v_segment.rs @@ -7,12 +7,17 @@ use i_float::triangle::Triangle; use i_tree::{Expiration, ExpiredKey}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) struct VSegment { - pub(crate) a: IntPoint, - pub(crate) b: IntPoint, +pub struct VSegment { + pub a: IntPoint, + pub b: IntPoint, } impl VSegment { + #[inline(always)] + pub const fn new(a: IntPoint, b: IntPoint) -> Self { + Self { a, b } + } + #[inline(always)] fn is_under_segment_order(&self, other: &VSegment) -> Ordering { match self.a.cmp(&other.a) { @@ -40,7 +45,7 @@ impl VSegment { } #[inline(always)] - pub(crate) fn cmp_by_angle(&self, other: &Self) -> Ordering { + pub fn cmp_by_angle(&self, other: &Self) -> Ordering { // sort angles counterclockwise // debug_assert!(self.a == other.a); let v0 = self.b - self.a; From 8281f9b5cd5210468325200630e69f2fa5e039de Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sun, 26 Jul 2026 22:59:08 +0300 Subject: [PATCH 04/13] fix clippy --- iOverlay/src/geom/v_segment.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOverlay/src/geom/v_segment.rs b/iOverlay/src/geom/v_segment.rs index 154e1546..344dd0ab 100644 --- a/iOverlay/src/geom/v_segment.rs +++ b/iOverlay/src/geom/v_segment.rs @@ -63,7 +63,7 @@ impl BottomSegment for Option> { #[inline(always)] fn update_if_under(&mut self, segment: VSegment) { if let Some(best) = self { - if segment.is_under_segment(&best) { + if segment.is_under_segment(best) { *best = segment } } else { From 81834e396d2d936b415a6d5bbc71d32c28424f0b Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sat, 1 Aug 2026 20:10:11 +0300 Subject: [PATCH 05/13] add OverlayInt wrapper --- iOverlay/Cargo.toml | 4 +-- iOverlay/README.md | 2 +- iOverlay/src/core/edge_overlay.rs | 9 +++---- iOverlay/src/core/extract.rs | 5 ++-- iOverlay/src/core/extract_ogc.rs | 6 ++--- iOverlay/src/core/integer.rs | 25 ++++++++++++++++++ iOverlay/src/core/mod.rs | 1 + iOverlay/src/core/overlay.rs | 8 +++--- iOverlay/src/core/relate.rs | 8 +++--- iOverlay/src/core/simplify.rs | 11 ++++---- iOverlay/src/float/clip.rs | 20 +++++++-------- iOverlay/src/float/graph.rs | 5 ++-- iOverlay/src/float/overlay.rs | 7 +++-- iOverlay/src/float/relate.rs | 36 ++++++++++++-------------- iOverlay/src/float/scale.rs | 36 ++++++++++++-------------- iOverlay/src/float/simplify.rs | 12 ++++----- iOverlay/src/float/single.rs | 8 +++--- iOverlay/src/float/slice.rs | 20 +++++++-------- iOverlay/src/float/string_graph.rs | 5 ++-- iOverlay/src/float/string_overlay.rs | 8 +++--- iOverlay/src/mesh/outline/offset.rs | 37 +++++++++++++-------------- iOverlay/src/mesh/overlay.rs | 5 ++-- iOverlay/src/mesh/stroke/offset.rs | 37 +++++++++++++-------------- iOverlay/src/split/solver.rs | 5 ++-- iOverlay/src/split/solver_fragment.rs | 6 ++--- iOverlay/src/split/solver_list.rs | 6 ++--- iOverlay/src/split/solver_tree.rs | 6 ++--- iOverlay/src/string/clip.rs | 9 +++---- iOverlay/src/string/extract.rs | 6 ++--- iOverlay/src/string/overlay.rs | 8 +++--- iOverlay/src/string/slice.rs | 9 +++---- iOverlay/src/vector/extract.rs | 3 ++- iOverlay/tests/crash_tests.rs | 7 +++-- iOverlay/tests/dynamic_tests.rs | 7 +++-- 34 files changed, 186 insertions(+), 201 deletions(-) create mode 100644 iOverlay/src/core/integer.rs diff --git a/iOverlay/Cargo.toml b/iOverlay/Cargo.toml index ce825483..c7215acb 100644 --- a/iOverlay/Cargo.toml +++ b/iOverlay/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "i_overlay" -version = "7.0.2" +version = "8.0.0" authors = ["Nail Sharipov "] edition = "2024" rust-version = "1.88" @@ -15,7 +15,7 @@ categories = ["algorithms", "graphics", "science::geo", "mathematics", "no-std"] #i_float = { version = "^3.0.0" } #i_shape = { version = "^3.0.0" } i_tree = { version = "^0.19.0" } -i_key_sort = { version = "^0.10.3" } +i_key_sort = { version = "^0.11.0" } i_float = { path = "../../iFloat"} i_shape = { path = "../../iShape"} diff --git a/iOverlay/README.md b/iOverlay/README.md index b7a70883..545d0c60 100644 --- a/iOverlay/README.md +++ b/iOverlay/README.md @@ -87,7 +87,7 @@ See the detailed reports: [Performance Comparison](https://ishape-rust.github.io Add the following to your Cargo.toml: ```toml [dependencies] -i_overlay = "^7.0" +i_overlay = "^8.0" ``` Read full [documentation](https://ishape-rust.github.io/iShape-js/overlay/doc.html) diff --git a/iOverlay/src/core/edge_overlay.rs b/iOverlay/src/core/edge_overlay.rs index 342089bc..5b6da03e 100644 --- a/iOverlay/src/core/edge_overlay.rs +++ b/iOverlay/src/core/edge_overlay.rs @@ -3,6 +3,7 @@ use crate::core::edge_data::OverlayEdgeData; use crate::core::extract::BooleanExtractionBuffer; use crate::core::fill_rule::FillRule; use crate::core::graph::OverlayNode; +use crate::core::integer::OverlayInt; use crate::core::overlay::{IntOverlayOptions, ShapeType}; use crate::core::overlay_rule::OverlayRule; use crate::core::solver::Solver; @@ -13,8 +14,6 @@ use crate::vector::edge::{DataVectorEdge, DataVectorShape}; use alloc::vec::Vec; use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; -use i_key_sort::sort::key::SortKey; -use i_tree::{Expiration, LayoutNumber}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct InputEdge { @@ -23,7 +22,7 @@ pub struct InputEdge { pub data: D, } -pub struct EdgeOverlay { +pub struct EdgeOverlay { pub solver: Solver, pub options: IntOverlayOptions, pub boolean_buffer: Option>, @@ -35,7 +34,7 @@ pub struct EdgeOverlay { impl EdgeOverlay where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, D: OverlayEdgeData, { pub fn new(capacity: usize) -> Self { @@ -133,7 +132,7 @@ where impl EdgeOverlay where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, D: OverlayEdgeData, { pub fn edges(&self) -> impl Iterator; 2]> + '_ { diff --git a/iOverlay/src/core/extract.rs b/iOverlay/src/core/extract.rs index e5c3ace7..93a49369 100644 --- a/iOverlay/src/core/extract.rs +++ b/iOverlay/src/core/extract.rs @@ -2,6 +2,7 @@ use super::overlay_rule::OverlayRule; use crate::bind::segment::{ContourIndex, IdSegment}; use crate::bind::solver::{JoinHoles, LeftBottomSegment}; use crate::core::graph::{OverlayGraph, OverlayNode}; +use crate::core::integer::OverlayInt; use crate::core::link::OverlayLink; use crate::core::link::OverlayLinkFilter; use crate::core::nearest_vector::NearestVector; @@ -14,12 +15,10 @@ use i_float::int::number::uint::UIntNumber; use i_float::int::number::wide_int::WideIntNumber; use i_float::int::point::IntPoint; use i_float::triangle::Triangle; -use i_key_sort::sort::key::SortKey; use i_shape::int::path::ContourExtension; use i_shape::int::shape::{IntContour, IntShapes}; use i_shape::int::simple::Simplify; use i_shape::util::reserve::Reserve; -use i_tree::Expiration; #[repr(u8)] #[derive(Copy, Clone, PartialEq, Default)] @@ -49,7 +48,7 @@ impl Default for BooleanExtractionBuffer { impl OverlayGraph<'_, I> where - I: IntNumber + Expiration + SortKey, + I: OverlayInt, { /// Extracts shapes from the overlay graph based on the specified overlay rule. This method is used to retrieve the final geometric shapes after boolean operations have been applied. It's suitable for most use cases where the minimum area of shapes is not a concern. /// - `overlay_rule`: The boolean operation rule to apply when extracting shapes from the graph, such as union or intersection. diff --git a/iOverlay/src/core/extract_ogc.rs b/iOverlay/src/core/extract_ogc.rs index d4dcda86..d8ef3dc6 100644 --- a/iOverlay/src/core/extract_ogc.rs +++ b/iOverlay/src/core/extract_ogc.rs @@ -4,20 +4,18 @@ use crate::core::extract::{ BooleanExtractionBuffer, GraphContour, GraphUtil, StartPathData, Visit, VisitState, }; use crate::core::graph::OverlayGraph; +use crate::core::integer::OverlayInt; use crate::core::overlay::ContourDirection; use crate::core::overlay_rule::OverlayRule; use alloc::vec; use alloc::vec::Vec; -use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; -use i_key_sort::sort::key::SortKey; use i_shape::int::shape::{IntShape, IntShapes}; use i_shape::util::reserve::Reserve; -use i_tree::Expiration; impl OverlayGraph<'_, I> where - I: IntNumber + Expiration + SortKey, + I: OverlayInt, { pub(crate) fn extract_ogc( &self, diff --git a/iOverlay/src/core/integer.rs b/iOverlay/src/core/integer.rs new file mode 100644 index 00000000..55702e21 --- /dev/null +++ b/iOverlay/src/core/integer.rs @@ -0,0 +1,25 @@ +//! Integer types supported by the overlay engine. + +use i_float::int::number::int::IntNumber; +use i_key_sort::sort::key::SortKey; +use i_tree::{Expiration, LayoutNumber}; + +mod private { + use super::{Expiration, IntNumber, LayoutNumber, SortKey}; + + pub trait OverlayIntSealed: IntNumber + Expiration + LayoutNumber + SortKey {} + + impl OverlayIntSealed for i16 {} + impl OverlayIntSealed for i32 {} + impl OverlayIntSealed for i64 {} +} + +/// An integer type supported by the overlay engine. +/// +/// This trait is sealed. The supported integer engines are [`i16`], [`i32`], +/// and [`i64`]. +pub trait OverlayInt: private::OverlayIntSealed {} + +impl OverlayInt for i16 {} +impl OverlayInt for i32 {} +impl OverlayInt for i64 {} diff --git a/iOverlay/src/core/mod.rs b/iOverlay/src/core/mod.rs index 107ab834..7b4c715b 100644 --- a/iOverlay/src/core/mod.rs +++ b/iOverlay/src/core/mod.rs @@ -5,6 +5,7 @@ pub mod extract; mod extract_ogc; pub mod fill_rule; pub mod graph; +pub mod integer; pub(crate) mod link; pub(crate) mod nearest_vector; pub mod overlay; diff --git a/iOverlay/src/core/overlay.rs b/iOverlay/src/core/overlay.rs index 7074f3e0..85d1be58 100644 --- a/iOverlay/src/core/overlay.rs +++ b/iOverlay/src/core/overlay.rs @@ -5,6 +5,7 @@ use crate::build::builder::GraphBuilder; use crate::core::extract::BooleanExtractionBuffer; use crate::core::fill_rule::FillRule; +use crate::core::integer::OverlayInt; use crate::core::overlay_rule::OverlayRule; use crate::core::solver::Solver; use crate::i_shape::flat::buffer::FlatContoursBuffer; @@ -14,13 +15,10 @@ use crate::segm::segment::Segment; use crate::split::solver::SplitSolver; use crate::vector::edge::{DataVectorEdge, VectorShape}; use alloc::vec::Vec; -use i_float::int::number::int::IntNumber; use i_float::int::number::uint::UIntNumber; use i_float::int::point::IntPoint; -use i_key_sort::sort::key::SortKey; use i_shape::int::count::PointsCount; use i_shape::int::shape::{IntContour, IntShape, IntShapes}; -use i_tree::{Expiration, LayoutNumber}; use super::graph::{OverlayGraph, OverlayNode}; @@ -65,7 +63,7 @@ pub enum ContourDirection { } /// This struct is essential for describing and uploading the geometry or shapes required to construct an `OverlayGraph`. It prepares the necessary data for boolean operations. -pub struct Overlay { +pub struct Overlay { pub solver: Solver, pub options: IntOverlayOptions, pub boolean_buffer: Option>, @@ -76,7 +74,7 @@ pub struct Overlay { impl Overlay where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { /// Constructs a new `Overlay` instance, initializing it with a capacity that should closely match the total count of edges from all shapes being processed. /// This pre-allocation helps in optimizing memory usage and performance. diff --git a/iOverlay/src/core/relate.rs b/iOverlay/src/core/relate.rs index 602e98e0..3caf0d14 100644 --- a/iOverlay/src/core/relate.rs +++ b/iOverlay/src/core/relate.rs @@ -1,5 +1,6 @@ use crate::build::sweep::{FillHandler, SweepRunner}; use crate::core::fill_rule::FillRule; +use crate::core::integer::OverlayInt; use crate::core::overlay::ShapeType; use crate::core::predicate::{ InteriorsIntersectHandler, IntersectsHandler, PointIntersectsHandler, TouchesHandler, WithinHandler, @@ -10,11 +11,8 @@ use crate::segm::build::BuildSegments; use crate::segm::segment::Segment; use crate::split::solver::SplitSolver; use alloc::vec::Vec; -use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; -use i_key_sort::sort::key::SortKey; use i_shape::int::shape::{IntContour, IntShape}; -use i_tree::{Expiration, LayoutNumber}; /// Overlay structure optimized for spatial predicate evaluation. /// @@ -36,7 +34,7 @@ use i_tree::{Expiration, LayoutNumber}; /// /// For float coordinates, prefer using [`FloatPredicateOverlay`](crate::float::relate::FloatPredicateOverlay) /// or the [`FloatRelate`](crate::float::relate::FloatRelate) trait. -pub struct PredicateOverlay { +pub struct PredicateOverlay { /// Solver configuration for segment operations. pub solver: Solver, /// Fill rule for determining polygon interiors. @@ -48,7 +46,7 @@ pub struct PredicateOverlay { impl PredicateOverlay where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { #[inline] pub fn new(capacity: usize) -> Self { diff --git a/iOverlay/src/core/simplify.rs b/iOverlay/src/core/simplify.rs index 29f15572..12ee8b1b 100644 --- a/iOverlay/src/core/simplify.rs +++ b/iOverlay/src/core/simplify.rs @@ -2,6 +2,7 @@ //! (e.g., removing small artifacts or shapes below a certain area threshold) based on a build rule. use crate::core::fill_rule::FillRule; +use crate::core::integer::OverlayInt; use crate::core::overlay::ContourDirection; use crate::core::overlay::ContourDirection::Clockwise; use crate::core::overlay::{IntOverlayOptions, Overlay, ShapeType}; @@ -9,14 +10,12 @@ use crate::core::overlay_rule::OverlayRule; use crate::i_float::int::number::int::IntNumber; use crate::i_float::int::point::IntPoint; use alloc::vec; -use i_key_sort::sort::key::SortKey; use i_shape::flat::buffer::FlatContoursBuffer; use crate::segm::build::BuildSegments; use i_shape::int::count::PointsCount; use i_shape::int::path::ContourExtension; use i_shape::int::shape::{IntContour, IntShape, IntShapes}; -use i_tree::{Expiration, LayoutNumber}; /// Trait `Simplify` provides a method to simplify geometric shapes by reducing the number of points in contours or shapes /// while preserving overall shape and topology. The method applies a minimum area threshold and a build rule to @@ -38,7 +37,7 @@ pub trait Simplify { impl Simplify for [IntPoint] where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { #[inline] fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { @@ -51,7 +50,7 @@ where impl Simplify for [IntContour] where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { #[inline] fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { @@ -64,7 +63,7 @@ where impl Simplify for [IntShape] where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { #[inline] fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { @@ -80,7 +79,7 @@ enum ContourFillDirection { impl Overlay where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { /// Fast-path simplification for a single contour. /// diff --git a/iOverlay/src/float/clip.rs b/iOverlay/src/float/clip.rs index 85969c7a..a438d002 100644 --- a/iOverlay/src/float/clip.rs +++ b/iOverlay/src/float/clip.rs @@ -1,14 +1,12 @@ use crate::core::fill_rule::FillRule; +use crate::core::integer::OverlayInt; use crate::core::solver::Solver; use crate::float::scale::FixedScaleOverlayError; use crate::float::string_overlay::FloatStringOverlay; use crate::string::clip::ClipRule; use i_float::float::compatible::FloatPointCompatible; -use i_float::int::number::int::IntNumber; -use i_key_sort::sort::key::SortKey; use i_shape::base::data::Paths; use i_shape::source::resource::ShapeResource; -use i_tree::{Expiration, LayoutNumber}; /// Trait for clipping float string paths by float shapes. /// @@ -51,7 +49,7 @@ where /// Same as [`Self::clip_by`], but with an explicit integer engine. fn clip_by_as(&self, source: &R, fill_rule: FillRule, clip_rule: ClipRule) -> Paths

where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Clips paths according to the specified build and clip rules using a fixed float-to-integer scale. /// - `resource`: A clipping shape. @@ -82,7 +80,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Clips paths according to the specified build and clip rules. /// - `resource`: A clipping shape. @@ -113,7 +111,7 @@ where solver: Solver, ) -> Paths

where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Clips paths according to the specified build and clip rules using a fixed float-to-integer scale. /// - `resource`: A clipping shape. @@ -147,7 +145,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; } #[cfg(test)] @@ -227,7 +225,7 @@ where #[inline] fn clip_by_as(&self, resource: &R0, fill_rule: FillRule, clip_rule: ClipRule) -> Paths

where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { self.clip_by_with_solver_as::(resource, fill_rule, clip_rule, Default::default()) } @@ -253,7 +251,7 @@ where solver: Solver, ) -> Paths

where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { FloatStringOverlay::::from_shape_and_string(resource, self) .clip_string_lines_with_solver(fill_rule, clip_rule, solver) @@ -279,7 +277,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { self.clip_by_fixed_scale_with_solver_as::( resource, @@ -315,7 +313,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { Ok( FloatStringOverlay::::from_shape_and_string_fixed_scale(resource, self, scale)? diff --git a/iOverlay/src/float/graph.rs b/iOverlay/src/float/graph.rs index caa987a5..36303699 100644 --- a/iOverlay/src/float/graph.rs +++ b/iOverlay/src/float/graph.rs @@ -4,16 +4,15 @@ use crate::core::extract::BooleanExtractionBuffer; use crate::core::graph::OverlayGraph; +use crate::core::integer::OverlayInt; use crate::core::overlay_rule::OverlayRule; use i_float::adapter::FloatPointAdapter; use i_float::float::compatible::FloatPointCompatible; use i_float::int::number::int::IntNumber; -use i_key_sort::sort::key::SortKey; use i_shape::base::data::Shapes; use i_shape::float::adapter::ShapesToFloat; use i_shape::float::despike::DeSpikeContour; use i_shape::float::simple::SimplifyContour; -use i_tree::{Expiration, LayoutNumber}; /// The `FloatOverlayGraph` struct represents an overlay graph with floating point precision, /// providing methods to extract geometric shapes from the graph after applying boolean operations. @@ -27,7 +26,7 @@ pub struct FloatOverlayGraph<'a, P: FloatPointCompatible, I: IntNumber = i32> { impl<'a, P, I> FloatOverlayGraph<'a, P, I> where P: FloatPointCompatible, - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { #[inline] pub(crate) fn new( diff --git a/iOverlay/src/float/overlay.rs b/iOverlay/src/float/overlay.rs index 52b48d21..e8999178 100644 --- a/iOverlay/src/float/overlay.rs +++ b/iOverlay/src/float/overlay.rs @@ -3,6 +3,7 @@ //! manage subject and clip polygons and convert them into graphs for further operations. use crate::core::fill_rule::FillRule; +use crate::core::integer::OverlayInt; use crate::core::overlay::{ContourDirection, IntOverlayOptions, Overlay, ShapeType}; use crate::core::overlay_rule::OverlayRule; use crate::core::solver::Solver; @@ -16,14 +17,12 @@ use i_float::float::rect::FloatRect; use i_float::int::number::int::IntNumber; use i_float::int::number::uint::UIntNumber; use i_float::int::number::wide_int::WideIntNumber; -use i_key_sort::sort::key::SortKey; use i_shape::base::data::Shapes; use i_shape::flat::buffer::FlatContoursBuffer; use i_shape::flat::float::FloatFlatContoursBuffer; use i_shape::float::adapter::ShapesToFloat; use i_shape::float::despike::DeSpikeContour; use i_shape::float::simple::SimplifyContour; -use i_tree::{Expiration, LayoutNumber}; /// Options for float overlay extraction. /// @@ -55,7 +54,7 @@ pub struct OverlayOptions { } /// This struct is essential for describing and uploading the geometry or shapes required to construct an `FloatOverlay`. It prepares the necessary data for boolean operations. -pub struct FloatOverlay { +pub struct FloatOverlay { pub(super) overlay: Overlay, pub(super) clean_result: bool, pub(super) adapter: FloatPointAdapter, @@ -64,7 +63,7 @@ pub struct FloatOverlay FloatOverlay where P: FloatPointCompatible, - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { /// Constructs a new `FloatOverlay`, a builder for overlaying geometric shapes /// by converting float-based geometry to integer space, using a pre-configured adapter. diff --git a/iOverlay/src/float/relate.rs b/iOverlay/src/float/relate.rs index 7ac97a9f..0145741d 100644 --- a/iOverlay/src/float/relate.rs +++ b/iOverlay/src/float/relate.rs @@ -1,13 +1,11 @@ use crate::core::fill_rule::FillRule; +use crate::core::integer::OverlayInt; use crate::core::overlay::ShapeType; use crate::core::relate::PredicateOverlay; use crate::core::solver::Solver; use i_float::adapter::FloatPointAdapter; use i_float::float::compatible::FloatPointCompatible; -use i_float::int::number::int::IntNumber; -use i_key_sort::sort::key::SortKey; use i_shape::source::resource::ShapeResource; -use i_tree::{Expiration, LayoutNumber}; /// Float-coordinate wrapper for spatial predicate evaluation. /// @@ -29,7 +27,7 @@ use i_tree::{Expiration, LayoutNumber}; /// /// For a more ergonomic API, see the [`FloatRelate`] trait which provides /// methods directly on shape types. -pub struct FloatPredicateOverlay { +pub struct FloatPredicateOverlay { pub(crate) overlay: PredicateOverlay, pub(crate) adapter: FloatPointAdapter, } @@ -37,7 +35,7 @@ pub struct FloatPredicateOverlay FloatPredicateOverlay where P: FloatPointCompatible, - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { /// Creates a new predicate overlay with a pre-configured adapter. /// @@ -259,7 +257,7 @@ where /// Same as [`Self::intersects`], but with an explicit integer engine. fn intersects_as(&self, other: &R1) -> bool where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Returns `true` if the interiors of this shape and another overlap. /// @@ -270,7 +268,7 @@ where /// Same as [`Self::interiors_intersect`], but with an explicit integer engine. fn interiors_intersect_as(&self, other: &R1) -> bool where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Returns `true` if this shape touches another (boundaries intersect but interiors don't). /// @@ -280,7 +278,7 @@ where /// Same as [`Self::touches`], but with an explicit integer engine. fn touches_as(&self, other: &R1) -> bool where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Returns `true` if this shape intersects another by point coincidence only. fn point_intersects(&self, other: &R1) -> bool; @@ -288,7 +286,7 @@ where /// Same as [`Self::point_intersects`], but with an explicit integer engine. fn point_intersects_as(&self, other: &R1) -> bool where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Returns `true` if this shape is completely within another. /// @@ -299,7 +297,7 @@ where /// Same as [`Self::within`], but with an explicit integer engine. fn within_as(&self, other: &R1) -> bool where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Returns `true` if this shape does not intersect with another (no shared points). /// @@ -309,7 +307,7 @@ where /// Same as [`Self::disjoint`], but with an explicit integer engine. fn disjoint_as(&self, other: &R1) -> bool where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Returns `true` if this shape completely covers another. /// @@ -319,7 +317,7 @@ where /// Same as [`Self::covers`], but with an explicit integer engine. fn covers_as(&self, other: &R1) -> bool where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; } impl FloatRelate for R0 @@ -336,7 +334,7 @@ where #[inline] fn intersects_as(&self, other: &R1) -> bool where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { FloatPredicateOverlay::::from_subj_and_clip(self, other).intersects() } @@ -349,7 +347,7 @@ where #[inline] fn interiors_intersect_as(&self, other: &R1) -> bool where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { FloatPredicateOverlay::::from_subj_and_clip(self, other).interiors_intersect() } @@ -362,7 +360,7 @@ where #[inline] fn touches_as(&self, other: &R1) -> bool where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { FloatPredicateOverlay::::from_subj_and_clip(self, other).touches() } @@ -375,7 +373,7 @@ where #[inline] fn point_intersects_as(&self, other: &R1) -> bool where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { FloatPredicateOverlay::::from_subj_and_clip(self, other).point_intersects() } @@ -388,7 +386,7 @@ where #[inline] fn within_as(&self, other: &R1) -> bool where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { FloatPredicateOverlay::::from_subj_and_clip(self, other).within() } @@ -401,7 +399,7 @@ where #[inline] fn disjoint_as(&self, other: &R1) -> bool where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { !self.intersects_as::(other) } @@ -414,7 +412,7 @@ where #[inline] fn covers_as(&self, other: &R1) -> bool where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { other.within_as::(self) } diff --git a/iOverlay/src/float/scale.rs b/iOverlay/src/float/scale.rs index 6e567e80..7476bc73 100644 --- a/iOverlay/src/float/scale.rs +++ b/iOverlay/src/float/scale.rs @@ -1,4 +1,5 @@ use crate::core::fill_rule::FillRule; +use crate::core::integer::OverlayInt; use crate::core::overlay::ShapeType; use crate::core::overlay_rule::OverlayRule; use crate::core::solver::Solver; @@ -7,11 +8,8 @@ use crate::float::relate::FloatPredicateOverlay; use i_float::adapter::{FloatPointAdapter, FloatPointAdapterScaleError}; use i_float::float::compatible::FloatPointCompatible; use i_float::float::number::FloatNumber; -use i_float::int::number::int::IntNumber; -use i_key_sort::sort::key::SortKey; use i_shape::base::data::Shapes; use i_shape::source::resource::ShapeResource; -use i_tree::{Expiration, LayoutNumber}; #[derive(Debug, Clone, Copy)] pub enum FixedScaleOverlayError { @@ -111,7 +109,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; } impl FixedScaleFloatOverlay for R0 @@ -143,7 +141,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { Ok( FloatOverlay::::from_subj_and_clip_fixed_scale(self, source, scale)? @@ -155,7 +153,7 @@ where impl FloatOverlay where P: FloatPointCompatible, - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { /// Creates a new `FloatOverlay` instance and initializes it with subject and clip shapes. /// @@ -265,7 +263,7 @@ impl FloatOverlay

{ impl FloatPredicateOverlay where P: FloatPointCompatible, - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { /// Creates a new predicate overlay with subject and clip shapes using fixed-scale precision. /// @@ -412,7 +410,7 @@ where scale: P::Scalar, ) -> Result where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Returns `true` if interiors of shapes overlap, using fixed-scale precision. fn interiors_intersect_with_fixed_scale( @@ -428,7 +426,7 @@ where scale: P::Scalar, ) -> Result where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Returns `true` if shapes touch (boundaries intersect but interiors don't), using fixed-scale precision. fn touches_with_fixed_scale(&self, other: &R1, scale: P::Scalar) -> Result; @@ -440,7 +438,7 @@ where scale: P::Scalar, ) -> Result where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Returns `true` if this shape is completely within another, using fixed-scale precision. fn within_with_fixed_scale(&self, other: &R1, scale: P::Scalar) -> Result; @@ -452,7 +450,7 @@ where scale: P::Scalar, ) -> Result where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Returns `true` if shapes do not intersect, using fixed-scale precision. fn disjoint_with_fixed_scale(&self, other: &R1, scale: P::Scalar) @@ -465,7 +463,7 @@ where scale: P::Scalar, ) -> Result where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Returns `true` if this shape completely covers another, using fixed-scale precision. fn covers_with_fixed_scale(&self, other: &R1, scale: P::Scalar) -> Result; @@ -477,7 +475,7 @@ where scale: P::Scalar, ) -> Result where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; } impl FixedScaleFloatRelate for R0 @@ -502,7 +500,7 @@ where scale: P::Scalar, ) -> Result where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { Ok(FloatPredicateOverlay::::from_subj_and_clip_fixed_scale(self, other, scale)?.intersects()) } @@ -526,7 +524,7 @@ where scale: P::Scalar, ) -> Result where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { Ok( FloatPredicateOverlay::::from_subj_and_clip_fixed_scale(self, other, scale)? @@ -546,7 +544,7 @@ where scale: P::Scalar, ) -> Result where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { Ok(FloatPredicateOverlay::::from_subj_and_clip_fixed_scale(self, other, scale)?.touches()) } @@ -563,7 +561,7 @@ where scale: P::Scalar, ) -> Result where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { Ok(FloatPredicateOverlay::::from_subj_and_clip_fixed_scale(self, other, scale)?.within()) } @@ -584,7 +582,7 @@ where scale: P::Scalar, ) -> Result where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { Ok(!FloatPredicateOverlay::::from_subj_and_clip_fixed_scale(self, other, scale)?.intersects()) } @@ -601,7 +599,7 @@ where scale: P::Scalar, ) -> Result where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { Ok(FloatPredicateOverlay::::from_subj_and_clip_fixed_scale(other, self, scale)?.within()) } diff --git a/iOverlay/src/float/simplify.rs b/iOverlay/src/float/simplify.rs index 82ec47bb..295aeb9a 100644 --- a/iOverlay/src/float/simplify.rs +++ b/iOverlay/src/float/simplify.rs @@ -1,13 +1,11 @@ use crate::core::fill_rule::FillRule; +use crate::core::integer::OverlayInt; use crate::core::overlay_rule::OverlayRule; use crate::core::solver::Solver; use crate::float::overlay::{FloatOverlay, OverlayOptions}; use i_float::float::compatible::FloatPointCompatible; -use i_float::int::number::int::IntNumber; -use i_key_sort::sort::key::SortKey; use i_shape::base::data::Shapes; use i_shape::source::resource::ShapeResource; -use i_tree::{Expiration, LayoutNumber}; /// Trait `Simplify` provides a method to simplify geometric shapes by reducing the number of points in contours or shapes /// while preserving overall shape and topology. The method applies a minimum area threshold and a build rule to @@ -39,7 +37,7 @@ pub trait SimplifyShape { /// Same as [`Self::simplify_shape`], but with an explicit integer engine. fn simplify_shape_as(&self, fill_rule: FillRule) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Simplifies the shape or collection of points, contours, or shapes, based on a specified minimum area threshold. /// - `options`: Adjust custom behavior. @@ -62,7 +60,7 @@ pub trait SimplifyShape { solver: Solver, ) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; } impl SimplifyShape

for S @@ -79,7 +77,7 @@ where #[inline] fn simplify_shape_as(&self, fill_rule: FillRule) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { FloatOverlay::::from_subj_custom(self, Default::default(), Default::default()) .overlay(OverlayRule::Subject, fill_rule) @@ -103,7 +101,7 @@ where solver: Solver, ) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { FloatOverlay::::from_subj_custom(self, options, solver).overlay(OverlayRule::Subject, fill_rule) } diff --git a/iOverlay/src/float/single.rs b/iOverlay/src/float/single.rs index 567b2d5d..a1cfa463 100644 --- a/iOverlay/src/float/single.rs +++ b/iOverlay/src/float/single.rs @@ -1,12 +1,10 @@ use crate::core::fill_rule::FillRule; +use crate::core::integer::OverlayInt; use crate::core::overlay_rule::OverlayRule; use crate::float::overlay::FloatOverlay; use i_float::float::compatible::FloatPointCompatible; -use i_float::int::number::int::IntNumber; -use i_key_sort::sort::key::SortKey; use i_shape::base::data::Shapes; use i_shape::source::resource::ShapeResource; -use i_tree::{Expiration, LayoutNumber}; /// Trait `SingleFloatOverlay` provides methods for overlay operations between various geometric entities. /// This trait supports boolean operations on contours, shapes, and collections of shapes, using customizable overlay and build rules. @@ -49,7 +47,7 @@ where /// Same as [`Self::overlay`], but with an explicit integer engine. fn overlay_as(&self, source: &R1, overlay_rule: OverlayRule, fill_rule: FillRule) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; } impl SingleFloatOverlay for R0 @@ -66,7 +64,7 @@ where #[inline] fn overlay_as(&self, resource: &R1, overlay_rule: OverlayRule, fill_rule: FillRule) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { FloatOverlay::::from_subj_and_clip(self, resource).overlay(overlay_rule, fill_rule) } diff --git a/iOverlay/src/float/slice.rs b/iOverlay/src/float/slice.rs index 24e7cb2d..4a732606 100644 --- a/iOverlay/src/float/slice.rs +++ b/iOverlay/src/float/slice.rs @@ -1,15 +1,13 @@ use crate::core::fill_rule::FillRule; +use crate::core::integer::OverlayInt; use crate::core::solver::Solver; use crate::float::overlay::OverlayOptions; use crate::float::scale::FixedScaleOverlayError; use crate::float::string_overlay::FloatStringOverlay; use crate::string::rule::StringRule; use i_float::float::compatible::FloatPointCompatible; -use i_float::int::number::int::IntNumber; -use i_key_sort::sort::key::SortKey; use i_shape::base::data::Shapes; use i_shape::source::resource::ShapeResource; -use i_tree::{Expiration, LayoutNumber}; /// The `FloatSlice` trait provides methods to slice geometric shapes using a given path or set of paths, /// allowing for boolean operations based on the specified build rule. @@ -52,7 +50,7 @@ where /// Same as [`Self::slice_by`], but with an explicit integer engine. fn slice_by_as(&self, resource: &R, fill_rule: FillRule) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Slices the current shapes by string lines with a fixed float-to-integer scale. /// @@ -82,7 +80,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Slices the current shapes by string lines. /// @@ -114,7 +112,7 @@ where solver: Solver, ) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; /// Slices the current shapes by string lines with a fixed float-to-integer scale. /// @@ -149,7 +147,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey; + I: OverlayInt; } impl FloatSlice for R1 @@ -169,7 +167,7 @@ where #[inline] fn slice_by_as(&self, resource: &R0, fill_rule: FillRule) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { FloatStringOverlay::::from_shape_and_string(self, resource) .build_graph_view(fill_rule) @@ -200,7 +198,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { Ok( FloatStringOverlay::::from_shape_and_string_fixed_scale(self, resource, scale)? @@ -233,7 +231,7 @@ where solver: Solver, ) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { FloatStringOverlay::::from_shape_and_string(self, resource) .build_graph_view_with_solver(fill_rule, solver) @@ -268,7 +266,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { Ok( FloatStringOverlay::::from_shape_and_string_fixed_scale(self, resource, scale)? diff --git a/iOverlay/src/float/string_graph.rs b/iOverlay/src/float/string_graph.rs index 3abc86a8..7d62b4ed 100644 --- a/iOverlay/src/float/string_graph.rs +++ b/iOverlay/src/float/string_graph.rs @@ -1,15 +1,14 @@ +use crate::core::integer::OverlayInt; use crate::float::overlay::OverlayOptions; use crate::string::graph::StringGraph; use crate::string::rule::StringRule; use i_float::adapter::FloatPointAdapter; use i_float::float::compatible::FloatPointCompatible; use i_float::int::number::int::IntNumber; -use i_key_sort::sort::key::SortKey; use i_shape::base::data::Shapes; use i_shape::float::adapter::ShapesToFloat; use i_shape::float::despike::DeSpikeContour; use i_shape::float::simple::SimplifyContour; -use i_tree::{Expiration, LayoutNumber}; /// The `FloatStringGraph` struct represents a graph structure with floating-point precision, /// providing methods to extract geometric shapes from the graph after applying string-based operations. @@ -21,7 +20,7 @@ pub struct FloatStringGraph<'a, P: FloatPointCompatible, I: IntNumber = i32> { impl FloatStringGraph<'_, P, I> where P: FloatPointCompatible, - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { /// Extracts shapes from the overlay graph based on the specified string rule. /// This method is used to retrieve the final geometric shapes after boolean operations have been applied. diff --git a/iOverlay/src/float/string_overlay.rs b/iOverlay/src/float/string_overlay.rs index f6217514..53bd4113 100644 --- a/iOverlay/src/float/string_overlay.rs +++ b/iOverlay/src/float/string_overlay.rs @@ -1,4 +1,5 @@ use crate::core::fill_rule::FillRule; +use crate::core::integer::OverlayInt; use crate::core::solver::Solver; use crate::float::scale::FixedScaleOverlayError; use crate::float::string_graph::FloatStringGraph; @@ -6,12 +7,9 @@ use crate::string::clip::ClipRule; use crate::string::overlay::StringOverlay; use i_float::adapter::FloatPointAdapter; use i_float::float::compatible::FloatPointCompatible; -use i_float::int::number::int::IntNumber; -use i_key_sort::sort::key::SortKey; use i_shape::base::data::Paths; use i_shape::float::adapter::ShapeToFloat; use i_shape::source::resource::ShapeResource; -use i_tree::{Expiration, LayoutNumber}; /// The `FloatStringOverlay` struct is a builder for overlaying geometric shapes by converting /// floating-point geometry to integer space. It provides methods for adding paths and shapes, @@ -19,7 +17,7 @@ use i_tree::{Expiration, LayoutNumber}; /// /// The float-to-integer conversion is controlled by the `FloatPointAdapter` scale: /// `x_int = (x_float - offset_x) * scale`. Use a fixed scale if you need predictable precision. -pub struct FloatStringOverlay { +pub struct FloatStringOverlay { pub(super) overlay: StringOverlay, pub(super) adapter: FloatPointAdapter, } @@ -27,7 +25,7 @@ pub struct FloatStringOverlay FloatStringOverlay where P: FloatPointCompatible, - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { /// Constructs a new `FloatStringOverlay`, a builder for overlaying geometric shapes /// by converting float-based geometry to integer space, using a pre-configured adapter. diff --git a/iOverlay/src/mesh/outline/offset.rs b/iOverlay/src/mesh/outline/offset.rs index 41c27cf5..06f81839 100644 --- a/iOverlay/src/mesh/outline/offset.rs +++ b/iOverlay/src/mesh/outline/offset.rs @@ -1,5 +1,6 @@ use crate::core::extract::BooleanExtractionBuffer; use crate::core::fill_rule::FillRule; +use crate::core::integer::OverlayInt; use crate::core::overlay::ShapeType::Subject; use crate::core::overlay::{ContourDirection, Overlay}; use crate::core::overlay_rule::OverlayRule; @@ -16,7 +17,6 @@ use i_float::float::rect::FloatRect; use i_float::int::number::int::IntNumber; use i_float::int::number::uint::UIntNumber; use i_float::int::number::wide_int::WideIntNumber; -use i_key_sort::sort::key::SortKey; use i_shape::base::data::Shapes; use i_shape::flat::buffer::FlatContoursBuffer; use i_shape::flat::float::FloatFlatContoursBuffer; @@ -25,7 +25,6 @@ use i_shape::float::despike::DeSpikeContour; use i_shape::float::int_area::IntArea; use i_shape::float::simple::SimplifyContour; use i_shape::source::resource::ShapeResource; -use i_tree::{Expiration, LayoutNumber}; /// Trait for offsetting float contours and shapes. /// @@ -146,12 +145,12 @@ pub trait OutlineOffset { /// Same as [`Self::outline`], but with an explicit integer engine. fn outline_as(&self, style: &OutlineStyle) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + I: OverlayInt + 'static; /// Same as [`Self::outline_into`], but with an explicit integer engine. fn outline_into_as(&self, style: &OutlineStyle, output: &mut FloatFlatContoursBuffer

) where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + I: OverlayInt + 'static; /// Same as [`Self::outline_custom`], but with an explicit integer engine. fn outline_custom_as( @@ -160,7 +159,7 @@ pub trait OutlineOffset { options: OverlayOptions, ) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + I: OverlayInt + 'static; /// Same as [`Self::outline_custom_into`], but with an explicit integer engine. fn outline_custom_into_as( @@ -169,7 +168,7 @@ pub trait OutlineOffset { options: OverlayOptions, output: &mut FloatFlatContoursBuffer

, ) where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + I: OverlayInt + 'static; /// Same as [`Self::outline_fixed_scale`], but with an explicit integer engine. fn outline_fixed_scale_as( @@ -178,7 +177,7 @@ pub trait OutlineOffset { scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + I: OverlayInt + 'static; /// Same as [`Self::outline_fixed_scale_into`], but with an explicit integer engine. fn outline_fixed_scale_into_as( @@ -188,7 +187,7 @@ pub trait OutlineOffset { output: &mut FloatFlatContoursBuffer

, ) -> Result<(), FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + I: OverlayInt + 'static; /// Same as [`Self::outline_custom_fixed_scale`], but with an explicit integer engine. fn outline_custom_fixed_scale_as( @@ -198,7 +197,7 @@ pub trait OutlineOffset { scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + I: OverlayInt + 'static; /// Same as [`Self::outline_custom_fixed_scale_into`], but with an explicit integer engine. fn outline_custom_fixed_scale_into_as( @@ -209,7 +208,7 @@ pub trait OutlineOffset { output: &mut FloatFlatContoursBuffer

, ) -> Result<(), FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + I: OverlayInt + 'static; } impl OutlineOffset

for S @@ -302,14 +301,14 @@ where fn outline_as(&self, style: &OutlineStyle) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + I: OverlayInt + 'static, { self.outline_custom_as::(style, Default::default()) } fn outline_into_as(&self, style: &OutlineStyle, output: &mut FloatFlatContoursBuffer

) where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + I: OverlayInt + 'static, { self.outline_custom_into_as::(style, Default::default(), output) } @@ -320,7 +319,7 @@ where options: OverlayOptions, ) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + I: OverlayInt + 'static, { match OutlineSolver::::prepare(self, style) { Some(solver) => solver.build(self, options), @@ -334,7 +333,7 @@ where options: OverlayOptions, output: &mut FloatFlatContoursBuffer

, ) where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + I: OverlayInt + 'static, { match OutlineSolver::::prepare(self, style) { Some(solver) => solver.build_into(self, options, output), @@ -348,7 +347,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + I: OverlayInt + 'static, { self.outline_custom_fixed_scale_as::(style, Default::default(), scale) } @@ -360,7 +359,7 @@ where output: &mut FloatFlatContoursBuffer

, ) -> Result<(), FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + I: OverlayInt + 'static, { self.outline_custom_fixed_scale_into_as::(style, Default::default(), scale, output) } @@ -372,7 +371,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + I: OverlayInt + 'static, { let s = FixedScaleOverlayError::validate_scale(scale)?; let mut solver = match OutlineSolver::::prepare(self, style) { @@ -391,7 +390,7 @@ where output: &mut FloatFlatContoursBuffer

, ) -> Result<(), FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + I: OverlayInt + 'static, { let s = FixedScaleOverlayError::validate_scale(scale)?; let mut solver = match OutlineSolver::::prepare(self, style) { @@ -417,7 +416,7 @@ struct OutlineSolver { impl OutlineSolver where P: FloatPointCompatible + 'static, - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + I: OverlayInt + 'static, { fn prepare>(source: &S, style: &OutlineStyle) -> Option { let (points_count, paths_count) = { diff --git a/iOverlay/src/mesh/overlay.rs b/iOverlay/src/mesh/overlay.rs index aa6d8855..aacfba34 100644 --- a/iOverlay/src/mesh/overlay.rs +++ b/iOverlay/src/mesh/overlay.rs @@ -1,14 +1,13 @@ use crate::build::builder::GraphBuilder; use crate::core::graph::OverlayNode; +use crate::core::integer::OverlayInt; use crate::core::overlay::Overlay; use crate::segm::boolean::ShapeCountBoolean; use crate::segm::segment::Segment; use crate::split::solver::SplitSolver; use alloc::vec::Vec; -use i_float::int::number::int::IntNumber; -use i_tree::Expiration; -impl Overlay { +impl Overlay { #[inline] pub(crate) fn add_segments(&mut self, segments: &[Segment]) { self.segments.extend_from_slice(segments); diff --git a/iOverlay/src/mesh/stroke/offset.rs b/iOverlay/src/mesh/stroke/offset.rs index baed7c39..6303a64a 100644 --- a/iOverlay/src/mesh/stroke/offset.rs +++ b/iOverlay/src/mesh/stroke/offset.rs @@ -1,4 +1,5 @@ use crate::core::fill_rule::FillRule; +use crate::core::integer::OverlayInt; use crate::core::overlay::Overlay; use crate::core::overlay_rule::OverlayRule; use crate::float::overlay::OverlayOptions; @@ -15,14 +16,12 @@ use i_float::float::rect::FloatRect; use i_float::int::number::int::IntNumber; use i_float::int::number::uint::UIntNumber; use i_float::int::number::wide_int::WideIntNumber; -use i_key_sort::sort::key::SortKey; use i_shape::base::data::Shapes; use i_shape::flat::buffer::FlatContoursBuffer; use i_shape::flat::float::FloatFlatContoursBuffer; use i_shape::float::adapter::ShapesToFloat; use i_shape::float::despike::DeSpikeContour; use i_shape::float::simple::SimplifyContour; -use i_tree::{Expiration, LayoutNumber}; /// Trait for generating stroke outlines from float paths. /// @@ -166,7 +165,7 @@ pub trait StrokeOffset { /// Same as [`Self::stroke`], but with an explicit integer engine. fn stroke_as(&self, style: StrokeStyle

, is_closed_path: bool) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + I: OverlayInt + 'static; /// Same as [`Self::stroke_into`], but with an explicit integer engine. fn stroke_into_as( @@ -175,7 +174,7 @@ pub trait StrokeOffset { is_closed_path: bool, output: &mut FloatFlatContoursBuffer

, ) where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + I: OverlayInt + 'static; /// Same as [`Self::stroke_custom`], but with an explicit integer engine. fn stroke_custom_as( @@ -185,7 +184,7 @@ pub trait StrokeOffset { options: OverlayOptions, ) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + I: OverlayInt + 'static; /// Same as [`Self::stroke_custom_into`], but with an explicit integer engine. fn stroke_custom_into_as( @@ -195,7 +194,7 @@ pub trait StrokeOffset { options: OverlayOptions, output: &mut FloatFlatContoursBuffer

, ) where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + I: OverlayInt + 'static; /// Same as [`Self::stroke_fixed_scale`], but with an explicit integer engine. fn stroke_fixed_scale_as( @@ -205,7 +204,7 @@ pub trait StrokeOffset { scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + I: OverlayInt + 'static; /// Same as [`Self::stroke_fixed_scale_into`], but with an explicit integer engine. fn stroke_fixed_scale_into_as( @@ -216,7 +215,7 @@ pub trait StrokeOffset { output: &mut FloatFlatContoursBuffer

, ) -> Result<(), FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + I: OverlayInt + 'static; /// Same as [`Self::stroke_custom_fixed_scale`], but with an explicit integer engine. fn stroke_custom_fixed_scale_as( @@ -227,7 +226,7 @@ pub trait StrokeOffset { scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + I: OverlayInt + 'static; /// Same as [`Self::stroke_custom_fixed_scale_into`], but with an explicit integer engine. fn stroke_custom_fixed_scale_into_as( @@ -239,7 +238,7 @@ pub trait StrokeOffset { output: &mut FloatFlatContoursBuffer

, ) -> Result<(), FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + I: OverlayInt + 'static; } impl StrokeOffset

for S @@ -341,7 +340,7 @@ where fn stroke_as(&self, style: StrokeStyle

, is_closed_path: bool) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + I: OverlayInt + 'static, { self.stroke_custom_as::(style, is_closed_path, Default::default()) } @@ -352,7 +351,7 @@ where is_closed_path: bool, output: &mut FloatFlatContoursBuffer

, ) where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + I: OverlayInt + 'static, { self.stroke_custom_into_as::(style, is_closed_path, Default::default(), output) } @@ -364,7 +363,7 @@ where options: OverlayOptions, ) -> Shapes

where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + I: OverlayInt + 'static, { match StrokeSolver::::prepare(self, style) { Some(solver) => solver.build(self, is_closed_path, options), @@ -379,7 +378,7 @@ where options: OverlayOptions, output: &mut FloatFlatContoursBuffer

, ) where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + I: OverlayInt + 'static, { match StrokeSolver::::prepare(self, style) { Some(solver) => solver.build_into(self, is_closed_path, options, output), @@ -394,7 +393,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + I: OverlayInt + 'static, { self.stroke_custom_fixed_scale_as::(style, is_closed_path, Default::default(), scale) } @@ -407,7 +406,7 @@ where output: &mut FloatFlatContoursBuffer

, ) -> Result<(), FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + I: OverlayInt + 'static, { self.stroke_custom_fixed_scale_into_as::(style, is_closed_path, Default::default(), scale, output) } @@ -420,7 +419,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + I: OverlayInt + 'static, { let mut solver = match StrokeSolver::::prepare(self, style) { Some(solver) => solver, @@ -439,7 +438,7 @@ where output: &mut FloatFlatContoursBuffer

, ) -> Result<(), FixedScaleOverlayError> where - I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + I: OverlayInt + 'static, { let mut solver = match StrokeSolver::::prepare(self, style) { Some(solver) => solver, @@ -465,7 +464,7 @@ struct StrokeSolver { impl StrokeSolver where P: 'static + FloatPointCompatible, - I: 'static + IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt + 'static, { fn prepare>(source: &S, style: StrokeStyle

) -> Option { let mut paths_count = 0; diff --git a/iOverlay/src/split/solver.rs b/iOverlay/src/split/solver.rs index 6da3267c..fc8e23e6 100644 --- a/iOverlay/src/split/solver.rs +++ b/iOverlay/src/split/solver.rs @@ -1,4 +1,5 @@ use crate::core::edge_data::{EdgeDataSplit, OverlayEdgeData}; +use crate::core::integer::OverlayInt; use crate::core::solver::Solver; use crate::geom::x_segment::XSegment; use crate::segm::merge::ShapeSegmentsMerge; @@ -9,8 +10,6 @@ use crate::split::cross_solver::{CrossSolver, CrossType, EndMask}; use crate::split::line_mark::{LineMark, SortMarkByIndexAndPoint}; use alloc::vec::Vec; use i_float::int::number::int::IntNumber; -use i_key_sort::sort::key::SortKey; -use i_tree::{Expiration, LayoutNumber}; pub(crate) struct SplitSolver { pub(super) marks: Vec>, @@ -25,7 +24,7 @@ impl SplitSolver { impl SplitSolver where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { #[inline] pub(crate) fn split_segments>( diff --git a/iOverlay/src/split/solver_fragment.rs b/iOverlay/src/split/solver_fragment.rs index 6a228ac5..e5c613e7 100644 --- a/iOverlay/src/split/solver_fragment.rs +++ b/iOverlay/src/split/solver_fragment.rs @@ -1,4 +1,5 @@ use crate::core::edge_data::OverlayEdgeData; +use crate::core::integer::OverlayInt; use crate::core::solver::Solver; use crate::segm::segment::Segment; use crate::segm::winding::WindingCount; @@ -9,13 +10,10 @@ use crate::split::line_mark::LineMark; use crate::split::snap_radius::SnapRadius; use crate::split::solver::SplitSolver; use alloc::vec::Vec; -use i_float::int::number::int::IntNumber; -use i_key_sort::sort::key::SortKey; -use i_tree::{Expiration, LayoutNumber}; impl SplitSolver where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { pub(super) fn fragment_split>( &mut self, diff --git a/iOverlay/src/split/solver_list.rs b/iOverlay/src/split/solver_list.rs index 794081c7..5070eac8 100644 --- a/iOverlay/src/split/solver_list.rs +++ b/iOverlay/src/split/solver_list.rs @@ -1,17 +1,15 @@ use crate::core::edge_data::OverlayEdgeData; +use crate::core::integer::OverlayInt; use crate::core::solver::Solver; use crate::segm::segment::Segment; use crate::segm::winding::WindingCount; use crate::split::snap_radius::SnapRadius; use crate::split::solver::SplitSolver; use alloc::vec::Vec; -use i_float::int::number::int::IntNumber; -use i_key_sort::sort::key::SortKey; -use i_tree::{Expiration, LayoutNumber}; impl SplitSolver where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { pub(super) fn list_split>( &mut self, diff --git a/iOverlay/src/split/solver_tree.rs b/iOverlay/src/split/solver_tree.rs index 1f69555d..1ce193f3 100644 --- a/iOverlay/src/split/solver_tree.rs +++ b/iOverlay/src/split/solver_tree.rs @@ -1,4 +1,5 @@ use crate::core::edge_data::OverlayEdgeData; +use crate::core::integer::OverlayInt; use crate::core::solver::Solver; use crate::geom::line_range::LineRange; use crate::geom::x_segment::XSegment; @@ -8,11 +9,10 @@ use crate::split::snap_radius::SnapRadius; use crate::split::solver::SplitSolver; use alloc::vec::Vec; use i_float::int::number::int::IntNumber; -use i_key_sort::sort::key::SortKey; +use i_tree::Expiration; use i_tree::ExpiredVal; use i_tree::seg::exp::{SegExpCollection, SegRange}; use i_tree::seg::tree::SegExpTree; -use i_tree::{Expiration, LayoutNumber}; #[derive(Clone, Copy)] struct IdSegment { @@ -29,7 +29,7 @@ impl ExpiredVal for IdSegment { impl SplitSolver where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { pub(super) fn tree_split>( &mut self, diff --git a/iOverlay/src/string/clip.rs b/iOverlay/src/string/clip.rs index e45f0f12..90d25be2 100644 --- a/iOverlay/src/string/clip.rs +++ b/iOverlay/src/string/clip.rs @@ -1,4 +1,5 @@ use crate::core::fill_rule::FillRule; +use crate::core::integer::OverlayInt; use crate::core::link::OverlayLink; use crate::geom::id_point::IdPoint; use crate::segm::segment::SegmentFill; @@ -9,10 +10,8 @@ use crate::string::overlay::StringOverlay; use alloc::vec::Vec; use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; -use i_key_sort::sort::key::SortKey; use i_shape::int::path::IntPath; use i_shape::int::shape::{IntShape, IntShapes}; -use i_tree::{Expiration, LayoutNumber}; #[derive(Debug, Clone, Copy)] pub struct ClipRule { @@ -162,7 +161,7 @@ pub trait IntClip { impl IntClip for IntShapes where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { #[inline] fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { @@ -195,7 +194,7 @@ where impl IntClip for IntShape where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { #[inline] fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { @@ -228,7 +227,7 @@ where impl IntClip for [IntPoint] where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { #[inline] fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { diff --git a/iOverlay/src/string/extract.rs b/iOverlay/src/string/extract.rs index 0d063d5f..ffe272ba 100644 --- a/iOverlay/src/string/extract.rs +++ b/iOverlay/src/string/extract.rs @@ -1,4 +1,5 @@ use crate::bind::solver::JoinHoles; +use crate::core::integer::OverlayInt; use crate::core::nearest_vector::NearestVector; use crate::core::overlay::{ContourDirection, IntOverlayOptions}; use crate::segm::segment::SUBJ_TOP; @@ -7,13 +8,10 @@ use crate::string::rule::StringRule; use crate::string::split::{BinStore, Split}; use alloc::vec; use alloc::vec::Vec; -use i_float::int::number::int::IntNumber; -use i_key_sort::sort::key::SortKey; use i_shape::int::path::{ContourExtension, IntPath}; use i_shape::int::shape::IntShapes; -use i_tree::Expiration; -impl StringGraph<'_, I> { +impl StringGraph<'_, I> { /// Extracts shapes from the graph based on the specified `StringRule`. /// - `string_rule`: The rule used to determine how shapes are extracted. /// # Shape Representation diff --git a/iOverlay/src/string/overlay.rs b/iOverlay/src/string/overlay.rs index 62d9e4cf..340838b2 100644 --- a/iOverlay/src/string/overlay.rs +++ b/iOverlay/src/string/overlay.rs @@ -1,5 +1,6 @@ use crate::build::builder::GraphBuilder; use crate::core::fill_rule::FillRule; +use crate::core::integer::OverlayInt; use crate::core::overlay::{IntOverlayOptions, ShapeType}; use crate::core::solver::Solver; use crate::geom::x_segment::XSegment; @@ -14,15 +15,12 @@ use crate::string::graph::StringGraph; use crate::string::line::IntLine; use alloc::vec::Vec; use core::cmp::Ordering; -use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; -use i_key_sort::sort::key::SortKey; use i_shape::int::count::PointsCount; use i_shape::int::path::IntPath; use i_shape::int::shape::{IntContour, IntShape}; -use i_tree::{Expiration, LayoutNumber}; -pub struct StringOverlay { +pub struct StringOverlay { pub options: IntOverlayOptions, pub(super) segments: Vec>, pub(crate) split_solver: SplitSolver, @@ -31,7 +29,7 @@ pub struct StringOverlay { impl StringOverlay where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { /// Constructs a new `StringOverlay` instance, initializing it with a capacity that should closely match the total count of edges from all shapes being processed. /// This pre-allocation helps in optimizing memory usage and performance. diff --git a/iOverlay/src/string/slice.rs b/iOverlay/src/string/slice.rs index 9c82f038..a2f1559d 100644 --- a/iOverlay/src/string/slice.rs +++ b/iOverlay/src/string/slice.rs @@ -1,13 +1,12 @@ use crate::core::fill_rule::FillRule; +use crate::core::integer::OverlayInt; use crate::string::line::IntLine; use crate::string::overlay::StringOverlay; use crate::string::rule::StringRule; use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; -use i_key_sort::sort::key::SortKey; use i_shape::int::path::IntPath; use i_shape::int::shape::{IntShape, IntShapes}; -use i_tree::{Expiration, LayoutNumber}; pub trait IntSlice { fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes; @@ -18,7 +17,7 @@ pub trait IntSlice { impl IntSlice for IntShapes where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { #[inline] fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes { @@ -63,7 +62,7 @@ where impl IntSlice for IntShape where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { #[inline] fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes { @@ -108,7 +107,7 @@ where impl IntSlice for [IntPoint] where - I: IntNumber + Expiration + LayoutNumber + SortKey, + I: OverlayInt, { #[inline] fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes { diff --git a/iOverlay/src/vector/extract.rs b/iOverlay/src/vector/extract.rs index ef7f7053..c92ae50a 100644 --- a/iOverlay/src/vector/extract.rs +++ b/iOverlay/src/vector/extract.rs @@ -3,6 +3,7 @@ use crate::bind::solver::{ShapeBinder, SortByAngle}; use crate::core::edge_data::OverlayEdgeData; use crate::core::extract::{BooleanExtractionBuffer, GraphUtil, Visit, VisitState}; use crate::core::graph::OverlayGraph; +use crate::core::integer::OverlayInt; use crate::core::link::{OverlayLink, OverlayLinkFilter}; use crate::core::overlay::ContourDirection; use crate::core::overlay_rule::OverlayRule; @@ -21,7 +22,7 @@ use i_tree::Expiration; impl OverlayGraph<'_, I, D> where - I: IntNumber + Expiration + SortKey, + I: OverlayInt, D: OverlayEdgeData, { pub fn extract_separate_vectors(&self) -> Vec> { diff --git a/iOverlay/tests/crash_tests.rs b/iOverlay/tests/crash_tests.rs index ae04ba35..03304894 100644 --- a/iOverlay/tests/crash_tests.rs +++ b/iOverlay/tests/crash_tests.rs @@ -3,8 +3,8 @@ mod tests { use i_float::adapter::FloatPointAdapter; use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; - use i_key_sort::sort::key::SortKey; use i_overlay::core::fill_rule::FillRule; + use i_overlay::core::integer::OverlayInt; use i_overlay::core::overlay::{IntOverlayOptions, Overlay, ShapeType}; use i_overlay::core::overlay_rule::OverlayRule; use i_overlay::core::simplify::Simplify; @@ -12,10 +12,9 @@ mod tests { use i_overlay::float::overlay::{FloatOverlay, OverlayOptions}; use i_shape::base::data::{Path, Shape}; use i_shape::{int_path, int_shape}; - use i_tree::{Expiration, LayoutNumber}; - trait TestInt: IntNumber + Expiration + LayoutNumber + SortKey {} - impl TestInt for I where I: IntNumber + Expiration + LayoutNumber + SortKey {} + trait TestInt: OverlayInt {} + impl TestInt for I {} const SOLVERS: [Solver; 4] = [Solver::LIST, Solver::TREE, Solver::FRAG, Solver::AUTO]; fn point(x: i32, y: i32) -> IntPoint { diff --git a/iOverlay/tests/dynamic_tests.rs b/iOverlay/tests/dynamic_tests.rs index 570dcf1f..64e458fc 100644 --- a/iOverlay/tests/dynamic_tests.rs +++ b/iOverlay/tests/dynamic_tests.rs @@ -2,8 +2,8 @@ mod tests { use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; - use i_key_sort::sort::key::SortKey; use i_overlay::core::fill_rule::FillRule; + use i_overlay::core::integer::OverlayInt; use i_overlay::core::overlay::{Overlay, ShapeType}; use i_overlay::core::overlay_rule::OverlayRule; use i_overlay::core::solver::Solver; @@ -11,15 +11,14 @@ mod tests { use i_shape::base::data::Path; use i_shape::int::path::IntPath; use i_shape::int::shape::IntShape; - use i_tree::{Expiration, LayoutNumber}; use rand::RngExt; use std::f64::consts::PI; const SOLVERS: [Solver; 4] = [Solver::LIST, Solver::TREE, Solver::FRAG, Solver::AUTO]; - trait TestInt: IntNumber + Expiration + LayoutNumber + SortKey {} + trait TestInt: OverlayInt {} - impl TestInt for I where I: IntNumber + Expiration + LayoutNumber + SortKey {} + impl TestInt for I {} #[test] fn test_0() { From 3bfcd89512ae0f508c20fb7e838b8e3efaad0463 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sun, 2 Aug 2026 14:19:35 +0300 Subject: [PATCH 06/13] fix reserve --- iOverlay/src/build/boolean.rs | 15 +++++++-------- iOverlay/src/build/builder.rs | 5 ++--- iOverlay/src/core/extract.rs | 7 ++++--- iOverlay/src/core/extract_ogc.rs | 9 +++++---- iOverlay/src/string/split.rs | 3 +-- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/iOverlay/src/build/boolean.rs b/iOverlay/src/build/boolean.rs index 01b8da54..2014ff0b 100644 --- a/iOverlay/src/build/boolean.rs +++ b/iOverlay/src/build/boolean.rs @@ -21,7 +21,6 @@ use crate::segm::winding::WindingCount; use alloc::vec::Vec; use i_float::int::number::int::IntNumber; use i_key_sort::sort::key::SortKey; -use i_shape::util::reserve::Reserve; use i_tree::Expiration; impl GraphBuilder @@ -303,7 +302,7 @@ impl OverlayLinkFilter for [OverlayLink] { #[inline] fn filter_subject_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); - buffer.reserve_capacity(links.len()); + buffer.reserve(links.len()); for link in links.iter() { buffer.push(VisitState::new(!link.fill.is_subject())); } @@ -312,7 +311,7 @@ fn filter_subject_into(links: &[OverlayLink], buffer: &mu #[inline] fn filter_clip_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); - buffer.reserve_capacity(links.len()); + buffer.reserve(links.len()); for link in links.iter() { buffer.push(VisitState::new(!link.fill.is_clip())); } @@ -321,7 +320,7 @@ fn filter_clip_into(links: &[OverlayLink], buffer: &mut V #[inline] fn filter_intersect_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); - buffer.reserve_capacity(links.len()); + buffer.reserve(links.len()); for link in links.iter() { buffer.push(VisitState::new(!link.fill.is_intersect())); } @@ -330,7 +329,7 @@ fn filter_intersect_into(links: &[OverlayLink], buffer: & #[inline] fn filter_union_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); - buffer.reserve_capacity(links.len()); + buffer.reserve(links.len()); for link in links.iter() { buffer.push(VisitState::new(!link.fill.is_union())); } @@ -339,7 +338,7 @@ fn filter_union_into(links: &[OverlayLink], buffer: &mut #[inline] fn filter_difference_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); - buffer.reserve_capacity(links.len()); + buffer.reserve(links.len()); for link in links.iter() { buffer.push(VisitState::new(!link.fill.is_difference())); } @@ -351,7 +350,7 @@ fn filter_inverse_difference_into( buffer: &mut Vec, ) { buffer.clear(); - buffer.reserve_capacity(links.len()); + buffer.reserve(links.len()); for link in links.iter() { buffer.push(VisitState::new(!link.fill.is_inverse_difference())); } @@ -360,7 +359,7 @@ fn filter_inverse_difference_into( #[inline] fn filter_xor_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); - buffer.reserve_capacity(links.len()); + buffer.reserve(links.len()); for link in links.iter() { buffer.push(VisitState::new(!link.fill.is_xor())); } diff --git a/iOverlay/src/build/builder.rs b/iOverlay/src/build/builder.rs index e6a211e9..28435b06 100644 --- a/iOverlay/src/build/builder.rs +++ b/iOverlay/src/build/builder.rs @@ -9,7 +9,6 @@ use crate::segm::winding::WindingCount; use alloc::vec::Vec; use core::ops::ControlFlow; use i_float::int::number::int::IntNumber; -use i_shape::util::reserve::Reserve; use i_tree::Expiration; pub(super) trait InclusionFilterStrategy { @@ -89,7 +88,7 @@ where segments: &[Segment], ) { self.links.clear(); - self.links.reserve_capacity(segments.len()); + self.links.reserve(segments.len()); for (segment, &fill) in segments.iter().zip(&self.fills) { if !F::is_included(fill) { @@ -107,7 +106,7 @@ where #[inline] pub(super) fn build_links_all(&mut self, segments: &[Segment]) { self.links.clear(); - self.links.reserve_capacity(segments.len()); + self.links.reserve(segments.len()); for (segment, &fill) in segments.iter().zip(&self.fills) { self.links.push(OverlayLink::new_with_data( diff --git a/iOverlay/src/core/extract.rs b/iOverlay/src/core/extract.rs index 93a49369..1ec8db7d 100644 --- a/iOverlay/src/core/extract.rs +++ b/iOverlay/src/core/extract.rs @@ -18,7 +18,6 @@ use i_float::triangle::Triangle; use i_shape::int::path::ContourExtension; use i_shape::int::shape::{IntContour, IntShapes}; use i_shape::int::simple::Simplify; -use i_shape::util::reserve::Reserve; #[repr(u8)] #[derive(Copy, Clone, PartialEq, Default)] @@ -110,7 +109,9 @@ where let mut holes = Vec::new(); let mut anchors = Vec::new(); - buffer.points.reserve_capacity(buffer.visited.len()); + buffer + .points + .reserve(buffer.visited.len().saturating_sub(buffer.points.len())); let mut link_index = 0; let mut anchors_already_sorted = true; @@ -227,7 +228,7 @@ where ) { let clockwise = self.options.output_direction == ContourDirection::Clockwise; let len = buffer.visited.len(); - buffer.points.reserve_capacity(len); + buffer.points.reserve(len.saturating_sub(buffer.points.len())); output.clear_and_reserve(len, 4); let mut link_index = 0; diff --git a/iOverlay/src/core/extract_ogc.rs b/iOverlay/src/core/extract_ogc.rs index d8ef3dc6..e9f6b738 100644 --- a/iOverlay/src/core/extract_ogc.rs +++ b/iOverlay/src/core/extract_ogc.rs @@ -11,7 +11,6 @@ use alloc::vec; use alloc::vec::Vec; use i_float::int::point::IntPoint; use i_shape::int::shape::{IntShape, IntShapes}; -use i_shape::util::reserve::Reserve; impl OverlayGraph<'_, I> where @@ -37,7 +36,9 @@ where let mut shapes = Vec::new(); - buffer.points.reserve_capacity(buffer.visited.len()); + buffer + .points + .reserve(buffer.visited.len().saturating_sub(buffer.points.len())); let mut hole_count_hint = 0; let mut link_index = 0; @@ -237,7 +238,7 @@ where global_visited, ); - let mut original_contour_len = 1; + let mut original_contour_len: usize = 1; // Find a closed tour while link_id != last_link_id { @@ -270,7 +271,7 @@ where // Revisit the contour in reverse; // all links escape current contour are skipped in `contour_visited`. - points.reserve_capacity(original_contour_len); + points.reserve(original_contour_len.saturating_sub(points.len())); self.find_contour( start_data, !clockwise, diff --git a/iOverlay/src/string/split.rs b/iOverlay/src/string/split.rs index 782e5ba0..90d62fef 100644 --- a/iOverlay/src/string/split.rs +++ b/iOverlay/src/string/split.rs @@ -5,7 +5,6 @@ use i_float::int::number::wide_int::WideIntNumber; use i_float::int::point::IntPoint; use i_shape::int::path::ContourExtension; use i_shape::int::shape::IntContour; -use i_shape::util::reserve::Reserve; pub(super) trait Split { fn split_loops( @@ -28,8 +27,8 @@ impl Split for IntContour { if self.is_empty() { return Vec::new(); } - contour_buffer.reserve_capacity(self.len()); contour_buffer.clear(); + contour_buffer.reserve(self.len()); bin_store.init(&self); From a60b67e171642018a5e0d326b54903fdb266e726 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sun, 2 Aug 2026 14:23:28 +0300 Subject: [PATCH 07/13] fix trait constrant --- iOverlay/src/split/solver_fragment.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOverlay/src/split/solver_fragment.rs b/iOverlay/src/split/solver_fragment.rs index e5c613e7..8aff48ce 100644 --- a/iOverlay/src/split/solver_fragment.rs +++ b/iOverlay/src/split/solver_fragment.rs @@ -109,7 +109,7 @@ where use rayon::iter::IntoParallelRefMutIterator; use rayon::iter::ParallelIterator; - struct TaskResult { + struct TaskResult { any_round: bool, marks: Vec>, } From a63bbbe8a908d3fbd6fa89d9d0fd369badede3e7 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sun, 2 Aug 2026 14:30:01 +0300 Subject: [PATCH 08/13] clear buffer on empty geometry --- iOverlay/src/core/overlay.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/iOverlay/src/core/overlay.rs b/iOverlay/src/core/overlay.rs index 85d1be58..50dfb360 100644 --- a/iOverlay/src/core/overlay.rs +++ b/iOverlay/src/core/overlay.rs @@ -389,6 +389,7 @@ where ) { self.split_solver.split_segments(&mut self.segments, &self.solver); if self.segments.is_empty() { + output.clear_and_reserve(0, 0); return; } let mut buffer = self.boolean_buffer.take().unwrap_or_default(); @@ -454,6 +455,7 @@ mod tests { use crate::core::overlay_rule::OverlayRule; use alloc::vec; use i_float::int::point::IntPoint; + use i_shape::flat::buffer::FlatContoursBuffer; use i_shape::int::area::Area; use i_shape::int::shape::IntContour; @@ -858,4 +860,16 @@ mod tests { assert_eq!(result.len(), 0); } + + #[test] + fn test_overlay_into_clears_output_for_empty_input() { + let mut output = FlatContoursBuffer::default(); + output.set_with_contour(&[IntPoint::new(0, 0), IntPoint::new(10, 0), IntPoint::new(0, 10)]); + + let mut overlay = Overlay::::new(0); + overlay.overlay_into(OverlayRule::Subject, FillRule::NonZero, &mut output); + + assert!(output.points.is_empty()); + assert!(output.ranges.is_empty()); + } } From 9e2fb4cdd8989e45627bf71679325f60f1f33ad5 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sun, 2 Aug 2026 14:36:39 +0300 Subject: [PATCH 09/13] close vsegment --- iOverlay/src/core/mod.rs | 1 - iOverlay/src/core/v_segment.rs | 1 - iOverlay/src/geom/v_segment.rs | 13 ++++--------- 3 files changed, 4 insertions(+), 11 deletions(-) delete mode 100644 iOverlay/src/core/v_segment.rs diff --git a/iOverlay/src/core/mod.rs b/iOverlay/src/core/mod.rs index 7b4c715b..0277e466 100644 --- a/iOverlay/src/core/mod.rs +++ b/iOverlay/src/core/mod.rs @@ -14,4 +14,3 @@ pub mod predicate; pub mod relate; pub mod simplify; pub mod solver; -pub mod v_segment; diff --git a/iOverlay/src/core/v_segment.rs b/iOverlay/src/core/v_segment.rs deleted file mode 100644 index d7328b66..00000000 --- a/iOverlay/src/core/v_segment.rs +++ /dev/null @@ -1 +0,0 @@ -pub use crate::geom::v_segment::VSegment; diff --git a/iOverlay/src/geom/v_segment.rs b/iOverlay/src/geom/v_segment.rs index 344dd0ab..2ffc6c8a 100644 --- a/iOverlay/src/geom/v_segment.rs +++ b/iOverlay/src/geom/v_segment.rs @@ -7,17 +7,12 @@ use i_float::triangle::Triangle; use i_tree::{Expiration, ExpiredKey}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct VSegment { - pub a: IntPoint, - pub b: IntPoint, +pub(crate) struct VSegment { + pub(crate) a: IntPoint, + pub(crate) b: IntPoint, } impl VSegment { - #[inline(always)] - pub const fn new(a: IntPoint, b: IntPoint) -> Self { - Self { a, b } - } - #[inline(always)] fn is_under_segment_order(&self, other: &VSegment) -> Ordering { match self.a.cmp(&other.a) { @@ -45,7 +40,7 @@ impl VSegment { } #[inline(always)] - pub fn cmp_by_angle(&self, other: &Self) -> Ordering { + pub(crate) fn cmp_by_angle(&self, other: &Self) -> Ordering { // sort angles counterclockwise // debug_assert!(self.a == other.a); let v0 = self.b - self.a; From 3dda94072e60a3518da298dc0f9cd423c28b3a1d Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sun, 2 Aug 2026 14:40:19 +0300 Subject: [PATCH 10/13] readme fixes --- iOverlay/README.md | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/iOverlay/README.md b/iOverlay/README.md index 545d0c60..0038d33d 100644 --- a/iOverlay/README.md +++ b/iOverlay/README.md @@ -30,8 +30,8 @@ iOverlay powers polygon boolean operations in [geo](https://github.com/georust/g - [Slicing a Polygon with a Polyline](#slicing-a-polygon-with-a-polyline) - [Clipping a Polyline by a Polygon](#clipping-a-polyline-by-a-polygon) - [Buffering](#buffering) - - [Offseting a Path](#offseting-a-path) - - [Offseting a Polygon](#offseting-a-polygon) + - [Offsetting a Path](#offsetting-a-path) + - [Offsetting a Polygon](#offsetting-a-polygon) - [LineCap](#linecap) - [LineJoin](#linejoin) - [FAQ](#faq) @@ -56,7 +56,7 @@ iOverlay powers polygon boolean operations in [geo](https://github.com/georust/g - **Simplification**: removes degenerate vertices and merges collinear edges. - **Buffering**: offsets paths and polygons. - **Fill Rules**: even-odd, non-zero, positive and negative. -- **Data Types**: Supports `i16`/`i32`/`i64` integer APIs and `f32`/`f64` floating-point APIs. +- **Data Types**: supports `i16`/`i32`/`i64` integer APIs and `f32`/`f64` floating-point APIs.   ## Demo @@ -72,8 +72,13 @@ iOverlay powers polygon boolean operations in [geo](https://github.com/georust/g iOverlay supports: -- `i16`/`i32`/`i64` math solvers -- `on`/`off` multithreading feature +- `i16`/`i32`/`i64` integer engines +- Optional multithreading for large inputs through the `allow_multithreading` Cargo feature + +```toml +[dependencies] +i_overlay = { version = "^8.0", features = ["allow_multithreading"] } +``` Average relative time for iOverlay Rust solvers @@ -175,13 +180,13 @@ The result is a vec of shapes: ```   -The `overlay` function returns a `Vec`: +The `overlay` function returns `Shapes

`, which is an alias for `Vec>`: -- `Vec`: A collection of shapes. -- `Shape`: Represents a shape made up of: - - `Vec`: A list of contours. +- `Shapes

`: a collection of shapes. +- `Shape

`: a shape made up of: + - `Vec>`: a list of contours. - The first contour is the outer boundary (counterclockwise), and subsequent contours represent holes (clockwise). -- `Contour`: A sequence of points (`Vec`) forming a closed contour. +- `Contour

`: a sequence of points (`Vec

`) forming a closed contour, where `P` implements `FloatPointCompatible`. **Note**: By default, outer boundaries are counterclockwise and holes are clockwise—unless `main_direction` is set. [More information](https://ishape-rust.github.io/iShape-js/overlay/contours/contours.html) about contours. @@ -216,7 +221,9 @@ enum EdgeKind { } impl OverlayEdgeData for EdgeKind { - fn merge(ctx: EdgeDataMerge) -> Self { + type Store = (); + + fn merge(ctx: EdgeDataMerge, _store: &mut Self::Store) -> Self { match (ctx.lhs_data, ctx.rhs_data) { (EdgeKind::Red, EdgeKind::Red) => EdgeKind::Red, (EdgeKind::Green, EdgeKind::Green) => EdgeKind::Green, From 8efcd0e663c1d78c0aa1dcbdd035b0624dfa854b Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sun, 2 Aug 2026 15:17:12 +0300 Subject: [PATCH 11/13] add tests --- iOverlay/Cargo.toml | 13 ++-- iOverlay/src/mesh/outline/offset.rs | 49 +++++++++++++ iOverlay/src/mesh/stroke/builder_join.rs | 88 ++++++++++++++++++++++++ iOverlay/src/mesh/stroke/offset.rs | 35 ++++++++++ 4 files changed, 177 insertions(+), 8 deletions(-) diff --git a/iOverlay/Cargo.toml b/iOverlay/Cargo.toml index c7215acb..2ee916fc 100644 --- a/iOverlay/Cargo.toml +++ b/iOverlay/Cargo.toml @@ -12,13 +12,12 @@ readme = "README.md" categories = ["algorithms", "graphics", "science::geo", "mathematics", "no-std"] [dependencies] -#i_float = { version = "^3.0.0" } -#i_shape = { version = "^3.0.0" } i_tree = { version = "^0.19.0" } i_key_sort = { version = "^0.11.0" } -i_float = { path = "../../iFloat"} -i_shape = { path = "../../iShape"} +i_float = { version = "^4.0.0", path = "../../iFloat"} +i_shape = { version = "^4.0.0", path = "../../iShape"} + #i_tree = { path = "../../iTree" } #i_key_sort = { path = "../../iKeySort" } @@ -36,7 +35,5 @@ allow_multithreading = ["dep:rayon", "i_key_sort/allow_multithreading"] serde = { version = "^1.0", features = ["derive"] } serde_json = "^1.0" rand = { version = "~0.10", features = ["alloc"] } -i_float = { path = "../../iFloat", features = ["serde"] } -i_shape = { path = "../../iShape", features = ["serde"] } -#i_float = { version = "^3.0.0", features = ["serde"] } -#i_shape = { version = "^3.0.0", features = ["serde"] } +i_float = { version = "^4.0.0", path = "../../iFloat", features = ["serde"] } +i_shape = { version = "^4.0.0", path = "../../iShape", features = ["serde"] } \ No newline at end of file diff --git a/iOverlay/src/mesh/outline/offset.rs b/iOverlay/src/mesh/outline/offset.rs index 06f81839..96c8bf95 100644 --- a/iOverlay/src/mesh/outline/offset.rs +++ b/iOverlay/src/mesh/outline/offset.rs @@ -661,6 +661,55 @@ mod tests { assert_eq!(path.len(), 4); } + #[test] + fn test_near_zero_offset_preserves_geometry_at_fixed_scale() { + let path = [[-5.0, -5.0f32], [5.0, -5.0], [5.0, 5.0], [-5.0, 5.0]]; + let zero_style = OutlineStyle::new(0.0); + let near_zero_style = OutlineStyle::new(0.01); + + let expected = path.outline_fixed_scale(&zero_style, 10.0).unwrap(); + let shapes = path.outline_fixed_scale(&near_zero_style, 10.0).unwrap(); + + assert_eq!(shapes, expected); + assert_eq!(shapes.len(), 1); + assert_eq!(shapes[0].len(), 1); + assert_eq!(shapes[0][0].len(), 4); + } + + #[test] + fn test_repeated_points_preserve_zero_offset_geometry() { + let path = [ + [-5.0, -5.0f32], + [-5.0, -5.0], + [5.0, -5.0], + [5.0, -5.0], + [5.0, 5.0], + [-5.0, 5.0], + [-5.0, 5.0], + [-5.0, -5.0], + ]; + let expected_path = [[-5.0, -5.0f32], [5.0, -5.0], [5.0, 5.0], [-5.0, 5.0]]; + let style = OutlineStyle::new(0.0); + + let shapes = path.outline_fixed_scale(&style, 10.0).unwrap(); + let expected = expected_path.outline_fixed_scale(&style, 10.0).unwrap(); + + assert_eq!(shapes, expected); + assert_eq!(shapes.len(), 1); + assert_eq!(shapes[0].len(), 1); + assert_eq!(shapes[0][0].len(), 4); + } + + #[test] + fn test_degenerate_contours_return_empty_geometry() { + let repeated_point = [[1.0, 1.0f32], [1.0, 1.0], [1.0, 1.0]]; + let collinear = [[0.0, 0.0f32], [5.0, 0.0], [10.0, 0.0]]; + let style = OutlineStyle::new(1.0); + + assert!(repeated_point.outline(&style).is_empty()); + assert!(collinear.outline(&style).is_empty()); + } + #[test] fn test_outline_into_ok() { let path = [[-5.0, -5.0f32], [5.0, -5.0], [5.0, 5.0], [-5.0, 5.0]]; diff --git a/iOverlay/src/mesh/stroke/builder_join.rs b/iOverlay/src/mesh/stroke/builder_join.rs index 8dcaadc0..14ff729e 100644 --- a/iOverlay/src/mesh/stroke/builder_join.rs +++ b/iOverlay/src/mesh/stroke/builder_join.rs @@ -300,3 +300,91 @@ impl JoinBuilder for RoundJoinBuild P::Scalar::from_float(1.1) * radius } } + +#[cfg(test)] +mod tests { + use super::{BevelJoinBuilder, JoinBuilder, MiterJoinBuilder, RoundJoinBuilder}; + use crate::mesh::stroke::section::Section; + use crate::segm::boolean::ShapeCountBoolean; + use crate::segm::segment::Segment; + use alloc::vec::Vec; + use core::f64::consts::PI; + use i_float::adapter::FloatPointAdapter; + use i_float::float::rect::FloatRect; + + type TestSegment = Segment; + + fn build_join>( + builder: &J, + radius: f64, + a: [f64; 2], + b: [f64; 2], + c: [f64; 2], + scale: f64, + ) -> Vec { + let rect = FloatRect::new(-20.0, 20.0, -20.0, 20.0); + let adapter = FloatPointAdapter::try_with_scale(rect, scale).unwrap(); + let s0 = Section::new(radius, &a, &b); + let s1 = Section::new(radius, &b, &c); + let mut segments = Vec::new(); + + builder.add_join(&s0, &s1, &adapter, &mut segments); + + segments + } + + #[test] + fn acute_angle_uses_each_requested_join_type() { + let radius = 1.0; + let a = [-10.0, 0.0]; + let b = [0.0, 0.0]; + let c = [-10.0, 0.1]; + let scale = 1_000.0; + + let bevel = build_join(&BevelJoinBuilder, radius, a, b, c, scale); + let miter = build_join(&MiterJoinBuilder::new(PI / 6.0, radius), radius, a, b, c, scale); + let round = build_join(&RoundJoinBuilder::new(PI / 12.0, radius), radius, a, b, c, scale); + + assert_eq!(bevel.len(), 2); + assert_eq!(miter.len(), 4); + assert!(round.len() > miter.len()); + assert_ne!(miter, bevel); + assert_ne!(round, bevel); + } + + #[test] + fn near_collinear_segments_fall_back_to_stable_bevel_join() { + let radius = 1.0; + let a = [-10.0, 0.0]; + let b = [0.0, 0.0]; + let c = [10.0, 0.000_01]; + let scale = 1_000_000.0; + + let bevel = build_join(&BevelJoinBuilder, radius, a, b, c, scale); + let miter = build_join(&MiterJoinBuilder::new(PI / 6.0, radius), radius, a, b, c, scale); + let round = build_join(&RoundJoinBuilder::new(PI / 12.0, radius), radius, a, b, c, scale); + + assert_eq!(miter, bevel); + assert_eq!(round, bevel); + + let repeated = build_join(&MiterJoinBuilder::new(PI / 6.0, radius), radius, a, b, c, scale); + assert_eq!(repeated, miter); + } + + #[test] + fn tiny_offset_does_not_create_degenerate_join_segments() { + let radius = 0.01; + let a = [-10.0, 0.0]; + let b = [0.0, 0.0]; + let c = [0.0, 10.0]; + let scale = 10.0; + + let bevel = build_join(&BevelJoinBuilder, radius, a, b, c, scale); + let miter = build_join(&MiterJoinBuilder::new(PI / 6.0, radius), radius, a, b, c, scale); + let round = build_join(&RoundJoinBuilder::new(PI / 12.0, radius), radius, a, b, c, scale); + + assert!(bevel.is_empty()); + assert!(miter.is_empty()); + assert!(round.is_empty()); + } +} diff --git a/iOverlay/src/mesh/stroke/offset.rs b/iOverlay/src/mesh/stroke/offset.rs index 6303a64a..3b18c960 100644 --- a/iOverlay/src/mesh/stroke/offset.rs +++ b/iOverlay/src/mesh/stroke/offset.rs @@ -770,6 +770,41 @@ mod tests { assert_eq!(shapes.len(), 0); } + #[test] + fn test_zero_width_returns_empty_geometry() { + let path = [[0.0, 0.0], [10.0, 0.0]]; + + let shapes = path.stroke(StrokeStyle::new(0.0), false); + + assert!(shapes.is_empty()); + } + + #[test] + fn test_near_zero_width_returns_empty_geometry_at_fixed_scale() { + let path = [[0.0, 0.0], [10.0, 0.0]]; + + let shapes = path + .stroke_fixed_scale(StrokeStyle::new(0.1), false, 10.0) + .unwrap(); + + assert!(shapes.is_empty()); + } + + #[test] + fn test_repeated_points_do_not_change_stroke_geometry() { + let path = [[0.0, 0.0], [0.0, 0.0], [10.0, 0.0], [10.0, 0.0]]; + let expected_path = [[0.0, 0.0], [10.0, 0.0]]; + let style = StrokeStyle::new(2.0); + + let shapes = path.stroke_fixed_scale(style.clone(), false, 10.0).unwrap(); + let expected = expected_path.stroke_fixed_scale(style, false, 10.0).unwrap(); + + assert_eq!(shapes, expected); + assert_eq!(shapes.len(), 1); + assert_eq!(shapes[0].len(), 1); + assert_eq!(shapes[0][0].len(), 4); + } + #[test] fn test_many_paths() { let paths = [vec![[0.0, 0.0], [5.0, 0.0]], vec![[0.0, 0.0], [5.0, -5.0]]]; From aa30aa23041d37b21d8201c99c66ac4495f930df Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sun, 2 Aug 2026 15:24:10 +0300 Subject: [PATCH 12/13] more tests --- iOverlay/src/build/string.rs | 105 +++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/iOverlay/src/build/string.rs b/iOverlay/src/build/string.rs index ea039570..b91063fc 100644 --- a/iOverlay/src/build/string.rs +++ b/iOverlay/src/build/string.rs @@ -228,3 +228,108 @@ impl StringFillFilter for SegmentFill { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::build::builder::GraphBuilder; + use crate::core::solver::Solver; + use crate::string::overlay::StringOverlay; + use alloc::vec; + use i_float::int::point::IntPoint; + + fn clip_rule(invert: bool) -> ClipRule { + ClipRule { + invert, + boundary_included: false, + } + } + + fn clip_path(shape: &[IntPoint], path: &[IntPoint], invert: bool) -> Vec>> { + let mut overlay = StringOverlay::with_shape_contour(shape); + overlay.add_string_path(path); + overlay.clip_string_lines(FillRule::NonZero, clip_rule(invert)) + } + + #[test] + fn empty_segment_set_builds_empty_graph_for_every_fill_rule() { + let solver = Solver::default(); + let segments: Vec> = Vec::new(); + + for fill_rule in [ + FillRule::EvenOdd, + FillRule::NonZero, + FillRule::Positive, + FillRule::Negative, + ] { + let mut builder = GraphBuilder::new(); + let graph = builder.build_string_all(fill_rule, &solver, &segments); + + assert!(graph.nodes.is_empty()); + assert!(graph.links.is_empty()); + } + } + + #[test] + fn minimal_string_inputs_are_rejected() { + let point = IntPoint::new(1, 1); + let mut overlay = StringOverlay::::new(0); + + overlay.add_string_path(&[]); + overlay.add_string_path(&[point]); + overlay.add_string_line([point, point]); + overlay.add_string_path(&[point, point, point]); + overlay.add_string_contour(&[point, point]); + + assert!(overlay.build_graph_view(FillRule::NonZero).is_none()); + } + + #[test] + fn repeated_string_points_are_normalized_consistently() { + let shape = [ + IntPoint::new(0, 0), + IntPoint::new(10, 0), + IntPoint::new(10, 10), + IntPoint::new(0, 10), + ]; + let clean = [IntPoint::new(-5, 5), IntPoint::new(5, 5), IntPoint::new(15, 5)]; + let repeated = [ + IntPoint::new(-5, 5), + IntPoint::new(-5, 5), + IntPoint::new(5, 5), + IntPoint::new(5, 5), + IntPoint::new(15, 5), + IntPoint::new(15, 5), + ]; + + let expected = clip_path(&shape, &clean, false); + let result = clip_path(&shape, &repeated, false); + + assert_eq!(result, expected); + assert_eq!( + result, + vec![vec![ + IntPoint::new(0, 5), + IntPoint::new(5, 5), + IntPoint::new(10, 5), + ]] + ); + } + + #[test] + fn zero_area_subject_contours_do_not_affect_clipping() { + let line = [IntPoint::new(-5, 0), IntPoint::new(15, 0)]; + let expected_outside = vec![line.to_vec()]; + let contours = [ + vec![IntPoint::new(0, 0)], + vec![IntPoint::new(0, 0), IntPoint::new(0, 0)], + vec![IntPoint::new(0, 0), IntPoint::new(10, 0)], + vec![IntPoint::new(0, 0), IntPoint::new(5, 0), IntPoint::new(10, 0)], + ]; + + for contour in contours { + assert!(clip_path(&contour, &line, false).is_empty()); + assert_eq!(clip_path(&contour, &line, true), expected_outside); + } + } +} From fda2cb3057c2b99d8421525aa23ba6dbfcbd97f5 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sun, 2 Aug 2026 16:22:28 +0300 Subject: [PATCH 13/13] fix cargo to pass ci tests --- iOverlay/Cargo.toml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/iOverlay/Cargo.toml b/iOverlay/Cargo.toml index 2ee916fc..443525e1 100644 --- a/iOverlay/Cargo.toml +++ b/iOverlay/Cargo.toml @@ -15,9 +15,11 @@ categories = ["algorithms", "graphics", "science::geo", "mathematics", "no-std"] i_tree = { version = "^0.19.0" } i_key_sort = { version = "^0.11.0" } -i_float = { version = "^4.0.0", path = "../../iFloat"} -i_shape = { version = "^4.0.0", path = "../../iShape"} +i_float = { version = "^4.0.0"} +i_shape = { version = "^4.0.0"} +#i_float = { path = "../../iFloat"} +#i_shape = { path = "../../iShape"} #i_tree = { path = "../../iTree" } #i_key_sort = { path = "../../iKeySort" } @@ -35,5 +37,7 @@ allow_multithreading = ["dep:rayon", "i_key_sort/allow_multithreading"] serde = { version = "^1.0", features = ["derive"] } serde_json = "^1.0" rand = { version = "~0.10", features = ["alloc"] } -i_float = { version = "^4.0.0", path = "../../iFloat", features = ["serde"] } -i_shape = { version = "^4.0.0", path = "../../iShape", features = ["serde"] } \ No newline at end of file +i_float = { version = "^4.0.0", features = ["serde"] } +i_shape = { version = "^4.0.0", features = ["serde"] } +#i_float = { path = "../../iFloat", features = ["serde"] } +#i_shape = { path = "../../iShape", features = ["serde"] } \ No newline at end of file