From 683e8b43f3f79b80ff02f6ac95ec102cef6b8f55 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sat, 2 May 2026 21:55:17 +0300 Subject: [PATCH 01/36] base impl --- iOverlay/README.md | 71 +++++++++++++++ iOverlay/src/build/boolean.rs | 31 +++---- iOverlay/src/build/builder.rs | 25 +++--- iOverlay/src/build/graph.rs | 3 +- iOverlay/src/build/sweep.rs | 42 +++++---- iOverlay/src/core/edge_data.rs | 40 +++++++++ iOverlay/src/core/edge_overlay.rs | 82 +++++++++++++++++ iOverlay/src/core/graph.rs | 4 +- iOverlay/src/core/link.rs | 9 +- iOverlay/src/core/mod.rs | 2 + iOverlay/src/core/predicate.rs | 1 + iOverlay/src/core/solver.rs | 6 +- iOverlay/src/mesh/subject.rs | 2 + iOverlay/src/segm/boolean.rs | 6 -- iOverlay/src/segm/build.rs | 22 +++++ iOverlay/src/segm/merge.rs | 17 +++- iOverlay/src/segm/segment.rs | 22 +++-- iOverlay/src/segm/sort.rs | 2 +- iOverlay/src/segm/string.rs | 6 -- iOverlay/src/segm/winding.rs | 1 - iOverlay/src/split/solver.rs | 44 ++++++--- iOverlay/src/split/solver_fragment.rs | 5 +- iOverlay/src/split/solver_list.rs | 5 +- iOverlay/src/split/solver_tree.rs | 9 +- iOverlay/src/string/overlay.rs | 2 + iOverlay/src/vector/edge.rs | 23 +++++ iOverlay/src/vector/extract.rs | 12 ++- iOverlay/tests/edge_overlay_tests.rs | 89 +++++++++++++++++++ performance/rust_app/Cargo.toml | 4 +- performance/rust_app/src/main.rs | 2 +- .../rust_app/src/test/test_0_checkerboard.rs | 1 - 31 files changed, 494 insertions(+), 96 deletions(-) create mode 100644 iOverlay/src/core/edge_data.rs create mode 100644 iOverlay/src/core/edge_overlay.rs create mode 100644 iOverlay/tests/edge_overlay_tests.rs diff --git a/iOverlay/README.md b/iOverlay/README.md index 4d092f6..6fcdab2 100644 --- a/iOverlay/README.md +++ b/iOverlay/README.md @@ -23,6 +23,7 @@ iOverlay powers polygon boolean operations in [geo](https://github.com/georust/g - [Boolean Operations](#boolean-operations) - [Simple Example](#simple-example) - [Overlay Rules](#overlay-rules) + - [Edge Attributes and Provenance](#edge-attributes-and-provenance) - [Spatial Predicates](#spatial-predicates) - [Custom Point Type Support](#custom-point-type-support) - [Slicing & Clipping](#slicing--clipping) @@ -184,6 +185,76 @@ The `overlay` function returns a `Vec`: |---------|---------------|----------------------|----------------|--------------------|----------------| | AB | Union | Intersection | Difference | Inverse Difference | Exclusion | +  +### Edge Attributes and Provenance + +Use `EdgeOverlay` when the result boundary needs to keep data from the original input edges: source ids, layer ids, material ids, constraints, styles, or any other edge provenance. The regular `Overlay` API remains optimized for plain geometry; this API is opt-in and works with user-defined payloads. + +When an input edge is split by intersections, its data is copied by default. When coincident edges are merged, your `OverlayEdgeData` implementation decides what the resulting data means. A common policy is to keep identical values and mark conflicts as `Undefined`. + +```rust +use i_overlay::core::edge_data::{EdgeDataMerge, OverlayEdgeData}; +use i_overlay::core::edge_overlay::{EdgeOverlay, InputEdge}; +use i_overlay::core::fill_rule::FillRule; +use i_overlay::core::overlay::ShapeType; +use i_overlay::core::overlay_rule::OverlayRule; +use i_overlay::i_float::int::point::IntPoint; +use i_overlay::segm::boolean::ShapeCountBoolean; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum EdgeKind { + Red, + Green, + Undefined, +} + +impl OverlayEdgeData for EdgeKind { + fn merge(ctx: EdgeDataMerge) -> Self { + match (ctx.lhs_data, ctx.rhs_data) { + (EdgeKind::Red, EdgeKind::Red) => EdgeKind::Red, + (EdgeKind::Green, EdgeKind::Green) => EdgeKind::Green, + _ => EdgeKind::Undefined, + } + } +} + +let mut overlay = EdgeOverlay::new(8); + +let red_square = [ + [0, 0], [4, 0], [4, 4], [0, 4], +]; +let green_square = [ + [2, 0], [6, 0], [6, 4], [2, 4], +]; + +for edge in red_square.windows(2).map(|w| (w[0], w[1])) + .chain([(red_square[3], red_square[0])]) +{ + overlay.add_edge(InputEdge { + a: IntPoint::new(edge.0[0], edge.0[1]), + b: IntPoint::new(edge.1[0], edge.1[1]), + data: EdgeKind::Red, + }, ShapeType::Subject); +} + +for edge in green_square.windows(2).map(|w| (w[0], w[1])) + .chain([(green_square[3], green_square[0])]) +{ + overlay.add_edge(InputEdge { + a: IntPoint::new(edge.0[0], edge.0[1]), + b: IntPoint::new(edge.1[0], edge.1[1]), + data: EdgeKind::Green, + }, ShapeType::Clip); +} + +let edges = overlay.build_separate_vectors(OverlayRule::Union, FillRule::NonZero); + +// Each output edge contains geometry, fill, and the propagated user data. +assert!(edges.iter().any(|edge| edge.data == EdgeKind::Undefined)); +``` + +This API currently targets integer boolean operations and exports the result as edges. It intentionally preserves edge runs so downstream crates can decide how to simplify or resolve conflicting attributes. +   ## Spatial Predicates diff --git a/iOverlay/src/build/boolean.rs b/iOverlay/src/build/boolean.rs index c6b3a92..1cddb5c 100644 --- a/iOverlay/src/build/boolean.rs +++ b/iOverlay/src/build/boolean.rs @@ -2,6 +2,7 @@ use crate::build::builder::{GraphBuilder, InclusionFilterStrategy}; use crate::build::sweep::{ EvenOddStrategy, FillStrategy, NegativeStrategy, NonZeroStrategy, PositiveStrategy, }; +use crate::core::edge_data::OverlayEdgeData; use crate::core::extract::VisitState; use crate::core::fill_rule::FillRule; use crate::core::graph::OverlayGraph; @@ -20,15 +21,15 @@ use crate::segm::winding::WindingCount; use alloc::vec::Vec; use i_shape::util::reserve::Reserve; -impl GraphBuilder { +impl GraphBuilder { #[inline] pub(crate) fn build_boolean_all( &mut self, fill_rule: FillRule, options: IntOverlayOptions, solver: &Solver, - segments: &[Segment], - ) -> OverlayGraph<'_> { + segments: &[Segment], + ) -> OverlayGraph<'_, D> { self.build_boolean_fills(fill_rule, solver, segments); self.build_links_all(segments); self.boolean_graph(options, solver) @@ -41,8 +42,8 @@ impl GraphBuilder { overlay_rule: OverlayRule, options: IntOverlayOptions, solver: &Solver, - segments: &[Segment], - ) -> OverlayGraph<'_> { + segments: &[Segment], + ) -> OverlayGraph<'_, D> { self.build_boolean_fills(fill_rule, solver, segments); match overlay_rule { OverlayRule::Subject => self.build_links_by_filter::(segments), @@ -61,7 +62,7 @@ impl GraphBuilder { &mut self, fill_rule: FillRule, solver: &Solver, - segments: &[Segment], + segments: &[Segment], ) { match fill_rule { FillRule::EvenOdd => self.build_fills_with_strategy::(solver, segments), @@ -72,7 +73,7 @@ impl GraphBuilder { } #[inline] - fn boolean_graph(&mut self, options: IntOverlayOptions, solver: &Solver) -> OverlayGraph<'_> { + fn boolean_graph(&mut self, options: IntOverlayOptions, solver: &Solver) -> OverlayGraph<'_, D> { self.build_nodes_and_connect_links(solver); OverlayGraph { nodes: &self.nodes, @@ -273,7 +274,7 @@ impl BooleanFillFilter for SegmentFill { } } -impl OverlayLinkFilter for [OverlayLink] { +impl OverlayLinkFilter for [OverlayLink] { #[inline] fn filter_by_overlay_into(&self, overlay_rule: OverlayRule, buffer: &mut Vec) { match overlay_rule { @@ -289,7 +290,7 @@ impl OverlayLinkFilter for [OverlayLink] { } #[inline] -fn filter_subject_into(links: &[OverlayLink], buffer: &mut Vec) { +fn filter_subject_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); buffer.reserve_capacity(links.len()); for link in links.iter() { @@ -298,7 +299,7 @@ fn filter_subject_into(links: &[OverlayLink], buffer: &mut Vec) { } #[inline] -fn filter_clip_into(links: &[OverlayLink], buffer: &mut Vec) { +fn filter_clip_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); buffer.reserve_capacity(links.len()); for link in links.iter() { @@ -307,7 +308,7 @@ fn filter_clip_into(links: &[OverlayLink], buffer: &mut Vec) { } #[inline] -fn filter_intersect_into(links: &[OverlayLink], buffer: &mut Vec) { +fn filter_intersect_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); buffer.reserve_capacity(links.len()); for link in links.iter() { @@ -316,7 +317,7 @@ fn filter_intersect_into(links: &[OverlayLink], buffer: &mut Vec) { } #[inline] -fn filter_union_into(links: &[OverlayLink], buffer: &mut Vec) { +fn filter_union_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); buffer.reserve_capacity(links.len()); for link in links.iter() { @@ -325,7 +326,7 @@ fn filter_union_into(links: &[OverlayLink], buffer: &mut Vec) { } #[inline] -fn filter_difference_into(links: &[OverlayLink], buffer: &mut Vec) { +fn filter_difference_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); buffer.reserve_capacity(links.len()); for link in links.iter() { @@ -334,7 +335,7 @@ fn filter_difference_into(links: &[OverlayLink], buffer: &mut Vec) { } #[inline] -fn filter_inverse_difference_into(links: &[OverlayLink], buffer: &mut Vec) { +fn filter_inverse_difference_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); buffer.reserve_capacity(links.len()); for link in links.iter() { @@ -343,7 +344,7 @@ fn filter_inverse_difference_into(links: &[OverlayLink], buffer: &mut Vec) { +fn filter_xor_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); buffer.reserve_capacity(links.len()); for link in links.iter() { diff --git a/iOverlay/src/build/builder.rs b/iOverlay/src/build/builder.rs index db12a52..e021aa7 100644 --- a/iOverlay/src/build/builder.rs +++ b/iOverlay/src/build/builder.rs @@ -1,4 +1,5 @@ use crate::build::sweep::{FillHandler, FillStrategy, SweepRunner}; +use crate::core::edge_data::OverlayEdgeData; use crate::core::link::OverlayLink; use crate::core::solver::Solver; use crate::geom::end::End; @@ -24,11 +25,11 @@ impl<'a> StoreFillsHandler<'a> { } } -impl FillHandler for StoreFillsHandler<'_> { +impl FillHandler for StoreFillsHandler<'_> { type Output = (); #[inline(always)] - fn handle(&mut self, index: usize, _segment: &Segment, fill: SegmentFill) -> ControlFlow<()> { + fn handle(&mut self, index: usize, _segment: &Segment, fill: SegmentFill) -> ControlFlow<()> { // fills is pre-allocated to segments.len() and index is guaranteed // to be in range by the sweep algorithm unsafe { *self.fills.get_unchecked_mut(index) = fill }; @@ -43,15 +44,15 @@ pub(crate) trait GraphNode { fn with_indices(indices: &[usize]) -> Self; } -pub(crate) struct GraphBuilder { +pub(crate) struct GraphBuilder { sweep_runner: SweepRunner, - pub(super) links: Vec, + pub(super) links: Vec>, pub(super) nodes: Vec, pub(super) fills: Vec, pub(super) ends: Vec, } -impl GraphBuilder { +impl> GraphBuilder { #[inline] pub(crate) fn new() -> Self { Self { @@ -67,15 +68,15 @@ impl GraphBuilder { pub(super) fn build_fills_with_strategy>( &mut self, solver: &Solver, - segments: &[Segment], + segments: &[Segment], ) { self.fills.resize(segments.len(), NONE); self.sweep_runner - .run::(solver, segments, StoreFillsHandler::new(&mut self.fills)); + .run::(solver, segments, StoreFillsHandler::new(&mut self.fills)); } #[inline] - pub(super) fn build_links_by_filter(&mut self, segments: &[Segment]) { + pub(super) fn build_links_by_filter(&mut self, segments: &[Segment]) { self.links.clear(); self.links.reserve_capacity(segments.len()); @@ -83,24 +84,26 @@ impl GraphBuilder { if !F::is_included(fill) { continue; } - self.links.push(OverlayLink::new( + self.links.push(OverlayLink::new_with_data( IdPoint::new(0, segment.x_segment.a), IdPoint::new(0, segment.x_segment.b), fill, + segment.data, )); } } #[inline] - pub(super) fn build_links_all(&mut self, segments: &[Segment]) { + pub(super) fn build_links_all(&mut self, segments: &[Segment]) { self.links.clear(); self.links.reserve_capacity(segments.len()); for (segment, &fill) in segments.iter().zip(&self.fills) { - self.links.push(OverlayLink::new( + self.links.push(OverlayLink::new_with_data( IdPoint::new(0, segment.x_segment.a), IdPoint::new(0, segment.x_segment.b), fill, + segment.data, )); } } diff --git a/iOverlay/src/build/graph.rs b/iOverlay/src/build/graph.rs index 0661b04..72df7f6 100644 --- a/iOverlay/src/build/graph.rs +++ b/iOverlay/src/build/graph.rs @@ -1,11 +1,12 @@ use crate::build::builder::{GraphBuilder, GraphNode}; +use crate::core::edge_data::OverlayEdgeData; use crate::core::solver::Solver; use crate::geom::end::End; use crate::segm::winding::WindingCount; use alloc::vec::Vec; use i_key_sort::sort::two_keys::TwoKeysSort; -impl GraphBuilder { +impl> GraphBuilder { pub(super) fn build_nodes_and_connect_links(&mut self, solver: &Solver) { let n = self.links.len(); if n == 0 { diff --git a/iOverlay/src/build/sweep.rs b/iOverlay/src/build/sweep.rs index 0bf0286..70b793d 100644 --- a/iOverlay/src/build/sweep.rs +++ b/iOverlay/src/build/sweep.rs @@ -16,19 +16,24 @@ pub(crate) trait FillStrategy { fn add_and_fill(this: C, bot: C) -> (C, SegmentFill); } -pub(crate) trait FillHandler { +pub(crate) trait FillHandler { type Output; - fn handle(&mut self, index: usize, segment: &Segment, fill: SegmentFill) -> ControlFlow; + fn handle( + &mut self, + index: usize, + segment: &Segment, + fill: SegmentFill, + ) -> ControlFlow; fn finalize(self) -> Self::Output; } #[inline] -fn sweep_with_handler(scan: &mut S, segments: &[Segment], mut handler: H) -> H::Output +fn sweep_with_handler(scan: &mut S, segments: &[Segment], mut handler: H) -> H::Output where C: WindingCount, F: FillStrategy, S: KeyExpCollection, - H: FillHandler, + H: FillHandler, { let mut node = Vec::with_capacity(4); let n = segments.len(); @@ -92,47 +97,54 @@ impl SweepRunner { } #[inline] - pub(crate) fn run(&mut self, solver: &Solver, segments: &[Segment], handler: H) -> H::Output + pub(crate) fn run( + &mut self, + solver: &Solver, + segments: &[Segment], + handler: H, + ) -> H::Output where F: FillStrategy, - H: FillHandler, + H: FillHandler, + D: Send, { let count = segments.len(); if solver.is_list_fill(segments) { let capacity = count.log2_sqrt().max(4) * 2; let mut list = self.take_scan_list(capacity); - let result = sweep_with_handler::(&mut list, segments, handler); + let result = sweep_with_handler::(&mut list, segments, handler); self.list = Some(list); result } else { let capacity = count.log2_sqrt().max(8); let mut tree = self.take_scan_tree(capacity); - let result = sweep_with_handler::(&mut tree, segments, handler); + let result = sweep_with_handler::(&mut tree, segments, handler); self.tree = Some(tree); result } } #[inline] - pub(crate) fn run_with_fill_rule( + pub(crate) fn run_with_fill_rule( &mut self, fill_rule: FillRule, solver: &Solver, - segments: &[Segment], + segments: &[Segment], handler: H, ) -> H::Output where - H: FillHandler, + H: FillHandler, + D: Send, EvenOddStrategy: FillStrategy, NonZeroStrategy: FillStrategy, PositiveStrategy: FillStrategy, NegativeStrategy: FillStrategy, { match fill_rule { - FillRule::EvenOdd => self.run::(solver, segments, handler), - FillRule::NonZero => self.run::(solver, segments, handler), - FillRule::Positive => self.run::(solver, segments, handler), - FillRule::Negative => self.run::(solver, segments, handler), + FillRule::EvenOdd => self.run::(solver, segments, handler), + FillRule::NonZero => self.run::(solver, segments, handler), + FillRule::Positive => self.run::(solver, segments, handler), + FillRule::Negative => self.run::(solver, segments, handler), } } diff --git a/iOverlay/src/core/edge_data.rs b/iOverlay/src/core/edge_data.rs new file mode 100644 index 0000000..1285e71 --- /dev/null +++ b/iOverlay/src/core/edge_data.rs @@ -0,0 +1,40 @@ +use crate::segm::boolean::ShapeCountBoolean; +use i_float::int::point::IntPoint; + +pub trait OverlayEdgeData: Copy + Send + Sync { + #[inline(always)] + fn reversed(self) -> Self { + self + } + + #[inline(always)] + fn split(self, _ctx: EdgeDataSplit) -> (Self, Self) { + (self, self) + } + + fn merge(ctx: EdgeDataMerge) -> Self; +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EdgeDataSplit { + pub a: IntPoint, + pub p: IntPoint, + pub b: IntPoint, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EdgeDataMerge { + pub lhs_data: D, + pub lhs_count: C, + pub rhs_data: D, + pub rhs_count: C, + pub out_count: C, +} + +impl OverlayEdgeData for () +where + C: Copy + Send + Sync, +{ + #[inline(always)] + fn merge(_: EdgeDataMerge) -> Self {} +} diff --git a/iOverlay/src/core/edge_overlay.rs b/iOverlay/src/core/edge_overlay.rs new file mode 100644 index 0000000..4df7958 --- /dev/null +++ b/iOverlay/src/core/edge_overlay.rs @@ -0,0 +1,82 @@ +use crate::build::builder::GraphBuilder; +use crate::core::edge_data::OverlayEdgeData; +use crate::core::fill_rule::FillRule; +use crate::core::graph::OverlayNode; +use crate::core::overlay::{IntOverlayOptions, ShapeType}; +use crate::core::overlay_rule::OverlayRule; +use crate::core::solver::Solver; +use crate::segm::boolean::ShapeCountBoolean; +use crate::segm::segment::Segment; +use crate::segm::winding::WindingCount; +use crate::split::solver::SplitSolver; +use crate::vector::edge::DataVectorEdge; +use alloc::vec::Vec; +use i_float::int::point::IntPoint; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct InputEdge { + pub a: IntPoint, + pub b: IntPoint, + pub data: D, +} + +pub struct EdgeOverlay { + pub solver: Solver, + pub options: IntOverlayOptions, + segments: Vec>, + split_solver: SplitSolver, + graph_builder: GraphBuilder, +} + +impl EdgeOverlay { + pub fn new(capacity: usize) -> Self { + Self { + solver: Default::default(), + options: IntOverlayOptions::keep_output_points(), + segments: Vec::with_capacity(capacity), + split_solver: SplitSolver::new(), + graph_builder: GraphBuilder::::new(), + } + } + + pub fn add_edge(&mut self, edge: InputEdge, shape_type: ShapeType) { + if edge.a == edge.b { + return; + } + + let (direct, invert) = ShapeCountBoolean::with_shape_type(shape_type); + self.segments.push(Segment::with_ab_and_data( + edge.a, edge.b, direct, invert, edge.data, + )); + } + + pub fn add_edges(&mut self, edges: I, shape_type: ShapeType) + where + I: IntoIterator>, + { + for edge in edges { + self.add_edge(edge, shape_type); + } + } + + pub fn build_separate_vectors( + &mut self, + overlay_rule: OverlayRule, + fill_rule: FillRule, + ) -> Vec> { + self.split_solver.split_segments(&mut self.segments, &self.solver); + if self.segments.is_empty() { + return Vec::new(); + } + + self.graph_builder + .build_boolean_overlay( + fill_rule, + overlay_rule, + self.options, + &self.solver, + &self.segments, + ) + .extract_separate_data_vectors() + } +} diff --git a/iOverlay/src/core/graph.rs b/iOverlay/src/core/graph.rs index 9936bc4..8e5edfc 100644 --- a/iOverlay/src/core/graph.rs +++ b/iOverlay/src/core/graph.rs @@ -13,10 +13,10 @@ use alloc::vec::Vec; /// /// Use `OverlayGraph` to perform boolean operations on the geometric shapes you've added to an `Overlay`, after it has processed the shapes according to the specified build and overlay rules. /// [More information](https://ishape-rust.github.io/iShape-js/overlay/overlay_graph/overlay_graph.html) about Overlay Graph. -pub struct OverlayGraph<'a> { +pub struct OverlayGraph<'a, D = ()> { pub(crate) options: IntOverlayOptions, pub(crate) nodes: &'a [OverlayNode], - pub(crate) links: &'a [OverlayLink], + pub(crate) links: &'a [OverlayLink], } #[derive(Debug)] diff --git a/iOverlay/src/core/link.rs b/iOverlay/src/core/link.rs index bd67256..89a1b6e 100644 --- a/iOverlay/src/core/link.rs +++ b/iOverlay/src/core/link.rs @@ -5,16 +5,17 @@ use crate::segm::segment::SegmentFill; use alloc::vec::Vec; #[derive(Debug, Clone, Copy)] -pub(crate) struct OverlayLink { +pub(crate) struct OverlayLink { pub(crate) a: IdPoint, pub(crate) b: IdPoint, pub(crate) fill: SegmentFill, + pub(crate) data: D, } -impl OverlayLink { +impl OverlayLink { #[inline(always)] - pub(crate) fn new(a: IdPoint, b: IdPoint, fill: SegmentFill) -> OverlayLink { - OverlayLink { a, b, fill } + pub(crate) fn new_with_data(a: IdPoint, b: IdPoint, fill: SegmentFill, data: D) -> OverlayLink { + OverlayLink { a, b, fill, data } } #[inline(always)] diff --git a/iOverlay/src/core/mod.rs b/iOverlay/src/core/mod.rs index c8ae4d9..dff945b 100644 --- a/iOverlay/src/core/mod.rs +++ b/iOverlay/src/core/mod.rs @@ -1,4 +1,6 @@ pub mod divide; +pub mod edge_data; +pub mod edge_overlay; pub mod extract; mod extract_ogc; pub mod fill_rule; diff --git a/iOverlay/src/core/predicate.rs b/iOverlay/src/core/predicate.rs index 2bab0ab..08c6982 100644 --- a/iOverlay/src/core/predicate.rs +++ b/iOverlay/src/core/predicate.rs @@ -331,6 +331,7 @@ mod tests { b: IntPoint::new(bx, by), }, count: ShapeCountBoolean { subj, clip }, + data: (), } } diff --git a/iOverlay/src/core/solver.rs b/iOverlay/src/core/solver.rs index 144011c..f8b728c 100644 --- a/iOverlay/src/core/solver.rs +++ b/iOverlay/src/core/solver.rs @@ -161,7 +161,7 @@ impl Solver { } } - pub(crate) fn is_list_split(&self, segments: &[Segment]) -> bool { + pub(crate) fn is_list_split(&self, segments: &[Segment]) -> bool { match self.strategy { List => true, Tree | Frag => false, @@ -169,11 +169,11 @@ impl Solver { } } - pub(crate) fn is_fragmentation_required(&self, segments: &[Segment]) -> bool { + pub(crate) fn is_fragmentation_required(&self, segments: &[Segment]) -> bool { segments.len() > Self::MIN_FRAGMENT_COUNT || self.strategy == Frag } - pub(crate) fn is_list_fill(&self, segments: &[Segment]) -> bool { + pub(crate) fn is_list_fill(&self, segments: &[Segment]) -> bool { match self.strategy { List => true, Tree | Frag => false, diff --git a/iOverlay/src/mesh/subject.rs b/iOverlay/src/mesh/subject.rs index 3189623..099069a 100644 --- a/iOverlay/src/mesh/subject.rs +++ b/iOverlay/src/mesh/subject.rs @@ -10,11 +10,13 @@ impl Segment { Self { x_segment: XSegment { a: p0, b: p1 }, count: ShapeCountBoolean { subj: 1, clip: 0 }, + data: (), } } else { Self { x_segment: XSegment { a: p1, b: p0 }, count: ShapeCountBoolean { subj: -1, clip: 0 }, + data: (), } } } diff --git a/iOverlay/src/segm/boolean.rs b/iOverlay/src/segm/boolean.rs index 1779389..57487d8 100644 --- a/iOverlay/src/segm/boolean.rs +++ b/iOverlay/src/segm/boolean.rs @@ -41,12 +41,6 @@ impl WindingCount for ShapeCountBoolean { Self { subj, clip } } - #[inline(always)] - fn apply(&mut self, count: Self) { - self.subj += count.subj; - self.clip += count.clip; - } - #[inline(always)] fn invert(self) -> Self { Self { diff --git a/iOverlay/src/segm/build.rs b/iOverlay/src/segm/build.rs index 57f3cbd..4b08cb5 100644 --- a/iOverlay/src/segm/build.rs +++ b/iOverlay/src/segm/build.rs @@ -1,3 +1,4 @@ +use crate::core::edge_data::OverlayEdgeData; use crate::core::overlay::ShapeType; use crate::geom::x_segment::XSegment; use crate::segm::segment::Segment; @@ -126,11 +127,32 @@ impl Segment { Self { x_segment: XSegment { a: p0, b: p1 }, count: direct, + data: (), } } else { Self { x_segment: XSegment { a: p1, b: p0 }, count: invert, + data: (), + } + } + } +} + +impl> Segment { + #[inline] + pub(crate) fn with_ab_and_data(p0: IntPoint, p1: IntPoint, direct: C, invert: C, data: D) -> Self { + if p0 < p1 { + Self { + x_segment: XSegment { a: p0, b: p1 }, + count: direct, + data, + } + } else { + Self { + x_segment: XSegment { a: p1, b: p0 }, + count: invert, + data: data.reversed(), } } } diff --git a/iOverlay/src/segm/merge.rs b/iOverlay/src/segm/merge.rs index d015d9a..fba3d02 100644 --- a/iOverlay/src/segm/merge.rs +++ b/iOverlay/src/segm/merge.rs @@ -1,3 +1,4 @@ +use crate::core::edge_data::{EdgeDataMerge, OverlayEdgeData}; use crate::segm::segment::Segment; use crate::segm::winding::WindingCount; use alloc::vec::Vec; @@ -6,7 +7,7 @@ pub(crate) trait ShapeSegmentsMerge { fn merge_if_needed(&mut self) -> bool; } -impl ShapeSegmentsMerge for Vec> { +impl> ShapeSegmentsMerge for Vec> { fn merge_if_needed(&mut self) -> bool { if self.len() < 2 { return false; @@ -27,14 +28,24 @@ impl ShapeSegmentsMerge for Vec> { } } -fn merge(segments: &mut [Segment], after: usize) -> usize { +fn merge>(segments: &mut [Segment], after: usize) -> usize { let mut i = after; let mut j = i - 1; let mut prev = segments[j]; while i < segments.len() { if prev.x_segment.eq(&segments[i].x_segment) { - prev.count.apply(segments[i].count); + let lhs_count = prev.count; + let rhs_count = segments[i].count; + let out_count = lhs_count.add(rhs_count); + prev.data = D::merge(EdgeDataMerge { + lhs_data: prev.data, + lhs_count, + rhs_data: segments[i].data, + rhs_count, + out_count, + }); + prev.count = out_count; } else { if prev.count.is_not_empty() { segments[j] = prev; diff --git a/iOverlay/src/segm/segment.rs b/iOverlay/src/segm/segment.rs index b20ad13..e55113b 100644 --- a/iOverlay/src/segm/segment.rs +++ b/iOverlay/src/segm/segment.rs @@ -1,3 +1,4 @@ +use crate::core::edge_data::OverlayEdgeData; use crate::geom::x_segment::XSegment; use crate::segm::winding::WindingCount; use core::cmp::Ordering; @@ -20,45 +21,56 @@ pub const BOTH_BOTTOM: SegmentFill = SUBJ_BOTTOM | CLIP_BOTTOM; pub const ALL: SegmentFill = SUBJ_BOTH | CLIP_BOTH; #[derive(Debug, Clone, Copy)] -pub(crate) struct Segment { +pub(crate) struct Segment { pub(crate) x_segment: XSegment, pub(crate) count: C, + pub(crate) data: D, } impl Segment { #[inline(always)] + #[allow(dead_code)] pub(crate) fn create_and_validate(a: IntPoint, b: IntPoint, count: C) -> Self { + Self::create_and_validate_with_data(a, b, count, ()) + } +} + +impl> Segment { + #[inline(always)] + pub(crate) fn create_and_validate_with_data(a: IntPoint, b: IntPoint, count: C, data: D) -> Self { if a < b { Self { x_segment: XSegment { a, b }, count, + data, } } else { Self { x_segment: XSegment { a: b, b: a }, count: count.invert(), + data: data.reversed(), } } } } -impl PartialEq for Segment { +impl PartialEq for Segment { #[inline(always)] fn eq(&self, other: &Self) -> bool { self.x_segment == other.x_segment } } -impl Eq for Segment {} +impl Eq for Segment {} -impl PartialOrd for Segment { +impl PartialOrd for Segment { #[inline(always)] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for Segment { +impl Ord for Segment { #[inline(always)] fn cmp(&self, other: &Self) -> Ordering { self.x_segment.cmp(&other.x_segment) diff --git a/iOverlay/src/segm/sort.rs b/iOverlay/src/segm/sort.rs index 263265e..c5bc145 100644 --- a/iOverlay/src/segm/sort.rs +++ b/iOverlay/src/segm/sort.rs @@ -5,7 +5,7 @@ pub(crate) trait ShapeSegmentsSort { fn sort_by_ab(&mut self, parallel: bool); } -impl ShapeSegmentsSort for [Segment] { +impl ShapeSegmentsSort for [Segment] { #[inline] fn sort_by_ab(&mut self, parallel: bool) { self.sort_by_two_keys_then_by( diff --git a/iOverlay/src/segm/string.rs b/iOverlay/src/segm/string.rs index 21bce44..4ce95d4 100644 --- a/iOverlay/src/segm/string.rs +++ b/iOverlay/src/segm/string.rs @@ -54,12 +54,6 @@ impl WindingCount for ShapeCountString { Self { subj, clip } } - #[inline(always)] - fn apply(&mut self, count: Self) { - self.subj += count.subj; - self.clip |= count.clip; - } - #[inline(always)] fn invert(self) -> Self { let b0 = self.clip & 0b01; diff --git a/iOverlay/src/segm/winding.rs b/iOverlay/src/segm/winding.rs index 81cc7b3..9f800a5 100644 --- a/iOverlay/src/segm/winding.rs +++ b/iOverlay/src/segm/winding.rs @@ -8,6 +8,5 @@ where fn new(subj: i32, clip: i32) -> Self; fn with_shape_type(shape_type: ShapeType) -> (Self, Self); fn add(self, count: Self) -> Self; - fn apply(&mut self, count: Self); fn invert(self) -> Self; } diff --git a/iOverlay/src/split/solver.rs b/iOverlay/src/split/solver.rs index 60ccc6f..95b461f 100644 --- a/iOverlay/src/split/solver.rs +++ b/iOverlay/src/split/solver.rs @@ -1,3 +1,4 @@ +use crate::core::edge_data::{EdgeDataSplit, OverlayEdgeData}; use crate::core::solver::Solver; use crate::geom::x_segment::XSegment; use crate::segm::merge::ShapeSegmentsMerge; @@ -20,9 +21,9 @@ impl SplitSolver { } #[inline] - pub(crate) fn split_segments( + pub(crate) fn split_segments>( &mut self, - segments: &mut Vec>, + segments: &mut Vec>, solver: &Solver, ) -> bool { if segments.is_empty() { @@ -37,7 +38,11 @@ impl SplitSolver { } #[inline] - fn split(&mut self, segments: &mut Vec>, solver: &Solver) -> bool { + fn split>( + &mut self, + segments: &mut Vec>, + solver: &Solver, + ) -> bool { let is_list = solver.is_list_split(segments); let snap_radius = solver.snap_radius(); if is_list { @@ -129,9 +134,9 @@ impl SplitSolver { cross.is_round } - pub(super) fn apply( + pub(super) fn apply>( &mut self, - segments: &mut Vec>, + segments: &mut Vec>, reusable_buffer: &mut Vec, solver: &Solver, ) { @@ -161,12 +166,18 @@ impl SplitSolver { }; let count = s0.count; + let data = s0.data; let x_seg = s0.x_segment; if start + 1 == i { // single split - *s0 = Segment::create_and_validate(x_seg.a, m0.point, count); - let s1 = Segment::create_and_validate(m0.point, x_seg.b, count); + let (d0, d1) = data.split(EdgeDataSplit { + a: x_seg.a, + p: m0.point, + b: x_seg.b, + }); + *s0 = Segment::create_and_validate_with_data(x_seg.a, m0.point, count, d0); + let s1 = Segment::create_and_validate_with_data(m0.point, x_seg.b, count, d1); segments.push(s1); continue; @@ -177,16 +188,29 @@ impl SplitSolver { Self::sort_sub_marks(sub_marks, x_seg); let m0 = sub_marks[0]; - *s0 = Segment::create_and_validate(x_seg.a, m0.point, count); + let (d0, mut rest_data) = data.split(EdgeDataSplit { + a: x_seg.a, + p: m0.point, + b: x_seg.b, + }); + *s0 = Segment::create_and_validate_with_data(x_seg.a, m0.point, count, d0); let mut p0 = m0.point; for mi in sub_marks.iter().skip(1) { - segments.push(Segment::create_and_validate(p0, mi.point, count)); + let (di, next_data) = rest_data.split(EdgeDataSplit { + a: p0, + p: mi.point, + b: x_seg.b, + }); + segments.push(Segment::create_and_validate_with_data(p0, mi.point, count, di)); + rest_data = next_data; p0 = mi.point; } - segments.push(Segment::create_and_validate(p0, x_seg.b, count)); + segments.push(Segment::create_and_validate_with_data( + p0, x_seg.b, count, rest_data, + )); } segments.sort_by_ab(solver.is_parallel_sort_allowed()); diff --git a/iOverlay/src/split/solver_fragment.rs b/iOverlay/src/split/solver_fragment.rs index 01ac08a..11b09f2 100644 --- a/iOverlay/src/split/solver_fragment.rs +++ b/iOverlay/src/split/solver_fragment.rs @@ -1,3 +1,4 @@ +use crate::core::edge_data::OverlayEdgeData; use crate::core::solver::Solver; use crate::segm::segment::Segment; use crate::segm::winding::WindingCount; @@ -10,10 +11,10 @@ use crate::split::solver::SplitSolver; use alloc::vec::Vec; impl SplitSolver { - pub(super) fn fragment_split( + pub(super) fn fragment_split>( &mut self, snap_radius: SnapRadius, - segments: &mut Vec>, + segments: &mut Vec>, solver: &Solver, ) -> bool { let layout = diff --git a/iOverlay/src/split/solver_list.rs b/iOverlay/src/split/solver_list.rs index 039bfad..e9cfa93 100644 --- a/iOverlay/src/split/solver_list.rs +++ b/iOverlay/src/split/solver_list.rs @@ -1,3 +1,4 @@ +use crate::core::edge_data::OverlayEdgeData; use crate::core::solver::Solver; use crate::segm::segment::Segment; use crate::segm::winding::WindingCount; @@ -6,10 +7,10 @@ use crate::split::solver::SplitSolver; use alloc::vec::Vec; impl SplitSolver { - pub(super) fn list_split( + pub(super) fn list_split>( &mut self, snap_radius: SnapRadius, - segments: &mut Vec>, + segments: &mut Vec>, solver: &Solver, ) -> bool { let mut need_to_fix = true; diff --git a/iOverlay/src/split/solver_tree.rs b/iOverlay/src/split/solver_tree.rs index 83f9509..35f7559 100644 --- a/iOverlay/src/split/solver_tree.rs +++ b/iOverlay/src/split/solver_tree.rs @@ -1,3 +1,4 @@ +use crate::core::edge_data::OverlayEdgeData; use crate::core::solver::Solver; use crate::geom::line_range::LineRange; use crate::geom::x_segment::XSegment; @@ -24,10 +25,10 @@ impl ExpiredVal for IdSegment { } impl SplitSolver { - pub(super) fn tree_split( + pub(super) fn tree_split>( &mut self, snap_radius: SnapRadius, - segments: &mut Vec>, + segments: &mut Vec>, solver: &Solver, ) -> bool { let range: SegRange = segments.ver_range().into(); @@ -99,7 +100,7 @@ trait VerticalRange { fn ver_range(&self) -> LineRange; } -impl VerticalRange for Vec> { +impl VerticalRange for Vec> { fn ver_range(&self) -> LineRange { let mut min_y = self[0].x_segment.a.y; let mut max_y = min_y; @@ -118,7 +119,7 @@ impl VerticalRange for Vec> { } } -impl Segment { +impl Segment { #[inline] fn id_segment(&self, id: usize) -> IdSegment { IdSegment { diff --git a/iOverlay/src/string/overlay.rs b/iOverlay/src/string/overlay.rs index 631da41..c5b2a95 100644 --- a/iOverlay/src/string/overlay.rs +++ b/iOverlay/src/string/overlay.rs @@ -135,6 +135,7 @@ impl StringOverlay { subj: 0, clip: STRING_BACK_CLIP, }, + data: (), }, Ordering::Greater => Segment { x_segment: XSegment { a: b, b: a }, @@ -142,6 +143,7 @@ impl StringOverlay { subj: 0, clip: STRING_FORWARD_CLIP, }, + data: (), }, Ordering::Equal => return, }; diff --git a/iOverlay/src/vector/edge.rs b/iOverlay/src/vector/edge.rs index a4eae6a..4976e50 100644 --- a/iOverlay/src/vector/edge.rs +++ b/iOverlay/src/vector/edge.rs @@ -1,3 +1,4 @@ +use crate::core::edge_data::OverlayEdgeData; use alloc::vec::Vec; use i_float::int::point::IntPoint; use i_shape::int::path::IntPath; @@ -5,6 +6,8 @@ use i_shape::int::path::IntPath; pub type SideFill = u8; pub type VectorPath = Vec; pub type VectorShape = Vec; +pub type DataVectorPath = Vec>; +pub type DataVectorShape = Vec>; pub const SUBJ_LEFT: u8 = 0b0001; pub const SUBJ_RIGHT: u8 = 0b0010; @@ -33,6 +36,14 @@ pub struct VectorEdge { pub fill: SideFill, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DataVectorEdge { + pub a: IntPoint, + pub b: IntPoint, + pub fill: SideFill, + pub data: D, +} + impl VectorEdge { pub(crate) fn new(fill: SideFill, a: IntPoint, b: IntPoint) -> Self { let fill = if a < b { fill } else { fill.reverse() }; @@ -41,6 +52,18 @@ impl VectorEdge { } } +impl DataVectorEdge { + pub(crate) fn new(fill: SideFill, a: IntPoint, b: IntPoint, data: D) -> Self { + let (fill, data) = if a < b { + (fill, data) + } else { + (fill.reverse(), data.reversed()) + }; + + Self { a, b, fill, data } + } +} + pub trait ToPath { fn to_path(&self) -> IntPath; } diff --git a/iOverlay/src/vector/extract.rs b/iOverlay/src/vector/extract.rs index 49454b7..e5c9fe1 100644 --- a/iOverlay/src/vector/extract.rs +++ b/iOverlay/src/vector/extract.rs @@ -1,5 +1,6 @@ use crate::bind::segment::{ContourIndex, IdSegment, IdSegments}; use crate::bind::solver::{ShapeBinder, SortByAngle}; +use crate::core::edge_data::OverlayEdgeData; use crate::core::extract::{BooleanExtractionBuffer, GraphContour, GraphUtil, Visit, VisitState}; use crate::core::graph::OverlayGraph; use crate::core::link::{OverlayLink, OverlayLinkFilter}; @@ -7,7 +8,7 @@ use crate::core::overlay::ContourDirection; use crate::core::overlay_rule::OverlayRule; use crate::geom::v_segment::VSegment; use crate::segm::segment::SegmentFill; -use crate::vector::edge::{VectorEdge, VectorPath, VectorShape}; +use crate::vector::edge::{DataVectorEdge, VectorEdge, VectorPath, VectorShape}; use crate::vector::simplify::VectorSimplify; use alloc::vec; use alloc::vec::Vec; @@ -147,6 +148,15 @@ impl OverlayGraph<'_> { } } +impl OverlayGraph<'_, D> { + pub fn extract_separate_data_vectors(&self) -> Vec> { + self.links + .iter() + .map(|link| DataVectorEdge::new(link.fill, link.a.point, link.b.point, link.data)) + .collect() + } +} + struct StartVectorPathData { a: IntPoint, b: IntPoint, diff --git a/iOverlay/tests/edge_overlay_tests.rs b/iOverlay/tests/edge_overlay_tests.rs new file mode 100644 index 0000000..6194529 --- /dev/null +++ b/iOverlay/tests/edge_overlay_tests.rs @@ -0,0 +1,89 @@ +use i_float::int::point::IntPoint; +use i_overlay::core::edge_data::{EdgeDataMerge, OverlayEdgeData}; +use i_overlay::core::edge_overlay::{EdgeOverlay, InputEdge}; +use i_overlay::core::fill_rule::FillRule; +use i_overlay::core::overlay::ShapeType; +use i_overlay::core::overlay_rule::OverlayRule; +use i_overlay::segm::boolean::ShapeCountBoolean; +use i_overlay::vector::edge::DataVectorEdge; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Color { + Red, + Green, + Undefined, +} + +impl OverlayEdgeData for Color { + fn merge(ctx: EdgeDataMerge) -> Self { + match (ctx.lhs_data, ctx.rhs_data) { + (Color::Red, Color::Red) => Color::Red, + (Color::Green, Color::Green) => Color::Green, + _ => Color::Undefined, + } + } +} + +#[test] +fn union_keeps_source_edge_data_and_marks_shared_runs_undefined() { + let edges = overlay_edges(OverlayRule::Union); + assert_eq!(edges.len(), 8); + assert_counts(&edges, 3, 3, 2); +} + +#[test] +fn intersect_uses_cut_edges_and_marks_shared_runs_undefined() { + let edges = overlay_edges(OverlayRule::Intersect); + assert_eq!(edges.len(), 4); + assert_counts(&edges, 1, 1, 2); +} + +#[test] +fn difference_keeps_subject_edges_and_uses_clip_cut_edge() { + let edges = overlay_edges(OverlayRule::Difference); + assert_eq!(edges.len(), 4); + assert_counts(&edges, 3, 1, 0); +} + +#[test] +fn inverse_difference_keeps_clip_edges_and_uses_subject_cut_edge() { + let edges = overlay_edges(OverlayRule::InverseDifference); + assert_eq!(edges.len(), 4); + assert_counts(&edges, 1, 3, 0); +} + +fn overlay_edges(rule: OverlayRule) -> Vec> { + let subj = square(0, 0, 4, 4, Color::Red); + let clip = square(2, 0, 6, 4, Color::Green); + + let mut overlay = EdgeOverlay::new(subj.len() + clip.len()); + overlay.add_edges(subj, ShapeType::Subject); + overlay.add_edges(clip, ShapeType::Clip); + overlay.build_separate_vectors(rule, FillRule::NonZero) +} + +fn square(x0: i32, y0: i32, x1: i32, y1: i32, data: Color) -> Vec> { + let points = [ + IntPoint::new(x0, y0), + IntPoint::new(x1, y0), + IntPoint::new(x1, y1), + IntPoint::new(x0, y1), + ]; + + points + .iter() + .copied() + .zip(points.iter().copied().cycle().skip(1)) + .take(points.len()) + .map(|(a, b)| InputEdge { a, b, data }) + .collect() +} + +fn assert_counts(edges: &[DataVectorEdge], red: usize, green: usize, undefined: usize) { + assert_eq!(edges.iter().filter(|e| e.data == Color::Red).count(), red); + assert_eq!(edges.iter().filter(|e| e.data == Color::Green).count(), green); + assert_eq!( + edges.iter().filter(|e| e.data == Color::Undefined).count(), + undefined + ); +} diff --git a/performance/rust_app/Cargo.toml b/performance/rust_app/Cargo.toml index b0ca408..c9b82da 100644 --- a/performance/rust_app/Cargo.toml +++ b/performance/rust_app/Cargo.toml @@ -11,5 +11,5 @@ codegen-units = 1 [dependencies] #i_overlay = { path = "../../iOverlay", default-features = true } -i_overlay = { path = "../../iOverlay", features = ["allow_multithreading"] } -#i_overlay = "~3.4.0" \ No newline at end of file +#i_overlay = { path = "../../iOverlay", features = ["allow_multithreading"] } +i_overlay = "6.0.0" \ No newline at end of file diff --git a/performance/rust_app/src/main.rs b/performance/rust_app/src/main.rs index 9b8aba5..58c2721 100644 --- a/performance/rust_app/src/main.rs +++ b/performance/rust_app/src/main.rs @@ -181,7 +181,7 @@ fn run_test_4(geom: bool, solver: Solver) { fn run_test_5(geom: bool, solver: Solver) { println!("run NestedSquares test"); - for i in 1..19 { + for i in 1..18 { let n = 1 << i; CrossTest::run(n, OverlayRule::Xor, solver, 500.0, geom); } diff --git a/performance/rust_app/src/test/test_0_checkerboard.rs b/performance/rust_app/src/test/test_0_checkerboard.rs index 10b71b6..7e1e30a 100644 --- a/performance/rust_app/src/test/test_0_checkerboard.rs +++ b/performance/rust_app/src/test/test_0_checkerboard.rs @@ -26,7 +26,6 @@ multithreading on 1024(2095105 6.3) - 7.520601(0.9) 2048(8384513 6.9) - 30.258030(1.5) - multithreading off 2(5 0.7) - 0.000006(-5.2) From 8d01f8f882157edf2cd8677f2edff55ae0b4df27 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sun, 3 May 2026 21:12:08 +0300 Subject: [PATCH 02/36] add vector shape extraction --- iOverlay/README.md | 8 +- iOverlay/src/bind/segment.rs | 10 +- iOverlay/src/core/edge_data.rs | 2 +- iOverlay/src/core/edge_overlay.rs | 37 +++- iOverlay/src/core/extract.rs | 24 +-- iOverlay/src/core/overlay.rs | 6 +- iOverlay/src/vector/edge.rs | 21 +- iOverlay/src/vector/extract.rs | 101 +++++---- iOverlay/src/vector/simplify.rs | 164 +++++++++----- iOverlay/tests/edge_overlay_tests.rs | 309 ++++++++++++++++++++------- iOverlay/tests/vector_tests.rs | 32 ++- 11 files changed, 489 insertions(+), 225 deletions(-) diff --git a/iOverlay/README.md b/iOverlay/README.md index 6fcdab2..0f547c6 100644 --- a/iOverlay/README.md +++ b/iOverlay/README.md @@ -247,13 +247,15 @@ for edge in green_square.windows(2).map(|w| (w[0], w[1])) }, ShapeType::Clip); } -let edges = overlay.build_separate_vectors(OverlayRule::Union, FillRule::NonZero); +let shapes = overlay.build_vector_shapes(OverlayRule::Union, FillRule::NonZero); +// The result is grouped as shapes -> contours -> edges. // Each output edge contains geometry, fill, and the propagated user data. -assert!(edges.iter().any(|edge| edge.data == EdgeKind::Undefined)); +assert_eq!(shapes.len(), 1); +assert!(shapes[0][0].iter().any(|edge| edge.data == EdgeKind::Undefined)); ``` -This API currently targets integer boolean operations and exports the result as edges. It intentionally preserves edge runs so downstream crates can decide how to simplify or resolve conflicting attributes. +This API currently targets integer boolean operations and exports the result as vector shapes. It keeps the same contour structure as the regular polygon API, but each contour item is an edge with propagated data. Collinear edges are simplified only when their data is equal, so attribute boundaries are preserved.   ## Spatial Predicates diff --git a/iOverlay/src/bind/segment.rs b/iOverlay/src/bind/segment.rs index b1a95de..331adaf 100644 --- a/iOverlay/src/bind/segment.rs +++ b/iOverlay/src/bind/segment.rs @@ -1,5 +1,5 @@ use crate::geom::v_segment::VSegment; -use crate::vector::edge::{VectorEdge, VectorPath}; +use crate::vector::edge::{DataVectorEdge, DataVectorPath}; use alloc::vec::Vec; use i_float::int::point::IntPoint; use i_shape::int::path::IntPath; @@ -109,7 +109,7 @@ impl IdSegments for IntPath { } } -impl IdSegments for VectorPath { +impl IdSegments for DataVectorPath { #[inline] fn append_id_segments( &self, @@ -119,7 +119,7 @@ impl IdSegments for VectorPath { x_max: i32, clockwise: bool, ) { - fn inner>( + fn inner<'a, D: 'a, I: Iterator>>( iter: I, buffer: &mut Vec, id_data: ContourIndex, @@ -134,9 +134,9 @@ impl IdSegments for VectorPath { } if clockwise { - inner(self.iter().copied(), buffer, id_data, x_min, x_max); + inner(self.iter(), buffer, id_data, x_min, x_max); } else { - inner(self.iter().rev().copied(), buffer, id_data, x_min, x_max); + inner(self.iter().rev(), buffer, id_data, x_min, x_max); } } } diff --git a/iOverlay/src/core/edge_data.rs b/iOverlay/src/core/edge_data.rs index 1285e71..050b2bb 100644 --- a/iOverlay/src/core/edge_data.rs +++ b/iOverlay/src/core/edge_data.rs @@ -1,7 +1,7 @@ use crate::segm::boolean::ShapeCountBoolean; use i_float::int::point::IntPoint; -pub trait OverlayEdgeData: Copy + Send + Sync { +pub trait OverlayEdgeData: Copy + PartialEq + Send + Sync { #[inline(always)] fn reversed(self) -> Self { self diff --git a/iOverlay/src/core/edge_overlay.rs b/iOverlay/src/core/edge_overlay.rs index 4df7958..2ffe594 100644 --- a/iOverlay/src/core/edge_overlay.rs +++ b/iOverlay/src/core/edge_overlay.rs @@ -1,5 +1,6 @@ use crate::build::builder::GraphBuilder; 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::overlay::{IntOverlayOptions, ShapeType}; @@ -9,7 +10,7 @@ use crate::segm::boolean::ShapeCountBoolean; use crate::segm::segment::Segment; use crate::segm::winding::WindingCount; use crate::split::solver::SplitSolver; -use crate::vector::edge::DataVectorEdge; +use crate::vector::edge::{DataVectorEdge, DataVectorShape}; use alloc::vec::Vec; use i_float::int::point::IntPoint; @@ -23,6 +24,7 @@ pub struct InputEdge { pub struct EdgeOverlay { pub solver: Solver, pub options: IntOverlayOptions, + pub boolean_buffer: Option, segments: Vec>, split_solver: SplitSolver, graph_builder: GraphBuilder, @@ -33,6 +35,7 @@ impl EdgeOverlay { Self { solver: Default::default(), options: IntOverlayOptions::keep_output_points(), + boolean_buffer: None, segments: Vec::with_capacity(capacity), split_solver: SplitSolver::new(), graph_builder: GraphBuilder::::new(), @@ -59,7 +62,7 @@ impl EdgeOverlay { } } - pub fn build_separate_vectors( + pub fn build_vectors( &mut self, overlay_rule: OverlayRule, fill_rule: FillRule, @@ -77,6 +80,34 @@ impl EdgeOverlay { &self.solver, &self.segments, ) - .extract_separate_data_vectors() + .extract_vectors() + } + + pub fn build_vector_shapes( + &mut self, + overlay_rule: OverlayRule, + fill_rule: FillRule, + ) -> Vec> { + self.split_solver.split_segments(&mut self.segments, &self.solver); + if self.segments.is_empty() { + return Vec::new(); + } + + let mut buffer = self.boolean_buffer.take().unwrap_or_default(); + + let shapes = self + .graph_builder + .build_boolean_overlay( + fill_rule, + overlay_rule, + self.options, + &self.solver, + &self.segments, + ) + .extract_vector_shapes(overlay_rule, &mut buffer); + + self.boolean_buffer = Some(buffer); + + shapes } } diff --git a/iOverlay/src/core/extract.rs b/iOverlay/src/core/extract.rs index 8db879e..7a1bae4 100644 --- a/iOverlay/src/core/extract.rs +++ b/iOverlay/src/core/extract.rs @@ -295,7 +295,7 @@ impl StartPathData { pub(crate) trait GraphContour { fn validate(&mut self, min_output_area: u64, preserve_output_collinear: bool) -> (bool, bool); - fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize; + fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize; } impl GraphContour for IntContour { @@ -322,7 +322,7 @@ impl GraphContour for IntContour { } #[inline] - fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize { + fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize { if link.a.id == node_id { self.push(link.a.point); link.b.id @@ -386,8 +386,8 @@ impl GraphUtil { /// * For bridge nodes, both `bridge[k] < links.len()` /// * `visited` is at least `links.len()` long (or whatever invariant applies) #[inline] - pub(crate) unsafe fn find_left_top_link( - links: &[OverlayLink], + pub(crate) unsafe fn find_left_top_link( + links: &[OverlayLink], nodes: &[OverlayNode], link_index: usize, visited: &[VisitState], @@ -414,9 +414,9 @@ impl GraphUtil { } #[inline(always)] - fn find_left_top_link_on_indices( - links: &[OverlayLink], - link: &OverlayLink, + fn find_left_top_link_on_indices( + links: &[OverlayLink], + link: &OverlayLink, link_index: usize, indices: &[usize], visited: &[VisitState], @@ -450,7 +450,7 @@ impl GraphUtil { } #[inline(always)] - fn find_left_top_link_on_bridge(links: &[OverlayLink], bridge: &[usize; 2]) -> usize { + fn find_left_top_link_on_bridge(links: &[OverlayLink], bridge: &[usize; 2]) -> usize { // SAFETY: every bridge index comes straight from GraphBuilder::build_nodes_and_connect_links, // which only records values in 0..links.len(), so the unchecked lookups stay in-bounds. let (l0, l1) = unsafe { (links.get_unchecked(bridge[0]), links.get_unchecked(bridge[1])) }; @@ -462,8 +462,8 @@ impl GraphUtil { } #[inline(always)] - pub(crate) fn next_link( - links: &[OverlayLink], + pub(crate) fn next_link( + links: &[OverlayLink], nodes: &[OverlayNode], link_id: usize, node_id: usize, @@ -493,8 +493,8 @@ impl GraphUtil { // so every element is a valid index into `links`, and at least one of them is // still unvisited when we enter. The unchecked accesses rely on that invariant. #[inline] - fn find_nearest_link_to( - links: &[OverlayLink], + fn find_nearest_link_to( + links: &[OverlayLink], target_index: usize, node_id: usize, clockwise: bool, diff --git a/iOverlay/src/core/overlay.rs b/iOverlay/src/core/overlay.rs index 4a334bf..c12e075 100644 --- a/iOverlay/src/core/overlay.rs +++ b/iOverlay/src/core/overlay.rs @@ -12,7 +12,7 @@ use crate::segm::boolean::ShapeCountBoolean; use crate::segm::build::BuildSegments; use crate::segm::segment::Segment; use crate::split::solver::SplitSolver; -use crate::vector::edge::{VectorEdge, VectorShape}; +use crate::vector::edge::{DataVectorEdge, VectorShape}; use alloc::vec::Vec; use i_float::int::point::IntPoint; use i_shape::int::count::PointsCount; @@ -273,7 +273,7 @@ impl Overlay { &self.solver, &self.segments, ) - .extract_shape_vectors(overlay_rule, &mut buffer); + .extract_vector_shapes(overlay_rule, &mut buffer); self.boolean_buffer = Some(buffer); @@ -282,7 +282,7 @@ impl Overlay { /// Convert into vectors from the added paths or shapes, applying the specified build rule. This method is particularly useful for development purposes and for creating visualizations in educational demos, where understanding the impact of different rules on the final geometry is crucial. /// - `fill_rule`: The build rule to use for the shapes. - pub fn build_separate_vectors(&mut self, fill_rule: FillRule) -> Vec { + pub fn build_separate_vectors(&mut self, fill_rule: FillRule) -> Vec { self.split_solver.split_segments(&mut self.segments, &self.solver); if self.segments.is_empty() { return Vec::new(); diff --git a/iOverlay/src/vector/edge.rs b/iOverlay/src/vector/edge.rs index 4976e50..22e46d5 100644 --- a/iOverlay/src/vector/edge.rs +++ b/iOverlay/src/vector/edge.rs @@ -4,10 +4,10 @@ use i_float::int::point::IntPoint; use i_shape::int::path::IntPath; pub type SideFill = u8; -pub type VectorPath = Vec; -pub type VectorShape = Vec; pub type DataVectorPath = Vec>; pub type DataVectorShape = Vec>; +pub type VectorPath = DataVectorPath<()>; +pub type VectorShape = DataVectorShape<()>; pub const SUBJ_LEFT: u8 = 0b0001; pub const SUBJ_RIGHT: u8 = 0b0010; @@ -30,28 +30,13 @@ impl Reverse for SideFill { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct VectorEdge { - pub a: IntPoint, - pub b: IntPoint, - pub fill: SideFill, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct DataVectorEdge { +pub struct DataVectorEdge { pub a: IntPoint, pub b: IntPoint, pub fill: SideFill, pub data: D, } -impl VectorEdge { - pub(crate) fn new(fill: SideFill, a: IntPoint, b: IntPoint) -> Self { - let fill = if a < b { fill } else { fill.reverse() }; - - Self { a, b, fill } - } -} - impl DataVectorEdge { pub(crate) fn new(fill: SideFill, a: IntPoint, b: IntPoint, data: D) -> Self { let (fill, data) = if a < b { diff --git a/iOverlay/src/vector/extract.rs b/iOverlay/src/vector/extract.rs index e5c9fe1..641a82a 100644 --- a/iOverlay/src/vector/extract.rs +++ b/iOverlay/src/vector/extract.rs @@ -1,36 +1,32 @@ use crate::bind::segment::{ContourIndex, IdSegment, IdSegments}; use crate::bind::solver::{ShapeBinder, SortByAngle}; use crate::core::edge_data::OverlayEdgeData; -use crate::core::extract::{BooleanExtractionBuffer, GraphContour, GraphUtil, Visit, VisitState}; +use crate::core::extract::{BooleanExtractionBuffer, GraphUtil, Visit, VisitState}; use crate::core::graph::OverlayGraph; use crate::core::link::{OverlayLink, OverlayLinkFilter}; use crate::core::overlay::ContourDirection; use crate::core::overlay_rule::OverlayRule; use crate::geom::v_segment::VSegment; use crate::segm::segment::SegmentFill; -use crate::vector::edge::{DataVectorEdge, VectorEdge, VectorPath, VectorShape}; +use crate::vector::edge::{DataVectorEdge, DataVectorPath, DataVectorShape}; use crate::vector::simplify::VectorSimplify; use alloc::vec; use alloc::vec::Vec; use i_float::int::point::IntPoint; -impl OverlayGraph<'_> { - pub fn extract_separate_vectors(&self) -> Vec { +impl OverlayGraph<'_, D> { + pub fn extract_separate_vectors(&self) -> Vec { self.links .iter() - .map(|link| VectorEdge { - a: link.a.point, - b: link.b.point, - fill: link.fill, - }) + .map(|link| DataVectorEdge::new(link.fill, link.a.point, link.b.point, ())) .collect() } - pub fn extract_shape_vectors( + pub fn extract_vector_shapes( &self, overlay_rule: OverlayRule, buffer: &mut BooleanExtractionBuffer, - ) -> Vec { + ) -> Vec> { let clockwise = self.options.output_direction == ContourDirection::Clockwise; self.links .filter_by_overlay_into(overlay_rule, &mut buffer.visited); @@ -116,19 +112,24 @@ impl OverlayGraph<'_> { fn find_vector_contour( &self, - start_data: StartVectorPathData, + start_data: StartVectorPathData, clockwise: bool, visited_state: VisitState, visited: &mut [VisitState], - ) -> VectorPath { + ) -> DataVectorPath { let mut link_id = start_data.link_id; let mut node_id = start_data.node_id; let last_node_id = start_data.last_node_id; visited.visit_edge(link_id, visited_state); - let mut contour = VectorPath::new(); - contour.push(VectorEdge::new(start_data.fill, start_data.a, start_data.b)); + let mut contour = DataVectorPath::new(); + contour.push(DataVectorEdge::new( + start_data.fill, + start_data.a, + start_data.b, + start_data.data, + )); // Find a closed tour while node_id != last_node_id { @@ -149,7 +150,7 @@ impl OverlayGraph<'_> { } impl OverlayGraph<'_, D> { - pub fn extract_separate_data_vectors(&self) -> Vec> { + pub fn extract_vectors(&self) -> Vec> { self.links .iter() .map(|link| DataVectorEdge::new(link.fill, link.a.point, link.b.point, link.data)) @@ -157,18 +158,19 @@ impl OverlayGraph<'_, D> { } } -struct StartVectorPathData { +struct StartVectorPathData { a: IntPoint, b: IntPoint, node_id: usize, link_id: usize, last_node_id: usize, fill: SegmentFill, + data: D, } -impl StartVectorPathData { +impl StartVectorPathData { #[inline(always)] - fn new(direction: bool, link: &OverlayLink, link_id: usize) -> Self { + fn new(direction: bool, link: &OverlayLink, link_id: usize) -> Self { if direction { Self { a: link.b.point, @@ -177,6 +179,7 @@ impl StartVectorPathData { link_id, last_node_id: link.b.id, fill: link.fill, + data: link.data, } } else { Self { @@ -186,18 +189,19 @@ impl StartVectorPathData { link_id, last_node_id: link.a.id, fill: link.fill, + data: link.data, } } } } -trait JoinHoles { - fn join_sorted_holes(&mut self, holes: Vec, anchors: Vec, clockwise: bool); - fn scan_join(&mut self, holes: Vec, hole_segments: Vec, clockwise: bool); +trait JoinHoles { + fn join_sorted_holes(&mut self, holes: Vec>, anchors: Vec, clockwise: bool); + fn scan_join(&mut self, holes: Vec>, hole_segments: Vec, clockwise: bool); } -impl JoinHoles for Vec { - fn join_sorted_holes(&mut self, holes: Vec, anchors: Vec, clockwise: bool) { +impl JoinHoles for Vec> { + fn join_sorted_holes(&mut self, holes: Vec>, anchors: Vec, clockwise: bool) { if self.is_empty() || holes.is_empty() { return; } @@ -214,7 +218,7 @@ impl JoinHoles for Vec { self.scan_join(holes, anchors, clockwise); } - fn scan_join(&mut self, holes: Vec, hole_segments: Vec, clockwise: bool) { + fn scan_join(&mut self, holes: Vec>, hole_segments: Vec, clockwise: bool) { let x_min = hole_segments[0].v_segment.a.x; let x_max = hole_segments[hole_segments.len() - 1].v_segment.a.x; @@ -244,10 +248,10 @@ impl JoinHoles for Vec { } #[inline] -fn most_left_bottom(path: &VectorPath) -> VSegment { +fn most_left_bottom(path: &DataVectorPath) -> VSegment { let mut index = 0; let mut a = path[0].a; - for (i, &e) in path.iter().enumerate().skip(1) { + for (i, e) in path.iter().enumerate().skip(1) { if e.a < a { a = e.a; index = i; @@ -270,7 +274,12 @@ fn is_sorted(segments: &[IdSegment]) -> bool { .all(|slice| slice[0].v_segment.a <= slice[1].v_segment.a) } -impl GraphContour for VectorPath { +trait DataGraphContour { + fn validate(&mut self, min_output_area: u64, preserve_output_collinear: bool) -> (bool, bool); + fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize; +} + +impl DataGraphContour for DataVectorPath { #[inline] fn validate(&mut self, min_output_area: u64, preserve_output_collinear: bool) -> (bool, bool) { let is_modified = if !preserve_output_collinear { @@ -291,18 +300,26 @@ impl GraphContour for VectorPath { .iter() .fold(0i64, |acc, edge| acc + edge.a.cross_product(edge.b)); - let is_valid = (double_area.unsigned_abs() >> 1) >= min_output_area; - - (is_valid, is_modified) + ((double_area.unsigned_abs() >> 1) >= min_output_area, is_modified) } #[inline] - fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize { + fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize { if link.a.id == node_id { - self.push(VectorEdge::new(link.fill, link.a.point, link.b.point)); + self.push(DataVectorEdge::new( + link.fill, + link.a.point, + link.b.point, + link.data, + )); link.b.id } else { - self.push(VectorEdge::new(link.fill, link.b.point, link.a.point)); + self.push(DataVectorEdge::new( + link.fill, + link.b.point, + link.a.point, + link.data, + )); link.a.id } } @@ -329,7 +346,7 @@ mod tests { let shapes = overlay .build_graph_view(FillRule::NonZero) .unwrap() - .extract_shape_vectors(OverlayRule::Subject, &mut buffer); + .extract_vector_shapes(OverlayRule::Subject, &mut buffer); debug_assert!(shapes[0][0].len() == 6); @@ -338,7 +355,7 @@ mod tests { let shapes = overlay .build_graph_view(FillRule::NonZero) .unwrap() - .extract_shape_vectors(OverlayRule::Subject, &mut buffer); + .extract_vector_shapes(OverlayRule::Subject, &mut buffer); debug_assert!(shapes[0][0].len() == 4); } @@ -356,7 +373,7 @@ mod tests { let shapes = overlay .build_graph_view(FillRule::NonZero) .unwrap() - .extract_shape_vectors(OverlayRule::Subject, &mut buffer); + .extract_vector_shapes(OverlayRule::Subject, &mut buffer); debug_assert!(shapes[0][0].len() == 4); } @@ -375,7 +392,7 @@ mod tests { let shapes_0 = overlay .build_graph_view(FillRule::NonZero) .unwrap() - .extract_shape_vectors(OverlayRule::Subject, &mut buffer); + .extract_vector_shapes(OverlayRule::Subject, &mut buffer); debug_assert!(shapes_0.len() == 1); @@ -384,7 +401,7 @@ mod tests { let shapes_1 = overlay .build_graph_view(FillRule::NonZero) .unwrap() - .extract_shape_vectors(OverlayRule::Subject, &mut buffer); + .extract_vector_shapes(OverlayRule::Subject, &mut buffer); debug_assert!(shapes_1.len() == 1); } @@ -402,7 +419,7 @@ mod tests { let shapes = overlay .build_graph_view(FillRule::NonZero) .unwrap() - .extract_shape_vectors(OverlayRule::Subject, &mut buffer); + .extract_vector_shapes(OverlayRule::Subject, &mut buffer); debug_assert!(shapes.len() == 1); debug_assert!(shapes[0][0].len() == 4); @@ -420,7 +437,7 @@ mod tests { let shapes = overlay .build_graph_view(FillRule::NonZero) .unwrap() - .extract_shape_vectors(OverlayRule::Subject, &mut buffer); + .extract_vector_shapes(OverlayRule::Subject, &mut buffer); debug_assert!(shapes.len() == 2); } @@ -443,7 +460,7 @@ mod tests { let shapes = overlay .build_graph_view(FillRule::NonZero) .unwrap() - .extract_shape_vectors(OverlayRule::Subject, &mut buffer); + .extract_vector_shapes(OverlayRule::Subject, &mut buffer); debug_assert!(shapes.len() == 2); } diff --git a/iOverlay/src/vector/simplify.rs b/iOverlay/src/vector/simplify.rs index 387cdfe..474a6d1 100644 --- a/iOverlay/src/vector/simplify.rs +++ b/iOverlay/src/vector/simplify.rs @@ -1,4 +1,5 @@ -use crate::vector::edge::{VectorEdge, VectorPath, VectorShape}; +use crate::core::edge_data::OverlayEdgeData; +use crate::vector::edge::{DataVectorEdge, DataVectorPath, DataVectorShape}; use alloc::vec; use alloc::vec::Vec; use i_float::int::point::IntPoint; @@ -12,16 +13,18 @@ pub(super) trait VectorSimplify { } pub(super) trait VectorSimpleContour { + type Data: OverlayEdgeData; fn is_simple(&self) -> bool; - fn simplified(&self) -> Option; + fn simplified(&self) -> Option>; } pub(super) trait VectorSimpleShape { + type Data: OverlayEdgeData; fn is_simple(&self) -> bool; - fn simplified(&self) -> Option; + fn simplified(&self) -> Option>; } -impl VectorSimplify for VectorPath { +impl VectorSimplify for DataVectorPath { #[inline] fn simplify_contour(&mut self) -> bool { if self.is_simple() { @@ -36,7 +39,7 @@ impl VectorSimplify for VectorPath { } } -impl VectorSimplify for VectorShape { +impl VectorSimplify for DataVectorShape { #[inline] fn simplify_contour(&mut self) -> bool { let mut any_simplified = false; @@ -67,7 +70,7 @@ impl VectorSimplify for VectorShape { } } -impl VectorSimplify for Vec { +impl VectorSimplify for Vec> { #[inline] fn simplify_contour(&mut self) -> bool { let mut any_simplified = false; @@ -94,7 +97,9 @@ impl VectorSimplify for Vec { } } -impl VectorSimpleContour for [VectorEdge] { +impl VectorSimpleContour for [DataVectorEdge] { + type Data = D; + #[inline] fn is_simple(&self) -> bool { let count = self.len(); @@ -102,19 +107,19 @@ impl VectorSimpleContour for [VectorEdge] { return false; } - let mut prev = direction(&self[count - 1]); + let mut prev = &self[count - 1]; for edge in self.iter() { let curr = direction(edge); - if curr.cross_product(prev) == 0 { + if curr.cross_product(direction(prev)) == 0 && edge.data == prev.data { return false; } - prev = curr; + prev = edge; } true } - fn simplified(&self) -> Option { + fn simplified(&self) -> Option> { if self.len() < 3 { return None; } @@ -155,7 +160,9 @@ impl VectorSimpleContour for [VectorEdge] { let p1 = self[node.index].b; let p2 = self[node.next].b; - if p1.subtract(p0).cross_product(p2.subtract(p1)) == 0 { + if p1.subtract(p0).cross_product(p2.subtract(p1)) == 0 + && self[node.index].data == self[node.next].data + { n -= 1; if n < 3 { return None; @@ -192,7 +199,7 @@ impl VectorSimpleContour for [VectorEdge] { } } - let mut buffer = vec![VectorEdge::new(0, IntPoint::ZERO, IntPoint::ZERO); n]; + let mut buffer = vec![DataVectorEdge::new(0, IntPoint::ZERO, IntPoint::ZERO, self[0].data); n]; node = nodes[first]; let mut e0 = &self[node.index]; @@ -202,6 +209,7 @@ impl VectorSimpleContour for [VectorEdge] { item.a = e0.b; item.b = e1.b; item.fill = e1.fill; + item.data = e1.data; e0 = e1; } @@ -217,13 +225,15 @@ struct Node { prev: usize, } -impl VectorSimpleShape for [VectorPath] { +impl VectorSimpleShape for [DataVectorPath] { + type Data = D; + #[inline] fn is_simple(&self) -> bool { self.iter().all(|contour| contour.is_simple()) } - fn simplified(&self) -> Option { + fn simplified(&self) -> Option> { let mut contours = Vec::with_capacity(self.len()); for (i, contour) in self.iter().enumerate() { if contour.is_simple() { @@ -240,27 +250,47 @@ impl VectorSimpleShape for [VectorPath] { } #[inline] -fn direction(edge: &VectorEdge) -> IntPoint { +fn direction(edge: &DataVectorEdge) -> IntPoint { edge.b - edge.a } #[cfg(test)] mod tests { - use crate::vector::edge::VectorEdge; + use crate::core::edge_data::{EdgeDataMerge, OverlayEdgeData}; + use crate::vector::edge::DataVectorEdge; use crate::vector::simplify::{IntPoint, VectorSimplify}; use alloc::vec; use i_float::int_pnt; + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + enum TestData { + A, + B, + } + + impl OverlayEdgeData for TestData + where + C: Copy + Send + Sync, + { + fn merge(ctx: EdgeDataMerge) -> Self { + if ctx.lhs_data == ctx.rhs_data { + ctx.lhs_data + } else { + TestData::B + } + } + } + #[test] fn test_0() { #[rustfmt::skip] let mut contour = vec![ - VectorEdge::new(1, int_pnt!(0, -1), int_pnt!(0, -3)), - VectorEdge::new(2, int_pnt!(0, -3), int_pnt!(1, -3)), - VectorEdge::new(3, int_pnt!(1, -3), int_pnt!(3, -3)), - VectorEdge::new(4, int_pnt!(3, -3), int_pnt!(3, 0)), - VectorEdge::new(5, int_pnt!(3, 0), int_pnt!(0, 0)), - VectorEdge::new(6, int_pnt!(0, 0), int_pnt!(0, -1)), + DataVectorEdge::new(1, int_pnt!(0, -1), int_pnt!(0, -3), ()), + DataVectorEdge::new(2, int_pnt!(0, -3), int_pnt!(1, -3), ()), + DataVectorEdge::new(3, int_pnt!(1, -3), int_pnt!(3, -3), ()), + DataVectorEdge::new(4, int_pnt!(3, -3), int_pnt!(3, 0), ()), + DataVectorEdge::new(5, int_pnt!(3, 0), int_pnt!(0, 0), ()), + DataVectorEdge::new(6, int_pnt!(0, 0), int_pnt!(0, -1), ()), ]; let result = contour.simplify_contour(); @@ -273,16 +303,16 @@ mod tests { fn test_duplicate_points() { #[rustfmt::skip] let mut contour = vec![ - VectorEdge::new(1, int_pnt!(-1, 3), int_pnt!(-1, 1)), - VectorEdge::new(2, int_pnt!(-1, 1), int_pnt!(-1, 1)), - VectorEdge::new(3, int_pnt!(-1, 1), int_pnt!(-3, 1)), - VectorEdge::new(4, int_pnt!(-3, 1), int_pnt!(-3, -2)), - VectorEdge::new(5, int_pnt!(-3, -2), int_pnt!(3, -2)), - VectorEdge::new(6, int_pnt!(3, -2), int_pnt!(3, 1)), - VectorEdge::new(7, int_pnt!(3, 1), int_pnt!(3, 1)), - VectorEdge::new(8, int_pnt!(3, 1), int_pnt!(1, 1)), - VectorEdge::new(9, int_pnt!(1, 1), int_pnt!(1, 3)), - VectorEdge::new(10, int_pnt!(1, 3), int_pnt!(-1, 3)), + DataVectorEdge::new(1, int_pnt!(-1, 3), int_pnt!(-1, 1), ()), + DataVectorEdge::new(2, int_pnt!(-1, 1), int_pnt!(-1, 1), ()), + DataVectorEdge::new(3, int_pnt!(-1, 1), int_pnt!(-3, 1), ()), + DataVectorEdge::new(4, int_pnt!(-3, 1), int_pnt!(-3, -2), ()), + DataVectorEdge::new(5, int_pnt!(-3, -2), int_pnt!(3, -2), ()), + DataVectorEdge::new(6, int_pnt!(3, -2), int_pnt!(3, 1), ()), + DataVectorEdge::new(7, int_pnt!(3, 1), int_pnt!(3, 1), ()), + DataVectorEdge::new(8, int_pnt!(3, 1), int_pnt!(1, 1), ()), + DataVectorEdge::new(9, int_pnt!(1, 1), int_pnt!(1, 3), ()), + DataVectorEdge::new(10, int_pnt!(1, 3), int_pnt!(-1, 3), ()), ]; let result = contour.simplify_contour(); @@ -295,12 +325,12 @@ mod tests { fn test_tiny_segments() { #[rustfmt::skip] let mut contour = vec![ - VectorEdge::new(1, int_pnt!(0, 2), int_pnt!(-1, 1)), - VectorEdge::new(2, int_pnt!(-1, 1), int_pnt!(-2, 0)), - VectorEdge::new(3, int_pnt!(-2, 0), int_pnt!(0, -1)), - VectorEdge::new(4, int_pnt!(0, -1), int_pnt!(2, 0)), - VectorEdge::new(5, int_pnt!(2, 0), int_pnt!(1, 1)), - VectorEdge::new(6, int_pnt!(1, 1), int_pnt!(0, 2)), + DataVectorEdge::new(1, int_pnt!(0, 2), int_pnt!(-1, 1), ()), + DataVectorEdge::new(2, int_pnt!(-1, 1), int_pnt!(-2, 0), ()), + DataVectorEdge::new(3, int_pnt!(-2, 0), int_pnt!(0, -1), ()), + DataVectorEdge::new(4, int_pnt!(0, -1), int_pnt!(2, 0), ()), + DataVectorEdge::new(5, int_pnt!(2, 0), int_pnt!(1, 1), ()), + DataVectorEdge::new(6, int_pnt!(1, 1), int_pnt!(0, 2), ()), ]; let result = contour.simplify_contour(); @@ -313,14 +343,14 @@ mod tests { fn test_collinear_runs() { #[rustfmt::skip] let mut contour = vec![ - VectorEdge::new(1, int_pnt!(-2, -2), int_pnt!(0, -2)), - VectorEdge::new(2, int_pnt!(0, -2), int_pnt!(2, -2)), - VectorEdge::new(3, int_pnt!(2, -2), int_pnt!(2, 0)), - VectorEdge::new(4, int_pnt!(2, 0), int_pnt!(2, 2)), - VectorEdge::new(5, int_pnt!(2, 2), int_pnt!(0, 2)), - VectorEdge::new(6, int_pnt!(0, 2), int_pnt!(-2, 2)), - VectorEdge::new(7, int_pnt!(-2, 2), int_pnt!(-2, 0)), - VectorEdge::new(8, int_pnt!(-2, 0), int_pnt!(-2, -2)), + DataVectorEdge::new(1, int_pnt!(-2, -2), int_pnt!(0, -2), ()), + DataVectorEdge::new(2, int_pnt!(0, -2), int_pnt!(2, -2), ()), + DataVectorEdge::new(3, int_pnt!(2, -2), int_pnt!(2, 0), ()), + DataVectorEdge::new(4, int_pnt!(2, 0), int_pnt!(2, 2), ()), + DataVectorEdge::new(5, int_pnt!(2, 2), int_pnt!(0, 2), ()), + DataVectorEdge::new(6, int_pnt!(0, 2), int_pnt!(-2, 2), ()), + DataVectorEdge::new(7, int_pnt!(-2, 2), int_pnt!(-2, 0), ()), + DataVectorEdge::new(8, int_pnt!(-2, 0), int_pnt!(-2, -2), ()), ]; let result = contour.simplify_contour(); @@ -329,13 +359,47 @@ mod tests { debug_assert!(contour.len() == 4); } + #[test] + fn test_collinear_same_data() { + #[rustfmt::skip] + let mut contour = vec![ + DataVectorEdge::new(1, int_pnt!(0, 0), int_pnt!(2, 0), TestData::A), + DataVectorEdge::new(2, int_pnt!(2, 0), int_pnt!(4, 0), TestData::A), + DataVectorEdge::new(3, int_pnt!(4, 0), int_pnt!(4, 4), TestData::A), + DataVectorEdge::new(4, int_pnt!(4, 4), int_pnt!(0, 4), TestData::A), + DataVectorEdge::new(5, int_pnt!(0, 4), int_pnt!(0, 0), TestData::A), + ]; + + let result = contour.simplify_contour(); + + debug_assert!(result); + debug_assert!(contour.len() == 4); + } + + #[test] + fn test_collinear_different_data() { + #[rustfmt::skip] + let mut contour = vec![ + DataVectorEdge::new(1, int_pnt!(0, 0), int_pnt!(2, 0), TestData::A), + DataVectorEdge::new(2, int_pnt!(2, 0), int_pnt!(4, 0), TestData::B), + DataVectorEdge::new(3, int_pnt!(4, 0), int_pnt!(4, 4), TestData::A), + DataVectorEdge::new(4, int_pnt!(4, 4), int_pnt!(0, 4), TestData::A), + DataVectorEdge::new(5, int_pnt!(0, 4), int_pnt!(0, 0), TestData::A), + ]; + + let result = contour.simplify_contour(); + + debug_assert!(!result); + debug_assert!(contour.len() == 5); + } + #[test] fn test_zero_area_path() { #[rustfmt::skip] let mut contour = vec![ - VectorEdge::new(1, int_pnt!(-3, 0), int_pnt!(0, 0)), - VectorEdge::new(2, int_pnt!(0, 0), int_pnt!(3, 0)), - VectorEdge::new(3, int_pnt!(3, 0), int_pnt!(-3, 0)), + DataVectorEdge::new(1, int_pnt!(-3, 0), int_pnt!(0, 0), ()), + DataVectorEdge::new(2, int_pnt!(0, 0), int_pnt!(3, 0), ()), + DataVectorEdge::new(3, int_pnt!(3, 0), int_pnt!(-3, 0), ()), ]; let result = contour.simplify_contour(); diff --git a/iOverlay/tests/edge_overlay_tests.rs b/iOverlay/tests/edge_overlay_tests.rs index 6194529..1930ff3 100644 --- a/iOverlay/tests/edge_overlay_tests.rs +++ b/iOverlay/tests/edge_overlay_tests.rs @@ -1,89 +1,244 @@ -use i_float::int::point::IntPoint; -use i_overlay::core::edge_data::{EdgeDataMerge, OverlayEdgeData}; -use i_overlay::core::edge_overlay::{EdgeOverlay, InputEdge}; -use i_overlay::core::fill_rule::FillRule; -use i_overlay::core::overlay::ShapeType; -use i_overlay::core::overlay_rule::OverlayRule; -use i_overlay::segm::boolean::ShapeCountBoolean; -use i_overlay::vector::edge::DataVectorEdge; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum Color { - Red, - Green, - Undefined, -} +#[cfg(test)] +mod tests { + use i_float::int::point::IntPoint; + use i_float::int_pnt; + use i_overlay::core::edge_data::{EdgeDataMerge, OverlayEdgeData}; + use i_overlay::core::edge_overlay::{EdgeOverlay, InputEdge}; + use i_overlay::core::fill_rule::FillRule; + use i_overlay::core::overlay::ShapeType; + use i_overlay::core::overlay_rule::OverlayRule; + use i_overlay::segm::boolean::ShapeCountBoolean; + use i_overlay::vector::edge::DataVectorEdge; + + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + enum Color { + Red, + Green, + None, + Undefined, + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + struct EdgeColor { + subj: Color, + clip: Color, + } -impl OverlayEdgeData for Color { - fn merge(ctx: EdgeDataMerge) -> Self { - match (ctx.lhs_data, ctx.rhs_data) { - (Color::Red, Color::Red) => Color::Red, - (Color::Green, Color::Green) => Color::Green, - _ => Color::Undefined, + impl OverlayEdgeData for EdgeColor { + fn merge(ctx: EdgeDataMerge) -> Self { + let subj = if ctx.lhs_data.subj == ctx.rhs_data.subj { + ctx.lhs_data.subj + } else if ctx.rhs_count.subj == 0 || ctx.lhs_count.subj == 0 { + if ctx.rhs_count.subj == ctx.lhs_count.subj { + Color::None + } else if ctx.rhs_count.subj == 0 { + ctx.lhs_data.subj + } else { + ctx.rhs_data.subj + } + } else { + Color::Undefined + }; + + let clip = if ctx.lhs_data.clip == ctx.rhs_data.clip { + ctx.lhs_data.clip + } else if ctx.rhs_count.clip == 0 || ctx.lhs_count.clip == 0 { + if ctx.rhs_count.clip == ctx.lhs_count.clip { + Color::None + } else if ctx.rhs_count.clip == 0 { + ctx.lhs_data.clip + } else { + ctx.rhs_data.clip + } + } else { + Color::Undefined + }; + Self { subj, clip } } } -} -#[test] -fn union_keeps_source_edge_data_and_marks_shared_runs_undefined() { - let edges = overlay_edges(OverlayRule::Union); - assert_eq!(edges.len(), 8); - assert_counts(&edges, 3, 3, 2); -} + #[test] + fn union_squares() { + let subj = square( + 0, + 0, + 4, + 4, + EdgeColor { + subj: Color::Red, + clip: Color::None, + }, + ); + let clip = square( + 4, + 0, + 8, + 4, + EdgeColor { + subj: Color::None, + clip: Color::Green, + }, + ); -#[test] -fn intersect_uses_cut_edges_and_marks_shared_runs_undefined() { - let edges = overlay_edges(OverlayRule::Intersect); - assert_eq!(edges.len(), 4); - assert_counts(&edges, 1, 1, 2); -} + let mut overlay = EdgeOverlay::new(subj.len() + clip.len()); + overlay.add_edges(subj, ShapeType::Subject); + overlay.add_edges(clip, ShapeType::Clip); -#[test] -fn difference_keeps_subject_edges_and_uses_clip_cut_edge() { - let edges = overlay_edges(OverlayRule::Difference); - assert_eq!(edges.len(), 4); - assert_counts(&edges, 3, 1, 0); -} + let shapes = overlay.build_vector_shapes(OverlayRule::Union, FillRule::NonZero); -#[test] -fn inverse_difference_keeps_clip_edges_and_uses_subject_cut_edge() { - let edges = overlay_edges(OverlayRule::InverseDifference); - assert_eq!(edges.len(), 4); - assert_counts(&edges, 1, 3, 0); -} + assert_eq!(shapes.len(), 1); + assert_eq!(shapes[0].len(), 1); + let rect = &shapes[0][0]; + assert_eq!(rect.len(), 6); -fn overlay_edges(rule: OverlayRule) -> Vec> { - let subj = square(0, 0, 4, 4, Color::Red); - let clip = square(2, 0, 6, 4, Color::Green); + let template = vec![ + DataVectorEdge { + a: int_pnt!(0, 4), + b: int_pnt!(0, 0), + fill: 1, + data: EdgeColor { + subj: Color::Red, + clip: Color::None, + }, + }, + DataVectorEdge { + a: int_pnt!(0, 0), + b: int_pnt!(4, 0), + fill: 1, + data: EdgeColor { + subj: Color::Red, + clip: Color::None, + }, + }, + DataVectorEdge { + a: int_pnt!(4, 0), + b: int_pnt!(8, 0), + fill: 4, + data: EdgeColor { + subj: Color::None, + clip: Color::Green, + }, + }, + DataVectorEdge { + a: int_pnt!(8, 0), + b: int_pnt!(8, 4), + fill: 4, + data: EdgeColor { + subj: Color::None, + clip: Color::Green, + }, + }, + DataVectorEdge { + a: int_pnt!(8, 4), + b: int_pnt!(4, 4), + fill: 4, + data: EdgeColor { + subj: Color::None, + clip: Color::Green, + }, + }, + DataVectorEdge { + a: int_pnt!(4, 4), + b: int_pnt!(0, 4), + fill: 1, + data: EdgeColor { + subj: Color::Red, + clip: Color::None, + }, + }, + ]; + assert_eq!(rect, &template); + } - let mut overlay = EdgeOverlay::new(subj.len() + clip.len()); - overlay.add_edges(subj, ShapeType::Subject); - overlay.add_edges(clip, ShapeType::Clip); - overlay.build_separate_vectors(rule, FillRule::NonZero) -} + #[test] + fn intersect_squares() { + let subj = square( + 0, + 0, + 4, + 4, + EdgeColor { + subj: Color::Red, + clip: Color::None, + }, + ); + let clip = square( + 2, + 0, + 6, + 4, + EdgeColor { + subj: Color::None, + clip: Color::Green, + }, + ); -fn square(x0: i32, y0: i32, x1: i32, y1: i32, data: Color) -> Vec> { - let points = [ - IntPoint::new(x0, y0), - IntPoint::new(x1, y0), - IntPoint::new(x1, y1), - IntPoint::new(x0, y1), - ]; - - points - .iter() - .copied() - .zip(points.iter().copied().cycle().skip(1)) - .take(points.len()) - .map(|(a, b)| InputEdge { a, b, data }) - .collect() -} + let mut overlay = EdgeOverlay::new(subj.len() + clip.len()); + overlay.add_edges(subj, ShapeType::Subject); + overlay.add_edges(clip, ShapeType::Clip); + + let shapes = overlay.build_vector_shapes(OverlayRule::Intersect, FillRule::NonZero); -fn assert_counts(edges: &[DataVectorEdge], red: usize, green: usize, undefined: usize) { - assert_eq!(edges.iter().filter(|e| e.data == Color::Red).count(), red); - assert_eq!(edges.iter().filter(|e| e.data == Color::Green).count(), green); - assert_eq!( - edges.iter().filter(|e| e.data == Color::Undefined).count(), - undefined - ); + assert_eq!(shapes.len(), 1); + assert_eq!(shapes[0].len(), 1); + let rect = &shapes[0][0]; + assert_eq!(rect.len(), 4); + + let template = vec![ + DataVectorEdge { + a: int_pnt!(2, 4), + b: int_pnt!(2, 0), + fill: 7, + data: EdgeColor { + subj: Color::None, + clip: Color::Green, + }, + }, + DataVectorEdge { + a: int_pnt!(2, 0), + b: int_pnt!(4, 0), + fill: 5, + data: EdgeColor { + subj: Color::Red, + clip: Color::Green, + }, + }, + DataVectorEdge { + a: int_pnt!(4, 0), + b: int_pnt!(4, 4), + fill: 13, + data: EdgeColor { + subj: Color::Red, + clip: Color::None, + }, + }, + DataVectorEdge { + a: int_pnt!(4, 4), + b: int_pnt!(2, 4), + fill: 5, + data: EdgeColor { + subj: Color::Red, + clip: Color::Green, + }, + }, + ]; + assert_eq!(rect, &template); + } + + fn square(x0: i32, y0: i32, x1: i32, y1: i32, data: EdgeColor) -> Vec> { + let points = [ + IntPoint::new(x0, y0), + IntPoint::new(x1, y0), + IntPoint::new(x1, y1), + IntPoint::new(x0, y1), + ]; + + points + .iter() + .copied() + .zip(points.iter().copied().cycle().skip(1)) + .take(points.len()) + .map(|(a, b)| InputEdge { a, b, data }) + .collect() + } } diff --git a/iOverlay/tests/vector_tests.rs b/iOverlay/tests/vector_tests.rs index 9724cf5..7926486 100644 --- a/iOverlay/tests/vector_tests.rs +++ b/iOverlay/tests/vector_tests.rs @@ -4,7 +4,7 @@ mod tests { use i_overlay::core::fill_rule::FillRule; use i_overlay::core::overlay::{Overlay, ShapeType}; use i_overlay::core::overlay_rule::OverlayRule; - use i_overlay::vector::edge::VectorEdge; + use i_overlay::vector::edge::DataVectorEdge; #[test] fn test_0() { @@ -33,25 +33,29 @@ mod tests { let vectors = &shapes[0][0]; let template = [ - VectorEdge { + DataVectorEdge { a: IntPoint::new(-10240, 10240), b: IntPoint::new(-10240, -10240), fill: 1, + data: (), }, - VectorEdge { + DataVectorEdge { a: IntPoint::new(-10240, -10240), b: IntPoint::new(10240, -10240), fill: 1, + data: (), }, - VectorEdge { + DataVectorEdge { a: IntPoint::new(10240, -10240), b: IntPoint::new(10240, 10240), fill: 1, + data: (), }, - VectorEdge { + DataVectorEdge { a: IntPoint::new(10240, 10240), b: IntPoint::new(-10240, 10240), fill: 1, + data: (), }, ]; @@ -85,35 +89,41 @@ mod tests { let vectors = &shapes[0][0]; let template = [ - VectorEdge { + DataVectorEdge { a: IntPoint::new(-10240, 10240), b: IntPoint::new(-10240, -10240), fill: 1, + data: (), }, - VectorEdge { + DataVectorEdge { a: IntPoint::new(-10240, -10240), b: IntPoint::new(10240, -10240), fill: 1, + data: (), }, - VectorEdge { + DataVectorEdge { a: IntPoint::new(10240, -10240), b: IntPoint::new(10240, -5120), fill: 1, + data: (), }, - VectorEdge { + DataVectorEdge { a: IntPoint::new(10240, -5120), b: IntPoint::new(-5120, -5120), fill: 11, + data: (), }, - VectorEdge { + DataVectorEdge { a: IntPoint::new(-5120, -5120), b: IntPoint::new(-5120, 10240), fill: 11, + data: (), }, - VectorEdge { + DataVectorEdge { a: IntPoint::new(-5120, 10240), b: IntPoint::new(-10240, 10240), fill: 1, + data: (), }, ]; From 71275ece8172b3bac2403dbf1d45cdfe5921cd85 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sun, 17 May 2026 16:39:53 +0300 Subject: [PATCH 03/36] draft migration to new int api --- iOverlay/Cargo.toml | 16 +- iOverlay/src/bind/segment.rs | 43 +++--- iOverlay/src/bind/solver.rs | 60 +++++--- iOverlay/src/build/builder.rs | 5 +- iOverlay/src/build/sweep.rs | 43 +++--- iOverlay/src/core/extract.rs | 4 +- iOverlay/src/core/nearest_vector.rs | 62 ++++---- iOverlay/src/core/solver.rs | 13 +- iOverlay/src/float/graph.rs | 8 +- iOverlay/src/float/overlay.rs | 14 +- iOverlay/src/float/relate.rs | 6 +- iOverlay/src/float/scale.rs | 53 ++----- iOverlay/src/float/string_graph.rs | 2 +- iOverlay/src/float/string_overlay.rs | 15 +- iOverlay/src/geom/end.rs | 7 +- iOverlay/src/geom/line_range.rs | 8 +- iOverlay/src/geom/v_segment.rs | 50 +++--- iOverlay/src/geom/x_segment.rs | 17 ++- iOverlay/src/mesh/miter.rs | 2 +- iOverlay/src/mesh/outline/builder.rs | 12 +- iOverlay/src/mesh/outline/builder_join.rs | 10 +- iOverlay/src/mesh/outline/offset.rs | 10 +- iOverlay/src/mesh/outline/uniq_iter.rs | 12 +- iOverlay/src/mesh/stroke/builder.rs | 17 ++- iOverlay/src/mesh/stroke/builder_cap.rs | 4 +- iOverlay/src/mesh/stroke/builder_join.rs | 14 +- iOverlay/src/mesh/stroke/offset.rs | 15 +- iOverlay/src/mesh/stroke/section.rs | 4 +- iOverlay/src/segm/build.rs | 58 +++---- iOverlay/src/segm/segment.rs | 21 +-- iOverlay/src/split/cross_solver.rs | 176 +++++++++------------- iOverlay/src/split/grid_layout.rs | 16 +- iOverlay/src/vector/edge.rs | 27 ++-- iOverlay/src/vector/extract.rs | 28 +++- iOverlay/src/vector/simplify.rs | 7 +- iOverlay/tests/boolean/test_135.json | 14 +- 36 files changed, 437 insertions(+), 436 deletions(-) diff --git a/iOverlay/Cargo.toml b/iOverlay/Cargo.toml index 7018fe9..dbafde5 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 = "^2.0.0" } -i_shape = { version = "^2.0.0" } +#i_float = { version = "^2.0.0" } +#i_shape = { version = "^2.0.0" } i_tree = { version = "^0.18.0" } i_key_sort = { version = "^0.10.1" } -#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 = "^2.0.0", features = ["serde"] } -i_shape = { version = "^2.0.0", features = ["serde"] } +i_float = { path = "../../iFloat", features = ["serde"] } +i_shape = { path = "../../iShape", features = ["serde"] } +#i_float = { version = "^2.0.0", features = ["serde"] } +#i_shape = { version = "^2.0.0", features = ["serde"] } diff --git a/iOverlay/src/bind/segment.rs b/iOverlay/src/bind/segment.rs index 331adaf..43bd04f 100644 --- a/iOverlay/src/bind/segment.rs +++ b/iOverlay/src/bind/segment.rs @@ -1,6 +1,7 @@ use crate::geom::v_segment::VSegment; use crate::vector::edge::{DataVectorEdge, DataVectorPath}; use alloc::vec::Vec; +use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; use i_shape::int::path::IntPath; @@ -36,14 +37,14 @@ impl ContourIndex { } #[derive(Debug, Clone, Copy)] -pub(crate) struct IdSegment { +pub(crate) struct IdSegment { pub(crate) contour_index: ContourIndex, - pub(crate) v_segment: VSegment, + pub(crate) v_segment: VSegment, } -impl IdSegment { +impl IdSegment { #[inline] - fn new(data: ContourIndex, a: IntPoint, b: IntPoint) -> Self { + fn new(data: ContourIndex, a: IntPoint, b: IntPoint) -> Self { Self { contour_index: data, v_segment: VSegment { a, b }, @@ -51,7 +52,7 @@ impl IdSegment { } #[inline] - pub(crate) fn with_segment(data: ContourIndex, v_segment: VSegment) -> Self { + pub(crate) fn with_segment(data: ContourIndex, v_segment: VSegment) -> Self { Self { contour_index: data, v_segment, @@ -59,45 +60,45 @@ impl IdSegment { } } -pub(crate) trait IdSegments { +pub(crate) trait IdSegments { fn append_id_segments( &self, - buffer: &mut Vec, + buffer: &mut Vec>, id_data: ContourIndex, - x_min: i32, - x_max: i32, + x_min: I, + x_max: I, clockwise: bool, ); } -impl IdSegments for IntPath { +impl IdSegments for IntPath { #[inline] fn append_id_segments( &self, - buffer: &mut Vec, + buffer: &mut Vec>, id_data: ContourIndex, - x_min: i32, - x_max: i32, + x_min: I, + x_max: I, clockwise: bool, ) { - fn inner>( - mut iter: I, - buffer: &mut Vec, + fn inner>>( + mut iter: It, + buffer: &mut Vec>, id_data: ContourIndex, - x_min: i32, - x_max: i32, + x_min: I, + x_max: I, ) { let first = iter.next().unwrap(); let mut b = first; for a in iter { if a.x < b.x && x_min < b.x && a.x <= x_max { - buffer.push(IdSegment::new(id_data, a, b)); + buffer.push(IdSegment::::new(id_data, a, b)); } b = a; } let a = first; if a.x < b.x && x_min < b.x && a.x <= x_max { - buffer.push(IdSegment::new(id_data, a, b)); + buffer.push(IdSegment::::new(id_data, a, b)); } } @@ -109,7 +110,7 @@ impl IdSegments for IntPath { } } -impl IdSegments for DataVectorPath { +impl IdSegments for DataVectorPath { #[inline] fn append_id_segments( &self, diff --git a/iOverlay/src/bind/solver.rs b/iOverlay/src/bind/solver.rs index c10fbdf..105480f 100644 --- a/iOverlay/src/bind/solver.rs +++ b/iOverlay/src/bind/solver.rs @@ -4,9 +4,12 @@ use crate::util::log::Int; use alloc::vec; use alloc::vec::Vec; use core::cmp::Ordering; +use i_float::int::number::int::IntNumber; +use i_key_sort::sort::key::SortKey; use i_key_sort::sort::two_keys_cmp::TwoKeysAndCmpSort; use i_shape::int::path::IntPath; use i_shape::int::shape::{IntContour, IntShape}; +use i_tree::Expiration; use i_tree::key::exp::KeyExpCollection; use i_tree::key::list::KeyExpList; use i_tree::key::tree::KeyExpTree; @@ -20,15 +23,18 @@ pub(crate) struct ShapeBinder; impl ShapeBinder { #[inline] - pub(crate) fn bind( + pub(crate) fn bind( shape_count: usize, - hole_segments: Vec, - segments: Vec, - ) -> BindSolution { + hole_segments: Vec>, + segments: Vec>, + ) -> BindSolution + where + I: IntNumber + Expiration, + { if shape_count < 32 { let capacity = segments.len().log2_sqrt().max(4) * 2; let list = KeyExpList::new(capacity); - Self::private_solve::>( + Self::private_solve::, I, ContourIndex>>( list, shape_count, hole_segments, @@ -37,7 +43,7 @@ impl ShapeBinder { } else { let capacity = segments.len().log2_sqrt().max(8); let list = KeyExpTree::new(capacity); - Self::private_solve::>( + Self::private_solve::, I, ContourIndex>>( list, shape_count, hole_segments, @@ -46,12 +52,16 @@ impl ShapeBinder { } } - fn private_solve>( + fn private_solve( mut scan_list: S, shape_count: usize, - anchors: Vec, - segments: Vec, - ) -> BindSolution { + anchors: Vec>, + segments: Vec>, + ) -> BindSolution + where + I: IntNumber + Expiration, + S: KeyExpCollection, I, ContourIndex>, + { let children_count = anchors.len(); let mut parent_for_child = { #[cfg(debug_assertions)] @@ -105,15 +115,15 @@ impl ShapeBinder { } } -pub(crate) trait JoinHoles { - fn join_unsorted_holes(&mut self, holes: Vec, clockwise: bool); - fn join_sorted_holes(&mut self, holes: Vec, anchors: Vec, clockwise: bool); - fn scan_join(&mut self, holes: Vec, hole_segments: Vec, clockwise: bool); +pub(crate) trait JoinHoles { + fn join_unsorted_holes(&mut self, holes: Vec>, clockwise: bool); + fn join_sorted_holes(&mut self, holes: Vec>, anchors: Vec>, clockwise: bool); + fn scan_join(&mut self, holes: Vec>, hole_segments: Vec>, clockwise: bool); } -impl JoinHoles for Vec { +impl JoinHoles for Vec> { #[inline] - fn join_unsorted_holes(&mut self, holes: Vec, clockwise: bool) { + fn join_unsorted_holes(&mut self, holes: Vec>, clockwise: bool) { if self.is_empty() || holes.is_empty() { return; } @@ -140,7 +150,7 @@ impl JoinHoles for Vec { } #[inline] - fn join_sorted_holes(&mut self, holes: Vec, anchors: Vec, clockwise: bool) { + fn join_sorted_holes(&mut self, holes: Vec>, anchors: Vec>, clockwise: bool) { if self.is_empty() || holes.is_empty() { return; } @@ -157,7 +167,7 @@ impl JoinHoles for Vec { self.scan_join(holes, anchors, clockwise); } - fn scan_join(&mut self, holes: Vec, hole_segments: Vec, clockwise: bool) { + fn scan_join(&mut self, holes: Vec>, hole_segments: Vec>, clockwise: bool) { let x_min = hole_segments[0].v_segment.a.x; let x_max = hole_segments[hole_segments.len() - 1].v_segment.a.x; @@ -186,12 +196,12 @@ impl JoinHoles for Vec { } } -pub(crate) trait LeftBottomSegment { - fn left_bottom_segment(&self) -> VSegment; +pub(crate) trait LeftBottomSegment { + fn left_bottom_segment(&self) -> VSegment; } -impl LeftBottomSegment for IntContour { - fn left_bottom_segment(&self) -> VSegment { +impl LeftBottomSegment for IntContour { + fn left_bottom_segment(&self) -> VSegment { let mut index = 0; let mut a = *self.first().unwrap(); for (i, &p) in self.iter().enumerate().skip(1) { @@ -212,13 +222,13 @@ impl LeftBottomSegment for IntContour { } #[inline] -fn is_sorted(segments: &[IdSegment]) -> bool { +fn is_sorted(segments: &[IdSegment]) -> bool { segments .windows(2) .all(|slice| slice[0].v_segment.a <= slice[1].v_segment.a) } -impl IdSegment { +impl IdSegment { #[inline] fn cmp_by_a_then_by_angle(&self, other: &Self) -> Ordering { self.v_segment @@ -233,7 +243,7 @@ pub(crate) trait SortByAngle { fn add_sort_by_angle(&mut self); } -impl SortByAngle for [IdSegment] { +impl SortByAngle for [IdSegment] { #[inline] fn sort_by_a_then_by_angle(&mut self) { self.sort_by_two_keys_then_by( diff --git a/iOverlay/src/build/builder.rs b/iOverlay/src/build/builder.rs index e021aa7..c50c924 100644 --- a/iOverlay/src/build/builder.rs +++ b/iOverlay/src/build/builder.rs @@ -8,6 +8,7 @@ use crate::segm::segment::{NONE, Segment, SegmentFill}; 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; pub(super) trait InclusionFilterStrategy { @@ -25,11 +26,11 @@ impl<'a> StoreFillsHandler<'a> { } } -impl FillHandler for StoreFillsHandler<'_> { +impl FillHandler for StoreFillsHandler<'_> { type Output = (); #[inline(always)] - fn handle(&mut self, index: usize, _segment: &Segment, fill: SegmentFill) -> ControlFlow<()> { + fn handle(&mut self, index: usize, _segment: &Segment, fill: SegmentFill) -> ControlFlow<()> { // fills is pre-allocated to segments.len() and index is guaranteed // to be in range by the sweep algorithm unsafe { *self.fills.get_unchecked_mut(index) = fill }; diff --git a/iOverlay/src/build/sweep.rs b/iOverlay/src/build/sweep.rs index 70b793d..b6f7715 100644 --- a/iOverlay/src/build/sweep.rs +++ b/iOverlay/src/build/sweep.rs @@ -7,7 +7,9 @@ use crate::segm::winding::WindingCount; use crate::util::log::Int; use alloc::vec::Vec; use core::ops::ControlFlow; +use i_float::int::number::int::IntNumber; use i_float::triangle::Triangle; +use i_tree::Expiration; use i_tree::key::exp::KeyExpCollection; use i_tree::key::list::KeyExpList; use i_tree::key::tree::KeyExpTree; @@ -16,24 +18,29 @@ pub(crate) trait FillStrategy { fn add_and_fill(this: C, bot: C) -> (C, SegmentFill); } -pub(crate) trait FillHandler { +pub(crate) trait FillHandler { type Output; fn handle( &mut self, index: usize, - segment: &Segment, + segment: &Segment, fill: SegmentFill, ) -> ControlFlow; fn finalize(self) -> Self::Output; } #[inline] -fn sweep_with_handler(scan: &mut S, segments: &[Segment], mut handler: H) -> H::Output +fn sweep_with_handler( + scan: &mut S, + segments: &[Segment], + mut handler: H, +) -> H::Output where + I: IntNumber + Expiration, C: WindingCount, F: FillStrategy, - S: KeyExpCollection, - H: FillHandler, + S: KeyExpCollection, I, C>, + H: FillHandler, { let mut node = Vec::with_capacity(4); let n = segments.len(); @@ -57,7 +64,7 @@ where } if node.len() > 1 { - node.sort_by(|s0, s1| Triangle::clock_order_point(p, s1.point, s0.point)); + node.sort_by(|s0, s1| Triangle::clock_order(p, s1.point, s0.point)); } let mut sum_count = scan.first_less_or_equal_by(p.x, C::new(0, 0), |s| s.is_under_point_order(p)); @@ -82,12 +89,12 @@ where handler.finalize() } -pub(crate) struct SweepRunner { - list: Option>, - tree: Option>, +pub(crate) struct SweepRunner { + list: Option, I, C>>, + tree: Option, I, C>>, } -impl SweepRunner { +impl SweepRunner { #[inline] pub(crate) fn new() -> Self { Self { @@ -100,25 +107,25 @@ impl SweepRunner { pub(crate) fn run( &mut self, solver: &Solver, - segments: &[Segment], + segments: &[Segment], handler: H, ) -> H::Output where F: FillStrategy, - H: FillHandler, + H: FillHandler, D: Send, { let count = segments.len(); if solver.is_list_fill(segments) { let capacity = count.log2_sqrt().max(4) * 2; let mut list = self.take_scan_list(capacity); - let result = sweep_with_handler::(&mut list, segments, handler); + let result = sweep_with_handler::(&mut list, segments, handler); self.list = Some(list); result } else { let capacity = count.log2_sqrt().max(8); let mut tree = self.take_scan_tree(capacity); - let result = sweep_with_handler::(&mut tree, segments, handler); + let result = sweep_with_handler::(&mut tree, segments, handler); self.tree = Some(tree); result } @@ -129,11 +136,11 @@ impl SweepRunner { &mut self, fill_rule: FillRule, solver: &Solver, - segments: &[Segment], + segments: &[Segment], handler: H, ) -> H::Output where - H: FillHandler, + H: FillHandler, D: Send, EvenOddStrategy: FillStrategy, NonZeroStrategy: FillStrategy, @@ -149,7 +156,7 @@ impl SweepRunner { } #[inline] - fn take_scan_list(&mut self, capacity: usize) -> KeyExpList { + fn take_scan_list(&mut self, capacity: usize) -> KeyExpList, I, C> { if let Some(mut list) = self.list.take() { list.clear(); list.reserve_capacity(capacity); @@ -160,7 +167,7 @@ impl SweepRunner { } #[inline] - fn take_scan_tree(&mut self, capacity: usize) -> KeyExpTree { + fn take_scan_tree(&mut self, capacity: usize) -> KeyExpTree, I, C> { if let Some(mut tree) = self.tree.take() { tree.clear(); tree.reserve_capacity(capacity); diff --git a/iOverlay/src/core/extract.rs b/iOverlay/src/core/extract.rs index 7a1bae4..9339600 100644 --- a/iOverlay/src/core/extract.rs +++ b/iOverlay/src/core/extract.rs @@ -434,7 +434,7 @@ impl GraphUtil { // SAFETY: indices holds link ids emitted by GraphBuilder, so each i < links.len(). links.get_unchecked(i) }; - if !link.is_direct() || Triangle::is_clockwise_point(top.a.point, top.b.point, link.b.point) { + if !link.is_direct() || Triangle::is_clockwise(top.a.point, top.b.point, link.b.point) { continue; } @@ -454,7 +454,7 @@ impl GraphUtil { // SAFETY: every bridge index comes straight from GraphBuilder::build_nodes_and_connect_links, // which only records values in 0..links.len(), so the unchecked lookups stay in-bounds. let (l0, l1) = unsafe { (links.get_unchecked(bridge[0]), links.get_unchecked(bridge[1])) }; - if Triangle::is_clockwise_point(l0.a.point, l0.b.point, l1.b.point) { + if Triangle::is_clockwise(l0.a.point, l0.b.point, l1.b.point) { bridge[0] } else { bridge[1] diff --git a/iOverlay/src/core/nearest_vector.rs b/iOverlay/src/core/nearest_vector.rs index 1f7a1d4..b5b01c5 100644 --- a/iOverlay/src/core/nearest_vector.rs +++ b/iOverlay/src/core/nearest_vector.rs @@ -1,24 +1,32 @@ -use i_float::fix_vec::FixVec; +use i_float::int::number::int::IntNumber; +use i_float::int::number::wide_int::WideIntNumber; use i_float::int::point::IntPoint; +use i_float::int::vector::IntVector; -pub(crate) struct NearestVector { - c: IntPoint, // center - va: FixVec, // our target vector - vb: FixVec, // nearest vector to Va by specified rotation +pub(crate) struct NearestVector { + c: IntPoint, // center + va: IntVector, // our target vector + vb: IntVector, // nearest vector to Va by specified rotation ab_more_180: bool, // is angle between Va and Vb more than 180 degrees pub(crate) best_id: usize, - rotation_factor: i64, // +1 for clockwise, -1 for counterclockwise + rotation_factor: I::Wide, // +1 for clockwise, -1 for counterclockwise } -impl NearestVector { +impl NearestVector { #[inline] - pub(crate) fn new(c: IntPoint, a: IntPoint, b: IntPoint, best_id: usize, clockwise: bool) -> Self { - let va = a.subtract(c); - let vb = b.subtract(c); + pub(crate) fn new( + c: IntPoint, + a: IntPoint, + b: IntPoint, + best_id: usize, + clockwise: bool, + ) -> Self { + let va = a - c; + let vb = b - c; let (ab_more_180, rotation_factor) = if clockwise { - (va.cross_product(vb) >= 0, 1) + (va.cross_product(vb) >= I::Wide::ZERO, I::Wide::ONE) } else { - (va.cross_product(vb) <= 0, -1) + (va.cross_product(vb) <= I::Wide::ZERO, -I::Wide::ONE) }; Self { c, @@ -31,12 +39,12 @@ impl NearestVector { } #[inline] - pub(crate) fn add(&mut self, p: IntPoint, id: usize) { - let vp = p.subtract(self.c); - let ap_more_180 = self.va.cross_product(vp) * self.rotation_factor >= 0; + pub(crate) fn add(&mut self, p: IntPoint, id: usize) { + let vp = p - self.c; + let ap_more_180 = self.va.cross_product(vp) * self.rotation_factor >= I::Wide::ZERO; if self.ab_more_180 == ap_more_180 { - if vp.cross_product(self.vb) * self.rotation_factor < 0 { + if vp.cross_product(self.vb) * self.rotation_factor < I::Wide::ZERO { self.vb = vp; self.best_id = id; } @@ -51,8 +59,8 @@ impl NearestVector { #[cfg(test)] mod tests { use crate::core::nearest_vector::NearestVector; - use i_float::fix_vec::FixVec; use i_float::int::point::IntPoint; + use i_float::int::vector::IntVector; #[test] fn test_nearest_ccw_vector_creation() { @@ -62,8 +70,8 @@ mod tests { let nearest_ccw = NearestVector::new(c, a, b, 0, false); - assert_eq!(nearest_ccw.va, FixVec::new(1, 0)); - assert_eq!(nearest_ccw.vb, FixVec::new(0, 1)); + assert_eq!(nearest_ccw.va, IntVector::::new(1, 0)); + assert_eq!(nearest_ccw.vb, IntVector::::new(0, 1)); assert!(!nearest_ccw.ab_more_180); } @@ -77,7 +85,7 @@ mod tests { let p = IntPoint::new(-1, 0); nearest_ccw.add(p, 1); - assert_eq!(nearest_ccw.vb, FixVec::new(0, 1)); + assert_eq!(nearest_ccw.vb, IntVector::::new(0, 1)); assert!(!nearest_ccw.ab_more_180); } @@ -90,7 +98,7 @@ mod tests { let mut nearest_ccw = NearestVector::new(c, a, b, 0, false); let p = IntPoint::new(0, 1); nearest_ccw.add(p, 1); - assert_eq!(nearest_ccw.vb, FixVec::new(0, 1)); + assert_eq!(nearest_ccw.vb, IntVector::::new(0, 1)); assert!(!nearest_ccw.ab_more_180); } @@ -105,7 +113,7 @@ mod tests { nearest_ccw.add(p, 1); - assert_eq!(nearest_ccw.vb, FixVec::new(1, 1)); + assert_eq!(nearest_ccw.vb, IntVector::::new(1, 1)); } #[test] @@ -130,8 +138,8 @@ mod tests { let nearest_cw = NearestVector::new(c, a, b, 0, true); - assert_eq!(nearest_cw.va, FixVec::new(1, 0)); - assert_eq!(nearest_cw.vb, FixVec::new(0, -1)); + assert_eq!(nearest_cw.va, IntVector::::new(1, 0)); + assert_eq!(nearest_cw.vb, IntVector::::new(0, -1)); assert!(!nearest_cw.ab_more_180); } @@ -145,7 +153,7 @@ mod tests { let p = IntPoint::new(-1, 0); nearest_cw.add(p, 1); - assert_eq!(nearest_cw.vb, FixVec::new(0, -1)); + assert_eq!(nearest_cw.vb, IntVector::::new(0, -1)); assert!(!nearest_cw.ab_more_180); } @@ -158,7 +166,7 @@ mod tests { let mut nearest_cw = NearestVector::new(c, a, b, 0, true); let p = IntPoint::new(0, -1); nearest_cw.add(p, 1); - assert_eq!(nearest_cw.vb, FixVec::new(0, -1)); + assert_eq!(nearest_cw.vb, IntVector::::new(0, -1)); assert!(!nearest_cw.ab_more_180); } @@ -173,7 +181,7 @@ mod tests { nearest_cw.add(p, 1); - assert_eq!(nearest_cw.vb, FixVec::new(0, -1)); + assert_eq!(nearest_cw.vb, IntVector::::new(0, -1)); } #[test] diff --git a/iOverlay/src/core/solver.rs b/iOverlay/src/core/solver.rs index f8b728c..d2bee17 100644 --- a/iOverlay/src/core/solver.rs +++ b/iOverlay/src/core/solver.rs @@ -1,5 +1,6 @@ use crate::core::solver::Strategy::{Auto, Frag, List, Tree}; use crate::segm::segment::Segment; +use i_float::int::number::int::IntNumber; /// Represents the selection strategy or algorithm for processing geometric data, aimed at optimizing performance under various conditions. /// @@ -161,7 +162,10 @@ impl Solver { } } - pub(crate) fn is_list_split(&self, segments: &[Segment]) -> bool { + pub(crate) fn is_list_split( + &self, + segments: &[Segment], + ) -> bool { match self.strategy { List => true, Tree | Frag => false, @@ -169,11 +173,14 @@ impl Solver { } } - pub(crate) fn is_fragmentation_required(&self, segments: &[Segment]) -> bool { + pub(crate) fn is_fragmentation_required( + &self, + segments: &[Segment], + ) -> bool { segments.len() > Self::MIN_FRAGMENT_COUNT || self.strategy == Frag } - pub(crate) fn is_list_fill(&self, segments: &[Segment]) -> bool { + pub(crate) fn is_list_fill(&self, segments: &[Segment]) -> bool { match self.strategy { List => true, Tree | Frag => false, diff --git a/iOverlay/src/float/graph.rs b/iOverlay/src/float/graph.rs index a0209db..d2b2fd3 100644 --- a/iOverlay/src/float/graph.rs +++ b/iOverlay/src/float/graph.rs @@ -17,13 +17,17 @@ use i_shape::float::simple::SimplifyContour; /// [More information](https://ishape-rust.github.io/iShape-js/overlay/overlay_graph/overlay_graph.html) about Overlay Graph. pub struct FloatOverlayGraph<'a, P: FloatPointCompatible> { pub graph: OverlayGraph<'a>, - pub adapter: FloatPointAdapter

, + pub adapter: FloatPointAdapter, clean_result: bool, } impl<'a, P: FloatPointCompatible> FloatOverlayGraph<'a, P> { #[inline] - pub(crate) fn new(graph: OverlayGraph<'a>, adapter: FloatPointAdapter

, clean_result: bool) -> Self { + pub(crate) fn new( + graph: OverlayGraph<'a>, + adapter: FloatPointAdapter, + clean_result: bool, + ) -> Self { Self { graph, adapter, diff --git a/iOverlay/src/float/overlay.rs b/iOverlay/src/float/overlay.rs index cf383e6..c23f925 100644 --- a/iOverlay/src/float/overlay.rs +++ b/iOverlay/src/float/overlay.rs @@ -45,7 +45,7 @@ pub struct OverlayOptions { pub struct FloatOverlay { pub(super) overlay: Overlay, pub(super) clean_result: bool, - pub(super) adapter: FloatPointAdapter

, + pub(super) adapter: FloatPointAdapter, } impl FloatOverlay

{ @@ -57,7 +57,7 @@ impl FloatOverlay

{ /// - `capacity`: Initial capacity for storing segments, ideally matching the total number of /// segments for efficient memory allocation. #[inline] - pub fn with_adapter(adapter: FloatPointAdapter

, capacity: usize) -> Self { + pub fn with_adapter(adapter: FloatPointAdapter, capacity: usize) -> Self { Self::new_custom(adapter, Default::default(), Default::default(), capacity) } @@ -72,7 +72,7 @@ impl FloatOverlay

{ /// segments for efficient memory allocation. #[inline] pub fn new_custom( - adapter: FloatPointAdapter

, + adapter: FloatPointAdapter, options: OverlayOptions, solver: Solver, capacity: usize, @@ -371,7 +371,7 @@ impl FloatOverlay

{ impl Default for OverlayOptions { fn default() -> Self { // f32 precision is not enough to cover i32 - let clean_result = T::bit_width() <= 32; + let clean_result = T::BITS <= 32; Self { preserve_input_collinear: false, output_direction: ContourDirection::CounterClockwise, @@ -386,13 +386,13 @@ impl Default for OverlayOptions { impl OverlayOptions { pub(crate) fn int_with_adapter>( &self, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, ) -> IntOverlayOptions { IntOverlayOptions { preserve_input_collinear: self.preserve_input_collinear, output_direction: self.output_direction, preserve_output_collinear: self.preserve_output_collinear, - min_output_area: adapter.sqr_float_to_int(self.min_output_area), + min_output_area: adapter.round_sqr_len_to_int(self.min_output_area) as u64, ogc: self.ogc, } } @@ -409,7 +409,7 @@ impl OverlayOptions { pub fn ogc() -> Self { // f32 precision is not enough to cover i32 - let clean_result = T::bit_width() <= 32; + let clean_result = T::BITS <= 32; Self { preserve_input_collinear: false, output_direction: ContourDirection::CounterClockwise, diff --git a/iOverlay/src/float/relate.rs b/iOverlay/src/float/relate.rs index 1e360f3..de6fca6 100644 --- a/iOverlay/src/float/relate.rs +++ b/iOverlay/src/float/relate.rs @@ -28,7 +28,7 @@ use i_shape::source::resource::ShapeResource; /// methods directly on shape types. pub struct FloatPredicateOverlay { pub(crate) overlay: PredicateOverlay, - pub(crate) adapter: FloatPointAdapter

, + pub(crate) adapter: FloatPointAdapter, } impl FloatPredicateOverlay

{ @@ -40,7 +40,7 @@ impl FloatPredicateOverlay

{ /// * `adapter` - A `FloatPointAdapter` instance for coordinate conversion. /// * `capacity` - Initial capacity for storing segments. #[inline] - pub fn with_adapter(adapter: FloatPointAdapter

, capacity: usize) -> Self { + pub fn with_adapter(adapter: FloatPointAdapter, capacity: usize) -> Self { Self { overlay: PredicateOverlay::new(capacity), adapter, @@ -58,7 +58,7 @@ impl FloatPredicateOverlay

{ /// * `capacity` - Initial capacity for storing segments. #[inline] pub fn with_adapter_custom( - adapter: FloatPointAdapter

, + adapter: FloatPointAdapter, fill_rule: FillRule, solver: Solver, capacity: usize, diff --git a/iOverlay/src/float/scale.rs b/iOverlay/src/float/scale.rs index e2012e4..de9a49d 100644 --- a/iOverlay/src/float/scale.rs +++ b/iOverlay/src/float/scale.rs @@ -4,7 +4,7 @@ use crate::core::overlay_rule::OverlayRule; use crate::core::solver::Solver; use crate::float::overlay::{FloatOverlay, OverlayOptions}; use crate::float::relate::FloatPredicateOverlay; -use i_float::adapter::FloatPointAdapter; +use i_float::adapter::{FloatPointAdapter, FloatPointAdapterScaleError}; use i_float::float::compatible::FloatPointCompatible; use i_float::float::number::FloatNumber; use i_shape::base::data::Shapes; @@ -34,6 +34,17 @@ impl FixedScaleOverlayError { } } +impl From for FixedScaleOverlayError { + #[inline] + fn from(error: FloatPointAdapterScaleError) -> Self { + match error { + FloatPointAdapterScaleError::ScaleTooLarge => Self::ScaleTooLarge, + FloatPointAdapterScaleError::ScaleNonPositive => Self::ScaleNonPositive, + FloatPointAdapterScaleError::ScaleNotFinite => Self::ScaleNotFinite, + } + } +} + /// Trait `FixedScaleFloatOverlay` 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. /// @@ -107,16 +118,8 @@ impl FloatOverlay

{ R0: ShapeResource

+ ?Sized, R1: ShapeResource

+ ?Sized, { - let s = FixedScaleOverlayError::validate_scale(scale)?; - let iter = subj.iter_paths().chain(clip.iter_paths()).flatten(); - let mut adapter = FloatPointAdapter::with_iter(iter); - if adapter.dir_scale < scale { - return Err(FixedScaleOverlayError::ScaleTooLarge); - } - - adapter.dir_scale = scale; - adapter.inv_scale = P::Scalar::from_float(1.0 / s); + let adapter = FloatPointAdapter::with_iter_and_scale_checked(iter, scale)?; let subj_capacity = subj.iter_paths().fold(0, |s, c| s + c.len()); let clip_capacity = clip.iter_paths().fold(0, |s, c| s + c.len()); @@ -151,16 +154,8 @@ impl FloatOverlay

{ R0: ShapeResource

+ ?Sized, R1: ShapeResource

+ ?Sized, { - let s = FixedScaleOverlayError::validate_scale(scale)?; - let iter = subj.iter_paths().chain(clip.iter_paths()).flatten(); - let mut adapter = FloatPointAdapter::with_iter(iter); - if adapter.dir_scale < scale { - return Err(FixedScaleOverlayError::ScaleTooLarge); - } - - adapter.dir_scale = scale; - adapter.inv_scale = P::Scalar::from_float(1.0 / s); + let adapter = FloatPointAdapter::with_iter_and_scale_checked(iter, scale)?; let subj_capacity = subj.iter_paths().fold(0, |s, c| s + c.len()); let clip_capacity = clip.iter_paths().fold(0, |s, c| s + c.len()); @@ -194,16 +189,8 @@ impl FloatPredicateOverlay

{ R0: ShapeResource

+ ?Sized, R1: ShapeResource

+ ?Sized, { - let s = FixedScaleOverlayError::validate_scale(scale)?; - let iter = subj.iter_paths().chain(clip.iter_paths()).flatten(); - let mut adapter = FloatPointAdapter::with_iter(iter); - if adapter.dir_scale < scale { - return Err(FixedScaleOverlayError::ScaleTooLarge); - } - - adapter.dir_scale = scale; - adapter.inv_scale = P::Scalar::from_float(1.0 / s); + let adapter = FloatPointAdapter::with_iter_and_scale_checked(iter, scale)?; let subj_capacity = subj.iter_paths().fold(0, |s, c| s + c.len()); let clip_capacity = clip.iter_paths().fold(0, |s, c| s + c.len()); @@ -234,16 +221,8 @@ impl FloatPredicateOverlay

{ R0: ShapeResource

+ ?Sized, R1: ShapeResource

+ ?Sized, { - let s = FixedScaleOverlayError::validate_scale(scale)?; - let iter = subj.iter_paths().chain(clip.iter_paths()).flatten(); - let mut adapter = FloatPointAdapter::with_iter(iter); - if adapter.dir_scale < scale { - return Err(FixedScaleOverlayError::ScaleTooLarge); - } - - adapter.dir_scale = scale; - adapter.inv_scale = P::Scalar::from_float(1.0 / s); + let adapter = FloatPointAdapter::with_iter_and_scale_checked(iter, scale)?; let subj_capacity = subj.iter_paths().fold(0, |s, c| s + c.len()); let clip_capacity = clip.iter_paths().fold(0, |s, c| s + c.len()); diff --git a/iOverlay/src/float/string_graph.rs b/iOverlay/src/float/string_graph.rs index 592a6bb..0986a6f 100644 --- a/iOverlay/src/float/string_graph.rs +++ b/iOverlay/src/float/string_graph.rs @@ -12,7 +12,7 @@ use i_shape::float::simple::SimplifyContour; /// providing methods to extract geometric shapes from the graph after applying string-based operations. pub struct FloatStringGraph<'a, P: FloatPointCompatible> { pub graph: StringGraph<'a>, - pub adapter: FloatPointAdapter

, + pub adapter: FloatPointAdapter, } impl FloatStringGraph<'_, P> { diff --git a/iOverlay/src/float/string_overlay.rs b/iOverlay/src/float/string_overlay.rs index 5fc42d2..2f94b7b 100644 --- a/iOverlay/src/float/string_overlay.rs +++ b/iOverlay/src/float/string_overlay.rs @@ -6,7 +6,6 @@ use crate::string::clip::ClipRule; use crate::string::overlay::StringOverlay; use i_float::adapter::FloatPointAdapter; use i_float::float::compatible::FloatPointCompatible; -use i_float::float::number::FloatNumber; use i_shape::base::data::Paths; use i_shape::float::adapter::ShapeToFloat; use i_shape::source::resource::ShapeResource; @@ -19,7 +18,7 @@ use i_shape::source::resource::ShapeResource; /// `x_int = (x_float - offset_x) * scale`. Use a fixed scale if you need predictable precision. pub struct FloatStringOverlay { pub(super) overlay: StringOverlay, - pub(super) adapter: FloatPointAdapter

, + pub(super) adapter: FloatPointAdapter, } impl FloatStringOverlay

{ @@ -33,7 +32,7 @@ impl FloatStringOverlay

{ /// - `capacity`: Initial capacity for storing segments, ideally matching the total number of /// segments for efficient memory allocation. #[inline] - pub fn with_adapter(adapter: FloatPointAdapter

, capacity: usize) -> Self { + pub fn with_adapter(adapter: FloatPointAdapter, capacity: usize) -> Self { Self { overlay: StringOverlay::new(capacity), adapter, @@ -81,16 +80,8 @@ impl FloatStringOverlay

{ R0: ShapeResource

, R1: ShapeResource

, { - let s = FixedScaleOverlayError::validate_scale(scale)?; - let iter = shape.iter_paths().chain(string.iter_paths()).flatten(); - let mut adapter = FloatPointAdapter::with_iter(iter); - if adapter.dir_scale < scale { - return Err(FixedScaleOverlayError::ScaleTooLarge); - } - - adapter.dir_scale = scale; - adapter.inv_scale = P::Scalar::from_float(1.0 / s); + let adapter = FloatPointAdapter::with_iter_and_scale_checked(iter, scale)?; let shape_capacity = shape.iter_paths().fold(0, |s, c| s + c.len()); let string_capacity = string.iter_paths().fold(0, |s, c| s + c.len()); diff --git a/iOverlay/src/geom/end.rs b/iOverlay/src/geom/end.rs index 9b72fe3..29cee90 100644 --- a/iOverlay/src/geom/end.rs +++ b/iOverlay/src/geom/end.rs @@ -1,12 +1,13 @@ +use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; #[derive(Clone, Copy)] -pub(crate) struct End { +pub(crate) struct End { pub(crate) index: usize, - pub(crate) point: IntPoint, + pub(crate) point: IntPoint, } -impl Default for End { +impl Default for End { #[inline(always)] fn default() -> Self { Self { diff --git a/iOverlay/src/geom/line_range.rs b/iOverlay/src/geom/line_range.rs index 1a0bc40..6c2d7e0 100644 --- a/iOverlay/src/geom/line_range.rs +++ b/iOverlay/src/geom/line_range.rs @@ -1,5 +1,7 @@ +use i_float::int::number::int::IntNumber; + #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) struct LineRange { - pub(crate) min: i32, - pub(crate) max: i32, +pub(crate) struct LineRange { + pub(crate) min: I, + pub(crate) max: I, } diff --git a/iOverlay/src/geom/v_segment.rs b/iOverlay/src/geom/v_segment.rs index 3ea0e6d..6f3ae48 100644 --- a/iOverlay/src/geom/v_segment.rs +++ b/iOverlay/src/geom/v_segment.rs @@ -1,39 +1,41 @@ use crate::geom::x_segment::XSegment; use core::cmp::Ordering; +use i_float::int::number::int::IntNumber; +use i_float::int::number::wide_int::WideIntNumber; use i_float::int::point::IntPoint; use i_float::triangle::Triangle; -use i_tree::ExpiredKey; +use i_tree::{Expiration, ExpiredKey}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) struct VSegment { - pub(crate) a: IntPoint, - pub(crate) b: IntPoint, +pub(crate) struct VSegment { + pub(crate) a: IntPoint, + pub(crate) b: IntPoint, } -impl VSegment { +impl VSegment { #[inline(always)] - fn is_under_segment_order(&self, other: &VSegment) -> Ordering { + fn is_under_segment_order(&self, other: &VSegment) -> Ordering { match self.a.cmp(&other.a) { - Ordering::Less => Triangle::clock_order_point(self.a, other.a, self.b), - Ordering::Equal => Triangle::clock_order_point(self.a, other.b, self.b), - Ordering::Greater => Triangle::clock_order_point(other.a, other.b, self.a), + Ordering::Less => Triangle::clock_order(self.a, other.a, self.b), + Ordering::Equal => Triangle::clock_order(self.a, other.b, self.b), + Ordering::Greater => Triangle::clock_order(other.a, other.b, self.a), } } #[inline(always)] - pub(crate) fn is_under_point_order(&self, p: IntPoint) -> Ordering { + pub(crate) fn is_under_point_order(&self, p: IntPoint) -> Ordering { debug_assert!(self.a.x <= p.x && p.x <= self.b.x); debug_assert!(p != self.a && p != self.b); - Triangle::clock_order_point(self.a, p, self.b) + Triangle::clock_order(self.a, p, self.b) } #[inline(always)] - pub(crate) fn is_under_segment(&self, other: &VSegment) -> bool { + pub(crate) fn is_under_segment(&self, other: &VSegment) -> bool { match self.a.cmp(&other.a) { - Ordering::Less => Triangle::is_clockwise_point(self.a, other.a, self.b), - Ordering::Equal => Triangle::is_clockwise_point(self.a, other.b, self.b), - Ordering::Greater => Triangle::is_clockwise_point(other.a, other.b, self.a), + Ordering::Less => Triangle::is_clockwise(self.a, other.a, self.b), + Ordering::Equal => Triangle::is_clockwise(self.a, other.b, self.b), + Ordering::Greater => Triangle::is_clockwise(other.a, other.b, self.a), } } @@ -41,37 +43,37 @@ impl VSegment { pub(crate) fn cmp_by_angle(&self, other: &Self) -> Ordering { // sort angles counterclockwise // debug_assert!(self.a == other.a); - let v0 = self.b.subtract(self.a); - let v1 = other.b.subtract(other.a); + let v0 = self.b - self.a; + let v1 = other.b - other.a; let cross = v0.cross_product(v1); - 0.cmp(&cross) + I::Wide::ZERO.cmp(&cross) } } -impl From for VSegment { +impl From> for VSegment { #[inline(always)] - fn from(seg: XSegment) -> Self { + fn from(seg: XSegment) -> Self { VSegment { a: seg.a, b: seg.b } } } -impl PartialOrd for VSegment { +impl PartialOrd for VSegment { #[inline(always)] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for VSegment { +impl Ord for VSegment { #[inline(always)] fn cmp(&self, other: &Self) -> Ordering { self.is_under_segment_order(other) } } -impl ExpiredKey for VSegment { +impl ExpiredKey for VSegment { #[inline] - fn expiration(&self) -> i32 { + fn expiration(&self) -> I { self.b.x } } diff --git a/iOverlay/src/geom/x_segment.rs b/iOverlay/src/geom/x_segment.rs index cae600b..516392f 100644 --- a/iOverlay/src/geom/x_segment.rs +++ b/iOverlay/src/geom/x_segment.rs @@ -1,16 +1,17 @@ use crate::geom::line_range::LineRange; use core::cmp::Ordering; +use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) struct XSegment { - pub(crate) a: IntPoint, - pub(crate) b: IntPoint, +pub(crate) struct XSegment { + pub(crate) a: IntPoint, + pub(crate) b: IntPoint, } -impl XSegment { +impl XSegment { #[inline(always)] - pub(crate) fn y_range(&self) -> LineRange { + pub(crate) fn y_range(&self) -> LineRange { if self.a.y < self.b.y { LineRange { min: self.a.y, @@ -30,19 +31,19 @@ impl XSegment { } #[inline(always)] - pub(crate) fn is_not_intersect_y_range(&self, range: &LineRange) -> bool { + pub(crate) fn is_not_intersect_y_range(&self, range: &LineRange) -> bool { range.min > self.a.y && range.min > self.b.y || range.max < self.a.y && range.max < self.b.y } } -impl PartialOrd for XSegment { +impl PartialOrd for XSegment { #[inline(always)] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for XSegment { +impl Ord for XSegment { #[inline(always)] fn cmp(&self, other: &Self) -> Ordering { let a = self.a.cmp(&other.a); diff --git a/iOverlay/src/mesh/miter.rs b/iOverlay/src/mesh/miter.rs index 504d910..38d1c95 100644 --- a/iOverlay/src/mesh/miter.rs +++ b/iOverlay/src/mesh/miter.rs @@ -18,7 +18,7 @@ impl Miter { pb: P, va: P, vb: P, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, ) -> SharpMiter { let ia = adapter.float_to_int(&pa); let ib = adapter.float_to_int(&pb); diff --git a/iOverlay/src/mesh/outline/builder.rs b/iOverlay/src/mesh/outline/builder.rs index 5dbe2a1..5f56bb9 100644 --- a/iOverlay/src/mesh/outline/builder.rs +++ b/iOverlay/src/mesh/outline/builder.rs @@ -18,7 +18,7 @@ trait OutlineBuild { fn build( &self, path: &[P], - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ); @@ -70,7 +70,7 @@ impl OutlineBuilder

{ pub(super) fn build( &self, path: &[P], - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { self.builder.build(path, adapter, segments); @@ -92,7 +92,7 @@ impl, P: FloatPointCompatible> OutlineBuild

for Builder, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { if path.len() < 2 { @@ -117,7 +117,7 @@ impl, P: FloatPointCompatible> Builder { fn build( &self, path: &[P], - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { let iter = path.iter().map(|p| adapter.float_to_int(p)); @@ -152,7 +152,7 @@ impl, P: FloatPointCompatible> Builder { &self, s0: &OffsetSection

, s1: &OffsetSection

, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { let vi = s1.b - s1.a; @@ -175,7 +175,7 @@ impl, P: FloatPointCompatible> Builder { impl OffsetSection

{ #[inline] - fn new(radius: P::Scalar, s: &UniqueSegment, adapter: &FloatPointAdapter

) -> Self { + fn new(radius: P::Scalar, s: &UniqueSegment, adapter: &FloatPointAdapter) -> Self { let a = adapter.int_to_float(&s.a); let b = adapter.int_to_float(&s.b); let ab = FloatPointMath::sub(&b, &a); diff --git a/iOverlay/src/mesh/outline/builder_join.rs b/iOverlay/src/mesh/outline/builder_join.rs index 5eabce4..5ec4639 100644 --- a/iOverlay/src/mesh/outline/builder_join.rs +++ b/iOverlay/src/mesh/outline/builder_join.rs @@ -15,7 +15,7 @@ pub(super) trait JoinBuilder { &self, s0: &OffsetSection

, s1: &OffsetSection

, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ); fn capacity(&self) -> usize; @@ -29,7 +29,7 @@ impl BevelJoinBuilder { fn join( s0: &OffsetSection

, s1: &OffsetSection

, - _adapter: &FloatPointAdapter

, + _adapter: &FloatPointAdapter, segments: &mut Vec>, ) { debug_assert_ne!(s0.b_top, s1.a_top, "must be validated before"); @@ -43,7 +43,7 @@ impl JoinBuilder

for BevelJoinBuilder { &self, s0: &OffsetSection

, s1: &OffsetSection

, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { Self::join(s0, s1, adapter, segments); @@ -96,7 +96,7 @@ impl JoinBuilder

for MiterJoinBuilder { &self, s0: &OffsetSection

, s1: &OffsetSection

, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { let ia = s0.b_top; @@ -196,7 +196,7 @@ impl JoinBuilder

for RoundJoinBuilder { &self, s0: &OffsetSection

, s1: &OffsetSection

, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { let dot_product = FloatPointMath::dot_product(&s0.dir, &s1.dir); diff --git a/iOverlay/src/mesh/outline/offset.rs b/iOverlay/src/mesh/outline/offset.rs index e8215e3..de77231 100644 --- a/iOverlay/src/mesh/outline/offset.rs +++ b/iOverlay/src/mesh/outline/offset.rs @@ -213,7 +213,7 @@ where struct OutlineSolver { outer_builder: OutlineBuilder

, inner_builder: OutlineBuilder

, - adapter: FloatPointAdapter

, + adapter: FloatPointAdapter, points_count: usize, } @@ -260,13 +260,7 @@ impl OutlineSolver

{ fn apply_scale(&mut self, scale: f64) -> Result<(), FixedScaleOverlayError> { let s = P::Scalar::from_float(scale); - if self.adapter.dir_scale < s { - return Err(FixedScaleOverlayError::ScaleTooLarge); - } - - self.adapter.dir_scale = s; - self.adapter.inv_scale = P::Scalar::from_float(1.0 / scale); - + self.adapter = FloatPointAdapter::try_with_scale(self.adapter.rect().clone(), s)?; Ok(()) } diff --git a/iOverlay/src/mesh/outline/uniq_iter.rs b/iOverlay/src/mesh/outline/uniq_iter.rs index 82bdd82..3026da6 100644 --- a/iOverlay/src/mesh/outline/uniq_iter.rs +++ b/iOverlay/src/mesh/outline/uniq_iter.rs @@ -1,4 +1,6 @@ use core::iter::Chain; +use i_float::int::number::int::IntNumber; +use i_float::int::number::wide_int::WideIntNumber; use i_float::int::point::IntPoint; pub(super) struct UniqueSegment { @@ -87,17 +89,17 @@ where } #[inline] -fn include_point(p0: IntPoint, p1: IntPoint, p2: IntPoint) -> bool { - let a = p1.subtract(p0); - let b = p1.subtract(p2); +fn include_point(p0: IntPoint, p1: IntPoint, p2: IntPoint) -> bool { + let a = p1 - p0; + let b = p1 - p2; - if a.cross_product(b) != 0 { + if a.cross_product(b) != I::Wide::ZERO { // not collinear return true; } // collinear – keep only if we keep going opposite direction - a.dot_product(b) > 0 + a.dot_product(b) > I::Wide::ZERO } #[cfg(test)] mod tests { diff --git a/iOverlay/src/mesh/stroke/builder.rs b/iOverlay/src/mesh/stroke/builder.rs index 5489573..ad396ce 100644 --- a/iOverlay/src/mesh/stroke/builder.rs +++ b/iOverlay/src/mesh/stroke/builder.rs @@ -15,7 +15,7 @@ trait StrokeBuild { &self, path: &[P], is_closed_path: bool, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ); @@ -70,7 +70,7 @@ impl StrokeBuilder

{ &self, path: &[P], is_closed_path: bool, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { self.builder.build(path, is_closed_path, adapter, segments); @@ -93,7 +93,7 @@ impl, P: FloatPointCompatible> StrokeBuild

for Builder, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { if is_closed_path { @@ -126,7 +126,7 @@ impl, P: FloatPointCompatible> Builder { fn open_segments( &self, path: &[P], - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { // build segments only from points which are not equal in int space @@ -179,7 +179,7 @@ impl, P: FloatPointCompatible> Builder { fn closed_segments( &self, path: &[P], - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { if path.len() < 2 { @@ -212,7 +212,12 @@ impl, P: FloatPointCompatible> Builder { } #[inline] - fn next_unique_point(start: usize, index: usize, path: &[P], adapter: &FloatPointAdapter

) -> usize { + fn next_unique_point( + start: usize, + index: usize, + path: &[P], + adapter: &FloatPointAdapter, + ) -> usize { let a = adapter.float_to_int(&path[start]); for (j, p) in path.iter().enumerate().skip(index) { let b = adapter.float_to_int(p); diff --git a/iOverlay/src/mesh/stroke/builder_cap.rs b/iOverlay/src/mesh/stroke/builder_cap.rs index 12a6732..bfff84f 100644 --- a/iOverlay/src/mesh/stroke/builder_cap.rs +++ b/iOverlay/src/mesh/stroke/builder_cap.rs @@ -69,7 +69,7 @@ impl CapBuilder

{ pub(super) fn add_to_start( &self, section: &Section

, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { let mut a = adapter.float_to_int(§ion.a_top); @@ -91,7 +91,7 @@ impl CapBuilder

{ pub(super) fn add_to_end( &self, section: &Section

, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { let mut a = adapter.float_to_int(§ion.b_bot); diff --git a/iOverlay/src/mesh/stroke/builder_join.rs b/iOverlay/src/mesh/stroke/builder_join.rs index b3e0961..42ff5ea 100644 --- a/iOverlay/src/mesh/stroke/builder_join.rs +++ b/iOverlay/src/mesh/stroke/builder_join.rs @@ -15,7 +15,7 @@ pub(super) trait JoinBuilder { &self, s0: &Section

, s1: &Section

, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ); fn capacity(&self) -> usize; @@ -29,7 +29,7 @@ impl BevelJoinBuilder { fn join_top( s0: &Section

, s1: &Section

, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { Self::add_segment(&s0.b_top, &s1.a_top, adapter, segments); @@ -39,7 +39,7 @@ impl BevelJoinBuilder { fn join_bot( s0: &Section

, s1: &Section

, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { Self::add_segment(&s1.a_bot, &s0.b_bot, adapter, segments); @@ -49,7 +49,7 @@ impl BevelJoinBuilder { fn add_segment( a: &P, b: &P, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { let ia = adapter.float_to_int(a); @@ -66,7 +66,7 @@ impl JoinBuilder

for BevelJoinBuilder { &self, s0: &Section

, s1: &Section

, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { Self::join_top(s0, s1, adapter, segments); @@ -122,7 +122,7 @@ impl JoinBuilder

for MiterJoinBuilder { &self, s0: &Section

, s1: &Section

, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { let cross_product = FloatPointMath::cross_product(&s0.dir, &s1.dir); @@ -242,7 +242,7 @@ impl JoinBuilder

for RoundJoinBuilder { &self, s0: &Section

, s1: &Section

, - adapter: &FloatPointAdapter

, + adapter: &FloatPointAdapter, segments: &mut Vec>, ) { let dot_product = FloatPointMath::dot_product(&s0.dir, &s1.dir); diff --git a/iOverlay/src/mesh/stroke/offset.rs b/iOverlay/src/mesh/stroke/offset.rs index 768e9f7..72fe160 100644 --- a/iOverlay/src/mesh/stroke/offset.rs +++ b/iOverlay/src/mesh/stroke/offset.rs @@ -242,7 +242,7 @@ where struct StrokeSolver { r: P::Scalar, builder: StrokeBuilder

, - adapter: FloatPointAdapter

, + adapter: FloatPointAdapter, paths_count: usize, points_count: usize, } @@ -278,14 +278,7 @@ impl StrokeSolver

{ } fn apply_scale(&mut self, scale: P::Scalar) -> Result<(), FixedScaleOverlayError> { - let s = FixedScaleOverlayError::validate_scale(scale)?; - if self.adapter.dir_scale < scale { - return Err(FixedScaleOverlayError::ScaleTooLarge); - } - - self.adapter.dir_scale = scale; - self.adapter.inv_scale = P::Scalar::from_float(1.0 / s); - + self.adapter = FloatPointAdapter::try_with_scale(self.adapter.rect().clone(), scale)?; Ok(()) } @@ -295,7 +288,7 @@ impl StrokeSolver

{ is_closed_path: bool, options: OverlayOptions, ) -> Shapes

{ - let ir = self.adapter.len_float_to_int(self.r).abs(); + let ir = self.adapter.round_len_to_int(self.r).abs(); if ir <= 1 { // offset is too small return vec![]; @@ -336,7 +329,7 @@ impl StrokeSolver

{ options: OverlayOptions, output: &mut FloatFlatContoursBuffer

, ) { - let ir = self.adapter.len_float_to_int(self.r).abs(); + let ir = self.adapter.round_len_to_int(self.r).abs(); if ir <= 1 { // offset is too small output.clear_and_reserve(0, 0); diff --git a/iOverlay/src/mesh/stroke/section.rs b/iOverlay/src/mesh/stroke/section.rs index 2fcbdf2..9f17f03 100644 --- a/iOverlay/src/mesh/stroke/section.rs +++ b/iOverlay/src/mesh/stroke/section.rs @@ -41,11 +41,11 @@ impl Section

{ } pub(crate) trait SectionToSegment { - fn add_section(&mut self, section: &Section

, adapter: &FloatPointAdapter

); + fn add_section(&mut self, section: &Section

, adapter: &FloatPointAdapter); } impl SectionToSegment

for Vec> { - fn add_section(&mut self, section: &Section

, adapter: &FloatPointAdapter

) { + fn add_section(&mut self, section: &Section

, adapter: &FloatPointAdapter) { let a_top = adapter.float_to_int(§ion.a_top); let b_top = adapter.float_to_int(§ion.b_top); let a_bot = adapter.float_to_int(§ion.a_bot); diff --git a/iOverlay/src/segm/build.rs b/iOverlay/src/segm/build.rs index 4b08cb5..512e15c 100644 --- a/iOverlay/src/segm/build.rs +++ b/iOverlay/src/segm/build.rs @@ -4,36 +4,38 @@ use crate::geom::x_segment::XSegment; use crate::segm::segment::Segment; use crate::segm::winding::WindingCount; use alloc::vec::Vec; +use i_float::int::number::int::IntNumber; +use i_float::int::number::wide_int::WideIntNumber; use i_float::int::point::IntPoint; -pub(crate) trait BuildSegments { - fn append_path_iter>( +pub(crate) trait BuildSegments { + fn append_path_iter>>( &mut self, - iter: I, + iter: It, shape_type: ShapeType, keep_same_line_points: bool, ) -> bool; } -impl BuildSegments for Vec> { +impl BuildSegments for Vec> { #[inline] - fn append_path_iter>( + fn append_path_iter>>( &mut self, - iter: I, + iter: It, shape_type: ShapeType, keep_same_line_points: bool, ) -> bool { if keep_same_line_points { - build_segments_with_filter::(self, iter, shape_type) + build_segments_with_filter::(self, iter, shape_type) } else { - build_segments_with_filter::(self, iter, shape_type) + build_segments_with_filter::(self, iter, shape_type) } } } -fn build_segments_with_filter, C: WindingCount>( - segments: &mut Vec>, - mut iter: I, +fn build_segments_with_filter, It: Iterator>, C: WindingCount>( + segments: &mut Vec>, + mut iter: It, shape_type: ShapeType, ) -> bool { // our goal add all not degenerate segments @@ -88,41 +90,41 @@ fn build_segments_with_filter, C: W filtered } -trait PointFilter { - fn include_point(a: IntPoint, b: IntPoint, c: IntPoint) -> bool; +trait PointFilter { + fn include_point(a: IntPoint, b: IntPoint, c: IntPoint) -> bool; } struct DropOppositeCollinear; struct DropCollinear; -impl PointFilter for DropOppositeCollinear { +impl PointFilter for DropOppositeCollinear { #[inline] - fn include_point(p0: IntPoint, p1: IntPoint, p2: IntPoint) -> bool { - let a = p1.subtract(p0); - let b = p1.subtract(p2); + fn include_point(p0: IntPoint, p1: IntPoint, p2: IntPoint) -> bool { + let a = p1 - p0; + let b = p1 - p2; - if a.cross_product(b) != 0 { + if a.cross_product(b) != I::Wide::ZERO { // not collinear return true; } // collinear – keep only if we keep going same direction - a.dot_product(b) < 0 + a.dot_product(b) < I::Wide::ZERO } } -impl PointFilter for DropCollinear { +impl PointFilter for DropCollinear { #[inline] - fn include_point(p0: IntPoint, p1: IntPoint, p2: IntPoint) -> bool { - let a = p1.subtract(p0); - let b = p1.subtract(p2); - a.cross_product(b) != 0 + fn include_point(p0: IntPoint, p1: IntPoint, p2: IntPoint) -> bool { + let a = p1 - p0; + let b = p1 - p2; + a.cross_product(b) != I::Wide::ZERO } } -impl Segment { +impl Segment { #[inline] - pub(crate) fn with_ab(p0: IntPoint, p1: IntPoint, direct: C, invert: C) -> Self { + pub(crate) fn with_ab(p0: IntPoint, p1: IntPoint, direct: C, invert: C) -> Self { if p0 < p1 { Self { x_segment: XSegment { a: p0, b: p1 }, @@ -139,9 +141,9 @@ impl Segment { } } -impl> Segment { +impl> Segment { #[inline] - pub(crate) fn with_ab_and_data(p0: IntPoint, p1: IntPoint, direct: C, invert: C, data: D) -> Self { + pub(crate) fn with_ab_and_data(p0: IntPoint, p1: IntPoint, direct: C, invert: C, data: D) -> Self { if p0 < p1 { Self { x_segment: XSegment { a: p0, b: p1 }, diff --git a/iOverlay/src/segm/segment.rs b/iOverlay/src/segm/segment.rs index e55113b..5fc2ef1 100644 --- a/iOverlay/src/segm/segment.rs +++ b/iOverlay/src/segm/segment.rs @@ -2,6 +2,7 @@ use crate::core::edge_data::OverlayEdgeData; use crate::geom::x_segment::XSegment; use crate::segm::winding::WindingCount; use core::cmp::Ordering; +use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; pub type SegmentFill = u8; @@ -21,23 +22,23 @@ pub const BOTH_BOTTOM: SegmentFill = SUBJ_BOTTOM | CLIP_BOTTOM; pub const ALL: SegmentFill = SUBJ_BOTH | CLIP_BOTH; #[derive(Debug, Clone, Copy)] -pub(crate) struct Segment { - pub(crate) x_segment: XSegment, +pub(crate) struct Segment { + pub(crate) x_segment: XSegment, pub(crate) count: C, pub(crate) data: D, } -impl Segment { +impl Segment { #[inline(always)] #[allow(dead_code)] - pub(crate) fn create_and_validate(a: IntPoint, b: IntPoint, count: C) -> Self { + pub(crate) fn create_and_validate(a: IntPoint, b: IntPoint, count: C) -> Self { Self::create_and_validate_with_data(a, b, count, ()) } } -impl> Segment { +impl, I: IntNumber> Segment { #[inline(always)] - pub(crate) fn create_and_validate_with_data(a: IntPoint, b: IntPoint, count: C, data: D) -> Self { + pub(crate) fn create_and_validate_with_data(a: IntPoint, b: IntPoint, count: C, data: D) -> Self { if a < b { Self { x_segment: XSegment { a, b }, @@ -54,23 +55,23 @@ impl> Segment { } } -impl PartialEq for Segment { +impl PartialEq for Segment { #[inline(always)] fn eq(&self, other: &Self) -> bool { self.x_segment == other.x_segment } } -impl Eq for Segment {} +impl Eq for Segment {} -impl PartialOrd for Segment { +impl PartialOrd for Segment { #[inline(always)] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for Segment { +impl Ord for Segment { #[inline(always)] fn cmp(&self, other: &Self) -> Ordering { self.x_segment.cmp(&other.x_segment) diff --git a/iOverlay/src/split/cross_solver.rs b/iOverlay/src/split/cross_solver.rs index 7b0c544..07b755c 100644 --- a/iOverlay/src/split/cross_solver.rs +++ b/iOverlay/src/split/cross_solver.rs @@ -1,8 +1,11 @@ use crate::geom::x_segment::XSegment; -use i_float::fix_vec::FixVec; +use core::marker::PhantomData; +use i_float::int::number::int::IntNumber; +use i_float::int::number::product_uint::UIntProduct; +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_float::u128::UInt128; pub(super) type CollinearMask = u8; @@ -52,8 +55,8 @@ impl EndMask for CollinearMask { } } -pub(super) struct CrossResult { - pub(super) point: IntPoint, +pub(super) struct CrossResult { + pub(super) point: IntPoint, pub(super) cross_type: CrossType, pub(super) is_round: bool, } @@ -65,19 +68,28 @@ pub(super) enum CrossType { Overlay, } -pub(super) struct CrossSolver {} +pub(super) struct CrossSolver { + phantom_data: PhantomData, +} + +impl CrossSolver { + pub(super) fn cross( + target: &XSegment, + other: &XSegment, + radius: I::Wide, + ) -> Option> { + let a0b0a1 = Triangle::clock_direction(target.a, target.b, other.a); + let a0b0b1 = Triangle::clock_direction(target.a, target.b, other.b); -impl CrossSolver { - pub(super) fn cross(target: &XSegment, other: &XSegment, radius: i64) -> Option { - let a0b0a1 = Triangle::clock_direction_point(target.a, target.b, other.a); - let a0b0b1 = Triangle::clock_direction_point(target.a, target.b, other.b); + let a1b1a0 = Triangle::clock_direction(other.a, other.b, target.a); + let a1b1b0 = Triangle::clock_direction(other.a, other.b, target.b); - let a1b1a0 = Triangle::clock_direction_point(other.a, other.b, target.a); - let a1b1b0 = Triangle::clock_direction_point(other.a, other.b, target.b); + let one = I::Wide::ONE; - let s = (1 & (a0b0a1 + 1)) + (1 & (a0b0b1 + 1)) + (1 & (a1b1a0 + 1)) + (1 & (a1b1b0 + 1)); + let s = + (one & (a0b0a1 + one)) + (one & (a0b0b1 + one)) + (one & (a1b1a0 + one)) + (one & (a1b1b0 + one)); - if s == 4 { + if s == I::Wide::FOUR { return Some(CrossResult { point: IntPoint::ZERO, cross_type: CrossType::Overlay, @@ -87,24 +99,24 @@ impl CrossSolver { let is_not_cross = a0b0a1 == a0b0b1 || a1b1a0 == a1b1b0; - if s > 1 || is_not_cross { + if s > I::Wide::ONE || is_not_cross { return None; } - if s != 0 { - return if a0b0a1 == 0 { + if s != I::Wide::ZERO { + return if a0b0a1 == I::Wide::ZERO { Some(CrossResult { point: other.a, cross_type: CrossType::OtherEnd, is_round: false, }) - } else if a0b0b1 == 0 { + } else if a0b0b1 == I::Wide::ZERO { Some(CrossResult { point: other.b, cross_type: CrossType::OtherEnd, is_round: false, }) - } else if a1b1a0 == 0 { + } else if a1b1a0 == I::Wide::ZERO { Some(CrossResult { point: target.a, cross_type: CrossType::TargetEnd, @@ -122,11 +134,11 @@ impl CrossSolver { Self::middle_cross(target, other, radius) } - pub(super) fn collinear(target: &XSegment, other: &XSegment) -> CollinearMask { - let a0 = FixVec::new_point(target.a); - let b0 = FixVec::new_point(target.b); - let a1 = FixVec::new_point(other.a); - let b1 = FixVec::new_point(other.b); + pub(super) fn collinear(target: &XSegment, other: &XSegment) -> CollinearMask { + let a0 = target.a; + let b0 = target.b; + let a1 = other.a; + let b1 = other.b; let v1 = b1 - a1; @@ -140,19 +152,19 @@ impl CrossSolver { let ba1 = -ab0; let bb1 = -bb0; - let is_target_a = aa0 == -ab0 && aa0 != 0; - let is_target_b = ba0 == -bb0 && ba0 != 0; + let is_target_a = aa0 == -ab0 && aa0 != I::Wide::ZERO; + let is_target_b = ba0 == -bb0 && ba0 != I::Wide::ZERO; - let is_other_a = aa1 == -ab1 && aa1 != 0; - let is_other_b = ba1 == -bb1 && ba1 != 0; + let is_other_a = aa1 == -ab1 && aa1 != I::Wide::ZERO; + let is_other_b = ba1 == -bb1 && ba1 != I::Wide::ZERO; CollinearMask::new(is_target_a, is_target_b, is_other_a, is_other_b) } - fn middle_cross(target: &XSegment, other: &XSegment, radius: i64) -> Option { + fn middle_cross(target: &XSegment, other: &XSegment, radius: I::Wide) -> Option> { let p = CrossSolver::cross_point(target, other); - if Triangle::is_line_point(target.a, p, target.b) && Triangle::is_line_point(other.a, p, other.b) { + if Triangle::is_line(target.a, p, target.b) && Triangle::is_line(other.a, p, other.b) { return Some(CrossResult { point: p, cross_type: CrossType::Pure, @@ -176,7 +188,7 @@ impl CrossSolver { if r0 <= r1 { let p = if ra0 < rb0 { target.a } else { target.b }; // ignore if it's a clean point - if Triangle::is_not_line_point(other.a, p, other.b) { + if Triangle::is_not_line(other.a, p, other.b) { return Some(CrossResult { point: p, cross_type: CrossType::TargetEnd, @@ -187,7 +199,7 @@ impl CrossSolver { let p = if ra1 < rb1 { other.a } else { other.b }; // ignore if it's a clean point - if Triangle::is_not_line_point(target.a, p, target.b) { + if Triangle::is_not_line(target.a, p, target.b) { return Some(CrossResult { point: p, cross_type: CrossType::OtherEnd, @@ -204,7 +216,7 @@ impl CrossSolver { }) } - fn cross_point(target: &XSegment, other: &XSegment) -> IntPoint { + fn cross_point(target: &XSegment, other: &XSegment) -> IntPoint { // edges are not parallel // any abs(x) and abs(y) < 2^30 // The result must be < 2^30 @@ -230,25 +242,25 @@ impl CrossSolver { // let x = kx / divider // let y = ky / divider // - // return FixVec(x, y) + // return IntPoint(x, y) // offset approach // move all picture by -a0. Point a0 will be equal (0, 0) // move a0.x to 0 // move all by a0.x - let a0x = target.a.x as i64; - let a0y = target.a.y as i64; + let a0x = target.a.x.wide(); + let a0y = target.a.y.wide(); - let a1x = target.b.x as i64 - a0x; - let b0x = other.a.x as i64 - a0x; - let b1x = other.b.x as i64 - a0x; + let a1x = target.b.x.wide() - a0x; + let b0x = other.a.x.wide() - a0x; + let b1x = other.b.x.wide() - a0x; // move a0.y to 0 // move all by a0.y - let a1y = target.b.y as i64 - a0y; - let b0y = other.a.y as i64 - a0y; - let b1y = other.b.y as i64 - a0y; + let a1y = target.b.y.wide() - a0y; + let b0y = other.a.y.wide() - a0y; + let b1y = other.b.y.wide() - a0y; let dy_b = b0y - b1y; let dx_b = b0x - b1x; @@ -256,18 +268,18 @@ impl CrossSolver { // let xyA = 0 let xy_b = b0x * b1y - b0y * b1x; - let x0: i64; - let y0: i64; + let x0: I::Wide; + let y0: I::Wide; // a1y and a1x cannot be zero simultaneously, because we will get edge a0<>a1 zero length and it is impossible - if a1x == 0 { + if a1x == I::Wide::ZERO { // dxB is not zero because it will be parallel case and it's impossible - x0 = 0; + x0 = I::Wide::ZERO; y0 = xy_b / dx_b; - } else if a1y == 0 { + } else if a1y == I::Wide::ZERO { // dyB is not zero because it will be parallel case and it's impossible - y0 = 0; + y0 = I::Wide::ZERO; x0 = -xy_b / dy_b; } else { // divider @@ -282,78 +294,36 @@ impl CrossSolver { let uxy_b = xy_b.unsigned_abs(); let udiv = div.unsigned_abs(); - let kx = UInt128::multiply(a1x.unsigned_abs(), uxy_b); - let ky = UInt128::multiply(a1y.unsigned_abs(), uxy_b); + let kx = <::UInt as UIntNumber>::Product::multiply( + a1x.unsigned_abs(), + uxy_b, + ); + let ky = <::UInt as UIntNumber>::Product::multiply( + a1y.unsigned_abs(), + uxy_b, + ); let ux = kx.divide_with_rounding(udiv); let uy = ky.divide_with_rounding(udiv); - // get i64 bit result - x0 = sx * ux as i64; - y0 = sy * uy as i64; + x0 = sx * I::Wide::from_uint(ux); + y0 = sy * I::Wide::from_uint(uy); } - let x = (x0 + a0x) as i32; - let y = (y0 + a0y) as i32; + let x = I::from_wide(x0 + a0x); + let y = I::from_wide(y0 + a0y); IntPoint::new(x, y) } } -const LAST_BIT_INDEX: usize = 63; - -trait RoundDivide { - fn divide_with_rounding(&self, divisor: u64) -> u64; -} - -impl RoundDivide for UInt128 { - fn divide_with_rounding(&self, divisor: u64) -> u64 { - if self.high == 0 { - let result = self.low / divisor; - let remainder = self.low - result * divisor; - return if remainder >= (divisor + 1) >> 1 { - result + 1 - } else { - result - }; - } - - let dn = divisor.leading_zeros(); - let norm_divisor = divisor << dn; - let mut norm_dividend_high = (self.high << dn) | (self.low >> (u64::BITS - dn)); - let mut norm_dividend_low = self.low << dn; - - let mut quotient = 0; - let one = 1 << LAST_BIT_INDEX; - - for _ in 0..u64::BITS { - let bit = (norm_dividend_high & one) != 0; - norm_dividend_high = (norm_dividend_high << 1) | (norm_dividend_low >> LAST_BIT_INDEX); - norm_dividend_low <<= 1; - quotient <<= 1; - if norm_dividend_high >= norm_divisor || bit { - norm_dividend_high = norm_dividend_high.wrapping_sub(norm_divisor); - quotient |= 1; - } - } - - // Check remainder for rounding - let remainder = (norm_dividend_high << (u64::BITS - dn)) | (norm_dividend_low >> dn); - if remainder >= (divisor + 1) >> 1 { - quotient += 1; - } - - quotient - } -} - #[cfg(test)] mod tests { use crate::geom::x_segment::XSegment; use crate::split::cross_solver::{CrossSolver, CrossType}; use i_float::int::point::IntPoint; - impl XSegment { + impl XSegment { fn new(a: IntPoint, b: IntPoint) -> Self { Self { a, b } } @@ -509,7 +479,7 @@ mod tests { match result.cross_type { CrossType::Pure => { - assert_eq!(IntPoint::new(-1048691, -5244), result.point); + assert_eq!(IntPoint::new(-1048691, -5243), result.point); } _ => { panic!("Fail cross result"); diff --git a/iOverlay/src/split/grid_layout.rs b/iOverlay/src/split/grid_layout.rs index fc47f1c..b821b83 100644 --- a/iOverlay/src/split/grid_layout.rs +++ b/iOverlay/src/split/grid_layout.rs @@ -1134,10 +1134,10 @@ mod tests { let p2 = IntPoint::new(rect.min_x, rect.max_y); let p3 = IntPoint::new(rect.max_x, rect.max_y); - assert!(Triangle::is_cw_or_line_point(p0, segment.a, segment.b)); - assert!(Triangle::is_cw_or_line_point(p1, segment.a, segment.b)); - assert!(Triangle::is_cw_or_line_point(p2, segment.b, segment.a)); - assert!(Triangle::is_cw_or_line_point(p3, segment.b, segment.a)); + assert!(Triangle::is_cw_or_line(p0, segment.a, segment.b)); + assert!(Triangle::is_cw_or_line(p1, segment.a, segment.b)); + assert!(Triangle::is_cw_or_line(p2, segment.b, segment.a)); + assert!(Triangle::is_cw_or_line(p3, segment.b, segment.a)); } } @@ -1551,10 +1551,10 @@ mod tests { let p2 = IntPoint::new(rect.min_x, rect.max_y); let p3 = IntPoint::new(rect.max_x, rect.max_y); - let a0 = Triangle::area_two_point(p0, segment.a, segment.b); - let a1 = Triangle::area_two_point(p1, segment.a, segment.b); - let a2 = Triangle::area_two_point(p2, segment.b, segment.a); - let a3 = Triangle::area_two_point(p3, segment.b, segment.a); + let a0 = Triangle::area_two(p0, segment.a, segment.b); + let a1 = Triangle::area_two(p1, segment.a, segment.b); + let a2 = Triangle::area_two(p2, segment.b, segment.a); + let a3 = Triangle::area_two(p3, segment.b, segment.a); assert!(a0 >= 0); assert!(a1 >= 0); diff --git a/iOverlay/src/vector/edge.rs b/iOverlay/src/vector/edge.rs index 22e46d5..4e547a1 100644 --- a/iOverlay/src/vector/edge.rs +++ b/iOverlay/src/vector/edge.rs @@ -1,13 +1,14 @@ use crate::core::edge_data::OverlayEdgeData; use alloc::vec::Vec; +use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; use i_shape::int::path::IntPath; pub type SideFill = u8; -pub type DataVectorPath = Vec>; -pub type DataVectorShape = Vec>; -pub type VectorPath = DataVectorPath<()>; -pub type VectorShape = DataVectorShape<()>; +pub type DataVectorPath = Vec>; +pub type DataVectorShape = Vec>; +pub type VectorPath = DataVectorPath<(), I>; +pub type VectorShape = DataVectorShape<(), I>; pub const SUBJ_LEFT: u8 = 0b0001; pub const SUBJ_RIGHT: u8 = 0b0010; @@ -30,15 +31,15 @@ impl Reverse for SideFill { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct DataVectorEdge { - pub a: IntPoint, - pub b: IntPoint, +pub struct DataVectorEdge { + pub a: IntPoint, + pub b: IntPoint, pub fill: SideFill, pub data: D, } -impl DataVectorEdge { - pub(crate) fn new(fill: SideFill, a: IntPoint, b: IntPoint, data: D) -> Self { +impl DataVectorEdge { + pub(crate) fn new(fill: SideFill, a: IntPoint, b: IntPoint, data: D) -> Self { let (fill, data) = if a < b { (fill, data) } else { @@ -49,12 +50,12 @@ impl DataVectorEdge { } } -pub trait ToPath { - fn to_path(&self) -> IntPath; +pub trait ToPath { + fn to_path(&self) -> IntPath; } -impl ToPath for VectorPath { - fn to_path(&self) -> IntPath { +impl ToPath for VectorPath { + fn to_path(&self) -> IntPath { self.iter().map(|e| e.a).collect() } } diff --git a/iOverlay/src/vector/extract.rs b/iOverlay/src/vector/extract.rs index 641a82a..d793790 100644 --- a/iOverlay/src/vector/extract.rs +++ b/iOverlay/src/vector/extract.rs @@ -196,12 +196,27 @@ impl StartVectorPathData { } trait JoinHoles { - fn join_sorted_holes(&mut self, holes: Vec>, anchors: Vec, clockwise: bool); - fn scan_join(&mut self, holes: Vec>, hole_segments: Vec, clockwise: bool); + fn join_sorted_holes( + &mut self, + holes: Vec>, + anchors: Vec, + clockwise: bool, + ); + fn scan_join( + &mut self, + holes: Vec>, + hole_segments: Vec, + clockwise: bool, + ); } impl JoinHoles for Vec> { - fn join_sorted_holes(&mut self, holes: Vec>, anchors: Vec, clockwise: bool) { + fn join_sorted_holes( + &mut self, + holes: Vec>, + anchors: Vec, + clockwise: bool, + ) { if self.is_empty() || holes.is_empty() { return; } @@ -218,7 +233,12 @@ impl JoinHoles for Vec> { self.scan_join(holes, anchors, clockwise); } - fn scan_join(&mut self, holes: Vec>, hole_segments: Vec, clockwise: bool) { + fn scan_join( + &mut self, + holes: Vec>, + hole_segments: Vec, + clockwise: bool, + ) { let x_min = hole_segments[0].v_segment.a.x; let x_max = hole_segments[hole_segments.len() - 1].v_segment.a.x; diff --git a/iOverlay/src/vector/simplify.rs b/iOverlay/src/vector/simplify.rs index 474a6d1..075c9c8 100644 --- a/iOverlay/src/vector/simplify.rs +++ b/iOverlay/src/vector/simplify.rs @@ -3,6 +3,7 @@ use crate::vector::edge::{DataVectorEdge, DataVectorPath, DataVectorShape}; use alloc::vec; use alloc::vec::Vec; use i_float::int::point::IntPoint; +use i_float::int::vector::IntVector; /// Simplifies vector contours by removing collinear points when possible. pub(super) trait VectorSimplify { @@ -160,9 +161,7 @@ impl VectorSimpleContour for [DataVectorEdge] { let p1 = self[node.index].b; let p2 = self[node.next].b; - if p1.subtract(p0).cross_product(p2.subtract(p1)) == 0 - && self[node.index].data == self[node.next].data - { + if (p1 - p0).cross_product(p2 - p1) == 0 && self[node.index].data == self[node.next].data { n -= 1; if n < 3 { return None; @@ -250,7 +249,7 @@ impl VectorSimpleShape for [DataVectorPath] { } #[inline] -fn direction(edge: &DataVectorEdge) -> IntPoint { +fn direction(edge: &DataVectorEdge) -> IntVector { edge.b - edge.a } diff --git a/iOverlay/tests/boolean/test_135.json b/iOverlay/tests/boolean/test_135.json index 123f78c..17312cf 100644 --- a/iOverlay/tests/boolean/test_135.json +++ b/iOverlay/tests/boolean/test_135.json @@ -2,11 +2,11 @@ "fillRule": 1, "subjPaths": [[[-368243454, -716798528], [216742014, -234153472], [161596286, -387121984], [161210942, -390975072], [271787262, -224556880], [368243454, -588348048]], [[-206371890, 284626496], [102168414, 680240192], [238020158, 716798528], [292483838, 355651392], [161731326, 385867967]], [[183615526, 693260116], [112710830, 677451824], [165983866, 395695016]], [[231794622, 706732224], [225376830, 598504960], [228629310, 596974400]]], "clipPaths": [[[209943806, -236444448], [214486910, -227461600], [237766206, -239235072], [277238334, -271024672], [270924350, -278864608], [232277566, -247739648]]], - "subject": [[[[[-368243454, -716798528], [212436980, -237705361], [216742014, -234153472], [214995134, -238999135], [161596286, -387121984], [161210942, -390975072], [247980463, -260386291], [253574782, -251966799], [271787262, -224556880], [368243454, -588348048]]], [[[-206371890, 284626496], [102168414, 680240192], [238020158, 716798528], [292483838, 355651392], [161731326, 385867967]], [[183615526, 693260116], [112710830, 677451824], [165983866, 395695016]], [[231794622, 706732224], [225376830, 598504960], [228629310, 596974400]]]]], - "clip": [[[[[209943806, -236444448], [214486910, -227461600], [237766206, -239235072], [253574782, -251966799], [277238334, -271024672], [270924350, -278864608], [247980463, -260386291], [232277566, -247739648], [214995134, -238999135], [212436980, -237705361]]]]], - "union": [[[[[-368243454, -716798528], [212436980, -237705361], [209943806, -236444448], [214486910, -227461600], [237766206, -239235072], [253574782, -251966799], [271787262, -224556880], [368243454, -588348048]], [[161596286, -387121984], [161210942, -390975072], [247980463, -260386291], [232277566, -247739648], [214995134, -238999135]]], [[[-206371890, 284626496], [102168414, 680240192], [238020158, 716798528], [292483838, 355651392], [161731326, 385867967]], [[183615526, 693260116], [112710830, 677451824], [165983866, 395695016]], [[231794622, 706732224], [225376830, 598504960], [228629310, 596974400]]]]], - "intersect": [[[[[212436980, -237705361], [216742014, -234153472], [214995134, -238999135]]], [[[247980463, -260386291], [253574782, -251966799], [277238334, -271024672], [270924350, -278864608]]]]], - "difference": [[[[[-368243454, -716798528], [212436980, -237705361], [214995134, -238999135], [161596286, -387121984], [161210942, -390975072], [247980463, -260386291], [270924350, -278864608], [277238334, -271024672], [253574782, -251966799], [271787262, -224556880], [368243454, -588348048]]], [[[-206371890, 284626496], [102168414, 680240192], [238020158, 716798528], [292483838, 355651392], [161731326, 385867967]], [[183615526, 693260116], [112710830, 677451824], [165983866, 395695016]], [[231794622, 706732224], [225376830, 598504960], [228629310, 596974400]]]]], - "inverseDifference": [[[[[209943806, -236444448], [214486910, -227461600], [237766206, -239235072], [253574782, -251966799], [247980463, -260386291], [232277566, -247739648], [214995134, -238999135], [216742014, -234153472], [212436980, -237705361]]]]], - "xor": [[[[[-368243454, -716798528], [212436980, -237705361], [214995134, -238999135], [161596286, -387121984], [161210942, -390975072], [247980463, -260386291], [270924350, -278864608], [277238334, -271024672], [253574782, -251966799], [271787262, -224556880], [368243454, -588348048]]], [[[-206371890, 284626496], [102168414, 680240192], [238020158, 716798528], [292483838, 355651392], [161731326, 385867967]], [[183615526, 693260116], [112710830, 677451824], [165983866, 395695016]], [[231794622, 706732224], [225376830, 598504960], [228629310, 596974400]]], [[[209943806, -236444448], [214486910, -227461600], [237766206, -239235072], [253574782, -251966799], [247980463, -260386291], [232277566, -247739648], [214995134, -238999135], [216742014, -234153472], [212436980, -237705361]]]]] + "subject": [[[[[-368243454, -716798528], [212436981, -237705360], [216742014, -234153472], [214995134, -238999135], [161596286, -387121984], [161210942, -390975072], [247980464, -260386290], [253574783, -251966798], [271787262, -224556880], [368243454, -588348048]]], [[[-206371890, 284626496], [102168414, 680240192], [238020158, 716798528], [292483838, 355651392], [161731326, 385867967]], [[183615526, 693260116], [112710830, 677451824], [165983866, 395695016]], [[231794622, 706732224], [225376830, 598504960], [228629310, 596974400]]]]], + "clip": [[[[[209943806, -236444448], [214486910, -227461600], [237766206, -239235072], [253574783, -251966798], [277238334, -271024672], [270924350, -278864608], [247980464, -260386290], [232277566, -247739648], [214995134, -238999135], [212436981, -237705360]]]]], + "union": [[[[[-368243454, -716798528], [212436981, -237705360], [209943806, -236444448], [214486910, -227461600], [237766206, -239235072], [253574783, -251966798], [271787262, -224556880], [368243454, -588348048]], [[161596286, -387121984], [161210942, -390975072], [247980464, -260386290], [232277566, -247739648], [214995134, -238999135]]], [[[-206371890, 284626496], [102168414, 680240192], [238020158, 716798528], [292483838, 355651392], [161731326, 385867967]], [[183615526, 693260116], [112710830, 677451824], [165983866, 395695016]], [[231794622, 706732224], [225376830, 598504960], [228629310, 596974400]]]]], + "intersect": [[[[[212436981, -237705360], [216742014, -234153472], [214995134, -238999135]]], [[[247980464, -260386290], [253574783, -251966798], [277238334, -271024672], [270924350, -278864608]]]]], + "difference": [[[[[-368243454, -716798528], [212436981, -237705360], [214995134, -238999135], [161596286, -387121984], [161210942, -390975072], [247980464, -260386290], [270924350, -278864608], [277238334, -271024672], [253574783, -251966798], [271787262, -224556880], [368243454, -588348048]]], [[[-206371890, 284626496], [102168414, 680240192], [238020158, 716798528], [292483838, 355651392], [161731326, 385867967]], [[183615526, 693260116], [112710830, 677451824], [165983866, 395695016]], [[231794622, 706732224], [225376830, 598504960], [228629310, 596974400]]]]], + "inverseDifference": [[[[[209943806, -236444448], [214486910, -227461600], [237766206, -239235072], [253574783, -251966798], [247980464, -260386290], [232277566, -247739648], [214995134, -238999135], [216742014, -234153472], [212436981, -237705360]]]]], + "xor": [[[[[-368243454, -716798528], [212436981, -237705360], [214995134, -238999135], [161596286, -387121984], [161210942, -390975072], [247980464, -260386290], [270924350, -278864608], [277238334, -271024672], [253574783, -251966798], [271787262, -224556880], [368243454, -588348048]]], [[[-206371890, 284626496], [102168414, 680240192], [238020158, 716798528], [292483838, 355651392], [161731326, 385867967]], [[183615526, 693260116], [112710830, 677451824], [165983866, 395695016]], [[231794622, 706732224], [225376830, 598504960], [228629310, 596974400]]], [[[209943806, -236444448], [214486910, -227461600], [237766206, -239235072], [253574783, -251966798], [247980464, -260386290], [232277566, -247739648], [214995134, -238999135], [216742014, -234153472], [212436981, -237705360]]]]] } \ No newline at end of file From dfddf6788903741d3d37fd233d4306be8832bcf6 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sun, 17 May 2026 16:50:05 +0300 Subject: [PATCH 04/36] fix clippy --- iOverlay/src/core/divide.rs | 2 +- iOverlay/src/core/extract.rs | 2 +- iOverlay/src/core/extract_ogc.rs | 2 +- iOverlay/src/core/overlay.rs | 2 +- iOverlay/src/float/clip.rs | 1 + iOverlay/src/float/scale.rs | 2 +- iOverlay/src/mesh/outline/offset.rs | 32 +++++++++++++-------------- iOverlay/src/split/cross_solver.rs | 12 +++++----- iOverlay/src/split/grid_layout.rs | 6 +++-- iOverlay/src/split/solver_fragment.rs | 6 ++--- iOverlay/src/string/clip.rs | 2 ++ iOverlay/src/string/slice.rs | 2 ++ iOverlay/src/vector/extract.rs | 2 +- iOverlay/tests/direction_tests.rs | 2 ++ iOverlay/tests/dynamic_tests.rs | 30 ++++++++++++------------- iOverlay/tests/float_overlay_tests.rs | 7 ++++++ iOverlay/tests/ocg_tests.rs | 2 ++ iOverlay/tests/overlay_tests.rs | 2 ++ iOverlay/tests/simplify_tests.rs | 2 ++ iOverlay/tests/slice_tests.rs | 2 ++ iOverlay/tests/string_tests.rs | 2 ++ iOverlay/tests/util.rs | 4 ++-- 22 files changed, 76 insertions(+), 50 deletions(-) diff --git a/iOverlay/src/core/divide.rs b/iOverlay/src/core/divide.rs index bd1191a..9cbf99e 100644 --- a/iOverlay/src/core/divide.rs +++ b/iOverlay/src/core/divide.rs @@ -68,7 +68,7 @@ impl ContourDecomposition for IntContour { return None; } - anchors.sort_by(|p0, p1| p0.id.cmp(&p1.id)); + anchors.sort_by_key(|p0| p0.id); let mut contours = Vec::with_capacity((anchors.len() >> 1) + 1); diff --git a/iOverlay/src/core/extract.rs b/iOverlay/src/core/extract.rs index 9339600..c732361 100644 --- a/iOverlay/src/core/extract.rs +++ b/iOverlay/src/core/extract.rs @@ -170,7 +170,7 @@ impl OverlayGraph<'_> { } if !anchors_already_sorted { - anchors.sort_unstable_by(|s0, s1| s0.v_segment.a.cmp(&s1.v_segment.a)); + anchors.sort_unstable_by_key(|s0| s0.v_segment.a); } shapes.join_sorted_holes(holes, anchors, clockwise); diff --git a/iOverlay/src/core/extract_ogc.rs b/iOverlay/src/core/extract_ogc.rs index 0ab1ebc..6ba392d 100644 --- a/iOverlay/src/core/extract_ogc.rs +++ b/iOverlay/src/core/extract_ogc.rs @@ -168,7 +168,7 @@ impl OverlayGraph<'_> { } if !anchors_already_sorted { - anchors.sort_unstable_by(|s0, s1| s0.v_segment.a.cmp(&s1.v_segment.a)); + anchors.sort_unstable_by_key(|s0| s0.v_segment.a); } shapes.join_sorted_holes(holes, anchors, is_main_dir_cw); diff --git a/iOverlay/src/core/overlay.rs b/iOverlay/src/core/overlay.rs index c12e075..afbd7ed 100644 --- a/iOverlay/src/core/overlay.rs +++ b/iOverlay/src/core/overlay.rs @@ -848,7 +848,7 @@ mod tests { fn test_empty_input() { let subj: &[IntContour] = &[]; - let mut overlay = Overlay::with_contours(&subj, &[]); + let mut overlay = Overlay::with_contours(subj, &[]); let result = overlay.overlay(OverlayRule::Subject, FillRule::NonZero); assert_eq!(result.len(), 0); diff --git a/iOverlay/src/float/clip.rs b/iOverlay/src/float/clip.rs index 9f83a6d..e835cea 100644 --- a/iOverlay/src/float/clip.rs +++ b/iOverlay/src/float/clip.rs @@ -89,6 +89,7 @@ where } #[cfg(test)] +#[allow(clippy::items_after_test_module)] mod tests { use crate::core::fill_rule::FillRule; use crate::float::clip::FloatClip; diff --git a/iOverlay/src/float/scale.rs b/iOverlay/src/float/scale.rs index de9a49d..db798bc 100644 --- a/iOverlay/src/float/scale.rs +++ b/iOverlay/src/float/scale.rs @@ -406,7 +406,7 @@ mod tests { let result = FloatOverlay::with_subj_and_clip_fixed_scale(&shapes, &right_bottom_rect, scale); - assert!(!result.is_ok()); + assert!(result.is_err()); } #[test] diff --git a/iOverlay/src/mesh/outline/offset.rs b/iOverlay/src/mesh/outline/offset.rs index de77231..ea6e819 100644 --- a/iOverlay/src/mesh/outline/offset.rs +++ b/iOverlay/src/mesh/outline/offset.rs @@ -1183,25 +1183,25 @@ mod tests { #[test] fn test_real_case_0_simplified() { let main = vec![ - [410_000.0, 5847_000.0], - [413_000.0, 5847_000.0], - [413_000.0, 5850_000.0], - [410_000.0, 5850_000.0], + [410_000.0, 5_847_000.0], + [413_000.0, 5_847_000.0], + [413_000.0, 5_850_000.0], + [410_000.0, 5_850_000.0], ]; let hole = vec![ - [411_294.2500422625, 5848_072.3189725485], - [411_373.9180110125, 5848_124.016970595], - [411_377.22904616874, 5848_118.990969619], - [411_393.0859797625, 5848_020.979983291], - [411_394.7030451922, 5848_005.639040908], - [411_397.4359553484, 5848_003.376955947], - [411_431.1029475359, 5847_937.537966689], - [411_431.2639582781, 5847_933.187991103], - [411_314.8390315203, 5848_005.308962783], - [411_314.5590022234, 5848_009.708010634], - [411_309.3459895281, 5848_009.447024306], - [411_305.3719905047, 5848_010.244997939], + [411_294.2500422625, 5_848_072.318_972_548_5], + [411_373.9180110125, 5_848_124.016_970_595], + [411_377.22904616874, 5_848_118.990_969_619], + [411_393.0859797625, 5_848_020.979_983_291], + [411_394.7030451922, 5_848_005.639_040_908], + [411_397.4359553484, 5_848_003.376_955_947], + [411_431.1029475359, 5_847_937.537_966_689], + [411_431.2639582781, 5_847_933.187_991_103], + [411_314.8390315203, 5_848_005.308_962_783], + [411_314.5590022234, 5_848_009.708_010_634], + [411_309.3459895281, 5_848_009.447_024_306], + [411_305.3719905047, 5_848_010.244_997_939], ]; let shape = vec![main, hole]; diff --git a/iOverlay/src/split/cross_solver.rs b/iOverlay/src/split/cross_solver.rs index 07b755c..455e0ce 100644 --- a/iOverlay/src/split/cross_solver.rs +++ b/iOverlay/src/split/cross_solver.rs @@ -350,7 +350,7 @@ mod tests { #[test] fn test_big_cross_1() { - let s: i32 = 1024_000_000; + let s: i32 = 1_024_000_000; let ea = XSegment::new(IntPoint::new(-s, 0), IntPoint::new(s, 0)); let eb = XSegment::new(IntPoint::new(0, -s), IntPoint::new(0, s)); @@ -369,7 +369,7 @@ mod tests { #[test] fn test_big_cross_2() { - let s: i32 = 1024_000_000; + let s: i32 = 1_024_000_000; let ea = XSegment::new(IntPoint::new(-s, 0), IntPoint::new(s, 0)); let eb = XSegment::new(IntPoint::new(1024, -s), IntPoint::new(1024, s)); @@ -388,7 +388,7 @@ mod tests { #[test] fn test_big_cross_3() { - let s: i32 = 1024_000_000; + let s: i32 = 1_024_000_000; let q: i32 = s / 2; let ea = XSegment::new(IntPoint::new(-s, -s), IntPoint::new(s, s)); @@ -408,7 +408,7 @@ mod tests { #[test] fn test_left_end() { - let s: i32 = 1024_000_000; + let s: i32 = 1_024_000_000; let ea = XSegment::new(IntPoint::new(-s, 0), IntPoint::new(s, 0)); let eb = XSegment::new(IntPoint::new(-s, -s), IntPoint::new(-s, s)); @@ -427,7 +427,7 @@ mod tests { #[test] fn test_right_end() { - let s: i32 = 1024_000_000; + let s: i32 = 1_024_000_000; let ea = XSegment::new(IntPoint::new(-s, 0), IntPoint::new(s, 0)); let eb = XSegment::new(IntPoint::new(s, -s), IntPoint::new(s, s)); @@ -446,7 +446,7 @@ mod tests { #[test] fn test_left_top() { - let s: i32 = 1024_000_000; + let s: i32 = 1_024_000_000; let ea = XSegment::new(IntPoint::new(-s, s), IntPoint::new(s, s)); let eb = XSegment::new(IntPoint::new(-s, s), IntPoint::new(-s, -s)); diff --git a/iOverlay/src/split/grid_layout.rs b/iOverlay/src/split/grid_layout.rs index b821b83..8a13447 100644 --- a/iOverlay/src/split/grid_layout.rs +++ b/iOverlay/src/split/grid_layout.rs @@ -294,6 +294,8 @@ impl GridLayout { #[cfg(test)] mod tests { + #![allow(clippy::useless_vec)] + use crate::geom::x_segment::XSegment; use crate::split::grid_layout::vec; use crate::split::grid_layout::{FragmentBuffer, GridLayout}; @@ -1105,8 +1107,8 @@ mod tests { #[test] fn test_10() { let layout = GridLayout { - min_x: -1000_000, - max_x: 1000_000, + min_x: -1_000_000, + max_x: 1_000_000, power: 10, }; diff --git a/iOverlay/src/split/solver_fragment.rs b/iOverlay/src/split/solver_fragment.rs index 11b09f2..11b65a9 100644 --- a/iOverlay/src/split/solver_fragment.rs +++ b/iOverlay/src/split/solver_fragment.rs @@ -150,7 +150,7 @@ impl SplitSolver { return false; } - fragments.sort_unstable_by(|a, b| a.rect.min_y.cmp(&b.rect.min_y)); + fragments.sort_unstable_by_key(|a| a.rect.min_y); let mut any_round = false; @@ -195,8 +195,8 @@ impl SplitSolver { return; } - points.sort_unstable_by(|p0, p1| p0.y.cmp(&p1.y)); - vertical_segments.sort_by(|s0, s1| s0.y_range.min.cmp(&s1.y_range.min)); + points.sort_unstable_by_key(|p0| p0.y); + vertical_segments.sort_by_key(|s0| s0.y_range.min); let mut i = 0; for s in vertical_segments.iter() { diff --git a/iOverlay/src/string/clip.rs b/iOverlay/src/string/clip.rs index 8066212..b94e583 100644 --- a/iOverlay/src/string/clip.rs +++ b/iOverlay/src/string/clip.rs @@ -249,6 +249,8 @@ impl IntClip for [IntPoint] { #[cfg(test)] mod tests { + #![allow(clippy::useless_vec)] + use crate::core::fill_rule::FillRule; use crate::string::clip::{ClipRule, IntClip}; use alloc::vec; diff --git a/iOverlay/src/string/slice.rs b/iOverlay/src/string/slice.rs index fe35fb0..f3e3206 100644 --- a/iOverlay/src/string/slice.rs +++ b/iOverlay/src/string/slice.rs @@ -141,6 +141,8 @@ impl IntSlice for [IntPoint] { #[cfg(test)] mod tests { + #![allow(clippy::useless_vec)] + use crate::core::fill_rule::FillRule; use crate::string::slice::IntSlice; use alloc::vec; diff --git a/iOverlay/src/vector/extract.rs b/iOverlay/src/vector/extract.rs index d793790..378bfec 100644 --- a/iOverlay/src/vector/extract.rs +++ b/iOverlay/src/vector/extract.rs @@ -102,7 +102,7 @@ impl OverlayGraph<'_, D> { } if !anchors_already_sorted { - anchors.sort_by(|s0, s1| s0.v_segment.a.cmp(&s1.v_segment.a)); + anchors.sort_by_key(|s0| s0.v_segment.a); } shapes.join_sorted_holes(holes, anchors, clockwise); diff --git a/iOverlay/tests/direction_tests.rs b/iOverlay/tests/direction_tests.rs index 284a062..e3b3a94 100644 --- a/iOverlay/tests/direction_tests.rs +++ b/iOverlay/tests/direction_tests.rs @@ -1,5 +1,7 @@ #[cfg(test)] mod tests { + #![allow(clippy::useless_vec)] + use i_float::int::point::IntPoint; use i_overlay::core::fill_rule::FillRule; use i_overlay::core::overlay::{ContourDirection, IntOverlayOptions, Overlay}; diff --git a/iOverlay/tests/dynamic_tests.rs b/iOverlay/tests/dynamic_tests.rs index d49ec16..b4402fc 100644 --- a/iOverlay/tests/dynamic_tests.rs +++ b/iOverlay/tests/dynamic_tests.rs @@ -30,7 +30,7 @@ mod tests { { graph.validate(); let result = graph.extract_shapes(OverlayRule::Union, &mut Default::default()); - assert!(result.len() > 0); + assert!(!result.is_empty()); } a += 0.005 } @@ -117,7 +117,7 @@ mod tests { #[test] fn test_5() { let clip = create_star(202.5, 33.75, 24, 0.0); - let a = -9.9999999999999995E-7; + let a = -1E-6; let subj = create_star(202.5, 33.75, 24, a); // println!("subj {:?}", subj); @@ -127,7 +127,7 @@ mod tests { { graph.validate(); let result = graph.extract_shapes(OverlayRule::Xor, &mut Default::default()); - assert!(result.len() > 0); + assert!(!result.is_empty()); } } } @@ -154,7 +154,7 @@ mod tests { #[test] fn test_7() { let n = 1010; - let subj_paths = random_polygon(1000_000.0, 0.0, n); + let subj_paths = random_polygon(1_000_000.0, 0.0, n); let mut overlay = Overlay::new(n); overlay.add_contour(&subj_paths, ShapeType::Subject); @@ -162,7 +162,7 @@ mod tests { if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { graph.validate(); let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); - assert!(result.len() > 0); + assert!(!result.is_empty()); } } @@ -180,7 +180,7 @@ mod tests { if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { graph.validate(); let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); - assert!(result.len() > 0); + assert!(!result.is_empty()); } } r += 0.001; @@ -205,7 +205,7 @@ mod tests { { graph.validate(); let result = graph.extract_shapes(OverlayRule::Union, &mut Default::default()); - assert!(result.len() > 0); + assert!(!result.is_empty()); } a += 0.005 } @@ -218,7 +218,7 @@ mod tests { fn test_10() { let solver = Solver::AUTO; let clip = create_star(1.0, 2.0, 7, 0.0); - let a = 0.44000000000000028; + let a = 0.440_000_000_000_000_3; let r = 1.01; let subj = create_star(1.0, r, 7, a); @@ -227,7 +227,7 @@ mod tests { { graph.validate(); let result = graph.extract_shapes(OverlayRule::Union, &mut Default::default()); - assert!(result.len() > 0); + assert!(!result.is_empty()); } } @@ -245,7 +245,7 @@ mod tests { if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { graph.validate(); let result = graph.extract_shapes(OverlayRule::Union, &mut Default::default()); - assert!(result.len() > 0); + assert!(!result.is_empty()); } } } @@ -260,7 +260,7 @@ mod tests { if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { graph.validate(); let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); - assert!(result.len() > 0); + assert!(!result.is_empty()); } } } @@ -275,7 +275,7 @@ mod tests { if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { graph.graph.validate(); let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); - assert!(result.len() > 0); + assert!(!result.is_empty()); } } } @@ -286,7 +286,7 @@ mod tests { let mut rng = rand::rng(); let paths_count = 3; let mut subj_paths = Vec::with_capacity(paths_count); - for _ in 0..1000_000 { + for _ in 0..1_000_000 { subj_paths.clear(); let x_range = 0..=8; let y_range = -8..=8; @@ -303,7 +303,7 @@ mod tests { if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { graph.validate(); let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); - assert!(result.len() > 0); + assert!(!result.is_empty()); } } } @@ -320,7 +320,7 @@ mod tests { if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { graph.validate(); let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); - assert!(result.len() > 0); + assert!(!result.is_empty()); } } diff --git a/iOverlay/tests/float_overlay_tests.rs b/iOverlay/tests/float_overlay_tests.rs index d51606f..c450e80 100644 --- a/iOverlay/tests/float_overlay_tests.rs +++ b/iOverlay/tests/float_overlay_tests.rs @@ -1,5 +1,12 @@ #[cfg(test)] mod tests { + #![allow( + clippy::bool_assert_comparison, + clippy::needless_range_loop, + clippy::useless_vec, + clippy::zero_repeat_side_effects + )] + use i_float::adapter::FloatPointAdapter; use i_float::float::compatible::FloatPointCompatible; use i_overlay::core::fill_rule::FillRule; diff --git a/iOverlay/tests/ocg_tests.rs b/iOverlay/tests/ocg_tests.rs index be52a82..97e7473 100644 --- a/iOverlay/tests/ocg_tests.rs +++ b/iOverlay/tests/ocg_tests.rs @@ -1,5 +1,7 @@ #[cfg(test)] mod tests { + #![allow(clippy::explicit_counter_loop)] + use i_overlay::core::fill_rule::FillRule; use i_overlay::core::overlay::{ContourDirection, IntOverlayOptions, Overlay}; use i_overlay::core::overlay_rule::OverlayRule; diff --git a/iOverlay/tests/overlay_tests.rs b/iOverlay/tests/overlay_tests.rs index 3c388de..5cab35a 100644 --- a/iOverlay/tests/overlay_tests.rs +++ b/iOverlay/tests/overlay_tests.rs @@ -3,6 +3,8 @@ mod util; #[cfg(test)] mod tests { + #![allow(clippy::bool_assert_comparison)] + use crate::data::overlay::BooleanTest; use crate::util::overlay; use crate::util::overlay::JsonPrint; diff --git a/iOverlay/tests/simplify_tests.rs b/iOverlay/tests/simplify_tests.rs index 4e35f9c..5829efd 100644 --- a/iOverlay/tests/simplify_tests.rs +++ b/iOverlay/tests/simplify_tests.rs @@ -1,5 +1,7 @@ #[cfg(test)] mod tests { + #![allow(clippy::field_reassign_with_default, clippy::useless_vec)] + use i_float::int::point::IntPoint; use i_overlay::core::fill_rule::FillRule; use i_overlay::core::overlay::{ContourDirection, IntOverlayOptions, Overlay, ShapeType}; diff --git a/iOverlay/tests/slice_tests.rs b/iOverlay/tests/slice_tests.rs index 22e1cb6..06e251a 100644 --- a/iOverlay/tests/slice_tests.rs +++ b/iOverlay/tests/slice_tests.rs @@ -1,5 +1,7 @@ #[cfg(test)] mod tests { + #![allow(clippy::len_zero, clippy::useless_vec)] + use i_float::int::point::IntPoint; use i_overlay::core::fill_rule::FillRule; use i_overlay::string::line::IntLine; diff --git a/iOverlay/tests/string_tests.rs b/iOverlay/tests/string_tests.rs index 9c98456..8252e7d 100644 --- a/iOverlay/tests/string_tests.rs +++ b/iOverlay/tests/string_tests.rs @@ -3,6 +3,8 @@ mod util; #[cfg(test)] mod tests { + #![allow(clippy::bool_assert_comparison)] + use crate::data::overlay::StringTest; use crate::util::overlay; use crate::util::overlay::JsonPrint; diff --git a/iOverlay/tests/util.rs b/iOverlay/tests/util.rs index 3a92e8c..9ce4a89 100644 --- a/iOverlay/tests/util.rs +++ b/iOverlay/tests/util.rs @@ -61,7 +61,7 @@ pub mod overlay { } #[allow(dead_code)] - pub fn is_group_of_shapes_one_of(group: &Vec, groups: &Vec>) -> bool { + pub fn is_group_of_shapes_one_of(group: &Vec, groups: &[Vec]) -> bool { for item in groups.iter() { if item.are_equal(group) { return true; @@ -72,7 +72,7 @@ pub mod overlay { } #[allow(dead_code)] - pub fn is_paths_one_of(paths: &IntPaths, groups: &Vec) -> bool { + pub fn is_paths_one_of(paths: &IntPaths, groups: &[IntPaths]) -> bool { for item in groups.iter() { if item.eq(paths) { return true; From 9d9ed8b7676072193998c3e753287cf67d75805c Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sun, 17 May 2026 20:30:11 +0300 Subject: [PATCH 05/36] replace i32 --- iOverlay/src/bind/segment.rs | 22 +++--- iOverlay/src/build/boolean.rs | 40 ++++++----- iOverlay/src/build/builder.rs | 29 +++++--- iOverlay/src/build/graph.rs | 11 ++- iOverlay/src/build/string.rs | 22 +++--- iOverlay/src/build/sweep.rs | 18 ++--- iOverlay/src/build/util.rs | 2 +- iOverlay/src/core/divide.rs | 33 ++++----- iOverlay/src/core/edge_overlay.rs | 10 +-- iOverlay/src/core/extract.rs | 38 +++++------ iOverlay/src/core/extract_ogc.rs | 6 +- iOverlay/src/core/graph.rs | 7 +- iOverlay/src/core/link.rs | 18 +++-- iOverlay/src/core/overlay.rs | 60 ++++++++--------- iOverlay/src/core/predicate.rs | 31 +++++---- iOverlay/src/core/relate.rs | 18 ++--- iOverlay/src/core/simplify.rs | 32 +++++---- iOverlay/src/core/solver.rs | 6 +- iOverlay/src/float/graph.rs | 4 +- iOverlay/src/float/overlay.rs | 2 +- iOverlay/src/float/string_graph.rs | 2 +- iOverlay/src/geom/end.rs | 2 +- iOverlay/src/geom/id_point.rs | 9 +-- iOverlay/src/geom/line_range.rs | 2 +- iOverlay/src/geom/v_segment.rs | 2 +- iOverlay/src/geom/x_segment.rs | 2 +- iOverlay/src/mesh/outline/builder.rs | 10 +-- iOverlay/src/mesh/outline/builder_join.rs | 10 +-- iOverlay/src/mesh/outline/offset.rs | 4 +- iOverlay/src/mesh/outline/section.rs | 6 +- iOverlay/src/mesh/outline/uniq_iter.rs | 2 +- iOverlay/src/mesh/overlay.rs | 6 +- iOverlay/src/mesh/stroke/builder.rs | 10 +-- iOverlay/src/mesh/stroke/builder_cap.rs | 4 +- iOverlay/src/mesh/stroke/builder_join.rs | 14 ++-- iOverlay/src/mesh/stroke/offset.rs | 2 +- iOverlay/src/mesh/stroke/section.rs | 2 +- iOverlay/src/mesh/subject.rs | 2 +- iOverlay/src/segm/build.rs | 19 ++++-- iOverlay/src/segm/merge.rs | 10 ++- iOverlay/src/segm/segment.rs | 14 ++-- iOverlay/src/segm/sort.rs | 6 +- iOverlay/src/split/fragment.rs | 6 +- iOverlay/src/split/grid_layout.rs | 14 ++-- iOverlay/src/split/solver.rs | 14 ++-- iOverlay/src/split/solver_fragment.rs | 2 +- iOverlay/src/split/solver_list.rs | 2 +- iOverlay/src/split/solver_tree.rs | 16 ++--- iOverlay/src/string/clip.rs | 82 ++++++++++++++--------- iOverlay/src/string/extract.rs | 28 ++++---- iOverlay/src/string/filter.rs | 4 +- iOverlay/src/string/graph.rs | 5 +- iOverlay/src/string/overlay.rs | 44 ++++++------ iOverlay/src/string/slice.rs | 36 +++++----- iOverlay/src/string/split.rs | 34 +++++----- iOverlay/src/vector/edge.rs | 12 ++-- iOverlay/src/vector/extract.rs | 55 +++++++++------ iOverlay/src/vector/simplify.rs | 32 +++++---- iOverlay/tests/board_tests.rs | 4 +- iOverlay/tests/data.rs | 28 ++++---- iOverlay/tests/dynamic_tests.rs | 6 +- iOverlay/tests/fill_rule_tests.rs | 2 +- iOverlay/tests/fragment_tests.rs | 31 +++++---- iOverlay/tests/simplify_tests.rs | 2 +- iOverlay/tests/slice_tests.rs | 2 +- iOverlay/tests/util.rs | 12 ++-- 66 files changed, 570 insertions(+), 452 deletions(-) diff --git a/iOverlay/src/bind/segment.rs b/iOverlay/src/bind/segment.rs index 43bd04f..289427d 100644 --- a/iOverlay/src/bind/segment.rs +++ b/iOverlay/src/bind/segment.rs @@ -37,7 +37,7 @@ impl ContourIndex { } #[derive(Debug, Clone, Copy)] -pub(crate) struct IdSegment { +pub(crate) struct IdSegment { pub(crate) contour_index: ContourIndex, pub(crate) v_segment: VSegment, } @@ -110,26 +110,26 @@ impl IdSegments for IntPath { } } -impl IdSegments for DataVectorPath { +impl IdSegments for DataVectorPath { #[inline] fn append_id_segments( &self, - buffer: &mut Vec, + buffer: &mut Vec>, id_data: ContourIndex, - x_min: i32, - x_max: i32, + x_min: I, + x_max: I, clockwise: bool, ) { - fn inner<'a, D: 'a, I: Iterator>>( - iter: I, - buffer: &mut Vec, + fn inner<'a, D: 'a, I: IntNumber + 'a, It: Iterator>>( + iter: It, + buffer: &mut Vec>, id_data: ContourIndex, - x_min: i32, - x_max: i32, + x_min: I, + x_max: I, ) { for vec in iter { if vec.a.x < vec.b.x && x_min < vec.b.x && vec.a.x <= x_max { - buffer.push(IdSegment::new(id_data, vec.a, vec.b)); + buffer.push(IdSegment::::new(id_data, vec.a, vec.b)); } } } diff --git a/iOverlay/src/build/boolean.rs b/iOverlay/src/build/boolean.rs index 1cddb5c..1208db5 100644 --- a/iOverlay/src/build/boolean.rs +++ b/iOverlay/src/build/boolean.rs @@ -19,17 +19,24 @@ use crate::segm::segment::{ }; 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 { +impl GraphBuilder +where + I: IntNumber + Expiration + SortKey + Send + Sync, + D: OverlayEdgeData, +{ #[inline] pub(crate) fn build_boolean_all( &mut self, fill_rule: FillRule, options: IntOverlayOptions, solver: &Solver, - segments: &[Segment], - ) -> OverlayGraph<'_, D> { + segments: &[Segment], + ) -> OverlayGraph<'_, I, D> { self.build_boolean_fills(fill_rule, solver, segments); self.build_links_all(segments); self.boolean_graph(options, solver) @@ -42,8 +49,8 @@ impl GraphBuilder { overlay_rule: OverlayRule, options: IntOverlayOptions, solver: &Solver, - segments: &[Segment], - ) -> OverlayGraph<'_, D> { + segments: &[Segment], + ) -> OverlayGraph<'_, I, D> { self.build_boolean_fills(fill_rule, solver, segments); match overlay_rule { OverlayRule::Subject => self.build_links_by_filter::(segments), @@ -62,7 +69,7 @@ impl GraphBuilder { &mut self, fill_rule: FillRule, solver: &Solver, - segments: &[Segment], + segments: &[Segment], ) { match fill_rule { FillRule::EvenOdd => self.build_fills_with_strategy::(solver, segments), @@ -73,7 +80,7 @@ impl GraphBuilder { } #[inline] - fn boolean_graph(&mut self, options: IntOverlayOptions, solver: &Solver) -> OverlayGraph<'_, D> { + fn boolean_graph(&mut self, options: IntOverlayOptions, solver: &Solver) -> OverlayGraph<'_, I, D> { self.build_nodes_and_connect_links(solver); OverlayGraph { nodes: &self.nodes, @@ -274,7 +281,7 @@ impl BooleanFillFilter for SegmentFill { } } -impl OverlayLinkFilter for [OverlayLink] { +impl OverlayLinkFilter for [OverlayLink] { #[inline] fn filter_by_overlay_into(&self, overlay_rule: OverlayRule, buffer: &mut Vec) { match overlay_rule { @@ -290,7 +297,7 @@ impl OverlayLinkFilter for [OverlayLink] { } #[inline] -fn filter_subject_into(links: &[OverlayLink], buffer: &mut Vec) { +fn filter_subject_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); buffer.reserve_capacity(links.len()); for link in links.iter() { @@ -299,7 +306,7 @@ fn filter_subject_into(links: &[OverlayLink], buffer: &mut Vec } #[inline] -fn filter_clip_into(links: &[OverlayLink], buffer: &mut Vec) { +fn filter_clip_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); buffer.reserve_capacity(links.len()); for link in links.iter() { @@ -308,7 +315,7 @@ fn filter_clip_into(links: &[OverlayLink], buffer: &mut Vec) { } #[inline] -fn filter_intersect_into(links: &[OverlayLink], buffer: &mut Vec) { +fn filter_intersect_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); buffer.reserve_capacity(links.len()); for link in links.iter() { @@ -317,7 +324,7 @@ fn filter_intersect_into(links: &[OverlayLink], buffer: &mut Vec(links: &[OverlayLink], buffer: &mut Vec) { +fn filter_union_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); buffer.reserve_capacity(links.len()); for link in links.iter() { @@ -326,7 +333,7 @@ fn filter_union_into(links: &[OverlayLink], buffer: &mut Vec) } #[inline] -fn filter_difference_into(links: &[OverlayLink], buffer: &mut Vec) { +fn filter_difference_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); buffer.reserve_capacity(links.len()); for link in links.iter() { @@ -335,7 +342,10 @@ fn filter_difference_into(links: &[OverlayLink], buffer: &mut Vec(links: &[OverlayLink], buffer: &mut Vec) { +fn filter_inverse_difference_into( + links: &[OverlayLink], + buffer: &mut Vec, +) { buffer.clear(); buffer.reserve_capacity(links.len()); for link in links.iter() { @@ -344,7 +354,7 @@ fn filter_inverse_difference_into(links: &[OverlayLink], buffer: &mut Vec< } #[inline] -fn filter_xor_into(links: &[OverlayLink], buffer: &mut Vec) { +fn filter_xor_into(links: &[OverlayLink], buffer: &mut Vec) { buffer.clear(); buffer.reserve_capacity(links.len()); for link in links.iter() { diff --git a/iOverlay/src/build/builder.rs b/iOverlay/src/build/builder.rs index c50c924..33bb20c 100644 --- a/iOverlay/src/build/builder.rs +++ b/iOverlay/src/build/builder.rs @@ -26,11 +26,11 @@ impl<'a> StoreFillsHandler<'a> { } } -impl FillHandler for StoreFillsHandler<'_> { +impl FillHandler for StoreFillsHandler<'_> { type Output = (); #[inline(always)] - fn handle(&mut self, index: usize, _segment: &Segment, fill: SegmentFill) -> ControlFlow<()> { + fn handle(&mut self, index: usize, _segment: &Segment, fill: SegmentFill) -> ControlFlow<()> { // fills is pre-allocated to segments.len() and index is guaranteed // to be in range by the sweep algorithm unsafe { *self.fills.get_unchecked_mut(index) = fill }; @@ -45,15 +45,21 @@ pub(crate) trait GraphNode { fn with_indices(indices: &[usize]) -> Self; } -pub(crate) struct GraphBuilder { - sweep_runner: SweepRunner, - pub(super) links: Vec>, +pub(crate) struct GraphBuilder { + sweep_runner: SweepRunner, + pub(super) links: Vec>, pub(super) nodes: Vec, pub(super) fills: Vec, - pub(super) ends: Vec, + pub(super) ends: Vec>, } -impl> GraphBuilder { +impl GraphBuilder +where + C: WindingCount, + N: GraphNode, + I: IntNumber + i_tree::Expiration, + D: OverlayEdgeData, +{ #[inline] pub(crate) fn new() -> Self { Self { @@ -69,7 +75,7 @@ impl> GraphBuilder pub(super) fn build_fills_with_strategy>( &mut self, solver: &Solver, - segments: &[Segment], + segments: &[Segment], ) { self.fills.resize(segments.len(), NONE); self.sweep_runner @@ -77,7 +83,10 @@ impl> GraphBuilder } #[inline] - pub(super) fn build_links_by_filter(&mut self, segments: &[Segment]) { + pub(super) fn build_links_by_filter( + &mut self, + segments: &[Segment], + ) { self.links.clear(); self.links.reserve_capacity(segments.len()); @@ -95,7 +104,7 @@ impl> GraphBuilder } #[inline] - pub(super) fn build_links_all(&mut self, segments: &[Segment]) { + pub(super) fn build_links_all(&mut self, segments: &[Segment]) { self.links.clear(); self.links.reserve_capacity(segments.len()); diff --git a/iOverlay/src/build/graph.rs b/iOverlay/src/build/graph.rs index 72df7f6..40acd99 100644 --- a/iOverlay/src/build/graph.rs +++ b/iOverlay/src/build/graph.rs @@ -4,9 +4,18 @@ use crate::core::solver::Solver; use crate::geom::end::End; 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_key_sort::sort::two_keys::TwoKeysSort; +use i_tree::Expiration; -impl> GraphBuilder { +impl GraphBuilder +where + I: IntNumber + Expiration + SortKey + Send + Sync, + C: WindingCount, + N: GraphNode, + D: OverlayEdgeData, +{ pub(super) fn build_nodes_and_connect_links(&mut self, solver: &Solver) { let n = self.links.len(); if n == 0 { diff --git a/iOverlay/src/build/string.rs b/iOverlay/src/build/string.rs index 157637e..52ca55f 100644 --- a/iOverlay/src/build/string.rs +++ b/iOverlay/src/build/string.rs @@ -7,15 +7,21 @@ use crate::segm::string::ShapeCountString; use crate::string::clip::ClipRule; use crate::string::graph::StringGraph; use alloc::vec::Vec; - -impl GraphBuilder> { +use i_float::int::number::int::IntNumber; +use i_key_sort::sort::key::SortKey; +use i_tree::Expiration; + +impl GraphBuilder, I> +where + I: IntNumber + Expiration + SortKey + Send + Sync, +{ #[inline] pub(crate) fn build_string_all( &mut self, fill_rule: FillRule, solver: &Solver, - segments: &[Segment], - ) -> StringGraph<'_> { + segments: &[Segment], + ) -> StringGraph<'_, I> { self.build_string_fills(fill_rule, solver, segments); self.build_links_all(segments); self.string_graph(solver) @@ -27,8 +33,8 @@ impl GraphBuilder> { fill_rule: FillRule, clip_rule: ClipRule, solver: &Solver, - segments: &[Segment], - ) -> StringGraph<'_> { + segments: &[Segment], + ) -> StringGraph<'_, I> { self.build_string_fills(fill_rule, solver, segments); match clip_rule { ClipRule { @@ -56,7 +62,7 @@ impl GraphBuilder> { &mut self, fill_rule: FillRule, solver: &Solver, - segments: &[Segment], + segments: &[Segment], ) { match fill_rule { FillRule::EvenOdd => self.build_fills_with_strategy::(solver, segments), @@ -67,7 +73,7 @@ impl GraphBuilder> { } #[inline] - fn string_graph(&mut self, solver: &Solver) -> StringGraph<'_> { + fn string_graph(&mut self, solver: &Solver) -> StringGraph<'_, I> { self.build_nodes_and_connect_links(solver); StringGraph { nodes: &self.nodes, diff --git a/iOverlay/src/build/sweep.rs b/iOverlay/src/build/sweep.rs index b6f7715..09d7604 100644 --- a/iOverlay/src/build/sweep.rs +++ b/iOverlay/src/build/sweep.rs @@ -18,12 +18,12 @@ pub(crate) trait FillStrategy { fn add_and_fill(this: C, bot: C) -> (C, SegmentFill); } -pub(crate) trait FillHandler { +pub(crate) trait FillHandler { type Output; fn handle( &mut self, index: usize, - segment: &Segment, + segment: &Segment, fill: SegmentFill, ) -> ControlFlow; fn finalize(self) -> Self::Output; @@ -32,7 +32,7 @@ pub(crate) trait FillHandler { #[inline] fn sweep_with_handler( scan: &mut S, - segments: &[Segment], + segments: &[Segment], mut handler: H, ) -> H::Output where @@ -40,7 +40,7 @@ where C: WindingCount, F: FillStrategy, S: KeyExpCollection, I, C>, - H: FillHandler, + H: FillHandler, { let mut node = Vec::with_capacity(4); let n = segments.len(); @@ -89,7 +89,7 @@ where handler.finalize() } -pub(crate) struct SweepRunner { +pub(crate) struct SweepRunner { list: Option, I, C>>, tree: Option, I, C>>, } @@ -107,12 +107,12 @@ impl SweepRunner { pub(crate) fn run( &mut self, solver: &Solver, - segments: &[Segment], + segments: &[Segment], handler: H, ) -> H::Output where F: FillStrategy, - H: FillHandler, + H: FillHandler, D: Send, { let count = segments.len(); @@ -136,11 +136,11 @@ impl SweepRunner { &mut self, fill_rule: FillRule, solver: &Solver, - segments: &[Segment], + segments: &[Segment], handler: H, ) -> H::Output where - H: FillHandler, + H: FillHandler, D: Send, EvenOddStrategy: FillStrategy, NonZeroStrategy: FillStrategy, diff --git a/iOverlay/src/build/util.rs b/iOverlay/src/build/util.rs index da6a302..4a9b741 100644 --- a/iOverlay/src/build/util.rs +++ b/iOverlay/src/build/util.rs @@ -4,7 +4,7 @@ use alloc::vec::Vec; use i_float::int::point::IntPoint; use i_key_sort::sort::two_keys::TwoKeysSort; -impl GraphBuilder { +impl GraphBuilder { pub(crate) fn test_contour_for_loops( &mut self, contour: &[IntPoint], diff --git a/iOverlay/src/core/divide.rs b/iOverlay/src/core/divide.rs index 9cbf99e..bcacb80 100644 --- a/iOverlay/src/core/divide.rs +++ b/iOverlay/src/core/divide.rs @@ -1,18 +1,19 @@ use crate::geom::id_point::IdPoint; use alloc::vec; use alloc::vec::Vec; +use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; use i_shape::int::path::IntPath; use i_shape::int::shape::IntContour; -struct SubPath { +struct SubPath { last: usize, - node: IntPoint, - path: IntPath, + node: IntPoint, + path: IntPath, } -impl SubPath { - fn start(point: IdPoint) -> Self { +impl SubPath { + fn start(point: IdPoint) -> Self { Self { last: point.id + 1, node: point.point, @@ -20,22 +21,22 @@ impl SubPath { } } - fn join(&mut self, point: IdPoint, source: &IntContour) { + fn join(&mut self, point: IdPoint, source: &IntContour) { self.path.extend_from_slice(&source[self.last..point.id]); self.last = point.id; } - fn shift(&mut self, point: IdPoint) { + fn shift(&mut self, point: IdPoint) { self.last = point.id; } } pub trait ContourDecomposition { - fn decompose_contours(&self) -> Option>; + fn decompose_contours(&self) -> Option>>; } -impl ContourDecomposition for IntContour { - fn decompose_contours(&self) -> Option> { +impl ContourDecomposition for IntContour { + fn decompose_contours(&self) -> Option>> { if self.len() < 3 { return None; } @@ -77,10 +78,10 @@ impl ContourDecomposition for IntContour { let mut i = 0; while i < anchors.len() { let a = anchors[i]; - let mut sub_path: SubPath = if let Some(sub_path) = queue.pop() { + let mut sub_path: SubPath = if let Some(sub_path) = queue.pop() { sub_path } else { - queue.push(SubPath::start(a)); + queue.push(SubPath::::start(a)); i += 1; continue; }; @@ -91,17 +92,17 @@ impl ContourDecomposition for IntContour { if let Some(prev) = queue.last_mut() { prev.shift(a); } else { - queue.push(SubPath::start(a)); + queue.push(SubPath::::start(a)); } } else { sub_path.join(a, self); queue.push(sub_path); - queue.push(SubPath::start(a)); + queue.push(SubPath::::start(a)); } i += 1; } - let mut sub_path: SubPath = queue.pop().unwrap(); + let mut sub_path: SubPath = queue.pop().unwrap(); if sub_path.last < self.len() { sub_path.path.extend_from_slice(&self[sub_path.last..]); @@ -276,7 +277,7 @@ mod tests { } } - fn rotate(contour: &IntContour, s: usize) -> IntContour { + fn rotate(contour: &IntContour, s: usize) -> IntContour { contour .iter() .cycle() diff --git a/iOverlay/src/core/edge_overlay.rs b/iOverlay/src/core/edge_overlay.rs index 2ffe594..a16ae5c 100644 --- a/iOverlay/src/core/edge_overlay.rs +++ b/iOverlay/src/core/edge_overlay.rs @@ -25,9 +25,9 @@ pub struct EdgeOverlay { pub solver: Solver, pub options: IntOverlayOptions, pub boolean_buffer: Option, - segments: Vec>, + segments: Vec>, split_solver: SplitSolver, - graph_builder: GraphBuilder, + graph_builder: GraphBuilder, } impl EdgeOverlay { @@ -38,7 +38,7 @@ impl EdgeOverlay { boolean_buffer: None, segments: Vec::with_capacity(capacity), split_solver: SplitSolver::new(), - graph_builder: GraphBuilder::::new(), + graph_builder: GraphBuilder::::new(), } } @@ -66,7 +66,7 @@ impl EdgeOverlay { &mut self, overlay_rule: OverlayRule, fill_rule: FillRule, - ) -> Vec> { + ) -> Vec> { self.split_solver.split_segments(&mut self.segments, &self.solver); if self.segments.is_empty() { return Vec::new(); @@ -87,7 +87,7 @@ impl EdgeOverlay { &mut self, overlay_rule: OverlayRule, fill_rule: FillRule, - ) -> Vec> { + ) -> Vec> { self.split_solver.split_segments(&mut self.segments, &self.solver); if self.segments.is_empty() { return Vec::new(); diff --git a/iOverlay/src/core/extract.rs b/iOverlay/src/core/extract.rs index c732361..d84974e 100644 --- a/iOverlay/src/core/extract.rs +++ b/iOverlay/src/core/extract.rs @@ -34,15 +34,15 @@ pub struct BooleanExtractionBuffer { pub(crate) contour_visited: Option>, } -impl OverlayGraph<'_> { +impl OverlayGraph<'_, i32> { /// 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. /// - `buffer`: Reusable buffer, optimisation purpose only. - /// - Returns: A vector of `IntShape`, representing the geometric result of the applied overlay rule. + /// - Returns: A vector of `IntShape`, representing the geometric result of the applied overlay rule. /// # Shape Representation - /// The output is a `IntShapes`, where: - /// - The outer `Vec` represents a set of shapes. - /// - Each shape `Vec` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. + /// The output is a `IntShapes`, where: + /// - The outer `Vec>` represents a set of shapes. + /// - Each shape `Vec>` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. /// - Each path `Vec` is a sequence of points, forming a closed path. /// /// Note: Outer boundary paths have a counterclockwise order, and holes have a clockwise order. @@ -51,7 +51,7 @@ impl OverlayGraph<'_> { &self, overlay_rule: OverlayRule, buffer: &mut BooleanExtractionBuffer, - ) -> IntShapes { + ) -> IntShapes { self.links .filter_by_overlay_into(overlay_rule, &mut buffer.visited); if self.options.ogc { @@ -77,7 +77,7 @@ impl OverlayGraph<'_> { &self, overlay_rule: OverlayRule, buffer: &mut BooleanExtractionBuffer, - output: &mut FlatContoursBuffer, + output: &mut FlatContoursBuffer, ) { self.links .filter_by_overlay_into(overlay_rule, &mut buffer.visited); @@ -88,7 +88,7 @@ impl OverlayGraph<'_> { &self, overlay_rule: OverlayRule, buffer: &mut BooleanExtractionBuffer, - ) -> IntShapes { + ) -> IntShapes { let clockwise = self.options.output_direction == ContourDirection::Clockwise; let mut shapes = Vec::new(); @@ -213,7 +213,7 @@ impl OverlayGraph<'_> { &self, overlay_rule: OverlayRule, buffer: &mut BooleanExtractionBuffer, - output: &mut FlatContoursBuffer, + output: &mut FlatContoursBuffer, ) { let clockwise = self.options.output_direction == ContourDirection::Clockwise; let len = buffer.visited.len(); @@ -274,7 +274,7 @@ pub(crate) struct StartPathData { impl StartPathData { #[inline(always)] - pub(crate) fn new(direction: bool, link: &OverlayLink, link_id: usize) -> Self { + pub(crate) fn new(direction: bool, link: &OverlayLink, link_id: usize) -> Self { if direction { Self { begin: link.b.point, @@ -295,10 +295,10 @@ impl StartPathData { pub(crate) trait GraphContour { fn validate(&mut self, min_output_area: u64, preserve_output_collinear: bool) -> (bool, bool); - fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize; + fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize; } -impl GraphContour for IntContour { +impl GraphContour for IntContour { #[inline] fn validate(&mut self, min_output_area: u64, preserve_output_collinear: bool) -> (bool, bool) { let is_modified = if !preserve_output_collinear { @@ -322,7 +322,7 @@ impl GraphContour for IntContour { } #[inline] - fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize { + fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize { if link.a.id == node_id { self.push(link.a.point); link.b.id @@ -387,7 +387,7 @@ impl GraphUtil { /// * `visited` is at least `links.len()` long (or whatever invariant applies) #[inline] pub(crate) unsafe fn find_left_top_link( - links: &[OverlayLink], + links: &[OverlayLink], nodes: &[OverlayNode], link_index: usize, visited: &[VisitState], @@ -415,8 +415,8 @@ impl GraphUtil { #[inline(always)] fn find_left_top_link_on_indices( - links: &[OverlayLink], - link: &OverlayLink, + links: &[OverlayLink], + link: &OverlayLink, link_index: usize, indices: &[usize], visited: &[VisitState], @@ -450,7 +450,7 @@ impl GraphUtil { } #[inline(always)] - fn find_left_top_link_on_bridge(links: &[OverlayLink], bridge: &[usize; 2]) -> usize { + fn find_left_top_link_on_bridge(links: &[OverlayLink], bridge: &[usize; 2]) -> usize { // SAFETY: every bridge index comes straight from GraphBuilder::build_nodes_and_connect_links, // which only records values in 0..links.len(), so the unchecked lookups stay in-bounds. let (l0, l1) = unsafe { (links.get_unchecked(bridge[0]), links.get_unchecked(bridge[1])) }; @@ -463,7 +463,7 @@ impl GraphUtil { #[inline(always)] pub(crate) fn next_link( - links: &[OverlayLink], + links: &[OverlayLink], nodes: &[OverlayNode], link_id: usize, node_id: usize, @@ -494,7 +494,7 @@ impl GraphUtil { // still unvisited when we enter. The unchecked accesses rely on that invariant. #[inline] fn find_nearest_link_to( - links: &[OverlayLink], + links: &[OverlayLink], target_index: usize, node_id: usize, clockwise: bool, diff --git a/iOverlay/src/core/extract_ogc.rs b/iOverlay/src/core/extract_ogc.rs index 6ba392d..cd02a62 100644 --- a/iOverlay/src/core/extract_ogc.rs +++ b/iOverlay/src/core/extract_ogc.rs @@ -13,12 +13,12 @@ use i_float::int::point::IntPoint; use i_shape::int::shape::{IntShape, IntShapes}; use i_shape::util::reserve::Reserve; -impl OverlayGraph<'_> { +impl OverlayGraph<'_, i32> { pub(crate) fn extract_ogc( &self, overlay_rule: OverlayRule, buffer: &mut BooleanExtractionBuffer, - ) -> IntShapes { + ) -> IntShapes { let is_main_dir_cw = self.options.output_direction == ContourDirection::Clockwise; let mut contour_visited = if let Some(mut visited) = buffer.contour_visited.take() { @@ -219,7 +219,7 @@ impl OverlayGraph<'_> { global_visited: &mut [VisitState], contour_visited: &mut [VisitState], points: &mut Vec, - ) -> Option { + ) -> Option> { let mut link_id = start_data.link_id; let mut node_id = start_data.node_id; let last_node_id = start_data.last_node_id; diff --git a/iOverlay/src/core/graph.rs b/iOverlay/src/core/graph.rs index 8e5edfc..0dfe110 100644 --- a/iOverlay/src/core/graph.rs +++ b/iOverlay/src/core/graph.rs @@ -6,6 +6,7 @@ use super::link::OverlayLink; use crate::build::builder::GraphNode; use crate::core::overlay::IntOverlayOptions; use alloc::vec::Vec; +use i_float::int::number::int::IntNumber; /// A representation of geometric shapes organized for efficient boolean operations. /// @@ -13,10 +14,10 @@ use alloc::vec::Vec; /// /// Use `OverlayGraph` to perform boolean operations on the geometric shapes you've added to an `Overlay`, after it has processed the shapes according to the specified build and overlay rules. /// [More information](https://ishape-rust.github.io/iShape-js/overlay/overlay_graph/overlay_graph.html) about Overlay Graph. -pub struct OverlayGraph<'a, D = ()> { +pub struct OverlayGraph<'a, I: IntNumber, D = ()> { pub(crate) options: IntOverlayOptions, pub(crate) nodes: &'a [OverlayNode], - pub(crate) links: &'a [OverlayLink], + pub(crate) links: &'a [OverlayLink], } #[derive(Debug)] @@ -36,7 +37,7 @@ impl GraphNode for OverlayNode { } } -impl OverlayGraph<'_> { +impl OverlayGraph<'_, I, D> { pub fn validate(&self) { for node in self.nodes.iter() { if let OverlayNode::Cross(indices) = node { diff --git a/iOverlay/src/core/link.rs b/iOverlay/src/core/link.rs index 89a1b6e..7c2120c 100644 --- a/iOverlay/src/core/link.rs +++ b/iOverlay/src/core/link.rs @@ -3,23 +3,29 @@ use crate::core::overlay_rule::OverlayRule; use crate::geom::id_point::IdPoint; use crate::segm::segment::SegmentFill; use alloc::vec::Vec; +use i_float::int::number::int::IntNumber; #[derive(Debug, Clone, Copy)] -pub(crate) struct OverlayLink { - pub(crate) a: IdPoint, - pub(crate) b: IdPoint, +pub(crate) struct OverlayLink { + pub(crate) a: IdPoint, + pub(crate) b: IdPoint, pub(crate) fill: SegmentFill, pub(crate) data: D, } -impl OverlayLink { +impl OverlayLink { #[inline(always)] - pub(crate) fn new_with_data(a: IdPoint, b: IdPoint, fill: SegmentFill, data: D) -> OverlayLink { + pub(crate) fn new_with_data( + a: IdPoint, + b: IdPoint, + fill: SegmentFill, + data: D, + ) -> OverlayLink { OverlayLink { a, b, fill, data } } #[inline(always)] - pub(crate) fn other(&self, node_id: usize) -> IdPoint { + pub(crate) fn other(&self, node_id: usize) -> IdPoint { if self.a.id == node_id { self.b } else { self.a } } diff --git a/iOverlay/src/core/overlay.rs b/iOverlay/src/core/overlay.rs index afbd7ed..875f0f3 100644 --- a/iOverlay/src/core/overlay.rs +++ b/iOverlay/src/core/overlay.rs @@ -65,9 +65,9 @@ pub struct Overlay { pub solver: Solver, pub options: IntOverlayOptions, pub boolean_buffer: Option, - pub(crate) segments: Vec>, + pub(crate) segments: Vec>, pub(crate) split_solver: SplitSolver, - pub(crate) graph_builder: GraphBuilder, + pub(crate) graph_builder: GraphBuilder, } impl Overlay { @@ -81,7 +81,7 @@ impl Overlay { boolean_buffer: Some(Default::default()), segments: Vec::with_capacity(capacity), split_solver: SplitSolver::new(), - graph_builder: GraphBuilder::::new(), + graph_builder: GraphBuilder::::new(), } } @@ -97,7 +97,7 @@ impl Overlay { boolean_buffer: Some(Default::default()), segments: Vec::with_capacity(capacity), split_solver: SplitSolver::new(), - graph_builder: GraphBuilder::::new(), + graph_builder: GraphBuilder::::new(), } } @@ -131,7 +131,7 @@ impl Overlay { /// Creates a new `Overlay` instance and initializes it with subject and clip contours. /// - `subj`: An array of contours that together define the subject shape. /// - `clip`: An array of contours that together define the clip shape. - pub fn with_contours(subj: &[IntContour], clip: &[IntContour]) -> Self { + pub fn with_contours(subj: &[IntContour], clip: &[IntContour]) -> Self { let mut overlay = Self::new(subj.points_count() + clip.points_count()); overlay.add_contours(subj, ShapeType::Subject); overlay.add_contours(clip, ShapeType::Clip); @@ -144,8 +144,8 @@ impl Overlay { /// - `options`: Adjust custom behavior. /// - `solver`: Type of solver to use. pub fn with_contours_custom( - subj: &[IntContour], - clip: &[IntContour], + subj: &[IntContour], + clip: &[IntContour], options: IntOverlayOptions, solver: Solver, ) -> Self { @@ -158,7 +158,7 @@ impl Overlay { /// Creates a new `Overlay` instance and initializes it with subject and clip shapes. /// - `subj`: An array of shapes to be used as the subject in the overlay operation. /// - `clip`: An array of shapes to be used as the clip in the overlay operation. - pub fn with_shapes(subj: &[IntShape], clip: &[IntShape]) -> Self { + pub fn with_shapes(subj: &[IntShape], clip: &[IntShape]) -> Self { let mut overlay = Self::new(subj.points_count() + clip.points_count()); overlay.add_shapes(subj, ShapeType::Subject); overlay.add_shapes(clip, ShapeType::Clip); @@ -171,8 +171,8 @@ impl Overlay { /// - `options`: Adjust custom behavior. /// - `solver`: Type of solver to use. pub fn with_shapes_options( - subj: &[IntShape], - clip: &[IntShape], + subj: &[IntShape], + clip: &[IntShape], options: IntOverlayOptions, solver: Solver, ) -> Self { @@ -206,28 +206,28 @@ impl Overlay { } /// Adds multiple paths to the overlay as either subject or clip paths. - /// - `contours`: An array of `IntContour` instances to be added to the overlay. + /// - `contours`: An array of `IntContour` instances to be added to the overlay. /// - `shape_type`: Specifies the role of the added paths in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_contours(&mut self, contours: &[IntContour], shape_type: ShapeType) { + pub fn add_contours(&mut self, contours: &[IntContour], shape_type: ShapeType) { for contour in contours.iter() { self.add_contour(contour, shape_type); } } /// Adds a single shape to the overlay as either a subject or clip shape. - /// - `shape`: A reference to a `IntShape` instance to be added. + /// - `shape`: A reference to a `IntShape` instance to be added. /// - `shape_type`: Specifies the role of the added shape in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_shape(&mut self, shape: &IntShape, shape_type: ShapeType) { + pub fn add_shape(&mut self, shape: &IntShape, shape_type: ShapeType) { self.add_contours(shape, shape_type); } /// Adds multiple shapes to the overlay as either subject or clip shapes. - /// - `shapes`: An array of `IntShape` instances to be added to the overlay. + /// - `shapes`: An array of `IntShape` instances to be added to the overlay. /// - `shape_type`: Specifies the role of the added shapes in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_shapes(&mut self, shapes: &[IntShape], shape_type: ShapeType) { + pub fn add_shapes(&mut self, shapes: &[IntShape], shape_type: ShapeType) { for shape in shapes.iter() { self.add_contours(shape, shape_type); } @@ -239,10 +239,10 @@ impl Overlay { } /// Adds multiple flat-shape to the overlay as either subject or clip shapes. - /// - `buffer`: A buffer of `IntShapes` instances to be added to the overlay. + /// - `buffer`: A buffer of `IntShapes` instances to be added to the overlay. /// - `shape_type`: Specifies the role of the added shapes in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_flat_buffer(&mut self, buffer: &FlatContoursBuffer, shape_type: ShapeType) { + pub fn add_flat_buffer(&mut self, buffer: &FlatContoursBuffer, shape_type: ShapeType) { for range in buffer.ranges.iter() { let contour = &buffer.points[range.clone()]; self.add_contour(contour, shape_type); @@ -256,7 +256,7 @@ impl Overlay { &mut self, fill_rule: FillRule, overlay_rule: OverlayRule, - ) -> Vec { + ) -> Vec> { self.split_solver.split_segments(&mut self.segments, &self.solver); if self.segments.is_empty() { return Vec::new(); @@ -282,7 +282,7 @@ impl Overlay { /// Convert into vectors from the added paths or shapes, applying the specified build rule. This method is particularly useful for development purposes and for creating visualizations in educational demos, where understanding the impact of different rules on the final geometry is crucial. /// - `fill_rule`: The build rule to use for the shapes. - pub fn build_separate_vectors(&mut self, fill_rule: FillRule) -> Vec { + pub fn build_separate_vectors(&mut self, fill_rule: FillRule) -> Vec> { self.split_solver.split_segments(&mut self.segments, &self.solver); if self.segments.is_empty() { return Vec::new(); @@ -295,7 +295,7 @@ impl Overlay { /// Convert into `OverlayGraph` from the added paths or shapes using the specified build rule. This graph is the foundation for executing boolean operations, allowing for the analysis and manipulation of the geometric data. The `OverlayGraph` created by this method represents a preprocessed state of the input shapes, optimized for the application of boolean operations based on the provided build rule. /// - `fill_rule`: Specifies the rule for determining filled areas within the shapes, influencing how the resulting graph represents intersections and unions. #[inline] - pub fn build_graph_view(&mut self, fill_rule: FillRule) -> Option> { + pub fn build_graph_view(&mut self, fill_rule: FillRule) -> Option> { self.split_solver.split_segments(&mut self.segments, &self.solver); if self.segments.is_empty() { return None; @@ -316,11 +316,11 @@ impl Overlay { /// ### Parameters: /// - `overlay_rule`: The boolean operation rule to apply, determining how shapes are combined or subtracted. /// - `fill_rule`: Specifies the rule for determining filled areas within the shapes, influencing how the resulting graph represents intersections and unions. - /// - Returns: A vector of `IntShape` that meet the specified area criteria, representing the cleaned-up geometric result. + /// - Returns: A vector of `IntShape` that meet the specified area criteria, representing the cleaned-up geometric result. /// # Shape Representation - /// The output is a `IntShapes`, where: - /// - The outer `Vec` represents a set of shapes. - /// - Each shape `Vec` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. + /// The output is a `IntShapes`, where: + /// - The outer `Vec>` represents a set of shapes. + /// - Each shape `Vec>` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. /// - Each path `Vec` is a sequence of points, forming a closed path. /// /// Note: Outer boundary paths have a counterclockwise order, and holes have a clockwise order. @@ -346,7 +346,7 @@ impl Overlay { /// without subsequent modifications. By excluding unnecessary graph structures, it optimizes performance, /// particularly for complex or resource-intensive geometries. #[inline] - pub fn overlay(&mut self, overlay_rule: OverlayRule, fill_rule: FillRule) -> IntShapes { + pub fn overlay(&mut self, overlay_rule: OverlayRule, fill_rule: FillRule) -> IntShapes { self.split_solver.split_segments(&mut self.segments, &self.solver); if self.segments.is_empty() { return Vec::new(); @@ -369,18 +369,18 @@ impl Overlay { /// Executes a single Boolean operation and writes the result into a flat contour buffer. /// /// This is a lower-allocation alternative to [`Self::overlay`] when you want flat contour - /// output (`points` + `ranges`) instead of nested `IntShapes`. + /// output (`points` + `ranges`) instead of nested `IntShapes`. /// /// - `overlay_rule`: The Boolean operation to apply. /// - `fill_rule`: Fill rule used to determine interior regions. - /// - `output`: Destination [`FlatContoursBuffer`] that receives resulting contours. + /// - `output`: Destination [`FlatContoursBuffer`] that receives resulting contours. /// Existing buffer contents are replaced. #[inline] pub fn overlay_into( &mut self, overlay_rule: OverlayRule, fill_rule: FillRule, - output: &mut FlatContoursBuffer, + output: &mut FlatContoursBuffer, ) { self.split_solver.split_segments(&mut self.segments, &self.solver); if self.segments.is_empty() { @@ -846,7 +846,7 @@ mod tests { #[test] fn test_empty_input() { - let subj: &[IntContour] = &[]; + let subj: &[IntContour] = &[]; let mut overlay = Overlay::with_contours(subj, &[]); let result = overlay.overlay(OverlayRule::Subject, FillRule::NonZero); diff --git a/iOverlay/src/core/predicate.rs b/iOverlay/src/core/predicate.rs index 08c6982..4c91bec 100644 --- a/iOverlay/src/core/predicate.rs +++ b/iOverlay/src/core/predicate.rs @@ -37,7 +37,7 @@ impl PointCoincidenceChecker { /// clip in the segment are skipped for clip collection /// - Similarly for clip-only interior segments #[inline] - pub(crate) fn add_segment(&mut self, segment: &Segment, fill: SegmentFill) { + pub(crate) fn add_segment(&mut self, segment: &Segment, fill: SegmentFill) { // Skip inner segments optimization: // If segment is entirely inside one shape's interior (filled on both sides) // and has no contribution from the other shape, it's not on a boundary @@ -116,14 +116,14 @@ impl IntersectsHandler { } } -impl FillHandler for IntersectsHandler { +impl FillHandler for IntersectsHandler { type Output = bool; #[inline(always)] fn handle( &mut self, _index: usize, - segment: &Segment, + segment: &Segment, fill: SegmentFill, ) -> ControlFlow { // Shapes intersect if both contribute to any segment (interior overlap or boundary contact) @@ -152,14 +152,14 @@ impl FillHandler for IntersectsHandler { /// Early-exits `true` on first interior overlap. pub(crate) struct InteriorsIntersectHandler; -impl FillHandler for InteriorsIntersectHandler { +impl FillHandler for InteriorsIntersectHandler { type Output = bool; #[inline(always)] fn handle( &mut self, _index: usize, - _segment: &Segment, + _segment: &Segment, fill: SegmentFill, ) -> ControlFlow { // Interiors intersect if both shapes fill the same side @@ -197,14 +197,14 @@ impl TouchesHandler { } } -impl FillHandler for TouchesHandler { +impl FillHandler for TouchesHandler { type Output = bool; #[inline(always)] fn handle( &mut self, _index: usize, - segment: &Segment, + segment: &Segment, fill: SegmentFill, ) -> ControlFlow { // Interior overlap = not a touch (early exit false) @@ -243,14 +243,14 @@ impl PointIntersectsHandler { } } -impl FillHandler for PointIntersectsHandler { +impl FillHandler for PointIntersectsHandler { type Output = bool; #[inline(always)] fn handle( &mut self, _index: usize, - segment: &Segment, + segment: &Segment, fill: SegmentFill, ) -> ControlFlow { // Interior overlap = not a point-only intersection (early exit false) @@ -285,14 +285,14 @@ impl WithinHandler { } } -impl FillHandler for WithinHandler { +impl FillHandler for WithinHandler { type Output = bool; #[inline(always)] fn handle( &mut self, _index: usize, - _segment: &Segment, + _segment: &Segment, fill: SegmentFill, ) -> ControlFlow { let subj_top = (fill & SUBJ_TOP) != 0; @@ -324,7 +324,14 @@ mod tests { use super::*; use crate::geom::x_segment::XSegment; - fn make_segment(ax: i32, ay: i32, bx: i32, by: i32, subj: i32, clip: i32) -> Segment { + fn make_segment( + ax: i32, + ay: i32, + bx: i32, + by: i32, + subj: i32, + clip: i32, + ) -> Segment { Segment { x_segment: XSegment { a: IntPoint::new(ax, ay), diff --git a/iOverlay/src/core/relate.rs b/iOverlay/src/core/relate.rs index e62cd7e..6bc0e7d 100644 --- a/iOverlay/src/core/relate.rs +++ b/iOverlay/src/core/relate.rs @@ -38,9 +38,9 @@ pub struct PredicateOverlay { pub solver: Solver, /// Fill rule for determining polygon interiors. pub fill_rule: FillRule, - pub(crate) segments: Vec>, + pub(crate) segments: Vec>, pub(crate) split_solver: SplitSolver, - sweep_runner: SweepRunner, + sweep_runner: SweepRunner, } impl PredicateOverlay { @@ -55,7 +55,7 @@ impl PredicateOverlay { } } - fn evaluate>(&mut self, handler: H) -> T { + fn evaluate>(&mut self, handler: H) -> T { if self.segments.is_empty() { return T::default(); } @@ -134,28 +134,28 @@ impl PredicateOverlay { } /// Adds multiple paths to the overlay as either subject or clip paths. - /// - `contours`: An array of `IntContour` instances to be added to the overlay. + /// - `contours`: An array of `IntContour` instances to be added to the overlay. /// - `shape_type`: Specifies the role of the added paths in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_contours(&mut self, contours: &[IntContour], shape_type: ShapeType) { + pub fn add_contours(&mut self, contours: &[IntContour], shape_type: ShapeType) { for contour in contours.iter() { self.add_contour(contour, shape_type); } } /// Adds a single shape to the overlay as either a subject or clip shape. - /// - `shape`: A reference to a `IntShape` instance to be added. + /// - `shape`: A reference to a `IntShape` instance to be added. /// - `shape_type`: Specifies the role of the added shape in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_shape(&mut self, shape: &IntShape, shape_type: ShapeType) { + pub fn add_shape(&mut self, shape: &IntShape, shape_type: ShapeType) { self.add_contours(shape, shape_type); } /// Adds multiple shapes to the overlay as either subject or clip shapes. - /// - `shapes`: An array of `IntShape` instances to be added to the overlay. + /// - `shapes`: An array of `IntShape` instances to be added to the overlay. /// - `shape_type`: Specifies the role of the added shapes in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_shapes(&mut self, shapes: &[IntShape], shape_type: ShapeType) { + pub fn add_shapes(&mut self, shapes: &[IntShape], shape_type: ShapeType) { for shape in shapes.iter() { self.add_contours(shape, shape_type); } diff --git a/iOverlay/src/core/simplify.rs b/iOverlay/src/core/simplify.rs index a4196bd..e5376b5 100644 --- a/iOverlay/src/core/simplify.rs +++ b/iOverlay/src/core/simplify.rs @@ -24,18 +24,18 @@ pub trait Simplify { /// - `fill_rule`: Fill rule to determine filled areas (non-zero, even-odd, positive, negative). /// - `options`: Adjust custom behavior. /// # Shape Representation - /// The output is a `IntShapes`, where: - /// - The outer `Vec` represents a set of shapes. - /// - Each shape `Vec` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. + /// The output is a `IntShapes`, where: + /// - The outer `Vec>` represents a set of shapes. + /// - Each shape `Vec>` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. /// - Each path `Vec` is a sequence of points, forming a closed path. /// /// Note: Outer boundary paths have a **main_direction** order, and holes have an opposite to **main_direction** order. - fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes; + fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes; } impl Simplify for [IntPoint] { #[inline] - fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { + fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { match Overlay::new_custom(self.len(), options, Default::default()).simplify_contour(self, fill_rule) { Some(shapes) => shapes, None => vec![vec![self.to_vec()]], @@ -43,9 +43,9 @@ impl Simplify for [IntPoint] { } } -impl Simplify for [IntContour] { +impl Simplify for [IntContour] { #[inline] - fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { + fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { match Overlay::new_custom(self.len(), options, Default::default()).simplify_shape(self, fill_rule) { Some(shapes) => shapes, None => vec![self.to_vec()], @@ -53,9 +53,9 @@ impl Simplify for [IntContour] { } } -impl Simplify for [IntShape] { +impl Simplify for [IntShape] { #[inline] - fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { + fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { Overlay::new_custom(self.points_count(), options, Default::default()).simplify_shapes(self, fill_rule) } } @@ -72,9 +72,9 @@ impl Overlay { /// Skips full overlay if the contour is already simple (no splits, no loops, no collinear issues). /// Ensures correct winding order based on `fill_rule` and `options.output_direction`. /// - /// Returns `None` if the contour is valid and needs no changes, or `Some(IntShapes)` with the simplified result. + /// Returns `None` if the contour is valid and needs no changes, or `Some(IntShapes)` with the simplified result. #[inline] - pub fn simplify_contour(&mut self, contour: &[IntPoint], fill_rule: FillRule) -> Option { + pub fn simplify_contour(&mut self, contour: &[IntPoint], fill_rule: FillRule) -> Option> { self.clear(); let is_perfect = self.find_intersections(contour); @@ -148,7 +148,11 @@ impl Overlay { } #[inline] - pub fn simplify_shape(&mut self, shape: &[IntContour], fill_rule: FillRule) -> Option { + pub fn simplify_shape( + &mut self, + shape: &[IntContour], + fill_rule: FillRule, + ) -> Option> { if shape.len() == 1 { return self.simplify_contour(&shape[0], fill_rule); } @@ -158,14 +162,14 @@ impl Overlay { } #[inline] - pub fn simplify_shapes(&mut self, shapes: &[IntShape], fill_rule: FillRule) -> IntShapes { + pub fn simplify_shapes(&mut self, shapes: &[IntShape], fill_rule: FillRule) -> IntShapes { self.clear(); self.add_shapes(shapes, ShapeType::Subject); self.overlay(OverlayRule::Subject, fill_rule) } #[inline] - pub fn simplify_flat_buffer(&mut self, flat_buffer: &mut FlatContoursBuffer, fill_rule: FillRule) { + pub fn simplify_flat_buffer(&mut self, flat_buffer: &mut FlatContoursBuffer, fill_rule: FillRule) { self.clear(); if flat_buffer.is_single_contour() { diff --git a/iOverlay/src/core/solver.rs b/iOverlay/src/core/solver.rs index d2bee17..679fe1b 100644 --- a/iOverlay/src/core/solver.rs +++ b/iOverlay/src/core/solver.rs @@ -164,7 +164,7 @@ impl Solver { pub(crate) fn is_list_split( &self, - segments: &[Segment], + segments: &[Segment], ) -> bool { match self.strategy { List => true, @@ -175,12 +175,12 @@ impl Solver { pub(crate) fn is_fragmentation_required( &self, - segments: &[Segment], + segments: &[Segment], ) -> bool { segments.len() > Self::MIN_FRAGMENT_COUNT || self.strategy == Frag } - pub(crate) fn is_list_fill(&self, segments: &[Segment]) -> bool { + pub(crate) fn is_list_fill(&self, segments: &[Segment]) -> bool { match self.strategy { List => true, Tree | Frag => false, diff --git a/iOverlay/src/float/graph.rs b/iOverlay/src/float/graph.rs index d2b2fd3..fe62476 100644 --- a/iOverlay/src/float/graph.rs +++ b/iOverlay/src/float/graph.rs @@ -16,7 +16,7 @@ use i_shape::float::simple::SimplifyContour; /// providing methods to extract geometric shapes from the graph after applying boolean operations. /// [More information](https://ishape-rust.github.io/iShape-js/overlay/overlay_graph/overlay_graph.html) about Overlay Graph. pub struct FloatOverlayGraph<'a, P: FloatPointCompatible> { - pub graph: OverlayGraph<'a>, + pub graph: OverlayGraph<'a, i32>, pub adapter: FloatPointAdapter, clean_result: bool, } @@ -24,7 +24,7 @@ pub struct FloatOverlayGraph<'a, P: FloatPointCompatible> { impl<'a, P: FloatPointCompatible> FloatOverlayGraph<'a, P> { #[inline] pub(crate) fn new( - graph: OverlayGraph<'a>, + graph: OverlayGraph<'a, i32>, adapter: FloatPointAdapter, clean_result: bool, ) -> Self { diff --git a/iOverlay/src/float/overlay.rs b/iOverlay/src/float/overlay.rs index c23f925..41d22d5 100644 --- a/iOverlay/src/float/overlay.rs +++ b/iOverlay/src/float/overlay.rs @@ -352,7 +352,7 @@ impl FloatOverlay

{ output: &mut FloatFlatContoursBuffer

, ) { let preserve_output_collinear = self.overlay.options.preserve_output_collinear; - let mut int_output = FlatContoursBuffer::default(); + let mut int_output = FlatContoursBuffer::::default(); self.overlay .overlay_into(overlay_rule, fill_rule, &mut int_output); let iter = int_output.points.iter().map(|p| self.adapter.int_to_float(p)); diff --git a/iOverlay/src/float/string_graph.rs b/iOverlay/src/float/string_graph.rs index 0986a6f..4b73927 100644 --- a/iOverlay/src/float/string_graph.rs +++ b/iOverlay/src/float/string_graph.rs @@ -11,7 +11,7 @@ use i_shape::float::simple::SimplifyContour; /// 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. pub struct FloatStringGraph<'a, P: FloatPointCompatible> { - pub graph: StringGraph<'a>, + pub graph: StringGraph<'a, i32>, pub adapter: FloatPointAdapter, } diff --git a/iOverlay/src/geom/end.rs b/iOverlay/src/geom/end.rs index 29cee90..6abf2da 100644 --- a/iOverlay/src/geom/end.rs +++ b/iOverlay/src/geom/end.rs @@ -2,7 +2,7 @@ use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; #[derive(Clone, Copy)] -pub(crate) struct End { +pub(crate) struct End { pub(crate) index: usize, pub(crate) point: IntPoint, } diff --git a/iOverlay/src/geom/id_point.rs b/iOverlay/src/geom/id_point.rs index d9a82f1..127536b 100644 --- a/iOverlay/src/geom/id_point.rs +++ b/iOverlay/src/geom/id_point.rs @@ -1,13 +1,14 @@ +use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; #[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub(crate) struct IdPoint { +pub(crate) struct IdPoint { pub(crate) id: usize, - pub(crate) point: IntPoint, + pub(crate) point: IntPoint, } -impl IdPoint { - pub(crate) fn new(id: usize, point: IntPoint) -> Self { +impl IdPoint { + pub(crate) fn new(id: usize, point: IntPoint) -> Self { Self { id, point } } } diff --git a/iOverlay/src/geom/line_range.rs b/iOverlay/src/geom/line_range.rs index 6c2d7e0..92f3a6e 100644 --- a/iOverlay/src/geom/line_range.rs +++ b/iOverlay/src/geom/line_range.rs @@ -1,7 +1,7 @@ use i_float::int::number::int::IntNumber; #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) struct LineRange { +pub(crate) struct LineRange { pub(crate) min: I, pub(crate) max: I, } diff --git a/iOverlay/src/geom/v_segment.rs b/iOverlay/src/geom/v_segment.rs index 6f3ae48..ee3b245 100644 --- a/iOverlay/src/geom/v_segment.rs +++ b/iOverlay/src/geom/v_segment.rs @@ -7,7 +7,7 @@ use i_float::triangle::Triangle; use i_tree::{Expiration, ExpiredKey}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) struct VSegment { +pub(crate) struct VSegment { pub(crate) a: IntPoint, pub(crate) b: IntPoint, } diff --git a/iOverlay/src/geom/x_segment.rs b/iOverlay/src/geom/x_segment.rs index 516392f..ac615ec 100644 --- a/iOverlay/src/geom/x_segment.rs +++ b/iOverlay/src/geom/x_segment.rs @@ -4,7 +4,7 @@ use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) struct XSegment { +pub(crate) struct XSegment { pub(crate) a: IntPoint, pub(crate) b: IntPoint, } diff --git a/iOverlay/src/mesh/outline/builder.rs b/iOverlay/src/mesh/outline/builder.rs index 5f56bb9..b56b89b 100644 --- a/iOverlay/src/mesh/outline/builder.rs +++ b/iOverlay/src/mesh/outline/builder.rs @@ -19,7 +19,7 @@ trait OutlineBuild { &self, path: &[P], adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ); fn capacity(&self, points_count: usize) -> usize; @@ -71,7 +71,7 @@ impl OutlineBuilder

{ &self, path: &[P], adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { self.builder.build(path, adapter, segments); } @@ -93,7 +93,7 @@ impl, P: FloatPointCompatible> OutlineBuild

for Builder, - segments: &mut Vec>, + segments: &mut Vec>, ) { if path.len() < 2 { return; @@ -118,7 +118,7 @@ impl, P: FloatPointCompatible> Builder { &self, path: &[P], adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { let iter = path.iter().map(|p| adapter.float_to_int(p)); let mut uniq_segments = if let Some(iter) = UniqueSegmentsIter::new(iter) { @@ -153,7 +153,7 @@ impl, P: FloatPointCompatible> Builder { s0: &OffsetSection

, s1: &OffsetSection

, adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { let vi = s1.b - s1.a; let vp = s0.b - s0.a; diff --git a/iOverlay/src/mesh/outline/builder_join.rs b/iOverlay/src/mesh/outline/builder_join.rs index 5ec4639..d085168 100644 --- a/iOverlay/src/mesh/outline/builder_join.rs +++ b/iOverlay/src/mesh/outline/builder_join.rs @@ -16,7 +16,7 @@ pub(super) trait JoinBuilder { s0: &OffsetSection

, s1: &OffsetSection

, adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ); fn capacity(&self) -> usize; fn additional_offset(&self, radius: P::Scalar) -> P::Scalar; @@ -30,7 +30,7 @@ impl BevelJoinBuilder { s0: &OffsetSection

, s1: &OffsetSection

, _adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { debug_assert_ne!(s0.b_top, s1.a_top, "must be validated before"); segments.push(Segment::subject(s0.b_top, s1.a_top)); @@ -44,7 +44,7 @@ impl JoinBuilder

for BevelJoinBuilder { s0: &OffsetSection

, s1: &OffsetSection

, adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { Self::join(s0, s1, adapter, segments); } @@ -97,7 +97,7 @@ impl JoinBuilder

for MiterJoinBuilder { s0: &OffsetSection

, s1: &OffsetSection

, adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { let ia = s0.b_top; let ib = s1.a_top; @@ -197,7 +197,7 @@ impl JoinBuilder

for RoundJoinBuilder { s0: &OffsetSection

, s1: &OffsetSection

, adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { let dot_product = FloatPointMath::dot_product(&s0.dir, &s1.dir); if self.limit_dot_product < dot_product { diff --git a/iOverlay/src/mesh/outline/offset.rs b/iOverlay/src/mesh/outline/offset.rs index ea6e819..794840a 100644 --- a/iOverlay/src/mesh/outline/offset.rs +++ b/iOverlay/src/mesh/outline/offset.rs @@ -277,7 +277,7 @@ impl OutlineSolver

{ let mut segments = Vec::new(); let mut bool_buffer = BooleanExtractionBuffer::default(); - let mut flat_buffer = FlatContoursBuffer::default(); + let mut flat_buffer = FlatContoursBuffer::::default(); for path in source.iter_paths() { let area = path.unsafe_int_area(&self.adapter); @@ -343,7 +343,7 @@ impl OutlineSolver

{ let clean_result = options.clean_result; let mut overlay = self.build_overlay(source, options); - let mut int_output = FlatContoursBuffer::default(); + let mut int_output = FlatContoursBuffer::::default(); overlay.overlay_into(OverlayRule::Subject, FillRule::Positive, &mut int_output); let iter = int_output.points.iter().map(|p| self.adapter.int_to_float(p)); output.set_with_iter(iter, &int_output.ranges); diff --git a/iOverlay/src/mesh/outline/section.rs b/iOverlay/src/mesh/outline/section.rs index 3f36e99..9503a0a 100644 --- a/iOverlay/src/mesh/outline/section.rs +++ b/iOverlay/src/mesh/outline/section.rs @@ -14,7 +14,7 @@ pub(super) struct OffsetSection { impl OffsetSection

{ #[inline] - pub(super) fn top_segment(&self) -> Option> { + pub(super) fn top_segment(&self) -> Option> { if self.a_top != self.b_top { Some(Segment::subject(self.a_top, self.b_top)) } else { @@ -23,7 +23,7 @@ impl OffsetSection

{ } #[inline] - pub(super) fn a_segment(&self) -> Option> { + pub(super) fn a_segment(&self) -> Option> { if self.a_top != self.a { Some(Segment::subject(self.a, self.a_top)) } else { @@ -32,7 +32,7 @@ impl OffsetSection

{ } #[inline] - pub(super) fn b_segment(&self) -> Option> { + pub(super) fn b_segment(&self) -> Option> { if self.b_top != self.b { Some(Segment::subject(self.b_top, self.b)) } else { diff --git a/iOverlay/src/mesh/outline/uniq_iter.rs b/iOverlay/src/mesh/outline/uniq_iter.rs index 3026da6..6ca789b 100644 --- a/iOverlay/src/mesh/outline/uniq_iter.rs +++ b/iOverlay/src/mesh/outline/uniq_iter.rs @@ -89,7 +89,7 @@ where } #[inline] -fn include_point(p0: IntPoint, p1: IntPoint, p2: IntPoint) -> bool { +fn include_point(p0: IntPoint, p1: IntPoint, p2: IntPoint) -> bool { let a = p1 - p0; let b = p1 - p2; diff --git a/iOverlay/src/mesh/overlay.rs b/iOverlay/src/mesh/overlay.rs index 64e759a..a9c4377 100644 --- a/iOverlay/src/mesh/overlay.rs +++ b/iOverlay/src/mesh/overlay.rs @@ -8,19 +8,19 @@ use alloc::vec::Vec; impl Overlay { #[inline] - pub(crate) fn add_segments(&mut self, segments: &[Segment]) { + pub(crate) fn add_segments(&mut self, segments: &[Segment]) { self.segments.extend_from_slice(segments); } #[inline] - pub(crate) fn with_segments(segments: Vec>) -> Self { + pub(crate) fn with_segments(segments: Vec>) -> Self { Self { solver: Default::default(), options: Default::default(), boolean_buffer: None, segments, split_solver: SplitSolver::new(), - graph_builder: GraphBuilder::::new(), + graph_builder: GraphBuilder::::new(), } } } diff --git a/iOverlay/src/mesh/stroke/builder.rs b/iOverlay/src/mesh/stroke/builder.rs index ad396ce..c7f3ba7 100644 --- a/iOverlay/src/mesh/stroke/builder.rs +++ b/iOverlay/src/mesh/stroke/builder.rs @@ -16,7 +16,7 @@ trait StrokeBuild { path: &[P], is_closed_path: bool, adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ); fn capacity(&self, paths_count: usize, points_count: usize, is_closed_path: bool) -> usize; @@ -71,7 +71,7 @@ impl StrokeBuilder

{ path: &[P], is_closed_path: bool, adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { self.builder.build(path, is_closed_path, adapter, segments); } @@ -94,7 +94,7 @@ impl, P: FloatPointCompatible> StrokeBuild

for Builder, - segments: &mut Vec>, + segments: &mut Vec>, ) { if is_closed_path { self.closed_segments(path, adapter, segments); @@ -127,7 +127,7 @@ impl, P: FloatPointCompatible> Builder { &self, path: &[P], adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { // build segments only from points which are not equal in int space @@ -180,7 +180,7 @@ impl, P: FloatPointCompatible> Builder { &self, path: &[P], adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { if path.len() < 2 { return; diff --git a/iOverlay/src/mesh/stroke/builder_cap.rs b/iOverlay/src/mesh/stroke/builder_cap.rs index bfff84f..e7be30b 100644 --- a/iOverlay/src/mesh/stroke/builder_cap.rs +++ b/iOverlay/src/mesh/stroke/builder_cap.rs @@ -70,7 +70,7 @@ impl CapBuilder

{ &self, section: &Section

, adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { let mut a = adapter.float_to_int(§ion.a_top); if let Some(points) = &self.points { @@ -92,7 +92,7 @@ impl CapBuilder

{ &self, section: &Section

, adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { let mut a = adapter.float_to_int(§ion.b_bot); if let Some(points) = &self.points { diff --git a/iOverlay/src/mesh/stroke/builder_join.rs b/iOverlay/src/mesh/stroke/builder_join.rs index 42ff5ea..97f670b 100644 --- a/iOverlay/src/mesh/stroke/builder_join.rs +++ b/iOverlay/src/mesh/stroke/builder_join.rs @@ -16,7 +16,7 @@ pub(super) trait JoinBuilder { s0: &Section

, s1: &Section

, adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ); fn capacity(&self) -> usize; fn additional_offset(&self, radius: P::Scalar) -> P::Scalar; @@ -30,7 +30,7 @@ impl BevelJoinBuilder { s0: &Section

, s1: &Section

, adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { Self::add_segment(&s0.b_top, &s1.a_top, adapter, segments); } @@ -40,7 +40,7 @@ impl BevelJoinBuilder { s0: &Section

, s1: &Section

, adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { Self::add_segment(&s1.a_bot, &s0.b_bot, adapter, segments); } @@ -50,7 +50,7 @@ impl BevelJoinBuilder { a: &P, b: &P, adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { let ia = adapter.float_to_int(a); let ib = adapter.float_to_int(b); @@ -67,7 +67,7 @@ impl JoinBuilder

for BevelJoinBuilder { s0: &Section

, s1: &Section

, adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { Self::join_top(s0, s1, adapter, segments); Self::join_bot(s0, s1, adapter, segments); @@ -123,7 +123,7 @@ impl JoinBuilder

for MiterJoinBuilder { s0: &Section

, s1: &Section

, adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { let cross_product = FloatPointMath::cross_product(&s0.dir, &s1.dir); if cross_product.abs() < P::Scalar::from_float(0.0001) { @@ -243,7 +243,7 @@ impl JoinBuilder

for RoundJoinBuilder { s0: &Section

, s1: &Section

, adapter: &FloatPointAdapter, - segments: &mut Vec>, + segments: &mut Vec>, ) { let dot_product = FloatPointMath::dot_product(&s0.dir, &s1.dir); if self.limit_dot_product < dot_product { diff --git a/iOverlay/src/mesh/stroke/offset.rs b/iOverlay/src/mesh/stroke/offset.rs index 72fe160..04173be 100644 --- a/iOverlay/src/mesh/stroke/offset.rs +++ b/iOverlay/src/mesh/stroke/offset.rs @@ -349,7 +349,7 @@ impl StrokeSolver

{ let mut overlay = Overlay::with_segments(segments); overlay.options = options.int_with_adapter(&self.adapter); - let mut int_output = FlatContoursBuffer::default(); + let mut int_output = FlatContoursBuffer::::default(); overlay.overlay_into(OverlayRule::Subject, FillRule::Positive, &mut int_output); let iter = int_output.points.iter().map(|p| self.adapter.int_to_float(p)); diff --git a/iOverlay/src/mesh/stroke/section.rs b/iOverlay/src/mesh/stroke/section.rs index 9f17f03..b67ff54 100644 --- a/iOverlay/src/mesh/stroke/section.rs +++ b/iOverlay/src/mesh/stroke/section.rs @@ -44,7 +44,7 @@ pub(crate) trait SectionToSegment { fn add_section(&mut self, section: &Section

, adapter: &FloatPointAdapter); } -impl SectionToSegment

for Vec> { +impl SectionToSegment

for Vec> { fn add_section(&mut self, section: &Section

, adapter: &FloatPointAdapter) { let a_top = adapter.float_to_int(§ion.a_top); let b_top = adapter.float_to_int(§ion.b_top); diff --git a/iOverlay/src/mesh/subject.rs b/iOverlay/src/mesh/subject.rs index 099069a..3602641 100644 --- a/iOverlay/src/mesh/subject.rs +++ b/iOverlay/src/mesh/subject.rs @@ -3,7 +3,7 @@ use crate::segm::boolean::ShapeCountBoolean; use crate::segm::segment::Segment; use i_float::int::point::IntPoint; -impl Segment { +impl Segment { #[inline] pub(crate) fn subject(p0: IntPoint, p1: IntPoint) -> Self { if p0 < p1 { diff --git a/iOverlay/src/segm/build.rs b/iOverlay/src/segm/build.rs index 512e15c..c7aaf6b 100644 --- a/iOverlay/src/segm/build.rs +++ b/iOverlay/src/segm/build.rs @@ -17,7 +17,7 @@ pub(crate) trait BuildSegments { ) -> bool; } -impl BuildSegments for Vec> { +impl BuildSegments for Vec> { #[inline] fn append_path_iter>>( &mut self, @@ -33,8 +33,13 @@ impl BuildSegments for Vec> } } -fn build_segments_with_filter, It: Iterator>, C: WindingCount>( - segments: &mut Vec>, +fn build_segments_with_filter< + I: IntNumber, + F: PointFilter, + It: Iterator>, + C: WindingCount, +>( + segments: &mut Vec>, mut iter: It, shape_type: ShapeType, ) -> bool { @@ -122,7 +127,7 @@ impl PointFilter for DropCollinear { } } -impl Segment { +impl Segment { #[inline] pub(crate) fn with_ab(p0: IntPoint, p1: IntPoint, direct: C, invert: C) -> Self { if p0 < p1 { @@ -141,7 +146,7 @@ impl Segment { } } -impl> Segment { +impl> Segment { #[inline] pub(crate) fn with_ab_and_data(p0: IntPoint, p1: IntPoint, direct: C, invert: C, data: D) -> Self { if p0 < p1 { @@ -415,7 +420,7 @@ mod tests { } fn test_count(points: &[IntPoint], count: usize, keep_same_line_points: bool) { - let mut segments: Vec> = Vec::new(); + let mut segments: Vec> = Vec::new(); segments.append_path_iter(points.iter().copied(), ShapeType::Subject, keep_same_line_points); segments.merge_if_needed(); @@ -425,7 +430,7 @@ mod tests { fn test_roll_count(slice: &[IntPoint], count: usize, keep_same_line_points: bool) { let mut points = slice.to_vec(); let n = points.len(); - let mut segments: Vec> = Vec::with_capacity(n); + let mut segments: Vec> = Vec::with_capacity(n); for _ in 0..n { segments.append_path_iter(points.iter().copied(), ShapeType::Subject, keep_same_line_points); segments.merge_if_needed(); diff --git a/iOverlay/src/segm/merge.rs b/iOverlay/src/segm/merge.rs index fba3d02..7061954 100644 --- a/iOverlay/src/segm/merge.rs +++ b/iOverlay/src/segm/merge.rs @@ -2,12 +2,13 @@ use crate::core::edge_data::{EdgeDataMerge, OverlayEdgeData}; use crate::segm::segment::Segment; use crate::segm::winding::WindingCount; use alloc::vec::Vec; +use i_float::int::number::int::IntNumber; pub(crate) trait ShapeSegmentsMerge { fn merge_if_needed(&mut self) -> bool; } -impl> ShapeSegmentsMerge for Vec> { +impl> ShapeSegmentsMerge for Vec> { fn merge_if_needed(&mut self) -> bool { if self.len() < 2 { return false; @@ -28,7 +29,10 @@ impl> ShapeSegmentsMerge for Vec>(segments: &mut [Segment], after: usize) -> usize { +fn merge>( + segments: &mut [Segment], + after: usize, +) -> usize { let mut i = after; let mut j = i - 1; let mut prev = segments[j]; @@ -73,7 +77,7 @@ mod tests { #[test] fn test_merge_if_needed_empty() { - let mut segments: Vec> = Vec::new(); + let mut segments: Vec> = Vec::new(); segments.merge_if_needed(); assert!( segments.is_empty(), diff --git a/iOverlay/src/segm/segment.rs b/iOverlay/src/segm/segment.rs index 5fc2ef1..944f7ad 100644 --- a/iOverlay/src/segm/segment.rs +++ b/iOverlay/src/segm/segment.rs @@ -22,13 +22,13 @@ pub const BOTH_BOTTOM: SegmentFill = SUBJ_BOTTOM | CLIP_BOTTOM; pub const ALL: SegmentFill = SUBJ_BOTH | CLIP_BOTH; #[derive(Debug, Clone, Copy)] -pub(crate) struct Segment { +pub(crate) struct Segment { pub(crate) x_segment: XSegment, pub(crate) count: C, pub(crate) data: D, } -impl Segment { +impl Segment { #[inline(always)] #[allow(dead_code)] pub(crate) fn create_and_validate(a: IntPoint, b: IntPoint, count: C) -> Self { @@ -36,7 +36,7 @@ impl Segment { } } -impl, I: IntNumber> Segment { +impl> Segment { #[inline(always)] pub(crate) fn create_and_validate_with_data(a: IntPoint, b: IntPoint, count: C, data: D) -> Self { if a < b { @@ -55,23 +55,23 @@ impl, I: IntNumber> Segment { } } -impl PartialEq for Segment { +impl PartialEq for Segment { #[inline(always)] fn eq(&self, other: &Self) -> bool { self.x_segment == other.x_segment } } -impl Eq for Segment {} +impl Eq for Segment {} -impl PartialOrd for Segment { +impl PartialOrd for Segment { #[inline(always)] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for Segment { +impl Ord for Segment { #[inline(always)] fn cmp(&self, other: &Self) -> Ordering { self.x_segment.cmp(&other.x_segment) diff --git a/iOverlay/src/segm/sort.rs b/iOverlay/src/segm/sort.rs index c5bc145..f7b8d17 100644 --- a/iOverlay/src/segm/sort.rs +++ b/iOverlay/src/segm/sort.rs @@ -1,11 +1,15 @@ use crate::segm::segment::Segment; +use i_float::int::number::int::IntNumber; +use i_key_sort::sort::key::SortKey; use i_key_sort::sort::two_keys_cmp::TwoKeysAndCmpSort; pub(crate) trait ShapeSegmentsSort { fn sort_by_ab(&mut self, parallel: bool); } -impl ShapeSegmentsSort for [Segment] { +impl ShapeSegmentsSort + for [Segment] +{ #[inline] fn sort_by_ab(&mut self, parallel: bool) { self.sort_by_two_keys_then_by( diff --git a/iOverlay/src/split/fragment.rs b/iOverlay/src/split/fragment.rs index 63e898d..53066e9 100644 --- a/iOverlay/src/split/fragment.rs +++ b/iOverlay/src/split/fragment.rs @@ -4,13 +4,13 @@ use i_float::int::rect::IntRect; #[derive(Debug, Clone)] pub(super) struct Fragment { pub(super) index: usize, - pub(super) rect: IntRect, - pub(super) x_segment: XSegment, + pub(super) rect: IntRect, + pub(super) x_segment: XSegment, } impl Fragment { #[inline] - pub(super) fn with_index_and_segment(index: usize, x_segment: XSegment) -> Self { + pub(super) fn with_index_and_segment(index: usize, x_segment: XSegment) -> Self { let (min_y, max_y) = if x_segment.a.y < x_segment.b.y { (x_segment.a.y, x_segment.b.y) } else { diff --git a/iOverlay/src/split/grid_layout.rs b/iOverlay/src/split/grid_layout.rs index 8a13447..4b43067 100644 --- a/iOverlay/src/split/grid_layout.rs +++ b/iOverlay/src/split/grid_layout.rs @@ -9,7 +9,7 @@ use i_float::int::rect::IntRect; pub(super) struct BorderVSegment { pub(super) id: usize, pub(super) x: i32, - pub(super) y_range: LineRange, + pub(super) y_range: LineRange, } pub(super) struct FragmentBuffer { @@ -31,7 +31,7 @@ impl FragmentBuffer { pub(super) fn init_fragment_buffer(&mut self, iter: I) where - I: Iterator, + I: Iterator>, { let mut counts = vec![0; self.groups.len()]; for s in iter { @@ -83,7 +83,7 @@ impl FragmentBuffer { } #[inline] - pub(super) fn add_segment(&mut self, segment_index: usize, s: XSegment) { + pub(super) fn add_segment(&mut self, segment_index: usize, s: XSegment) { if s.a.y == s.b.y { self.add_horizontal(segment_index, s); return; @@ -189,7 +189,7 @@ impl FragmentBuffer { ); } - fn add_horizontal(&mut self, segment_index: usize, s: XSegment) { + fn add_horizontal(&mut self, segment_index: usize, s: XSegment) { let i0 = self.layout.index(s.a.x); let i1 = self.layout.index(s.b.x - 1); @@ -263,7 +263,7 @@ impl GridLayout { pub(super) fn new(iter: I, count: usize) -> Option where - I: Iterator, + I: Iterator>, { let mut iter = iter.peekable(); let first = iter.peek()?; @@ -1539,7 +1539,7 @@ mod tests { } #[inline] - fn rect_compare(a: &IntRect, b: &IntRect) { + fn rect_compare(a: &IntRect, b: &IntRect) { assert_eq!(a.min_x, b.min_x); assert_eq!(a.max_x, b.max_x); assert_eq!(a.min_y, b.min_y); @@ -1547,7 +1547,7 @@ mod tests { } #[inline] - fn validate_rect(segment: &XSegment, rect: &IntRect) { + fn validate_rect(segment: &XSegment, rect: &IntRect) { let p0 = IntPoint::new(rect.min_x, rect.min_y); let p1 = IntPoint::new(rect.max_x, rect.min_y); let p2 = IntPoint::new(rect.min_x, rect.max_y); diff --git a/iOverlay/src/split/solver.rs b/iOverlay/src/split/solver.rs index 95b461f..2aad0db 100644 --- a/iOverlay/src/split/solver.rs +++ b/iOverlay/src/split/solver.rs @@ -23,7 +23,7 @@ impl SplitSolver { #[inline] pub(crate) fn split_segments>( &mut self, - segments: &mut Vec>, + segments: &mut Vec>, solver: &Solver, ) -> bool { if segments.is_empty() { @@ -40,7 +40,7 @@ impl SplitSolver { #[inline] fn split>( &mut self, - segments: &mut Vec>, + segments: &mut Vec>, solver: &Solver, ) -> bool { let is_list = solver.is_list_split(segments); @@ -61,8 +61,8 @@ impl SplitSolver { pub(super) fn cross( i: usize, j: usize, - ei: &XSegment, - ej: &XSegment, + ei: &XSegment, + ej: &XSegment, marks: &mut Vec, radius: i64, ) -> bool { @@ -136,7 +136,7 @@ impl SplitSolver { pub(super) fn apply>( &mut self, - segments: &mut Vec>, + segments: &mut Vec>, reusable_buffer: &mut Vec, solver: &Solver, ) { @@ -218,7 +218,7 @@ impl SplitSolver { } #[inline] - fn sort_sub_marks(marks: &mut [LineMark], x_seg: XSegment) { + fn sort_sub_marks(marks: &mut [LineMark], x_seg: XSegment) { let mut j0 = 0; let mut j = 1; @@ -248,7 +248,7 @@ impl SplitSolver { } #[inline] - fn y_range(j0: usize, j1: usize, s: XSegment, marks: &[LineMark]) -> (i32, i32) { + fn y_range(j0: usize, j1: usize, s: XSegment, marks: &[LineMark]) -> (i32, i32) { let y0 = if j0 == 0 { s.a.y } else { marks[j0 - 1].point.y }; let y1 = if j1 == marks.len() { s.b.y diff --git a/iOverlay/src/split/solver_fragment.rs b/iOverlay/src/split/solver_fragment.rs index 11b65a9..bd49486 100644 --- a/iOverlay/src/split/solver_fragment.rs +++ b/iOverlay/src/split/solver_fragment.rs @@ -14,7 +14,7 @@ impl SplitSolver { pub(super) fn fragment_split>( &mut self, snap_radius: SnapRadius, - segments: &mut Vec>, + segments: &mut Vec>, solver: &Solver, ) -> bool { let layout = diff --git a/iOverlay/src/split/solver_list.rs b/iOverlay/src/split/solver_list.rs index e9cfa93..e3a4f69 100644 --- a/iOverlay/src/split/solver_list.rs +++ b/iOverlay/src/split/solver_list.rs @@ -10,7 +10,7 @@ impl SplitSolver { pub(super) fn list_split>( &mut self, snap_radius: SnapRadius, - segments: &mut Vec>, + segments: &mut Vec>, solver: &Solver, ) -> bool { let mut need_to_fix = true; diff --git a/iOverlay/src/split/solver_tree.rs b/iOverlay/src/split/solver_tree.rs index 35f7559..67ea54d 100644 --- a/iOverlay/src/split/solver_tree.rs +++ b/iOverlay/src/split/solver_tree.rs @@ -14,7 +14,7 @@ use i_tree::seg::tree::SegExpTree; #[derive(Debug, Clone, Copy)] struct IdSegment { id: usize, - x_segment: XSegment, + x_segment: XSegment, } impl ExpiredVal for IdSegment { @@ -28,7 +28,7 @@ impl SplitSolver { pub(super) fn tree_split>( &mut self, snap_radius: SnapRadius, - segments: &mut Vec>, + segments: &mut Vec>, solver: &Solver, ) -> bool { let range: SegRange = segments.ver_range().into(); @@ -86,9 +86,9 @@ impl SplitSolver { } } -impl From for SegRange { +impl From> for SegRange { #[inline] - fn from(value: LineRange) -> Self { + fn from(value: LineRange) -> Self { Self { min: value.min, max: value.max, @@ -97,11 +97,11 @@ impl From for SegRange { } trait VerticalRange { - fn ver_range(&self) -> LineRange; + fn ver_range(&self) -> LineRange; } -impl VerticalRange for Vec> { - fn ver_range(&self) -> LineRange { +impl VerticalRange for Vec> { + fn ver_range(&self) -> LineRange { let mut min_y = self[0].x_segment.a.y; let mut max_y = min_y; @@ -119,7 +119,7 @@ impl VerticalRange for Vec> { } } -impl Segment { +impl Segment { #[inline] fn id_segment(&self, id: usize) -> IdSegment { IdSegment { diff --git a/iOverlay/src/string/clip.rs b/iOverlay/src/string/clip.rs index b94e583..db4ac9c 100644 --- a/iOverlay/src/string/clip.rs +++ b/iOverlay/src/string/clip.rs @@ -20,9 +20,9 @@ pub struct ClipRule { pub boundary_included: bool, } -impl StringGraph<'_> { +impl StringGraph<'_, i32> { #[inline] - pub(super) fn into_clip_string_lines(self) -> Vec { + pub(super) fn into_clip_string_lines(self) -> Vec> { let mut paths = Vec::new(); let links = self.links; @@ -61,10 +61,10 @@ impl StringGraph<'_> { #[inline] fn find_next_point( nodes: &[Vec], - links: &mut [OverlayLink], - a: IdPoint, + links: &mut [OverlayLink], + a: IdPoint, is_out_node: bool, - ) -> Option { + ) -> Option> { let node = unsafe { // SAFETY: a.id comes from an existing link endpoint, so it indexes nodes. nodes.get_unchecked(a.id) @@ -89,7 +89,7 @@ const CLIP_BACK: SegmentFill = STRING_BACK_CLIP << 2; const CLIP_FORWARD: SegmentFill = STRING_FORWARD_CLIP << 2; const CLIP_ALL: SegmentFill = CLIP_BACK | CLIP_FORWARD; -impl OverlayLink { +impl OverlayLink { #[inline] fn visit_if_possible(&mut self, is_forward: bool) -> bool { if is_forward { @@ -126,8 +126,8 @@ pub trait IntClip { /// - `clip_rule`: The rule for clipping, determining how the boundary and inversion settings affect the result. /// /// # Returns - /// A vector of `IntPath` instances representing the clipped sections of the input line. - fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec; + /// A vector of `IntPath` instances representing the clipped sections of the input line. + fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec>; /// Clips multiple lines according to the specified build and clip rules. /// - `lines`: A slice of `IntLine` instances representing lines to be clipped. @@ -135,82 +135,97 @@ pub trait IntClip { /// - `clip_rule`: The rule for clipping, determining how boundary and inversion settings affect the results. /// /// # Returns - /// A vector of `IntPath` instances containing the clipped portions of the input lines. - fn clip_lines(&self, lines: &[IntLine], fill_rule: FillRule, clip_rule: ClipRule) -> Vec; + /// A vector of `IntPath` instances containing the clipped portions of the input lines. + fn clip_lines(&self, lines: &[IntLine], fill_rule: FillRule, clip_rule: ClipRule) -> Vec>; /// Clips a single path according to the specified build and clip rules. - /// - `path`: A reference to an `IntPath`, which is a sequence of points representing the path to be clipped. + /// - `path`: A reference to an `IntPath`, which is a sequence of points representing the path to be clipped. /// - `fill_rule`: Specifies the rule determining the filled areas, influencing the inclusion of path segments. /// - `clip_rule`: The rule for clipping, determining how boundary and inversion settings affect the result. /// /// # Returns - /// A vector of `IntPath` instances representing the clipped sections of the path. - fn clip_path(&self, path: &IntPath, fill_rule: FillRule, clip_rule: ClipRule) -> Vec; + /// A vector of `IntPath` instances representing the clipped sections of the path. + fn clip_path(&self, path: &IntPath, fill_rule: FillRule, clip_rule: ClipRule) -> Vec>; /// Clips multiple paths according to the specified build and clip rules. - /// - `paths`: A slice of `IntPath` instances, each representing a path to be clipped. + /// - `paths`: A slice of `IntPath` instances, each representing a path to be clipped. /// - `fill_rule`: Specifies the rule determining the filled areas, influencing the inclusion of path segments. /// - `clip_rule`: The rule for clipping, determining how boundary and inversion settings affect the result. /// /// # Returns - /// A vector of `IntPath` instances containing the clipped portions of the input paths. - fn clip_paths(&self, paths: &[IntPath], fill_rule: FillRule, clip_rule: ClipRule) -> Vec; + /// A vector of `IntPath` instances containing the clipped portions of the input paths. + fn clip_paths( + &self, + paths: &[IntPath], + fill_rule: FillRule, + clip_rule: ClipRule, + ) -> Vec>; } -impl IntClip for IntShapes { +impl IntClip for IntShapes { #[inline] - fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec { + fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shapes(self); overlay.add_string_line(line); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_lines(&self, lines: &[IntLine], fill_rule: FillRule, clip_rule: ClipRule) -> Vec { + fn clip_lines(&self, lines: &[IntLine], fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shapes(self); overlay.add_string_lines(lines); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_path(&self, path: &IntPath, fill_rule: FillRule, clip_rule: ClipRule) -> Vec { + fn clip_path(&self, path: &IntPath, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shapes(self); overlay.add_string_path(path); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_paths(&self, paths: &[IntPath], fill_rule: FillRule, clip_rule: ClipRule) -> Vec { + fn clip_paths( + &self, + paths: &[IntPath], + fill_rule: FillRule, + clip_rule: ClipRule, + ) -> Vec> { let mut overlay = StringOverlay::with_shapes(self); overlay.add_string_paths(paths); overlay.clip_string_lines(fill_rule, clip_rule) } } -impl IntClip for IntShape { +impl IntClip for IntShape { #[inline] - fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec { + fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shape(self); overlay.add_string_line(line); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_lines(&self, lines: &[IntLine], fill_rule: FillRule, clip_rule: ClipRule) -> Vec { + fn clip_lines(&self, lines: &[IntLine], fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shape(self); overlay.add_string_lines(lines); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_path(&self, path: &IntPath, fill_rule: FillRule, clip_rule: ClipRule) -> Vec { + fn clip_path(&self, path: &IntPath, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shape(self); overlay.add_string_path(path); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_paths(&self, paths: &[IntPath], fill_rule: FillRule, clip_rule: ClipRule) -> Vec { + fn clip_paths( + &self, + paths: &[IntPath], + fill_rule: FillRule, + clip_rule: ClipRule, + ) -> Vec> { let mut overlay = StringOverlay::with_shape(self); overlay.add_string_paths(paths); overlay.clip_string_lines(fill_rule, clip_rule) @@ -219,28 +234,33 @@ impl IntClip for IntShape { impl IntClip for [IntPoint] { #[inline] - fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec { + fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shape_contour(self); overlay.add_string_line(line); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_lines(&self, lines: &[IntLine], fill_rule: FillRule, clip_rule: ClipRule) -> Vec { + fn clip_lines(&self, lines: &[IntLine], fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shape_contour(self); overlay.add_string_lines(lines); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_path(&self, path: &IntPath, fill_rule: FillRule, clip_rule: ClipRule) -> Vec { + fn clip_path(&self, path: &IntPath, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shape_contour(self); overlay.add_string_path(path); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_paths(&self, paths: &[IntPath], fill_rule: FillRule, clip_rule: ClipRule) -> Vec { + fn clip_paths( + &self, + paths: &[IntPath], + fill_rule: FillRule, + clip_rule: ClipRule, + ) -> Vec> { let mut overlay = StringOverlay::with_shape_contour(self); overlay.add_string_paths(paths); overlay.clip_string_lines(fill_rule, clip_rule) @@ -259,7 +279,7 @@ mod tests { #[test] fn test_empty_path() { - let path: IntPath = vec![]; + let path: IntPath = vec![]; let result_0 = path.clip_line( [IntPoint::new(0, 0), IntPoint::new(0, 0)], FillRule::NonZero, diff --git a/iOverlay/src/string/extract.rs b/iOverlay/src/string/extract.rs index fdf22da..02ec5f8 100644 --- a/iOverlay/src/string/extract.rs +++ b/iOverlay/src/string/extract.rs @@ -10,18 +10,18 @@ use alloc::vec::Vec; use i_shape::int::path::{ContourExtension, IntPath}; use i_shape::int::shape::IntShapes; -impl StringGraph<'_> { +impl StringGraph<'_, i32> { /// Extracts shapes from the graph based on the specified `StringRule`. /// - `string_rule`: The rule used to determine how shapes are extracted. /// # Shape Representation - /// The output is a `IntShapes`, where: - /// - The outer `Vec` represents a set of shapes. - /// - Each shape `Vec` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. + /// The output is a `IntShapes`, where: + /// - The outer `Vec>` represents a set of shapes. + /// - Each shape `Vec>` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. /// - Each path `Vec` is a sequence of points, forming a closed path. /// /// Note: Outer boundary paths have a counterclockwise order, and holes have a clockwise order. #[inline(always)] - pub fn extract_shapes(&self, string_rule: StringRule) -> IntShapes { + pub fn extract_shapes(&self, string_rule: StringRule) -> IntShapes { self.extract_shapes_custom(string_rule, Default::default()) } @@ -29,15 +29,19 @@ impl StringGraph<'_> { /// - `string_rule`: The rule used to determine how shapes are extracted. /// - `main_direction`: Winding direction for the **output** main (outer) contour. All hole contours will automatically use the opposite direction. Impact on **output** only! /// - `min_area`: The minimum area that a shape must have to be included in the results. Shapes smaller than this will be excluded. - /// - Returns: A vector of `IntShape`, representing the geometric result of the applied overlay rule. + /// - Returns: A vector of `IntShape`, representing the geometric result of the applied overlay rule. /// # Shape Representation - /// The output is a `IntShapes`, where: - /// - The outer `Vec` represents a set of shapes. - /// - Each shape `Vec` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. + /// The output is a `IntShapes`, where: + /// - The outer `Vec>` represents a set of shapes. + /// - Each shape `Vec>` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. /// - Each path `Vec` is a sequence of points, forming a closed path. /// /// Note: Outer boundary paths have a **main_direction** order, and holes have an opposite to **main_direction** order. - pub fn extract_shapes_custom(&self, string_rule: StringRule, options: IntOverlayOptions) -> IntShapes { + pub fn extract_shapes_custom( + &self, + string_rule: StringRule, + options: IntOverlayOptions, + ) -> IntShapes { let clockwise = options.output_direction == ContourDirection::Clockwise; let mut fills = self.filter(string_rule); let mut shapes = Vec::new(); @@ -85,7 +89,7 @@ impl StringGraph<'_> { } #[inline] - fn get_paths(&self, start_index: usize, clockwise: bool, fills: &mut [u8]) -> IntPath { + fn get_paths(&self, start_index: usize, clockwise: bool, fills: &mut [u8]) -> IntPath { let start_link = unsafe { // SAFETY: start_index originates from iterating the fills array, which mirrors links. self.links.get_unchecked(start_index) @@ -95,7 +99,7 @@ impl StringGraph<'_> { let mut node_id = start_link.b.id; let last_node_id = start_link.a.id; - let mut path = IntPath::new(); + let mut path = IntPath::::new(); path.push(start_link.a.point); fills[start_index] = start_link.visit_fill(fills[start_index], start_link.a.id, clockwise); diff --git a/iOverlay/src/string/filter.rs b/iOverlay/src/string/filter.rs index c02815a..3262276 100644 --- a/iOverlay/src/string/filter.rs +++ b/iOverlay/src/string/filter.rs @@ -4,7 +4,7 @@ use crate::string::graph::StringGraph; use crate::string::rule::StringRule; use alloc::vec::Vec; -impl OverlayLink { +impl OverlayLink { #[inline] pub(super) fn visit_fill(&self, fill: u8, node_id: usize, clockwise: bool) -> u8 { let is_a = self.a.id == node_id; @@ -42,7 +42,7 @@ impl OverlayLink { } } -impl StringGraph<'_> { +impl StringGraph<'_, i32> { #[inline(always)] pub(super) fn filter(&self, ext_rule: StringRule) -> Vec { match ext_rule { diff --git a/iOverlay/src/string/graph.rs b/iOverlay/src/string/graph.rs index 901c239..d8340cf 100644 --- a/iOverlay/src/string/graph.rs +++ b/iOverlay/src/string/graph.rs @@ -1,10 +1,11 @@ use crate::build::builder::GraphNode; use crate::core::link::OverlayLink; use alloc::vec::Vec; +use i_float::int::number::int::IntNumber; -pub struct StringGraph<'a> { +pub struct StringGraph<'a, I: IntNumber> { pub(crate) nodes: &'a [Vec], - pub(crate) links: &'a mut [OverlayLink], + pub(crate) links: &'a mut [OverlayLink], } impl GraphNode for Vec { diff --git a/iOverlay/src/string/overlay.rs b/iOverlay/src/string/overlay.rs index c5b2a95..847d337 100644 --- a/iOverlay/src/string/overlay.rs +++ b/iOverlay/src/string/overlay.rs @@ -21,9 +21,9 @@ use i_shape::int::shape::{IntContour, IntShape}; pub struct StringOverlay { pub options: IntOverlayOptions, - pub(super) segments: Vec>, + pub(super) segments: Vec>, pub(crate) split_solver: SplitSolver, - pub(crate) graph_builder: GraphBuilder>, + pub(crate) graph_builder: GraphBuilder, i32>, } impl StringOverlay { @@ -36,7 +36,7 @@ impl StringOverlay { options: Default::default(), segments: Vec::with_capacity(capacity), split_solver: SplitSolver::new(), - graph_builder: GraphBuilder::>::new(), + graph_builder: GraphBuilder::, i32>::new(), } } @@ -49,7 +49,7 @@ impl StringOverlay { options, segments: Vec::with_capacity(capacity), split_solver: SplitSolver::new(), - graph_builder: GraphBuilder::>::new(), + graph_builder: GraphBuilder::, i32>::new(), } } @@ -63,27 +63,27 @@ impl StringOverlay { } /// Creates a new `StringOverlay` instance and initializes it with multiple shape contours. - /// - `contours`: An array of `IntContour` instances to be added to the overlay. + /// - `contours`: An array of `IntContour` instances to be added to the overlay. #[inline] - pub fn with_shape_contours(contours: &[IntContour]) -> Self { + pub fn with_shape_contours(contours: &[IntContour]) -> Self { let mut overlay = Self::new(contours.points_count()); overlay.add_shape_contours(contours); overlay } /// Creates a new `StringOverlay` instance and initializes it with s shape. - /// - `shape`: An `IntShape` instances to be added to the overlay. + /// - `shape`: An `IntShape` instances to be added to the overlay. #[inline] - pub fn with_shape(shape: &[IntContour]) -> Self { + pub fn with_shape(shape: &[IntContour]) -> Self { let mut overlay = Self::new(shape.points_count()); overlay.add_shape_contours(shape); overlay } /// Creates a new `StringOverlay` instance and initializes it with subject and clip shapes. - /// - `shapes`: An array of `IntShape` instances to be added to the overlay. + /// - `shapes`: An array of `IntShape` instances to be added to the overlay. #[inline] - pub fn with_shapes(shapes: &[IntShape]) -> Self { + pub fn with_shapes(shapes: &[IntShape]) -> Self { let mut overlay = Self::new(shapes.points_count()); overlay.add_shapes(shapes); overlay @@ -106,17 +106,17 @@ impl StringOverlay { } /// Adds multiple paths to the overlay as shape paths. - /// - `contours`: An array of `IntContour` instances to be added to the overlay. - pub fn add_shape_contours(&mut self, contours: &[IntContour]) { + /// - `contours`: An array of `IntContour` instances to be added to the overlay. + pub fn add_shape_contours(&mut self, contours: &[IntContour]) { for contour in contours.iter() { self.add_shape_contour(contour); } } /// Adds a list of shape to the overlay. - /// - `shapes`: An array of `IntShape` instances to be added to the overlay. + /// - `shapes`: An array of `IntShape` instances to be added to the overlay. #[inline] - pub fn add_shapes(&mut self, shapes: &[IntShape]) { + pub fn add_shapes(&mut self, shapes: &[IntShape]) { for shape in shapes { self.add_shape_contours(shape); } @@ -199,7 +199,7 @@ impl StringOverlay { /// Adds a string line paths to the overlay. /// - `paths`: A collection of paths, each representing a string line. #[inline] - pub fn add_string_paths(&mut self, paths: &[IntPath]) { + pub fn add_string_paths(&mut self, paths: &[IntPath]) { for path in paths { self.add_string_path(path); } @@ -208,7 +208,7 @@ impl StringOverlay { /// Adds a string line contours to the overlay. /// - `contours`: A collection of contours, each representing a string line closed path. #[inline] - pub fn add_string_contours(&mut self, contours: &[IntContour]) { + pub fn add_string_contours(&mut self, contours: &[IntContour]) { for contour in contours { self.add_string_contour(contour); } @@ -218,9 +218,9 @@ impl StringOverlay { /// - `fill_rule`: Specifies the rule determining the filled areas, influencing the inclusion of line segments. /// - `clip_rule`: The rule for clipping, determining how the boundary and inversion settings affect the result. /// # Returns - /// A vector of `IntPath` instances representing the clipped sections of the input lines. + /// A vector of `IntPath` instances representing the clipped sections of the input lines. #[inline] - pub fn clip_string_lines(self, fill_rule: FillRule, clip_rule: ClipRule) -> Vec { + pub fn clip_string_lines(self, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { self.clip_string_lines_with_solver(fill_rule, clip_rule, Default::default()) } @@ -230,14 +230,14 @@ impl StringOverlay { /// - `solver`: A solver type to be used for advanced control over the graph building process. /// /// # Returns - /// A vector of `IntPath` instances representing the clipped sections of the input lines. + /// A vector of `IntPath` instances representing the clipped sections of the input lines. #[inline] pub fn clip_string_lines_with_solver( mut self, fill_rule: FillRule, clip_rule: ClipRule, solver: Solver, - ) -> Vec { + ) -> Vec> { self.split_solver.split_segments(&mut self.segments, &solver); if self.segments.is_empty() { return Vec::new(); @@ -251,7 +251,7 @@ impl StringOverlay { /// This graph is used for string operations, enabling analysis and manipulation of geometric data. /// - `fill_rule`: The rule that defines how to build shapes (e.g., non-zero, even-odd). #[inline] - pub fn build_graph_view(&mut self, fill_rule: FillRule) -> Option> { + pub fn build_graph_view(&mut self, fill_rule: FillRule) -> Option> { self.build_graph_view_with_solver(fill_rule, Default::default()) } @@ -264,7 +264,7 @@ impl StringOverlay { &mut self, fill_rule: FillRule, solver: Solver, - ) -> Option> { + ) -> Option> { self.split_solver.split_segments(&mut self.segments, &solver); if self.segments.is_empty() { return None; diff --git a/iOverlay/src/string/slice.rs b/iOverlay/src/string/slice.rs index f3e3206..7ac25ca 100644 --- a/iOverlay/src/string/slice.rs +++ b/iOverlay/src/string/slice.rs @@ -7,15 +7,15 @@ use i_shape::int::path::IntPath; use i_shape::int::shape::{IntShape, IntShapes}; pub trait IntSlice { - fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes; - fn slice_by_lines(&self, lines: &[IntLine], fill_rule: FillRule) -> IntShapes; - fn slice_by_path(&self, path: &IntPath, fill_rule: FillRule) -> IntShapes; - fn slice_by_paths(&self, paths: &[IntPath], fill_rule: FillRule) -> IntShapes; + fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes; + fn slice_by_lines(&self, lines: &[IntLine], fill_rule: FillRule) -> IntShapes; + fn slice_by_path(&self, path: &IntPath, fill_rule: FillRule) -> IntShapes; + fn slice_by_paths(&self, paths: &[IntPath], fill_rule: FillRule) -> IntShapes; } -impl IntSlice for IntShapes { +impl IntSlice for IntShapes { #[inline] - fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes { + fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shapes(self); overlay.add_string_line(line); overlay @@ -25,7 +25,7 @@ impl IntSlice for IntShapes { } #[inline] - fn slice_by_lines(&self, lines: &[IntLine], fill_rule: FillRule) -> IntShapes { + fn slice_by_lines(&self, lines: &[IntLine], fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shapes(self); overlay.add_string_lines(lines); overlay @@ -35,7 +35,7 @@ impl IntSlice for IntShapes { } #[inline] - fn slice_by_path(&self, path: &IntPath, fill_rule: FillRule) -> IntShapes { + fn slice_by_path(&self, path: &IntPath, fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shapes(self); overlay.add_string_path(path); overlay @@ -45,7 +45,7 @@ impl IntSlice for IntShapes { } #[inline] - fn slice_by_paths(&self, paths: &[IntPath], fill_rule: FillRule) -> IntShapes { + fn slice_by_paths(&self, paths: &[IntPath], fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shapes(self); overlay.add_string_paths(paths); overlay @@ -55,9 +55,9 @@ impl IntSlice for IntShapes { } } -impl IntSlice for IntShape { +impl IntSlice for IntShape { #[inline] - fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes { + fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shape(self); overlay.add_string_line(line); overlay @@ -67,7 +67,7 @@ impl IntSlice for IntShape { } #[inline] - fn slice_by_lines(&self, lines: &[IntLine], fill_rule: FillRule) -> IntShapes { + fn slice_by_lines(&self, lines: &[IntLine], fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shape(self); overlay.add_string_lines(lines); overlay @@ -77,7 +77,7 @@ impl IntSlice for IntShape { } #[inline] - fn slice_by_path(&self, path: &IntPath, fill_rule: FillRule) -> IntShapes { + fn slice_by_path(&self, path: &IntPath, fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shape(self); overlay.add_string_path(path); overlay @@ -87,7 +87,7 @@ impl IntSlice for IntShape { } #[inline] - fn slice_by_paths(&self, paths: &[IntPath], fill_rule: FillRule) -> IntShapes { + fn slice_by_paths(&self, paths: &[IntPath], fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shape(self); overlay.add_string_paths(paths); overlay @@ -99,7 +99,7 @@ impl IntSlice for IntShape { impl IntSlice for [IntPoint] { #[inline] - fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes { + fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shape_contour(self); overlay.add_string_line(line); overlay @@ -109,7 +109,7 @@ impl IntSlice for [IntPoint] { } #[inline] - fn slice_by_lines(&self, lines: &[IntLine], fill_rule: FillRule) -> IntShapes { + fn slice_by_lines(&self, lines: &[IntLine], fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shape_contour(self); overlay.add_string_lines(lines); overlay @@ -119,7 +119,7 @@ impl IntSlice for [IntPoint] { } #[inline] - fn slice_by_path(&self, path: &IntPath, fill_rule: FillRule) -> IntShapes { + fn slice_by_path(&self, path: &IntPath, fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shape_contour(self); overlay.add_string_path(path); overlay @@ -129,7 +129,7 @@ impl IntSlice for [IntPoint] { } #[inline] - fn slice_by_paths(&self, paths: &[IntPath], fill_rule: FillRule) -> IntShapes { + fn slice_by_paths(&self, paths: &[IntPath], fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shape_contour(self); overlay.add_string_paths(paths); overlay diff --git a/iOverlay/src/string/split.rs b/iOverlay/src/string/split.rs index 9e63705..4c38caa 100644 --- a/iOverlay/src/string/split.rs +++ b/iOverlay/src/string/split.rs @@ -8,18 +8,18 @@ pub(super) trait Split { fn split_loops( self, min_area: u64, - contour_buffer: &mut IntContour, + contour_buffer: &mut IntContour, bin_store: &mut BinStore, ) -> Vec where Self: Sized; } -impl Split for IntContour { +impl Split for IntContour { fn split_loops( self, min_area: u64, - contour_buffer: &mut IntContour, + contour_buffer: &mut IntContour, bin_store: &mut BinStore, ) -> Vec { if self.is_empty() { @@ -30,7 +30,7 @@ impl Split for IntContour { bin_store.init(&self); - let mut result: Vec = Vec::with_capacity(16); + let mut result: Vec> = Vec::with_capacity(16); for point in self { let next_pos = contour_buffer.len() + 1; @@ -88,7 +88,7 @@ impl BinStore { } } - fn init(&mut self, contour: &IntContour) { + fn init(&mut self, contour: &IntContour) { let log = contour.len().ilog2().saturating_sub(4).clamp(1, 30); let bins_count = (1 << log) as usize; @@ -162,7 +162,7 @@ trait ValidateArea { fn validate_area(&self, min_area: u64) -> bool; } -impl ValidateArea for IntContour { +impl ValidateArea for IntContour { #[inline] fn validate_area(&self, min_area: u64) -> bool { if min_area == 0 { @@ -181,17 +181,17 @@ mod tests { #[test] fn test_empty_path() { - let path: IntPath = vec![]; - let mut contour: IntContour = Vec::new(); + let path: IntPath = vec![]; + let mut contour: IntContour = Vec::new(); let mut bin_store = BinStore::new(); let result = path.split_loops(0, &mut contour, &mut bin_store); - assert_eq!(result, vec![] as Vec); + assert_eq!(result, vec![] as Vec>); } #[test] fn test_single_point() { let path = vec![IntPoint::new(0, 0)]; - let mut contour: IntContour = Vec::new(); + let mut contour: IntContour = Vec::new(); let mut bin_store = BinStore::new(); let result = path.split_loops(0, &mut contour, &mut bin_store); assert!(result.is_empty()); @@ -200,7 +200,7 @@ mod tests { #[test] fn test_two_points() { let path = vec![IntPoint::new(0, 0), IntPoint::new(1, 1)]; - let mut contour: IntContour = Vec::new(); + let mut contour: IntContour = Vec::new(); let mut bin_store = BinStore::new(); let result = path.split_loops(0, &mut contour, &mut bin_store); assert!(result.is_empty()); @@ -215,7 +215,7 @@ mod tests { IntPoint::new(1, 0), ]; - let mut contour: IntContour = Vec::new(); + let mut contour: IntContour = Vec::new(); let mut bin_store = BinStore::new(); let result = path.clone().split_loops(0, &mut contour, &mut bin_store); assert_eq!(result, vec![path]); @@ -234,7 +234,7 @@ mod tests { IntPoint::new(1, -1), ]; - let mut contour: IntContour = Vec::new(); + let mut contour: IntContour = Vec::new(); let mut bin_store = BinStore::new(); let result = path.split_loops(0, &mut contour, &mut bin_store); assert_eq!(result.len(), 2); @@ -272,7 +272,7 @@ mod tests { IntPoint::new(1, -1), ]; - let mut contour: IntContour = Vec::new(); + let mut contour: IntContour = Vec::new(); let mut bin_store = BinStore::new(); let result = path.split_loops(0, &mut contour, &mut bin_store); assert_eq!(result.len(), 2); @@ -309,7 +309,7 @@ mod tests { IntPoint::new(0, 0), ]; - let mut contour: IntContour = Vec::new(); + let mut contour: IntContour = Vec::new(); let mut bin_store = BinStore::new(); let result = path.split_loops(0, &mut contour, &mut bin_store); assert_eq!(result.len(), 2); @@ -344,7 +344,7 @@ mod tests { IntPoint::new(0, 0), // same point, forms a loop ]; - let mut contour: IntContour = Vec::new(); + let mut contour: IntContour = Vec::new(); let mut bin_store = BinStore::new(); let result = path.split_loops(0, &mut contour, &mut bin_store); assert_eq!(result.len(), 1); @@ -371,7 +371,7 @@ mod tests { IntPoint::new(0, 0), // same point, forms a loop ]; - let mut contour: IntContour = Vec::new(); + let mut contour: IntContour = Vec::new(); let mut bin_store = BinStore::new(); let result = path.split_loops(0, &mut contour, &mut bin_store); assert_eq!(result.len(), 4); diff --git a/iOverlay/src/vector/edge.rs b/iOverlay/src/vector/edge.rs index 4e547a1..4f5b817 100644 --- a/iOverlay/src/vector/edge.rs +++ b/iOverlay/src/vector/edge.rs @@ -5,10 +5,10 @@ use i_float::int::point::IntPoint; use i_shape::int::path::IntPath; pub type SideFill = u8; -pub type DataVectorPath = Vec>; -pub type DataVectorShape = Vec>; -pub type VectorPath = DataVectorPath<(), I>; -pub type VectorShape = DataVectorShape<(), I>; +pub type DataVectorPath = Vec>; +pub type DataVectorShape = Vec>; +pub type VectorPath = DataVectorPath; +pub type VectorShape = DataVectorShape; pub const SUBJ_LEFT: u8 = 0b0001; pub const SUBJ_RIGHT: u8 = 0b0010; @@ -31,14 +31,14 @@ impl Reverse for SideFill { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct DataVectorEdge { +pub struct DataVectorEdge { pub a: IntPoint, pub b: IntPoint, pub fill: SideFill, pub data: D, } -impl DataVectorEdge { +impl DataVectorEdge { pub(crate) fn new(fill: SideFill, a: IntPoint, b: IntPoint, data: D) -> Self { let (fill, data) = if a < b { (fill, data) diff --git a/iOverlay/src/vector/extract.rs b/iOverlay/src/vector/extract.rs index 378bfec..634c7ab 100644 --- a/iOverlay/src/vector/extract.rs +++ b/iOverlay/src/vector/extract.rs @@ -12,10 +12,13 @@ use crate::vector::edge::{DataVectorEdge, DataVectorPath, DataVectorShape}; use crate::vector::simplify::VectorSimplify; 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_tree::Expiration; -impl OverlayGraph<'_, D> { - pub fn extract_separate_vectors(&self) -> Vec { +impl OverlayGraph<'_, i32, D> { + pub fn extract_separate_vectors(&self) -> Vec> { self.links .iter() .map(|link| DataVectorEdge::new(link.fill, link.a.point, link.b.point, ())) @@ -26,7 +29,7 @@ impl OverlayGraph<'_, D> { &self, overlay_rule: OverlayRule, buffer: &mut BooleanExtractionBuffer, - ) -> Vec> { + ) -> Vec> { let clockwise = self.options.output_direction == ContourDirection::Clockwise; self.links .filter_by_overlay_into(overlay_rule, &mut buffer.visited); @@ -116,7 +119,7 @@ impl OverlayGraph<'_, D> { clockwise: bool, visited_state: VisitState, visited: &mut [VisitState], - ) -> DataVectorPath { + ) -> DataVectorPath { let mut link_id = start_data.link_id; let mut node_id = start_data.node_id; let last_node_id = start_data.last_node_id; @@ -149,8 +152,8 @@ impl OverlayGraph<'_, D> { } } -impl OverlayGraph<'_, D> { - pub fn extract_vectors(&self) -> Vec> { +impl OverlayGraph<'_, i32, D> { + pub fn extract_vectors(&self) -> Vec> { self.links .iter() .map(|link| DataVectorEdge::new(link.fill, link.a.point, link.b.point, link.data)) @@ -170,7 +173,7 @@ struct StartVectorPathData { impl StartVectorPathData { #[inline(always)] - fn new(direction: bool, link: &OverlayLink, link_id: usize) -> Self { + fn new(direction: bool, link: &OverlayLink, link_id: usize) -> Self { if direction { Self { a: link.b.point, @@ -195,26 +198,34 @@ impl StartVectorPathData { } } -trait JoinHoles { +trait JoinHoles +where + I: IntNumber + Expiration + SortKey + Send + Sync, + D: OverlayEdgeData, +{ fn join_sorted_holes( &mut self, - holes: Vec>, - anchors: Vec, + holes: Vec>, + anchors: Vec>, clockwise: bool, ); fn scan_join( &mut self, - holes: Vec>, - hole_segments: Vec, + holes: Vec>, + hole_segments: Vec>, clockwise: bool, ); } -impl JoinHoles for Vec> { +impl JoinHoles for Vec> +where + I: IntNumber + Expiration + SortKey + Send + Sync, + D: OverlayEdgeData, +{ fn join_sorted_holes( &mut self, - holes: Vec>, - anchors: Vec, + holes: Vec>, + anchors: Vec>, clockwise: bool, ) { if self.is_empty() || holes.is_empty() { @@ -235,8 +246,8 @@ impl JoinHoles for Vec> { fn scan_join( &mut self, - holes: Vec>, - hole_segments: Vec, + holes: Vec>, + hole_segments: Vec>, clockwise: bool, ) { let x_min = hole_segments[0].v_segment.a.x; @@ -268,7 +279,7 @@ impl JoinHoles for Vec> { } #[inline] -fn most_left_bottom(path: &DataVectorPath) -> VSegment { +fn most_left_bottom(path: &DataVectorPath) -> VSegment { let mut index = 0; let mut a = path[0].a; for (i, e) in path.iter().enumerate().skip(1) { @@ -288,7 +299,7 @@ fn most_left_bottom(path: &DataVectorPath) -> VSegment { } #[inline] -fn is_sorted(segments: &[IdSegment]) -> bool { +fn is_sorted(segments: &[IdSegment]) -> bool { segments .windows(2) .all(|slice| slice[0].v_segment.a <= slice[1].v_segment.a) @@ -296,10 +307,10 @@ fn is_sorted(segments: &[IdSegment]) -> bool { trait DataGraphContour { fn validate(&mut self, min_output_area: u64, preserve_output_collinear: bool) -> (bool, bool); - fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize; + fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize; } -impl DataGraphContour for DataVectorPath { +impl DataGraphContour for DataVectorPath { #[inline] fn validate(&mut self, min_output_area: u64, preserve_output_collinear: bool) -> (bool, bool) { let is_modified = if !preserve_output_collinear { @@ -324,7 +335,7 @@ impl DataGraphContour for DataVectorPath { } #[inline] - fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize { + fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize { if link.a.id == node_id { self.push(DataVectorEdge::new( link.fill, diff --git a/iOverlay/src/vector/simplify.rs b/iOverlay/src/vector/simplify.rs index 075c9c8..e1e6d09 100644 --- a/iOverlay/src/vector/simplify.rs +++ b/iOverlay/src/vector/simplify.rs @@ -2,6 +2,8 @@ use crate::core::edge_data::OverlayEdgeData; use crate::vector::edge::{DataVectorEdge, DataVectorPath, DataVectorShape}; use alloc::vec; use alloc::vec::Vec; +use i_float::int::number::int::IntNumber; +use i_float::int::number::wide_int::WideIntNumber; use i_float::int::point::IntPoint; use i_float::int::vector::IntVector; @@ -14,18 +16,20 @@ pub(super) trait VectorSimplify { } pub(super) trait VectorSimpleContour { + type Int: IntNumber; type Data: OverlayEdgeData; fn is_simple(&self) -> bool; - fn simplified(&self) -> Option>; + fn simplified(&self) -> Option>; } pub(super) trait VectorSimpleShape { + type Int: IntNumber; type Data: OverlayEdgeData; fn is_simple(&self) -> bool; - fn simplified(&self) -> Option>; + fn simplified(&self) -> Option>; } -impl VectorSimplify for DataVectorPath { +impl VectorSimplify for DataVectorPath { #[inline] fn simplify_contour(&mut self) -> bool { if self.is_simple() { @@ -40,7 +44,7 @@ impl VectorSimplify for DataVectorPath { } } -impl VectorSimplify for DataVectorShape { +impl VectorSimplify for DataVectorShape { #[inline] fn simplify_contour(&mut self) -> bool { let mut any_simplified = false; @@ -71,7 +75,7 @@ impl VectorSimplify for DataVectorShape { } } -impl VectorSimplify for Vec> { +impl VectorSimplify for Vec> { #[inline] fn simplify_contour(&mut self) -> bool { let mut any_simplified = false; @@ -98,7 +102,8 @@ impl VectorSimplify for Vec> { } } -impl VectorSimpleContour for [DataVectorEdge] { +impl VectorSimpleContour for [DataVectorEdge] { + type Int = I; type Data = D; #[inline] @@ -111,7 +116,7 @@ impl VectorSimpleContour for [DataVectorEdge] { let mut prev = &self[count - 1]; for edge in self.iter() { let curr = direction(edge); - if curr.cross_product(direction(prev)) == 0 && edge.data == prev.data { + if curr.cross_product(direction(prev)) == I::Wide::ZERO && edge.data == prev.data { return false; } prev = edge; @@ -120,7 +125,7 @@ impl VectorSimpleContour for [DataVectorEdge] { true } - fn simplified(&self) -> Option> { + fn simplified(&self) -> Option> { if self.len() < 3 { return None; } @@ -161,7 +166,9 @@ impl VectorSimpleContour for [DataVectorEdge] { let p1 = self[node.index].b; let p2 = self[node.next].b; - if (p1 - p0).cross_product(p2 - p1) == 0 && self[node.index].data == self[node.next].data { + if (p1 - p0).cross_product(p2 - p1) == I::Wide::ZERO + && self[node.index].data == self[node.next].data + { n -= 1; if n < 3 { return None; @@ -224,7 +231,8 @@ struct Node { prev: usize, } -impl VectorSimpleShape for [DataVectorPath] { +impl VectorSimpleShape for [DataVectorPath] { + type Int = I; type Data = D; #[inline] @@ -232,7 +240,7 @@ impl VectorSimpleShape for [DataVectorPath] { self.iter().all(|contour| contour.is_simple()) } - fn simplified(&self) -> Option> { + fn simplified(&self) -> Option> { let mut contours = Vec::with_capacity(self.len()); for (i, contour) in self.iter().enumerate() { if contour.is_simple() { @@ -249,7 +257,7 @@ impl VectorSimpleShape for [DataVectorPath] { } #[inline] -fn direction(edge: &DataVectorEdge) -> IntVector { +fn direction(edge: &DataVectorEdge) -> IntVector { edge.b - edge.a } diff --git a/iOverlay/tests/board_tests.rs b/iOverlay/tests/board_tests.rs index b54be60..7dc851a 100644 --- a/iOverlay/tests/board_tests.rs +++ b/iOverlay/tests/board_tests.rs @@ -49,13 +49,13 @@ mod tests { result.len() } - fn many_squares(start: IntPoint, size: i32, offset: i32, n: usize) -> Vec { + fn many_squares(start: IntPoint, size: i32, offset: i32, n: usize) -> Vec> { let mut result = Vec::with_capacity(n * n); let mut y = start.y; for _ in 0..n { let mut x = start.x; for _ in 0..n { - let path: IntPath = vec![ + let path: IntPath = vec![ IntPoint::new(x, y), IntPoint::new(x, y + size), IntPoint::new(x + size, y + size), diff --git a/iOverlay/tests/data.rs b/iOverlay/tests/data.rs index 9c0e760..f1c4681 100644 --- a/iOverlay/tests/data.rs +++ b/iOverlay/tests/data.rs @@ -28,17 +28,17 @@ pub mod overlay { #[serde(default, deserialize_with = "deserialize_fill_rule")] pub fill_rule: Option, #[serde(rename = "subjPaths")] - pub subj_paths: Vec, + pub subj_paths: Vec>, #[serde(rename = "clipPaths")] - pub clip_paths: Vec, - pub clip: Vec, - pub subject: Vec, - pub difference: Vec, + pub clip_paths: Vec>, + pub clip: Vec>, + pub subject: Vec>, + pub difference: Vec>, #[serde(rename = "inverseDifference")] - pub inverse_difference: Vec, - pub intersect: Vec, - pub union: Vec, - pub xor: Vec, + pub inverse_difference: Vec>, + pub intersect: Vec>, + pub union: Vec>, + pub xor: Vec>, } impl BooleanTest { @@ -72,11 +72,11 @@ pub mod overlay { #[serde(rename = "fillRule")] #[serde(default, deserialize_with = "deserialize_fill_rule")] pub fill_rule: Option, - pub body: Vec, - pub string: IntPaths, - pub slice: Vec, - pub clip_direct: Vec, - pub clip_invert: Vec, + pub body: Vec>, + pub string: IntPaths, + pub slice: Vec>, + pub clip_direct: Vec>, + pub clip_invert: Vec>, } impl StringTest { diff --git a/iOverlay/tests/dynamic_tests.rs b/iOverlay/tests/dynamic_tests.rs index b4402fc..e537648 100644 --- a/iOverlay/tests/dynamic_tests.rs +++ b/iOverlay/tests/dynamic_tests.rs @@ -324,7 +324,7 @@ mod tests { } } - fn create_star(r0: f64, r1: f64, count: usize, angle: f64) -> IntShape { + fn create_star(r0: f64, r1: f64, count: usize, angle: f64) -> IntShape { let da = PI / count as f64; let mut a = angle; @@ -351,7 +351,7 @@ mod tests { [points].to_vec() } - fn random_polygon(radius: f64, angle: f64, n: usize) -> IntPath { + fn random_polygon(radius: f64, angle: f64, n: usize) -> IntPath { let mut result = Vec::with_capacity(n); let da: f64 = PI * 0.7; let mut a: f64 = angle; @@ -369,7 +369,7 @@ mod tests { result } - fn random(radius: i32, n: usize) -> IntPath { + fn random(radius: i32, n: usize) -> IntPath { let a = radius / 2; let range = -a..=a; let mut points = Vec::with_capacity(n); diff --git a/iOverlay/tests/fill_rule_tests.rs b/iOverlay/tests/fill_rule_tests.rs index 605a40a..0350f24 100644 --- a/iOverlay/tests/fill_rule_tests.rs +++ b/iOverlay/tests/fill_rule_tests.rs @@ -174,7 +174,7 @@ mod tests { assert_eq!(positive[0].len(), 2); } - fn square(radius: i32, is_clockwise: bool) -> IntPath { + fn square(radius: i32, is_clockwise: bool) -> IntPath { let mut square = [ IntPoint::new(-radius, -radius), IntPoint::new(-radius, radius), diff --git a/iOverlay/tests/fragment_tests.rs b/iOverlay/tests/fragment_tests.rs index a9c4ffc..e3bed8f 100644 --- a/iOverlay/tests/fragment_tests.rs +++ b/iOverlay/tests/fragment_tests.rs @@ -204,13 +204,13 @@ mod tests { println!("clip: {}", clip.json_print()); } - fn many_squares(start: IntPoint, size: i32, offset: i32, n: usize) -> Vec { + fn many_squares(start: IntPoint, size: i32, offset: i32, n: usize) -> Vec> { let mut result = Vec::with_capacity(n * n); let mut y = start.y; for _ in 0..n { let mut x = start.x; for _ in 0..n { - let path: IntPath = vec![ + let path: IntPath = vec![ IntPoint::new(x, y), IntPoint::new(x, y + size), IntPoint::new(x + size, y + size), @@ -225,13 +225,13 @@ mod tests { result } - fn many_lines_x(a: i32, n: usize) -> Vec { + fn many_lines_x(a: i32, n: usize) -> Vec> { let w = a / 2; let s = a * (n as i32) / 2; let mut x = -s + w / 2; let mut result = Vec::with_capacity(n); for _ in 0..n { - let path: IntPath = vec![ + let path: IntPath = vec![ IntPoint::new(x, -s), IntPoint::new(x, s), IntPoint::new(x + w, s), @@ -244,13 +244,13 @@ mod tests { result } - fn many_lines_y(a: i32, n: usize) -> Vec { + fn many_lines_y(a: i32, n: usize) -> Vec> { let h = a / 2; let s = a * (n as i32) / 2; let mut y = -s + h / 2; let mut result = Vec::with_capacity(n); for _ in 0..n { - let path: IntPath = vec![ + let path: IntPath = vec![ IntPoint::new(-s, y), IntPoint::new(s, y), IntPoint::new(s, y - h), @@ -263,7 +263,7 @@ mod tests { result } - fn discrete_spiral(count: usize, a: i32) -> Vec { + fn discrete_spiral(count: usize, a: i32) -> Vec> { let mut rects = Vec::with_capacity(8 * count); let a2 = 2 * a; @@ -353,7 +353,7 @@ mod tests { rects } - fn romb(x: i32, y: i32, a: i32) -> IntContour { + fn romb(x: i32, y: i32, a: i32) -> IntContour { vec![ IntPoint::new(x - a, y), IntPoint::new(x, y - a), @@ -362,7 +362,7 @@ mod tests { ] } - fn square(x: i32, y: i32, a: i32) -> IntContour { + fn square(x: i32, y: i32, a: i32) -> IntContour { vec![ IntPoint::new(x - a, y + a), IntPoint::new(x - a, y - a), @@ -371,7 +371,14 @@ mod tests { ] } - fn repeat_xy(origin: IntContour, x0: i32, y0: i32, dx: i32, dy: i32, count: usize) -> Vec { + fn repeat_xy( + origin: IntContour, + x0: i32, + y0: i32, + dx: i32, + dy: i32, + count: usize, + ) -> Vec> { let mut contours = Vec::with_capacity(8 * count); let mut x = x0; for _ in 0..count { @@ -391,7 +398,7 @@ mod tests { contours } - fn repeat_x(origin: IntContour, x0: i32, y0: i32, dx: i32, count: usize) -> Vec { + fn repeat_x(origin: IntContour, x0: i32, y0: i32, dx: i32, count: usize) -> Vec> { let mut contours = Vec::with_capacity(8 * count); let mut x = x0; for _ in 0..count { @@ -407,7 +414,7 @@ mod tests { contours } - fn repeat_y(origin: IntContour, x0: i32, y0: i32, dy: i32, count: usize) -> Vec { + fn repeat_y(origin: IntContour, x0: i32, y0: i32, dy: i32, count: usize) -> Vec> { let mut contours = Vec::with_capacity(8 * count); let mut y = y0; for _ in 0..count { diff --git a/iOverlay/tests/simplify_tests.rs b/iOverlay/tests/simplify_tests.rs index 5829efd..2210022 100644 --- a/iOverlay/tests/simplify_tests.rs +++ b/iOverlay/tests/simplify_tests.rs @@ -166,7 +166,7 @@ mod tests { .to_vec() } - fn square_shape(pos: IntPoint) -> IntShape { + fn square_shape(pos: IntPoint) -> IntShape { [square(pos)].to_vec() } } diff --git a/iOverlay/tests/slice_tests.rs b/iOverlay/tests/slice_tests.rs index 06e251a..ec67aee 100644 --- a/iOverlay/tests/slice_tests.rs +++ b/iOverlay/tests/slice_tests.rs @@ -489,7 +489,7 @@ mod tests { } } - fn random_polygon(radius: i32, n: usize) -> IntPath { + fn random_polygon(radius: i32, n: usize) -> IntPath { let a = radius / 2; let range = -a..=a; let mut points = Vec::with_capacity(n); diff --git a/iOverlay/tests/util.rs b/iOverlay/tests/util.rs index 9ce4a89..59907f4 100644 --- a/iOverlay/tests/util.rs +++ b/iOverlay/tests/util.rs @@ -34,7 +34,7 @@ pub mod overlay { } } - impl CircleCompare for Vec { + impl CircleCompare for Vec> { fn are_equal(&self, other: &Self) -> bool { if self.len() != other.len() { return false; @@ -61,7 +61,7 @@ pub mod overlay { } #[allow(dead_code)] - pub fn is_group_of_shapes_one_of(group: &Vec, groups: &[Vec]) -> bool { + pub fn is_group_of_shapes_one_of(group: &Vec>, groups: &[Vec>]) -> bool { for item in groups.iter() { if item.are_equal(group) { return true; @@ -72,7 +72,7 @@ pub mod overlay { } #[allow(dead_code)] - pub fn is_paths_one_of(paths: &IntPaths, groups: &[IntPaths]) -> bool { + pub fn is_paths_one_of(paths: &IntPaths, groups: &[IntPaths]) -> bool { for item in groups.iter() { if item.eq(paths) { return true; @@ -92,7 +92,7 @@ pub mod overlay { } } - impl JsonPrint for IntPath { + impl JsonPrint for IntPath { fn json_print(&self) -> String { let mut s = String::with_capacity(16 * self.len()); s.push('['); @@ -107,7 +107,7 @@ pub mod overlay { } } - impl JsonPrint for IntPaths { + impl JsonPrint for IntPaths { fn json_print(&self) -> String { let mut s = String::with_capacity(100 * self.len()); s.push('['); @@ -122,7 +122,7 @@ pub mod overlay { } } - impl JsonPrint for IntShapes { + impl JsonPrint for IntShapes { fn json_print(&self) -> String { let mut s = String::with_capacity(200 * self.len()); s.push('['); From 2c068f1b5bf4fe98e9474e9d04991151bea181ba Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Tue, 19 May 2026 16:14:06 +0300 Subject: [PATCH 06/36] migrate to int api --- iOverlay/Cargo.toml | 2 +- iOverlay/src/build/util.rs | 13 +++- iOverlay/src/core/edge_data.rs | 11 ++-- iOverlay/src/core/edge_overlay.rs | 4 +- iOverlay/src/core/extract.rs | 89 +++++++++++++++---------- iOverlay/src/core/extract_ogc.rs | 22 ++++--- iOverlay/src/core/overlay.rs | 94 ++++++++++++++------------- iOverlay/src/core/relate.rs | 2 +- iOverlay/src/core/simplify.rs | 49 ++++++++------ iOverlay/src/float/graph.rs | 2 +- iOverlay/src/float/overlay.rs | 2 +- iOverlay/src/mesh/outline/offset.rs | 6 +- iOverlay/src/mesh/overlay.rs | 2 +- iOverlay/src/split/fragment.rs | 11 ++-- iOverlay/src/split/grid_layout.rs | 92 ++++++++++++++------------ iOverlay/src/split/line_mark.rs | 13 ++-- iOverlay/src/split/snap_radius.rs | 6 +- iOverlay/src/split/solver.rs | 40 +++++++----- iOverlay/src/split/solver_fragment.rs | 51 +++++++++------ iOverlay/src/split/solver_list.rs | 14 ++-- iOverlay/src/split/solver_tree.rs | 45 ++++++++----- iOverlay/src/string/overlay.rs | 2 +- iOverlay/src/vector/extract.rs | 47 ++++++++------ iOverlay/tests/direction_tests.rs | 16 ++--- iOverlay/tests/fill_rule_tests.rs | 8 +-- iOverlay/tests/overlay_tests.rs | 2 +- 26 files changed, 376 insertions(+), 269 deletions(-) diff --git a/iOverlay/Cargo.toml b/iOverlay/Cargo.toml index dbafde5..dfff2d5 100644 --- a/iOverlay/Cargo.toml +++ b/iOverlay/Cargo.toml @@ -14,7 +14,7 @@ categories = ["algorithms", "graphics", "science::geo", "mathematics", "no-std"] [dependencies] #i_float = { version = "^2.0.0" } #i_shape = { version = "^2.0.0" } -i_tree = { version = "^0.18.0" } +i_tree = { version = "^0.18.0", path = "../../iTree" } i_key_sort = { version = "^0.10.1" } i_float = { path = "../../iFloat"} diff --git a/iOverlay/src/build/util.rs b/iOverlay/src/build/util.rs index 4a9b741..e0f4d10 100644 --- a/iOverlay/src/build/util.rs +++ b/iOverlay/src/build/util.rs @@ -1,14 +1,21 @@ use crate::build::builder::{GraphBuilder, GraphNode}; use crate::segm::winding::WindingCount; 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_key_sort::sort::two_keys::TwoKeysSort; -impl GraphBuilder { +impl GraphBuilder +where + C: WindingCount, + N: GraphNode, + I: IntNumber + i_tree::Expiration + SortKey + Send + Sync, +{ pub(crate) fn test_contour_for_loops( &mut self, - contour: &[IntPoint], - buffer: &mut Vec, + contour: &[IntPoint], + buffer: &mut Vec>, ) -> bool { let n = contour.len(); if n < 64 { diff --git a/iOverlay/src/core/edge_data.rs b/iOverlay/src/core/edge_data.rs index 050b2bb..161c1a9 100644 --- a/iOverlay/src/core/edge_data.rs +++ b/iOverlay/src/core/edge_data.rs @@ -1,4 +1,5 @@ use crate::segm::boolean::ShapeCountBoolean; +use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; pub trait OverlayEdgeData: Copy + PartialEq + Send + Sync { @@ -8,7 +9,7 @@ pub trait OverlayEdgeData: Copy + PartialEq + Send + Sync } #[inline(always)] - fn split(self, _ctx: EdgeDataSplit) -> (Self, Self) { + fn split(self, _ctx: EdgeDataSplit) -> (Self, Self) { (self, self) } @@ -16,10 +17,10 @@ pub trait OverlayEdgeData: Copy + PartialEq + Send + Sync } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct EdgeDataSplit { - pub a: IntPoint, - pub p: IntPoint, - pub b: IntPoint, +pub struct EdgeDataSplit { + pub a: IntPoint, + pub p: IntPoint, + pub b: IntPoint, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/iOverlay/src/core/edge_overlay.rs b/iOverlay/src/core/edge_overlay.rs index a16ae5c..2b174cd 100644 --- a/iOverlay/src/core/edge_overlay.rs +++ b/iOverlay/src/core/edge_overlay.rs @@ -24,9 +24,9 @@ pub struct InputEdge { pub struct EdgeOverlay { pub solver: Solver, pub options: IntOverlayOptions, - pub boolean_buffer: Option, + pub boolean_buffer: Option>, segments: Vec>, - split_solver: SplitSolver, + split_solver: SplitSolver, graph_builder: GraphBuilder, } diff --git a/iOverlay/src/core/extract.rs b/iOverlay/src/core/extract.rs index d84974e..90e45d4 100644 --- a/iOverlay/src/core/extract.rs +++ b/iOverlay/src/core/extract.rs @@ -10,12 +10,17 @@ use crate::geom::v_segment::VSegment; use crate::i_shape::flat::buffer::FlatContoursBuffer; use alloc::vec; use alloc::vec::Vec; +use i_float::int::number::int::IntNumber; +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, Debug, PartialEq, Eq, Default)] @@ -27,14 +32,26 @@ pub(crate) enum VisitState { HullVisited = 3, } -#[derive(Default)] -pub struct BooleanExtractionBuffer { - pub(crate) points: Vec, +pub struct BooleanExtractionBuffer { + pub(crate) points: Vec>, pub(crate) visited: Vec, pub(crate) contour_visited: Option>, } -impl OverlayGraph<'_, i32> { +impl Default for BooleanExtractionBuffer { + fn default() -> Self { + Self { + points: Vec::new(), + visited: Vec::new(), + contour_visited: None, + } + } +} + +impl OverlayGraph<'_, I> +where + I: IntNumber + Expiration + SortKey + Send + Sync, +{ /// 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. /// - `buffer`: Reusable buffer, optimisation purpose only. @@ -50,8 +67,8 @@ impl OverlayGraph<'_, i32> { pub fn extract_shapes( &self, overlay_rule: OverlayRule, - buffer: &mut BooleanExtractionBuffer, - ) -> IntShapes { + buffer: &mut BooleanExtractionBuffer, + ) -> IntShapes { self.links .filter_by_overlay_into(overlay_rule, &mut buffer.visited); if self.options.ogc { @@ -76,8 +93,8 @@ impl OverlayGraph<'_, i32> { pub fn extract_contours_into( &self, overlay_rule: OverlayRule, - buffer: &mut BooleanExtractionBuffer, - output: &mut FlatContoursBuffer, + buffer: &mut BooleanExtractionBuffer, + output: &mut FlatContoursBuffer, ) { self.links .filter_by_overlay_into(overlay_rule, &mut buffer.visited); @@ -87,8 +104,8 @@ impl OverlayGraph<'_, i32> { pub(crate) fn extract( &self, overlay_rule: OverlayRule, - buffer: &mut BooleanExtractionBuffer, - ) -> IntShapes { + buffer: &mut BooleanExtractionBuffer, + ) -> IntShapes { let clockwise = self.options.output_direction == ContourDirection::Clockwise; let mut shapes = Vec::new(); @@ -160,7 +177,7 @@ impl OverlayGraph<'_, i32> { } }; - debug_assert_eq!(v_segment, contour.left_bottom_segment()); + debug_assert!(v_segment == contour.left_bottom_segment()); let id_data = ContourIndex::new_hole(holes.len()); anchors.push(IdSegment::with_segment(id_data, v_segment)); holes.push(contour); @@ -180,11 +197,11 @@ impl OverlayGraph<'_, i32> { pub(crate) fn find_contour( &self, - start_data: &StartPathData, + start_data: &StartPathData, clockwise: bool, visited_state: VisitState, visited: &mut [VisitState], - points: &mut Vec, + points: &mut Vec>, ) { let mut link_id = start_data.link_id; let mut node_id = start_data.node_id; @@ -212,8 +229,8 @@ impl OverlayGraph<'_, i32> { fn extract_contours( &self, overlay_rule: OverlayRule, - buffer: &mut BooleanExtractionBuffer, - output: &mut FlatContoursBuffer, + buffer: &mut BooleanExtractionBuffer, + output: &mut FlatContoursBuffer, ) { let clockwise = self.options.output_direction == ContourDirection::Clockwise; let len = buffer.visited.len(); @@ -265,16 +282,16 @@ impl OverlayGraph<'_, i32> { } } -pub(crate) struct StartPathData { - pub(crate) begin: IntPoint, +pub(crate) struct StartPathData { + pub(crate) begin: IntPoint, pub(crate) node_id: usize, pub(crate) link_id: usize, pub(crate) last_node_id: usize, } -impl StartPathData { +impl StartPathData { #[inline(always)] - pub(crate) fn new(direction: bool, link: &OverlayLink, link_id: usize) -> Self { + pub(crate) fn new(direction: bool, link: &OverlayLink, link_id: usize) -> Self { if direction { Self { begin: link.b.point, @@ -293,12 +310,12 @@ impl StartPathData { } } -pub(crate) trait GraphContour { +pub(crate) trait GraphContour { fn validate(&mut self, min_output_area: u64, preserve_output_collinear: bool) -> (bool, bool); - fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize; + fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize; } -impl GraphContour for IntContour { +impl GraphContour for IntContour { #[inline] fn validate(&mut self, min_output_area: u64, preserve_output_collinear: bool) -> (bool, bool) { let is_modified = if !preserve_output_collinear { @@ -316,13 +333,14 @@ impl GraphContour for IntContour { } let area = self.unsafe_area(); let abs_area = area.unsigned_abs() >> 1; - let is_valid = abs_area >= min_output_area; + let min_area = <::UInt as UIntNumber>::from_u64(min_output_area); + let is_valid = abs_area >= min_area; (is_valid, is_modified) } #[inline] - fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize { + fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize { if link.a.id == node_id { self.push(link.a.point); link.b.id @@ -386,8 +404,8 @@ impl GraphUtil { /// * For bridge nodes, both `bridge[k] < links.len()` /// * `visited` is at least `links.len()` long (or whatever invariant applies) #[inline] - pub(crate) unsafe fn find_left_top_link( - links: &[OverlayLink], + pub(crate) unsafe fn find_left_top_link( + links: &[OverlayLink], nodes: &[OverlayNode], link_index: usize, visited: &[VisitState], @@ -414,9 +432,9 @@ impl GraphUtil { } #[inline(always)] - fn find_left_top_link_on_indices( - links: &[OverlayLink], - link: &OverlayLink, + fn find_left_top_link_on_indices( + links: &[OverlayLink], + link: &OverlayLink, link_index: usize, indices: &[usize], visited: &[VisitState], @@ -450,7 +468,10 @@ impl GraphUtil { } #[inline(always)] - fn find_left_top_link_on_bridge(links: &[OverlayLink], bridge: &[usize; 2]) -> usize { + fn find_left_top_link_on_bridge( + links: &[OverlayLink], + bridge: &[usize; 2], + ) -> usize { // SAFETY: every bridge index comes straight from GraphBuilder::build_nodes_and_connect_links, // which only records values in 0..links.len(), so the unchecked lookups stay in-bounds. let (l0, l1) = unsafe { (links.get_unchecked(bridge[0]), links.get_unchecked(bridge[1])) }; @@ -462,8 +483,8 @@ impl GraphUtil { } #[inline(always)] - pub(crate) fn next_link( - links: &[OverlayLink], + pub(crate) fn next_link( + links: &[OverlayLink], nodes: &[OverlayNode], link_id: usize, node_id: usize, @@ -493,8 +514,8 @@ impl GraphUtil { // so every element is a valid index into `links`, and at least one of them is // still unvisited when we enter. The unchecked accesses rely on that invariant. #[inline] - fn find_nearest_link_to( - links: &[OverlayLink], + fn find_nearest_link_to( + links: &[OverlayLink], target_index: usize, node_id: usize, clockwise: bool, diff --git a/iOverlay/src/core/extract_ogc.rs b/iOverlay/src/core/extract_ogc.rs index cd02a62..4d0d108 100644 --- a/iOverlay/src/core/extract_ogc.rs +++ b/iOverlay/src/core/extract_ogc.rs @@ -9,16 +9,22 @@ use crate::core::overlay_rule::OverlayRule; use crate::geom::v_segment::VSegment; 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<'_, i32> { +impl OverlayGraph<'_, I> +where + I: IntNumber + Expiration + SortKey + Send + Sync, +{ pub(crate) fn extract_ogc( &self, overlay_rule: OverlayRule, - buffer: &mut BooleanExtractionBuffer, - ) -> IntShapes { + buffer: &mut BooleanExtractionBuffer, + ) -> IntShapes { let is_main_dir_cw = self.options.output_direction == ContourDirection::Clockwise; let mut contour_visited = if let Some(mut visited) = buffer.contour_visited.take() { @@ -161,7 +167,7 @@ impl OverlayGraph<'_, i32> { } }; - debug_assert_eq!(v_segment, contour.left_bottom_segment()); + debug_assert!(v_segment == contour.left_bottom_segment()); let id_data = ContourIndex::new_hole(holes.len()); anchors.push(IdSegment::with_segment(id_data, v_segment)); holes.push(contour); @@ -181,7 +187,7 @@ impl OverlayGraph<'_, i32> { fn skip_contour( &self, - start_data: &StartPathData, + start_data: &StartPathData, clockwise: bool, visited_state: VisitState, visited: &mut [VisitState], @@ -214,12 +220,12 @@ impl OverlayGraph<'_, i32> { fn collect_shape( &self, - start_data: &StartPathData, + start_data: &StartPathData, clockwise: bool, global_visited: &mut [VisitState], contour_visited: &mut [VisitState], - points: &mut Vec, - ) -> Option> { + points: &mut Vec>, + ) -> Option> { let mut link_id = start_data.link_id; let mut node_id = start_data.node_id; let last_node_id = start_data.last_node_id; diff --git a/iOverlay/src/core/overlay.rs b/iOverlay/src/core/overlay.rs index 875f0f3..7258165 100644 --- a/iOverlay/src/core/overlay.rs +++ b/iOverlay/src/core/overlay.rs @@ -14,9 +14,12 @@ 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::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}; @@ -61,16 +64,19 @@ 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, - pub(crate) segments: Vec>, - pub(crate) split_solver: SplitSolver, - pub(crate) graph_builder: GraphBuilder, + pub boolean_buffer: Option>, + pub(crate) segments: Vec>, + pub(crate) split_solver: SplitSolver, + pub(crate) graph_builder: GraphBuilder, } -impl Overlay { +impl Overlay +where + I: IntNumber + Expiration + LayoutNumber + SortKey + Send + Sync, +{ /// 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. /// - `capacity`: The initial capacity for storing edge data. Ideally, this should be set to the sum of the edges of all shapes to be added to the overlay, ensuring efficient data management. @@ -81,7 +87,7 @@ impl Overlay { boolean_buffer: Some(Default::default()), segments: Vec::with_capacity(capacity), split_solver: SplitSolver::new(), - graph_builder: GraphBuilder::::new(), + graph_builder: GraphBuilder::::new(), } } @@ -97,14 +103,14 @@ impl Overlay { boolean_buffer: Some(Default::default()), segments: Vec::with_capacity(capacity), split_solver: SplitSolver::new(), - graph_builder: GraphBuilder::::new(), + graph_builder: GraphBuilder::::new(), } } /// Creates a new `Overlay` instance and initializes it with subject and clip contours. /// - `subj`: An array of contours that together define the subject. /// - `clip`: An array of contours that together define the clip. - pub fn with_contour(subj: &[IntPoint], clip: &[IntPoint]) -> Self { + pub fn with_contour(subj: &[IntPoint], clip: &[IntPoint]) -> Self { let mut overlay = Self::new(subj.len() + clip.len()); overlay.add_contour(subj, ShapeType::Subject); overlay.add_contour(clip, ShapeType::Clip); @@ -117,8 +123,8 @@ impl Overlay { /// - `options`: Adjust custom behavior. /// - `solver`: Type of solver to use. pub fn with_contour_custom( - subj: &[IntPoint], - clip: &[IntPoint], + subj: &[IntPoint], + clip: &[IntPoint], options: IntOverlayOptions, solver: Solver, ) -> Self { @@ -131,7 +137,7 @@ impl Overlay { /// Creates a new `Overlay` instance and initializes it with subject and clip contours. /// - `subj`: An array of contours that together define the subject shape. /// - `clip`: An array of contours that together define the clip shape. - pub fn with_contours(subj: &[IntContour], clip: &[IntContour]) -> Self { + pub fn with_contours(subj: &[IntContour], clip: &[IntContour]) -> Self { let mut overlay = Self::new(subj.points_count() + clip.points_count()); overlay.add_contours(subj, ShapeType::Subject); overlay.add_contours(clip, ShapeType::Clip); @@ -144,8 +150,8 @@ impl Overlay { /// - `options`: Adjust custom behavior. /// - `solver`: Type of solver to use. pub fn with_contours_custom( - subj: &[IntContour], - clip: &[IntContour], + subj: &[IntContour], + clip: &[IntContour], options: IntOverlayOptions, solver: Solver, ) -> Self { @@ -158,7 +164,7 @@ impl Overlay { /// Creates a new `Overlay` instance and initializes it with subject and clip shapes. /// - `subj`: An array of shapes to be used as the subject in the overlay operation. /// - `clip`: An array of shapes to be used as the clip in the overlay operation. - pub fn with_shapes(subj: &[IntShape], clip: &[IntShape]) -> Self { + pub fn with_shapes(subj: &[IntShape], clip: &[IntShape]) -> Self { let mut overlay = Self::new(subj.points_count() + clip.points_count()); overlay.add_shapes(subj, ShapeType::Subject); overlay.add_shapes(clip, ShapeType::Clip); @@ -171,8 +177,8 @@ impl Overlay { /// - `options`: Adjust custom behavior. /// - `solver`: Type of solver to use. pub fn with_shapes_options( - subj: &[IntShape], - clip: &[IntShape], + subj: &[IntShape], + clip: &[IntShape], options: IntOverlayOptions, solver: Solver, ) -> Self { @@ -188,7 +194,7 @@ impl Overlay { /// - `iter`: An iterator over references to `IntPoint` that defines the path. /// - `shape_type`: Specifies the role of the added path in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_path_iter>(&mut self, iter: I, shape_type: ShapeType) { + pub fn add_path_iter>>(&mut self, iter: It, shape_type: ShapeType) { self.segments .append_path_iter(iter, shape_type, self.options.preserve_input_collinear); } @@ -197,7 +203,7 @@ impl Overlay { /// - `contour`: An array of points that form a closed path. /// - `shape_type`: Specifies the role of the added path in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_contour(&mut self, contour: &[IntPoint], shape_type: ShapeType) { + pub fn add_contour(&mut self, contour: &[IntPoint], shape_type: ShapeType) { self.segments.append_path_iter( contour.iter().copied(), shape_type, @@ -209,7 +215,7 @@ impl Overlay { /// - `contours`: An array of `IntContour` instances to be added to the overlay. /// - `shape_type`: Specifies the role of the added paths in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_contours(&mut self, contours: &[IntContour], shape_type: ShapeType) { + pub fn add_contours(&mut self, contours: &[IntContour], shape_type: ShapeType) { for contour in contours.iter() { self.add_contour(contour, shape_type); } @@ -219,7 +225,7 @@ impl Overlay { /// - `shape`: A reference to a `IntShape` instance to be added. /// - `shape_type`: Specifies the role of the added shape in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_shape(&mut self, shape: &IntShape, shape_type: ShapeType) { + pub fn add_shape(&mut self, shape: &IntShape, shape_type: ShapeType) { self.add_contours(shape, shape_type); } @@ -227,7 +233,7 @@ impl Overlay { /// - `shapes`: An array of `IntShape` instances to be added to the overlay. /// - `shape_type`: Specifies the role of the added shapes in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_shapes(&mut self, shapes: &[IntShape], shape_type: ShapeType) { + pub fn add_shapes(&mut self, shapes: &[IntShape], shape_type: ShapeType) { for shape in shapes.iter() { self.add_contours(shape, shape_type); } @@ -242,7 +248,7 @@ impl Overlay { /// - `buffer`: A buffer of `IntShapes` instances to be added to the overlay. /// - `shape_type`: Specifies the role of the added shapes in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_flat_buffer(&mut self, buffer: &FlatContoursBuffer, shape_type: ShapeType) { + pub fn add_flat_buffer(&mut self, buffer: &FlatContoursBuffer, shape_type: ShapeType) { for range in buffer.ranges.iter() { let contour = &buffer.points[range.clone()]; self.add_contour(contour, shape_type); @@ -256,7 +262,7 @@ impl Overlay { &mut self, fill_rule: FillRule, overlay_rule: OverlayRule, - ) -> Vec> { + ) -> Vec> { self.split_solver.split_segments(&mut self.segments, &self.solver); if self.segments.is_empty() { return Vec::new(); @@ -282,7 +288,7 @@ impl Overlay { /// Convert into vectors from the added paths or shapes, applying the specified build rule. This method is particularly useful for development purposes and for creating visualizations in educational demos, where understanding the impact of different rules on the final geometry is crucial. /// - `fill_rule`: The build rule to use for the shapes. - pub fn build_separate_vectors(&mut self, fill_rule: FillRule) -> Vec> { + pub fn build_separate_vectors(&mut self, fill_rule: FillRule) -> Vec> { self.split_solver.split_segments(&mut self.segments, &self.solver); if self.segments.is_empty() { return Vec::new(); @@ -295,7 +301,7 @@ impl Overlay { /// Convert into `OverlayGraph` from the added paths or shapes using the specified build rule. This graph is the foundation for executing boolean operations, allowing for the analysis and manipulation of the geometric data. The `OverlayGraph` created by this method represents a preprocessed state of the input shapes, optimized for the application of boolean operations based on the provided build rule. /// - `fill_rule`: Specifies the rule for determining filled areas within the shapes, influencing how the resulting graph represents intersections and unions. #[inline] - pub fn build_graph_view(&mut self, fill_rule: FillRule) -> Option> { + pub fn build_graph_view(&mut self, fill_rule: FillRule) -> Option> { self.split_solver.split_segments(&mut self.segments, &self.solver); if self.segments.is_empty() { return None; @@ -346,7 +352,7 @@ impl Overlay { /// without subsequent modifications. By excluding unnecessary graph structures, it optimizes performance, /// particularly for complex or resource-intensive geometries. #[inline] - pub fn overlay(&mut self, overlay_rule: OverlayRule, fill_rule: FillRule) -> IntShapes { + pub fn overlay(&mut self, overlay_rule: OverlayRule, fill_rule: FillRule) -> IntShapes { self.split_solver.split_segments(&mut self.segments, &self.solver); if self.segments.is_empty() { return Vec::new(); @@ -380,7 +386,7 @@ impl Overlay { &mut self, overlay_rule: OverlayRule, fill_rule: FillRule, - output: &mut FlatContoursBuffer, + output: &mut FlatContoursBuffer, ) { self.split_solver.split_segments(&mut self.segments, &self.solver); if self.segments.is_empty() { @@ -468,7 +474,7 @@ mod tests { let shape = &result[0]; assert_eq!(shape.len(), 1); assert_eq!(shape[0].len(), 4); - assert_eq!(shape[0].area(), -100); + assert_eq!(shape[0].area(), -100i64); } #[test] @@ -495,7 +501,7 @@ mod tests { let shape = &result[0]; assert_eq!(shape.len(), 1); assert_eq!(shape[0].len(), 4); - assert_eq!(shape[0].area(), -100); + assert_eq!(shape[0].area(), -100i64); } #[test] @@ -522,9 +528,9 @@ mod tests { let shape = &result[0]; assert_eq!(shape.len(), 2); assert_eq!(shape[0].len(), 4); - assert_eq!(shape[0].area(), -16); + assert_eq!(shape[0].area(), -16i64); assert_eq!(shape[1].len(), 4); - assert_eq!(shape[1].area(), 4); + assert_eq!(shape[1].area(), 4i64); } #[test] @@ -550,7 +556,7 @@ mod tests { assert_eq!(result.len(), 1); let shape = &result[0]; assert_eq!(shape.len(), 1); - assert_eq!(shape[0].area(), -10); + assert_eq!(shape[0].area(), -10i64); } #[test] @@ -582,7 +588,7 @@ mod tests { assert_eq!(result.len(), 1); let shape = &result[0]; assert_eq!(shape.len(), 1); - assert_eq!(shape[0].area(), -17); + assert_eq!(shape[0].area(), -17i64); } #[test] @@ -608,7 +614,7 @@ mod tests { assert_eq!(result.len(), 1); let shape = &result[0]; assert_eq!(shape.len(), 1); - assert_eq!(shape[0].area(), -16); + assert_eq!(shape[0].area(), -16i64); } #[test] @@ -646,7 +652,7 @@ mod tests { assert_eq!(result.len(), 1); let shape = &result[0]; assert_eq!(shape.len(), 1); - assert_eq!(shape[0].area(), -27); + assert_eq!(shape[0].area(), -27i64); } #[test] @@ -672,7 +678,7 @@ mod tests { assert_eq!(result.len(), 1); let shape = &result[0]; assert_eq!(shape.len(), 1); - assert_eq!(shape[0].area(), -12); + assert_eq!(shape[0].area(), -12i64); } #[test] @@ -698,7 +704,7 @@ mod tests { assert_eq!(result.len(), 1); let shape = &result[0]; assert_eq!(shape.len(), 1); - assert_eq!(shape[0].area(), -4); + assert_eq!(shape[0].area(), -4i64); } #[test] @@ -724,7 +730,7 @@ mod tests { assert_eq!(result.len(), 2); assert_eq!(result[0].len(), 1); assert_eq!(result[1].len(), 1); - assert_eq!(result.area(), -4); + assert_eq!(result.area(), -4i64); } #[test] @@ -749,7 +755,7 @@ mod tests { assert_eq!(result.len(), 1); assert_eq!(result[0].len(), 1); - assert_eq!(result.area(), -6); + assert_eq!(result.area(), -6i64); } #[test] @@ -774,7 +780,7 @@ mod tests { assert_eq!(result.len(), 1); assert_eq!(result[0].len(), 1); - assert_eq!(result.area(), -14); + assert_eq!(result.area(), -14i64); } #[test] @@ -799,7 +805,7 @@ mod tests { assert_eq!(result.len(), 1); assert_eq!(result[0].len(), 1); - assert_eq!(result.area(), -25); + assert_eq!(result.area(), -25i64); } #[test] @@ -824,7 +830,7 @@ mod tests { assert_eq!(result.len(), 1); assert_eq!(result[0].len(), 1); - assert_eq!(result.area(), -25); + assert_eq!(result.area(), -25i64); } #[test] @@ -841,7 +847,7 @@ mod tests { assert_eq!(result.len(), 1); assert_eq!(result[0].len(), 1); - assert_eq!(result.area(), -8); + assert_eq!(result.area(), -8i64); } #[test] diff --git a/iOverlay/src/core/relate.rs b/iOverlay/src/core/relate.rs index 6bc0e7d..6e78bd3 100644 --- a/iOverlay/src/core/relate.rs +++ b/iOverlay/src/core/relate.rs @@ -39,7 +39,7 @@ pub struct PredicateOverlay { /// Fill rule for determining polygon interiors. pub fill_rule: FillRule, pub(crate) segments: Vec>, - pub(crate) split_solver: SplitSolver, + pub(crate) split_solver: SplitSolver, sweep_runner: SweepRunner, } diff --git a/iOverlay/src/core/simplify.rs b/iOverlay/src/core/simplify.rs index e5376b5..4b92303 100644 --- a/iOverlay/src/core/simplify.rs +++ b/iOverlay/src/core/simplify.rs @@ -6,19 +6,22 @@ use crate::core::overlay::ContourDirection; use crate::core::overlay::ContourDirection::Clockwise; use crate::core::overlay::{IntOverlayOptions, Overlay, ShapeType}; 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 /// determine which areas should be retained or excluded. -pub trait Simplify { +pub trait Simplify { /// Simplifies the shape or collection of points, contours, or shapes, based on a specified minimum area threshold. /// /// - `fill_rule`: Fill rule to determine filled areas (non-zero, even-odd, positive, negative). @@ -30,12 +33,15 @@ pub trait Simplify { /// - Each path `Vec` is a sequence of points, forming a closed path. /// /// Note: Outer boundary paths have a **main_direction** order, and holes have an opposite to **main_direction** order. - fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes; + fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes; } -impl Simplify for [IntPoint] { +impl Simplify for [IntPoint] +where + I: IntNumber + Expiration + LayoutNumber + SortKey + Send + Sync, +{ #[inline] - fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { + fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { match Overlay::new_custom(self.len(), options, Default::default()).simplify_contour(self, fill_rule) { Some(shapes) => shapes, None => vec![vec![self.to_vec()]], @@ -43,9 +49,12 @@ impl Simplify for [IntPoint] { } } -impl Simplify for [IntContour] { +impl Simplify for [IntContour] +where + I: IntNumber + Expiration + LayoutNumber + SortKey + Send + Sync, +{ #[inline] - fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { + fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { match Overlay::new_custom(self.len(), options, Default::default()).simplify_shape(self, fill_rule) { Some(shapes) => shapes, None => vec![self.to_vec()], @@ -53,9 +62,12 @@ impl Simplify for [IntContour] { } } -impl Simplify for [IntShape] { +impl Simplify for [IntShape] +where + I: IntNumber + Expiration + LayoutNumber + SortKey + Send + Sync, +{ #[inline] - fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { + fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { Overlay::new_custom(self.points_count(), options, Default::default()).simplify_shapes(self, fill_rule) } } @@ -66,7 +78,10 @@ enum ContourFillDirection { Empty, } -impl Overlay { +impl Overlay +where + I: IntNumber + Expiration + LayoutNumber + SortKey + Send + Sync, +{ /// Fast-path simplification for a single contour. /// /// Skips full overlay if the contour is already simple (no splits, no loops, no collinear issues). @@ -74,7 +89,7 @@ impl Overlay { /// /// Returns `None` if the contour is valid and needs no changes, or `Some(IntShapes)` with the simplified result. #[inline] - pub fn simplify_contour(&mut self, contour: &[IntPoint], fill_rule: FillRule) -> Option> { + pub fn simplify_contour(&mut self, contour: &[IntPoint], fill_rule: FillRule) -> Option> { self.clear(); let is_perfect = self.find_intersections(contour); @@ -117,7 +132,7 @@ impl Overlay { fn contour_direction( output_direction: ContourDirection, fill_rule: FillRule, - contour: &[IntPoint], + contour: &[IntPoint], ) -> ContourFillDirection { let contour_clockwise = contour.is_clockwise_ordered(); let output_clockwise = output_direction == Clockwise; @@ -148,11 +163,7 @@ impl Overlay { } #[inline] - pub fn simplify_shape( - &mut self, - shape: &[IntContour], - fill_rule: FillRule, - ) -> Option> { + pub fn simplify_shape(&mut self, shape: &[IntContour], fill_rule: FillRule) -> Option> { if shape.len() == 1 { return self.simplify_contour(&shape[0], fill_rule); } @@ -162,14 +173,14 @@ impl Overlay { } #[inline] - pub fn simplify_shapes(&mut self, shapes: &[IntShape], fill_rule: FillRule) -> IntShapes { + pub fn simplify_shapes(&mut self, shapes: &[IntShape], fill_rule: FillRule) -> IntShapes { self.clear(); self.add_shapes(shapes, ShapeType::Subject); self.overlay(OverlayRule::Subject, fill_rule) } #[inline] - pub fn simplify_flat_buffer(&mut self, flat_buffer: &mut FlatContoursBuffer, fill_rule: FillRule) { + pub fn simplify_flat_buffer(&mut self, flat_buffer: &mut FlatContoursBuffer, fill_rule: FillRule) { self.clear(); if flat_buffer.is_single_contour() { @@ -216,7 +227,7 @@ impl Overlay { self.boolean_buffer = Some(boolean_buffer); } - fn find_intersections(&mut self, contour: &[IntPoint]) -> bool { + fn find_intersections(&mut self, contour: &[IntPoint]) -> bool { let append_modified = self.segments.append_path_iter( contour.iter().copied(), ShapeType::Subject, diff --git a/iOverlay/src/float/graph.rs b/iOverlay/src/float/graph.rs index fe62476..de7720f 100644 --- a/iOverlay/src/float/graph.rs +++ b/iOverlay/src/float/graph.rs @@ -56,7 +56,7 @@ impl<'a, P: FloatPointCompatible> FloatOverlayGraph<'a, P> { pub fn extract_shapes( &self, overlay_rule: OverlayRule, - buffer: &mut BooleanExtractionBuffer, + buffer: &mut BooleanExtractionBuffer, ) -> Shapes

{ let shapes = self.graph.extract_shapes(overlay_rule, buffer); let mut float = shapes.to_float(&self.adapter); diff --git a/iOverlay/src/float/overlay.rs b/iOverlay/src/float/overlay.rs index 41d22d5..cffaef2 100644 --- a/iOverlay/src/float/overlay.rs +++ b/iOverlay/src/float/overlay.rs @@ -43,7 +43,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(super) overlay: Overlay, + pub(super) overlay: Overlay, pub(super) clean_result: bool, pub(super) adapter: FloatPointAdapter, } diff --git a/iOverlay/src/mesh/outline/offset.rs b/iOverlay/src/mesh/outline/offset.rs index 794840a..346cb09 100644 --- a/iOverlay/src/mesh/outline/offset.rs +++ b/iOverlay/src/mesh/outline/offset.rs @@ -264,7 +264,11 @@ impl OutlineSolver

{ Ok(()) } - fn build_overlay>(&self, source: &S, options: OverlayOptions) -> Overlay { + fn build_overlay>( + &self, + source: &S, + options: OverlayOptions, + ) -> Overlay { let total_capacity = self.outer_builder.capacity(self.points_count); let mut overlay = Overlay::new_custom( total_capacity, diff --git a/iOverlay/src/mesh/overlay.rs b/iOverlay/src/mesh/overlay.rs index a9c4377..a90e930 100644 --- a/iOverlay/src/mesh/overlay.rs +++ b/iOverlay/src/mesh/overlay.rs @@ -6,7 +6,7 @@ use crate::segm::segment::Segment; use crate::split::solver::SplitSolver; use alloc::vec::Vec; -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/split/fragment.rs b/iOverlay/src/split/fragment.rs index 53066e9..c3626c1 100644 --- a/iOverlay/src/split/fragment.rs +++ b/iOverlay/src/split/fragment.rs @@ -1,16 +1,17 @@ use crate::geom::x_segment::XSegment; +use i_float::int::number::int::IntNumber; use i_float::int::rect::IntRect; #[derive(Debug, Clone)] -pub(super) struct Fragment { +pub(super) struct Fragment { pub(super) index: usize, - pub(super) rect: IntRect, - pub(super) x_segment: XSegment, + pub(super) rect: IntRect, + pub(super) x_segment: XSegment, } -impl Fragment { +impl Fragment { #[inline] - pub(super) fn with_index_and_segment(index: usize, x_segment: XSegment) -> Self { + pub(super) fn with_index_and_segment(index: usize, x_segment: XSegment) -> Self { let (min_y, max_y) = if x_segment.a.y < x_segment.b.y { (x_segment.a.y, x_segment.b.y) } else { diff --git a/iOverlay/src/split/grid_layout.rs b/iOverlay/src/split/grid_layout.rs index 4b43067..9867696 100644 --- a/iOverlay/src/split/grid_layout.rs +++ b/iOverlay/src/split/grid_layout.rs @@ -3,24 +3,27 @@ use crate::geom::x_segment::XSegment; use crate::split::fragment::Fragment; use alloc::vec; use alloc::vec::Vec; +use i_float::int::number::int::IntNumber; +use i_float::int::number::uint::UIntNumber; +use i_float::int::number::wide_int::WideIntNumber; use i_float::int::rect::IntRect; #[derive(Debug, Clone)] -pub(super) struct BorderVSegment { +pub(super) struct BorderVSegment { pub(super) id: usize, - pub(super) x: i32, - pub(super) y_range: LineRange, + pub(super) x: I, + pub(super) y_range: LineRange, } -pub(super) struct FragmentBuffer { - pub(super) layout: GridLayout, - pub(super) groups: Vec>, - pub(super) on_border: Vec, +pub(super) struct FragmentBuffer { + pub(super) layout: GridLayout, + pub(super) groups: Vec>>, + pub(super) on_border: Vec>, } -impl FragmentBuffer { +impl FragmentBuffer { #[inline] - pub(super) fn new(layout: GridLayout) -> Self { + pub(super) fn new(layout: GridLayout) -> Self { let n = layout.index(layout.max_x) + 1; Self { layout, @@ -29,15 +32,15 @@ impl FragmentBuffer { } } - pub(super) fn init_fragment_buffer(&mut self, iter: I) + pub(super) fn init_fragment_buffer(&mut self, iter: It) where - I: Iterator>, + It: Iterator>, { let mut counts = vec![0; self.groups.len()]; for s in iter { let i0 = self.layout.index(s.a.x); if s.a.x < s.b.x { - let i1 = self.layout.index(s.b.x - 1); + let i1 = self.layout.index(s.b.x - I::ONE); for count in counts.iter_mut().take(i1).skip(i0) { *count += 1; } @@ -52,7 +55,7 @@ impl FragmentBuffer { } #[inline] - fn insert(&mut self, fragment: Fragment, bin_index: usize) { + fn insert(&mut self, fragment: Fragment, bin_index: usize) { debug_assert!(bin_index < self.groups.len()); // SAFETY: `bin_index` is produced by GridLayout::index(...) on coordinates clamped to // [min_x, max_x]. We sized `groups` to `index(max_x) + 1` in `new`, so @@ -62,7 +65,7 @@ impl FragmentBuffer { } #[inline] - fn insert_vertical(&mut self, fragment: Fragment, bin_index: usize) { + fn insert_vertical(&mut self, fragment: Fragment, bin_index: usize) { let x = fragment.x_segment.a.x; if bin_index > 0 && x == self.layout.pos(bin_index) { self.on_border.push(BorderVSegment { @@ -83,7 +86,7 @@ impl FragmentBuffer { } #[inline] - pub(super) fn add_segment(&mut self, segment_index: usize, s: XSegment) { + pub(super) fn add_segment(&mut self, segment_index: usize, s: XSegment) { if s.a.y == s.b.y { self.add_horizontal(segment_index, s); return; @@ -95,7 +98,7 @@ impl FragmentBuffer { return; } - let i1 = self.layout.index(s.b.x - 1); + let i1 = self.layout.index(s.b.x - I::ONE); if i0 >= i1 { self.insert(Fragment::with_index_and_segment(segment_index, s), i0); return; @@ -109,44 +112,45 @@ impl FragmentBuffer { let is_inc = s.a.y <= s.b.y; - let width = (s.b.x - s.a.x) as u64; - let height = (s.b.y - s.a.y).unsigned_abs() as u64; + let width = (s.b.x.wide() - s.a.x.wide()).to_uint(); + let height = (s.b.y.wide() - s.a.y.wide()).unsigned_abs(); let log = (width * height).ilog2(); - let p = 63 - log; + let p = <::UInt as UIntNumber>::LAST_BIT_INDEX - log; let k = (height << p) / width; - let mut w = (self.layout.pos(i0 + 1) - s.a.x) as u64; - let dw = 1 << self.layout.power; + let mut w = (self.layout.pos(i0 + 1).wide() - s.a.x.wide()).to_uint(); + let dw = <::UInt as UIntNumber>::one_shl(self.layout.power); + let one = <::UInt as UIntNumber>::ONE; for i in i0..i1 { let h_min = (w * k) >> p; let mut h_max = h_min; while h_max * width < height * w { - h_max += 1; + h_max += one; } - let max_x = x0 + (w as i32); + let max_x = x0 + I::from_uint(w); let rect = if is_inc { - let max_y = y0 + h_max as i32; + let max_y = y0 + I::from_uint(h_max); let rect = IntRect { min_x: prev_x, max_x, min_y: prev_y, max_y, }; - prev_y = y0 + h_min as i32; + prev_y = y0 + I::from_uint(h_min); rect } else { - let min_y = y0 - h_max as i32; + let min_y = y0 - I::from_uint(h_max); let rect = IntRect { min_x: prev_x, max_x, min_y, max_y: prev_y, }; - prev_y = y0 - h_min as i32; + prev_y = y0 - I::from_uint(h_min); rect }; @@ -189,9 +193,9 @@ impl FragmentBuffer { ); } - fn add_horizontal(&mut self, segment_index: usize, s: XSegment) { + fn add_horizontal(&mut self, segment_index: usize, s: XSegment) { let i0 = self.layout.index(s.a.x); - let i1 = self.layout.index(s.b.x - 1); + let i1 = self.layout.index(s.b.x - I::ONE); let y = s.a.y; let mut x0 = s.a.x; @@ -244,26 +248,28 @@ impl FragmentBuffer { } } -pub(super) struct GridLayout { - min_x: i32, - max_x: i32, +pub(super) struct GridLayout { + min_x: I, + max_x: I, power: u32, } -impl GridLayout { +impl GridLayout { #[inline] - pub(super) fn index(&self, x: i32) -> usize { - ((x - self.min_x) >> self.power) as usize + pub(super) fn index(&self, x: I) -> usize { + let step = I::Wide::one_shl(self.power); + ((x.wide() - self.min_x.wide()) / step).to_usize() } #[inline] - pub(super) fn pos(&self, index: usize) -> i32 { - (index << self.power) as i32 + self.min_x + pub(super) fn pos(&self, index: usize) -> I { + let offset = I::Wide::from_usize(index) * I::Wide::one_shl(self.power); + I::from_wide(offset + self.min_x.wide()) } - pub(super) fn new(iter: I, count: usize) -> Option + pub(super) fn new(iter: It, count: usize) -> Option where - I: Iterator>, + It: Iterator>, { let mut iter = iter.peekable(); let first = iter.peek()?; @@ -280,12 +286,12 @@ impl GridLayout { Self::with_min_max(min_x, max_x, max_power) } - fn with_min_max(min_x: i32, max_x: i32, max_power: u32) -> Option { - let dx = max_x - min_x; - if dx < 4 { + fn with_min_max(min_x: I, max_x: I, max_power: u32) -> Option { + let dx = max_x.wide() - min_x.wide(); + if dx < I::Wide::FOUR { return None; } - let log = dx.ilog2(); + let log = dx.to_uint().ilog2(); let power = if log > max_power { log - max_power } else { 1 }; Some(Self { min_x, max_x, power }) diff --git a/iOverlay/src/split/line_mark.rs b/iOverlay/src/split/line_mark.rs index 21e7931..c9b8d7d 100644 --- a/iOverlay/src/split/line_mark.rs +++ b/iOverlay/src/split/line_mark.rs @@ -1,20 +1,21 @@ use alloc::vec::Vec; +use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; use i_key_sort::sort::one_key_cmp::OneKeyAndCmpSort; #[derive(Clone, Copy, PartialEq)] -pub(super) struct LineMark { +pub(super) struct LineMark { pub(super) index: usize, - pub(super) point: IntPoint, + pub(super) point: IntPoint, } -pub(super) trait SortMarkByIndexAndPoint { - fn sort_by_index_and_point(&mut self, parallel: bool, reusable_buffer: &mut Vec); +pub(super) trait SortMarkByIndexAndPoint { + fn sort_by_index_and_point(&mut self, parallel: bool, reusable_buffer: &mut Vec>); } -impl SortMarkByIndexAndPoint for [LineMark] { +impl SortMarkByIndexAndPoint for [LineMark] { #[inline] - fn sort_by_index_and_point(&mut self, parallel: bool, reusable_buffer: &mut Vec) { + fn sort_by_index_and_point(&mut self, parallel: bool, reusable_buffer: &mut Vec>) { self.sort_by_one_key_then_by_and_buffer( parallel, reusable_buffer, diff --git a/iOverlay/src/split/snap_radius.rs b/iOverlay/src/split/snap_radius.rs index 2a13b7a..ee8701b 100644 --- a/iOverlay/src/split/snap_radius.rs +++ b/iOverlay/src/split/snap_radius.rs @@ -1,4 +1,6 @@ use crate::core::solver::Solver; +use i_float::int::number::int::IntNumber; +use i_float::int::number::wide_int::WideIntNumber; pub(super) struct SnapRadius { current: usize, @@ -10,8 +12,8 @@ impl SnapRadius { self.current = 60.min(self.current + self.step); } - pub(super) fn radius(&self) -> i64 { - 1 << self.current + pub(super) fn radius(&self) -> I::Wide { + I::Wide::one_shl(self.current as u32) } } diff --git a/iOverlay/src/split/solver.rs b/iOverlay/src/split/solver.rs index 2aad0db..9a0cc7b 100644 --- a/iOverlay/src/split/solver.rs +++ b/iOverlay/src/split/solver.rs @@ -8,22 +8,30 @@ use crate::segm::winding::WindingCount; 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}; #[derive(Clone)] -pub(crate) struct SplitSolver { - pub(super) marks: Vec, +pub(crate) struct SplitSolver { + pub(super) marks: Vec>, } -impl SplitSolver { +impl SplitSolver { #[inline(always)] pub(crate) fn new() -> Self { Self { marks: Vec::new() } } +} +impl SplitSolver +where + I: IntNumber + Expiration + LayoutNumber + SortKey + Send + Sync, +{ #[inline] pub(crate) fn split_segments>( &mut self, - segments: &mut Vec>, + segments: &mut Vec>, solver: &Solver, ) -> bool { if segments.is_empty() { @@ -40,7 +48,7 @@ impl SplitSolver { #[inline] fn split>( &mut self, - segments: &mut Vec>, + segments: &mut Vec>, solver: &Solver, ) -> bool { let is_list = solver.is_list_split(segments); @@ -61,12 +69,12 @@ impl SplitSolver { pub(super) fn cross( i: usize, j: usize, - ei: &XSegment, - ej: &XSegment, - marks: &mut Vec, - radius: i64, + ei: &XSegment, + ej: &XSegment, + marks: &mut Vec>, + radius: I::Wide, ) -> bool { - let cross = if let Some(cross) = CrossSolver::cross(ei, ej, radius) { + let cross = if let Some(cross) = CrossSolver::::cross(ei, ej, radius) { cross } else { return false; @@ -96,7 +104,7 @@ impl SplitSolver { }); } CrossType::Overlay => { - let mask = CrossSolver::collinear(ei, ej); + let mask = CrossSolver::::collinear(ei, ej); if mask == 0 { return false; } @@ -136,8 +144,8 @@ impl SplitSolver { pub(super) fn apply>( &mut self, - segments: &mut Vec>, - reusable_buffer: &mut Vec, + segments: &mut Vec>, + reusable_buffer: &mut Vec>, solver: &Solver, ) { self.marks @@ -218,7 +226,7 @@ impl SplitSolver { } #[inline] - fn sort_sub_marks(marks: &mut [LineMark], x_seg: XSegment) { + fn sort_sub_marks(marks: &mut [LineMark], x_seg: XSegment) { let mut j0 = 0; let mut j = 1; @@ -248,7 +256,7 @@ impl SplitSolver { } #[inline] - fn y_range(j0: usize, j1: usize, s: XSegment, marks: &[LineMark]) -> (i32, i32) { + fn y_range(j0: usize, j1: usize, s: XSegment, marks: &[LineMark]) -> (I, I) { let y0 = if j0 == 0 { s.a.y } else { marks[j0 - 1].point.y }; let y1 = if j1 == marks.len() { s.b.y @@ -259,7 +267,7 @@ impl SplitSolver { } #[inline] - fn sort_sub_marks_by_y(y0: i32, y1: i32, marks: &mut [LineMark]) { + fn sort_sub_marks_by_y(y0: I, y1: I, marks: &mut [LineMark]) { // The x-coordinate is the same for every point // By default, the range should be sorted in ascending order by the y-coordinate. if y0 > y1 { diff --git a/iOverlay/src/split/solver_fragment.rs b/iOverlay/src/split/solver_fragment.rs index bd49486..85b1685 100644 --- a/iOverlay/src/split/solver_fragment.rs +++ b/iOverlay/src/split/solver_fragment.rs @@ -9,12 +9,18 @@ use crate::split::line_mark::LineMark; use crate::split::snap_radius::SnapRadius; use crate::split::solver::SplitSolver; use alloc::vec::Vec; - -impl SplitSolver { +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 + Send + Sync, +{ pub(super) fn fragment_split>( &mut self, snap_radius: SnapRadius, - segments: &mut Vec>, + segments: &mut Vec>, solver: &Solver, ) -> bool { let layout = @@ -40,7 +46,7 @@ impl SplitSolver { buffer.add_segment(i, segment.x_segment); } - need_to_fix = self.process(snap_radius.radius(), &mut buffer, solver); + need_to_fix = self.process(snap_radius.radius::(), &mut buffer, solver); #[cfg(debug_assertions)] debug_assert!(buffer.is_on_border_sorted()); @@ -75,7 +81,7 @@ impl SplitSolver { } #[inline] - fn process(&mut self, radius: i64, buffer: &mut FragmentBuffer, _solver: &Solver) -> bool { + fn process(&mut self, radius: I::Wide, buffer: &mut FragmentBuffer, _solver: &Solver) -> bool { #[cfg(feature = "allow_multithreading")] { if _solver.multithreading.is_some() { @@ -87,37 +93,37 @@ impl SplitSolver { } #[inline] - fn serial_split(&mut self, radius: i64, buffer: &mut FragmentBuffer) -> bool { + fn serial_split(&mut self, radius: I::Wide, buffer: &mut FragmentBuffer) -> bool { let mut is_any_round = false; for group in buffer.groups.iter_mut() { if group.is_empty() { continue; } - let any_round = SplitSolver::bin_split(radius, group, &mut self.marks); + let any_round = Self::bin_split(radius, group, &mut self.marks); is_any_round = is_any_round || any_round; } is_any_round } #[cfg(feature = "allow_multithreading")] - fn parallel_split(&mut self, radius: i64, buffer: &mut FragmentBuffer) -> bool { + fn parallel_split(&mut self, radius: I::Wide, buffer: &mut FragmentBuffer) -> bool { use rayon::iter::IntoParallelRefMutIterator; use rayon::iter::ParallelIterator; - struct TaskResult { + struct TaskResult { any_round: bool, - marks: Vec, + marks: Vec>, } debug_assert!(!buffer.groups.is_empty(), "groups.len() >= 1"); let marks_capacity = self.marks.capacity() / buffer.groups.len(); - let results: Vec = buffer + let results: Vec> = buffer .groups .par_iter_mut() .map(|group| { let mut marks = Vec::with_capacity(marks_capacity); - let any_round = SplitSolver::bin_split(radius, group, &mut marks); + let any_round = Self::bin_split(radius, group, &mut marks); TaskResult { any_round, marks } }) .collect(); @@ -145,7 +151,7 @@ impl SplitSolver { is_any_round } - fn bin_split(radius: i64, fragments: &mut [Fragment], marks: &mut Vec) -> bool { + fn bin_split(radius: I::Wide, fragments: &mut [Fragment], marks: &mut Vec>) -> bool { if fragments.len() < 2 { return false; } @@ -180,9 +186,9 @@ impl SplitSolver { fn on_border_split( &mut self, - border_x: i32, - fragments: &[Fragment], - vertical_segments: &mut [BorderVSegment], + border_x: I, + fragments: &[Fragment], + vertical_segments: &mut [BorderVSegment], ) { let mut points = Vec::new(); for fragment in fragments.iter() { @@ -214,18 +220,23 @@ impl SplitSolver { } } - fn cross_fragments(fi: &Fragment, fj: &Fragment, radius: i64, marks: &mut Vec) -> bool { - let cross = if let Some(cross) = CrossSolver::cross(&fi.x_segment, &fj.x_segment, radius) { + fn cross_fragments( + fi: &Fragment, + fj: &Fragment, + radius: I::Wide, + marks: &mut Vec>, + ) -> bool { + let cross = if let Some(cross) = CrossSolver::::cross(&fi.x_segment, &fj.x_segment, radius) { cross } else { return false; }; - let r = radius as i32; + let r = I::from_wide(radius); match cross.cross_type { CrossType::Overlay => { - let mask = CrossSolver::collinear(&fi.x_segment, &fj.x_segment); + let mask = CrossSolver::::collinear(&fi.x_segment, &fj.x_segment); if mask == 0 { return false; } diff --git a/iOverlay/src/split/solver_list.rs b/iOverlay/src/split/solver_list.rs index e3a4f69..4c001a6 100644 --- a/iOverlay/src/split/solver_list.rs +++ b/iOverlay/src/split/solver_list.rs @@ -5,12 +5,18 @@ 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 { +impl SplitSolver +where + I: IntNumber + Expiration + LayoutNumber + SortKey + Send + Sync, +{ pub(super) fn list_split>( &mut self, snap_radius: SnapRadius, - segments: &mut Vec>, + segments: &mut Vec>, solver: &Solver, ) -> bool { let mut need_to_fix = true; @@ -23,7 +29,7 @@ impl SplitSolver { need_to_fix = false; self.marks.clear(); - let radius: i64 = snap_radius.radius(); + let radius = snap_radius.radius::(); for (i, si) in segments.iter().enumerate() { let xsi = &si.x_segment; @@ -38,7 +44,7 @@ impl SplitSolver { continue; } - let is_round = SplitSolver::cross(i, j, xsi, xsj, &mut self.marks, radius); + let is_round = Self::cross(i, j, xsi, xsj, &mut self.marks, radius); need_to_fix = need_to_fix || is_round } } diff --git a/iOverlay/src/split/solver_tree.rs b/iOverlay/src/split/solver_tree.rs index 67ea54d..b8c7554 100644 --- a/iOverlay/src/split/solver_tree.rs +++ b/iOverlay/src/split/solver_tree.rs @@ -7,32 +7,38 @@ 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::ExpiredVal; use i_tree::seg::exp::{SegExpCollection, SegRange}; use i_tree::seg::tree::SegExpTree; +use i_tree::{Expiration, LayoutNumber}; #[derive(Debug, Clone, Copy)] -struct IdSegment { +struct IdSegment { id: usize, - x_segment: XSegment, + x_segment: XSegment, } -impl ExpiredVal for IdSegment { +impl ExpiredVal for IdSegment { #[inline] - fn expiration(&self) -> i32 { + fn expiration(&self) -> I { self.x_segment.b.x } } -impl SplitSolver { +impl SplitSolver +where + I: IntNumber + Expiration + LayoutNumber + SortKey + Send + Sync, +{ pub(super) fn tree_split>( &mut self, snap_radius: SnapRadius, - segments: &mut Vec>, + segments: &mut Vec>, solver: &Solver, ) -> bool { - let range: SegRange = segments.ver_range().into(); - let mut tree: SegExpTree = if let Some(tree) = SegExpTree::new(range) { + let range: SegRange = segments.ver_range().into(); + let mut tree: SegExpTree> = if let Some(tree) = SegExpTree::new(range) { tree } else { return self.list_split(snap_radius, segments, solver); @@ -49,7 +55,7 @@ impl SplitSolver { need_to_fix = false; self.marks.clear(); - let radius = snap_radius.radius(); + let radius = snap_radius.radius::(); for (i, si) in segments.iter().enumerate() { let time = si.x_segment.a.x; @@ -61,8 +67,7 @@ impl SplitSolver { (sj.id, i, &sj.x_segment, &si.x_segment) }; - let is_round = - SplitSolver::cross(this_index, scan_index, this, scan, &mut self.marks, radius); + let is_round = Self::cross(this_index, scan_index, this, scan, &mut self.marks, radius); need_to_fix = is_round || need_to_fix; } @@ -86,9 +91,9 @@ impl SplitSolver { } } -impl From> for SegRange { +impl From> for SegRange { #[inline] - fn from(value: LineRange) -> Self { + fn from(value: LineRange) -> Self { Self { min: value.min, max: value.max, @@ -97,11 +102,15 @@ impl From> for SegRange { } trait VerticalRange { - fn ver_range(&self) -> LineRange; + type Int: IntNumber; + + fn ver_range(&self) -> LineRange; } -impl VerticalRange for Vec> { - fn ver_range(&self) -> LineRange { +impl VerticalRange for Vec> { + type Int = I; + + fn ver_range(&self) -> LineRange { let mut min_y = self[0].x_segment.a.y; let mut max_y = min_y; @@ -119,9 +128,9 @@ impl VerticalRange for Vec> { } } -impl Segment { +impl Segment { #[inline] - fn id_segment(&self, id: usize) -> IdSegment { + fn id_segment(&self, id: usize) -> IdSegment { IdSegment { id, x_segment: self.x_segment, diff --git a/iOverlay/src/string/overlay.rs b/iOverlay/src/string/overlay.rs index 847d337..07c2037 100644 --- a/iOverlay/src/string/overlay.rs +++ b/iOverlay/src/string/overlay.rs @@ -22,7 +22,7 @@ use i_shape::int::shape::{IntContour, IntShape}; pub struct StringOverlay { pub options: IntOverlayOptions, pub(super) segments: Vec>, - pub(crate) split_solver: SplitSolver, + pub(crate) split_solver: SplitSolver, pub(crate) graph_builder: GraphBuilder, i32>, } diff --git a/iOverlay/src/vector/extract.rs b/iOverlay/src/vector/extract.rs index 634c7ab..cfb6a80 100644 --- a/iOverlay/src/vector/extract.rs +++ b/iOverlay/src/vector/extract.rs @@ -13,12 +13,18 @@ use crate::vector::simplify::VectorSimplify; use alloc::vec; use alloc::vec::Vec; use i_float::int::number::int::IntNumber; +use i_float::int::number::uint::UIntNumber; +use i_float::int::number::wide_int::WideIntNumber; use i_float::int::point::IntPoint; use i_key_sort::sort::key::SortKey; use i_tree::Expiration; -impl OverlayGraph<'_, i32, D> { - pub fn extract_separate_vectors(&self) -> Vec> { +impl OverlayGraph<'_, I, D> +where + I: IntNumber + Expiration + SortKey + Send + Sync, + D: OverlayEdgeData, +{ + pub fn extract_separate_vectors(&self) -> Vec> { self.links .iter() .map(|link| DataVectorEdge::new(link.fill, link.a.point, link.b.point, ())) @@ -28,8 +34,8 @@ impl OverlayGraph<'_, i32, D> { pub fn extract_vector_shapes( &self, overlay_rule: OverlayRule, - buffer: &mut BooleanExtractionBuffer, - ) -> Vec> { + buffer: &mut BooleanExtractionBuffer, + ) -> Vec> { let clockwise = self.options.output_direction == ContourDirection::Clockwise; self.links .filter_by_overlay_into(overlay_rule, &mut buffer.visited); @@ -95,7 +101,7 @@ impl OverlayGraph<'_, i32, D> { } }; - debug_assert_eq!(v_segment, most_left_bottom(&contour)); + debug_assert!(v_segment == most_left_bottom(&contour)); let id_data = ContourIndex::new_hole(holes.len()); anchors.push(IdSegment::with_segment(id_data, v_segment)); holes.push(contour); @@ -115,11 +121,11 @@ impl OverlayGraph<'_, i32, D> { fn find_vector_contour( &self, - start_data: StartVectorPathData, + start_data: StartVectorPathData, clockwise: bool, visited_state: VisitState, visited: &mut [VisitState], - ) -> DataVectorPath { + ) -> DataVectorPath { let mut link_id = start_data.link_id; let mut node_id = start_data.node_id; let last_node_id = start_data.last_node_id; @@ -152,8 +158,8 @@ impl OverlayGraph<'_, i32, D> { } } -impl OverlayGraph<'_, i32, D> { - pub fn extract_vectors(&self) -> Vec> { +impl OverlayGraph<'_, I, D> { + pub fn extract_vectors(&self) -> Vec> { self.links .iter() .map(|link| DataVectorEdge::new(link.fill, link.a.point, link.b.point, link.data)) @@ -161,9 +167,9 @@ impl OverlayGraph<'_, i32, D> { } } -struct StartVectorPathData { - a: IntPoint, - b: IntPoint, +struct StartVectorPathData { + a: IntPoint, + b: IntPoint, node_id: usize, link_id: usize, last_node_id: usize, @@ -171,9 +177,9 @@ struct StartVectorPathData { data: D, } -impl StartVectorPathData { +impl StartVectorPathData { #[inline(always)] - fn new(direction: bool, link: &OverlayLink, link_id: usize) -> Self { + fn new(direction: bool, link: &OverlayLink, link_id: usize) -> Self { if direction { Self { a: link.b.point, @@ -305,12 +311,12 @@ fn is_sorted(segments: &[IdSegment]) -> bool { .all(|slice| slice[0].v_segment.a <= slice[1].v_segment.a) } -trait DataGraphContour { +trait DataGraphContour { fn validate(&mut self, min_output_area: u64, preserve_output_collinear: bool) -> (bool, bool); - fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize; + fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize; } -impl DataGraphContour for DataVectorPath { +impl DataGraphContour for DataVectorPath { #[inline] fn validate(&mut self, min_output_area: u64, preserve_output_collinear: bool) -> (bool, bool) { let is_modified = if !preserve_output_collinear { @@ -329,13 +335,14 @@ impl DataGraphContour for DataVectorPath { let double_area = self .iter() - .fold(0i64, |acc, edge| acc + edge.a.cross_product(edge.b)); + .fold(I::Wide::ZERO, |acc, edge| acc + edge.a.cross_product(edge.b)); + let min_area = <::UInt as UIntNumber>::from_u64(min_output_area); - ((double_area.unsigned_abs() >> 1) >= min_output_area, is_modified) + ((double_area.unsigned_abs() >> 1) >= min_area, is_modified) } #[inline] - fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize { + fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize { if link.a.id == node_id { self.push(DataVectorEdge::new( link.fill, diff --git a/iOverlay/tests/direction_tests.rs b/iOverlay/tests/direction_tests.rs index e3b3a94..41129c6 100644 --- a/iOverlay/tests/direction_tests.rs +++ b/iOverlay/tests/direction_tests.rs @@ -35,10 +35,10 @@ mod tests { }; let r0 = &path.simplify(FillRule::NonZero, op0)[0][0]; - debug_assert!(r0.area_two() < 0); + debug_assert!(r0.area_two() < 0i64); let r1 = &path.simplify(FillRule::NonZero, op1)[0][0]; - debug_assert!(r1.area_two() > 0); + debug_assert!(r1.area_two() > 0i64); } #[test] @@ -75,12 +75,12 @@ mod tests { }; let r0 = &path.simplify(FillRule::NonZero, op0)[0]; - debug_assert!(r0[0].area_two() < 0); - debug_assert!(r0[1].area_two() > 0); + debug_assert!(r0[0].area_two() < 0i64); + debug_assert!(r0[1].area_two() > 0i64); let r1 = &path.simplify(FillRule::NonZero, op1)[0]; - debug_assert!(r1[0].area_two() > 0); - debug_assert!(r1[1].area_two() < 0); + debug_assert!(r1[0].area_two() > 0i64); + debug_assert!(r1[1].area_two() < 0i64); } #[test] @@ -102,7 +102,7 @@ mod tests { // test default behavior let r = Overlay::with_contours(&path, &[]).overlay(OverlayRule::Subject, FillRule::NonZero); - debug_assert!(r[0][0].area_two() < 0); - debug_assert!(r[0][1].area_two() > 0); + debug_assert!(r[0][0].area_two() < 0i64); + debug_assert!(r[0][1].area_two() > 0i64); } } diff --git a/iOverlay/tests/fill_rule_tests.rs b/iOverlay/tests/fill_rule_tests.rs index 0350f24..8489c42 100644 --- a/iOverlay/tests/fill_rule_tests.rs +++ b/iOverlay/tests/fill_rule_tests.rs @@ -8,7 +8,7 @@ mod tests { #[test] fn test_both_clock_wise() { - fn overlay() -> Overlay { + fn overlay() -> Overlay { let mut overlay = Overlay::new(2); overlay.add_contour(&square(10, true), ShapeType::Subject); @@ -50,7 +50,7 @@ mod tests { #[test] fn test_both_counter_clock_wise() { - fn overlay() -> Overlay { + fn overlay() -> Overlay { let mut overlay = Overlay::new(2); overlay.add_contour(&square(10, false), ShapeType::Subject); @@ -92,7 +92,7 @@ mod tests { #[test] fn test_cw_and_ccw() { - fn overlay() -> Overlay { + fn overlay() -> Overlay { let mut overlay = Overlay::new(2); overlay.add_contour(&square(10, true), ShapeType::Subject); @@ -134,7 +134,7 @@ mod tests { #[test] fn test_ccw_and_cw() { - fn overlay() -> Overlay { + fn overlay() -> Overlay { let mut overlay = Overlay::new(2); overlay.add_contour(&square(10, false), ShapeType::Subject); diff --git a/iOverlay/tests/overlay_tests.rs b/iOverlay/tests/overlay_tests.rs index 5cab35a..1a84fa4 100644 --- a/iOverlay/tests/overlay_tests.rs +++ b/iOverlay/tests/overlay_tests.rs @@ -27,7 +27,7 @@ mod tests { ogc: false, }; - fn overlay(test: &BooleanTest, options: IntOverlayOptions, solver: Solver) -> Overlay { + fn overlay(test: &BooleanTest, options: IntOverlayOptions, solver: Solver) -> Overlay { Overlay::with_contours_custom(&test.subj_paths, &test.clip_paths, options, solver) } From 144e8423fd4500a6de7ed449dee5910f69a9d083 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Wed, 20 May 2026 20:18:49 +0300 Subject: [PATCH 07/36] draft int api --- iOverlay/src/bind/solver.rs | 6 +- iOverlay/src/build/boolean.rs | 2 +- iOverlay/src/build/graph.rs | 2 +- iOverlay/src/build/string.rs | 2 +- iOverlay/src/build/util.rs | 2 +- iOverlay/src/core/divide.rs | 18 ++-- iOverlay/src/core/edge_overlay.rs | 37 +++++--- iOverlay/src/core/extract.rs | 2 +- iOverlay/src/core/extract_ogc.rs | 2 +- iOverlay/src/core/overlay.rs | 2 +- iOverlay/src/core/predicate.rs | 107 ++++++++++++---------- iOverlay/src/core/relate.rs | 60 +++++++++--- iOverlay/src/core/simplify.rs | 8 +- iOverlay/src/float/overlay.rs | 8 +- iOverlay/src/float/relate.rs | 2 +- iOverlay/src/float/string_overlay.rs | 2 +- iOverlay/src/mesh/miter.rs | 13 +-- iOverlay/src/mesh/outline/builder.rs | 82 +++++++++++------ iOverlay/src/mesh/outline/builder_join.rs | 56 +++++------ iOverlay/src/mesh/outline/offset.rs | 43 +++++---- iOverlay/src/mesh/outline/section.rs | 19 ++-- iOverlay/src/mesh/outline/uniq_iter.rs | 39 ++++---- iOverlay/src/mesh/overlay.rs | 10 +- iOverlay/src/mesh/stroke/builder.rs | 76 ++++++++++----- iOverlay/src/mesh/stroke/builder_cap.rs | 13 +-- iOverlay/src/mesh/stroke/builder_join.rs | 43 ++++----- iOverlay/src/mesh/stroke/offset.rs | 39 +++++--- iOverlay/src/mesh/stroke/section.rs | 9 +- iOverlay/src/mesh/subject.rs | 5 +- iOverlay/src/segm/sort.rs | 2 +- iOverlay/src/split/line_mark.rs | 2 +- iOverlay/src/split/solver.rs | 2 +- iOverlay/src/split/solver_fragment.rs | 2 +- iOverlay/src/split/solver_list.rs | 2 +- iOverlay/src/split/solver_tree.rs | 2 +- iOverlay/src/string/clip.rs | 105 +++++++++++---------- iOverlay/src/string/extract.rs | 42 +++++++-- iOverlay/src/string/filter.rs | 5 +- iOverlay/src/string/line.rs | 2 +- iOverlay/src/string/overlay.rs | 56 ++++++----- iOverlay/src/string/slice.rs | 66 +++++++++---- iOverlay/src/string/split.rs | 47 +++++----- iOverlay/src/vector/extract.rs | 6 +- iOverlay/tests/edge_overlay_tests.rs | 57 +++++++++++- iOverlay/tests/slice_tests.rs | 2 +- 45 files changed, 684 insertions(+), 425 deletions(-) diff --git a/iOverlay/src/bind/solver.rs b/iOverlay/src/bind/solver.rs index 105480f..90e793c 100644 --- a/iOverlay/src/bind/solver.rs +++ b/iOverlay/src/bind/solver.rs @@ -115,13 +115,13 @@ impl ShapeBinder { } } -pub(crate) trait JoinHoles { +pub(crate) trait JoinHoles { fn join_unsorted_holes(&mut self, holes: Vec>, clockwise: bool); fn join_sorted_holes(&mut self, holes: Vec>, anchors: Vec>, clockwise: bool); fn scan_join(&mut self, holes: Vec>, hole_segments: Vec>, clockwise: bool); } -impl JoinHoles for Vec> { +impl JoinHoles for Vec> { #[inline] fn join_unsorted_holes(&mut self, holes: Vec>, clockwise: bool) { if self.is_empty() || holes.is_empty() { @@ -243,7 +243,7 @@ pub(crate) trait SortByAngle { fn add_sort_by_angle(&mut self); } -impl SortByAngle for [IdSegment] { +impl SortByAngle for [IdSegment] { #[inline] fn sort_by_a_then_by_angle(&mut self) { self.sort_by_two_keys_then_by( diff --git a/iOverlay/src/build/boolean.rs b/iOverlay/src/build/boolean.rs index 1208db5..6d28a86 100644 --- a/iOverlay/src/build/boolean.rs +++ b/iOverlay/src/build/boolean.rs @@ -26,7 +26,7 @@ use i_tree::Expiration; impl GraphBuilder where - I: IntNumber + Expiration + SortKey + Send + Sync, + I: IntNumber + Expiration + SortKey, D: OverlayEdgeData, { #[inline] diff --git a/iOverlay/src/build/graph.rs b/iOverlay/src/build/graph.rs index 40acd99..4974913 100644 --- a/iOverlay/src/build/graph.rs +++ b/iOverlay/src/build/graph.rs @@ -11,7 +11,7 @@ use i_tree::Expiration; impl GraphBuilder where - I: IntNumber + Expiration + SortKey + Send + Sync, + I: IntNumber + Expiration + SortKey, C: WindingCount, N: GraphNode, D: OverlayEdgeData, diff --git a/iOverlay/src/build/string.rs b/iOverlay/src/build/string.rs index 52ca55f..ea03957 100644 --- a/iOverlay/src/build/string.rs +++ b/iOverlay/src/build/string.rs @@ -13,7 +13,7 @@ use i_tree::Expiration; impl GraphBuilder, I> where - I: IntNumber + Expiration + SortKey + Send + Sync, + I: IntNumber + Expiration + SortKey, { #[inline] pub(crate) fn build_string_all( diff --git a/iOverlay/src/build/util.rs b/iOverlay/src/build/util.rs index e0f4d10..d7d9384 100644 --- a/iOverlay/src/build/util.rs +++ b/iOverlay/src/build/util.rs @@ -10,7 +10,7 @@ impl GraphBuilder where C: WindingCount, N: GraphNode, - I: IntNumber + i_tree::Expiration + SortKey + Send + Sync, + I: IntNumber + i_tree::Expiration + SortKey, { pub(crate) fn test_contour_for_loops( &mut self, diff --git a/iOverlay/src/core/divide.rs b/iOverlay/src/core/divide.rs index bcacb80..1b61680 100644 --- a/iOverlay/src/core/divide.rs +++ b/iOverlay/src/core/divide.rs @@ -31,12 +31,12 @@ impl SubPath { } } -pub trait ContourDecomposition { - fn decompose_contours(&self) -> Option>>; +pub trait ContourDecomposition { + fn decompose_contours(&self) -> Option>>; } -impl ContourDecomposition for IntContour { - fn decompose_contours(&self) -> Option>> { +impl ContourDecomposition for IntContour { + fn decompose_contours(&self) -> Option>> { if self.len() < 3 { return None; } @@ -78,10 +78,10 @@ impl ContourDecomposition for IntContour { let mut i = 0; while i < anchors.len() { let a = anchors[i]; - let mut sub_path: SubPath = if let Some(sub_path) = queue.pop() { + let mut sub_path: SubPath = if let Some(sub_path) = queue.pop() { sub_path } else { - queue.push(SubPath::::start(a)); + queue.push(SubPath::::start(a)); i += 1; continue; }; @@ -92,17 +92,17 @@ impl ContourDecomposition for IntContour { if let Some(prev) = queue.last_mut() { prev.shift(a); } else { - queue.push(SubPath::::start(a)); + queue.push(SubPath::::start(a)); } } else { sub_path.join(a, self); queue.push(sub_path); - queue.push(SubPath::::start(a)); + queue.push(SubPath::::start(a)); } i += 1; } - let mut sub_path: SubPath = queue.pop().unwrap(); + let mut sub_path: SubPath = queue.pop().unwrap(); if sub_path.last < self.len() { sub_path.path.extend_from_slice(&self[sub_path.last..]); diff --git a/iOverlay/src/core/edge_overlay.rs b/iOverlay/src/core/edge_overlay.rs index 2b174cd..9eb0165 100644 --- a/iOverlay/src/core/edge_overlay.rs +++ b/iOverlay/src/core/edge_overlay.rs @@ -12,25 +12,32 @@ use crate::segm::winding::WindingCount; use crate::split::solver::SplitSolver; 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 { - pub a: IntPoint, - pub b: IntPoint, +pub struct InputEdge { + pub a: IntPoint, + pub b: IntPoint, pub data: D, } -pub struct EdgeOverlay { +pub struct EdgeOverlay { pub solver: Solver, pub options: IntOverlayOptions, - pub boolean_buffer: Option>, - segments: Vec>, - split_solver: SplitSolver, - graph_builder: GraphBuilder, + pub boolean_buffer: Option>, + segments: Vec>, + split_solver: SplitSolver, + graph_builder: GraphBuilder, } -impl EdgeOverlay { +impl EdgeOverlay +where + I: IntNumber + Expiration + LayoutNumber + SortKey, + D: OverlayEdgeData, +{ pub fn new(capacity: usize) -> Self { Self { solver: Default::default(), @@ -38,11 +45,11 @@ impl EdgeOverlay { boolean_buffer: None, segments: Vec::with_capacity(capacity), split_solver: SplitSolver::new(), - graph_builder: GraphBuilder::::new(), + graph_builder: GraphBuilder::::new(), } } - pub fn add_edge(&mut self, edge: InputEdge, shape_type: ShapeType) { + pub fn add_edge(&mut self, edge: InputEdge, shape_type: ShapeType) { if edge.a == edge.b { return; } @@ -53,9 +60,9 @@ impl EdgeOverlay { )); } - pub fn add_edges(&mut self, edges: I, shape_type: ShapeType) + pub fn add_edges(&mut self, edges: It, shape_type: ShapeType) where - I: IntoIterator>, + It: IntoIterator>, { for edge in edges { self.add_edge(edge, shape_type); @@ -66,7 +73,7 @@ impl EdgeOverlay { &mut self, overlay_rule: OverlayRule, fill_rule: FillRule, - ) -> Vec> { + ) -> Vec> { self.split_solver.split_segments(&mut self.segments, &self.solver); if self.segments.is_empty() { return Vec::new(); @@ -87,7 +94,7 @@ impl EdgeOverlay { &mut self, overlay_rule: OverlayRule, fill_rule: FillRule, - ) -> Vec> { + ) -> Vec> { self.split_solver.split_segments(&mut self.segments, &self.solver); if self.segments.is_empty() { return Vec::new(); diff --git a/iOverlay/src/core/extract.rs b/iOverlay/src/core/extract.rs index 90e45d4..fb37faf 100644 --- a/iOverlay/src/core/extract.rs +++ b/iOverlay/src/core/extract.rs @@ -50,7 +50,7 @@ impl Default for BooleanExtractionBuffer { impl OverlayGraph<'_, I> where - I: IntNumber + Expiration + SortKey + Send + Sync, + I: IntNumber + Expiration + SortKey, { /// 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 4d0d108..8ff7824 100644 --- a/iOverlay/src/core/extract_ogc.rs +++ b/iOverlay/src/core/extract_ogc.rs @@ -18,7 +18,7 @@ use i_tree::Expiration; impl OverlayGraph<'_, I> where - I: IntNumber + Expiration + SortKey + Send + Sync, + I: IntNumber + Expiration + SortKey, { pub(crate) fn extract_ogc( &self, diff --git a/iOverlay/src/core/overlay.rs b/iOverlay/src/core/overlay.rs index 7258165..56ad627 100644 --- a/iOverlay/src/core/overlay.rs +++ b/iOverlay/src/core/overlay.rs @@ -75,7 +75,7 @@ pub struct Overlay { impl Overlay where - I: IntNumber + Expiration + LayoutNumber + SortKey + Send + Sync, + I: IntNumber + Expiration + LayoutNumber + SortKey, { /// 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/predicate.rs b/iOverlay/src/core/predicate.rs index 4c91bec..5eee3a5 100644 --- a/iOverlay/src/core/predicate.rs +++ b/iOverlay/src/core/predicate.rs @@ -6,19 +6,21 @@ use crate::segm::segment::{ }; use alloc::vec::Vec; use core::ops::ControlFlow; +use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; +use i_key_sort::sort::key::SortKey; use i_key_sort::sort::two_keys::TwoKeysSort; /// Collects segment endpoints and checks for coincidence between subject and clip. /// /// Uses optimized algorithm: collect into separate Vecs, sort with `sort_by_two_keys`, /// dedup, then binary search from shorter into longer array. -pub(crate) struct PointCoincidenceChecker { - subj_points: Vec, - clip_points: Vec, +pub(crate) struct PointCoincidenceChecker { + subj_points: Vec>, + clip_points: Vec>, } -impl PointCoincidenceChecker { +impl PointCoincidenceChecker { /// Create a new checker with pre-allocated capacity. /// /// `capacity` is the number of segments; each segment contributes 2 endpoints. @@ -37,7 +39,7 @@ impl PointCoincidenceChecker { /// clip in the segment are skipped for clip collection /// - Similarly for clip-only interior segments #[inline] - pub(crate) fn add_segment(&mut self, segment: &Segment, fill: SegmentFill) { + pub(crate) fn add_segment(&mut self, segment: &Segment, fill: SegmentFill) { // Skip inner segments optimization: // If segment is entirely inside one shape's interior (filled on both sides) // and has no contribution from the other shape, it's not on a boundary @@ -104,11 +106,11 @@ impl PointCoincidenceChecker { /// loop as soon as an intersection is detected, avoiding processing of remaining segments. /// /// Also collects endpoint information for point coincidence check in finalize. -pub(crate) struct IntersectsHandler { - point_checker: PointCoincidenceChecker, +pub(crate) struct IntersectsHandler { + point_checker: PointCoincidenceChecker, } -impl IntersectsHandler { +impl IntersectsHandler { pub(crate) fn new(capacity: usize) -> Self { Self { point_checker: PointCoincidenceChecker::new(capacity), @@ -116,14 +118,14 @@ impl IntersectsHandler { } } -impl FillHandler for IntersectsHandler { +impl FillHandler for IntersectsHandler { type Output = bool; #[inline(always)] fn handle( &mut self, _index: usize, - segment: &Segment, + segment: &Segment, fill: SegmentFill, ) -> ControlFlow { // Shapes intersect if both contribute to any segment (interior overlap or boundary contact) @@ -152,14 +154,14 @@ impl FillHandler for IntersectsHandler { /// Early-exits `true` on first interior overlap. pub(crate) struct InteriorsIntersectHandler; -impl FillHandler for InteriorsIntersectHandler { +impl FillHandler for InteriorsIntersectHandler { type Output = bool; #[inline(always)] fn handle( &mut self, _index: usize, - _segment: &Segment, + _segment: &Segment, fill: SegmentFill, ) -> ControlFlow { // Interiors intersect if both shapes fill the same side @@ -183,12 +185,12 @@ impl FillHandler for InteriorsIntersectHandler { /// the shapes don't just touch. /// /// Also collects endpoint information for point coincidence check in finalize. -pub(crate) struct TouchesHandler { +pub(crate) struct TouchesHandler { has_boundary_contact: bool, - point_checker: PointCoincidenceChecker, + point_checker: PointCoincidenceChecker, } -impl TouchesHandler { +impl TouchesHandler { pub(crate) fn new(capacity: usize) -> Self { Self { has_boundary_contact: false, @@ -197,14 +199,14 @@ impl TouchesHandler { } } -impl FillHandler for TouchesHandler { +impl FillHandler for TouchesHandler { type Output = bool; #[inline(always)] fn handle( &mut self, _index: usize, - segment: &Segment, + segment: &Segment, fill: SegmentFill, ) -> ControlFlow { // Interior overlap = not a touch (early exit false) @@ -231,11 +233,11 @@ impl FillHandler for TouchesHandler { /// - Returns `false` if there's interior overlap (early exit) /// - Returns `false` if there's edge/boundary contact (shared segments, early exit) /// - Returns `true` ONLY if shapes touch by point coincidence without any edge overlap -pub(crate) struct PointIntersectsHandler { - point_checker: PointCoincidenceChecker, +pub(crate) struct PointIntersectsHandler { + point_checker: PointCoincidenceChecker, } -impl PointIntersectsHandler { +impl PointIntersectsHandler { pub(crate) fn new(capacity: usize) -> Self { Self { point_checker: PointCoincidenceChecker::new(capacity), @@ -243,14 +245,14 @@ impl PointIntersectsHandler { } } -impl FillHandler for PointIntersectsHandler { +impl FillHandler for PointIntersectsHandler { type Output = bool; #[inline(always)] fn handle( &mut self, _index: usize, - segment: &Segment, + segment: &Segment, fill: SegmentFill, ) -> ControlFlow { // Interior overlap = not a point-only intersection (early exit false) @@ -285,14 +287,14 @@ impl WithinHandler { } } -impl FillHandler for WithinHandler { +impl FillHandler for WithinHandler { type Output = bool; #[inline(always)] fn handle( &mut self, _index: usize, - _segment: &Segment, + _segment: &Segment, fill: SegmentFill, ) -> ControlFlow { let subj_top = (fill & SUBJ_TOP) != 0; @@ -342,22 +344,29 @@ mod tests { } } + fn finalize_i32(handler: H) -> H::Output + where + H: crate::build::sweep::FillHandler, + { + handler.finalize() + } + #[test] fn test_point_coincidence_no_points() { - let checker = PointCoincidenceChecker::new(10); + let checker = PointCoincidenceChecker::::new(10); assert!(!checker.has_coincidence()); } #[test] fn test_point_coincidence_subj_only() { - let mut checker = PointCoincidenceChecker::new(10); + let mut checker = PointCoincidenceChecker::::new(10); checker.add_segment(&make_segment(0, 0, 10, 0, 1, 0), SUBJ_TOP); assert!(!checker.has_coincidence()); } #[test] fn test_point_coincidence_coincident_point() { - let mut checker = PointCoincidenceChecker::new(10); + let mut checker = PointCoincidenceChecker::::new(10); // Subject segment with endpoint at (10, 10) checker.add_segment(&make_segment(0, 0, 10, 10, 1, 0), SUBJ_TOP); // Clip segment with endpoint at (10, 10) @@ -367,7 +376,7 @@ mod tests { #[test] fn test_point_coincidence_no_coincidence() { - let mut checker = PointCoincidenceChecker::new(10); + let mut checker = PointCoincidenceChecker::::new(10); checker.add_segment(&make_segment(0, 0, 5, 5, 1, 0), SUBJ_TOP); checker.add_segment(&make_segment(10, 10, 20, 20, 0, 1), CLIP_TOP); assert!(!checker.has_coincidence()); @@ -375,7 +384,7 @@ mod tests { #[test] fn test_point_coincidence_shared_segment_is_line_not_point() { - let mut checker = PointCoincidenceChecker::new(10); + let mut checker = PointCoincidenceChecker::::new(10); // Segment with both SUBJ and CLIP fill is a shared edge (line intersection), // not a point coincidence. Only one array gets populated, so no coincidence. checker.add_segment(&make_segment(0, 0, 10, 10, 1, 1), SUBJ_TOP | CLIP_BOTTOM); @@ -384,7 +393,7 @@ mod tests { #[test] fn test_point_coincidence_dedup_works() { - let mut checker = PointCoincidenceChecker::new(10); + let mut checker = PointCoincidenceChecker::::new(10); // Two subject segments sharing endpoint (5, 5) checker.add_segment(&make_segment(0, 0, 5, 5, 1, 0), SUBJ_TOP); checker.add_segment(&make_segment(5, 5, 10, 10, 1, 0), SUBJ_TOP); @@ -396,7 +405,7 @@ mod tests { #[test] fn test_intersects_handler_both_top() { let seg = make_segment(0, 0, 10, 0, 1, 1); - let mut handler = IntersectsHandler::new(10); + let mut handler = IntersectsHandler::::new(10); let fill = SUBJ_TOP | CLIP_TOP; let result = handler.handle(0, &seg, fill); assert!(matches!(result, ControlFlow::Break(true))); @@ -405,7 +414,7 @@ mod tests { #[test] fn test_intersects_handler_both_bottom() { let seg = make_segment(0, 0, 10, 0, 1, 1); - let mut handler = IntersectsHandler::new(10); + let mut handler = IntersectsHandler::::new(10); let fill = SUBJ_BOTTOM | CLIP_BOTTOM; let result = handler.handle(0, &seg, fill); assert!(matches!(result, ControlFlow::Break(true))); @@ -415,7 +424,7 @@ mod tests { fn test_intersects_handler_boundary_contact() { // Boundary contact (edge sharing) is still an intersection per DE-9IM let seg = make_segment(0, 0, 10, 0, 1, 1); - let mut handler = IntersectsHandler::new(10); + let mut handler = IntersectsHandler::::new(10); let fill = SUBJ_TOP | CLIP_BOTTOM; let result = handler.handle(0, &seg, fill); assert!(matches!(result, ControlFlow::Break(true))); @@ -425,7 +434,7 @@ mod tests { fn test_intersects_handler_no_intersection() { // Only subject contributes - no intersection let seg = make_segment(0, 0, 10, 0, 1, 0); - let mut handler = IntersectsHandler::new(10); + let mut handler = IntersectsHandler::::new(10); let fill = SUBJ_TOP; let result = handler.handle(0, &seg, fill); assert!(matches!(result, ControlFlow::Continue(()))); @@ -439,13 +448,13 @@ mod tests { #[test] fn test_intersects_handler_finalize_with_coincidence() { - let mut handler = IntersectsHandler::new(10); + let mut handler = IntersectsHandler::::new(10); // Add segments that don't trigger early exit but have point coincidence let seg1 = make_segment(0, 0, 10, 10, 1, 0); let seg2 = make_segment(10, 10, 20, 20, 0, 1); let _ = handler.handle(0, &seg1, SUBJ_TOP); let _ = handler.handle(1, &seg2, CLIP_TOP); - assert!(handler.finalize()); + assert!(finalize_i32(handler)); } #[test] @@ -474,13 +483,13 @@ mod tests { let fill = SUBJ_TOP | CLIP_BOTTOM; let result = handler.handle(0, &seg, fill); assert!(matches!(result, ControlFlow::Continue(()))); - assert!(!handler.finalize()); + assert!(!finalize_i32(handler)); } #[test] fn test_touches_handler_boundary_only() { let seg = make_segment(0, 0, 10, 0, 1, 1); - let mut handler = TouchesHandler::new(10); + let mut handler = TouchesHandler::::new(10); let fill = SUBJ_TOP | CLIP_BOTTOM; let result = handler.handle(0, &seg, fill); assert!(matches!(result, ControlFlow::Continue(()))); @@ -490,7 +499,7 @@ mod tests { #[test] fn test_touches_handler_interior_overlap() { let seg = make_segment(0, 0, 10, 0, 1, 1); - let mut handler = TouchesHandler::new(10); + let mut handler = TouchesHandler::::new(10); let fill = SUBJ_TOP | CLIP_TOP; let result = handler.handle(0, &seg, fill); assert!(matches!(result, ControlFlow::Break(false))); // early exit on interior overlap @@ -499,7 +508,7 @@ mod tests { #[test] fn test_touches_handler_no_contact() { let seg = make_segment(0, 0, 10, 0, 1, 0); - let mut handler = TouchesHandler::new(10); + let mut handler = TouchesHandler::::new(10); let fill = SUBJ_TOP; let result = handler.handle(0, &seg, fill); assert!(matches!(result, ControlFlow::Continue(()))); @@ -508,13 +517,13 @@ mod tests { #[test] fn test_touches_handler_point_coincidence() { - let mut handler = TouchesHandler::new(10); + let mut handler = TouchesHandler::::new(10); // Add segments that don't touch via fill but have point coincidence let seg1 = make_segment(0, 0, 10, 10, 1, 0); let seg2 = make_segment(10, 10, 20, 20, 0, 1); let _ = handler.handle(0, &seg1, SUBJ_TOP); let _ = handler.handle(1, &seg2, CLIP_TOP); - assert!(handler.finalize()); + assert!(finalize_i32(handler)); } #[test] @@ -525,7 +534,7 @@ mod tests { let fill = SUBJ_TOP | CLIP_TOP; let result = handler.handle(0, &seg, fill); assert!(matches!(result, ControlFlow::Continue(()))); - assert!(handler.finalize()); + assert!(finalize_i32(handler)); } #[test] @@ -542,7 +551,7 @@ mod tests { fn test_within_handler_empty_subject() { let handler = WithinHandler::new(); // Empty subject is not within anything - assert!(!handler.finalize()); + assert!(!finalize_i32(handler)); } #[test] @@ -553,12 +562,12 @@ mod tests { let fill = CLIP_TOP; let result = handler.handle(0, &seg, fill); assert!(matches!(result, ControlFlow::Continue(()))); - assert!(!handler.finalize()); + assert!(!finalize_i32(handler)); } #[test] fn test_point_intersects_handler_point_only() { - let mut handler = PointIntersectsHandler::new(10); + let mut handler = PointIntersectsHandler::::new(10); // Subject segment ending at (10, 10) let seg1 = make_segment(0, 0, 10, 10, 1, 0); // Clip segment starting at (10, 10) @@ -573,7 +582,7 @@ mod tests { fn test_point_intersects_handler_edge_contact() { // Segment belongs to both subject and clip (shared edge) let seg = make_segment(0, 0, 10, 0, 1, 1); - let mut handler = PointIntersectsHandler::new(10); + let mut handler = PointIntersectsHandler::::new(10); // Both shapes have fill on opposite sides (boundary contact) let fill = SUBJ_TOP | CLIP_BOTTOM; let result = handler.handle(0, &seg, fill); @@ -584,7 +593,7 @@ mod tests { #[test] fn test_point_intersects_handler_interior_overlap() { let seg = make_segment(0, 0, 10, 0, 1, 1); - let mut handler = PointIntersectsHandler::new(10); + let mut handler = PointIntersectsHandler::::new(10); // Interior overlap (both shapes fill the same side) let fill = SUBJ_TOP | CLIP_TOP; let result = handler.handle(0, &seg, fill); @@ -596,7 +605,7 @@ mod tests { fn test_point_intersects_handler_no_contact() { let seg1 = make_segment(0, 0, 5, 5, 1, 0); let seg2 = make_segment(10, 10, 20, 20, 0, 1); - let mut handler = PointIntersectsHandler::new(10); + let mut handler = PointIntersectsHandler::::new(10); let _ = handler.handle(0, &seg1, SUBJ_TOP); let _ = handler.handle(1, &seg2, CLIP_TOP); // No contact at all → false diff --git a/iOverlay/src/core/relate.rs b/iOverlay/src/core/relate.rs index 6e78bd3..2e3733d 100644 --- a/iOverlay/src/core/relate.rs +++ b/iOverlay/src/core/relate.rs @@ -10,8 +10,11 @@ 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. /// @@ -26,24 +29,27 @@ use i_shape::int::shape::{IntContour, IntShape}; /// use i_overlay::core::relate::PredicateOverlay; /// use i_overlay::core::overlay::ShapeType; /// -/// let mut overlay = PredicateOverlay::new(16); +/// let mut overlay = PredicateOverlay::::new(16); /// // Add subject and clip segments... /// let intersects = overlay.intersects(); /// ``` /// /// 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. pub fill_rule: FillRule, - pub(crate) segments: Vec>, - pub(crate) split_solver: SplitSolver, - sweep_runner: SweepRunner, + pub(crate) segments: Vec>, + pub(crate) split_solver: SplitSolver, + sweep_runner: SweepRunner, } -impl PredicateOverlay { +impl PredicateOverlay +where + I: IntNumber + Expiration + LayoutNumber + SortKey, +{ #[inline] pub fn new(capacity: usize) -> Self { Self { @@ -55,7 +61,7 @@ impl PredicateOverlay { } } - fn evaluate>(&mut self, handler: H) -> T { + fn evaluate>(&mut self, handler: H) -> T { if self.segments.is_empty() { return T::default(); } @@ -73,7 +79,7 @@ impl PredicateOverlay { #[inline] pub fn intersects(&mut self) -> bool { let capacity = self.segments.len(); - self.evaluate(IntersectsHandler::new(capacity)) + self.evaluate(IntersectsHandler::::new(capacity)) } /// Returns `true` if the interiors of subject and clip shapes overlap. @@ -92,7 +98,7 @@ impl PredicateOverlay { #[inline] pub fn touches(&mut self) -> bool { let capacity = self.segments.len(); - self.evaluate(TouchesHandler::new(capacity)) + self.evaluate(TouchesHandler::::new(capacity)) } /// Returns `true` if subject and clip shapes intersect by point coincidence only. @@ -102,7 +108,7 @@ impl PredicateOverlay { #[inline] pub fn point_intersects(&mut self) -> bool { let capacity = self.segments.len(); - self.evaluate(PointIntersectsHandler::new(capacity)) + self.evaluate(PointIntersectsHandler::::new(capacity)) } /// Returns `true` if subject is completely within clip. @@ -120,7 +126,7 @@ impl PredicateOverlay { /// - `iter`: An iterator over references to `IntPoint` that defines the path. /// - `shape_type`: Specifies the role of the added path in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_path_iter>(&mut self, iter: I, shape_type: ShapeType) { + pub fn add_path_iter>>(&mut self, iter: It, shape_type: ShapeType) { self.segments.append_path_iter(iter, shape_type, false); } @@ -128,7 +134,7 @@ impl PredicateOverlay { /// - `contour`: An array of points that form a closed path. /// - `shape_type`: Specifies the role of the added path in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_contour(&mut self, contour: &[IntPoint], shape_type: ShapeType) { + pub fn add_contour(&mut self, contour: &[IntPoint], shape_type: ShapeType) { self.segments .append_path_iter(contour.iter().copied(), shape_type, false); } @@ -137,7 +143,7 @@ impl PredicateOverlay { /// - `contours`: An array of `IntContour` instances to be added to the overlay. /// - `shape_type`: Specifies the role of the added paths in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_contours(&mut self, contours: &[IntContour], shape_type: ShapeType) { + pub fn add_contours(&mut self, contours: &[IntContour], shape_type: ShapeType) { for contour in contours.iter() { self.add_contour(contour, shape_type); } @@ -147,7 +153,7 @@ impl PredicateOverlay { /// - `shape`: A reference to a `IntShape` instance to be added. /// - `shape_type`: Specifies the role of the added shape in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_shape(&mut self, shape: &IntShape, shape_type: ShapeType) { + pub fn add_shape(&mut self, shape: &IntShape, shape_type: ShapeType) { self.add_contours(shape, shape_type); } @@ -155,7 +161,7 @@ impl PredicateOverlay { /// - `shapes`: An array of `IntShape` instances to be added to the overlay. /// - `shape_type`: Specifies the role of the added shapes in the overlay operation, either as `Subject` or `Clip`. #[inline] - pub fn add_shapes(&mut self, shapes: &[IntShape], shape_type: ShapeType) { + pub fn add_shapes(&mut self, shapes: &[IntShape], shape_type: ShapeType) { for shape in shapes.iter() { self.add_contours(shape, shape_type); } @@ -189,6 +195,30 @@ mod tests { assert!(overlay.intersects()); } + #[test] + fn test_add_contour_intersects_i64() { + let mut overlay = PredicateOverlay::::new(16); + overlay.add_contour( + &[ + IntPoint::new(0, 0), + IntPoint::new(0, 10), + IntPoint::new(10, 10), + IntPoint::new(10, 0), + ], + ShapeType::Subject, + ); + overlay.add_contour( + &[ + IntPoint::new(5, 5), + IntPoint::new(5, 15), + IntPoint::new(15, 15), + IntPoint::new(15, 5), + ], + ShapeType::Clip, + ); + assert!(overlay.intersects()); + } + #[test] fn test_add_contour_disjoint() { let mut overlay = PredicateOverlay::new(16); diff --git a/iOverlay/src/core/simplify.rs b/iOverlay/src/core/simplify.rs index 4b92303..0f2935a 100644 --- a/iOverlay/src/core/simplify.rs +++ b/iOverlay/src/core/simplify.rs @@ -38,7 +38,7 @@ pub trait Simplify { impl Simplify for [IntPoint] where - I: IntNumber + Expiration + LayoutNumber + SortKey + Send + Sync, + I: IntNumber + Expiration + LayoutNumber + SortKey, { #[inline] fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { @@ -51,7 +51,7 @@ where impl Simplify for [IntContour] where - I: IntNumber + Expiration + LayoutNumber + SortKey + Send + Sync, + I: IntNumber + Expiration + LayoutNumber + SortKey, { #[inline] fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { @@ -64,7 +64,7 @@ where impl Simplify for [IntShape] where - I: IntNumber + Expiration + LayoutNumber + SortKey + Send + Sync, + I: IntNumber + Expiration + LayoutNumber + SortKey, { #[inline] fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { @@ -80,7 +80,7 @@ enum ContourFillDirection { impl Overlay where - I: IntNumber + Expiration + LayoutNumber + SortKey + Send + Sync, + I: IntNumber + Expiration + LayoutNumber + SortKey, { /// Fast-path simplification for a single contour. /// diff --git a/iOverlay/src/float/overlay.rs b/iOverlay/src/float/overlay.rs index cffaef2..a39de7b 100644 --- a/iOverlay/src/float/overlay.rs +++ b/iOverlay/src/float/overlay.rs @@ -12,6 +12,8 @@ use i_float::adapter::FloatPointAdapter; use i_float::float::compatible::FloatPointCompatible; use i_float::float::number::FloatNumber; use i_float::float::rect::FloatRect; +use i_float::int::number::int::IntNumber; +use i_float::int::number::wide_int::WideIntNumber; use i_shape::base::data::Shapes; use i_shape::flat::buffer::FlatContoursBuffer; use i_shape::flat::float::FloatFlatContoursBuffer; @@ -384,15 +386,15 @@ impl Default for OverlayOptions { } impl OverlayOptions { - pub(crate) fn int_with_adapter>( + pub(crate) fn int_with_adapter, I: IntNumber>( &self, - adapter: &FloatPointAdapter, + adapter: &FloatPointAdapter, ) -> IntOverlayOptions { IntOverlayOptions { preserve_input_collinear: self.preserve_input_collinear, output_direction: self.output_direction, preserve_output_collinear: self.preserve_output_collinear, - min_output_area: adapter.round_sqr_len_to_int(self.min_output_area) as u64, + min_output_area: adapter.round_sqr_len_to_int(self.min_output_area).to_usize() as u64, ogc: self.ogc, } } diff --git a/iOverlay/src/float/relate.rs b/iOverlay/src/float/relate.rs index de6fca6..804031c 100644 --- a/iOverlay/src/float/relate.rs +++ b/iOverlay/src/float/relate.rs @@ -27,7 +27,7 @@ use i_shape::source::resource::ShapeResource; /// For a more ergonomic API, see the [`FloatRelate`] trait which provides /// methods directly on shape types. pub struct FloatPredicateOverlay { - pub(crate) overlay: PredicateOverlay, + pub(crate) overlay: PredicateOverlay, pub(crate) adapter: FloatPointAdapter, } diff --git a/iOverlay/src/float/string_overlay.rs b/iOverlay/src/float/string_overlay.rs index 2f94b7b..85be8c2 100644 --- a/iOverlay/src/float/string_overlay.rs +++ b/iOverlay/src/float/string_overlay.rs @@ -17,7 +17,7 @@ use i_shape::source::resource::ShapeResource; /// 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(super) overlay: StringOverlay, + pub(super) overlay: StringOverlay, pub(super) adapter: FloatPointAdapter, } diff --git a/iOverlay/src/mesh/miter.rs b/iOverlay/src/mesh/miter.rs index 38d1c95..55a2c51 100644 --- a/iOverlay/src/mesh/miter.rs +++ b/iOverlay/src/mesh/miter.rs @@ -1,25 +1,26 @@ use i_float::adapter::FloatPointAdapter; use i_float::float::compatible::FloatPointCompatible; use i_float::float::number::FloatNumber; +use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; pub(super) struct Miter; -pub(super) enum SharpMiter { +pub(super) enum SharpMiter { Degenerate, - AB(IntPoint, IntPoint), - AcB(IntPoint, IntPoint, IntPoint), + AB(IntPoint, IntPoint), + AcB(IntPoint, IntPoint, IntPoint), } impl Miter { #[inline] - pub(super) fn sharp( + pub(super) fn sharp( pa: P, pb: P, va: P, vb: P, - adapter: &FloatPointAdapter, - ) -> SharpMiter { + adapter: &FloatPointAdapter, + ) -> SharpMiter { let ia = adapter.float_to_int(&pa); let ib = adapter.float_to_int(&pb); diff --git a/iOverlay/src/mesh/outline/builder.rs b/iOverlay/src/mesh/outline/builder.rs index b56b89b..6f9f189 100644 --- a/iOverlay/src/mesh/outline/builder.rs +++ b/iOverlay/src/mesh/outline/builder.rs @@ -13,34 +13,36 @@ use i_float::adapter::FloatPointAdapter; use i_float::float::compatible::FloatPointCompatible; use i_float::float::number::FloatNumber; use i_float::float::vector::FloatPointMath; +use i_float::int::number::int::IntNumber; +use i_float::int::number::wide_int::WideIntNumber; -trait OutlineBuild { +trait OutlineBuild { fn build( &self, path: &[P], - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ); fn capacity(&self, points_count: usize) -> usize; fn additional_offset(&self, radius: P::Scalar) -> P::Scalar; } -pub(super) struct OutlineBuilder { - builder: Box>, +pub(super) struct OutlineBuilder { + builder: Box>, } -struct Builder, P: FloatPointCompatible> { +struct Builder, P: FloatPointCompatible, I: IntNumber> { extend: bool, radius: P::Scalar, join_builder: J, - _phantom: PhantomData

, + _phantom: PhantomData<(P, I)>, } -impl OutlineBuilder

{ - pub(super) fn new(radius: P::Scalar, join: &LineJoin) -> OutlineBuilder

{ +impl OutlineBuilder { + pub(super) fn new(radius: P::Scalar, join: &LineJoin) -> OutlineBuilder { let extend = radius > P::Scalar::from_float(0.0); - let builder: Box> = { + let builder: Box> = { match join { LineJoin::Miter(ratio) => Box::new(Builder { extend, @@ -70,8 +72,8 @@ impl OutlineBuilder

{ pub(super) fn build( &self, path: &[P], - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { self.builder.build(path, adapter, segments); } @@ -87,13 +89,13 @@ impl OutlineBuilder

{ } } -impl, P: FloatPointCompatible> OutlineBuild

for Builder { +impl, P: FloatPointCompatible, I: IntNumber> OutlineBuild for Builder { #[inline] fn build( &self, path: &[P], - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { if path.len() < 2 { return; @@ -113,12 +115,12 @@ impl, P: FloatPointCompatible> OutlineBuild

for Builder, P: FloatPointCompatible> Builder { +impl, P: FloatPointCompatible, I: IntNumber> Builder { fn build( &self, path: &[P], - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { let iter = path.iter().map(|p| adapter.float_to_int(p)); let mut uniq_segments = if let Some(iter) = UniqueSegmentsIter::new(iter) { @@ -150,17 +152,20 @@ impl, P: FloatPointCompatible> Builder { #[inline] fn feed_join( &self, - s0: &OffsetSection

, - s1: &OffsetSection

, - adapter: &FloatPointAdapter, - segments: &mut Vec>, + s0: &OffsetSection, + s1: &OffsetSection, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { let vi = s1.b - s1.a; let vp = s0.b - s0.a; let cross = vi.cross_product(vp); - debug_assert!(cross != 0, "not possible! UniqueSegmentsIter guarantee it"); - let outer_corner = (cross > 0) == self.extend; + debug_assert!( + cross != I::Wide::ZERO, + "not possible! UniqueSegmentsIter guarantee it" + ); + let outer_corner = (cross > I::Wide::ZERO) == self.extend; if outer_corner { if s0.b_top != s1.a_top { self.join_builder.add_join(s0, s1, adapter, segments); @@ -173,9 +178,9 @@ impl, P: FloatPointCompatible> Builder { } } -impl OffsetSection

{ +impl OffsetSection { #[inline] - fn new(radius: P::Scalar, s: &UniqueSegment, adapter: &FloatPointAdapter) -> Self { + fn new(radius: P::Scalar, s: &UniqueSegment, adapter: &FloatPointAdapter) -> Self { let a = adapter.int_to_float(&s.a); let b = adapter.int_to_float(&s.b); let ab = FloatPointMath::sub(&b, &a); @@ -209,3 +214,28 @@ impl VecPushSome for Vec { } } } + +#[cfg(test)] +mod tests { + use crate::mesh::outline::builder::OutlineBuilder; + use crate::mesh::style::LineJoin; + use crate::segm::boolean::ShapeCountBoolean; + use crate::segm::segment::Segment; + use alloc::vec::Vec; + use i_float::adapter::FloatPointAdapter; + use i_float::float::rect::FloatRect; + + #[test] + fn test_i64_builder() { + let path = [[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0]]; + let mut rect = FloatRect::with_iter(path.iter()).unwrap(); + rect.add_offset(2.0); + let adapter = FloatPointAdapter::<[f64; 2], i64>::new(rect); + let builder = OutlineBuilder::<[f64; 2], i64>::new(1.0, &LineJoin::Bevel); + + let mut segments = Vec::>::new(); + builder.build(&path, &adapter, &mut segments); + + assert!(!segments.is_empty()); + } +} diff --git a/iOverlay/src/mesh/outline/builder_join.rs b/iOverlay/src/mesh/outline/builder_join.rs index d085168..835ca76 100644 --- a/iOverlay/src/mesh/outline/builder_join.rs +++ b/iOverlay/src/mesh/outline/builder_join.rs @@ -9,14 +9,16 @@ use i_float::adapter::FloatPointAdapter; use i_float::float::compatible::FloatPointCompatible; use i_float::float::number::FloatNumber; use i_float::float::vector::FloatPointMath; +use i_float::int::number::int::IntNumber; +use i_float::int::number::wide_int::WideIntNumber; -pub(super) trait JoinBuilder { +pub(super) trait JoinBuilder { fn add_join( &self, - s0: &OffsetSection

, - s1: &OffsetSection

, - adapter: &FloatPointAdapter, - segments: &mut Vec>, + s0: &OffsetSection, + s1: &OffsetSection, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ); fn capacity(&self) -> usize; fn additional_offset(&self, radius: P::Scalar) -> P::Scalar; @@ -26,25 +28,25 @@ pub(super) struct BevelJoinBuilder; impl BevelJoinBuilder { #[inline] - fn join( - s0: &OffsetSection

, - s1: &OffsetSection

, - _adapter: &FloatPointAdapter, - segments: &mut Vec>, + fn join( + s0: &OffsetSection, + s1: &OffsetSection, + _adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { - debug_assert_ne!(s0.b_top, s1.a_top, "must be validated before"); + debug_assert!(s0.b_top != s1.a_top, "must be validated before"); segments.push(Segment::subject(s0.b_top, s1.a_top)); } } -impl JoinBuilder

for BevelJoinBuilder { +impl JoinBuilder for BevelJoinBuilder { #[inline] fn add_join( &self, - s0: &OffsetSection

, - s1: &OffsetSection

, - adapter: &FloatPointAdapter, - segments: &mut Vec>, + s0: &OffsetSection, + s1: &OffsetSection, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { Self::join(s0, s1, adapter, segments); } @@ -91,19 +93,19 @@ impl MiterJoinBuilder { } } -impl JoinBuilder

for MiterJoinBuilder { +impl JoinBuilder for MiterJoinBuilder { fn add_join( &self, - s0: &OffsetSection

, - s1: &OffsetSection

, - adapter: &FloatPointAdapter, - segments: &mut Vec>, + s0: &OffsetSection, + s1: &OffsetSection, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { let ia = s0.b_top; let ib = s1.a_top; let sq_len = ia.sqr_distance(ib); - if sq_len < 4 { + if sq_len < I::Wide::from_usize(4) { BevelJoinBuilder::join(s0, s1, adapter, segments); return; } @@ -191,13 +193,13 @@ impl RoundJoinBuilder { } } } -impl JoinBuilder

for RoundJoinBuilder { +impl JoinBuilder for RoundJoinBuilder { fn add_join( &self, - s0: &OffsetSection

, - s1: &OffsetSection

, - adapter: &FloatPointAdapter, - segments: &mut Vec>, + s0: &OffsetSection, + s1: &OffsetSection, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { let dot_product = FloatPointMath::dot_product(&s0.dir, &s1.dir); if self.limit_dot_product < dot_product { diff --git a/iOverlay/src/mesh/outline/offset.rs b/iOverlay/src/mesh/outline/offset.rs index 346cb09..4813a8d 100644 --- a/iOverlay/src/mesh/outline/offset.rs +++ b/iOverlay/src/mesh/outline/offset.rs @@ -13,6 +13,10 @@ use i_float::adapter::FloatPointAdapter; use i_float::float::compatible::FloatPointCompatible; use i_float::float::number::FloatNumber; 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; @@ -21,6 +25,7 @@ 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}; pub trait OutlineOffset { /// Generates an outline shapes for contours, or shapes. @@ -139,7 +144,7 @@ where style: &OutlineStyle, options: OverlayOptions, ) -> Shapes

{ - match OutlineSolver::prepare(self, style) { + match OutlineSolver::::prepare(self, style) { Some(solver) => solver.build(self, options), None => vec![], } @@ -151,7 +156,7 @@ where options: OverlayOptions, output: &mut FloatFlatContoursBuffer

, ) { - match OutlineSolver::prepare(self, style) { + match OutlineSolver::::prepare(self, style) { Some(solver) => solver.build_into(self, options, output), None => output.clear_and_reserve(0, 0), } @@ -181,7 +186,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> { let s = FixedScaleOverlayError::validate_scale(scale)?; - let mut solver = match OutlineSolver::prepare(self, style) { + let mut solver = match OutlineSolver::::prepare(self, style) { Some(solver) => solver, None => return Ok(vec![]), }; @@ -197,7 +202,7 @@ where output: &mut FloatFlatContoursBuffer

, ) -> Result<(), FixedScaleOverlayError> { let s = FixedScaleOverlayError::validate_scale(scale)?; - let mut solver = match OutlineSolver::prepare(self, style) { + let mut solver = match OutlineSolver::::prepare(self, style) { Some(solver) => solver, None => { output.clear_and_reserve(0, 0); @@ -210,14 +215,18 @@ where } } -struct OutlineSolver { - outer_builder: OutlineBuilder

, - inner_builder: OutlineBuilder

, - adapter: FloatPointAdapter, +struct OutlineSolver { + outer_builder: OutlineBuilder, + inner_builder: OutlineBuilder, + adapter: FloatPointAdapter, points_count: usize, } -impl OutlineSolver

{ +impl OutlineSolver +where + P: FloatPointCompatible + 'static, + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, +{ fn prepare>(source: &S, style: &OutlineStyle) -> Option { let (points_count, paths_count) = { let mut points_count = 0; @@ -234,8 +243,8 @@ impl OutlineSolver

{ } let join = style.join.clone().normalize(); - let outer_builder: OutlineBuilder

= OutlineBuilder::new(-style.outer_offset, &join); - let inner_builder: OutlineBuilder

= OutlineBuilder::new(-style.inner_offset, &join); + let outer_builder: OutlineBuilder = OutlineBuilder::new(-style.outer_offset, &join); + let inner_builder: OutlineBuilder = OutlineBuilder::new(-style.inner_offset, &join); let outer_radius = style.outer_offset; let inner_radius = style.inner_offset; @@ -248,7 +257,7 @@ impl OutlineSolver

{ let mut rect = FloatRect::with_iter(source.iter_paths().flatten()).unwrap_or(FloatRect::zero()); rect.add_offset(additional_offset); - let adapter = FloatPointAdapter::new(rect); + let adapter = FloatPointAdapter::::new(rect); Some(Self { outer_builder, @@ -268,7 +277,7 @@ impl OutlineSolver

{ &self, source: &S, options: OverlayOptions, - ) -> Overlay { + ) -> Overlay { let total_capacity = self.outer_builder.capacity(self.points_count); let mut overlay = Overlay::new_custom( total_capacity, @@ -281,11 +290,11 @@ impl OutlineSolver

{ let mut segments = Vec::new(); let mut bool_buffer = BooleanExtractionBuffer::default(); - let mut flat_buffer = FlatContoursBuffer::::default(); + let mut flat_buffer = FlatContoursBuffer::::with_capacity(0); for path in source.iter_paths() { let area = path.unsafe_int_area(&self.adapter); - if area.abs() <= 1 { + if area.unsigned_abs() <= <::UInt as UIntNumber>::from_u64(1) { // ignore degenerate paths continue; } @@ -293,7 +302,7 @@ impl OutlineSolver

{ offset_overlay.clear(); segments.clear(); - let contour_fill_rule = if area < 0 { + let contour_fill_rule = if area < I::Wide::ZERO { offset_overlay.options.output_direction = ContourDirection::CounterClockwise; segments.reserve(self.outer_builder.capacity(path.len())); self.outer_builder.build(path, &self.adapter, &mut segments); @@ -347,7 +356,7 @@ impl OutlineSolver

{ let clean_result = options.clean_result; let mut overlay = self.build_overlay(source, options); - let mut int_output = FlatContoursBuffer::::default(); + let mut int_output = FlatContoursBuffer::::with_capacity(0); overlay.overlay_into(OverlayRule::Subject, FillRule::Positive, &mut int_output); let iter = int_output.points.iter().map(|p| self.adapter.int_to_float(p)); output.set_with_iter(iter, &int_output.ranges); diff --git a/iOverlay/src/mesh/outline/section.rs b/iOverlay/src/mesh/outline/section.rs index 9503a0a..2525748 100644 --- a/iOverlay/src/mesh/outline/section.rs +++ b/iOverlay/src/mesh/outline/section.rs @@ -1,20 +1,21 @@ use crate::segm::boolean::ShapeCountBoolean; use crate::segm::segment::Segment; use i_float::float::compatible::FloatPointCompatible; +use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; #[derive(Debug, Clone)] -pub(super) struct OffsetSection { - pub(super) a: IntPoint, - pub(super) b: IntPoint, - pub(super) a_top: IntPoint, - pub(super) b_top: IntPoint, +pub(super) struct OffsetSection { + pub(super) a: IntPoint, + pub(super) b: IntPoint, + pub(super) a_top: IntPoint, + pub(super) b_top: IntPoint, pub(super) dir: P, } -impl OffsetSection

{ +impl OffsetSection { #[inline] - pub(super) fn top_segment(&self) -> Option> { + pub(super) fn top_segment(&self) -> Option> { if self.a_top != self.b_top { Some(Segment::subject(self.a_top, self.b_top)) } else { @@ -23,7 +24,7 @@ impl OffsetSection

{ } #[inline] - pub(super) fn a_segment(&self) -> Option> { + pub(super) fn a_segment(&self) -> Option> { if self.a_top != self.a { Some(Segment::subject(self.a, self.a_top)) } else { @@ -32,7 +33,7 @@ impl OffsetSection

{ } #[inline] - pub(super) fn b_segment(&self) -> Option> { + pub(super) fn b_segment(&self) -> Option> { if self.b_top != self.b { Some(Segment::subject(self.b_top, self.b)) } else { diff --git a/iOverlay/src/mesh/outline/uniq_iter.rs b/iOverlay/src/mesh/outline/uniq_iter.rs index 6ca789b..c473320 100644 --- a/iOverlay/src/mesh/outline/uniq_iter.rs +++ b/iOverlay/src/mesh/outline/uniq_iter.rs @@ -3,26 +3,28 @@ use i_float::int::number::int::IntNumber; use i_float::int::number::wide_int::WideIntNumber; use i_float::int::point::IntPoint; -pub(super) struct UniqueSegment { - pub(super) a: IntPoint, - pub(super) b: IntPoint, +pub(super) struct UniqueSegment { + pub(super) a: IntPoint, + pub(super) b: IntPoint, } -pub(super) struct UniqueSegmentsIter +pub(super) struct UniqueSegmentsIter where - I: Iterator, + It: Iterator>, + I: IntNumber, { - iter: Chain>, - p0: IntPoint, - p1: IntPoint, + iter: Chain, 2>>, + p0: IntPoint, + p1: IntPoint, } -impl UniqueSegmentsIter +impl UniqueSegmentsIter where - I: Iterator, + It: Iterator>, + I: IntNumber, { #[inline] - pub(super) fn new(iter: I) -> Option { + pub(super) fn new(iter: It) -> Option { let mut iter = iter; let mut p0 = iter.next()?; @@ -51,11 +53,12 @@ where } } -impl Iterator for UniqueSegmentsIter +impl Iterator for UniqueSegmentsIter where - I: Iterator, + It: Iterator>, + I: IntNumber, { - type Item = UniqueSegment; + type Item = UniqueSegment; #[inline] fn next(&mut self) -> Option { for p2 in &mut self.iter { @@ -110,7 +113,7 @@ mod tests { #[test] fn test_empty() { - let uniq_iter = UniqueSegmentsIter::new(core::iter::empty::()); + let uniq_iter = UniqueSegmentsIter::new(core::iter::empty::>()); assert!(uniq_iter.is_none()); } @@ -171,7 +174,7 @@ mod tests { validate_case_all_rotations(&path, 4); } - fn validate_case_all_rotations(path: &[IntPoint], expected_segments_count: usize) { + fn validate_case_all_rotations(path: &[IntPoint], expected_segments_count: usize) { assert!(!path.is_empty(), "path must not be empty"); for shift in 0..path.len() { @@ -191,7 +194,7 @@ mod tests { } } - fn validate_segments(segments: &[UniqueSegment]) { + fn validate_segments(segments: &[UniqueSegment]) { assert!(!segments.is_empty(), "expected at least one segment"); for (i, s) in segments.iter().enumerate() { @@ -209,7 +212,7 @@ mod tests { validate_pair(&segments[last_i], &segments[0], last_i, 0); } - fn validate_pair(s0: &UniqueSegment, s1: &UniqueSegment, i: usize, j: usize) { + fn validate_pair(s0: &UniqueSegment, s1: &UniqueSegment, i: usize, j: usize) { assert_eq!(s0.b, s1.a, "segment {} end does not match segment {} start", i, j); let v0 = s0.a - s0.b; diff --git a/iOverlay/src/mesh/overlay.rs b/iOverlay/src/mesh/overlay.rs index a90e930..aa6d885 100644 --- a/iOverlay/src/mesh/overlay.rs +++ b/iOverlay/src/mesh/overlay.rs @@ -5,22 +5,24 @@ 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]) { + pub(crate) fn add_segments(&mut self, segments: &[Segment]) { self.segments.extend_from_slice(segments); } #[inline] - pub(crate) fn with_segments(segments: Vec>) -> Self { + pub(crate) fn with_segments(segments: Vec>) -> Self { Self { solver: Default::default(), options: Default::default(), boolean_buffer: None, segments, split_solver: SplitSolver::new(), - graph_builder: GraphBuilder::::new(), + graph_builder: GraphBuilder::::new(), } } } diff --git a/iOverlay/src/mesh/stroke/builder.rs b/iOverlay/src/mesh/stroke/builder.rs index c7f3ba7..d5ac0b7 100644 --- a/iOverlay/src/mesh/stroke/builder.rs +++ b/iOverlay/src/mesh/stroke/builder.rs @@ -6,59 +6,65 @@ use crate::segm::boolean::ShapeCountBoolean; use crate::segm::segment::Segment; use alloc::boxed::Box; use alloc::vec::Vec; +use core::marker::PhantomData; use i_float::adapter::FloatPointAdapter; use i_float::float::compatible::FloatPointCompatible; use i_float::float::number::FloatNumber; +use i_float::int::number::int::IntNumber; -trait StrokeBuild { +trait StrokeBuild { fn build( &self, path: &[P], is_closed_path: bool, - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ); fn capacity(&self, paths_count: usize, points_count: usize, is_closed_path: bool) -> usize; fn additional_offset(&self, radius: P::Scalar) -> P::Scalar; } -pub(super) struct StrokeBuilder { - builder: Box>, +pub(super) struct StrokeBuilder { + builder: Box>, } -struct Builder, P: FloatPointCompatible> { +struct Builder, P: FloatPointCompatible, I: IntNumber> { radius: P::Scalar, join_builder: J, start_cap_builder: CapBuilder

, end_cap_builder: CapBuilder

, + _phantom: PhantomData, } -impl StrokeBuilder

{ - pub(super) fn new(style: StrokeStyle

) -> StrokeBuilder

{ +impl StrokeBuilder { + pub(super) fn new(style: StrokeStyle

) -> StrokeBuilder { let radius = P::Scalar::from_float(0.5 * style.width.to_f64().max(0.0)); let start_cap_builder = CapBuilder::new(style.start_cap.normalize(), radius); let end_cap_builder = CapBuilder::new(style.end_cap.normalize(), radius); - let builder: Box> = match style.join.normalize() { + let builder: Box> = match style.join.normalize() { LineJoin::Miter(ratio) => Box::new(Builder { radius, join_builder: MiterJoinBuilder::new(ratio, radius), start_cap_builder, end_cap_builder, + _phantom: Default::default(), }), LineJoin::Round(ratio) => Box::new(Builder { radius, join_builder: RoundJoinBuilder::new(ratio, radius), start_cap_builder, end_cap_builder, + _phantom: Default::default(), }), LineJoin::Bevel => Box::new(Builder { radius, join_builder: BevelJoinBuilder {}, start_cap_builder, end_cap_builder, + _phantom: Default::default(), }), }; @@ -70,8 +76,8 @@ impl StrokeBuilder

{ &self, path: &[P], is_closed_path: bool, - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { self.builder.build(path, is_closed_path, adapter, segments); } @@ -87,14 +93,14 @@ impl StrokeBuilder

{ } } -impl, P: FloatPointCompatible> StrokeBuild

for Builder { +impl, P: FloatPointCompatible, I: IntNumber> StrokeBuild for Builder { #[inline] fn build( &self, path: &[P], is_closed_path: bool, - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { if is_closed_path { self.closed_segments(path, adapter, segments); @@ -122,12 +128,12 @@ impl, P: FloatPointCompatible> StrokeBuild

for Builder, P: FloatPointCompatible> Builder { +impl, P: FloatPointCompatible, I: IntNumber> Builder { fn open_segments( &self, path: &[P], - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { // build segments only from points which are not equal in int space @@ -179,8 +185,8 @@ impl, P: FloatPointCompatible> Builder { fn closed_segments( &self, path: &[P], - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { if path.len() < 2 { return; @@ -212,12 +218,7 @@ impl, P: FloatPointCompatible> Builder { } #[inline] - fn next_unique_point( - start: usize, - index: usize, - path: &[P], - adapter: &FloatPointAdapter, - ) -> usize { + fn next_unique_point(start: usize, index: usize, path: &[P], adapter: &FloatPointAdapter) -> usize { let a = adapter.float_to_int(&path[start]); for (j, p) in path.iter().enumerate().skip(index) { let b = adapter.float_to_int(p); @@ -229,3 +230,28 @@ impl, P: FloatPointCompatible> Builder { usize::MAX } } + +#[cfg(test)] +mod tests { + use crate::mesh::stroke::builder::StrokeBuilder; + use crate::mesh::style::StrokeStyle; + use crate::segm::boolean::ShapeCountBoolean; + use crate::segm::segment::Segment; + use alloc::vec::Vec; + use i_float::adapter::FloatPointAdapter; + use i_float::float::rect::FloatRect; + + #[test] + fn test_i64_builder() { + let path = [[0.0, 0.0], [10.0, 0.0], [10.0, 10.0]]; + let mut rect = FloatRect::with_iter(path.iter()).unwrap(); + rect.add_offset(2.0); + let adapter = FloatPointAdapter::<[f64; 2], i64>::new(rect); + let builder = StrokeBuilder::<[f64; 2], i64>::new(StrokeStyle::new(2.0)); + + let mut segments = Vec::>::new(); + builder.build(&path, false, &adapter, &mut segments); + + assert!(!segments.is_empty()); + } +} diff --git a/iOverlay/src/mesh/stroke/builder_cap.rs b/iOverlay/src/mesh/stroke/builder_cap.rs index e7be30b..2e0ce90 100644 --- a/iOverlay/src/mesh/stroke/builder_cap.rs +++ b/iOverlay/src/mesh/stroke/builder_cap.rs @@ -11,6 +11,7 @@ use i_float::float::compatible::FloatPointCompatible; use i_float::float::number::FloatNumber; use i_float::float::rect::FloatRect; use i_float::float::vector::FloatPointMath; +use i_float::int::number::int::IntNumber; #[derive(Debug, Clone)] pub(super) struct CapBuilder

{ @@ -66,11 +67,11 @@ impl CapBuilder

{ scaled } - pub(super) fn add_to_start( + pub(super) fn add_to_start( &self, section: &Section

, - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { let mut a = adapter.float_to_int(§ion.a_top); if let Some(points) = &self.points { @@ -88,11 +89,11 @@ impl CapBuilder

{ segments.push(Segment::subject(a, last)); } - pub(super) fn add_to_end( + pub(super) fn add_to_end( &self, section: &Section

, - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { let mut a = adapter.float_to_int(§ion.b_bot); if let Some(points) = &self.points { diff --git a/iOverlay/src/mesh/stroke/builder_join.rs b/iOverlay/src/mesh/stroke/builder_join.rs index 97f670b..8dcaadc 100644 --- a/iOverlay/src/mesh/stroke/builder_join.rs +++ b/iOverlay/src/mesh/stroke/builder_join.rs @@ -9,14 +9,15 @@ use i_float::adapter::FloatPointAdapter; use i_float::float::compatible::FloatPointCompatible; use i_float::float::number::FloatNumber; use i_float::float::vector::FloatPointMath; +use i_float::int::number::int::IntNumber; -pub(super) trait JoinBuilder { +pub(super) trait JoinBuilder { fn add_join( &self, s0: &Section

, s1: &Section

, - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ); fn capacity(&self) -> usize; fn additional_offset(&self, radius: P::Scalar) -> P::Scalar; @@ -26,31 +27,31 @@ pub(super) struct BevelJoinBuilder; impl BevelJoinBuilder { #[inline] - fn join_top( + fn join_top( s0: &Section

, s1: &Section

, - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { Self::add_segment(&s0.b_top, &s1.a_top, adapter, segments); } #[inline] - fn join_bot( + fn join_bot( s0: &Section

, s1: &Section

, - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { Self::add_segment(&s1.a_bot, &s0.b_bot, adapter, segments); } #[inline] - fn add_segment( + fn add_segment( a: &P, b: &P, - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { let ia = adapter.float_to_int(a); let ib = adapter.float_to_int(b); @@ -60,14 +61,14 @@ impl BevelJoinBuilder { } } -impl JoinBuilder

for BevelJoinBuilder { +impl JoinBuilder for BevelJoinBuilder { #[inline] fn add_join( &self, s0: &Section

, s1: &Section

, - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { Self::join_top(s0, s1, adapter, segments); Self::join_bot(s0, s1, adapter, segments); @@ -117,13 +118,13 @@ impl MiterJoinBuilder { } } -impl JoinBuilder

for MiterJoinBuilder { +impl JoinBuilder for MiterJoinBuilder { fn add_join( &self, s0: &Section

, s1: &Section

, - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { let cross_product = FloatPointMath::cross_product(&s0.dir, &s1.dir); if cross_product.abs() < P::Scalar::from_float(0.0001) { @@ -237,13 +238,13 @@ impl RoundJoinBuilder { } } } -impl JoinBuilder

for RoundJoinBuilder { +impl JoinBuilder for RoundJoinBuilder { fn add_join( &self, s0: &Section

, s1: &Section

, - adapter: &FloatPointAdapter, - segments: &mut Vec>, + adapter: &FloatPointAdapter, + segments: &mut Vec>, ) { let dot_product = FloatPointMath::dot_product(&s0.dir, &s1.dir); if self.limit_dot_product < dot_product { diff --git a/iOverlay/src/mesh/stroke/offset.rs b/iOverlay/src/mesh/stroke/offset.rs index 04173be..b94f5ab 100644 --- a/iOverlay/src/mesh/stroke/offset.rs +++ b/iOverlay/src/mesh/stroke/offset.rs @@ -12,12 +12,17 @@ use i_float::adapter::FloatPointAdapter; use i_float::float::compatible::FloatPointCompatible; use i_float::float::number::FloatNumber; 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}; pub trait StrokeOffset { /// Generates a stroke shapes for paths, contours, or shapes. @@ -184,7 +189,7 @@ where is_closed_path: bool, options: OverlayOptions, ) -> Shapes

{ - match StrokeSolver::prepare(self, style) { + match StrokeSolver::::prepare(self, style) { Some(solver) => solver.build(self, is_closed_path, options), None => vec![], } @@ -197,7 +202,7 @@ where options: OverlayOptions, output: &mut FloatFlatContoursBuffer

, ) { - match StrokeSolver::prepare(self, style) { + match StrokeSolver::::prepare(self, style) { Some(solver) => solver.build_into(self, is_closed_path, options, output), None => output.clear_and_reserve(0, 0), } @@ -210,7 +215,7 @@ where options: OverlayOptions, scale: P::Scalar, ) -> Result, FixedScaleOverlayError> { - let mut solver = match StrokeSolver::prepare(self, style) { + let mut solver = match StrokeSolver::::prepare(self, style) { Some(solver) => solver, None => return Ok(vec![]), }; @@ -226,7 +231,7 @@ where scale: P::Scalar, output: &mut FloatFlatContoursBuffer

, ) -> Result<(), FixedScaleOverlayError> { - let mut solver = match StrokeSolver::prepare(self, style) { + let mut solver = match StrokeSolver::::prepare(self, style) { Some(solver) => solver, None => { output.clear_and_reserve(0, 0); @@ -239,15 +244,19 @@ where } } -struct StrokeSolver { +struct StrokeSolver { r: P::Scalar, - builder: StrokeBuilder

, - adapter: FloatPointAdapter, + builder: StrokeBuilder, + adapter: FloatPointAdapter, paths_count: usize, points_count: usize, } -impl StrokeSolver

{ +impl StrokeSolver +where + P: 'static + FloatPointCompatible, + I: 'static + IntNumber + Expiration + LayoutNumber + SortKey, +{ fn prepare>(source: &S, style: StrokeStyle

) -> Option { let mut paths_count = 0; let mut points_count = 0; @@ -261,12 +270,12 @@ impl StrokeSolver

{ } let r = P::Scalar::from_float(0.5 * style.width.to_f64()); - let builder = StrokeBuilder::new(style); + let builder = StrokeBuilder::::new(style); let a = builder.additional_offset(r); let mut rect = FloatRect::with_iter(source.iter_paths().flatten()).unwrap_or(FloatRect::zero()); rect.add_offset(a); - let adapter = FloatPointAdapter::new(rect); + let adapter = FloatPointAdapter::::new(rect); Some(Self { r, @@ -288,8 +297,8 @@ impl StrokeSolver

{ is_closed_path: bool, options: OverlayOptions, ) -> Shapes

{ - let ir = self.adapter.round_len_to_int(self.r).abs(); - if ir <= 1 { + let ir = self.adapter.round_len_to_int(self.r).wide().unsigned_abs(); + if ir <= <::UInt as UIntNumber>::from_u64(1) { // offset is too small return vec![]; } @@ -329,8 +338,8 @@ impl StrokeSolver

{ options: OverlayOptions, output: &mut FloatFlatContoursBuffer

, ) { - let ir = self.adapter.round_len_to_int(self.r).abs(); - if ir <= 1 { + let ir = self.adapter.round_len_to_int(self.r).wide().unsigned_abs(); + if ir <= <::UInt as UIntNumber>::from_u64(1) { // offset is too small output.clear_and_reserve(0, 0); return; @@ -349,7 +358,7 @@ impl StrokeSolver

{ let mut overlay = Overlay::with_segments(segments); overlay.options = options.int_with_adapter(&self.adapter); - let mut int_output = FlatContoursBuffer::::default(); + let mut int_output = FlatContoursBuffer::::with_capacity(0); overlay.overlay_into(OverlayRule::Subject, FillRule::Positive, &mut int_output); let iter = int_output.points.iter().map(|p| self.adapter.int_to_float(p)); diff --git a/iOverlay/src/mesh/stroke/section.rs b/iOverlay/src/mesh/stroke/section.rs index b67ff54..2f7a448 100644 --- a/iOverlay/src/mesh/stroke/section.rs +++ b/iOverlay/src/mesh/stroke/section.rs @@ -5,6 +5,7 @@ use alloc::vec::Vec; use i_float::adapter::FloatPointAdapter; use i_float::float::compatible::FloatPointCompatible; use i_float::float::vector::FloatPointMath; +use i_float::int::number::int::IntNumber; #[derive(Debug, Clone)] pub(super) struct Section { @@ -40,12 +41,12 @@ impl Section

{ } } -pub(crate) trait SectionToSegment { - fn add_section(&mut self, section: &Section

, adapter: &FloatPointAdapter); +pub(crate) trait SectionToSegment { + fn add_section(&mut self, section: &Section

, adapter: &FloatPointAdapter); } -impl SectionToSegment

for Vec> { - fn add_section(&mut self, section: &Section

, adapter: &FloatPointAdapter) { +impl SectionToSegment for Vec> { + fn add_section(&mut self, section: &Section

, adapter: &FloatPointAdapter) { let a_top = adapter.float_to_int(§ion.a_top); let b_top = adapter.float_to_int(§ion.b_top); let a_bot = adapter.float_to_int(§ion.a_bot); diff --git a/iOverlay/src/mesh/subject.rs b/iOverlay/src/mesh/subject.rs index 3602641..3cde1bc 100644 --- a/iOverlay/src/mesh/subject.rs +++ b/iOverlay/src/mesh/subject.rs @@ -1,11 +1,12 @@ use crate::geom::x_segment::XSegment; use crate::segm::boolean::ShapeCountBoolean; use crate::segm::segment::Segment; +use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; -impl Segment { +impl Segment { #[inline] - pub(crate) fn subject(p0: IntPoint, p1: IntPoint) -> Self { + pub(crate) fn subject(p0: IntPoint, p1: IntPoint) -> Self { if p0 < p1 { Self { x_segment: XSegment { a: p0, b: p1 }, diff --git a/iOverlay/src/segm/sort.rs b/iOverlay/src/segm/sort.rs index f7b8d17..8bd4fde 100644 --- a/iOverlay/src/segm/sort.rs +++ b/iOverlay/src/segm/sort.rs @@ -7,7 +7,7 @@ pub(crate) trait ShapeSegmentsSort { fn sort_by_ab(&mut self, parallel: bool); } -impl ShapeSegmentsSort +impl ShapeSegmentsSort for [Segment] { #[inline] diff --git a/iOverlay/src/split/line_mark.rs b/iOverlay/src/split/line_mark.rs index c9b8d7d..15e806e 100644 --- a/iOverlay/src/split/line_mark.rs +++ b/iOverlay/src/split/line_mark.rs @@ -13,7 +13,7 @@ pub(super) trait SortMarkByIndexAndPoint { fn sort_by_index_and_point(&mut self, parallel: bool, reusable_buffer: &mut Vec>); } -impl SortMarkByIndexAndPoint for [LineMark] { +impl SortMarkByIndexAndPoint for [LineMark] { #[inline] fn sort_by_index_and_point(&mut self, parallel: bool, reusable_buffer: &mut Vec>) { self.sort_by_one_key_then_by_and_buffer( diff --git a/iOverlay/src/split/solver.rs b/iOverlay/src/split/solver.rs index 9a0cc7b..aa6f180 100644 --- a/iOverlay/src/split/solver.rs +++ b/iOverlay/src/split/solver.rs @@ -26,7 +26,7 @@ impl SplitSolver { impl SplitSolver where - I: IntNumber + Expiration + LayoutNumber + SortKey + Send + Sync, + I: IntNumber + Expiration + LayoutNumber + SortKey, { #[inline] pub(crate) fn split_segments>( diff --git a/iOverlay/src/split/solver_fragment.rs b/iOverlay/src/split/solver_fragment.rs index 85b1685..093edda 100644 --- a/iOverlay/src/split/solver_fragment.rs +++ b/iOverlay/src/split/solver_fragment.rs @@ -15,7 +15,7 @@ use i_tree::{Expiration, LayoutNumber}; impl SplitSolver where - I: IntNumber + Expiration + LayoutNumber + SortKey + Send + Sync, + I: IntNumber + Expiration + LayoutNumber + SortKey, { pub(super) fn fragment_split>( &mut self, diff --git a/iOverlay/src/split/solver_list.rs b/iOverlay/src/split/solver_list.rs index 4c001a6..65d20cc 100644 --- a/iOverlay/src/split/solver_list.rs +++ b/iOverlay/src/split/solver_list.rs @@ -11,7 +11,7 @@ use i_tree::{Expiration, LayoutNumber}; impl SplitSolver where - I: IntNumber + Expiration + LayoutNumber + SortKey + Send + Sync, + I: IntNumber + Expiration + LayoutNumber + SortKey, { pub(super) fn list_split>( &mut self, diff --git a/iOverlay/src/split/solver_tree.rs b/iOverlay/src/split/solver_tree.rs index b8c7554..696fbdd 100644 --- a/iOverlay/src/split/solver_tree.rs +++ b/iOverlay/src/split/solver_tree.rs @@ -29,7 +29,7 @@ impl ExpiredVal for IdSegment { impl SplitSolver where - I: IntNumber + Expiration + LayoutNumber + SortKey + Send + Sync, + I: IntNumber + Expiration + LayoutNumber + SortKey, { pub(super) fn tree_split>( &mut self, diff --git a/iOverlay/src/string/clip.rs b/iOverlay/src/string/clip.rs index db4ac9c..a75d8fa 100644 --- a/iOverlay/src/string/clip.rs +++ b/iOverlay/src/string/clip.rs @@ -7,9 +7,12 @@ use crate::string::graph::StringGraph; use crate::string::line::IntLine; 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 { @@ -20,9 +23,9 @@ pub struct ClipRule { pub boundary_included: bool, } -impl StringGraph<'_, i32> { +impl StringGraph<'_, I> { #[inline] - pub(super) fn into_clip_string_lines(self) -> Vec> { + pub(super) fn into_clip_string_lines(self) -> Vec> { let mut paths = Vec::new(); let links = self.links; @@ -61,10 +64,10 @@ impl StringGraph<'_, i32> { #[inline] fn find_next_point( nodes: &[Vec], - links: &mut [OverlayLink], - a: IdPoint, + links: &mut [OverlayLink], + a: IdPoint, is_out_node: bool, - ) -> Option> { + ) -> Option> { let node = unsafe { // SAFETY: a.id comes from an existing link endpoint, so it indexes nodes. nodes.get_unchecked(a.id) @@ -89,7 +92,7 @@ const CLIP_BACK: SegmentFill = STRING_BACK_CLIP << 2; const CLIP_FORWARD: SegmentFill = STRING_FORWARD_CLIP << 2; const CLIP_ALL: SegmentFill = CLIP_BACK | CLIP_FORWARD; -impl OverlayLink { +impl OverlayLink { #[inline] fn visit_if_possible(&mut self, is_forward: bool) -> bool { if is_forward { @@ -119,7 +122,7 @@ impl OverlayLink { } } -pub trait IntClip { +pub trait IntClip { /// Clips a single line according to the specified build and clip rules. /// - `line`: The line to be clipped, represented by two points. /// - `fill_rule`: Specifies the rule determining the filled areas, influencing the inclusion of line segments. @@ -127,7 +130,7 @@ pub trait IntClip { /// /// # Returns /// A vector of `IntPath` instances representing the clipped sections of the input line. - fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec>; + fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec>; /// Clips multiple lines according to the specified build and clip rules. /// - `lines`: A slice of `IntLine` instances representing lines to be clipped. @@ -136,7 +139,7 @@ pub trait IntClip { /// /// # Returns /// A vector of `IntPath` instances containing the clipped portions of the input lines. - fn clip_lines(&self, lines: &[IntLine], fill_rule: FillRule, clip_rule: ClipRule) -> Vec>; + fn clip_lines(&self, lines: &[IntLine], fill_rule: FillRule, clip_rule: ClipRule) -> Vec>; /// Clips a single path according to the specified build and clip rules. /// - `path`: A reference to an `IntPath`, which is a sequence of points representing the path to be clipped. @@ -145,7 +148,7 @@ pub trait IntClip { /// /// # Returns /// A vector of `IntPath` instances representing the clipped sections of the path. - fn clip_path(&self, path: &IntPath, fill_rule: FillRule, clip_rule: ClipRule) -> Vec>; + fn clip_path(&self, path: &IntPath, fill_rule: FillRule, clip_rule: ClipRule) -> Vec>; /// Clips multiple paths according to the specified build and clip rules. /// - `paths`: A slice of `IntPath` instances, each representing a path to be clipped. @@ -154,113 +157,102 @@ pub trait IntClip { /// /// # Returns /// A vector of `IntPath` instances containing the clipped portions of the input paths. - fn clip_paths( - &self, - paths: &[IntPath], - fill_rule: FillRule, - clip_rule: ClipRule, - ) -> Vec>; + fn clip_paths(&self, paths: &[IntPath], fill_rule: FillRule, clip_rule: ClipRule) -> Vec>; } -impl IntClip for IntShapes { +impl IntClip for IntShapes +where + I: IntNumber + Expiration + LayoutNumber + SortKey, +{ #[inline] - fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { + fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shapes(self); overlay.add_string_line(line); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_lines(&self, lines: &[IntLine], fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { + fn clip_lines(&self, lines: &[IntLine], fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shapes(self); overlay.add_string_lines(lines); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_path(&self, path: &IntPath, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { + fn clip_path(&self, path: &IntPath, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shapes(self); overlay.add_string_path(path); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_paths( - &self, - paths: &[IntPath], - fill_rule: FillRule, - clip_rule: ClipRule, - ) -> Vec> { + fn clip_paths(&self, paths: &[IntPath], fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shapes(self); overlay.add_string_paths(paths); overlay.clip_string_lines(fill_rule, clip_rule) } } -impl IntClip for IntShape { +impl IntClip for IntShape +where + I: IntNumber + Expiration + LayoutNumber + SortKey, +{ #[inline] - fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { + fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shape(self); overlay.add_string_line(line); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_lines(&self, lines: &[IntLine], fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { + fn clip_lines(&self, lines: &[IntLine], fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shape(self); overlay.add_string_lines(lines); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_path(&self, path: &IntPath, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { + fn clip_path(&self, path: &IntPath, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shape(self); overlay.add_string_path(path); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_paths( - &self, - paths: &[IntPath], - fill_rule: FillRule, - clip_rule: ClipRule, - ) -> Vec> { + fn clip_paths(&self, paths: &[IntPath], fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shape(self); overlay.add_string_paths(paths); overlay.clip_string_lines(fill_rule, clip_rule) } } -impl IntClip for [IntPoint] { +impl IntClip for [IntPoint] +where + I: IntNumber + Expiration + LayoutNumber + SortKey, +{ #[inline] - fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { + fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shape_contour(self); overlay.add_string_line(line); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_lines(&self, lines: &[IntLine], fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { + fn clip_lines(&self, lines: &[IntLine], fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shape_contour(self); overlay.add_string_lines(lines); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_path(&self, path: &IntPath, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { + fn clip_path(&self, path: &IntPath, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shape_contour(self); overlay.add_string_path(path); overlay.clip_string_lines(fill_rule, clip_rule) } #[inline] - fn clip_paths( - &self, - paths: &[IntPath], - fill_rule: FillRule, - clip_rule: ClipRule, - ) -> Vec> { + fn clip_paths(&self, paths: &[IntPath], fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { let mut overlay = StringOverlay::with_shape_contour(self); overlay.add_string_paths(paths); overlay.clip_string_lines(fill_rule, clip_rule) @@ -333,6 +325,27 @@ mod tests { assert_eq!(result_1.len(), 2); } + #[test] + fn test_i64_clip() { + let path = vec![ + IntPoint::::new(-10, -10), + IntPoint::new(-10, 10), + IntPoint::new(10, 10), + IntPoint::new(10, -10), + ]; + + let result = path.clip_line( + [IntPoint::new(0, -15), IntPoint::new(0, 15)], + FillRule::NonZero, + ClipRule { + invert: false, + boundary_included: false, + }, + ); + + assert_eq!(result.len(), 1); + } + #[test] fn test_boundary() { let path = vec![ diff --git a/iOverlay/src/string/extract.rs b/iOverlay/src/string/extract.rs index 02ec5f8..1d67772 100644 --- a/iOverlay/src/string/extract.rs +++ b/iOverlay/src/string/extract.rs @@ -7,10 +7,13 @@ 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<'_, i32> { +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 @@ -21,7 +24,7 @@ impl StringGraph<'_, i32> { /// /// Note: Outer boundary paths have a counterclockwise order, and holes have a clockwise order. #[inline(always)] - pub fn extract_shapes(&self, string_rule: StringRule) -> IntShapes { + pub fn extract_shapes(&self, string_rule: StringRule) -> IntShapes { self.extract_shapes_custom(string_rule, Default::default()) } @@ -37,11 +40,7 @@ impl StringGraph<'_, i32> { /// - Each path `Vec` is a sequence of points, forming a closed path. /// /// Note: Outer boundary paths have a **main_direction** order, and holes have an opposite to **main_direction** order. - pub fn extract_shapes_custom( - &self, - string_rule: StringRule, - options: IntOverlayOptions, - ) -> IntShapes { + pub fn extract_shapes_custom(&self, string_rule: StringRule, options: IntOverlayOptions) -> IntShapes { let clockwise = options.output_direction == ContourDirection::Clockwise; let mut fills = self.filter(string_rule); let mut shapes = Vec::new(); @@ -89,7 +88,7 @@ impl StringGraph<'_, i32> { } #[inline] - fn get_paths(&self, start_index: usize, clockwise: bool, fills: &mut [u8]) -> IntPath { + fn get_paths(&self, start_index: usize, clockwise: bool, fills: &mut [u8]) -> IntPath { let start_link = unsafe { // SAFETY: start_index originates from iterating the fills array, which mirrors links. self.links.get_unchecked(start_index) @@ -99,7 +98,7 @@ impl StringGraph<'_, i32> { let mut node_id = start_link.b.id; let last_node_id = start_link.a.id; - let mut path = IntPath::::new(); + let mut path = IntPath::::new(); path.push(start_link.a.point); fills[start_index] = start_link.visit_fill(fills[start_index], start_link.a.id, clockwise); @@ -262,4 +261,29 @@ mod tests { assert_eq!(r.len(), 2); } + + #[test] + fn test_i64_overlay() { + let paths = vec![vec![ + IntPoint::::new(-10, 10), + IntPoint::new(-10, -10), + IntPoint::new(10, -10), + IntPoint::new(10, 10), + ]]; + + let window = vec![ + IntPoint::new(-5, -5), + IntPoint::new(-5, 5), + IntPoint::new(5, 5), + IntPoint::new(5, -5), + ]; + + let mut overlay = StringOverlay::::with_shape(&paths); + overlay.add_string_contour(&window); + let graph = overlay.build_graph_view(FillRule::NonZero).unwrap(); + + let r = graph.extract_shapes_custom(StringRule::Slice, Default::default()); + + assert_eq!(r.len(), 2); + } } diff --git a/iOverlay/src/string/filter.rs b/iOverlay/src/string/filter.rs index 3262276..fe460dd 100644 --- a/iOverlay/src/string/filter.rs +++ b/iOverlay/src/string/filter.rs @@ -3,8 +3,9 @@ use crate::segm::segment::{SUBJ_BOTH, SUBJ_BOTTOM, SUBJ_TOP}; use crate::string::graph::StringGraph; use crate::string::rule::StringRule; use alloc::vec::Vec; +use i_float::int::number::int::IntNumber; -impl OverlayLink { +impl OverlayLink { #[inline] pub(super) fn visit_fill(&self, fill: u8, node_id: usize, clockwise: bool) -> u8 { let is_a = self.a.id == node_id; @@ -42,7 +43,7 @@ impl OverlayLink { } } -impl StringGraph<'_, i32> { +impl StringGraph<'_, I> { #[inline(always)] pub(super) fn filter(&self, ext_rule: StringRule) -> Vec { match ext_rule { diff --git a/iOverlay/src/string/line.rs b/iOverlay/src/string/line.rs index eca01cc..0dca5dc 100644 --- a/iOverlay/src/string/line.rs +++ b/iOverlay/src/string/line.rs @@ -1,3 +1,3 @@ use i_float::int::point::IntPoint; -pub type IntLine = [IntPoint; 2]; +pub type IntLine = [IntPoint; 2]; diff --git a/iOverlay/src/string/overlay.rs b/iOverlay/src/string/overlay.rs index 07c2037..658fd86 100644 --- a/iOverlay/src/string/overlay.rs +++ b/iOverlay/src/string/overlay.rs @@ -14,19 +14,25 @@ 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, - pub(crate) graph_builder: GraphBuilder, i32>, + pub(super) segments: Vec>, + pub(crate) split_solver: SplitSolver, + pub(crate) graph_builder: GraphBuilder, I>, } -impl StringOverlay { +impl StringOverlay +where + I: IntNumber + Expiration + LayoutNumber + SortKey, +{ /// 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. /// - `capacity`: The initial capacity for storing edge data. Ideally, this should be set to the sum of the edges of all shapes to be added to the overlay, ensuring efficient data management. @@ -36,7 +42,7 @@ impl StringOverlay { options: Default::default(), segments: Vec::with_capacity(capacity), split_solver: SplitSolver::new(), - graph_builder: GraphBuilder::, i32>::new(), + graph_builder: GraphBuilder::, I>::new(), } } @@ -49,14 +55,14 @@ impl StringOverlay { options, segments: Vec::with_capacity(capacity), split_solver: SplitSolver::new(), - graph_builder: GraphBuilder::, i32>::new(), + graph_builder: GraphBuilder::, I>::new(), } } /// Creates a new `StringOverlay` instance and initializes it with a single shape contour. /// - `contour`: An array of points that form a closed path. #[inline] - pub fn with_shape_contour(contour: &[IntPoint]) -> Self { + pub fn with_shape_contour(contour: &[IntPoint]) -> Self { let mut overlay = Self::new(contour.len()); overlay.add_shape_contour(contour); overlay @@ -65,7 +71,7 @@ impl StringOverlay { /// Creates a new `StringOverlay` instance and initializes it with multiple shape contours. /// - `contours`: An array of `IntContour` instances to be added to the overlay. #[inline] - pub fn with_shape_contours(contours: &[IntContour]) -> Self { + pub fn with_shape_contours(contours: &[IntContour]) -> Self { let mut overlay = Self::new(contours.points_count()); overlay.add_shape_contours(contours); overlay @@ -74,7 +80,7 @@ impl StringOverlay { /// Creates a new `StringOverlay` instance and initializes it with s shape. /// - `shape`: An `IntShape` instances to be added to the overlay. #[inline] - pub fn with_shape(shape: &[IntContour]) -> Self { + pub fn with_shape(shape: &[IntContour]) -> Self { let mut overlay = Self::new(shape.points_count()); overlay.add_shape_contours(shape); overlay @@ -83,7 +89,7 @@ impl StringOverlay { /// Creates a new `StringOverlay` instance and initializes it with subject and clip shapes. /// - `shapes`: An array of `IntShape` instances to be added to the overlay. #[inline] - pub fn with_shapes(shapes: &[IntShape]) -> Self { + pub fn with_shapes(shapes: &[IntShape]) -> Self { let mut overlay = Self::new(shapes.points_count()); overlay.add_shapes(shapes); overlay @@ -94,20 +100,20 @@ impl StringOverlay { /// when paths are not directly stored in a collection. /// - `iter`: An iterator over references to `IntPoint` that defines the path. #[inline] - pub fn add_shape_contour_iter>(&mut self, iter: I) { + pub fn add_shape_contour_iter>>(&mut self, iter: It) { self.segments.append_path_iter(iter, ShapeType::Subject, false); } /// Adds a single path to the overlay as a shape paths. /// - `contour`: An array of points that form a closed path. #[inline] - pub fn add_shape_contour(&mut self, contour: &[IntPoint]) { + pub fn add_shape_contour(&mut self, contour: &[IntPoint]) { self.add_shape_contour_iter(contour.iter().copied()); } /// Adds multiple paths to the overlay as shape paths. /// - `contours`: An array of `IntContour` instances to be added to the overlay. - pub fn add_shape_contours(&mut self, contours: &[IntContour]) { + pub fn add_shape_contours(&mut self, contours: &[IntContour]) { for contour in contours.iter() { self.add_shape_contour(contour); } @@ -116,7 +122,7 @@ impl StringOverlay { /// Adds a list of shape to the overlay. /// - `shapes`: An array of `IntShape` instances to be added to the overlay. #[inline] - pub fn add_shapes(&mut self, shapes: &[IntShape]) { + pub fn add_shapes(&mut self, shapes: &[IntShape]) { for shape in shapes { self.add_shape_contours(shape); } @@ -125,7 +131,7 @@ impl StringOverlay { /// Adds a single line (open path) to the overlay. /// - `line`: An `IntLine` representing the open line (defined by two points). #[inline] - pub fn add_string_line(&mut self, line: IntLine) { + pub fn add_string_line(&mut self, line: IntLine) { let a = line[0]; let b = line[1]; let segment = match a.cmp(&b) { @@ -154,7 +160,7 @@ impl StringOverlay { /// Adds multiple lines (open paths) to the overlay. /// - `lines`: An array of `IntLine` instances to be added. #[inline] - pub fn add_string_lines(&mut self, lines: &[IntLine]) { + pub fn add_string_lines(&mut self, lines: &[IntLine]) { for &line in lines { self.add_string_line(line); } @@ -163,7 +169,7 @@ impl StringOverlay { /// Adds a string path to the overlay. /// - `path`: A path representing a string line. #[inline] - pub fn add_string_path(&mut self, path: &[IntPoint]) { + pub fn add_string_path(&mut self, path: &[IntPoint]) { if path.len() < 2 { return; } @@ -181,7 +187,7 @@ impl StringOverlay { /// Adds a string line contour to the overlay. /// - `contour`: A contour representing a string line closed path. This path is interpreted as closed, so it doesn’t require the start and endpoint to be the same for processing. #[inline] - pub fn add_string_contour(&mut self, contour: &[IntPoint]) { + pub fn add_string_contour(&mut self, contour: &[IntPoint]) { if contour.len() < 2 { return; } @@ -199,7 +205,7 @@ impl StringOverlay { /// Adds a string line paths to the overlay. /// - `paths`: A collection of paths, each representing a string line. #[inline] - pub fn add_string_paths(&mut self, paths: &[IntPath]) { + pub fn add_string_paths(&mut self, paths: &[IntPath]) { for path in paths { self.add_string_path(path); } @@ -208,7 +214,7 @@ impl StringOverlay { /// Adds a string line contours to the overlay. /// - `contours`: A collection of contours, each representing a string line closed path. #[inline] - pub fn add_string_contours(&mut self, contours: &[IntContour]) { + pub fn add_string_contours(&mut self, contours: &[IntContour]) { for contour in contours { self.add_string_contour(contour); } @@ -220,7 +226,7 @@ impl StringOverlay { /// # Returns /// A vector of `IntPath` instances representing the clipped sections of the input lines. #[inline] - pub fn clip_string_lines(self, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { + pub fn clip_string_lines(self, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { self.clip_string_lines_with_solver(fill_rule, clip_rule, Default::default()) } @@ -237,7 +243,7 @@ impl StringOverlay { fill_rule: FillRule, clip_rule: ClipRule, solver: Solver, - ) -> Vec> { + ) -> Vec> { self.split_solver.split_segments(&mut self.segments, &solver); if self.segments.is_empty() { return Vec::new(); @@ -251,7 +257,7 @@ impl StringOverlay { /// This graph is used for string operations, enabling analysis and manipulation of geometric data. /// - `fill_rule`: The rule that defines how to build shapes (e.g., non-zero, even-odd). #[inline] - pub fn build_graph_view(&mut self, fill_rule: FillRule) -> Option> { + pub fn build_graph_view(&mut self, fill_rule: FillRule) -> Option> { self.build_graph_view_with_solver(fill_rule, Default::default()) } @@ -264,7 +270,7 @@ impl StringOverlay { &mut self, fill_rule: FillRule, solver: Solver, - ) -> Option> { + ) -> Option> { self.split_solver.split_segments(&mut self.segments, &solver); if self.segments.is_empty() { return None; diff --git a/iOverlay/src/string/slice.rs b/iOverlay/src/string/slice.rs index 7ac25ca..9c82f03 100644 --- a/iOverlay/src/string/slice.rs +++ b/iOverlay/src/string/slice.rs @@ -2,20 +2,26 @@ use crate::core::fill_rule::FillRule; 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; - fn slice_by_lines(&self, lines: &[IntLine], fill_rule: FillRule) -> IntShapes; - fn slice_by_path(&self, path: &IntPath, fill_rule: FillRule) -> IntShapes; - fn slice_by_paths(&self, paths: &[IntPath], fill_rule: FillRule) -> IntShapes; +pub trait IntSlice { + fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes; + fn slice_by_lines(&self, lines: &[IntLine], fill_rule: FillRule) -> IntShapes; + fn slice_by_path(&self, path: &IntPath, fill_rule: FillRule) -> IntShapes; + fn slice_by_paths(&self, paths: &[IntPath], fill_rule: FillRule) -> IntShapes; } -impl IntSlice for IntShapes { +impl IntSlice for IntShapes +where + I: IntNumber + Expiration + LayoutNumber + SortKey, +{ #[inline] - fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes { + fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shapes(self); overlay.add_string_line(line); overlay @@ -25,7 +31,7 @@ impl IntSlice for IntShapes { } #[inline] - fn slice_by_lines(&self, lines: &[IntLine], fill_rule: FillRule) -> IntShapes { + fn slice_by_lines(&self, lines: &[IntLine], fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shapes(self); overlay.add_string_lines(lines); overlay @@ -35,7 +41,7 @@ impl IntSlice for IntShapes { } #[inline] - fn slice_by_path(&self, path: &IntPath, fill_rule: FillRule) -> IntShapes { + fn slice_by_path(&self, path: &IntPath, fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shapes(self); overlay.add_string_path(path); overlay @@ -45,7 +51,7 @@ impl IntSlice for IntShapes { } #[inline] - fn slice_by_paths(&self, paths: &[IntPath], fill_rule: FillRule) -> IntShapes { + fn slice_by_paths(&self, paths: &[IntPath], fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shapes(self); overlay.add_string_paths(paths); overlay @@ -55,9 +61,12 @@ impl IntSlice for IntShapes { } } -impl IntSlice for IntShape { +impl IntSlice for IntShape +where + I: IntNumber + Expiration + LayoutNumber + SortKey, +{ #[inline] - fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes { + fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shape(self); overlay.add_string_line(line); overlay @@ -67,7 +76,7 @@ impl IntSlice for IntShape { } #[inline] - fn slice_by_lines(&self, lines: &[IntLine], fill_rule: FillRule) -> IntShapes { + fn slice_by_lines(&self, lines: &[IntLine], fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shape(self); overlay.add_string_lines(lines); overlay @@ -77,7 +86,7 @@ impl IntSlice for IntShape { } #[inline] - fn slice_by_path(&self, path: &IntPath, fill_rule: FillRule) -> IntShapes { + fn slice_by_path(&self, path: &IntPath, fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shape(self); overlay.add_string_path(path); overlay @@ -87,7 +96,7 @@ impl IntSlice for IntShape { } #[inline] - fn slice_by_paths(&self, paths: &[IntPath], fill_rule: FillRule) -> IntShapes { + fn slice_by_paths(&self, paths: &[IntPath], fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shape(self); overlay.add_string_paths(paths); overlay @@ -97,9 +106,12 @@ impl IntSlice for IntShape { } } -impl IntSlice for [IntPoint] { +impl IntSlice for [IntPoint] +where + I: IntNumber + Expiration + LayoutNumber + SortKey, +{ #[inline] - fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes { + fn slice_by_line(&self, line: IntLine, fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shape_contour(self); overlay.add_string_line(line); overlay @@ -109,7 +121,7 @@ impl IntSlice for [IntPoint] { } #[inline] - fn slice_by_lines(&self, lines: &[IntLine], fill_rule: FillRule) -> IntShapes { + fn slice_by_lines(&self, lines: &[IntLine], fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shape_contour(self); overlay.add_string_lines(lines); overlay @@ -119,7 +131,7 @@ impl IntSlice for [IntPoint] { } #[inline] - fn slice_by_path(&self, path: &IntPath, fill_rule: FillRule) -> IntShapes { + fn slice_by_path(&self, path: &IntPath, fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shape_contour(self); overlay.add_string_path(path); overlay @@ -129,7 +141,7 @@ impl IntSlice for [IntPoint] { } #[inline] - fn slice_by_paths(&self, paths: &[IntPath], fill_rule: FillRule) -> IntShapes { + fn slice_by_paths(&self, paths: &[IntPath], fill_rule: FillRule) -> IntShapes { let mut overlay = StringOverlay::with_shape_contour(self); overlay.add_string_paths(paths); overlay @@ -177,6 +189,20 @@ mod tests { assert_eq!(result[1][0].len(), 4); } + #[test] + fn test_i64_slice() { + let paths = vec![vec![ + IntPoint::::new(-10, 10), + IntPoint::new(-10, -10), + IntPoint::new(10, -10), + IntPoint::new(10, 10), + ]]; + + let result = paths.slice_by_line([IntPoint::new(-20, 0), IntPoint::new(20, 0)], FillRule::NonZero); + + assert_eq!(result.len(), 2); + } + #[test] fn test_1() { let paths = vec![ diff --git a/iOverlay/src/string/split.rs b/iOverlay/src/string/split.rs index 4c38caa..fda9d0f 100644 --- a/iOverlay/src/string/split.rs +++ b/iOverlay/src/string/split.rs @@ -1,26 +1,29 @@ use alloc::vec::Vec; +use i_float::int::number::int::IntNumber; +use i_float::int::number::uint::UIntNumber; +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 { +pub(super) trait Split { fn split_loops( self, min_area: u64, - contour_buffer: &mut IntContour, - bin_store: &mut BinStore, + contour_buffer: &mut IntContour, + bin_store: &mut BinStore, ) -> Vec where Self: Sized; } -impl Split for IntContour { +impl Split for IntContour { fn split_loops( self, min_area: u64, - contour_buffer: &mut IntContour, - bin_store: &mut BinStore, + contour_buffer: &mut IntContour, + bin_store: &mut BinStore, ) -> Vec { if self.is_empty() { return Vec::new(); @@ -30,7 +33,7 @@ impl Split for IntContour { bin_store.init(&self); - let mut result: Vec> = Vec::with_capacity(16); + let mut result: Vec> = Vec::with_capacity(16); for point in self { let next_pos = contour_buffer.len() + 1; @@ -62,8 +65,8 @@ impl Split for IntContour { } #[derive(Debug, Clone, Copy)] -struct PointItem { - point: IntPoint, +struct PointItem { + point: IntPoint, pos: usize, } @@ -73,13 +76,13 @@ struct Bin { data: usize, } -pub(super) struct BinStore { +pub(super) struct BinStore { mask: u32, bins: Vec, - items: Vec, + items: Vec>, } -impl BinStore { +impl BinStore { pub(super) fn new() -> Self { Self { mask: 0, @@ -88,7 +91,7 @@ impl BinStore { } } - fn init(&mut self, contour: &IntContour) { + fn init(&mut self, contour: &IntContour) { let log = contour.len().ilog2().saturating_sub(4).clamp(1, 30); let bins_count = (1 << log) as usize; @@ -123,7 +126,7 @@ impl BinStore { } #[inline] - fn insert_if_not_exist(&mut self, point: IntPoint, pos: usize) -> usize { + fn insert_if_not_exist(&mut self, point: IntPoint, pos: usize) -> usize { let index = self.bin_index(point); let bin = unsafe { // SAFETY: insert_if_not_exist only touches bins within the pre-sized array; bin_index shares the same invariant. @@ -150,26 +153,26 @@ impl BinStore { } #[inline] - fn bin_index(&self, p: IntPoint) -> usize { - let x = p.x.unsigned_abs(); - let y = p.y.unsigned_abs(); - let hash = x.wrapping_mul(31) ^ y.wrapping_mul(17); - (hash & self.mask) as usize + 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 hash = x.wrapping_add(y); + (hash & I::Wide::from_usize(self.mask as usize)).to_usize() } } -trait ValidateArea { +trait ValidateArea { fn validate_area(&self, min_area: u64) -> bool; } -impl ValidateArea for IntContour { +impl ValidateArea for IntContour { #[inline] fn validate_area(&self, min_area: u64) -> bool { if min_area == 0 { return true; } let abs_area = self.unsafe_area().unsigned_abs() >> 1; - abs_area < min_area + abs_area < ::UInt::from_u64(min_area) } } diff --git a/iOverlay/src/vector/extract.rs b/iOverlay/src/vector/extract.rs index cfb6a80..63b3b49 100644 --- a/iOverlay/src/vector/extract.rs +++ b/iOverlay/src/vector/extract.rs @@ -21,7 +21,7 @@ use i_tree::Expiration; impl OverlayGraph<'_, I, D> where - I: IntNumber + Expiration + SortKey + Send + Sync, + I: IntNumber + Expiration + SortKey, D: OverlayEdgeData, { pub fn extract_separate_vectors(&self) -> Vec> { @@ -206,7 +206,7 @@ impl StartVectorPathData { trait JoinHoles where - I: IntNumber + Expiration + SortKey + Send + Sync, + I: IntNumber + Expiration + SortKey, D: OverlayEdgeData, { fn join_sorted_holes( @@ -225,7 +225,7 @@ where impl JoinHoles for Vec> where - I: IntNumber + Expiration + SortKey + Send + Sync, + I: IntNumber + Expiration + SortKey, D: OverlayEdgeData, { fn join_sorted_holes( diff --git a/iOverlay/tests/edge_overlay_tests.rs b/iOverlay/tests/edge_overlay_tests.rs index 1930ff3..83d9230 100644 --- a/iOverlay/tests/edge_overlay_tests.rs +++ b/iOverlay/tests/edge_overlay_tests.rs @@ -80,7 +80,7 @@ mod tests { }, ); - let mut overlay = EdgeOverlay::new(subj.len() + clip.len()); + let mut overlay = EdgeOverlay::::new(subj.len() + clip.len()); overlay.add_edges(subj, ShapeType::Subject); overlay.add_edges(clip, ShapeType::Clip); @@ -173,7 +173,7 @@ mod tests { }, ); - let mut overlay = EdgeOverlay::new(subj.len() + clip.len()); + let mut overlay = EdgeOverlay::::new(subj.len() + clip.len()); overlay.add_edges(subj, ShapeType::Subject); overlay.add_edges(clip, ShapeType::Clip); @@ -225,7 +225,58 @@ mod tests { assert_eq!(rect, &template); } - fn square(x0: i32, y0: i32, x1: i32, y1: i32, data: EdgeColor) -> Vec> { + #[test] + fn union_squares_i64() { + let subj = square_i64( + 0, + 0, + 4, + 4, + EdgeColor { + subj: Color::Red, + clip: Color::None, + }, + ); + let clip = square_i64( + 4, + 0, + 8, + 4, + EdgeColor { + subj: Color::None, + clip: Color::Green, + }, + ); + + let mut overlay = EdgeOverlay::::new(subj.len() + clip.len()); + overlay.add_edges(subj, ShapeType::Subject); + overlay.add_edges(clip, ShapeType::Clip); + + let shapes = overlay.build_vector_shapes(OverlayRule::Union, FillRule::NonZero); + + assert_eq!(shapes.len(), 1); + assert_eq!(shapes[0].len(), 1); + assert_eq!(shapes[0][0].len(), 6); + } + + fn square(x0: i32, y0: i32, x1: i32, y1: i32, data: EdgeColor) -> Vec> { + let points = [ + IntPoint::new(x0, y0), + IntPoint::new(x1, y0), + IntPoint::new(x1, y1), + IntPoint::new(x0, y1), + ]; + + points + .iter() + .copied() + .zip(points.iter().copied().cycle().skip(1)) + .take(points.len()) + .map(|(a, b)| InputEdge { a, b, data }) + .collect() + } + + fn square_i64(x0: i64, y0: i64, x1: i64, y1: i64, data: EdgeColor) -> Vec> { let points = [ IntPoint::new(x0, y0), IntPoint::new(x1, y0), diff --git a/iOverlay/tests/slice_tests.rs b/iOverlay/tests/slice_tests.rs index ec67aee..c19671f 100644 --- a/iOverlay/tests/slice_tests.rs +++ b/iOverlay/tests/slice_tests.rs @@ -503,7 +503,7 @@ mod tests { points } - fn random_lines(radius: i32, n: usize) -> Vec { + fn random_lines(radius: i32, n: usize) -> Vec> { let a = radius / 2; let range = -a..=a; let mut lines = Vec::with_capacity(n); From 09363bc7d84c2a52d0463925b7bcd4faf9f3e0cf Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Wed, 20 May 2026 21:49:01 +0300 Subject: [PATCH 08/36] code clean --- iOverlay/src/build/boolean.rs | 11 ++++++++--- iOverlay/src/build/builder.rs | 5 +++-- iOverlay/src/core/edge_overlay.rs | 3 ++- iOverlay/src/core/extract.rs | 17 +++++++++++----- iOverlay/src/core/graph.rs | 3 ++- iOverlay/src/core/overlay.rs | 32 ++++++++++++++++++------------- iOverlay/src/core/simplify.rs | 25 ++++++++++++++++++++---- iOverlay/src/float/overlay.rs | 11 ++++++----- iOverlay/src/split/grid_layout.rs | 3 ++- iOverlay/src/string/extract.rs | 7 ++++++- iOverlay/src/string/overlay.rs | 8 ++++++-- iOverlay/src/string/split.rs | 12 ++++++------ iOverlay/src/vector/extract.rs | 17 +++++++++++----- iOverlay/tests/direction_tests.rs | 8 ++++---- iOverlay/tests/overlay_tests.rs | 4 ++-- iOverlay/tests/simplify_tests.rs | 8 ++++---- 16 files changed, 115 insertions(+), 59 deletions(-) diff --git a/iOverlay/src/build/boolean.rs b/iOverlay/src/build/boolean.rs index 6d28a86..334e1af 100644 --- a/iOverlay/src/build/boolean.rs +++ b/iOverlay/src/build/boolean.rs @@ -20,6 +20,7 @@ use crate::segm::segment::{ use crate::segm::winding::WindingCount; use alloc::vec::Vec; use i_float::int::number::int::IntNumber; +use i_float::int::number::wide_int::WideIntNumber; use i_key_sort::sort::key::SortKey; use i_shape::util::reserve::Reserve; use i_tree::Expiration; @@ -33,7 +34,7 @@ where pub(crate) fn build_boolean_all( &mut self, fill_rule: FillRule, - options: IntOverlayOptions, + options: IntOverlayOptions<::UInt>, solver: &Solver, segments: &[Segment], ) -> OverlayGraph<'_, I, D> { @@ -47,7 +48,7 @@ where &mut self, fill_rule: FillRule, overlay_rule: OverlayRule, - options: IntOverlayOptions, + options: IntOverlayOptions<::UInt>, solver: &Solver, segments: &[Segment], ) -> OverlayGraph<'_, I, D> { @@ -80,7 +81,11 @@ where } #[inline] - fn boolean_graph(&mut self, options: IntOverlayOptions, solver: &Solver) -> OverlayGraph<'_, I, D> { + fn boolean_graph( + &mut self, + options: IntOverlayOptions<::UInt>, + solver: &Solver, + ) -> OverlayGraph<'_, I, D> { self.build_nodes_and_connect_links(solver); OverlayGraph { nodes: &self.nodes, diff --git a/iOverlay/src/build/builder.rs b/iOverlay/src/build/builder.rs index 33bb20c..e6a211e 100644 --- a/iOverlay/src/build/builder.rs +++ b/iOverlay/src/build/builder.rs @@ -10,6 +10,7 @@ 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 { fn is_included(fill: SegmentFill) -> bool; @@ -45,7 +46,7 @@ pub(crate) trait GraphNode { fn with_indices(indices: &[usize]) -> Self; } -pub(crate) struct GraphBuilder { +pub(crate) struct GraphBuilder { sweep_runner: SweepRunner, pub(super) links: Vec>, pub(super) nodes: Vec, @@ -57,7 +58,7 @@ impl GraphBuilder where C: WindingCount, N: GraphNode, - I: IntNumber + i_tree::Expiration, + I: IntNumber + Expiration, D: OverlayEdgeData, { #[inline] diff --git a/iOverlay/src/core/edge_overlay.rs b/iOverlay/src/core/edge_overlay.rs index 9eb0165..0272a0d 100644 --- a/iOverlay/src/core/edge_overlay.rs +++ b/iOverlay/src/core/edge_overlay.rs @@ -13,6 +13,7 @@ use crate::split::solver::SplitSolver; use crate::vector::edge::{DataVectorEdge, DataVectorShape}; use alloc::vec::Vec; use i_float::int::number::int::IntNumber; +use i_float::int::number::wide_int::WideIntNumber; use i_float::int::point::IntPoint; use i_key_sort::sort::key::SortKey; use i_tree::{Expiration, LayoutNumber}; @@ -26,7 +27,7 @@ pub struct InputEdge { pub struct EdgeOverlay { pub solver: Solver, - pub options: IntOverlayOptions, + pub options: IntOverlayOptions<::UInt>, pub boolean_buffer: Option>, segments: Vec>, split_solver: SplitSolver, diff --git a/iOverlay/src/core/extract.rs b/iOverlay/src/core/extract.rs index fb37faf..9dcdba6 100644 --- a/iOverlay/src/core/extract.rs +++ b/iOverlay/src/core/extract.rs @@ -311,13 +311,21 @@ impl StartPathData { } pub(crate) trait GraphContour { - fn validate(&mut self, min_output_area: u64, preserve_output_collinear: bool) -> (bool, bool); + fn validate( + &mut self, + min_output_area: ::UInt, + preserve_output_collinear: bool, + ) -> (bool, bool); fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize; } impl GraphContour for IntContour { #[inline] - fn validate(&mut self, min_output_area: u64, preserve_output_collinear: bool) -> (bool, bool) { + fn validate( + &mut self, + min_output_area: ::UInt, + preserve_output_collinear: bool, + ) -> (bool, bool) { let is_modified = if !preserve_output_collinear { self.simplify_contour() } else { @@ -328,13 +336,12 @@ impl GraphContour for IntContour { return (false, is_modified); } - if min_output_area == 0 { + if min_output_area == ::UInt::ZERO { return (true, is_modified); } let area = self.unsafe_area(); let abs_area = area.unsigned_abs() >> 1; - let min_area = <::UInt as UIntNumber>::from_u64(min_output_area); - let is_valid = abs_area >= min_area; + let is_valid = abs_area >= min_output_area; (is_valid, is_modified) } diff --git a/iOverlay/src/core/graph.rs b/iOverlay/src/core/graph.rs index 0dfe110..d57121c 100644 --- a/iOverlay/src/core/graph.rs +++ b/iOverlay/src/core/graph.rs @@ -7,6 +7,7 @@ use crate::build::builder::GraphNode; use crate::core::overlay::IntOverlayOptions; use alloc::vec::Vec; use i_float::int::number::int::IntNumber; +use i_float::int::number::wide_int::WideIntNumber; /// A representation of geometric shapes organized for efficient boolean operations. /// @@ -15,7 +16,7 @@ use i_float::int::number::int::IntNumber; /// Use `OverlayGraph` to perform boolean operations on the geometric shapes you've added to an `Overlay`, after it has processed the shapes according to the specified build and overlay rules. /// [More information](https://ishape-rust.github.io/iShape-js/overlay/overlay_graph/overlay_graph.html) about Overlay Graph. pub struct OverlayGraph<'a, I: IntNumber, D = ()> { - pub(crate) options: IntOverlayOptions, + pub(crate) options: IntOverlayOptions<::UInt>, pub(crate) nodes: &'a [OverlayNode], pub(crate) links: &'a [OverlayLink], } diff --git a/iOverlay/src/core/overlay.rs b/iOverlay/src/core/overlay.rs index 56ad627..412dc76 100644 --- a/iOverlay/src/core/overlay.rs +++ b/iOverlay/src/core/overlay.rs @@ -15,6 +15,8 @@ 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::number::wide_int::WideIntNumber; use i_float::int::point::IntPoint; use i_key_sort::sort::key::SortKey; use i_shape::int::count::PointsCount; @@ -29,7 +31,7 @@ use super::graph::{OverlayGraph, OverlayNode}; /// during the Boolean operation process. You can use this to adjust output /// direction, eliminate small artifacts, or retain collinear points. #[derive(Debug, Clone, Copy)] -pub struct IntOverlayOptions { +pub struct IntOverlayOptions { /// Preserve collinear points in the input before Boolean operations. pub preserve_input_collinear: bool, @@ -40,7 +42,7 @@ pub struct IntOverlayOptions { pub preserve_output_collinear: bool, /// Minimum area threshold to include a contour in the result. - pub min_output_area: u64, + pub min_output_area: U, /// If true, extract OGC-valid shapes. pub ogc: bool, @@ -66,7 +68,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 solver: Solver, - pub options: IntOverlayOptions, + pub options: IntOverlayOptions<::UInt>, pub boolean_buffer: Option>, pub(crate) segments: Vec>, pub(crate) split_solver: SplitSolver, @@ -96,7 +98,11 @@ where /// - `capacity`: The initial capacity for storing edge data. Ideally, this should be set to the sum of the edges of all shapes to be added to the overlay, ensuring efficient data management. /// - `options`: Adjust custom behavior. /// - `solver`: Type of solver to use. - pub fn new_custom(capacity: usize, options: IntOverlayOptions, solver: Solver) -> Self { + pub fn new_custom( + capacity: usize, + options: IntOverlayOptions<::UInt>, + solver: Solver, + ) -> Self { Self { solver, options, @@ -125,7 +131,7 @@ where pub fn with_contour_custom( subj: &[IntPoint], clip: &[IntPoint], - options: IntOverlayOptions, + options: IntOverlayOptions<::UInt>, solver: Solver, ) -> Self { let mut overlay = Self::new_custom(subj.len() + clip.len(), options, solver); @@ -152,7 +158,7 @@ where pub fn with_contours_custom( subj: &[IntContour], clip: &[IntContour], - options: IntOverlayOptions, + options: IntOverlayOptions<::UInt>, solver: Solver, ) -> Self { let mut overlay = Self::new_custom(subj.points_count() + clip.points_count(), options, solver); @@ -179,7 +185,7 @@ where pub fn with_shapes_options( subj: &[IntShape], clip: &[IntShape], - options: IntOverlayOptions, + options: IntOverlayOptions<::UInt>, solver: Solver, ) -> Self { let mut overlay = Self::new_custom(subj.points_count() + clip.points_count(), options, solver); @@ -406,25 +412,25 @@ where } } -impl Default for IntOverlayOptions { +impl Default for IntOverlayOptions { fn default() -> Self { Self { preserve_input_collinear: false, output_direction: ContourDirection::CounterClockwise, preserve_output_collinear: false, - min_output_area: 0, + min_output_area: U::ZERO, ogc: false, } } } -impl IntOverlayOptions { +impl IntOverlayOptions { pub fn keep_all_points() -> Self { Self { preserve_input_collinear: true, output_direction: ContourDirection::CounterClockwise, preserve_output_collinear: true, - min_output_area: 0, + min_output_area: U::ZERO, ogc: false, } } @@ -433,7 +439,7 @@ impl IntOverlayOptions { preserve_input_collinear: false, output_direction: ContourDirection::CounterClockwise, preserve_output_collinear: true, - min_output_area: 0, + min_output_area: U::ZERO, ogc: false, } } @@ -442,7 +448,7 @@ impl IntOverlayOptions { preserve_input_collinear: false, output_direction: ContourDirection::CounterClockwise, preserve_output_collinear: false, - min_output_area: 0, + min_output_area: U::ZERO, ogc: true, } } diff --git a/iOverlay/src/core/simplify.rs b/iOverlay/src/core/simplify.rs index 0f2935a..6b4dd1e 100644 --- a/iOverlay/src/core/simplify.rs +++ b/iOverlay/src/core/simplify.rs @@ -7,6 +7,7 @@ use crate::core::overlay::ContourDirection::Clockwise; use crate::core::overlay::{IntOverlayOptions, Overlay, ShapeType}; use crate::core::overlay_rule::OverlayRule; use crate::i_float::int::number::int::IntNumber; +use crate::i_float::int::number::wide_int::WideIntNumber; use crate::i_float::int::point::IntPoint; use alloc::vec; use i_key_sort::sort::key::SortKey; @@ -33,7 +34,11 @@ pub trait Simplify { /// - Each path `Vec` is a sequence of points, forming a closed path. /// /// Note: Outer boundary paths have a **main_direction** order, and holes have an opposite to **main_direction** order. - fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes; + fn simplify( + &self, + fill_rule: FillRule, + options: IntOverlayOptions<::UInt>, + ) -> IntShapes; } impl Simplify for [IntPoint] @@ -41,7 +46,11 @@ where I: IntNumber + Expiration + LayoutNumber + SortKey, { #[inline] - fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { + fn simplify( + &self, + fill_rule: FillRule, + options: IntOverlayOptions<::UInt>, + ) -> IntShapes { match Overlay::new_custom(self.len(), options, Default::default()).simplify_contour(self, fill_rule) { Some(shapes) => shapes, None => vec![vec![self.to_vec()]], @@ -54,7 +63,11 @@ where I: IntNumber + Expiration + LayoutNumber + SortKey, { #[inline] - fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { + fn simplify( + &self, + fill_rule: FillRule, + options: IntOverlayOptions<::UInt>, + ) -> IntShapes { match Overlay::new_custom(self.len(), options, Default::default()).simplify_shape(self, fill_rule) { Some(shapes) => shapes, None => vec![self.to_vec()], @@ -67,7 +80,11 @@ where I: IntNumber + Expiration + LayoutNumber + SortKey, { #[inline] - fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { + fn simplify( + &self, + fill_rule: FillRule, + options: IntOverlayOptions<::UInt>, + ) -> IntShapes { Overlay::new_custom(self.points_count(), options, Default::default()).simplify_shapes(self, fill_rule) } } diff --git a/iOverlay/src/float/overlay.rs b/iOverlay/src/float/overlay.rs index a39de7b..8bac94d 100644 --- a/iOverlay/src/float/overlay.rs +++ b/iOverlay/src/float/overlay.rs @@ -13,6 +13,7 @@ use i_float::float::compatible::FloatPointCompatible; use i_float::float::number::FloatNumber; 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_shape::base::data::Shapes; use i_shape::flat::buffer::FlatContoursBuffer; @@ -98,7 +99,7 @@ impl FloatOverlay

{ pub fn new_empty(options: OverlayOptions, solver: Solver, capacity: usize) -> Self { let clean_result = options.clean_result; let adapter = FloatPointAdapter::new(FloatRect::zero()); - let overlay = Overlay::new_custom(capacity, options.int_default(), solver); + let overlay = Overlay::new_custom(capacity, options.int_default::(), solver); Self { overlay, clean_result, @@ -389,22 +390,22 @@ impl OverlayOptions { pub(crate) fn int_with_adapter, I: IntNumber>( &self, adapter: &FloatPointAdapter, - ) -> IntOverlayOptions { + ) -> IntOverlayOptions<::UInt> { IntOverlayOptions { preserve_input_collinear: self.preserve_input_collinear, output_direction: self.output_direction, preserve_output_collinear: self.preserve_output_collinear, - min_output_area: adapter.round_sqr_len_to_int(self.min_output_area).to_usize() as u64, + min_output_area: adapter.round_sqr_len_to_int(self.min_output_area).to_uint(), ogc: self.ogc, } } - pub(crate) fn int_default(&self) -> IntOverlayOptions { + pub(crate) fn int_default(&self) -> IntOverlayOptions<::UInt> { IntOverlayOptions { preserve_input_collinear: self.preserve_input_collinear, output_direction: self.output_direction, preserve_output_collinear: self.preserve_output_collinear, - min_output_area: 0, + min_output_area: ::UInt::ZERO, ogc: self.ogc, } } diff --git a/iOverlay/src/split/grid_layout.rs b/iOverlay/src/split/grid_layout.rs index 9867696..a5622d9 100644 --- a/iOverlay/src/split/grid_layout.rs +++ b/iOverlay/src/split/grid_layout.rs @@ -120,8 +120,9 @@ impl FragmentBuffer { let k = (height << p) / width; let mut w = (self.layout.pos(i0 + 1).wide() - s.a.x.wide()).to_uint(); - let dw = <::UInt as UIntNumber>::one_shl(self.layout.power); + let one = <::UInt as UIntNumber>::ONE; + let dw = one << self.layout.power; for i in i0..i1 { let h_min = (w * k) >> p; diff --git a/iOverlay/src/string/extract.rs b/iOverlay/src/string/extract.rs index 1d67772..270d11a 100644 --- a/iOverlay/src/string/extract.rs +++ b/iOverlay/src/string/extract.rs @@ -8,6 +8,7 @@ use crate::string::split::{BinStore, Split}; use alloc::vec; use alloc::vec::Vec; use i_float::int::number::int::IntNumber; +use i_float::int::number::wide_int::WideIntNumber; use i_key_sort::sort::key::SortKey; use i_shape::int::path::{ContourExtension, IntPath}; use i_shape::int::shape::IntShapes; @@ -40,7 +41,11 @@ impl StringGraph<'_, I> { /// - Each path `Vec` is a sequence of points, forming a closed path. /// /// Note: Outer boundary paths have a **main_direction** order, and holes have an opposite to **main_direction** order. - pub fn extract_shapes_custom(&self, string_rule: StringRule, options: IntOverlayOptions) -> IntShapes { + pub fn extract_shapes_custom( + &self, + string_rule: StringRule, + options: IntOverlayOptions<::UInt>, + ) -> IntShapes { let clockwise = options.output_direction == ContourDirection::Clockwise; let mut fills = self.filter(string_rule); let mut shapes = Vec::new(); diff --git a/iOverlay/src/string/overlay.rs b/iOverlay/src/string/overlay.rs index 658fd86..8bbb4f5 100644 --- a/iOverlay/src/string/overlay.rs +++ b/iOverlay/src/string/overlay.rs @@ -15,6 +15,7 @@ use crate::string::line::IntLine; use alloc::vec::Vec; use core::cmp::Ordering; use i_float::int::number::int::IntNumber; +use i_float::int::number::wide_int::WideIntNumber; use i_float::int::point::IntPoint; use i_key_sort::sort::key::SortKey; use i_shape::int::count::PointsCount; @@ -23,7 +24,7 @@ use i_shape::int::shape::{IntContour, IntShape}; use i_tree::{Expiration, LayoutNumber}; pub struct StringOverlay { - pub options: IntOverlayOptions, + pub options: IntOverlayOptions<::UInt>, pub(super) segments: Vec>, pub(crate) split_solver: SplitSolver, pub(crate) graph_builder: GraphBuilder, I>, @@ -50,7 +51,10 @@ where /// This pre-allocation helps in optimizing memory usage and performance. /// - `capacity`: The initial capacity for storing edge data. Ideally, this should be set to the sum of the edges of all shapes to be added to the overlay, ensuring efficient data management. /// - `options`: Adjust custom behavior. - pub fn with_options(capacity: usize, options: IntOverlayOptions) -> Self { + pub fn with_options( + capacity: usize, + options: IntOverlayOptions<::UInt>, + ) -> Self { Self { options, segments: Vec::with_capacity(capacity), diff --git a/iOverlay/src/string/split.rs b/iOverlay/src/string/split.rs index fda9d0f..ef623ee 100644 --- a/iOverlay/src/string/split.rs +++ b/iOverlay/src/string/split.rs @@ -10,7 +10,7 @@ use i_shape::util::reserve::Reserve; pub(super) trait Split { fn split_loops( self, - min_area: u64, + min_area: ::UInt, contour_buffer: &mut IntContour, bin_store: &mut BinStore, ) -> Vec @@ -21,7 +21,7 @@ pub(super) trait Split { impl Split for IntContour { fn split_loops( self, - min_area: u64, + min_area: ::UInt, contour_buffer: &mut IntContour, bin_store: &mut BinStore, ) -> Vec { @@ -162,17 +162,17 @@ impl BinStore { } trait ValidateArea { - fn validate_area(&self, min_area: u64) -> bool; + fn validate_area(&self, min_area: ::UInt) -> bool; } impl ValidateArea for IntContour { #[inline] - fn validate_area(&self, min_area: u64) -> bool { - if min_area == 0 { + fn validate_area(&self, min_area: ::UInt) -> bool { + if min_area == ::UInt::ZERO { return true; } let abs_area = self.unsafe_area().unsigned_abs() >> 1; - abs_area < ::UInt::from_u64(min_area) + abs_area < min_area } } diff --git a/iOverlay/src/vector/extract.rs b/iOverlay/src/vector/extract.rs index 63b3b49..7faa07a 100644 --- a/iOverlay/src/vector/extract.rs +++ b/iOverlay/src/vector/extract.rs @@ -312,13 +312,21 @@ fn is_sorted(segments: &[IdSegment]) -> bool { } trait DataGraphContour { - fn validate(&mut self, min_output_area: u64, preserve_output_collinear: bool) -> (bool, bool); + fn validate( + &mut self, + min_output_area: ::UInt, + preserve_output_collinear: bool, + ) -> (bool, bool); fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize; } impl DataGraphContour for DataVectorPath { #[inline] - fn validate(&mut self, min_output_area: u64, preserve_output_collinear: bool) -> (bool, bool) { + fn validate( + &mut self, + min_output_area: ::UInt, + preserve_output_collinear: bool, + ) -> (bool, bool) { let is_modified = if !preserve_output_collinear { self.simplify_contour() } else { @@ -329,16 +337,15 @@ impl DataGraphContour for DataVectorPath return (false, is_modified); } - if min_output_area == 0 { + if min_output_area == ::UInt::ZERO { return (true, is_modified); } let double_area = self .iter() .fold(I::Wide::ZERO, |acc, edge| acc + edge.a.cross_product(edge.b)); - let min_area = <::UInt as UIntNumber>::from_u64(min_output_area); - ((double_area.unsigned_abs() >> 1) >= min_area, is_modified) + ((double_area.unsigned_abs() >> 1) >= min_output_area, is_modified) } #[inline] diff --git a/iOverlay/tests/direction_tests.rs b/iOverlay/tests/direction_tests.rs index 41129c6..2a9658d 100644 --- a/iOverlay/tests/direction_tests.rs +++ b/iOverlay/tests/direction_tests.rs @@ -22,7 +22,7 @@ mod tests { preserve_input_collinear: false, output_direction: ContourDirection::CounterClockwise, preserve_output_collinear: false, - min_output_area: 0, + min_output_area: 0u64, ogc: false, }; @@ -30,7 +30,7 @@ mod tests { preserve_input_collinear: false, output_direction: ContourDirection::Clockwise, preserve_output_collinear: false, - min_output_area: 0, + min_output_area: 0u64, ogc: false, }; @@ -62,7 +62,7 @@ mod tests { preserve_input_collinear: false, output_direction: ContourDirection::CounterClockwise, preserve_output_collinear: false, - min_output_area: 0, + min_output_area: 0u64, ogc: false, }; @@ -70,7 +70,7 @@ mod tests { preserve_input_collinear: false, output_direction: ContourDirection::Clockwise, preserve_output_collinear: false, - min_output_area: 0, + min_output_area: 0u64, ogc: false, }; diff --git a/iOverlay/tests/overlay_tests.rs b/iOverlay/tests/overlay_tests.rs index 1a84fa4..4f7f99d 100644 --- a/iOverlay/tests/overlay_tests.rs +++ b/iOverlay/tests/overlay_tests.rs @@ -23,11 +23,11 @@ mod tests { preserve_input_collinear: false, output_direction: ContourDirection::Clockwise, preserve_output_collinear: false, - min_output_area: 0, + min_output_area: 0u64, ogc: false, }; - fn overlay(test: &BooleanTest, options: IntOverlayOptions, solver: Solver) -> Overlay { + fn overlay(test: &BooleanTest, options: IntOverlayOptions, solver: Solver) -> Overlay { Overlay::with_contours_custom(&test.subj_paths, &test.clip_paths, options, solver) } diff --git a/iOverlay/tests/simplify_tests.rs b/iOverlay/tests/simplify_tests.rs index 2210022..345d60c 100644 --- a/iOverlay/tests/simplify_tests.rs +++ b/iOverlay/tests/simplify_tests.rs @@ -25,7 +25,7 @@ mod tests { preserve_input_collinear: true, output_direction: ContourDirection::CounterClockwise, preserve_output_collinear: true, - min_output_area: 0, + min_output_area: 0u64, ogc: false, }; @@ -53,7 +53,7 @@ mod tests { preserve_input_collinear: true, output_direction: ContourDirection::CounterClockwise, preserve_output_collinear: true, - min_output_area: 0, + min_output_area: 0u64, ogc: false, }; @@ -81,7 +81,7 @@ mod tests { preserve_input_collinear: true, output_direction: ContourDirection::CounterClockwise, preserve_output_collinear: true, - min_output_area: 0, + min_output_area: 0u64, ogc: false, }; @@ -144,7 +144,7 @@ mod tests { preserve_input_collinear: false, output_direction: ContourDirection::CounterClockwise, preserve_output_collinear: true, - min_output_area: 0, + min_output_area: 0u64, ogc: false, }; From bcf9e85ee7c6d479b24433dad5414afa1612f754 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Wed, 20 May 2026 22:12:26 +0300 Subject: [PATCH 09/36] fix --- iOverlay/src/split/grid_layout.rs | 6 ++---- iOverlay/src/split/snap_radius.rs | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/iOverlay/src/split/grid_layout.rs b/iOverlay/src/split/grid_layout.rs index a5622d9..5e483b3 100644 --- a/iOverlay/src/split/grid_layout.rs +++ b/iOverlay/src/split/grid_layout.rs @@ -258,14 +258,12 @@ pub(super) struct GridLayout { impl GridLayout { #[inline] pub(super) fn index(&self, x: I) -> usize { - let step = I::Wide::one_shl(self.power); - ((x.wide() - self.min_x.wide()) / step).to_usize() + ((x - self.min_x) >> self.power).to_usize() } #[inline] pub(super) fn pos(&self, index: usize) -> I { - let offset = I::Wide::from_usize(index) * I::Wide::one_shl(self.power); - I::from_wide(offset + self.min_x.wide()) + I::from_usize(index << self.power) + self.min_x } pub(super) fn new(iter: It, count: usize) -> Option diff --git a/iOverlay/src/split/snap_radius.rs b/iOverlay/src/split/snap_radius.rs index ee8701b..08eb92a 100644 --- a/iOverlay/src/split/snap_radius.rs +++ b/iOverlay/src/split/snap_radius.rs @@ -13,7 +13,7 @@ impl SnapRadius { } pub(super) fn radius(&self) -> I::Wide { - I::Wide::one_shl(self.current as u32) + I::Wide::ONE << self.current as u32 } } From ae336dca8850b63e821771b22ef25f2d37a8af41 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Wed, 20 May 2026 22:24:01 +0300 Subject: [PATCH 10/36] fix --- iOverlay/src/split/grid_layout.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iOverlay/src/split/grid_layout.rs b/iOverlay/src/split/grid_layout.rs index 5e483b3..6e34a3b 100644 --- a/iOverlay/src/split/grid_layout.rs +++ b/iOverlay/src/split/grid_layout.rs @@ -286,11 +286,11 @@ impl GridLayout { } fn with_min_max(min_x: I, max_x: I, max_power: u32) -> Option { - let dx = max_x.wide() - min_x.wide(); - if dx < I::Wide::FOUR { + let dx = max_x - min_x; + if dx < I::FOUR { return None; } - let log = dx.to_uint().ilog2(); + let log = dx.ilog2(); let power = if log > max_power { log - max_power } else { 1 }; Some(Self { min_x, max_x, power }) From 4307c84ba8e22864a4ae10f4f51143595e3befe8 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Thu, 21 May 2026 09:37:05 +0300 Subject: [PATCH 11/36] fix grammar --- iOverlay/src/split/solver.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOverlay/src/split/solver.rs b/iOverlay/src/split/solver.rs index aa6f180..d1f09aa 100644 --- a/iOverlay/src/split/solver.rs +++ b/iOverlay/src/split/solver.rs @@ -191,7 +191,7 @@ where continue; } - // we have servral points + // we have several points let sub_marks = &mut self.marks[start..i]; Self::sort_sub_marks(sub_marks, x_seg); From 9d1124b696ec9a16a354272bcc8007efb9b65708 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Thu, 21 May 2026 11:10:11 +0300 Subject: [PATCH 12/36] update float api --- iOverlay/src/float/clip.rs | 4 +- iOverlay/src/float/graph.rs | 21 ++-- iOverlay/src/float/overlay.rs | 140 ++++++++++++++++++++------ iOverlay/src/float/relate.rs | 70 +++++++++---- iOverlay/src/float/scale.rs | 118 +++++++++++++++++++--- iOverlay/src/float/simplify.rs | 4 +- iOverlay/src/float/single.rs | 2 +- iOverlay/src/float/slice.rs | 8 +- iOverlay/src/float/string_graph.rs | 17 +++- iOverlay/src/float/string_overlay.rs | 53 ++++++++-- iOverlay/src/mesh/outline/offset.rs | 6 +- iOverlay/src/mesh/stroke/offset.rs | 4 +- iOverlay/tests/float_overlay_tests.rs | 68 +++++++------ iOverlay/tests/float_point_adapter.rs | 8 +- 14 files changed, 392 insertions(+), 131 deletions(-) diff --git a/iOverlay/src/float/clip.rs b/iOverlay/src/float/clip.rs index e835cea..0dab649 100644 --- a/iOverlay/src/float/clip.rs +++ b/iOverlay/src/float/clip.rs @@ -164,7 +164,7 @@ where clip_rule: ClipRule, solver: Solver, ) -> Paths

{ - FloatStringOverlay::with_shape_and_string(resource, self) + FloatStringOverlay::

::with_shape_and_string(resource, self) .clip_string_lines_with_solver(fill_rule, clip_rule, solver) } @@ -189,7 +189,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> { Ok( - FloatStringOverlay::with_shape_and_string_fixed_scale(resource, self, scale)? + FloatStringOverlay::

::with_shape_and_string_fixed_scale(resource, self, scale)? .clip_string_lines_with_solver(fill_rule, clip_rule, solver), ) } diff --git a/iOverlay/src/float/graph.rs b/iOverlay/src/float/graph.rs index de7720f..caa987a 100644 --- a/iOverlay/src/float/graph.rs +++ b/iOverlay/src/float/graph.rs @@ -7,25 +7,32 @@ use crate::core::graph::OverlayGraph; 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. /// [More information](https://ishape-rust.github.io/iShape-js/overlay/overlay_graph/overlay_graph.html) about Overlay Graph. -pub struct FloatOverlayGraph<'a, P: FloatPointCompatible> { - pub graph: OverlayGraph<'a, i32>, - pub adapter: FloatPointAdapter, +pub struct FloatOverlayGraph<'a, P: FloatPointCompatible, I: IntNumber = i32> { + pub graph: OverlayGraph<'a, I>, + pub adapter: FloatPointAdapter, clean_result: bool, } -impl<'a, P: FloatPointCompatible> FloatOverlayGraph<'a, P> { +impl<'a, P, I> FloatOverlayGraph<'a, P, I> +where + P: FloatPointCompatible, + I: IntNumber + Expiration + LayoutNumber + SortKey, +{ #[inline] pub(crate) fn new( - graph: OverlayGraph<'a, i32>, - adapter: FloatPointAdapter, + graph: OverlayGraph<'a, I>, + adapter: FloatPointAdapter, clean_result: bool, ) -> Self { Self { @@ -56,7 +63,7 @@ impl<'a, P: FloatPointCompatible> FloatOverlayGraph<'a, P> { pub fn extract_shapes( &self, overlay_rule: OverlayRule, - buffer: &mut BooleanExtractionBuffer, + buffer: &mut BooleanExtractionBuffer, ) -> Shapes

{ let shapes = self.graph.extract_shapes(overlay_rule, buffer); let mut float = shapes.to_float(&self.adapter); diff --git a/iOverlay/src/float/overlay.rs b/iOverlay/src/float/overlay.rs index 8bac94d..6ce2256 100644 --- a/iOverlay/src/float/overlay.rs +++ b/iOverlay/src/float/overlay.rs @@ -8,6 +8,7 @@ use crate::core::overlay_rule::OverlayRule; use crate::core::solver::Solver; use crate::float::graph::FloatOverlayGraph; use crate::i_shape::source::resource::ShapeResource; +use core::marker::PhantomData; use i_float::adapter::FloatPointAdapter; use i_float::float::compatible::FloatPointCompatible; use i_float::float::number::FloatNumber; @@ -15,15 +16,17 @@ 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}; #[derive(Debug, Clone, Copy)] -pub struct OverlayOptions { +pub struct OverlayOptions { /// Preserve collinear points in the input before Boolean operations. pub preserve_input_collinear: bool, @@ -34,7 +37,7 @@ pub struct OverlayOptions { pub preserve_output_collinear: bool, /// Minimum area threshold to include a contour in the result. - pub min_output_area: T, + pub min_output_area: F, /// If true, extract OGC-valid shapes. pub ogc: bool, @@ -42,16 +45,22 @@ pub struct OverlayOptions { /// If true, the result will be cleaned from precision-related issues /// such as duplicate or nearly identical points. Especially useful for `f32` coordinates. pub clean_result: bool, + + phantom_data: PhantomData, } /// 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(super) overlay: Overlay, +pub struct FloatOverlay { + pub(super) overlay: Overlay, pub(super) clean_result: bool, - pub(super) adapter: FloatPointAdapter, + pub(super) adapter: FloatPointAdapter, } -impl FloatOverlay

{ +impl FloatOverlay +where + P: FloatPointCompatible, + I: IntNumber + Expiration + LayoutNumber + SortKey, +{ /// Constructs a new `FloatOverlay`, a builder for overlaying geometric shapes /// by converting float-based geometry to integer space, using a pre-configured adapter. /// @@ -60,7 +69,7 @@ impl FloatOverlay

{ /// - `capacity`: Initial capacity for storing segments, ideally matching the total number of /// segments for efficient memory allocation. #[inline] - pub fn with_adapter(adapter: FloatPointAdapter, capacity: usize) -> Self { + pub fn with_adapter(adapter: FloatPointAdapter, capacity: usize) -> Self { Self::new_custom(adapter, Default::default(), Default::default(), capacity) } @@ -75,8 +84,8 @@ impl FloatOverlay

{ /// segments for efficient memory allocation. #[inline] pub fn new_custom( - adapter: FloatPointAdapter, - options: OverlayOptions, + adapter: FloatPointAdapter, + options: OverlayOptions, solver: Solver, capacity: usize, ) -> Self { @@ -96,10 +105,10 @@ impl FloatOverlay

{ /// - `capacity`: Initial capacity for storing segments, ideally matching the total number of /// segments for efficient memory allocation. #[inline] - pub fn new_empty(options: OverlayOptions, solver: Solver, capacity: usize) -> Self { + pub fn new_empty(options: OverlayOptions, solver: Solver, capacity: usize) -> Self { let clean_result = options.clean_result; let adapter = FloatPointAdapter::new(FloatRect::zero()); - let overlay = Overlay::new_custom(capacity, options.int_default::(), solver); + let overlay = Overlay::new_custom(capacity, options.int_default(), solver); Self { overlay, clean_result, @@ -114,7 +123,7 @@ impl FloatOverlay

{ /// - `Contour`: A contour representing a closed path. This path is interpreted as closed, so it doesn’t require the start and endpoint to be the same for processing. /// - `Contours`: A collection of contours, each representing a closed path. /// - `Shapes`: A collection of shapes, where each shape may consist of multiple contours. - pub fn with_subj_and_clip(subj: &R0, clip: &R1) -> Self + pub fn with_subj_and_clip_with_int(subj: &R0, clip: &R1) -> Self where R0: ShapeResource

+ ?Sized, R1: ShapeResource

+ ?Sized, @@ -138,10 +147,10 @@ impl FloatOverlay

{ /// - `Shapes`: A collection of shapes, where each shape may consist of multiple contours. /// - `options`: Adjust custom behavior. /// - `solver`: Type of solver to use. - pub fn with_subj_and_clip_custom( + pub fn with_subj_and_clip_custom_with_int( subj: &R0, clip: &R1, - options: OverlayOptions, + options: OverlayOptions, solver: Solver, ) -> Self where @@ -164,7 +173,7 @@ impl FloatOverlay

{ /// - `Contour`: A contour representing a closed path. This path is interpreted as closed, so it doesn’t require the start and endpoint to be the same for processing. /// - `Contours`: A collection of contours, each representing a closed path. /// - `Shapes`: A collection of shapes, where each shape may consist of multiple contours. - pub fn with_subj(subj: &R) -> Self + pub fn with_subj_with_int(subj: &R) -> Self where R: ShapeResource

+ ?Sized, { @@ -183,7 +192,11 @@ impl FloatOverlay

{ /// - `Shapes`: A collection of shapes, where each shape may consist of multiple contours. /// - `options`: Adjust custom behavior. /// - `solver`: Type of solver to use. - pub fn with_subj_custom(subj: &R, options: OverlayOptions, solver: Solver) -> Self + pub fn with_subj_custom_with_int( + subj: &R, + options: OverlayOptions, + solver: Solver, + ) -> Self where R: ShapeResource

+ ?Sized, { @@ -275,7 +288,7 @@ impl FloatOverlay

{ /// Convert into `FloatOverlayGraph` from the added paths or shapes using the specified build rule. This graph is the foundation for executing boolean operations, allowing for the analysis and manipulation of the geometric data. The `OverlayGraph` created by this method represents a preprocessed state of the input shapes, optimized for the application of boolean operations based on the provided build rule. /// - `fill_rule`: Specifies the rule for determining filled areas within the shapes, influencing how the resulting graph represents intersections and unions. #[inline] - pub fn build_graph_view(&mut self, fill_rule: FillRule) -> Option> { + pub fn build_graph_view(&mut self, fill_rule: FillRule) -> Option> { let graph = self.overlay.build_graph_view(fill_rule)?; Some(FloatOverlayGraph::new( graph, @@ -355,7 +368,7 @@ impl FloatOverlay

{ output: &mut FloatFlatContoursBuffer

, ) { let preserve_output_collinear = self.overlay.options.preserve_output_collinear; - let mut int_output = FlatContoursBuffer::::default(); + let mut int_output = FlatContoursBuffer::::with_capacity(0); self.overlay .overlay_into(overlay_rule, fill_rule, &mut int_output); let iter = int_output.points.iter().map(|p| self.adapter.int_to_float(p)); @@ -371,23 +384,72 @@ impl FloatOverlay

{ } } -impl Default for OverlayOptions { +impl FloatOverlay

{ + /// Creates a new `FloatOverlay` instance and initializes it with subject and clip shapes. + /// Uses the default integer engine (`i32`). + #[inline] + pub fn with_subj_and_clip(subj: &R0, clip: &R1) -> Self + where + R0: ShapeResource

+ ?Sized, + R1: ShapeResource

+ ?Sized, + { + Self::with_subj_and_clip_with_int(subj, clip) + } + + /// Creates a new `FloatOverlay` instance and initializes it with subject and clip shapes. + /// Uses the default integer engine (`i32`). + #[inline] + pub fn with_subj_and_clip_custom( + subj: &R0, + clip: &R1, + options: OverlayOptions, + solver: Solver, + ) -> Self + where + R0: ShapeResource

+ ?Sized, + R1: ShapeResource

+ ?Sized, + { + Self::with_subj_and_clip_custom_with_int(subj, clip, options, solver) + } + + /// Creates a new `FloatOverlay` instance and initializes it with subject. + /// Uses the default integer engine (`i32`). + #[inline] + pub fn with_subj(subj: &R) -> Self + where + R: ShapeResource

+ ?Sized, + { + Self::with_subj_with_int(subj) + } + + /// Creates a new `FloatOverlay` instance and initializes it with subject. + /// Uses the default integer engine (`i32`). + #[inline] + pub fn with_subj_custom(subj: &R, options: OverlayOptions, solver: Solver) -> Self + where + R: ShapeResource

+ ?Sized, + { + Self::with_subj_custom_with_int(subj, options, solver) + } +} + +impl Default for OverlayOptions { fn default() -> Self { - // f32 precision is not enough to cover i32 - let clean_result = T::BITS <= 32; + let clean_result = F::BITS <= I::BITS; Self { preserve_input_collinear: false, output_direction: ContourDirection::CounterClockwise, preserve_output_collinear: false, - min_output_area: T::from_float(0.0), + min_output_area: F::from_float(0.0), ogc: false, clean_result, + phantom_data: Default::default(), } } } -impl OverlayOptions { - pub(crate) fn int_with_adapter, I: IntNumber>( +impl OverlayOptions { + pub(crate) fn int_with_adapter>( &self, adapter: &FloatPointAdapter, ) -> IntOverlayOptions<::UInt> { @@ -400,7 +462,7 @@ impl OverlayOptions { } } - pub(crate) fn int_default(&self) -> IntOverlayOptions<::UInt> { + pub(crate) fn int_default(&self) -> IntOverlayOptions<::UInt> { IntOverlayOptions { preserve_input_collinear: self.preserve_input_collinear, output_direction: self.output_direction, @@ -411,8 +473,7 @@ impl OverlayOptions { } pub fn ogc() -> Self { - // f32 precision is not enough to cover i32 - let clean_result = T::BITS <= 32; + let clean_result = T::BITS <= I::BITS; Self { preserve_input_collinear: false, output_direction: ContourDirection::CounterClockwise, @@ -420,6 +481,7 @@ impl OverlayOptions { min_output_area: T::from_float(0.0), ogc: true, clean_result, + phantom_data: Default::default(), } } } @@ -428,7 +490,7 @@ impl OverlayOptions { mod tests { use crate::core::fill_rule::FillRule; use crate::core::overlay_rule::OverlayRule; - use crate::float::overlay::FloatOverlay; + use crate::float::overlay::{FloatOverlay, OverlayOptions}; use alloc::vec; #[test] @@ -457,6 +519,28 @@ mod tests { assert_eq!(shapes[0][0].len(), 4); } + #[test] + fn test_custom_int_engines() { + let left_rect = [[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]; + let right_rect = [[1.0, 0.0], [1.0, 1.0], [2.0, 1.0], [2.0, 0.0]]; + + let low = FloatOverlay::<[f64; 2], i16>::with_subj_and_clip_with_int(&left_rect, &right_rect) + .overlay(OverlayRule::Union, FillRule::EvenOdd); + let high = FloatOverlay::<[f64; 2], i64>::with_subj_and_clip_with_int(&left_rect, &right_rect) + .overlay(OverlayRule::Union, FillRule::EvenOdd); + + assert_eq!(low[0][0].len(), 4); + assert_eq!(high[0][0].len(), 4); + } + + #[test] + fn test_options_clean_result_depends_on_int_bits() { + assert!(!OverlayOptions::::default().clean_result); + assert!(OverlayOptions::::default().clean_result); + assert!(!OverlayOptions::::default().clean_result); + assert!(OverlayOptions::::default().clean_result); + } + #[test] fn test_contour_slice() { let left_rect = [[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]; diff --git a/iOverlay/src/float/relate.rs b/iOverlay/src/float/relate.rs index 804031c..25ff1c2 100644 --- a/iOverlay/src/float/relate.rs +++ b/iOverlay/src/float/relate.rs @@ -4,7 +4,10 @@ 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. /// @@ -26,12 +29,16 @@ use i_shape::source::resource::ShapeResource; /// /// For a more ergonomic API, see the [`FloatRelate`] trait which provides /// methods directly on shape types. -pub struct FloatPredicateOverlay { - pub(crate) overlay: PredicateOverlay, - pub(crate) adapter: FloatPointAdapter, +pub struct FloatPredicateOverlay { + pub(crate) overlay: PredicateOverlay, + pub(crate) adapter: FloatPointAdapter, } -impl FloatPredicateOverlay

{ +impl FloatPredicateOverlay +where + P: FloatPointCompatible, + I: IntNumber + Expiration + LayoutNumber + SortKey, +{ /// Creates a new predicate overlay with a pre-configured adapter. /// /// Use this when you need fixed-scale precision via `FloatPointAdapter::with_scale()`. @@ -40,7 +47,7 @@ impl FloatPredicateOverlay

{ /// * `adapter` - A `FloatPointAdapter` instance for coordinate conversion. /// * `capacity` - Initial capacity for storing segments. #[inline] - pub fn with_adapter(adapter: FloatPointAdapter, capacity: usize) -> Self { + pub fn with_adapter(adapter: FloatPointAdapter, capacity: usize) -> Self { Self { overlay: PredicateOverlay::new(capacity), adapter, @@ -58,7 +65,7 @@ impl FloatPredicateOverlay

{ /// * `capacity` - Initial capacity for storing segments. #[inline] pub fn with_adapter_custom( - adapter: FloatPointAdapter, + adapter: FloatPointAdapter, fill_rule: FillRule, solver: Solver, capacity: usize, @@ -70,13 +77,13 @@ impl FloatPredicateOverlay

{ } /// Creates a new predicate overlay from subject and clip shapes. - pub fn with_subj_and_clip(subj: &R0, clip: &R1) -> Self + pub fn with_subj_and_clip_with_int(subj: &R0, clip: &R1) -> Self where R0: ShapeResource

+ ?Sized, R1: ShapeResource

+ ?Sized, { let iter = subj.iter_paths().chain(clip.iter_paths()).flatten(); - let adapter = FloatPointAdapter::with_iter(iter); + let adapter = FloatPointAdapter::<_, I>::with_iter(iter); let subj_capacity = subj.iter_paths().fold(0, |s, c| s + c.len()); let clip_capacity = clip.iter_paths().fold(0, |s, c| s + c.len()); @@ -90,7 +97,7 @@ impl FloatPredicateOverlay

{ } /// Creates a new predicate overlay with custom solver and fill rule. - pub fn with_subj_and_clip_custom( + pub fn with_subj_and_clip_custom_with_int( subj: &R0, clip: &R1, fill_rule: FillRule, @@ -101,7 +108,7 @@ impl FloatPredicateOverlay

{ R1: ShapeResource

+ ?Sized, { let iter = subj.iter_paths().chain(clip.iter_paths()).flatten(); - let adapter = FloatPointAdapter::with_iter(iter); + let adapter = FloatPointAdapter::<_, I>::with_iter(iter); let subj_capacity = subj.iter_paths().fold(0, |s, c| s + c.len()); let clip_capacity = clip.iter_paths().fold(0, |s, c| s + c.len()); @@ -167,6 +174,35 @@ impl FloatPredicateOverlay

{ } } +impl FloatPredicateOverlay

{ + /// Creates a new predicate overlay from subject and clip shapes. + /// Uses the default integer engine (`i32`). + #[inline] + pub fn with_subj_and_clip(subj: &R0, clip: &R1) -> Self + where + R0: ShapeResource

+ ?Sized, + R1: ShapeResource

+ ?Sized, + { + Self::with_subj_and_clip_with_int(subj, clip) + } + + /// Creates a new predicate overlay with custom solver and fill rule. + /// Uses the default integer engine (`i32`). + #[inline] + pub fn with_subj_and_clip_custom( + subj: &R0, + clip: &R1, + fill_rule: FillRule, + solver: Solver, + ) -> Self + where + R0: ShapeResource

+ ?Sized, + R1: ShapeResource

+ ?Sized, + { + Self::with_subj_and_clip_custom_with_int(subj, clip, fill_rule, solver) + } +} + /// Ergonomic trait for spatial predicate operations on shape resources. /// /// This trait provides convenient methods for testing spatial relationships @@ -253,27 +289,27 @@ where { #[inline] fn intersects(&self, other: &R1) -> bool { - FloatPredicateOverlay::with_subj_and_clip(self, other).intersects() + FloatPredicateOverlay::

::with_subj_and_clip(self, other).intersects() } #[inline] fn interiors_intersect(&self, other: &R1) -> bool { - FloatPredicateOverlay::with_subj_and_clip(self, other).interiors_intersect() + FloatPredicateOverlay::

::with_subj_and_clip(self, other).interiors_intersect() } #[inline] fn touches(&self, other: &R1) -> bool { - FloatPredicateOverlay::with_subj_and_clip(self, other).touches() + FloatPredicateOverlay::

::with_subj_and_clip(self, other).touches() } #[inline] fn point_intersects(&self, other: &R1) -> bool { - FloatPredicateOverlay::with_subj_and_clip(self, other).point_intersects() + FloatPredicateOverlay::

::with_subj_and_clip(self, other).point_intersects() } #[inline] fn within(&self, other: &R1) -> bool { - FloatPredicateOverlay::with_subj_and_clip(self, other).within() + FloatPredicateOverlay::

::with_subj_and_clip(self, other).within() } #[inline] @@ -537,7 +573,7 @@ mod tests { let other = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]]; let iter = square.iter().chain(other.iter()); - let adapter = FloatPointAdapter::with_iter(iter); + let adapter = FloatPointAdapter::<_, i32>::with_iter(iter); let mut overlay = FloatPredicateOverlay::with_adapter(adapter, 16); overlay.add_source(&square, ShapeType::Subject); @@ -556,7 +592,7 @@ mod tests { let other = vec![[5.0, 5.0], [5.0, 15.0], [15.0, 15.0], [15.0, 5.0]]; let iter = square.iter().chain(other.iter()); - let adapter = FloatPointAdapter::with_iter(iter); + let adapter = FloatPointAdapter::<_, i32>::with_iter(iter); let mut overlay = FloatPredicateOverlay::with_adapter_custom(adapter, FillRule::NonZero, Solver::default(), 16); diff --git a/iOverlay/src/float/scale.rs b/iOverlay/src/float/scale.rs index db798bc..dad1ffe 100644 --- a/iOverlay/src/float/scale.rs +++ b/iOverlay/src/float/scale.rs @@ -7,8 +7,11 @@ 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 { @@ -91,12 +94,18 @@ where fill_rule: FillRule, scale: P::Scalar, ) -> Result, FixedScaleOverlayError> { - Ok(FloatOverlay::with_subj_and_clip_fixed_scale(self, source, scale)? - .overlay(overlay_rule, fill_rule)) + Ok( + FloatOverlay::

::with_subj_and_clip_fixed_scale(self, source, scale)? + .overlay(overlay_rule, fill_rule), + ) } } -impl FloatOverlay

{ +impl FloatOverlay +where + P: FloatPointCompatible, + I: IntNumber + Expiration + LayoutNumber + SortKey, +{ /// Creates a new `FloatOverlay` instance and initializes it with subject and clip shapes. /// /// This variant uses a fixed float-to-integer scale instead of auto-scaling. @@ -109,7 +118,7 @@ impl FloatOverlay

{ /// - `Contour`: A contour representing a closed path. This path is interpreted as closed, so it doesn’t require the start and endpoint to be the same for processing. /// - `Contours`: A collection of contours, each representing a closed path. /// - `Shapes`: A collection of shapes, where each shape may consist of multiple contours. - pub fn with_subj_and_clip_fixed_scale( + pub fn with_subj_and_clip_fixed_scale_with_int( subj: &R0, clip: &R1, scale: P::Scalar, @@ -143,10 +152,10 @@ impl FloatOverlay

{ /// - `Shapes`: A collection of shapes, where each shape may consist of multiple contours. /// - `options`: Adjust custom behavior. /// - `solver`: Type of solver to use. - pub fn with_subj_and_clip_fixed_scale_custom( + pub fn with_subj_and_clip_fixed_scale_custom_with_int( subj: &R0, clip: &R1, - options: OverlayOptions, + options: OverlayOptions, solver: Solver, scale: P::Scalar, ) -> Result @@ -168,7 +177,47 @@ impl FloatOverlay

{ } } -impl FloatPredicateOverlay

{ +impl FloatOverlay

{ + /// Creates a new `FloatOverlay` instance with a fixed float-to-integer scale. + /// Uses the default integer engine (`i32`). + #[inline] + pub fn with_subj_and_clip_fixed_scale( + subj: &R0, + clip: &R1, + scale: P::Scalar, + ) -> Result + where + R0: ShapeResource

+ ?Sized, + R1: ShapeResource

+ ?Sized, + { + FloatOverlay::::with_subj_and_clip_fixed_scale_with_int(subj, clip, scale) + } + + /// Creates a new `FloatOverlay` instance with a fixed float-to-integer scale. + /// Uses the default integer engine (`i32`). + #[inline] + pub fn with_subj_and_clip_fixed_scale_custom( + subj: &R0, + clip: &R1, + options: OverlayOptions, + solver: Solver, + scale: P::Scalar, + ) -> Result + where + R0: ShapeResource

+ ?Sized, + R1: ShapeResource

+ ?Sized, + { + FloatOverlay::::with_subj_and_clip_fixed_scale_custom_with_int( + subj, clip, options, solver, scale, + ) + } +} + +impl FloatPredicateOverlay +where + P: FloatPointCompatible, + I: IntNumber + Expiration + LayoutNumber + SortKey, +{ /// Creates a new predicate overlay with subject and clip shapes using fixed-scale precision. /// /// This variant uses a fixed float-to-integer scale instead of auto-scaling. @@ -180,7 +229,7 @@ impl FloatPredicateOverlay

{ /// * `subj` - A `ShapeResource` defining the subject geometry. /// * `clip` - A `ShapeResource` defining the clip geometry. /// * `scale` - Fixed float-to-integer scale factor. - pub fn with_subj_and_clip_fixed_scale( + pub fn with_subj_and_clip_fixed_scale_with_int( subj: &R0, clip: &R1, scale: P::Scalar, @@ -210,7 +259,7 @@ impl FloatPredicateOverlay

{ /// * `fill_rule` - Fill rule to determine filled areas. /// * `solver` - Type of solver to use. /// * `scale` - Fixed float-to-integer scale factor. - pub fn with_subj_and_clip_fixed_scale_custom( + pub fn with_subj_and_clip_fixed_scale_custom_with_int( subj: &R0, clip: &R1, fill_rule: FillRule, @@ -234,6 +283,42 @@ impl FloatPredicateOverlay

{ } } +impl FloatPredicateOverlay

{ + /// Creates a new predicate overlay with subject and clip shapes using fixed-scale precision. + /// Uses the default integer engine (`i32`). + #[inline] + pub fn with_subj_and_clip_fixed_scale( + subj: &R0, + clip: &R1, + scale: P::Scalar, + ) -> Result + where + R0: ShapeResource

+ ?Sized, + R1: ShapeResource

+ ?Sized, + { + FloatPredicateOverlay::::with_subj_and_clip_fixed_scale_with_int(subj, clip, scale) + } + + /// Creates a new predicate overlay with subject and clip shapes using fixed-scale precision + /// and custom fill rule and solver. Uses the default integer engine (`i32`). + #[inline] + pub fn with_subj_and_clip_fixed_scale_custom( + subj: &R0, + clip: &R1, + fill_rule: FillRule, + solver: Solver, + scale: P::Scalar, + ) -> Result + where + R0: ShapeResource

+ ?Sized, + R1: ShapeResource

+ ?Sized, + { + FloatPredicateOverlay::::with_subj_and_clip_fixed_scale_custom_with_int( + subj, clip, fill_rule, solver, scale, + ) + } +} + /// Trait for spatial predicate operations with fixed-scale precision. /// /// This trait provides methods for testing spatial relationships using a fixed @@ -297,7 +382,7 @@ where other: &R1, scale: P::Scalar, ) -> Result { - Ok(FloatPredicateOverlay::with_subj_and_clip_fixed_scale(self, other, scale)?.intersects()) + Ok(FloatPredicateOverlay::

::with_subj_and_clip_fixed_scale(self, other, scale)?.intersects()) } #[inline] @@ -306,17 +391,20 @@ where other: &R1, scale: P::Scalar, ) -> Result { - Ok(FloatPredicateOverlay::with_subj_and_clip_fixed_scale(self, other, scale)?.interiors_intersect()) + Ok( + FloatPredicateOverlay::

::with_subj_and_clip_fixed_scale(self, other, scale)? + .interiors_intersect(), + ) } #[inline] fn touches_with_fixed_scale(&self, other: &R1, scale: P::Scalar) -> Result { - Ok(FloatPredicateOverlay::with_subj_and_clip_fixed_scale(self, other, scale)?.touches()) + Ok(FloatPredicateOverlay::

::with_subj_and_clip_fixed_scale(self, other, scale)?.touches()) } #[inline] fn within_with_fixed_scale(&self, other: &R1, scale: P::Scalar) -> Result { - Ok(FloatPredicateOverlay::with_subj_and_clip_fixed_scale(self, other, scale)?.within()) + Ok(FloatPredicateOverlay::

::with_subj_and_clip_fixed_scale(self, other, scale)?.within()) } #[inline] @@ -325,12 +413,12 @@ where other: &R1, scale: P::Scalar, ) -> Result { - Ok(!FloatPredicateOverlay::with_subj_and_clip_fixed_scale(self, other, scale)?.intersects()) + Ok(!FloatPredicateOverlay::

::with_subj_and_clip_fixed_scale(self, other, scale)?.intersects()) } #[inline] fn covers_with_fixed_scale(&self, other: &R1, scale: P::Scalar) -> Result { - Ok(FloatPredicateOverlay::with_subj_and_clip_fixed_scale(other, self, scale)?.within()) + Ok(FloatPredicateOverlay::

::with_subj_and_clip_fixed_scale(other, self, scale)?.within()) } } diff --git a/iOverlay/src/float/simplify.rs b/iOverlay/src/float/simplify.rs index 74053bc..9a063a6 100644 --- a/iOverlay/src/float/simplify.rs +++ b/iOverlay/src/float/simplify.rs @@ -38,7 +38,7 @@ where { #[inline] fn simplify_shape(&self, fill_rule: FillRule) -> Shapes

{ - FloatOverlay::with_subj_custom(self, Default::default(), Default::default()) + FloatOverlay::

::with_subj_custom(self, Default::default(), Default::default()) .overlay(OverlayRule::Subject, fill_rule) } @@ -49,7 +49,7 @@ where options: OverlayOptions, solver: Solver, ) -> Shapes

{ - FloatOverlay::with_subj_custom(self, options, solver).overlay(OverlayRule::Subject, fill_rule) + FloatOverlay::

::with_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 19d7197..db62a51 100644 --- a/iOverlay/src/float/single.rs +++ b/iOverlay/src/float/single.rs @@ -34,7 +34,7 @@ where { #[inline] fn overlay(&self, resource: &R1, overlay_rule: OverlayRule, fill_rule: FillRule) -> Shapes

{ - FloatOverlay::with_subj_and_clip(self, resource).overlay(overlay_rule, fill_rule) + FloatOverlay::

::with_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 d434de7..5f764bd 100644 --- a/iOverlay/src/float/slice.rs +++ b/iOverlay/src/float/slice.rs @@ -102,7 +102,7 @@ where { #[inline] fn slice_by(&self, resource: &R0, fill_rule: FillRule) -> Shapes

{ - FloatStringOverlay::with_shape_and_string(self, resource) + FloatStringOverlay::

::with_shape_and_string(self, resource) .build_graph_view(fill_rule) .map(|graph| graph.extract_shapes(StringRule::Slice)) .unwrap_or_default() @@ -116,7 +116,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> { Ok( - FloatStringOverlay::with_shape_and_string_fixed_scale(self, resource, scale)? + FloatStringOverlay::

::with_shape_and_string_fixed_scale(self, resource, scale)? .build_graph_view(fill_rule) .map(|graph| graph.extract_shapes(StringRule::Slice)) .unwrap_or_default(), @@ -131,7 +131,7 @@ where options: OverlayOptions, solver: Solver, ) -> Shapes

{ - FloatStringOverlay::with_shape_and_string(self, resource) + FloatStringOverlay::

::with_shape_and_string(self, resource) .build_graph_view_with_solver(fill_rule, solver) .map(|graph| graph.extract_shapes_custom(StringRule::Slice, options)) .unwrap_or_default() @@ -147,7 +147,7 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError> { Ok( - FloatStringOverlay::with_shape_and_string_fixed_scale(self, resource, scale)? + FloatStringOverlay::

::with_shape_and_string_fixed_scale(self, resource, scale)? .build_graph_view_with_solver(fill_rule, solver) .map(|graph| graph.extract_shapes_custom(StringRule::Slice, options)) .unwrap_or_default(), diff --git a/iOverlay/src/float/string_graph.rs b/iOverlay/src/float/string_graph.rs index 4b73927..3abc86a 100644 --- a/iOverlay/src/float/string_graph.rs +++ b/iOverlay/src/float/string_graph.rs @@ -3,19 +3,26 @@ 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. -pub struct FloatStringGraph<'a, P: FloatPointCompatible> { - pub graph: StringGraph<'a, i32>, - pub adapter: FloatPointAdapter, +pub struct FloatStringGraph<'a, P: FloatPointCompatible, I: IntNumber = i32> { + pub graph: StringGraph<'a, I>, + pub adapter: FloatPointAdapter, } -impl FloatStringGraph<'_, P> { +impl FloatStringGraph<'_, P, I> +where + P: FloatPointCompatible, + I: IntNumber + Expiration + LayoutNumber + SortKey, +{ /// 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. /// It's suitable for most use cases where the minimum area of shapes is not a concern. @@ -59,7 +66,7 @@ impl FloatStringGraph<'_, P> { pub fn extract_shapes_custom( &self, string_rule: StringRule, - options: OverlayOptions, + options: OverlayOptions, ) -> Shapes

{ let shapes = self .graph diff --git a/iOverlay/src/float/string_overlay.rs b/iOverlay/src/float/string_overlay.rs index 85be8c2..05aa9d2 100644 --- a/iOverlay/src/float/string_overlay.rs +++ b/iOverlay/src/float/string_overlay.rs @@ -6,9 +6,12 @@ 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, @@ -16,12 +19,16 @@ use i_shape::source::resource::ShapeResource; /// /// 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(super) overlay: StringOverlay, - pub(super) adapter: FloatPointAdapter, +pub struct FloatStringOverlay { + pub(super) overlay: StringOverlay, + pub(super) adapter: FloatPointAdapter, } -impl FloatStringOverlay

{ +impl FloatStringOverlay +where + P: FloatPointCompatible, + I: IntNumber + Expiration + LayoutNumber + SortKey, +{ /// Constructs a new `FloatStringOverlay`, a builder for overlaying geometric shapes /// by converting float-based geometry to integer space, using a pre-configured adapter. /// @@ -32,7 +39,7 @@ impl FloatStringOverlay

{ /// - `capacity`: Initial capacity for storing segments, ideally matching the total number of /// segments for efficient memory allocation. #[inline] - pub fn with_adapter(adapter: FloatPointAdapter, capacity: usize) -> Self { + pub fn with_adapter(adapter: FloatPointAdapter, capacity: usize) -> Self { Self { overlay: StringOverlay::new(capacity), adapter, @@ -52,7 +59,7 @@ impl FloatStringOverlay

{ /// - `Path`: A path representing a string line. /// - `Paths`: A collection of paths, each representing a string line. /// - `Vec`: A collection of grouped paths, where each group may consist of multiple paths. - pub fn with_shape_and_string(shape: &R0, string: &R1) -> Self + pub fn with_shape_and_string_with_int(shape: &R0, string: &R1) -> Self where R0: ShapeResource

, R1: ShapeResource

, @@ -71,7 +78,7 @@ impl FloatStringOverlay

{ /// /// This variant validates that the requested scale is finite, positive, and fits the /// input bounds. Use `scale = 1.0 / grid_size` if you want a grid-size style parameter. - pub fn with_shape_and_string_fixed_scale( + pub fn with_shape_and_string_fixed_scale_with_int( shape: &R0, string: &R1, scale: P::Scalar, @@ -149,7 +156,7 @@ impl FloatStringOverlay

{ /// - `fill_rule`: Fill rule to determine filled areas (non-zero, even-odd, positive, negative). /// - Returns: A `FloatStringGraph` containing the graph representation of the overlay's geometry. #[inline] - pub fn build_graph_view(&mut self, fill_rule: FillRule) -> Option> { + pub fn build_graph_view(&mut self, fill_rule: FillRule) -> Option> { self.build_graph_view_with_solver(fill_rule, Default::default()) } @@ -163,7 +170,7 @@ impl FloatStringOverlay

{ &mut self, fill_rule: FillRule, solver: Solver, - ) -> Option> { + ) -> Option> { let graph = self.overlay.build_graph_view_with_solver(fill_rule, solver)?; Some(FloatStringGraph { graph, @@ -192,6 +199,34 @@ impl FloatStringOverlay

{ } } +impl FloatStringOverlay

{ + /// Creates a new `FloatStringOverlay` instance with shape and string paths. + /// Uses the default integer engine (`i32`). + #[inline] + pub fn with_shape_and_string(shape: &R0, string: &R1) -> Self + where + R0: ShapeResource

, + R1: ShapeResource

, + { + Self::with_shape_and_string_with_int(shape, string) + } + + /// Creates a new `FloatStringOverlay` instance with a fixed float-to-integer scale. + /// Uses the default integer engine (`i32`). + #[inline] + pub fn with_shape_and_string_fixed_scale( + shape: &R0, + string: &R1, + scale: P::Scalar, + ) -> Result + where + R0: ShapeResource

, + R1: ShapeResource

, + { + Self::with_shape_and_string_fixed_scale_with_int(shape, string, scale) + } +} + #[cfg(test)] mod tests { use crate::float::string_overlay::FloatStringOverlay; diff --git a/iOverlay/src/mesh/outline/offset.rs b/iOverlay/src/mesh/outline/offset.rs index 4813a8d..e34b648 100644 --- a/iOverlay/src/mesh/outline/offset.rs +++ b/iOverlay/src/mesh/outline/offset.rs @@ -276,7 +276,7 @@ where fn build_overlay>( &self, source: &S, - options: OverlayOptions, + options: OverlayOptions, ) -> Overlay { let total_capacity = self.outer_builder.capacity(self.points_count); let mut overlay = Overlay::new_custom( @@ -327,7 +327,7 @@ where overlay } - fn build>(self, source: &S, options: OverlayOptions) -> Shapes

{ + fn build>(self, source: &S, options: OverlayOptions) -> Shapes

{ let preserve_output_collinear = options.preserve_output_collinear; let clean_result = options.clean_result; let mut overlay = self.build_overlay(source, options); @@ -349,7 +349,7 @@ where fn build_into>( self, source: &S, - options: OverlayOptions, + options: OverlayOptions, output: &mut FloatFlatContoursBuffer

, ) { let preserve_output_collinear = options.preserve_output_collinear; diff --git a/iOverlay/src/mesh/stroke/offset.rs b/iOverlay/src/mesh/stroke/offset.rs index b94f5ab..94eae6a 100644 --- a/iOverlay/src/mesh/stroke/offset.rs +++ b/iOverlay/src/mesh/stroke/offset.rs @@ -295,7 +295,7 @@ where self, source: &S, is_closed_path: bool, - options: OverlayOptions, + options: OverlayOptions, ) -> Shapes

{ let ir = self.adapter.round_len_to_int(self.r).wide().unsigned_abs(); if ir <= <::UInt as UIntNumber>::from_u64(1) { @@ -335,7 +335,7 @@ where self, source: &S, is_closed_path: bool, - options: OverlayOptions, + options: OverlayOptions, output: &mut FloatFlatContoursBuffer

, ) { let ir = self.adapter.round_len_to_int(self.r).wide().unsigned_abs(); diff --git a/iOverlay/tests/float_overlay_tests.rs b/iOverlay/tests/float_overlay_tests.rs index c450e80..160af83 100644 --- a/iOverlay/tests/float_overlay_tests.rs +++ b/iOverlay/tests/float_overlay_tests.rs @@ -279,11 +279,12 @@ mod tests { fn test_empty_0() { let path = vec![FPoint::new(-10.0, -10.0), FPoint::new(-10.0, 10.0)]; - let shapes = FloatOverlay::with_adapter(FloatPointAdapter::with_iter(path.iter()), path.len()) - .build_graph_view(FillRule::NonZero) - .map_or(Default::default(), |graph| { - graph.extract_shapes(OverlayRule::Subject, &mut Default::default()) - }); + let shapes = + FloatOverlay::with_adapter(FloatPointAdapter::<_, i32>::with_iter(path.iter()), path.len()) + .build_graph_view(FillRule::NonZero) + .map_or(Default::default(), |graph| { + graph.extract_shapes(OverlayRule::Subject, &mut Default::default()) + }); assert_eq!(shapes.is_empty(), true); } @@ -298,13 +299,15 @@ mod tests { ] .to_vec()]; - let shapes = - FloatOverlay::with_adapter(FloatPointAdapter::with_iter(shape.iter().flatten()), shape.len()) - .unsafe_add_source(&shape, ShapeType::Subject) - .build_graph_view(FillRule::NonZero) - .map_or(Default::default(), |graph| { - graph.extract_shapes(OverlayRule::Subject, &mut Default::default()) - }); + let shapes = FloatOverlay::with_adapter( + FloatPointAdapter::<_, i32>::with_iter(shape.iter().flatten()), + shape.len(), + ) + .unsafe_add_source(&shape, ShapeType::Subject) + .build_graph_view(FillRule::NonZero) + .map_or(Default::default(), |graph| { + graph.extract_shapes(OverlayRule::Subject, &mut Default::default()) + }); assert_eq!(shapes.len(), 1); assert_eq!(shapes[0].len(), 1); @@ -358,12 +361,13 @@ mod tests { #[test] fn test_empty_4() { let path = vec![FPoint::new(0.0, 0.0)]; - let shapes = FloatOverlay::with_adapter(FloatPointAdapter::with_iter(path.iter()), path.len()) - .unsafe_add_contour(&path, ShapeType::Subject) - .build_graph_view(FillRule::NonZero) - .map_or(Default::default(), |graph| { - graph.extract_shapes(OverlayRule::Subject, &mut Default::default()) - }); + let shapes = + FloatOverlay::with_adapter(FloatPointAdapter::<_, i32>::with_iter(path.iter()), path.len()) + .unsafe_add_contour(&path, ShapeType::Subject) + .build_graph_view(FillRule::NonZero) + .map_or(Default::default(), |graph| { + graph.extract_shapes(OverlayRule::Subject, &mut Default::default()) + }); assert_eq!(shapes.len(), 0); } @@ -383,12 +387,13 @@ mod tests { #[test] fn test_empty_6() { let path = vec![FPoint::new(0.0, 0.0), FPoint::new(1.0, 0.0)]; - let shapes = FloatOverlay::with_adapter(FloatPointAdapter::with_iter(path.iter()), path.len()) - .unsafe_add_contour(&path, ShapeType::Subject) - .build_graph_view(FillRule::NonZero) - .map_or(Default::default(), |graph| { - graph.extract_shapes(OverlayRule::Subject, &mut Default::default()) - }); + let shapes = + FloatOverlay::with_adapter(FloatPointAdapter::<_, i32>::with_iter(path.iter()), path.len()) + .unsafe_add_contour(&path, ShapeType::Subject) + .build_graph_view(FillRule::NonZero) + .map_or(Default::default(), |graph| { + graph.extract_shapes(OverlayRule::Subject, &mut Default::default()) + }); assert_eq!(shapes.len(), 0); } @@ -698,14 +703,13 @@ mod tests { let result_no_filter = FloatOverlay::with_subj_and_clip(&shape_0, &shape_1) .overlay(OverlayRule::Intersect, FillRule::EvenOdd); - let opt = OverlayOptions { - preserve_input_collinear: false, - output_direction: ContourDirection::CounterClockwise, - preserve_output_collinear: false, - min_output_area: 0.0, - ogc: false, - clean_result: false, - }; + let mut opt = OverlayOptions::default(); + opt.preserve_input_collinear = false; + opt.output_direction = ContourDirection::CounterClockwise; + opt.preserve_output_collinear = false; + opt.min_output_area = 0.0; + opt.ogc = false; + opt.clean_result = false; let result_with_filter = FloatOverlay::with_subj_and_clip_custom(&shape_0, &shape_1, opt, Default::default()) diff --git a/iOverlay/tests/float_point_adapter.rs b/iOverlay/tests/float_point_adapter.rs index 22d469e..3580ba8 100644 --- a/iOverlay/tests/float_point_adapter.rs +++ b/iOverlay/tests/float_point_adapter.rs @@ -18,8 +18,8 @@ mod tests { [s * 1.0, s * 0.0], ]]; - let adapter_100 = FloatPointAdapter::new(FloatRect::new(-100.0, 100.0, -100.0, 100.0)); - let adapter_1000 = FloatPointAdapter::new(FloatRect::new(-1000.0, 1000.0, -1000.0, 1000.0)); + let adapter_100 = FloatPointAdapter::<_, i32>::new(FloatRect::new(-100.0, 100.0, -100.0, 100.0)); + let adapter_1000 = FloatPointAdapter::<_, i32>::new(FloatRect::new(-1000.0, 1000.0, -1000.0, 1000.0)); let subj_100 = FloatOverlay::with_adapter(adapter_100, shape.len()) .unsafe_add_source(&shape, ShapeType::Subject) @@ -58,8 +58,8 @@ mod tests { rect.max_y + 0.1, ); - let adapter_100 = FloatPointAdapter::with_scale(buffer_rect.clone(), 100.0); - let adapter_1000 = FloatPointAdapter::with_scale(buffer_rect, 1000.0); + let adapter_100 = FloatPointAdapter::<_, i32>::with_scale(buffer_rect.clone(), 100.0); + let adapter_1000 = FloatPointAdapter::<_, i32>::with_scale(buffer_rect, 1000.0); let subj_100 = FloatOverlay::with_adapter(adapter_100, shape.len()) .unsafe_add_source(&shape, ShapeType::Subject) From e65c1757e0156986aa7ca177d11c394fd7eb7dc8 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Thu, 21 May 2026 12:28:27 +0300 Subject: [PATCH 13/36] convert performance app --- performance/rust_app/Cargo.toml | 4 +- performance/rust_app/src/main.rs | 57 +++++------- .../rust_app/src/test/test_0_checkerboard.rs | 15 +--- .../rust_app/src/test/test_1_not_overlap.rs | 15 +--- .../rust_app/src/test/test_2_lines_net.rs | 15 +--- .../rust_app/src/test/test_4_windows.rs | 15 +--- .../src/test/test_5_nested_squares.rs | 15 +--- .../rust_app/src/test/test_8_wind_mill.rs | 89 +++++++++---------- performance/rust_app/src/test/util.rs | 45 +++++----- 9 files changed, 113 insertions(+), 157 deletions(-) diff --git a/performance/rust_app/Cargo.toml b/performance/rust_app/Cargo.toml index c9b82da..491504b 100644 --- a/performance/rust_app/Cargo.toml +++ b/performance/rust_app/Cargo.toml @@ -11,5 +11,5 @@ codegen-units = 1 [dependencies] #i_overlay = { path = "../../iOverlay", default-features = true } -#i_overlay = { path = "../../iOverlay", features = ["allow_multithreading"] } -i_overlay = "6.0.0" \ No newline at end of file +i_overlay = { path = "../../iOverlay", features = ["allow_multithreading"] } +#i_overlay = "6.0.0" \ No newline at end of file diff --git a/performance/rust_app/src/main.rs b/performance/rust_app/src/main.rs index 58c2721..b83a97f 100644 --- a/performance/rust_app/src/main.rs +++ b/performance/rust_app/src/main.rs @@ -40,26 +40,17 @@ fn main() { args_map.insert("test".to_string(), 7.to_string()); let count = 32; args_map.insert("count".to_string(), count.to_string()); - args_map.insert("geom".to_string(), true.to_string()); } } let test_key = args_map.get("test").expect("Test number is not set"); let multithreading_key = args_map.get("multithreading").expect("Multithreading is not set"); let complex_key = args_map.get("complex").expect("Complex is not set"); - let geom_key = args_map.get("geom"); let test: usize = test_key.parse().expect("Unable to parse test as an integer"); let multithreading: bool = multithreading_key.parse().expect("Unable to parse multithreading as an boolean"); let complex: bool = complex_key.parse().expect("Unable to parse complex as an boolean"); - let geom = if let Some(key) = geom_key { - let value: bool = key.parse().expect("Unable to parse complex as an boolean"); - value - } else { - false - }; - let multithreading = if multithreading { Some(MultithreadOptions::default()) } else { @@ -71,22 +62,22 @@ fn main() { if complex { match test { 0 => { - run_test_0(geom, solver); + run_test_0(solver); } 1 => { - run_test_1(geom, solver); + run_test_1(solver); } 2 => { - run_test_2(geom, solver); + run_test_2(solver); } 3 => { run_test_3(); } 4 => { - run_test_4(geom, solver); + run_test_4(solver); } 5 => { - run_test_5(geom, solver); + run_test_5(solver); } 6 => { run_test_6(solver); @@ -95,7 +86,7 @@ fn main() { run_test_7(solver); } 8 => { - run_test_8(geom, solver); + run_test_8(solver); } _ => { println!("Test is not found"); @@ -106,22 +97,22 @@ fn main() { let count: usize = count_key.parse().expect("Unable to parse count as an integer"); match test { 0 => { - CheckerboardTest::run(count, OverlayRule::Xor, solver, 1.0, geom); + CheckerboardTest::run(count, OverlayRule::Xor, solver, 1.0); } 1 => { - NotOverlapTest::run(count, OverlayRule::Union, solver, 1.0, geom); + NotOverlapTest::run(count, OverlayRule::Union, solver, 1.0); } 2 => { - LinesNetTest::run(count, OverlayRule::Intersect, solver, 1.0, geom); + LinesNetTest::run(count, OverlayRule::Intersect, solver, 1.0); } 3 => { SpiralTest::run(count, 100.0); } 4 => { - WindowsTest::run(count, OverlayRule::Difference, solver, 1.0, geom); + WindowsTest::run(count, OverlayRule::Difference, solver, 1.0); } 5 => { - CrossTest::run(count, OverlayRule::Xor, solver, 1.0, geom); + CrossTest::run(count, OverlayRule::Xor, solver, 1.0); } 6 => { CorrosionTest::run(count, OverlayRule::Difference, solver, 1.0); @@ -130,7 +121,7 @@ fn main() { ConcentricTest::run(count, OverlayRule::Intersect, solver, 1.0); } 8 => { - WindMillTest::run(count, OverlayRule::Intersect, solver, 1.0, geom); + WindMillTest::run(count, OverlayRule::Intersect, solver, 1.0); } _ => { println!("Test is not found"); @@ -139,27 +130,27 @@ fn main() { } } -fn run_test_0(geom: bool, solver: Solver) { +fn run_test_0(solver: Solver) { println!("run Checkerboard test"); for i in 1..12 { let n = 1 << i; - CheckerboardTest::run(n, OverlayRule::Xor, solver, 1000.0, geom); + CheckerboardTest::run(n, OverlayRule::Xor, solver, 1000.0); } } -fn run_test_1(geom: bool, solver: Solver) { +fn run_test_1(solver: Solver) { println!("run NotOverlap test"); for i in 1..12 { let n = 1 << i; - NotOverlapTest::run(n, OverlayRule::Xor, solver, 1000.0, geom); + NotOverlapTest::run(n, OverlayRule::Xor, solver, 1000.0); } } -fn run_test_2(geom: bool, solver: Solver) { +fn run_test_2(solver: Solver) { println!("run LinesNet test"); for i in 1..12 { let n = 1 << i; - LinesNetTest::run(n, OverlayRule::Intersect, solver, 500.0, geom); + LinesNetTest::run(n, OverlayRule::Intersect, solver, 500.0); } } @@ -171,19 +162,19 @@ fn run_test_3() { } } -fn run_test_4(geom: bool, solver: Solver) { +fn run_test_4(solver: Solver) { println!("run Windows test"); for i in 1..12 { let n = 1 << i; - WindowsTest::run(n, OverlayRule::Difference, solver, 500.0, geom); + WindowsTest::run(n, OverlayRule::Difference, solver, 500.0); } } -fn run_test_5(geom: bool, solver: Solver) { +fn run_test_5(solver: Solver) { println!("run NestedSquares test"); for i in 1..18 { let n = 1 << i; - CrossTest::run(n, OverlayRule::Xor, solver, 500.0, geom); + CrossTest::run(n, OverlayRule::Xor, solver, 500.0); } } @@ -205,11 +196,11 @@ fn run_test_7(solver: Solver) { } } -fn run_test_8(geom: bool, solver: Solver) { +fn run_test_8(solver: Solver) { println!("run WindMill test"); let mut n = 1; for _ in 1..12 { - WindMillTest::run(n, OverlayRule::Difference, solver, 100.0, geom); + WindMillTest::run(n, OverlayRule::Difference, solver, 100.0); n = n << 1; } WindMillTest::validate(100, OverlayRule::Difference, solver); diff --git a/performance/rust_app/src/test/test_0_checkerboard.rs b/performance/rust_app/src/test/test_0_checkerboard.rs index 7e1e30a..4caeee8 100644 --- a/performance/rust_app/src/test/test_0_checkerboard.rs +++ b/performance/rust_app/src/test/test_0_checkerboard.rs @@ -43,7 +43,7 @@ multithreading off // A grid of overlapping squares forming a simple checkerboard pattern. impl CheckerboardTest { - pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64, simple_geometry: bool) { // 1000 + pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { // 1000 let subj_paths = Util::many_squares(IntPoint::new(0, 0), 20, 30, n); let clip_paths = Util::many_squares(IntPoint::new(15, 15), 20, 30, n - 1); @@ -52,16 +52,9 @@ impl CheckerboardTest { let start = Instant::now(); - if simple_geometry { - // for _i in 0..sq_it_count { - // let _ = Overlay::with_contours(&subj_paths, &clip_paths) - // .overlay_45geom_with_min_area_and_solver(rule, FillRule::NonZero, 0, solver); - // } - } else { - for _i in 0..sq_it_count { - let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) - .overlay(rule, FillRule::NonZero); - } + for _i in 0..sq_it_count { + let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) + .overlay(rule, FillRule::NonZero); } let duration = start.elapsed(); diff --git a/performance/rust_app/src/test/test_1_not_overlap.rs b/performance/rust_app/src/test/test_1_not_overlap.rs index 844b78c..83ad329 100644 --- a/performance/rust_app/src/test/test_1_not_overlap.rs +++ b/performance/rust_app/src/test/test_1_not_overlap.rs @@ -44,7 +44,7 @@ multithreading off // A grid of not overlapping squares. impl NotOverlapTest { - pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64, simple_geometry: bool) { // 1000 + pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { // 1000 let subj_paths = Util::many_squares(IntPoint::new(0, 0), 10, 30, n); let clip_paths = Util::many_squares(IntPoint::new(15, 15), 10, 30, n - 1); @@ -53,16 +53,9 @@ impl NotOverlapTest { let start = Instant::now(); - if simple_geometry { - // for _i in 0..sq_it_count { - // let _ = Overlay::with_contours(&subj_paths, &clip_paths) - // .overlay_45geom_with_min_area_and_solver(rule, FillRule::NonZero, 0, solver); - // } - } else { - for _i in 0..sq_it_count { - let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) - .overlay(rule, FillRule::NonZero); - } + for _i in 0..sq_it_count { + let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) + .overlay(rule, FillRule::NonZero); } let duration = start.elapsed(); diff --git a/performance/rust_app/src/test/test_2_lines_net.rs b/performance/rust_app/src/test/test_2_lines_net.rs index 14142cb..a60d388 100644 --- a/performance/rust_app/src/test/test_2_lines_net.rs +++ b/performance/rust_app/src/test/test_2_lines_net.rs @@ -72,7 +72,7 @@ geom multithreading off // A grid is formed by the intersection of a set of vertical and horizontal lines. impl LinesNetTest { - pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64, simple_geometry: bool) { // 500 + pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { // 500 let subj_paths = Util::many_lines_x(20, n); let clip_paths = Util::many_lines_y(20, n); @@ -81,16 +81,9 @@ impl LinesNetTest { let start = Instant::now(); - if simple_geometry { - // for _ in 0..sq_it_count { - // let _ = Overlay::with_contours(&subj_paths, &clip_paths) - // .overlay_45geom_with_min_area_and_solver(rule, FillRule::NonZero, 0, solver); - // } - } else { - for _ in 0..sq_it_count { - let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) - .overlay(rule, FillRule::NonZero); - } + for _ in 0..sq_it_count { + let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) + .overlay(rule, FillRule::NonZero); } let duration = start.elapsed(); diff --git a/performance/rust_app/src/test/test_4_windows.rs b/performance/rust_app/src/test/test_4_windows.rs index c72e9af..323d9ab 100644 --- a/performance/rust_app/src/test/test_4_windows.rs +++ b/performance/rust_app/src/test/test_4_windows.rs @@ -41,7 +41,7 @@ pub(crate) struct WindowsTest; // A grid of square frames, each with a smaller square cutout in the center. impl WindowsTest { - pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64, simple_geometry: bool) { // 500 + pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { // 500 let offset = 30; let x = (n as i32) * offset / 2; let origin = IntPoint::new(-x, -x); @@ -52,16 +52,9 @@ impl WindowsTest { let start = Instant::now(); - if simple_geometry { - // for _ in 0..sq_it_count { - // let _ = Overlay::with_contours(&subj_paths, &clip_paths) - // .overlay_45geom_with_min_area_and_solver(rule, FillRule::NonZero, 0, solver); - // } - } else { - for _ in 0..sq_it_count { - let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) - .overlay(rule, FillRule::NonZero); - } + for _ in 0..sq_it_count { + let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) + .overlay(rule, FillRule::NonZero); } let duration = start.elapsed(); diff --git a/performance/rust_app/src/test/test_5_nested_squares.rs b/performance/rust_app/src/test/test_5_nested_squares.rs index e8c8b9e..195d423 100644 --- a/performance/rust_app/src/test/test_5_nested_squares.rs +++ b/performance/rust_app/src/test/test_5_nested_squares.rs @@ -54,7 +54,7 @@ pub(crate) struct CrossTest; // A series of concentric squares, each progressively larger than the last. impl CrossTest { - pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64, simple_geometry: bool) { // 500 + pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { // 500 let (subj_paths, clip_paths) = Util::concentric_squares(4, n); let it_count = ((scale / (n as f64)) as usize).max(1); @@ -62,16 +62,9 @@ impl CrossTest { let start = Instant::now(); - if simple_geometry { - // for _ in 0..sq_it_count { - // let _ = Overlay::with_contours(&subj_paths, &clip_paths) - // .overlay_45geom_with_min_area_and_solver(rule, FillRule::NonZero, 0, solver); - // } - } else { - for _ in 0..sq_it_count { - let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) - .overlay(rule, FillRule::NonZero); - } + for _ in 0..sq_it_count { + let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) + .overlay(rule, FillRule::NonZero); } let duration = start.elapsed(); let time = duration.as_secs_f64() / sq_it_count as f64; diff --git a/performance/rust_app/src/test/test_8_wind_mill.rs b/performance/rust_app/src/test/test_8_wind_mill.rs index e2c8617..d04f6c1 100644 --- a/performance/rust_app/src/test/test_8_wind_mill.rs +++ b/performance/rust_app/src/test/test_8_wind_mill.rs @@ -3,6 +3,7 @@ use i_overlay::core::fill_rule::FillRule; use i_overlay::core::overlay::Overlay; use i_overlay::core::overlay_rule::OverlayRule; use i_overlay::core::solver::Solver; +use i_overlay::i_float::int::number::int::IntNumber; use i_overlay::i_float::int::point::IntPoint; use i_overlay::i_shape::int::shape::IntContour; @@ -40,7 +41,7 @@ pub(crate) struct WindMillTest; */ impl WindMillTest { - pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64, simple_geometry: bool) { + pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { let (subj_paths, clip_paths) = Self::geometry(80, n); let it_count = ((scale / (n as f64)) as usize).max(1); @@ -48,16 +49,9 @@ impl WindMillTest { let start = Instant::now(); - if simple_geometry { - // for _ in 0..sq_it_count { - // let _ = Overlay::with_contours(&subj_paths, &clip_paths) - // .overlay_45geom_with_min_area_and_solver(rule, FillRule::NonZero, 0, solver); - // } - } else { - for _ in 0..sq_it_count { - let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) - .overlay(rule, FillRule::NonZero); - } + for _ in 0..sq_it_count { + let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) + .overlay(rule, FillRule::NonZero); } let duration = start.elapsed(); @@ -67,7 +61,7 @@ impl WindMillTest { } pub(crate) fn validate(n: usize, rule: OverlayRule, solver: Solver) { - let (subj_paths, clip_paths) = Self::geometry(80, n); + let (subj_paths, clip_paths) = Self::geometry(80i32, n); let res = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) .overlay(rule, FillRule::NonZero); @@ -76,15 +70,15 @@ impl WindMillTest { println!("result validation PASS"); } - fn geometry(size: i32, count: usize) -> (Vec, Vec) { + fn geometry(size: I, count: usize) -> (Vec>, Vec>) { let mut subj_paths = Vec::with_capacity(4 * count * count); let mut clip_paths = Vec::with_capacity(4 * count * count); - let a = size / 8; + let a = size / I::from_usize(8); - let mut x = size / 2; + let mut x = size / I::TWO; for _ in 0..count { - let mut y = size / 2; + let mut y = size / I::TWO; for _ in 0..count { let (subj, clip) = Self::shapes(IntPoint::new(x, y), a); @@ -100,54 +94,59 @@ impl WindMillTest { (subj_paths, clip_paths) } - fn shapes(center: IntPoint, a: i32) -> (Vec, Vec) { + fn shapes(center: IntPoint, a: I) -> (Vec>, Vec>) { + let i0 = I::from_usize(0); + let i1 = I::from_usize(1); + let i2 = I::from_usize(2); + let i3 = I::from_usize(3); + let i4 = I::from_usize(4); let clip_paths = vec![ vec![ - IntPoint::new(-3 * a + center.x, 1 * a + center.y), - IntPoint::new(-3 * a + center.x, 3 * a + center.y), - IntPoint::new(-1 * a + center.x, 3 * a + center.y), - IntPoint::new(-1 * a + center.x, 1 * a + center.y), + IntPoint::new(-i3 * a + center.x, i1 * a + center.y), + IntPoint::new(-i3 * a + center.x, i3 * a + center.y), + IntPoint::new(-i1 * a + center.x, i3 * a + center.y), + IntPoint::new(-i1 * a + center.x, i1 * a + center.y), ], vec![ - IntPoint::new(1 * a + center.x, 2 * a + center.y), - IntPoint::new(1 * a + center.x, 4 * a + center.y), - IntPoint::new(3 * a + center.x, 4 * a + center.y), - IntPoint::new(3 * a + center.x, 2 * a + center.y), + IntPoint::new(i1 * a + center.x, i2 * a + center.y), + IntPoint::new(i1 * a + center.x, i4 * a + center.y), + IntPoint::new(i3 * a + center.x, i4 * a + center.y), + IntPoint::new(i3 * a + center.x, i2 * a + center.y), ], vec![ - IntPoint::new(-2 * a + center.x, -3 * a + center.y), - IntPoint::new(-2 * a + center.x, -1 * a + center.y), - IntPoint::new(0 * a + center.x, -1 * a + center.y), - IntPoint::new(0 * a + center.x, -3 * a + center.y), + IntPoint::new(-i2 * a + center.x, -i3 * a + center.y), + IntPoint::new(-i2 * a + center.x, -i1 * a + center.y), + IntPoint::new(i0 * a + center.x, -i1 * a + center.y), + IntPoint::new(i0 * a + center.x, -i3 * a + center.y), ], vec![ - IntPoint::new(2 * a + center.x, -2 * a + center.y), - IntPoint::new(2 * a + center.x, 0 * a + center.y), - IntPoint::new(4 * a + center.x, 0 * a + center.y), - IntPoint::new(4 * a + center.x, -2 * a + center.y), + IntPoint::new(i2 * a + center.x, -i2 * a + center.y), + IntPoint::new(i2 * a + center.x, i0 * a + center.y), + IntPoint::new(i4 * a + center.x, i0 * a + center.y), + IntPoint::new(i4 * a + center.x, -i2 * a + center.y), ], ]; let subj_paths = vec![ vec![ - IntPoint::new(0 * a + center.x, 0 * a + center.y), - IntPoint::new(-3 * a + center.x, 0 * a + center.y), - IntPoint::new(0 * a + center.x, 3 * a + center.y), + IntPoint::new(i0 * a + center.x, i0 * a + center.y), + IntPoint::new(-i3 * a + center.x, i0 * a + center.y), + IntPoint::new(i0 * a + center.x, i3 * a + center.y), ], vec![ - IntPoint::new(0 * a + center.x, 1 * a + center.y), - IntPoint::new(0 * a + center.x, 4 * a + center.y), - IntPoint::new(3 * a + center.x, 1 * a + center.y), + IntPoint::new(i0 * a + center.x, i1 * a + center.y), + IntPoint::new(i0 * a + center.x, i4 * a + center.y), + IntPoint::new(i3 * a + center.x, i1 * a + center.y), ], vec![ - IntPoint::new(1 * a + center.x, 0 * a + center.y), - IntPoint::new(1 * a + center.x, -3 * a + center.y), - IntPoint::new(-2 * a + center.x, 0 * a + center.y), + IntPoint::new(i1 * a + center.x, i0 * a + center.y), + IntPoint::new(i1 * a + center.x, -i3 * a + center.y), + IntPoint::new(-i2 * a + center.x, i0 * a + center.y), ], vec![ - IntPoint::new(1 * a + center.x, 1 * a + center.y), - IntPoint::new(4 * a + center.x, 1 * a + center.y), - IntPoint::new(1 * a + center.x, -2 * a + center.y), + IntPoint::new(i1 * a + center.x, i1 * a + center.y), + IntPoint::new(i4 * a + center.x, i1 * a + center.y), + IntPoint::new(i1 * a + center.x, -i2 * a + center.y), ], ]; diff --git a/performance/rust_app/src/test/util.rs b/performance/rust_app/src/test/util.rs index 2c18ae1..fce5b18 100644 --- a/performance/rust_app/src/test/util.rs +++ b/performance/rust_app/src/test/util.rs @@ -1,5 +1,6 @@ use std::f64::consts::PI; use i_overlay::i_float::float::point::FloatPoint; +use i_overlay::i_float::int::number::int::IntNumber; use i_overlay::i_float::int::point::IntPoint; use i_overlay::i_shape::base::data::Contour; use i_overlay::i_shape::int::path::IntPath; @@ -8,13 +9,13 @@ pub(super) struct Util; impl Util { - pub(super) fn many_squares(start: IntPoint, size: i32, offset: i32, n: usize) -> Vec { + pub(super) fn many_squares(start: IntPoint, size: I, offset: I, n: usize) -> Vec> { let mut result = Vec::with_capacity(n * n); let mut y = start.y; for _ in 0..n { let mut x = start.x; for _ in 0..n { - let path: IntPath = vec![ + let path: IntPath = vec![ IntPoint::new(x, y), IntPoint::new(x, y + size), IntPoint::new(x + size, y + size), @@ -30,16 +31,16 @@ impl Util { } - pub(super) fn many_windows(start: IntPoint, a: i32, b: i32, offset: i32, n: usize) -> (Vec, Vec) { + pub(super) fn many_windows(start: IntPoint, a: I, b: I, offset: I, n: usize) -> (Vec>, Vec>) { let mut boundaries = Vec::with_capacity(n * n); let mut holes = Vec::with_capacity(n * n); let mut y = start.y; - let c = (a - b) / 2; + let c = (a - b) / I::TWO; let d = b + c; for _ in 0..n { let mut x = start.x; for _ in 0..n { - let boundary: IntPath = vec![ + let boundary: IntPath = vec![ IntPoint::new(x, y), IntPoint::new(x, y + a), IntPoint::new(x + a, y + a), @@ -47,7 +48,7 @@ impl Util { ]; boundaries.push(boundary); - let hole: IntPath = vec![ + let hole: IntPath = vec![ IntPoint::new(x + c, y + c), IntPoint::new(x + c, y + d), IntPoint::new(x + d, y + d), @@ -63,19 +64,19 @@ impl Util { (boundaries, holes) } - pub(super) fn concentric_squares(a: i32, n: usize) -> (Vec, Vec) { + pub(super) fn concentric_squares(a: I, n: usize) -> (Vec>, Vec>) { let mut vert = Vec::with_capacity(2 * n); let mut horz = Vec::with_capacity(2 * n); - let s = 2 * a; + let s = I::TWO * a; let mut r = s; for _ in 0..n { - let hz_top: IntPath = vec![ + let hz_top: IntPath = vec![ IntPoint::new(-r, r - a), IntPoint::new(-r, r), IntPoint::new(r, r), IntPoint::new(r, r - a), ]; - let hz_bot: IntPath = vec![ + let hz_bot: IntPath = vec![ IntPoint::new(-r, -r), IntPoint::new(-r, -r + a), IntPoint::new(r, -r + a), @@ -84,13 +85,13 @@ impl Util { horz.push(hz_top); horz.push(hz_bot); - let vt_left: IntPath = vec![ + let vt_left: IntPath = vec![ IntPoint::new(-r, -r), IntPoint::new(-r, r), IntPoint::new(-r + a, r), IntPoint::new(-r + a, -r), ]; - let vt_right: IntPath = vec![ + let vt_right: IntPath = vec![ IntPoint::new(r - a, -r), IntPoint::new(r - a, r), IntPoint::new(r, r), @@ -105,13 +106,13 @@ impl Util { (vert, horz) } - pub(super) fn many_lines_x(a: i32, n: usize) -> Vec { - let w = a / 2; - let s = a * (n as i32) / 2; - let mut x = -s + w / 2; + pub(super) fn many_lines_x(a: I, n: usize) -> Vec> { + let w = a / I::TWO; + let s = a * I::from_usize(n) / I::TWO; + let mut x = -s + w / I::TWO; let mut result = Vec::with_capacity(n); for _ in 0..n { - let path: IntPath = vec![ + let path: IntPath = vec![ IntPoint::new(x, -s), IntPoint::new(x, s), IntPoint::new(x + w, s), @@ -124,13 +125,13 @@ impl Util { result } - pub(super) fn many_lines_y(a: i32, n: usize) -> Vec { - let h = a / 2; - let s = a * (n as i32) / 2; - let mut y = -s + h / 2; + pub(super) fn many_lines_y(a: I, n: usize) -> Vec> { + let h = a / I::TWO; + let s = a * I::from_usize(n) / I::TWO; + let mut y = -s + h / I::TWO; let mut result = Vec::with_capacity(n); for _ in 0..n { - let path: IntPath = vec![ + let path: IntPath = vec![ IntPoint::new(-s, y), IntPoint::new(s, y), IntPoint::new(s, y - h), From c3f7322a5846d0318b0bbcf4f30916c4fce32181 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Thu, 21 May 2026 16:33:17 +0300 Subject: [PATCH 14/36] update performance app --- performance/rust_app/Cargo.toml | 4 +- performance/rust_app/run.sh | 2 +- performance/rust_app/src/main.rs | 173 ++++++++++++------ performance/rust_app/src/test/mod.rs | 2 +- .../rust_app/src/test/test_0_checkerboard.rs | 39 +++- .../rust_app/src/test/test_1_not_overlap.rs | 34 +++- .../rust_app/src/test/test_2_lines_net.rs | 24 ++- .../rust_app/src/test/test_3_spiral.rs | 23 ++- .../rust_app/src/test/test_4_windows.rs | 27 ++- .../src/test/test_5_nested_squares.rs | 22 ++- .../rust_app/src/test/test_6_corrosion.rs | 14 +- .../rust_app/src/test/test_7_concentric.rs | 14 +- .../rust_app/src/test/test_8_wind_mill.rs | 27 ++- performance/rust_app/src/test/util.rs | 56 +++++- 14 files changed, 327 insertions(+), 134 deletions(-) diff --git a/performance/rust_app/Cargo.toml b/performance/rust_app/Cargo.toml index 491504b..920cba1 100644 --- a/performance/rust_app/Cargo.toml +++ b/performance/rust_app/Cargo.toml @@ -12,4 +12,6 @@ codegen-units = 1 #i_overlay = { path = "../../iOverlay", default-features = true } i_overlay = { path = "../../iOverlay", features = ["allow_multithreading"] } -#i_overlay = "6.0.0" \ No newline at end of file +#i_overlay = "6.0.0" +i_key_sort = "^0.10.1" +i_tree = { path = "../../../iTree" } diff --git a/performance/rust_app/run.sh b/performance/rust_app/run.sh index 6c36cf2..1969e79 100755 --- a/performance/rust_app/run.sh +++ b/performance/rust_app/run.sh @@ -1,3 +1,3 @@ #!/bin/bash -target/release/rust_app --multithreading false --complex true --test 0 \ No newline at end of file +target/release/rust_app --multithreading false --complex true --test 0 --int all diff --git a/performance/rust_app/src/main.rs b/performance/rust_app/src/main.rs index b83a97f..2e41db2 100644 --- a/performance/rust_app/src/main.rs +++ b/performance/rust_app/src/main.rs @@ -1,7 +1,3 @@ -use std::env; -use std::collections::HashMap; -use i_overlay::core::overlay_rule::OverlayRule; -use i_overlay::core::solver::{MultithreadOptions, Precision, Solver, Strategy}; use crate::test::test_0_checkerboard::CheckerboardTest; use crate::test::test_1_not_overlap::NotOverlapTest; use crate::test::test_2_lines_net::LinesNetTest; @@ -11,24 +7,54 @@ use crate::test::test_5_nested_squares::CrossTest; use crate::test::test_6_corrosion::CorrosionTest; use crate::test::test_7_concentric::ConcentricTest; use crate::test::test_8_wind_mill::WindMillTest; +use i_overlay::core::overlay_rule::OverlayRule; +use i_overlay::core::solver::{MultithreadOptions, Precision, Solver, Strategy}; +use std::collections::HashMap; +use std::env; mod test; +#[derive(Clone, Copy)] +enum IntEngine { + I16, + I32, + I64, +} + +impl IntEngine { + fn parse(value: &str) -> Vec { + match value { + "i16" | "16" => vec![Self::I16], + "i32" | "32" => vec![Self::I32], + "i64" | "64" => vec![Self::I64], + "all" => vec![Self::I16, Self::I32, Self::I64], + _ => panic!("Unknown int engine: {value}. Use i16, i32, i64, or all"), + } + } + + fn label(self) -> &'static str { + match self { + Self::I16 => "i16", + Self::I32 => "i32", + Self::I64 => "i64", + } + } +} + fn main() { let args = env::args(); let mut args_iter = args.peekable(); let mut args_map = HashMap::new(); while let Some(arg) = args_iter.next() { - if arg.starts_with("--") { - let key = arg.trim_start_matches("--").to_owned(); + if let Some(key) = arg.strip_prefix("--").or_else(|| arg.strip_prefix('-')) { // If the next argument is also a key, store a boolean flag; otherwise, store the value. - let value = if args_iter.peek().map_or(false, |a| a.starts_with("--")) { + let value = if args_iter.peek().map_or(false, |a| a.starts_with('-')) { "true".to_string() } else { args_iter.next().unwrap() }; - args_map.insert(key, value); + args_map.insert(key.to_owned(), value); } } @@ -38,18 +64,29 @@ fn main() { args_map.insert("multithreading".to_string(), "false".to_string()); args_map.insert("complex".to_string(), "false".to_string()); args_map.insert("test".to_string(), 7.to_string()); + args_map.insert("int".to_string(), "i32".to_string()); let count = 32; args_map.insert("count".to_string(), count.to_string()); } } let test_key = args_map.get("test").expect("Test number is not set"); - let multithreading_key = args_map.get("multithreading").expect("Multithreading is not set"); + let multithreading_key = args_map + .get("multithreading") + .expect("Multithreading is not set"); let complex_key = args_map.get("complex").expect("Complex is not set"); + let int_key = args_map.get("int").map(String::as_str).unwrap_or("i32"); - let test: usize = test_key.parse().expect("Unable to parse test as an integer"); - let multithreading: bool = multithreading_key.parse().expect("Unable to parse multithreading as an boolean"); - let complex: bool = complex_key.parse().expect("Unable to parse complex as an boolean"); + let test: usize = test_key + .parse() + .expect("Unable to parse test as an integer"); + let multithreading: bool = multithreading_key + .parse() + .expect("Unable to parse multithreading as an boolean"); + let complex: bool = complex_key + .parse() + .expect("Unable to parse complex as an boolean"); + let int_engines = IntEngine::parse(int_key); let multithreading = if multithreading { Some(MultithreadOptions::default()) @@ -57,36 +94,66 @@ fn main() { None }; - let solver = Solver { strategy: Strategy::Auto, precision: Precision::HIGH, multithreading}; + let solver = Solver { + strategy: Strategy::Auto, + precision: Precision::HIGH, + multithreading, + }; + + for int_engine in int_engines { + println!("int engine: {}", int_engine.label()); + run_selected_test(int_engine, test, complex, &args_map, solver); + } +} + +fn run_selected_test( + int_engine: IntEngine, + test: usize, + complex: bool, + args_map: &HashMap, + solver: Solver, +) { + match int_engine { + IntEngine::I16 => run_selected_test_with_int::(test, complex, args_map, solver), + IntEngine::I32 => run_selected_test_with_int::(test, complex, args_map, solver), + IntEngine::I64 => run_selected_test_with_int::(test, complex, args_map, solver), + } +} +fn run_selected_test_with_int( + test: usize, + complex: bool, + args_map: &HashMap, + solver: Solver, +) { if complex { match test { 0 => { - run_test_0(solver); + run_test_0::(solver); } 1 => { - run_test_1(solver); + run_test_1::(solver); } 2 => { - run_test_2(solver); + run_test_2::(solver); } 3 => { - run_test_3(); + run_test_3::(solver); } 4 => { - run_test_4(solver); + run_test_4::(solver); } 5 => { - run_test_5(solver); + run_test_5::(solver); } 6 => { - run_test_6(solver); + run_test_6::(solver); } 7 => { - run_test_7(solver); + run_test_7::(solver); } 8 => { - run_test_8(solver); + run_test_8::(solver); } _ => { println!("Test is not found"); @@ -94,34 +161,36 @@ fn main() { } } else { let count_key = args_map.get("count").expect("Count is not set"); - let count: usize = count_key.parse().expect("Unable to parse count as an integer"); + let count: usize = count_key + .parse() + .expect("Unable to parse count as an integer"); match test { 0 => { - CheckerboardTest::run(count, OverlayRule::Xor, solver, 1.0); + CheckerboardTest::run::(count, OverlayRule::Xor, solver, 1.0); } 1 => { - NotOverlapTest::run(count, OverlayRule::Union, solver, 1.0); + NotOverlapTest::run::(count, OverlayRule::Union, solver, 1.0); } 2 => { - LinesNetTest::run(count, OverlayRule::Intersect, solver, 1.0); + LinesNetTest::run::(count, OverlayRule::Intersect, solver, 1.0); } 3 => { - SpiralTest::run(count, 100.0); + SpiralTest::run::(count, solver, 100.0); } 4 => { - WindowsTest::run(count, OverlayRule::Difference, solver, 1.0); + WindowsTest::run::(count, OverlayRule::Difference, solver, 1.0); } 5 => { - CrossTest::run(count, OverlayRule::Xor, solver, 1.0); + CrossTest::run::(count, OverlayRule::Xor, solver, 1.0); } 6 => { - CorrosionTest::run(count, OverlayRule::Difference, solver, 1.0); + CorrosionTest::run::(count, OverlayRule::Difference, solver, 1.0); } 7 => { - ConcentricTest::run(count, OverlayRule::Intersect, solver, 1.0); + ConcentricTest::run::(count, OverlayRule::Intersect, solver, 1.0); } 8 => { - WindMillTest::run(count, OverlayRule::Intersect, solver, 1.0); + WindMillTest::run::(count, OverlayRule::Intersect, solver, 1.0); } _ => { println!("Test is not found"); @@ -130,78 +199,78 @@ fn main() { } } -fn run_test_0(solver: Solver) { +fn run_test_0(solver: Solver) { println!("run Checkerboard test"); for i in 1..12 { let n = 1 << i; - CheckerboardTest::run(n, OverlayRule::Xor, solver, 1000.0); + CheckerboardTest::run::(n, OverlayRule::Xor, solver, 1000.0); } } -fn run_test_1(solver: Solver) { +fn run_test_1(solver: Solver) { println!("run NotOverlap test"); for i in 1..12 { let n = 1 << i; - NotOverlapTest::run(n, OverlayRule::Xor, solver, 1000.0); + NotOverlapTest::run::(n, OverlayRule::Xor, solver, 1000.0); } } -fn run_test_2(solver: Solver) { +fn run_test_2(solver: Solver) { println!("run LinesNet test"); for i in 1..12 { let n = 1 << i; - LinesNetTest::run(n, OverlayRule::Intersect, solver, 500.0); + LinesNetTest::run::(n, OverlayRule::Intersect, solver, 500.0); } } -fn run_test_3() { +fn run_test_3(solver: Solver) { println!("run Spiral test"); for i in 1..21 { let n = 1 << i; - SpiralTest::run(n, 1000.0) + SpiralTest::run::(n, solver, 1000.0) } } -fn run_test_4(solver: Solver) { +fn run_test_4(solver: Solver) { println!("run Windows test"); for i in 1..12 { let n = 1 << i; - WindowsTest::run(n, OverlayRule::Difference, solver, 500.0); + WindowsTest::run::(n, OverlayRule::Difference, solver, 500.0); } } -fn run_test_5(solver: Solver) { +fn run_test_5(solver: Solver) { println!("run NestedSquares test"); for i in 1..18 { let n = 1 << i; - CrossTest::run(n, OverlayRule::Xor, solver, 500.0); + CrossTest::run::(n, OverlayRule::Xor, solver, 500.0); } } -fn run_test_6(solver: Solver) { +fn run_test_6(solver: Solver) { println!("run Corrosion test"); let mut n = 1; for _ in 1..12 { - CorrosionTest::run(n, OverlayRule::Difference, solver, 100.0); + CorrosionTest::run::(n, OverlayRule::Difference, solver, 100.0); n = n << 1; } } -fn run_test_7(solver: Solver) { +fn run_test_7(solver: Solver) { println!("run Concentric test"); let mut n = 1; for _ in 1..12 { - ConcentricTest::run(n, OverlayRule::Intersect, solver, 100.0); + ConcentricTest::run::(n, OverlayRule::Intersect, solver, 100.0); n = n << 1; } } -fn run_test_8(solver: Solver) { +fn run_test_8(solver: Solver) { println!("run WindMill test"); let mut n = 1; for _ in 1..12 { - WindMillTest::run(n, OverlayRule::Difference, solver, 100.0); + WindMillTest::run::(n, OverlayRule::Difference, solver, 100.0); n = n << 1; } - WindMillTest::validate(100, OverlayRule::Difference, solver); -} \ No newline at end of file + WindMillTest::validate::(100, OverlayRule::Difference, solver); +} diff --git a/performance/rust_app/src/test/mod.rs b/performance/rust_app/src/test/mod.rs index e263bf7..bdac2a5 100644 --- a/performance/rust_app/src/test/mod.rs +++ b/performance/rust_app/src/test/mod.rs @@ -7,4 +7,4 @@ pub(crate) mod test_5_nested_squares; pub(crate) mod test_6_corrosion; pub(crate) mod test_7_concentric; pub(crate) mod test_8_wind_mill; -mod util; \ No newline at end of file +pub(crate) mod util; diff --git a/performance/rust_app/src/test/test_0_checkerboard.rs b/performance/rust_app/src/test/test_0_checkerboard.rs index 4caeee8..62a67d0 100644 --- a/performance/rust_app/src/test/test_0_checkerboard.rs +++ b/performance/rust_app/src/test/test_0_checkerboard.rs @@ -1,10 +1,10 @@ -use std::time::Instant; +use crate::test::util::{OverlayInt, Util}; use i_overlay::core::fill_rule::FillRule; use i_overlay::core::overlay::Overlay; use i_overlay::core::overlay_rule::OverlayRule; use i_overlay::core::solver::Solver; use i_overlay::i_float::int::point::IntPoint; -use crate::test::util::Util; +use std::time::Instant; pub(crate) struct CheckerboardTest; @@ -43,18 +43,34 @@ multithreading off // A grid of overlapping squares forming a simple checkerboard pattern. impl CheckerboardTest { - pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { // 1000 - let subj_paths = Util::many_squares(IntPoint::new(0, 0), 20, 30, n); - let clip_paths = Util::many_squares(IntPoint::new(15, 15), 20, 30, n - 1); + pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { + // 1000 + if Util::skip_if_out_of_range::(n, 30 * n + 20) { + return; + } + + let subj_paths = Util::many_squares( + IntPoint::new(I::ZERO, I::ZERO), + I::from_usize(20), + I::from_usize(30), + n, + ); + let clip_paths = Util::many_squares( + IntPoint::new(I::from_usize(15), I::from_usize(15)), + I::from_usize(20), + I::from_usize(30), + n - 1, + ); let it_count = ((scale / (n as f64)) as usize).max(1); - let sq_it_count= it_count * it_count; + let sq_it_count = it_count * it_count; let start = Instant::now(); for _i in 0..sq_it_count { - let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) - .overlay(rule, FillRule::NonZero); + let _ = + Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) + .overlay(rule, FillRule::NonZero); } let duration = start.elapsed(); @@ -64,6 +80,9 @@ impl CheckerboardTest { let count_log = (polygons_count as f64).log10(); let time_log = time.log10(); - println!("{}({} {:.1}) - {:.6}({:.1})", n, polygons_count, count_log, time, time_log); + println!( + "{}({} {:.1}) - {:.6}({:.1})", + n, polygons_count, count_log, time, time_log + ); } -} \ No newline at end of file +} diff --git a/performance/rust_app/src/test/test_1_not_overlap.rs b/performance/rust_app/src/test/test_1_not_overlap.rs index 83ad329..9189786 100644 --- a/performance/rust_app/src/test/test_1_not_overlap.rs +++ b/performance/rust_app/src/test/test_1_not_overlap.rs @@ -1,10 +1,10 @@ -use std::time::Instant; +use crate::test::util::{OverlayInt, Util}; use i_overlay::core::fill_rule::FillRule; use i_overlay::core::overlay::Overlay; use i_overlay::core::overlay_rule::OverlayRule; use i_overlay::core::solver::Solver; use i_overlay::i_float::int::point::IntPoint; -use crate::test::util::Util; +use std::time::Instant; pub(crate) struct NotOverlapTest; /* @@ -44,18 +44,34 @@ multithreading off // A grid of not overlapping squares. impl NotOverlapTest { - pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { // 1000 - let subj_paths = Util::many_squares(IntPoint::new(0, 0), 10, 30, n); - let clip_paths = Util::many_squares(IntPoint::new(15, 15), 10, 30, n - 1); + pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { + // 1000 + if Util::skip_if_out_of_range::(n, 30 * n + 10) { + return; + } + + let subj_paths = Util::many_squares( + IntPoint::new(I::ZERO, I::ZERO), + I::from_usize(10), + I::from_usize(30), + n, + ); + let clip_paths = Util::many_squares( + IntPoint::new(I::from_usize(15), I::from_usize(15)), + I::from_usize(10), + I::from_usize(30), + n - 1, + ); let it_count = ((scale / (n as f64)) as usize).max(1); - let sq_it_count= it_count * it_count; + let sq_it_count = it_count * it_count; let start = Instant::now(); for _i in 0..sq_it_count { - let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) - .overlay(rule, FillRule::NonZero); + let _ = + Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) + .overlay(rule, FillRule::NonZero); } let duration = start.elapsed(); @@ -65,4 +81,4 @@ impl NotOverlapTest { println!("{:.1} - {:.6}", polygons_count, time); } -} \ No newline at end of file +} diff --git a/performance/rust_app/src/test/test_2_lines_net.rs b/performance/rust_app/src/test/test_2_lines_net.rs index a60d388..f7c9084 100644 --- a/performance/rust_app/src/test/test_2_lines_net.rs +++ b/performance/rust_app/src/test/test_2_lines_net.rs @@ -1,9 +1,9 @@ -use std::time::Instant; +use crate::test::util::{OverlayInt, Util}; use i_overlay::core::fill_rule::FillRule; use i_overlay::core::overlay::Overlay; use i_overlay::core::overlay_rule::OverlayRule; use i_overlay::core::solver::Solver; -use crate::test::util::Util; +use std::time::Instant; pub(crate) struct LinesNetTest; @@ -72,18 +72,24 @@ geom multithreading off // A grid is formed by the intersection of a set of vertical and horizontal lines. impl LinesNetTest { - pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { // 500 - let subj_paths = Util::many_lines_x(20, n); - let clip_paths = Util::many_lines_y(20, n); + pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { + // 500 + if Util::skip_if_out_of_range::(n, 10 * n + 10) { + return; + } + + let subj_paths = Util::many_lines_x(I::from_usize(20), n); + let clip_paths = Util::many_lines_y(I::from_usize(20), n); let it_count = ((scale / (n as f64)) as usize).max(1); - let sq_it_count= it_count * it_count; + let sq_it_count = it_count * it_count; let start = Instant::now(); for _ in 0..sq_it_count { - let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) - .overlay(rule, FillRule::NonZero); + let _ = + Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) + .overlay(rule, FillRule::NonZero); } let duration = start.elapsed(); @@ -93,4 +99,4 @@ impl LinesNetTest { println!("{} - {:.6}", polygons_count, time); } -} \ No newline at end of file +} diff --git a/performance/rust_app/src/test/test_3_spiral.rs b/performance/rust_app/src/test/test_3_spiral.rs index 6ed0763..70b9fa0 100644 --- a/performance/rust_app/src/test/test_3_spiral.rs +++ b/performance/rust_app/src/test/test_3_spiral.rs @@ -1,7 +1,10 @@ -use std::time::Instant; +use crate::test::util::{OverlayInt, Util}; use i_overlay::core::fill_rule::FillRule; -use i_overlay::float::simplify::SimplifyShape; -use crate::test::util::Util; +use i_overlay::core::overlay_rule::OverlayRule; +use i_overlay::core::solver::Solver; +use i_overlay::float::overlay::FloatOverlay; +use i_overlay::i_float::float::point::FloatPoint; +use std::time::Instant; pub(crate) struct SpiralTest; @@ -60,16 +63,22 @@ multithreading off // Two irregular self-intersecting polygons are generated, the vertices of which are defined by a fixed radius and angle. impl SpiralTest { - pub(crate) fn run(n: usize, scale: f64) { // 1000 + pub(crate) fn run(n: usize, solver: Solver, scale: f64) { + // 1000 let subj_path = Util::spiral(n, 100.0); let it_count = ((scale / (n as f64)) as usize).max(1); - let sq_it_count= it_count * it_count; + let sq_it_count = it_count * it_count; let start = Instant::now(); for _ in 0..sq_it_count { - let _ = subj_path.simplify_shape(FillRule::NonZero); + let _ = FloatOverlay::, I>::with_subj_custom_with_int( + &subj_path, + Default::default(), + solver, + ) + .overlay(OverlayRule::Subject, FillRule::NonZero); } let duration = start.elapsed(); @@ -79,4 +88,4 @@ impl SpiralTest { println!("{} - {:.6}", polygons_count, time); } -} \ No newline at end of file +} diff --git a/performance/rust_app/src/test/test_4_windows.rs b/performance/rust_app/src/test/test_4_windows.rs index 323d9ab..2d21532 100644 --- a/performance/rust_app/src/test/test_4_windows.rs +++ b/performance/rust_app/src/test/test_4_windows.rs @@ -1,10 +1,10 @@ -use std::time::Instant; +use crate::test::util::{OverlayInt, Util}; use i_overlay::core::fill_rule::FillRule; use i_overlay::core::overlay::Overlay; use i_overlay::core::overlay_rule::OverlayRule; use i_overlay::core::solver::Solver; use i_overlay::i_float::int::point::IntPoint; -use crate::test::util::Util; +use std::time::Instant; pub(crate) struct WindowsTest; /* @@ -41,20 +41,27 @@ pub(crate) struct WindowsTest; // A grid of square frames, each with a smaller square cutout in the center. impl WindowsTest { - pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { // 500 - let offset = 30; - let x = (n as i32) * offset / 2; + pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { + // 500 + if Util::skip_if_out_of_range::(n, 15 * n + 20) { + return; + } + + let offset = I::from_usize(30); + let x = I::from_usize(n) * offset / I::TWO; let origin = IntPoint::new(-x, -x); - let (subj_paths, clip_paths) = Util::many_windows(origin, 20, 10, offset, n); + let (subj_paths, clip_paths) = + Util::many_windows(origin, I::from_usize(20), I::from_usize(10), offset, n); let it_count = ((scale / (n as f64)) as usize).max(1); - let sq_it_count= it_count * it_count; + let sq_it_count = it_count * it_count; let start = Instant::now(); for _ in 0..sq_it_count { - let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) - .overlay(rule, FillRule::NonZero); + let _ = + Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) + .overlay(rule, FillRule::NonZero); } let duration = start.elapsed(); @@ -64,4 +71,4 @@ impl WindowsTest { println!("{} - {:.6}", polygons_count, time); } -} \ No newline at end of file +} diff --git a/performance/rust_app/src/test/test_5_nested_squares.rs b/performance/rust_app/src/test/test_5_nested_squares.rs index 195d423..39f0984 100644 --- a/performance/rust_app/src/test/test_5_nested_squares.rs +++ b/performance/rust_app/src/test/test_5_nested_squares.rs @@ -1,9 +1,9 @@ -use std::time::Instant; +use crate::test::util::{OverlayInt, Util}; use i_overlay::core::fill_rule::FillRule; use i_overlay::core::overlay::Overlay; use i_overlay::core::overlay_rule::OverlayRule; use i_overlay::core::solver::Solver; -use crate::test::util::Util; +use std::time::Instant; pub(crate) struct CrossTest; @@ -54,17 +54,23 @@ pub(crate) struct CrossTest; // A series of concentric squares, each progressively larger than the last. impl CrossTest { - pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { // 500 - let (subj_paths, clip_paths) = Util::concentric_squares(4, n); + pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { + // 500 + if Util::skip_if_out_of_range::(n, 8 * n + 8) { + return; + } + + let (subj_paths, clip_paths) = Util::concentric_squares(I::from_usize(4), n); let it_count = ((scale / (n as f64)) as usize).max(1); - let sq_it_count= it_count * it_count; + let sq_it_count = it_count * it_count; let start = Instant::now(); for _ in 0..sq_it_count { - let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) - .overlay(rule, FillRule::NonZero); + let _ = + Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) + .overlay(rule, FillRule::NonZero); } let duration = start.elapsed(); let time = duration.as_secs_f64() / sq_it_count as f64; @@ -73,4 +79,4 @@ impl CrossTest { println!("{} - {:.6}", polygons_count, time); } -} \ No newline at end of file +} diff --git a/performance/rust_app/src/test/test_6_corrosion.rs b/performance/rust_app/src/test/test_6_corrosion.rs index b1ea260..7859324 100644 --- a/performance/rust_app/src/test/test_6_corrosion.rs +++ b/performance/rust_app/src/test/test_6_corrosion.rs @@ -1,9 +1,10 @@ +use crate::test::util::OverlayInt; use i_overlay::core::fill_rule::FillRule; use i_overlay::core::overlay_rule::OverlayRule; -use std::f64::consts::PI; -use std::time::Instant; use i_overlay::core::solver::Solver; use i_overlay::float::overlay::FloatOverlay; +use std::f64::consts::PI; +use std::time::Instant; pub(crate) struct CorrosionTest; @@ -41,7 +42,7 @@ pub(crate) struct CorrosionTest; // A series of concentric squares, each progressively larger than the last. impl CorrosionTest { - pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { + pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { // 500 let (subj_paths, clip_paths) = Self::geometry(100.0, n); @@ -51,7 +52,12 @@ impl CorrosionTest { let start = Instant::now(); for _ in 0..sq_it_count { - let mut overlay = FloatOverlay::with_subj_and_clip_custom(&subj_paths, &clip_paths, Default::default(), solver); + let mut overlay = FloatOverlay::<[f64; 2], I>::with_subj_and_clip_custom_with_int( + &subj_paths, + &clip_paths, + Default::default(), + solver, + ); let _res = overlay.overlay(rule, FillRule::NonZero); } let duration = start.elapsed(); diff --git a/performance/rust_app/src/test/test_7_concentric.rs b/performance/rust_app/src/test/test_7_concentric.rs index 2087b41..b2eef89 100644 --- a/performance/rust_app/src/test/test_7_concentric.rs +++ b/performance/rust_app/src/test/test_7_concentric.rs @@ -1,9 +1,10 @@ +use crate::test::util::OverlayInt; use i_overlay::core::fill_rule::FillRule; use i_overlay::core::overlay_rule::OverlayRule; -use std::f64::consts::PI; -use std::time::Instant; use i_overlay::core::solver::Solver; use i_overlay::float::overlay::FloatOverlay; +use std::f64::consts::PI; +use std::time::Instant; pub(crate) struct ConcentricTest; @@ -55,7 +56,7 @@ pub(crate) struct ConcentricTest; // A series of concentric squares, each progressively larger than the last. impl ConcentricTest { - pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { + pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { let (subj_paths, clip_paths) = Self::geometry(100.0, n); let it_count = ((scale / (n as f64)) as usize).max(1); @@ -64,7 +65,12 @@ impl ConcentricTest { let start = Instant::now(); for _ in 0..sq_it_count { - let mut overlay = FloatOverlay::with_subj_and_clip_custom(&subj_paths, &clip_paths, Default::default(), solver); + let mut overlay = FloatOverlay::<[f64; 2], I>::with_subj_and_clip_custom_with_int( + &subj_paths, + &clip_paths, + Default::default(), + solver, + ); let _res = overlay.overlay(rule, FillRule::NonZero); } let duration = start.elapsed(); diff --git a/performance/rust_app/src/test/test_8_wind_mill.rs b/performance/rust_app/src/test/test_8_wind_mill.rs index d04f6c1..3571e5b 100644 --- a/performance/rust_app/src/test/test_8_wind_mill.rs +++ b/performance/rust_app/src/test/test_8_wind_mill.rs @@ -1,4 +1,4 @@ -use std::time::Instant; +use crate::test::util::{OverlayInt, Util}; use i_overlay::core::fill_rule::FillRule; use i_overlay::core::overlay::Overlay; use i_overlay::core::overlay_rule::OverlayRule; @@ -6,6 +6,7 @@ use i_overlay::core::solver::Solver; use i_overlay::i_float::int::number::int::IntNumber; use i_overlay::i_float::int::point::IntPoint; use i_overlay::i_shape::int::shape::IntContour; +use std::time::Instant; pub(crate) struct WindMillTest; @@ -41,8 +42,12 @@ pub(crate) struct WindMillTest; */ impl WindMillTest { - pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { - let (subj_paths, clip_paths) = Self::geometry(80, n); + pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { + if Util::skip_if_out_of_range::(n, 80 * n + 80) { + return; + } + + let (subj_paths, clip_paths) = Self::geometry(I::from_usize(80), n); let it_count = ((scale / (n as f64)) as usize).max(1); let sq_it_count = it_count * it_count; @@ -50,8 +55,9 @@ impl WindMillTest { let start = Instant::now(); for _ in 0..sq_it_count { - let _ = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) - .overlay(rule, FillRule::NonZero); + let _ = + Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) + .overlay(rule, FillRule::NonZero); } let duration = start.elapsed(); @@ -60,11 +66,12 @@ impl WindMillTest { println!("{} - {:.6}", n, time); } - pub(crate) fn validate(n: usize, rule: OverlayRule, solver: Solver) { - let (subj_paths, clip_paths) = Self::geometry(80i32, n); + pub(crate) fn validate(n: usize, rule: OverlayRule, solver: Solver) { + let (subj_paths, clip_paths) = Self::geometry(I::from_usize(80), n); - let res = Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) - .overlay(rule, FillRule::NonZero); + let res = + Overlay::with_contours_custom(&subj_paths, &clip_paths, Default::default(), solver) + .overlay(rule, FillRule::NonZero); assert_eq!(res.len(), n * n); println!("result validation PASS"); @@ -152,4 +159,4 @@ impl WindMillTest { (subj_paths, clip_paths) } -} \ No newline at end of file +} diff --git a/performance/rust_app/src/test/util.rs b/performance/rust_app/src/test/util.rs index fce5b18..08e6da4 100644 --- a/performance/rust_app/src/test/util.rs +++ b/performance/rust_app/src/test/util.rs @@ -1,15 +1,41 @@ -use std::f64::consts::PI; +use i_key_sort::sort::key::SortKey; use i_overlay::i_float::float::point::FloatPoint; use i_overlay::i_float::int::number::int::IntNumber; use i_overlay::i_float::int::point::IntPoint; use i_overlay::i_shape::base::data::Contour; use i_overlay::i_shape::int::path::IntPath; +use i_tree::{Expiration, LayoutNumber}; +use std::f64::consts::PI; pub(super) struct Util; +pub(crate) trait OverlayInt: IntNumber + Expiration + LayoutNumber + SortKey {} + +impl OverlayInt for I where I: IntNumber + Expiration + LayoutNumber + SortKey {} + impl Util { + pub(super) fn skip_if_out_of_range(n: usize, max_abs_coord: usize) -> bool { + let max = I::MAX.to_usize(); + if max_abs_coord <= max { + return false; + } - pub(super) fn many_squares(start: IntPoint, size: I, offset: I, n: usize) -> Vec> { + println!( + "{} - skipped (requires coordinate {}, i{} max {})", + n, + max_abs_coord, + I::BITS, + max + ); + true + } + + pub(super) fn many_squares( + start: IntPoint, + size: I, + offset: I, + n: usize, + ) -> Vec> { let mut result = Vec::with_capacity(n * n); let mut y = start.y; for _ in 0..n { @@ -30,8 +56,13 @@ impl Util { result } - - pub(super) fn many_windows(start: IntPoint, a: I, b: I, offset: I, n: usize) -> (Vec>, Vec>) { + pub(super) fn many_windows( + start: IntPoint, + a: I, + b: I, + offset: I, + n: usize, + ) -> (Vec>, Vec>) { let mut boundaries = Vec::with_capacity(n * n); let mut holes = Vec::with_capacity(n * n); let mut y = start.y; @@ -64,7 +95,10 @@ impl Util { (boundaries, holes) } - pub(super) fn concentric_squares(a: I, n: usize) -> (Vec>, Vec>) { + pub(super) fn concentric_squares( + a: I, + n: usize, + ) -> (Vec>, Vec>) { let mut vert = Vec::with_capacity(2 * n); let mut horz = Vec::with_capacity(2 * n); let s = I::TWO * a; @@ -164,9 +198,15 @@ impl Util { r - 0.2 * radius }; - let p = FloatPoint { x: rr * sx, y: rr * sy }; + let p = FloatPoint { + x: rr * sx, + y: rr * sy, + }; let n = (p - p0).normalize(); - let t = FloatPoint { x: w * -n.y, y: w * n.x }; + let t = FloatPoint { + x: w * -n.y, + y: w * n.x, + }; a_path.push(p0 + t); a_path.push(p + t); @@ -183,4 +223,4 @@ impl Util { a_path } -} \ No newline at end of file +} From 10602ce01694b8000967d35a9f388fea6da5fefd Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Thu, 21 May 2026 19:32:22 +0300 Subject: [PATCH 15/36] update documentation --- iOverlay/README.md | 24 +++++++++++++++++++++-- iOverlay/src/core/extract.rs | 8 ++++---- iOverlay/src/core/overlay.rs | 20 +++++++++---------- iOverlay/src/core/relate.rs | 6 +++--- iOverlay/src/core/simplify.rs | 8 ++++---- iOverlay/src/float/clip.rs | 5 +++++ iOverlay/src/float/overlay.rs | 29 ++++++++++++++-------------- iOverlay/src/float/relate.rs | 12 ++++++++---- iOverlay/src/float/scale.rs | 26 +++++++++++++++---------- iOverlay/src/float/simplify.rs | 4 ++++ iOverlay/src/float/single.rs | 4 ++++ iOverlay/src/float/slice.rs | 4 ++++ iOverlay/src/float/string_overlay.rs | 8 ++++---- iOverlay/src/lib.rs | 4 ++-- iOverlay/src/string/clip.rs | 12 ++++++------ iOverlay/src/string/extract.rs | 14 +++++++------- iOverlay/src/string/overlay.rs | 14 +++++++------- 17 files changed, 125 insertions(+), 77 deletions(-) diff --git a/iOverlay/README.md b/iOverlay/README.md index 0f547c6..bd081d0 100644 --- a/iOverlay/README.md +++ b/iOverlay/README.md @@ -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 i32, f32, and f64 APIs. +- **Data Types**: Supports `i16`/`i32`/`i64` integer APIs and `f32`/`f64` floating-point APIs.   ## Demo @@ -607,7 +607,27 @@ If you need more control, use `FloatPointAdapter::with_scale` and `FloatOverlay: --- -### 4. How do I enable OGC-valid output? +### 4. How do I select the integer engine for float overlays? + +If you need control over the float-to-integer precision range, select the integer engine at +compile time with the `from_*` constructors. The supported engines are `i16`, `i32`, and `i64`. +The default engine is `i32`; use `i64` when the input bounds need a wider integer range. + +```rust +use i_overlay::core::fill_rule::FillRule; +use i_overlay::core::overlay_rule::OverlayRule; +use i_overlay::float::overlay::FloatOverlay; + +let subj = vec![[0.0, 0.0], [0.0, 5.0], [5.0, 5.0], [5.0, 0.0]]; +let clip = vec![[2.0, 2.0], [2.0, 4.0], [4.0, 4.0], [4.0, 2.0]]; + +let mut overlay = FloatOverlay::<[f64; 2], i64>::from_subj_and_clip(&subj, &clip); +let result = overlay.overlay(OverlayRule::Difference, FillRule::EvenOdd); +``` + +--- + +### 5. How do I enable OGC-valid output? Set the `ogc` flag in `OverlayOptions`. diff --git a/iOverlay/src/core/extract.rs b/iOverlay/src/core/extract.rs index 9dcdba6..030488a 100644 --- a/iOverlay/src/core/extract.rs +++ b/iOverlay/src/core/extract.rs @@ -55,11 +55,11 @@ where /// 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. /// - `buffer`: Reusable buffer, optimisation purpose only. - /// - Returns: A vector of `IntShape`, representing the geometric result of the applied overlay rule. + /// - Returns: A vector of `IntShape`, representing the geometric result of the applied overlay rule. /// # Shape Representation - /// The output is a `IntShapes`, where: - /// - The outer `Vec>` represents a set of shapes. - /// - Each shape `Vec>` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. + /// The output is a `IntShapes`, where: + /// - The outer `Vec>` represents a set of shapes. + /// - Each shape `Vec>` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. /// - Each path `Vec` is a sequence of points, forming a closed path. /// /// Note: Outer boundary paths have a counterclockwise order, and holes have a clockwise order. diff --git a/iOverlay/src/core/overlay.rs b/iOverlay/src/core/overlay.rs index 412dc76..a4bff10 100644 --- a/iOverlay/src/core/overlay.rs +++ b/iOverlay/src/core/overlay.rs @@ -218,7 +218,7 @@ where } /// Adds multiple paths to the overlay as either subject or clip paths. - /// - `contours`: An array of `IntContour` instances to be added to the overlay. + /// - `contours`: An array of `IntContour` instances to be added to the overlay. /// - `shape_type`: Specifies the role of the added paths in the overlay operation, either as `Subject` or `Clip`. #[inline] pub fn add_contours(&mut self, contours: &[IntContour], shape_type: ShapeType) { @@ -228,7 +228,7 @@ where } /// Adds a single shape to the overlay as either a subject or clip shape. - /// - `shape`: A reference to a `IntShape` instance to be added. + /// - `shape`: A reference to a `IntShape` instance to be added. /// - `shape_type`: Specifies the role of the added shape in the overlay operation, either as `Subject` or `Clip`. #[inline] pub fn add_shape(&mut self, shape: &IntShape, shape_type: ShapeType) { @@ -236,7 +236,7 @@ where } /// Adds multiple shapes to the overlay as either subject or clip shapes. - /// - `shapes`: An array of `IntShape` instances to be added to the overlay. + /// - `shapes`: An array of `IntShape` instances to be added to the overlay. /// - `shape_type`: Specifies the role of the added shapes in the overlay operation, either as `Subject` or `Clip`. #[inline] pub fn add_shapes(&mut self, shapes: &[IntShape], shape_type: ShapeType) { @@ -251,7 +251,7 @@ where } /// Adds multiple flat-shape to the overlay as either subject or clip shapes. - /// - `buffer`: A buffer of `IntShapes` instances to be added to the overlay. + /// - `buffer`: A buffer of `IntShapes` instances to be added to the overlay. /// - `shape_type`: Specifies the role of the added shapes in the overlay operation, either as `Subject` or `Clip`. #[inline] pub fn add_flat_buffer(&mut self, buffer: &FlatContoursBuffer, shape_type: ShapeType) { @@ -328,11 +328,11 @@ where /// ### Parameters: /// - `overlay_rule`: The boolean operation rule to apply, determining how shapes are combined or subtracted. /// - `fill_rule`: Specifies the rule for determining filled areas within the shapes, influencing how the resulting graph represents intersections and unions. - /// - Returns: A vector of `IntShape` that meet the specified area criteria, representing the cleaned-up geometric result. + /// - Returns: A vector of `IntShape` that meet the specified area criteria, representing the cleaned-up geometric result. /// # Shape Representation - /// The output is a `IntShapes`, where: - /// - The outer `Vec>` represents a set of shapes. - /// - Each shape `Vec>` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. + /// The output is a `IntShapes`, where: + /// - The outer `Vec>` represents a set of shapes. + /// - Each shape `Vec>` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. /// - Each path `Vec` is a sequence of points, forming a closed path. /// /// Note: Outer boundary paths have a counterclockwise order, and holes have a clockwise order. @@ -381,11 +381,11 @@ where /// Executes a single Boolean operation and writes the result into a flat contour buffer. /// /// This is a lower-allocation alternative to [`Self::overlay`] when you want flat contour - /// output (`points` + `ranges`) instead of nested `IntShapes`. + /// output (`points` + `ranges`) instead of nested `IntShapes`. /// /// - `overlay_rule`: The Boolean operation to apply. /// - `fill_rule`: Fill rule used to determine interior regions. - /// - `output`: Destination [`FlatContoursBuffer`] that receives resulting contours. + /// - `output`: Destination [`FlatContoursBuffer`] that receives resulting contours. /// Existing buffer contents are replaced. #[inline] pub fn overlay_into( diff --git a/iOverlay/src/core/relate.rs b/iOverlay/src/core/relate.rs index 2e3733d..602e98e 100644 --- a/iOverlay/src/core/relate.rs +++ b/iOverlay/src/core/relate.rs @@ -140,7 +140,7 @@ where } /// Adds multiple paths to the overlay as either subject or clip paths. - /// - `contours`: An array of `IntContour` instances to be added to the overlay. + /// - `contours`: An array of `IntContour` instances to be added to the overlay. /// - `shape_type`: Specifies the role of the added paths in the overlay operation, either as `Subject` or `Clip`. #[inline] pub fn add_contours(&mut self, contours: &[IntContour], shape_type: ShapeType) { @@ -150,7 +150,7 @@ where } /// Adds a single shape to the overlay as either a subject or clip shape. - /// - `shape`: A reference to a `IntShape` instance to be added. + /// - `shape`: A reference to a `IntShape` instance to be added. /// - `shape_type`: Specifies the role of the added shape in the overlay operation, either as `Subject` or `Clip`. #[inline] pub fn add_shape(&mut self, shape: &IntShape, shape_type: ShapeType) { @@ -158,7 +158,7 @@ where } /// Adds multiple shapes to the overlay as either subject or clip shapes. - /// - `shapes`: An array of `IntShape` instances to be added to the overlay. + /// - `shapes`: An array of `IntShape` instances to be added to the overlay. /// - `shape_type`: Specifies the role of the added shapes in the overlay operation, either as `Subject` or `Clip`. #[inline] pub fn add_shapes(&mut self, shapes: &[IntShape], shape_type: ShapeType) { diff --git a/iOverlay/src/core/simplify.rs b/iOverlay/src/core/simplify.rs index 6b4dd1e..c47dc60 100644 --- a/iOverlay/src/core/simplify.rs +++ b/iOverlay/src/core/simplify.rs @@ -28,9 +28,9 @@ pub trait Simplify { /// - `fill_rule`: Fill rule to determine filled areas (non-zero, even-odd, positive, negative). /// - `options`: Adjust custom behavior. /// # Shape Representation - /// The output is a `IntShapes`, where: - /// - The outer `Vec>` represents a set of shapes. - /// - Each shape `Vec>` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. + /// The output is a `IntShapes`, where: + /// - The outer `Vec>` represents a set of shapes. + /// - Each shape `Vec>` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. /// - Each path `Vec` is a sequence of points, forming a closed path. /// /// Note: Outer boundary paths have a **main_direction** order, and holes have an opposite to **main_direction** order. @@ -104,7 +104,7 @@ where /// Skips full overlay if the contour is already simple (no splits, no loops, no collinear issues). /// Ensures correct winding order based on `fill_rule` and `options.output_direction`. /// - /// Returns `None` if the contour is valid and needs no changes, or `Some(IntShapes)` with the simplified result. + /// Returns `None` if the contour is valid and needs no changes, or `Some(IntShapes)` with the simplified result. #[inline] pub fn simplify_contour(&mut self, contour: &[IntPoint], fill_rule: FillRule) -> Option> { self.clear(); diff --git a/iOverlay/src/float/clip.rs b/iOverlay/src/float/clip.rs index 0dab649..7c9dae6 100644 --- a/iOverlay/src/float/clip.rs +++ b/iOverlay/src/float/clip.rs @@ -7,6 +7,11 @@ use i_float::float::compatible::FloatPointCompatible; use i_shape::base::data::Paths; use i_shape::source::resource::ShapeResource; +/// Trait for clipping float string paths by float shapes. +/// +/// This convenience trait uses the default integer engine (`i32`). Use +/// `FloatStringOverlay::::from_shape_and_string` when you need to select `i16`, `i32`, +/// or `i64` explicitly. pub trait FloatClip where R: ShapeResource

, diff --git a/iOverlay/src/float/overlay.rs b/iOverlay/src/float/overlay.rs index 6ce2256..94792b7 100644 --- a/iOverlay/src/float/overlay.rs +++ b/iOverlay/src/float/overlay.rs @@ -25,6 +25,11 @@ use i_shape::float::despike::DeSpikeContour; use i_shape::float::simple::SimplifyContour; use i_tree::{Expiration, LayoutNumber}; +/// Options for float overlay extraction. +/// +/// `F` is the floating-point scalar type (`f32` or `f64`). `I` is the integer engine +/// (`i16`, `i32`, or `i64`) used internally for float-to-integer conversion and +/// precision limits. The default integer engine is `i32`. #[derive(Debug, Clone, Copy)] pub struct OverlayOptions { /// Preserve collinear points in the input before Boolean operations. @@ -123,7 +128,7 @@ where /// - `Contour`: A contour representing a closed path. This path is interpreted as closed, so it doesn’t require the start and endpoint to be the same for processing. /// - `Contours`: A collection of contours, each representing a closed path. /// - `Shapes`: A collection of shapes, where each shape may consist of multiple contours. - pub fn with_subj_and_clip_with_int(subj: &R0, clip: &R1) -> Self + pub fn from_subj_and_clip(subj: &R0, clip: &R1) -> Self where R0: ShapeResource

+ ?Sized, R1: ShapeResource

+ ?Sized, @@ -147,7 +152,7 @@ where /// - `Shapes`: A collection of shapes, where each shape may consist of multiple contours. /// - `options`: Adjust custom behavior. /// - `solver`: Type of solver to use. - pub fn with_subj_and_clip_custom_with_int( + pub fn from_subj_and_clip_custom( subj: &R0, clip: &R1, options: OverlayOptions, @@ -173,7 +178,7 @@ where /// - `Contour`: A contour representing a closed path. This path is interpreted as closed, so it doesn’t require the start and endpoint to be the same for processing. /// - `Contours`: A collection of contours, each representing a closed path. /// - `Shapes`: A collection of shapes, where each shape may consist of multiple contours. - pub fn with_subj_with_int(subj: &R) -> Self + pub fn from_subj(subj: &R) -> Self where R: ShapeResource

+ ?Sized, { @@ -192,11 +197,7 @@ where /// - `Shapes`: A collection of shapes, where each shape may consist of multiple contours. /// - `options`: Adjust custom behavior. /// - `solver`: Type of solver to use. - pub fn with_subj_custom_with_int( - subj: &R, - options: OverlayOptions, - solver: Solver, - ) -> Self + pub fn from_subj_custom(subj: &R, options: OverlayOptions, solver: Solver) -> Self where R: ShapeResource

+ ?Sized, { @@ -393,7 +394,7 @@ impl FloatOverlay

{ R0: ShapeResource

+ ?Sized, R1: ShapeResource

+ ?Sized, { - Self::with_subj_and_clip_with_int(subj, clip) + Self::from_subj_and_clip(subj, clip) } /// Creates a new `FloatOverlay` instance and initializes it with subject and clip shapes. @@ -409,7 +410,7 @@ impl FloatOverlay

{ R0: ShapeResource

+ ?Sized, R1: ShapeResource

+ ?Sized, { - Self::with_subj_and_clip_custom_with_int(subj, clip, options, solver) + Self::from_subj_and_clip_custom(subj, clip, options, solver) } /// Creates a new `FloatOverlay` instance and initializes it with subject. @@ -419,7 +420,7 @@ impl FloatOverlay

{ where R: ShapeResource

+ ?Sized, { - Self::with_subj_with_int(subj) + Self::from_subj(subj) } /// Creates a new `FloatOverlay` instance and initializes it with subject. @@ -429,7 +430,7 @@ impl FloatOverlay

{ where R: ShapeResource

+ ?Sized, { - Self::with_subj_custom_with_int(subj, options, solver) + Self::from_subj_custom(subj, options, solver) } } @@ -524,9 +525,9 @@ mod tests { let left_rect = [[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]; let right_rect = [[1.0, 0.0], [1.0, 1.0], [2.0, 1.0], [2.0, 0.0]]; - let low = FloatOverlay::<[f64; 2], i16>::with_subj_and_clip_with_int(&left_rect, &right_rect) + let low = FloatOverlay::<[f64; 2], i16>::from_subj_and_clip(&left_rect, &right_rect) .overlay(OverlayRule::Union, FillRule::EvenOdd); - let high = FloatOverlay::<[f64; 2], i64>::with_subj_and_clip_with_int(&left_rect, &right_rect) + let high = FloatOverlay::<[f64; 2], i64>::from_subj_and_clip(&left_rect, &right_rect) .overlay(OverlayRule::Union, FillRule::EvenOdd); assert_eq!(low[0][0].len(), 4); diff --git a/iOverlay/src/float/relate.rs b/iOverlay/src/float/relate.rs index 25ff1c2..e52fd67 100644 --- a/iOverlay/src/float/relate.rs +++ b/iOverlay/src/float/relate.rs @@ -77,7 +77,7 @@ where } /// Creates a new predicate overlay from subject and clip shapes. - pub fn with_subj_and_clip_with_int(subj: &R0, clip: &R1) -> Self + pub fn from_subj_and_clip(subj: &R0, clip: &R1) -> Self where R0: ShapeResource

+ ?Sized, R1: ShapeResource

+ ?Sized, @@ -97,7 +97,7 @@ where } /// Creates a new predicate overlay with custom solver and fill rule. - pub fn with_subj_and_clip_custom_with_int( + pub fn from_subj_and_clip_custom( subj: &R0, clip: &R1, fill_rule: FillRule, @@ -183,7 +183,7 @@ impl FloatPredicateOverlay

{ R0: ShapeResource

+ ?Sized, R1: ShapeResource

+ ?Sized, { - Self::with_subj_and_clip_with_int(subj, clip) + Self::from_subj_and_clip(subj, clip) } /// Creates a new predicate overlay with custom solver and fill rule. @@ -199,7 +199,7 @@ impl FloatPredicateOverlay

{ R0: ShapeResource

+ ?Sized, R1: ShapeResource

+ ?Sized, { - Self::with_subj_and_clip_custom_with_int(subj, clip, fill_rule, solver) + Self::from_subj_and_clip_custom(subj, clip, fill_rule, solver) } } @@ -209,6 +209,10 @@ impl FloatPredicateOverlay

{ /// directly on contours, shapes, and shape collections without explicit /// overlay construction. /// +/// This convenience trait uses the default integer engine (`i32`). Use +/// `FloatPredicateOverlay::::from_subj_and_clip` when you need to select `i16`, `i32`, +/// or `i64` explicitly. +/// /// # Example /// /// ``` diff --git a/iOverlay/src/float/scale.rs b/iOverlay/src/float/scale.rs index dad1ffe..2a14b77 100644 --- a/iOverlay/src/float/scale.rs +++ b/iOverlay/src/float/scale.rs @@ -54,6 +54,10 @@ impl From for FixedScaleOverlayError { /// The `scale` parameter defines the float-to-integer conversion: /// `x_int = (x_float - offset_x) * scale`. /// Larger `scale` gives higher precision but must fit within the safe integer bounds. +/// +/// This convenience trait uses the default integer engine (`i32`). Use +/// `FloatOverlay::::from_subj_and_clip_fixed_scale` when you need to select `i16`, +/// `i32`, or `i64` explicitly. pub trait FixedScaleFloatOverlay where R0: ShapeResource

, @@ -118,7 +122,7 @@ where /// - `Contour`: A contour representing a closed path. This path is interpreted as closed, so it doesn’t require the start and endpoint to be the same for processing. /// - `Contours`: A collection of contours, each representing a closed path. /// - `Shapes`: A collection of shapes, where each shape may consist of multiple contours. - pub fn with_subj_and_clip_fixed_scale_with_int( + pub fn from_subj_and_clip_fixed_scale( subj: &R0, clip: &R1, scale: P::Scalar, @@ -152,7 +156,7 @@ where /// - `Shapes`: A collection of shapes, where each shape may consist of multiple contours. /// - `options`: Adjust custom behavior. /// - `solver`: Type of solver to use. - pub fn with_subj_and_clip_fixed_scale_custom_with_int( + pub fn from_subj_and_clip_fixed_scale_custom( subj: &R0, clip: &R1, options: OverlayOptions, @@ -190,7 +194,7 @@ impl FloatOverlay

{ R0: ShapeResource

+ ?Sized, R1: ShapeResource

+ ?Sized, { - FloatOverlay::::with_subj_and_clip_fixed_scale_with_int(subj, clip, scale) + FloatOverlay::::from_subj_and_clip_fixed_scale(subj, clip, scale) } /// Creates a new `FloatOverlay` instance with a fixed float-to-integer scale. @@ -207,9 +211,7 @@ impl FloatOverlay

{ R0: ShapeResource

+ ?Sized, R1: ShapeResource

+ ?Sized, { - FloatOverlay::::with_subj_and_clip_fixed_scale_custom_with_int( - subj, clip, options, solver, scale, - ) + FloatOverlay::::from_subj_and_clip_fixed_scale_custom(subj, clip, options, solver, scale) } } @@ -229,7 +231,7 @@ where /// * `subj` - A `ShapeResource` defining the subject geometry. /// * `clip` - A `ShapeResource` defining the clip geometry. /// * `scale` - Fixed float-to-integer scale factor. - pub fn with_subj_and_clip_fixed_scale_with_int( + pub fn from_subj_and_clip_fixed_scale( subj: &R0, clip: &R1, scale: P::Scalar, @@ -259,7 +261,7 @@ where /// * `fill_rule` - Fill rule to determine filled areas. /// * `solver` - Type of solver to use. /// * `scale` - Fixed float-to-integer scale factor. - pub fn with_subj_and_clip_fixed_scale_custom_with_int( + pub fn from_subj_and_clip_fixed_scale_custom( subj: &R0, clip: &R1, fill_rule: FillRule, @@ -296,7 +298,7 @@ impl FloatPredicateOverlay

{ R0: ShapeResource

+ ?Sized, R1: ShapeResource

+ ?Sized, { - FloatPredicateOverlay::::with_subj_and_clip_fixed_scale_with_int(subj, clip, scale) + FloatPredicateOverlay::::from_subj_and_clip_fixed_scale(subj, clip, scale) } /// Creates a new predicate overlay with subject and clip shapes using fixed-scale precision @@ -313,7 +315,7 @@ impl FloatPredicateOverlay

{ R0: ShapeResource

+ ?Sized, R1: ShapeResource

+ ?Sized, { - FloatPredicateOverlay::::with_subj_and_clip_fixed_scale_custom_with_int( + FloatPredicateOverlay::::from_subj_and_clip_fixed_scale_custom( subj, clip, fill_rule, solver, scale, ) } @@ -325,6 +327,10 @@ impl FloatPredicateOverlay

{ /// float-to-integer scale, which is useful when you need consistent precision /// across multiple operations or when working with known coordinate bounds. /// +/// This convenience trait uses the default integer engine (`i32`). Use +/// `FloatPredicateOverlay::::from_subj_and_clip_fixed_scale` when you need to select +/// `i16`, `i32`, or `i64` explicitly. +/// /// # Example /// /// ``` diff --git a/iOverlay/src/float/simplify.rs b/iOverlay/src/float/simplify.rs index 9a063a6..8dc1282 100644 --- a/iOverlay/src/float/simplify.rs +++ b/iOverlay/src/float/simplify.rs @@ -9,6 +9,10 @@ use i_shape::source::resource::ShapeResource; /// 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 /// determine which areas should be retained or excluded. +/// +/// This convenience trait uses the default integer engine (`i32`). Use +/// `FloatOverlay::::from_subj_custom` with `OverlayRule::Subject` when you need to select +/// `i16`, `i32`, or `i64` explicitly. pub trait SimplifyShape { /// Simplifies the shape or collection of points, contours, or shapes, based on a specified minimum area threshold. /// diff --git a/iOverlay/src/float/single.rs b/iOverlay/src/float/single.rs index db62a51..c9c903a 100644 --- a/iOverlay/src/float/single.rs +++ b/iOverlay/src/float/single.rs @@ -7,6 +7,10 @@ use i_shape::source::resource::ShapeResource; /// 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. +/// +/// This convenience trait uses the default integer engine (`i32`). Use +/// `FloatOverlay::::from_subj_and_clip` when you need to select `i16`, `i32`, or `i64` +/// explicitly. pub trait SingleFloatOverlay where R0: ShapeResource

, diff --git a/iOverlay/src/float/slice.rs b/iOverlay/src/float/slice.rs index 5f764bd..69ecd34 100644 --- a/iOverlay/src/float/slice.rs +++ b/iOverlay/src/float/slice.rs @@ -10,6 +10,10 @@ use i_shape::source::resource::ShapeResource; /// 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. +/// +/// This convenience trait uses the default integer engine (`i32`). Use +/// `FloatStringOverlay::::from_shape_and_string` when you need to select `i16`, `i32`, +/// or `i64` explicitly. pub trait FloatSlice where R: ShapeResource

, diff --git a/iOverlay/src/float/string_overlay.rs b/iOverlay/src/float/string_overlay.rs index 05aa9d2..f621751 100644 --- a/iOverlay/src/float/string_overlay.rs +++ b/iOverlay/src/float/string_overlay.rs @@ -59,7 +59,7 @@ where /// - `Path`: A path representing a string line. /// - `Paths`: A collection of paths, each representing a string line. /// - `Vec`: A collection of grouped paths, where each group may consist of multiple paths. - pub fn with_shape_and_string_with_int(shape: &R0, string: &R1) -> Self + pub fn from_shape_and_string(shape: &R0, string: &R1) -> Self where R0: ShapeResource

, R1: ShapeResource

, @@ -78,7 +78,7 @@ where /// /// This variant validates that the requested scale is finite, positive, and fits the /// input bounds. Use `scale = 1.0 / grid_size` if you want a grid-size style parameter. - pub fn with_shape_and_string_fixed_scale_with_int( + pub fn from_shape_and_string_fixed_scale( shape: &R0, string: &R1, scale: P::Scalar, @@ -208,7 +208,7 @@ impl FloatStringOverlay

{ R0: ShapeResource

, R1: ShapeResource

, { - Self::with_shape_and_string_with_int(shape, string) + Self::from_shape_and_string(shape, string) } /// Creates a new `FloatStringOverlay` instance with a fixed float-to-integer scale. @@ -223,7 +223,7 @@ impl FloatStringOverlay

{ R0: ShapeResource

, R1: ShapeResource

, { - Self::with_shape_and_string_fixed_scale_with_int(shape, string, scale) + Self::from_shape_and_string_fixed_scale(shape, string, scale) } } diff --git a/iOverlay/src/lib.rs b/iOverlay/src/lib.rs index f7fd8ea..dd7cc88 100644 --- a/iOverlay/src/lib.rs +++ b/iOverlay/src/lib.rs @@ -1,6 +1,6 @@ //! # iOverlay //! -//! The `iOverlay` library provides high-performance boolean operations on polygons, including union, intersection, difference, and xor. It is designed for applications that require precise polygon operations, such as computer graphics, CAD systems, and geographical information systems (GIS). By supporting both integer (i32) and floating-point (f32, f64) APIs, iOverlay offers flexibility and precision across diverse use cases. +//! The `iOverlay` library provides high-performance boolean operations on polygons, including union, intersection, difference, and xor. It is designed for applications that require precise polygon operations, such as computer graphics, CAD systems, and geographical information systems (GIS). By supporting integer (`i16`, `i32`, `i64`) and floating-point (`f32`, `f64`) APIs, iOverlay offers flexibility and precision across diverse use cases. //! //! ## Features //! - **Boolean Operations**: union, intersection, difference, and exclusion. @@ -8,7 +8,7 @@ //! - **Polygons**: with holes, self-intersections, and multiple contours. //! - **Simplification**: removes degenerate vertices and merges collinear edges. //! - **Fill Rules**: even-odd, non-zero, positive and negative. -//! - **Data Types**: Supports i32, f32, and f64 APIs. +//! - **Data Types**: Supports `i16`/`i32`/`i64` integer APIs and `f32`/`f64` floating-point APIs. //! //! ## Simple Example //! ![Simple Example](https://raw.githubusercontent.com/iShape-Rust/iOverlay/main/readme/example_union.svg) diff --git a/iOverlay/src/string/clip.rs b/iOverlay/src/string/clip.rs index a75d8fa..e45f0f1 100644 --- a/iOverlay/src/string/clip.rs +++ b/iOverlay/src/string/clip.rs @@ -129,7 +129,7 @@ pub trait IntClip { /// - `clip_rule`: The rule for clipping, determining how the boundary and inversion settings affect the result. /// /// # Returns - /// A vector of `IntPath` instances representing the clipped sections of the input line. + /// A vector of `IntPath` instances representing the clipped sections of the input line. fn clip_line(&self, line: IntLine, fill_rule: FillRule, clip_rule: ClipRule) -> Vec>; /// Clips multiple lines according to the specified build and clip rules. @@ -138,25 +138,25 @@ pub trait IntClip { /// - `clip_rule`: The rule for clipping, determining how boundary and inversion settings affect the results. /// /// # Returns - /// A vector of `IntPath` instances containing the clipped portions of the input lines. + /// A vector of `IntPath` instances containing the clipped portions of the input lines. fn clip_lines(&self, lines: &[IntLine], fill_rule: FillRule, clip_rule: ClipRule) -> Vec>; /// Clips a single path according to the specified build and clip rules. - /// - `path`: A reference to an `IntPath`, which is a sequence of points representing the path to be clipped. + /// - `path`: A reference to an `IntPath`, which is a sequence of points representing the path to be clipped. /// - `fill_rule`: Specifies the rule determining the filled areas, influencing the inclusion of path segments. /// - `clip_rule`: The rule for clipping, determining how boundary and inversion settings affect the result. /// /// # Returns - /// A vector of `IntPath` instances representing the clipped sections of the path. + /// A vector of `IntPath` instances representing the clipped sections of the path. fn clip_path(&self, path: &IntPath, fill_rule: FillRule, clip_rule: ClipRule) -> Vec>; /// Clips multiple paths according to the specified build and clip rules. - /// - `paths`: A slice of `IntPath` instances, each representing a path to be clipped. + /// - `paths`: A slice of `IntPath` instances, each representing a path to be clipped. /// - `fill_rule`: Specifies the rule determining the filled areas, influencing the inclusion of path segments. /// - `clip_rule`: The rule for clipping, determining how boundary and inversion settings affect the result. /// /// # Returns - /// A vector of `IntPath` instances containing the clipped portions of the input paths. + /// A vector of `IntPath` instances containing the clipped portions of the input paths. fn clip_paths(&self, paths: &[IntPath], fill_rule: FillRule, clip_rule: ClipRule) -> Vec>; } diff --git a/iOverlay/src/string/extract.rs b/iOverlay/src/string/extract.rs index 270d11a..7450d47 100644 --- a/iOverlay/src/string/extract.rs +++ b/iOverlay/src/string/extract.rs @@ -18,9 +18,9 @@ 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 - /// The output is a `IntShapes`, where: - /// - The outer `Vec>` represents a set of shapes. - /// - Each shape `Vec>` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. + /// The output is a `IntShapes`, where: + /// - The outer `Vec>` represents a set of shapes. + /// - Each shape `Vec>` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. /// - Each path `Vec` is a sequence of points, forming a closed path. /// /// Note: Outer boundary paths have a counterclockwise order, and holes have a clockwise order. @@ -33,11 +33,11 @@ impl StringGraph<'_, I> { /// - `string_rule`: The rule used to determine how shapes are extracted. /// - `main_direction`: Winding direction for the **output** main (outer) contour. All hole contours will automatically use the opposite direction. Impact on **output** only! /// - `min_area`: The minimum area that a shape must have to be included in the results. Shapes smaller than this will be excluded. - /// - Returns: A vector of `IntShape`, representing the geometric result of the applied overlay rule. + /// - Returns: A vector of `IntShape`, representing the geometric result of the applied overlay rule. /// # Shape Representation - /// The output is a `IntShapes`, where: - /// - The outer `Vec>` represents a set of shapes. - /// - Each shape `Vec>` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. + /// The output is a `IntShapes`, where: + /// - The outer `Vec>` represents a set of shapes. + /// - Each shape `Vec>` represents a collection of contours, where the first contour is the outer boundary, and all subsequent contours are holes in this boundary. /// - Each path `Vec` is a sequence of points, forming a closed path. /// /// Note: Outer boundary paths have a **main_direction** order, and holes have an opposite to **main_direction** order. diff --git a/iOverlay/src/string/overlay.rs b/iOverlay/src/string/overlay.rs index 8bbb4f5..5e18ca9 100644 --- a/iOverlay/src/string/overlay.rs +++ b/iOverlay/src/string/overlay.rs @@ -73,7 +73,7 @@ where } /// Creates a new `StringOverlay` instance and initializes it with multiple shape contours. - /// - `contours`: An array of `IntContour` instances to be added to the overlay. + /// - `contours`: An array of `IntContour` instances to be added to the overlay. #[inline] pub fn with_shape_contours(contours: &[IntContour]) -> Self { let mut overlay = Self::new(contours.points_count()); @@ -82,7 +82,7 @@ where } /// Creates a new `StringOverlay` instance and initializes it with s shape. - /// - `shape`: An `IntShape` instances to be added to the overlay. + /// - `shape`: An `IntShape` instances to be added to the overlay. #[inline] pub fn with_shape(shape: &[IntContour]) -> Self { let mut overlay = Self::new(shape.points_count()); @@ -91,7 +91,7 @@ where } /// Creates a new `StringOverlay` instance and initializes it with subject and clip shapes. - /// - `shapes`: An array of `IntShape` instances to be added to the overlay. + /// - `shapes`: An array of `IntShape` instances to be added to the overlay. #[inline] pub fn with_shapes(shapes: &[IntShape]) -> Self { let mut overlay = Self::new(shapes.points_count()); @@ -116,7 +116,7 @@ where } /// Adds multiple paths to the overlay as shape paths. - /// - `contours`: An array of `IntContour` instances to be added to the overlay. + /// - `contours`: An array of `IntContour` instances to be added to the overlay. pub fn add_shape_contours(&mut self, contours: &[IntContour]) { for contour in contours.iter() { self.add_shape_contour(contour); @@ -124,7 +124,7 @@ where } /// Adds a list of shape to the overlay. - /// - `shapes`: An array of `IntShape` instances to be added to the overlay. + /// - `shapes`: An array of `IntShape` instances to be added to the overlay. #[inline] pub fn add_shapes(&mut self, shapes: &[IntShape]) { for shape in shapes { @@ -228,7 +228,7 @@ where /// - `fill_rule`: Specifies the rule determining the filled areas, influencing the inclusion of line segments. /// - `clip_rule`: The rule for clipping, determining how the boundary and inversion settings affect the result. /// # Returns - /// A vector of `IntPath` instances representing the clipped sections of the input lines. + /// A vector of `IntPath` instances representing the clipped sections of the input lines. #[inline] pub fn clip_string_lines(self, fill_rule: FillRule, clip_rule: ClipRule) -> Vec> { self.clip_string_lines_with_solver(fill_rule, clip_rule, Default::default()) @@ -240,7 +240,7 @@ where /// - `solver`: A solver type to be used for advanced control over the graph building process. /// /// # Returns - /// A vector of `IntPath` instances representing the clipped sections of the input lines. + /// A vector of `IntPath` instances representing the clipped sections of the input lines. #[inline] pub fn clip_string_lines_with_solver( mut self, From 7aefd945b722a14be611ce85205f6b7540dd59fa Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Thu, 21 May 2026 21:00:38 +0300 Subject: [PATCH 16/36] sugar as api --- iOverlay/README.md | 14 ++ iOverlay/src/float/clip.rs | 114 +++++++++++++++- iOverlay/src/float/relate.rs | 97 ++++++++++++- iOverlay/src/float/scale.rs | 173 +++++++++++++++++++++++- iOverlay/src/float/simplify.rs | 56 +++++++- iOverlay/src/float/single.rs | 33 ++++- iOverlay/src/float/slice.rs | 118 +++++++++++++++- iOverlay/src/mesh/outline/offset.rs | 184 +++++++++++++++++++++++++ iOverlay/src/mesh/stroke/offset.rs | 202 ++++++++++++++++++++++++++++ 9 files changed, 970 insertions(+), 21 deletions(-) diff --git a/iOverlay/README.md b/iOverlay/README.md index bd081d0..12b2dc9 100644 --- a/iOverlay/README.md +++ b/iOverlay/README.md @@ -625,6 +625,20 @@ let mut overlay = FloatOverlay::<[f64; 2], i64>::from_subj_and_clip(&subj, &clip let result = overlay.overlay(OverlayRule::Difference, FillRule::EvenOdd); ``` +The sugar traits also use `i32` by default. Use the `*_as::` methods when you want to keep +the shorthand API and still select the integer engine explicitly: + +```rust +use i_overlay::core::fill_rule::FillRule; +use i_overlay::core::overlay_rule::OverlayRule; +use i_overlay::float::single::SingleFloatOverlay; + +let subj = vec![[0.0, 0.0], [0.0, 5.0], [5.0, 5.0], [5.0, 0.0]]; +let clip = vec![[2.0, 2.0], [2.0, 4.0], [4.0, 4.0], [4.0, 2.0]]; + +let result = subj.overlay_as::(&clip, OverlayRule::Difference, FillRule::EvenOdd); +``` + --- ### 5. How do I enable OGC-valid output? diff --git a/iOverlay/src/float/clip.rs b/iOverlay/src/float/clip.rs index 7c9dae6..4e1051c 100644 --- a/iOverlay/src/float/clip.rs +++ b/iOverlay/src/float/clip.rs @@ -4,14 +4,16 @@ 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. /// -/// This convenience trait uses the default integer engine (`i32`). Use -/// `FloatStringOverlay::::from_shape_and_string` when you need to select `i16`, `i32`, -/// or `i64` explicitly. +/// This convenience trait uses the default integer engine (`i32`). Use the `*_as::` methods +/// when you need to select `i16`, `i32`, or `i64` explicitly. pub trait FloatClip where R: ShapeResource

, @@ -30,6 +32,11 @@ where /// A `Paths

` collection of string lines that meet the clipping conditions. fn clip_by(&self, source: &R, fill_rule: FillRule, clip_rule: ClipRule) -> Paths

; + /// 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; + /// Clips paths according to the specified build and clip rules using a fixed float-to-integer scale. /// - `resource`: A clipping shape. /// `ShapeResource` can be one of the following: @@ -50,6 +57,17 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError>; + /// Same as [`Self::clip_by_fixed_scale`], but with an explicit integer engine. + fn clip_by_fixed_scale_as( + &self, + source: &R, + fill_rule: FillRule, + clip_rule: ClipRule, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey; + /// Clips paths according to the specified build and clip rules. /// - `resource`: A clipping shape. /// `ShapeResource` can be one of the following: @@ -70,6 +88,17 @@ where solver: Solver, ) -> Paths

; + /// Same as [`Self::clip_by_with_solver`], but with an explicit integer engine. + fn clip_by_with_solver_as( + &self, + source: &R, + fill_rule: FillRule, + clip_rule: ClipRule, + solver: Solver, + ) -> Paths

+ where + I: IntNumber + Expiration + LayoutNumber + SortKey; + /// Clips paths according to the specified build and clip rules using a fixed float-to-integer scale. /// - `resource`: A clipping shape. /// `ShapeResource` can be one of the following: @@ -91,6 +120,18 @@ where solver: Solver, scale: P::Scalar, ) -> Result, FixedScaleOverlayError>; + + /// Same as [`Self::clip_by_fixed_scale_with_solver`], but with an explicit integer engine. + fn clip_by_fixed_scale_with_solver_as( + &self, + source: &R, + fill_rule: FillRule, + clip_rule: ClipRule, + solver: Solver, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey; } #[cfg(test)] @@ -116,6 +157,12 @@ mod tests { assert_eq!(paths.len(), 1); assert_eq!(paths[0], [[0.0, 1.0], [2.0, 1.0]]); + + let paths = string + .clip_by_fixed_scale_as::(&shape, FillRule::EvenOdd, clip_rule, 10.0) + .unwrap(); + + assert_eq!(paths.len(), 1); } #[test] @@ -161,6 +208,14 @@ where self.clip_by_with_solver(resource, fill_rule, clip_rule, Default::default()) } + #[inline] + fn clip_by_as(&self, resource: &R0, fill_rule: FillRule, clip_rule: ClipRule) -> Paths

+ where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + self.clip_by_with_solver_as::(resource, fill_rule, clip_rule, Default::default()) + } + #[inline] fn clip_by_with_solver( &self, @@ -173,6 +228,21 @@ where .clip_string_lines_with_solver(fill_rule, clip_rule, solver) } + #[inline] + fn clip_by_with_solver_as( + &self, + resource: &R0, + fill_rule: FillRule, + clip_rule: ClipRule, + solver: Solver, + ) -> Paths

+ where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + FloatStringOverlay::::from_shape_and_string(resource, self) + .clip_string_lines_with_solver(fill_rule, clip_rule, solver) + } + #[inline] fn clip_by_fixed_scale( &self, @@ -184,6 +254,26 @@ where self.clip_by_fixed_scale_with_solver(resource, fill_rule, clip_rule, Default::default(), scale) } + #[inline] + fn clip_by_fixed_scale_as( + &self, + resource: &R0, + fill_rule: FillRule, + clip_rule: ClipRule, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + self.clip_by_fixed_scale_with_solver_as::( + resource, + fill_rule, + clip_rule, + Default::default(), + scale, + ) + } + #[inline] fn clip_by_fixed_scale_with_solver( &self, @@ -198,4 +288,22 @@ where .clip_string_lines_with_solver(fill_rule, clip_rule, solver), ) } + + #[inline] + fn clip_by_fixed_scale_with_solver_as( + &self, + resource: &R0, + fill_rule: FillRule, + clip_rule: ClipRule, + solver: Solver, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + Ok( + FloatStringOverlay::::from_shape_and_string_fixed_scale(resource, self, scale)? + .clip_string_lines_with_solver(fill_rule, clip_rule, solver), + ) + } } diff --git a/iOverlay/src/float/relate.rs b/iOverlay/src/float/relate.rs index e52fd67..17af695 100644 --- a/iOverlay/src/float/relate.rs +++ b/iOverlay/src/float/relate.rs @@ -209,9 +209,8 @@ impl FloatPredicateOverlay

{ /// directly on contours, shapes, and shape collections without explicit /// overlay construction. /// -/// This convenience trait uses the default integer engine (`i32`). Use -/// `FloatPredicateOverlay::::from_subj_and_clip` when you need to select `i16`, `i32`, -/// or `i64` explicitly. +/// This convenience trait uses the default integer engine (`i32`). Use the `*_as::` methods +/// when you need to select `i16`, `i32`, or `i64` explicitly. /// /// # Example /// @@ -254,35 +253,70 @@ where /// overlap and boundary contact (shapes sharing an edge). fn intersects(&self, other: &R1) -> bool; + /// Same as [`Self::intersects`], but with an explicit integer engine. + fn intersects_as(&self, other: &R1) -> bool + where + I: IntNumber + Expiration + LayoutNumber + SortKey; + /// Returns `true` if the interiors of this shape and another overlap. /// /// Unlike `intersects()`, this returns `false` for shapes that only share /// boundary points (edges or vertices) without interior overlap. fn interiors_intersect(&self, other: &R1) -> bool; + /// 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; + /// Returns `true` if this shape touches another (boundaries intersect but interiors don't). /// /// Returns `true` when shapes share boundary points but their interiors don't overlap. fn touches(&self, other: &R1) -> bool; + /// Same as [`Self::touches`], but with an explicit integer engine. + fn touches_as(&self, other: &R1) -> bool + where + I: IntNumber + Expiration + LayoutNumber + SortKey; + /// Returns `true` if this shape intersects another by point coincidence only. fn point_intersects(&self, other: &R1) -> bool; + /// 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; + /// Returns `true` if this shape is completely within another. /// /// Subject is within clip if everywhere the subject has fill, the clip /// also has fill on the same side. fn within(&self, other: &R1) -> bool; + /// Same as [`Self::within`], but with an explicit integer engine. + fn within_as(&self, other: &R1) -> bool + where + I: IntNumber + Expiration + LayoutNumber + SortKey; + /// Returns `true` if this shape does not intersect with another (no shared points). /// /// This is the negation of `intersects()`. fn disjoint(&self, other: &R1) -> bool; + /// Same as [`Self::disjoint`], but with an explicit integer engine. + fn disjoint_as(&self, other: &R1) -> bool + where + I: IntNumber + Expiration + LayoutNumber + SortKey; + /// Returns `true` if this shape completely covers another. /// /// `covers(A, B)` is equivalent to `within(B, A)`. fn covers(&self, other: &R1) -> bool; + + /// Same as [`Self::covers`], but with an explicit integer engine. + fn covers_as(&self, other: &R1) -> bool + where + I: IntNumber + Expiration + LayoutNumber + SortKey; } impl FloatRelate for R0 @@ -296,35 +330,91 @@ where FloatPredicateOverlay::

::with_subj_and_clip(self, other).intersects() } + #[inline] + fn intersects_as(&self, other: &R1) -> bool + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + FloatPredicateOverlay::::from_subj_and_clip(self, other).intersects() + } + #[inline] fn interiors_intersect(&self, other: &R1) -> bool { FloatPredicateOverlay::

::with_subj_and_clip(self, other).interiors_intersect() } + #[inline] + fn interiors_intersect_as(&self, other: &R1) -> bool + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + FloatPredicateOverlay::::from_subj_and_clip(self, other).interiors_intersect() + } + #[inline] fn touches(&self, other: &R1) -> bool { FloatPredicateOverlay::

::with_subj_and_clip(self, other).touches() } + #[inline] + fn touches_as(&self, other: &R1) -> bool + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + FloatPredicateOverlay::::from_subj_and_clip(self, other).touches() + } + #[inline] fn point_intersects(&self, other: &R1) -> bool { FloatPredicateOverlay::

::with_subj_and_clip(self, other).point_intersects() } + #[inline] + fn point_intersects_as(&self, other: &R1) -> bool + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + FloatPredicateOverlay::::from_subj_and_clip(self, other).point_intersects() + } + #[inline] fn within(&self, other: &R1) -> bool { FloatPredicateOverlay::

::with_subj_and_clip(self, other).within() } + #[inline] + fn within_as(&self, other: &R1) -> bool + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + FloatPredicateOverlay::::from_subj_and_clip(self, other).within() + } + #[inline] fn disjoint(&self, other: &R1) -> bool { !self.intersects(other) } + #[inline] + fn disjoint_as(&self, other: &R1) -> bool + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + !self.intersects_as::(other) + } + #[inline] fn covers(&self, other: &R1) -> bool { other.within(self) } + + #[inline] + fn covers_as(&self, other: &R1) -> bool + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + other.within_as::(self) + } } #[cfg(test)] @@ -340,6 +430,7 @@ mod tests { assert!(square.intersects(&other)); assert!(other.intersects(&square)); + assert!(square.intersects_as::(&other)); } #[test] diff --git a/iOverlay/src/float/scale.rs b/iOverlay/src/float/scale.rs index 2a14b77..238253e 100644 --- a/iOverlay/src/float/scale.rs +++ b/iOverlay/src/float/scale.rs @@ -55,9 +55,8 @@ impl From for FixedScaleOverlayError { /// `x_int = (x_float - offset_x) * scale`. /// Larger `scale` gives higher precision but must fit within the safe integer bounds. /// -/// This convenience trait uses the default integer engine (`i32`). Use -/// `FloatOverlay::::from_subj_and_clip_fixed_scale` when you need to select `i16`, -/// `i32`, or `i64` explicitly. +/// This convenience trait uses the default integer engine (`i32`). Use the `*_as::` methods +/// when you need to select `i16`, `i32`, or `i64` explicitly. pub trait FixedScaleFloatOverlay where R0: ShapeResource

, @@ -82,6 +81,17 @@ where fill_rule: FillRule, scale: P::Scalar, ) -> Result, FixedScaleOverlayError>; + + /// Same as [`Self::overlay_with_fixed_scale`], but with an explicit integer engine. + fn overlay_with_fixed_scale_as( + &self, + source: &R1, + overlay_rule: OverlayRule, + fill_rule: FillRule, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey; } impl FixedScaleFloatOverlay for R0 @@ -103,6 +113,23 @@ where .overlay(overlay_rule, fill_rule), ) } + + #[inline] + fn overlay_with_fixed_scale_as( + &self, + source: &R1, + overlay_rule: OverlayRule, + fill_rule: FillRule, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + Ok( + FloatOverlay::::from_subj_and_clip_fixed_scale(self, source, scale)? + .overlay(overlay_rule, fill_rule), + ) + } } impl FloatOverlay @@ -327,9 +354,8 @@ impl FloatPredicateOverlay

{ /// float-to-integer scale, which is useful when you need consistent precision /// across multiple operations or when working with known coordinate bounds. /// -/// This convenience trait uses the default integer engine (`i32`). Use -/// `FloatPredicateOverlay::::from_subj_and_clip_fixed_scale` when you need to select -/// `i16`, `i32`, or `i64` explicitly. +/// This convenience trait uses the default integer engine (`i32`). Use the `*_as::` methods +/// when you need to select `i16`, `i32`, or `i64` explicitly. /// /// # Example /// @@ -355,6 +381,15 @@ where scale: P::Scalar, ) -> Result; + /// Same as [`Self::intersects_with_fixed_scale`], but with an explicit integer engine. + fn intersects_with_fixed_scale_as( + &self, + other: &R1, + scale: P::Scalar, + ) -> Result + where + I: IntNumber + Expiration + LayoutNumber + SortKey; + /// Returns `true` if interiors of shapes overlap, using fixed-scale precision. fn interiors_intersect_with_fixed_scale( &self, @@ -362,18 +397,63 @@ where scale: P::Scalar, ) -> Result; + /// Same as [`Self::interiors_intersect_with_fixed_scale`], but with an explicit integer engine. + fn interiors_intersect_with_fixed_scale_as( + &self, + other: &R1, + scale: P::Scalar, + ) -> Result + where + I: IntNumber + Expiration + LayoutNumber + SortKey; + /// 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; + /// Same as [`Self::touches_with_fixed_scale`], but with an explicit integer engine. + fn touches_with_fixed_scale_as( + &self, + other: &R1, + scale: P::Scalar, + ) -> Result + where + I: IntNumber + Expiration + LayoutNumber + SortKey; + /// 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; + /// Same as [`Self::within_with_fixed_scale`], but with an explicit integer engine. + fn within_with_fixed_scale_as( + &self, + other: &R1, + scale: P::Scalar, + ) -> Result + where + I: IntNumber + Expiration + LayoutNumber + SortKey; + /// Returns `true` if shapes do not intersect, using fixed-scale precision. fn disjoint_with_fixed_scale(&self, other: &R1, scale: P::Scalar) -> Result; + /// Same as [`Self::disjoint_with_fixed_scale`], but with an explicit integer engine. + fn disjoint_with_fixed_scale_as( + &self, + other: &R1, + scale: P::Scalar, + ) -> Result + where + I: IntNumber + Expiration + LayoutNumber + SortKey; + /// Returns `true` if this shape completely covers another, using fixed-scale precision. fn covers_with_fixed_scale(&self, other: &R1, scale: P::Scalar) -> Result; + + /// Same as [`Self::covers_with_fixed_scale`], but with an explicit integer engine. + fn covers_with_fixed_scale_as( + &self, + other: &R1, + scale: P::Scalar, + ) -> Result + where + I: IntNumber + Expiration + LayoutNumber + SortKey; } impl FixedScaleFloatRelate for R0 @@ -391,6 +471,18 @@ where Ok(FloatPredicateOverlay::

::with_subj_and_clip_fixed_scale(self, other, scale)?.intersects()) } + #[inline] + fn intersects_with_fixed_scale_as( + &self, + other: &R1, + scale: P::Scalar, + ) -> Result + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + Ok(FloatPredicateOverlay::::from_subj_and_clip_fixed_scale(self, other, scale)?.intersects()) + } + #[inline] fn interiors_intersect_with_fixed_scale( &self, @@ -403,16 +495,55 @@ where ) } + #[inline] + fn interiors_intersect_with_fixed_scale_as( + &self, + other: &R1, + scale: P::Scalar, + ) -> Result + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + Ok( + FloatPredicateOverlay::::from_subj_and_clip_fixed_scale(self, other, scale)? + .interiors_intersect(), + ) + } + #[inline] fn touches_with_fixed_scale(&self, other: &R1, scale: P::Scalar) -> Result { Ok(FloatPredicateOverlay::

::with_subj_and_clip_fixed_scale(self, other, scale)?.touches()) } + #[inline] + fn touches_with_fixed_scale_as( + &self, + other: &R1, + scale: P::Scalar, + ) -> Result + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + Ok(FloatPredicateOverlay::::from_subj_and_clip_fixed_scale(self, other, scale)?.touches()) + } + #[inline] fn within_with_fixed_scale(&self, other: &R1, scale: P::Scalar) -> Result { Ok(FloatPredicateOverlay::

::with_subj_and_clip_fixed_scale(self, other, scale)?.within()) } + #[inline] + fn within_with_fixed_scale_as( + &self, + other: &R1, + scale: P::Scalar, + ) -> Result + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + Ok(FloatPredicateOverlay::::from_subj_and_clip_fixed_scale(self, other, scale)?.within()) + } + #[inline] fn disjoint_with_fixed_scale( &self, @@ -422,10 +553,34 @@ where Ok(!FloatPredicateOverlay::

::with_subj_and_clip_fixed_scale(self, other, scale)?.intersects()) } + #[inline] + fn disjoint_with_fixed_scale_as( + &self, + other: &R1, + scale: P::Scalar, + ) -> Result + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + Ok(!FloatPredicateOverlay::::from_subj_and_clip_fixed_scale(self, other, scale)?.intersects()) + } + #[inline] fn covers_with_fixed_scale(&self, other: &R1, scale: P::Scalar) -> Result { Ok(FloatPredicateOverlay::

::with_subj_and_clip_fixed_scale(other, self, scale)?.within()) } + + #[inline] + fn covers_with_fixed_scale_as( + &self, + other: &R1, + scale: P::Scalar, + ) -> Result + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + Ok(FloatPredicateOverlay::::from_subj_and_clip_fixed_scale(other, self, scale)?.within()) + } } #[cfg(test)] @@ -449,6 +604,12 @@ mod tests { assert_eq!(shapes.len(), 1); assert_eq!(shapes[0].len(), 1); assert_eq!(shapes[0][0].len(), 4); + + let shapes = left_rect + .overlay_with_fixed_scale_as::(&right_rect, OverlayRule::Union, FillRule::EvenOdd, 10.0) + .unwrap(); + + assert_eq!(shapes.len(), 1); } #[test] diff --git a/iOverlay/src/float/simplify.rs b/iOverlay/src/float/simplify.rs index 8dc1282..4d9935b 100644 --- a/iOverlay/src/float/simplify.rs +++ b/iOverlay/src/float/simplify.rs @@ -3,16 +3,18 @@ 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 /// determine which areas should be retained or excluded. /// -/// This convenience trait uses the default integer engine (`i32`). Use -/// `FloatOverlay::::from_subj_custom` with `OverlayRule::Subject` when you need to select -/// `i16`, `i32`, or `i64` explicitly. +/// This convenience trait uses the default integer engine (`i32`). Use the `*_as::` methods +/// when you need to select `i16`, `i32`, or `i64` explicitly. pub trait SimplifyShape { /// Simplifies the shape or collection of points, contours, or shapes, based on a specified minimum area threshold. /// @@ -21,6 +23,11 @@ pub trait SimplifyShape { /// Note: Outer boundary paths have a **main_direction** order, and holes have an opposite to **main_direction** order. fn simplify_shape(&self, fill_rule: FillRule) -> Shapes

; + /// 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; + /// Simplifies the shape or collection of points, contours, or shapes, based on a specified minimum area threshold. /// - `options`: Adjust custom behavior. /// - `solver`: Type of solver to use. @@ -33,6 +40,16 @@ pub trait SimplifyShape { options: OverlayOptions, solver: Solver, ) -> Shapes

; + + /// Same as [`Self::simplify_shape_custom`], but with an explicit integer engine. + fn simplify_shape_custom_as( + &self, + fill_rule: FillRule, + options: OverlayOptions, + solver: Solver, + ) -> Shapes

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

for S @@ -46,6 +63,15 @@ where .overlay(OverlayRule::Subject, fill_rule) } + #[inline] + fn simplify_shape_as(&self, fill_rule: FillRule) -> Shapes

+ where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + FloatOverlay::::from_subj_custom(self, Default::default(), Default::default()) + .overlay(OverlayRule::Subject, fill_rule) + } + #[inline] fn simplify_shape_custom( &self, @@ -55,6 +81,19 @@ where ) -> Shapes

{ FloatOverlay::

::with_subj_custom(self, options, solver).overlay(OverlayRule::Subject, fill_rule) } + + #[inline] + fn simplify_shape_custom_as( + &self, + fill_rule: FillRule, + options: OverlayOptions, + solver: Solver, + ) -> Shapes

+ where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + FloatOverlay::::from_subj_custom(self, options, solver).overlay(OverlayRule::Subject, fill_rule) + } } #[cfg(test)] @@ -74,6 +113,17 @@ mod tests { assert_eq!(shapes[0][0].len(), 4); } + #[test] + fn test_contour_slice_as() { + let rect = [[0.0, 0.0], [0.0, 0.5], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]; + + let shapes = rect.as_slice().simplify_shape_as::(FillRule::NonZero); + + assert_eq!(shapes.len(), 1); + assert_eq!(shapes[0].len(), 1); + assert_eq!(shapes[0][0].len(), 4); + } + #[test] fn test_contour_vec() { let rect = vec![[0.0, 0.0], [0.0, 0.5], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]; diff --git a/iOverlay/src/float/single.rs b/iOverlay/src/float/single.rs index c9c903a..83f3c54 100644 --- a/iOverlay/src/float/single.rs +++ b/iOverlay/src/float/single.rs @@ -2,15 +2,17 @@ use crate::core::fill_rule::FillRule; 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. /// -/// This convenience trait uses the default integer engine (`i32`). Use -/// `FloatOverlay::::from_subj_and_clip` when you need to select `i16`, `i32`, or `i64` -/// explicitly. +/// This convenience trait uses the default integer engine (`i32`). Use the `*_as::` methods +/// when you need to select `i16`, `i32`, or `i64` explicitly. pub trait SingleFloatOverlay where R0: ShapeResource

, @@ -28,6 +30,11 @@ where /// - `fill_rule`: Fill rule to determine filled areas (non-zero, even-odd, positive, negative). /// - Returns: A vector of `Shapes

` representing the cleaned-up geometric result. fn overlay(&self, source: &R1, overlay_rule: OverlayRule, fill_rule: FillRule) -> Shapes

; + + /// 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; } impl SingleFloatOverlay for R0 @@ -40,6 +47,14 @@ where fn overlay(&self, resource: &R1, overlay_rule: OverlayRule, fill_rule: FillRule) -> Shapes

{ FloatOverlay::

::with_subj_and_clip(self, resource).overlay(overlay_rule, fill_rule) } + + #[inline] + fn overlay_as(&self, resource: &R1, overlay_rule: OverlayRule, fill_rule: FillRule) -> Shapes

+ where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + FloatOverlay::::from_subj_and_clip(self, resource).overlay(overlay_rule, fill_rule) + } } #[cfg(test)] @@ -62,6 +77,18 @@ mod tests { assert_eq!(shapes[0][0].len(), 4); } + #[test] + fn test_contour_as() { + let left_rect = vec![[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]; + let right_rect = vec![[1.0, 0.0], [1.0, 1.0], [2.0, 1.0], [2.0, 0.0]]; + + let shapes = left_rect.overlay_as::(&right_rect, OverlayRule::Union, FillRule::EvenOdd); + + assert_eq!(shapes.len(), 1); + assert_eq!(shapes[0].len(), 1); + assert_eq!(shapes[0][0].len(), 4); + } + #[test] fn test_contours() { let r3 = vec![ diff --git a/iOverlay/src/float/slice.rs b/iOverlay/src/float/slice.rs index 69ecd34..aa1d82f 100644 --- a/iOverlay/src/float/slice.rs +++ b/iOverlay/src/float/slice.rs @@ -5,15 +5,17 @@ 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. /// -/// This convenience trait uses the default integer engine (`i32`). Use -/// `FloatStringOverlay::::from_shape_and_string` when you need to select `i16`, `i32`, -/// or `i64` explicitly. +/// This convenience trait uses the default integer engine (`i32`). Use the `*_as::` methods +/// when you need to select `i16`, `i32`, or `i64` explicitly. pub trait FloatSlice where R: ShapeResource

, @@ -33,6 +35,11 @@ where /// Note: Outer boundary paths have a counterclockwise order, and holes have a clockwise order. fn slice_by(&self, resource: &R, fill_rule: FillRule) -> Shapes

; + /// 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; + /// Slices the current shapes by string lines with a fixed float-to-integer scale. /// /// - `resource`: A string lines. @@ -53,6 +60,16 @@ where scale: P::Scalar, ) -> Result, FixedScaleOverlayError>; + /// Same as [`Self::slice_by_fixed_scale`], but with an explicit integer engine. + fn slice_by_fixed_scale_as( + &self, + resource: &R, + fill_rule: FillRule, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey; + /// Slices the current shapes by string lines. /// /// - `resource`: A string lines. @@ -74,6 +91,17 @@ where solver: Solver, ) -> Shapes

; + /// Same as [`Self::slice_custom_by`], but with an explicit integer engine. + fn slice_custom_by_as( + &self, + resource: &R, + fill_rule: FillRule, + options: OverlayOptions, + solver: Solver, + ) -> Shapes

+ where + I: IntNumber + Expiration + LayoutNumber + SortKey; + /// Slices the current shapes by string lines with a fixed float-to-integer scale. /// /// - `resource`: A string lines. @@ -96,6 +124,18 @@ where solver: Solver, scale: P::Scalar, ) -> Result, FixedScaleOverlayError>; + + /// Same as [`Self::slice_custom_by_fixed_scale`], but with an explicit integer engine. + fn slice_custom_by_fixed_scale_as( + &self, + resource: &R, + fill_rule: FillRule, + options: OverlayOptions, + solver: Solver, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey; } impl FloatSlice for R1 @@ -112,6 +152,17 @@ where .unwrap_or_default() } + #[inline] + fn slice_by_as(&self, resource: &R0, fill_rule: FillRule) -> Shapes

+ where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + FloatStringOverlay::::from_shape_and_string(self, resource) + .build_graph_view(fill_rule) + .map(|graph| graph.extract_shapes(StringRule::Slice)) + .unwrap_or_default() + } + #[inline] fn slice_by_fixed_scale( &self, @@ -127,6 +178,24 @@ where ) } + #[inline] + fn slice_by_fixed_scale_as( + &self, + resource: &R0, + fill_rule: FillRule, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + Ok( + FloatStringOverlay::::from_shape_and_string_fixed_scale(self, resource, scale)? + .build_graph_view(fill_rule) + .map(|graph| graph.extract_shapes(StringRule::Slice)) + .unwrap_or_default(), + ) + } + #[inline] fn slice_custom_by( &self, @@ -141,6 +210,23 @@ where .unwrap_or_default() } + #[inline] + fn slice_custom_by_as( + &self, + resource: &R0, + fill_rule: FillRule, + options: OverlayOptions, + solver: Solver, + ) -> Shapes

+ where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + FloatStringOverlay::::from_shape_and_string(self, resource) + .build_graph_view_with_solver(fill_rule, solver) + .map(|graph| graph.extract_shapes_custom(StringRule::Slice, options)) + .unwrap_or_default() + } + #[inline] fn slice_custom_by_fixed_scale( &self, @@ -157,6 +243,26 @@ where .unwrap_or_default(), ) } + + #[inline] + fn slice_custom_by_fixed_scale_as( + &self, + resource: &R0, + fill_rule: FillRule, + options: OverlayOptions, + solver: Solver, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey, + { + Ok( + FloatStringOverlay::::from_shape_and_string_fixed_scale(self, resource, scale)? + .build_graph_view_with_solver(fill_rule, solver) + .map(|graph| graph.extract_shapes_custom(StringRule::Slice, options)) + .unwrap_or_default(), + ) + } } #[cfg(test)] @@ -202,6 +308,12 @@ mod tests { assert_eq!(shapes[1].len(), 1); assert_eq!(shapes[0][0].len(), 4); assert_eq!(shapes[1][0].len(), 4); + + let shapes = shape + .slice_by_fixed_scale_as::(&string, FillRule::EvenOdd, 10.0) + .unwrap(); + + assert_eq!(shapes.len(), 2); } #[test] diff --git a/iOverlay/src/mesh/outline/offset.rs b/iOverlay/src/mesh/outline/offset.rs index e34b648..79edbe5 100644 --- a/iOverlay/src/mesh/outline/offset.rs +++ b/iOverlay/src/mesh/outline/offset.rs @@ -124,6 +124,74 @@ pub trait OutlineOffset { scale: P::Scalar, output: &mut FloatFlatContoursBuffer

, ) -> Result<(), FixedScaleOverlayError>; + + /// Same as [`Self::outline`], but with an explicit integer engine. + fn outline_as(&self, style: &OutlineStyle) -> Shapes

+ where + I: IntNumber + Expiration + LayoutNumber + SortKey + '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; + + /// Same as [`Self::outline_custom`], but with an explicit integer engine. + fn outline_custom_as( + &self, + style: &OutlineStyle, + options: OverlayOptions, + ) -> Shapes

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

, + ) where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + + /// Same as [`Self::outline_fixed_scale`], but with an explicit integer engine. + fn outline_fixed_scale_as( + &self, + style: &OutlineStyle, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + + /// Same as [`Self::outline_fixed_scale_into`], but with an explicit integer engine. + fn outline_fixed_scale_into_as( + &self, + style: &OutlineStyle, + scale: P::Scalar, + output: &mut FloatFlatContoursBuffer

, + ) -> Result<(), FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + + /// Same as [`Self::outline_custom_fixed_scale`], but with an explicit integer engine. + fn outline_custom_fixed_scale_as( + &self, + style: &OutlineStyle, + options: OverlayOptions, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + + /// Same as [`Self::outline_custom_fixed_scale_into`], but with an explicit integer engine. + fn outline_custom_fixed_scale_into_as( + &self, + style: &OutlineStyle, + options: OverlayOptions, + scale: P::Scalar, + output: &mut FloatFlatContoursBuffer

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

for S @@ -213,6 +281,112 @@ where solver.build_into(self, options, output); Ok(()) } + + fn outline_as(&self, style: &OutlineStyle) -> Shapes

+ where + I: IntNumber + Expiration + LayoutNumber + SortKey + '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, + { + self.outline_custom_into_as::(style, Default::default(), output) + } + + fn outline_custom_as( + &self, + style: &OutlineStyle, + options: OverlayOptions, + ) -> Shapes

+ where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + { + match OutlineSolver::::prepare(self, style) { + Some(solver) => solver.build(self, options), + None => vec![], + } + } + + fn outline_custom_into_as( + &self, + style: &OutlineStyle, + options: OverlayOptions, + output: &mut FloatFlatContoursBuffer

, + ) where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + { + match OutlineSolver::::prepare(self, style) { + Some(solver) => solver.build_into(self, options, output), + None => output.clear_and_reserve(0, 0), + } + } + + fn outline_fixed_scale_as( + &self, + style: &OutlineStyle, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + { + self.outline_custom_fixed_scale_as::(style, Default::default(), scale) + } + + fn outline_fixed_scale_into_as( + &self, + style: &OutlineStyle, + scale: P::Scalar, + output: &mut FloatFlatContoursBuffer

, + ) -> Result<(), FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + { + self.outline_custom_fixed_scale_into_as::(style, Default::default(), scale, output) + } + + fn outline_custom_fixed_scale_as( + &self, + style: &OutlineStyle, + options: OverlayOptions, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + { + let s = FixedScaleOverlayError::validate_scale(scale)?; + let mut solver = match OutlineSolver::::prepare(self, style) { + Some(solver) => solver, + None => return Ok(vec![]), + }; + solver.apply_scale(s)?; + Ok(solver.build(self, options)) + } + + fn outline_custom_fixed_scale_into_as( + &self, + style: &OutlineStyle, + options: OverlayOptions, + scale: P::Scalar, + output: &mut FloatFlatContoursBuffer

, + ) -> Result<(), FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + { + let s = FixedScaleOverlayError::validate_scale(scale)?; + let mut solver = match OutlineSolver::::prepare(self, style) { + Some(solver) => solver, + None => { + output.clear_and_reserve(0, 0); + return Ok(()); + } + }; + solver.apply_scale(s)?; + solver.build_into(self, options, output); + Ok(()) + } } struct OutlineSolver { @@ -434,6 +608,16 @@ mod tests { assert_eq!(shape.len(), 1); } + #[test] + fn test_triangle_round_corner_as() { + let path = [[0.0, 0.0f32], [10.0, 0.0f32], [0.0, 10.0f32]]; + + let style = OutlineStyle::new(5.0).line_join(LineJoin::Round(0.25 * PI)); + let shapes = path.outline_as::(&style); + + assert_eq!(shapes.len(), 1); + } + #[test] fn test_reversed_triangle_round_corner() { let path = [[0.0, 0.0f32], [0.0, 10.0f32], [10.0, 0.0f32]]; diff --git a/iOverlay/src/mesh/stroke/offset.rs b/iOverlay/src/mesh/stroke/offset.rs index 94eae6a..15a4331 100644 --- a/iOverlay/src/mesh/stroke/offset.rs +++ b/iOverlay/src/mesh/stroke/offset.rs @@ -144,6 +144,84 @@ pub trait StrokeOffset { scale: P::Scalar, output: &mut FloatFlatContoursBuffer

, ) -> Result<(), FixedScaleOverlayError>; + + /// 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; + + /// Same as [`Self::stroke_into`], but with an explicit integer engine. + fn stroke_into_as( + &self, + style: StrokeStyle

, + is_closed_path: bool, + output: &mut FloatFlatContoursBuffer

, + ) where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + + /// Same as [`Self::stroke_custom`], but with an explicit integer engine. + fn stroke_custom_as( + &self, + style: StrokeStyle

, + is_closed_path: bool, + options: OverlayOptions, + ) -> Shapes

+ where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + + /// Same as [`Self::stroke_custom_into`], but with an explicit integer engine. + fn stroke_custom_into_as( + &self, + style: StrokeStyle

, + is_closed_path: bool, + options: OverlayOptions, + output: &mut FloatFlatContoursBuffer

, + ) where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + + /// Same as [`Self::stroke_fixed_scale`], but with an explicit integer engine. + fn stroke_fixed_scale_as( + &self, + style: StrokeStyle

, + is_closed_path: bool, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + + /// Same as [`Self::stroke_fixed_scale_into`], but with an explicit integer engine. + fn stroke_fixed_scale_into_as( + &self, + style: StrokeStyle

, + is_closed_path: bool, + scale: P::Scalar, + output: &mut FloatFlatContoursBuffer

, + ) -> Result<(), FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + + /// Same as [`Self::stroke_custom_fixed_scale`], but with an explicit integer engine. + fn stroke_custom_fixed_scale_as( + &self, + style: StrokeStyle

, + is_closed_path: bool, + options: OverlayOptions, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static; + + /// Same as [`Self::stroke_custom_fixed_scale_into`], but with an explicit integer engine. + fn stroke_custom_fixed_scale_into_as( + &self, + style: StrokeStyle

, + is_closed_path: bool, + options: OverlayOptions, + scale: P::Scalar, + output: &mut FloatFlatContoursBuffer

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

for S @@ -242,6 +320,120 @@ where solver.build_into(self, is_closed_path, options, output); Ok(()) } + + fn stroke_as(&self, style: StrokeStyle

, is_closed_path: bool) -> Shapes

+ where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + { + self.stroke_custom_as::(style, is_closed_path, Default::default()) + } + + fn stroke_into_as( + &self, + style: StrokeStyle

, + is_closed_path: bool, + output: &mut FloatFlatContoursBuffer

, + ) where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + { + self.stroke_custom_into_as::(style, is_closed_path, Default::default(), output) + } + + fn stroke_custom_as( + &self, + style: StrokeStyle

, + is_closed_path: bool, + options: OverlayOptions, + ) -> Shapes

+ where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + { + match StrokeSolver::::prepare(self, style) { + Some(solver) => solver.build(self, is_closed_path, options), + None => vec![], + } + } + + fn stroke_custom_into_as( + &self, + style: StrokeStyle

, + is_closed_path: bool, + options: OverlayOptions, + output: &mut FloatFlatContoursBuffer

, + ) where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + { + match StrokeSolver::::prepare(self, style) { + Some(solver) => solver.build_into(self, is_closed_path, options, output), + None => output.clear_and_reserve(0, 0), + } + } + + fn stroke_fixed_scale_as( + &self, + style: StrokeStyle

, + is_closed_path: bool, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + { + self.stroke_custom_fixed_scale_as::(style, is_closed_path, Default::default(), scale) + } + + fn stroke_fixed_scale_into_as( + &self, + style: StrokeStyle

, + is_closed_path: bool, + scale: P::Scalar, + output: &mut FloatFlatContoursBuffer

, + ) -> Result<(), FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + { + self.stroke_custom_fixed_scale_into_as::(style, is_closed_path, Default::default(), scale, output) + } + + fn stroke_custom_fixed_scale_as( + &self, + style: StrokeStyle

, + is_closed_path: bool, + options: OverlayOptions, + scale: P::Scalar, + ) -> Result, FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + { + let mut solver = match StrokeSolver::::prepare(self, style) { + Some(solver) => solver, + None => return Ok(vec![]), + }; + solver.apply_scale(scale)?; + Ok(solver.build(self, is_closed_path, options)) + } + + fn stroke_custom_fixed_scale_into_as( + &self, + style: StrokeStyle

, + is_closed_path: bool, + options: OverlayOptions, + scale: P::Scalar, + output: &mut FloatFlatContoursBuffer

, + ) -> Result<(), FixedScaleOverlayError> + where + I: IntNumber + Expiration + LayoutNumber + SortKey + 'static, + { + let mut solver = match StrokeSolver::::prepare(self, style) { + Some(solver) => solver, + None => { + output.clear_and_reserve(0, 0); + return Ok(()); + } + }; + solver.apply_scale(scale)?; + solver.build_into(self, is_closed_path, options, output); + Ok(()) + } } struct StrokeSolver { @@ -419,6 +611,16 @@ mod tests { assert_eq!(shapes.len(), 1); } + #[test] + fn test_simple_as() { + let path = [[0.0, 0.0], [10.0, 0.0]]; + + let style = StrokeStyle::new(2.0); + let shapes = path.stroke_as::(style, false); + + assert_eq!(shapes.len(), 1); + } + #[test] fn test_bevel_join() { let path = [[-10.0, 0.0], [0.0, 0.0], [0.0, 10.0]]; From 916759d902a0ac1a8bde1085ba3961f6baeceb45 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Thu, 21 May 2026 21:12:33 +0300 Subject: [PATCH 17/36] doc + --- iOverlay/src/float/clip.rs | 16 ++++++++++++++++ iOverlay/src/float/overlay.rs | 16 ++++++++++++++++ iOverlay/src/float/relate.rs | 3 +++ iOverlay/src/float/scale.rs | 24 ++++++++++++++++++++++++ iOverlay/src/float/simplify.rs | 13 +++++++++++++ iOverlay/src/float/single.rs | 15 +++++++++++++++ iOverlay/src/float/slice.rs | 14 ++++++++++++++ iOverlay/src/mesh/outline/offset.rs | 18 ++++++++++++++++++ iOverlay/src/mesh/stroke/offset.rs | 18 ++++++++++++++++++ 9 files changed, 137 insertions(+) diff --git a/iOverlay/src/float/clip.rs b/iOverlay/src/float/clip.rs index 4e1051c..85969c7 100644 --- a/iOverlay/src/float/clip.rs +++ b/iOverlay/src/float/clip.rs @@ -14,6 +14,22 @@ use i_tree::{Expiration, LayoutNumber}; /// /// This convenience trait uses the default integer engine (`i32`). Use the `*_as::` methods /// when you need to select `i16`, `i32`, or `i64` explicitly. +/// +/// # Example +/// +/// ``` +/// use i_overlay::core::fill_rule::FillRule; +/// use i_overlay::float::clip::FloatClip; +/// use i_overlay::string::clip::ClipRule; +/// +/// let shape = vec![[0.0, 0.0], [0.0, 2.0], [2.0, 2.0], [2.0, 0.0]]; +/// let line = vec![[-1.0, 1.0], [3.0, 1.0]]; +/// let clip_rule = ClipRule { invert: false, boundary_included: false }; +/// +/// let result = line.clip_by_as::(&shape, FillRule::EvenOdd, clip_rule); +/// +/// assert_eq!(result.len(), 1); +/// ``` pub trait FloatClip where R: ShapeResource

, diff --git a/iOverlay/src/float/overlay.rs b/iOverlay/src/float/overlay.rs index 94792b7..0f9d036 100644 --- a/iOverlay/src/float/overlay.rs +++ b/iOverlay/src/float/overlay.rs @@ -128,6 +128,22 @@ where /// - `Contour`: A contour representing a closed path. This path is interpreted as closed, so it doesn’t require the start and endpoint to be the same for processing. /// - `Contours`: A collection of contours, each representing a closed path. /// - `Shapes`: A collection of shapes, where each shape may consist of multiple contours. + /// + /// # Example + /// + /// ``` + /// use i_overlay::core::fill_rule::FillRule; + /// use i_overlay::core::overlay_rule::OverlayRule; + /// use i_overlay::float::overlay::FloatOverlay; + /// + /// let subj = vec![[0.0, 0.0], [0.0, 5.0], [5.0, 5.0], [5.0, 0.0]]; + /// let clip = vec![[2.0, 2.0], [2.0, 4.0], [4.0, 4.0], [4.0, 2.0]]; + /// + /// let mut overlay = FloatOverlay::<[f64; 2], i64>::from_subj_and_clip(&subj, &clip); + /// let result = overlay.overlay(OverlayRule::Difference, FillRule::EvenOdd); + /// + /// assert_eq!(result.len(), 1); + /// ``` pub fn from_subj_and_clip(subj: &R0, clip: &R1) -> Self where R0: ShapeResource

+ ?Sized, diff --git a/iOverlay/src/float/relate.rs b/iOverlay/src/float/relate.rs index 17af695..7ac97a9 100644 --- a/iOverlay/src/float/relate.rs +++ b/iOverlay/src/float/relate.rs @@ -230,6 +230,9 @@ impl FloatPredicateOverlay

{ /// // Non-overlapping shapes (fast bounding-box rejection) /// assert!(!square.intersects(&distant)); /// assert!(square.disjoint(&distant)); +/// +/// // Select the integer engine explicitly. +/// assert!(square.intersects_as::(&other)); /// ``` /// /// # Supported Types diff --git a/iOverlay/src/float/scale.rs b/iOverlay/src/float/scale.rs index 238253e..6e567e8 100644 --- a/iOverlay/src/float/scale.rs +++ b/iOverlay/src/float/scale.rs @@ -57,6 +57,26 @@ impl From for FixedScaleOverlayError { /// /// This convenience trait uses the default integer engine (`i32`). Use the `*_as::` methods /// when you need to select `i16`, `i32`, or `i64` explicitly. +/// +/// # Example +/// +/// ``` +/// use i_overlay::core::fill_rule::FillRule; +/// use i_overlay::core::overlay_rule::OverlayRule; +/// use i_overlay::float::scale::FixedScaleFloatOverlay; +/// +/// let subj = vec![[0.0, 0.0], [0.0, 5.0], [5.0, 5.0], [5.0, 0.0]]; +/// let clip = vec![[2.0, 2.0], [2.0, 4.0], [4.0, 4.0], [4.0, 2.0]]; +/// +/// let result = subj.overlay_with_fixed_scale_as::( +/// &clip, +/// OverlayRule::Difference, +/// FillRule::EvenOdd, +/// 1000.0, +/// ); +/// +/// assert!(result.is_ok()); +/// ``` pub trait FixedScaleFloatOverlay where R0: ShapeResource

, @@ -368,6 +388,10 @@ impl FloatPredicateOverlay

{ /// // Use fixed scale of 1000.0 for consistent precision /// let result = square.intersects_with_fixed_scale(&other, 1000.0); /// assert!(result.unwrap()); +/// +/// // Select the integer engine explicitly. +/// let result = square.intersects_with_fixed_scale_as::(&other, 1000.0); +/// assert!(result.unwrap()); /// ``` pub trait FixedScaleFloatRelate where diff --git a/iOverlay/src/float/simplify.rs b/iOverlay/src/float/simplify.rs index 4d9935b..82ec47b 100644 --- a/iOverlay/src/float/simplify.rs +++ b/iOverlay/src/float/simplify.rs @@ -15,6 +15,19 @@ use i_tree::{Expiration, LayoutNumber}; /// /// This convenience trait uses the default integer engine (`i32`). Use the `*_as::` methods /// when you need to select `i16`, `i32`, or `i64` explicitly. +/// +/// # Example +/// +/// ``` +/// use i_overlay::core::fill_rule::FillRule; +/// use i_overlay::float::simplify::SimplifyShape; +/// +/// let shape = vec![[0.0, 0.0], [0.0, 0.5], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]; +/// +/// let result = shape.simplify_shape_as::(FillRule::NonZero); +/// +/// assert_eq!(result.len(), 1); +/// ``` pub trait SimplifyShape { /// Simplifies the shape or collection of points, contours, or shapes, based on a specified minimum area threshold. /// diff --git a/iOverlay/src/float/single.rs b/iOverlay/src/float/single.rs index 83f3c54..567b2d5 100644 --- a/iOverlay/src/float/single.rs +++ b/iOverlay/src/float/single.rs @@ -13,6 +13,21 @@ use i_tree::{Expiration, LayoutNumber}; /// /// This convenience trait uses the default integer engine (`i32`). Use the `*_as::` methods /// when you need to select `i16`, `i32`, or `i64` explicitly. +/// +/// # Example +/// +/// ``` +/// use i_overlay::core::fill_rule::FillRule; +/// use i_overlay::core::overlay_rule::OverlayRule; +/// use i_overlay::float::single::SingleFloatOverlay; +/// +/// let subj = vec![[0.0, 0.0], [0.0, 5.0], [5.0, 5.0], [5.0, 0.0]]; +/// let clip = vec![[2.0, 2.0], [2.0, 4.0], [4.0, 4.0], [4.0, 2.0]]; +/// +/// let result = subj.overlay_as::(&clip, OverlayRule::Difference, FillRule::EvenOdd); +/// +/// assert_eq!(result.len(), 1); +/// ``` pub trait SingleFloatOverlay where R0: ShapeResource

, diff --git a/iOverlay/src/float/slice.rs b/iOverlay/src/float/slice.rs index aa1d82f..24e7cb2 100644 --- a/iOverlay/src/float/slice.rs +++ b/iOverlay/src/float/slice.rs @@ -16,6 +16,20 @@ use i_tree::{Expiration, LayoutNumber}; /// /// This convenience trait uses the default integer engine (`i32`). Use the `*_as::` methods /// when you need to select `i16`, `i32`, or `i64` explicitly. +/// +/// # Example +/// +/// ``` +/// use i_overlay::core::fill_rule::FillRule; +/// use i_overlay::float::slice::FloatSlice; +/// +/// let shape = vec![[0.0, 0.0], [0.0, 2.0], [2.0, 2.0], [2.0, 0.0]]; +/// let line = vec![[-1.0, 1.0], [3.0, 1.0]]; +/// +/// let result = shape.slice_by_as::(&line, FillRule::EvenOdd); +/// +/// assert_eq!(result.len(), 2); +/// ``` pub trait FloatSlice where R: ShapeResource

, diff --git a/iOverlay/src/mesh/outline/offset.rs b/iOverlay/src/mesh/outline/offset.rs index 79edbe5..7424698 100644 --- a/iOverlay/src/mesh/outline/offset.rs +++ b/iOverlay/src/mesh/outline/offset.rs @@ -27,6 +27,24 @@ use i_shape::float::simple::SimplifyContour; use i_shape::source::resource::ShapeResource; use i_tree::{Expiration, LayoutNumber}; +/// Trait for offsetting float contours and shapes. +/// +/// Default methods use the `i32` integer engine. Use the `*_as::` methods when you need to +/// select `i16`, `i32`, or `i64` explicitly. +/// +/// # Example +/// +/// ``` +/// use i_overlay::mesh::outline::offset::OutlineOffset; +/// use i_overlay::mesh::style::OutlineStyle; +/// +/// let path = [[0.0, 0.0], [10.0, 0.0], [0.0, 10.0]]; +/// let style = OutlineStyle::new(1.0); +/// +/// let result = path.outline_as::(&style); +/// +/// assert_eq!(result.len(), 1); +/// ``` pub trait OutlineOffset { /// Generates an outline shapes for contours, or shapes. /// diff --git a/iOverlay/src/mesh/stroke/offset.rs b/iOverlay/src/mesh/stroke/offset.rs index 15a4331..eee953a 100644 --- a/iOverlay/src/mesh/stroke/offset.rs +++ b/iOverlay/src/mesh/stroke/offset.rs @@ -24,6 +24,24 @@ 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. +/// +/// Default methods use the `i32` integer engine. Use the `*_as::` methods when you need to +/// select `i16`, `i32`, or `i64` explicitly. +/// +/// # Example +/// +/// ``` +/// use i_overlay::mesh::stroke::offset::StrokeOffset; +/// use i_overlay::mesh::style::StrokeStyle; +/// +/// let path = [[0.0, 0.0], [10.0, 0.0]]; +/// let style = StrokeStyle::new(2.0); +/// +/// let result = path.stroke_as::(style, false); +/// +/// assert_eq!(result.len(), 1); +/// ``` pub trait StrokeOffset { /// Generates a stroke shapes for paths, contours, or shapes. /// From 5ab7095d484180ef27b0e1dce5fd1fe992860495 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Thu, 21 May 2026 21:42:45 +0300 Subject: [PATCH 18/36] rename --- performance/rust_app/src/test/test_3_spiral.rs | 2 +- performance/rust_app/src/test/test_6_corrosion.rs | 2 +- performance/rust_app/src/test/test_7_concentric.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/performance/rust_app/src/test/test_3_spiral.rs b/performance/rust_app/src/test/test_3_spiral.rs index 70b9fa0..6ead021 100644 --- a/performance/rust_app/src/test/test_3_spiral.rs +++ b/performance/rust_app/src/test/test_3_spiral.rs @@ -73,7 +73,7 @@ impl SpiralTest { let start = Instant::now(); for _ in 0..sq_it_count { - let _ = FloatOverlay::, I>::with_subj_custom_with_int( + let _ = FloatOverlay::, I>::from_subj_custom( &subj_path, Default::default(), solver, diff --git a/performance/rust_app/src/test/test_6_corrosion.rs b/performance/rust_app/src/test/test_6_corrosion.rs index 7859324..11e4add 100644 --- a/performance/rust_app/src/test/test_6_corrosion.rs +++ b/performance/rust_app/src/test/test_6_corrosion.rs @@ -52,7 +52,7 @@ impl CorrosionTest { let start = Instant::now(); for _ in 0..sq_it_count { - let mut overlay = FloatOverlay::<[f64; 2], I>::with_subj_and_clip_custom_with_int( + let mut overlay = FloatOverlay::<[f64; 2], I>::from_subj_and_clip_custom( &subj_paths, &clip_paths, Default::default(), diff --git a/performance/rust_app/src/test/test_7_concentric.rs b/performance/rust_app/src/test/test_7_concentric.rs index b2eef89..41355a7 100644 --- a/performance/rust_app/src/test/test_7_concentric.rs +++ b/performance/rust_app/src/test/test_7_concentric.rs @@ -65,7 +65,7 @@ impl ConcentricTest { let start = Instant::now(); for _ in 0..sq_it_count { - let mut overlay = FloatOverlay::<[f64; 2], I>::with_subj_and_clip_custom_with_int( + let mut overlay = FloatOverlay::<[f64; 2], I>::from_subj_and_clip_custom( &subj_paths, &clip_paths, Default::default(), From 1780a823c63571385258cfa853eb2bb8b4017dff Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Fri, 22 May 2026 08:57:37 +0300 Subject: [PATCH 19/36] run tests --- performance/rust_app/src/main.rs | 24 ++-- .../rust_app/src/test/test_0_checkerboard.rs | 104 +++++++++++--- .../rust_app/src/test/test_1_not_overlap.rs | 104 +++++++++++--- .../rust_app/src/test/test_2_lines_net.rs | 132 ++++++++++------- .../rust_app/src/test/test_3_spiral.rs | 136 ++++++++++++------ .../rust_app/src/test/test_4_windows.rs | 96 ++++++++++--- .../src/test/test_5_nested_squares.rs | 96 ++++++++----- 7 files changed, 490 insertions(+), 202 deletions(-) diff --git a/performance/rust_app/src/main.rs b/performance/rust_app/src/main.rs index 2e41db2..eccd745 100644 --- a/performance/rust_app/src/main.rs +++ b/performance/rust_app/src/main.rs @@ -63,9 +63,9 @@ fn main() { if args_map.is_empty() { args_map.insert("multithreading".to_string(), "false".to_string()); args_map.insert("complex".to_string(), "false".to_string()); - args_map.insert("test".to_string(), 7.to_string()); - args_map.insert("int".to_string(), "i32".to_string()); - let count = 32; + args_map.insert("test".to_string(), 3.to_string()); + args_map.insert("int".to_string(), "i64".to_string()); + let count = 4096; args_map.insert("count".to_string(), count.to_string()); } } @@ -201,7 +201,7 @@ fn run_selected_test_with_int( fn run_test_0(solver: Solver) { println!("run Checkerboard test"); - for i in 1..12 { + for i in 1..11 { let n = 1 << i; CheckerboardTest::run::(n, OverlayRule::Xor, solver, 1000.0); } @@ -209,7 +209,7 @@ fn run_test_0(solver: Solver) { fn run_test_1(solver: Solver) { println!("run NotOverlap test"); - for i in 1..12 { + for i in 1..11 { let n = 1 << i; NotOverlapTest::run::(n, OverlayRule::Xor, solver, 1000.0); } @@ -217,7 +217,7 @@ fn run_test_1(solver: Solver) { fn run_test_2(solver: Solver) { println!("run LinesNet test"); - for i in 1..12 { + for i in 1..11 { let n = 1 << i; LinesNetTest::run::(n, OverlayRule::Intersect, solver, 500.0); } @@ -225,7 +225,7 @@ fn run_test_2(solver: Solver) { fn run_test_3(solver: Solver) { println!("run Spiral test"); - for i in 1..21 { + for i in 1..20 { let n = 1 << i; SpiralTest::run::(n, solver, 1000.0) } @@ -233,7 +233,7 @@ fn run_test_3(solver: Solver) { fn run_test_4(solver: Solver) { println!("run Windows test"); - for i in 1..12 { + for i in 1..11 { let n = 1 << i; WindowsTest::run::(n, OverlayRule::Difference, solver, 500.0); } @@ -241,7 +241,7 @@ fn run_test_4(solver: Solver) { fn run_test_5(solver: Solver) { println!("run NestedSquares test"); - for i in 1..18 { + for i in 1..17 { let n = 1 << i; CrossTest::run::(n, OverlayRule::Xor, solver, 500.0); } @@ -250,7 +250,7 @@ fn run_test_5(solver: Solver) { fn run_test_6(solver: Solver) { println!("run Corrosion test"); let mut n = 1; - for _ in 1..12 { + for _ in 1..11 { CorrosionTest::run::(n, OverlayRule::Difference, solver, 100.0); n = n << 1; } @@ -259,7 +259,7 @@ fn run_test_6(solver: Solver) { fn run_test_7(solver: Solver) { println!("run Concentric test"); let mut n = 1; - for _ in 1..12 { + for _ in 1..11 { ConcentricTest::run::(n, OverlayRule::Intersect, solver, 100.0); n = n << 1; } @@ -268,7 +268,7 @@ fn run_test_7(solver: Solver) { fn run_test_8(solver: Solver) { println!("run WindMill test"); let mut n = 1; - for _ in 1..12 { + for _ in 1..11 { WindMillTest::run::(n, OverlayRule::Difference, solver, 100.0); n = n << 1; } diff --git a/performance/rust_app/src/test/test_0_checkerboard.rs b/performance/rust_app/src/test/test_0_checkerboard.rs index 62a67d0..bd44d41 100644 --- a/performance/rust_app/src/test/test_0_checkerboard.rs +++ b/performance/rust_app/src/test/test_0_checkerboard.rs @@ -12,33 +12,93 @@ pub(crate) struct CheckerboardTest; // test 0 // Xor: +i16 + +multithreading on + +2(5 0.7) - 0.000003(-5.6) +4(25 1.4) - 0.000015(-4.8) +8(113 2.1) - 0.000083(-4.1) +16(481 2.7) - 0.000437(-3.4) +32(1985 3.3) - 0.002305(-2.6) +64(8065 3.9) - 0.007140(-2.1) +128(32513 4.5) - 0.027857(-1.6) +256(130561 5.1) - 0.120606(-0.9) +512(523265 5.7) - 0.568957(-0.2) +1024(2095105 6.3) - 2.413621(0.4) + +multithreading off + +2(5 0.7) - 0.000002(-5.6) +4(25 1.4) - 0.000015(-4.8) +8(113 2.1) - 0.000082(-4.1) +16(481 2.7) - 0.000426(-3.4) +32(1985 3.3) - 0.002236(-2.7) +64(8065 3.9) - 0.008070(-2.1) +128(32513 4.5) - 0.037559(-1.4) +256(130561 5.1) - 0.167375(-0.8) +512(523265 5.7) - 0.776731(-0.1) +1024(2095105 6.3) - 3.385874(0.5) + +i32 + +multithreading on + +2(5 0.7) - 0.000002(-5.6) +4(25 1.4) - 0.000015(-4.8) +8(113 2.1) - 0.000081(-4.1) +16(481 2.7) - 0.000409(-3.4) +32(1985 3.3) - 0.002299(-2.6) +64(8065 3.9) - 0.007243(-2.1) +128(32513 4.5) - 0.028986(-1.5) +256(130561 5.1) - 0.125711(-0.9) +512(523265 5.7) - 0.592768(-0.2) +1024(2095105 6.3) - 2.610431(0.4) +2048(8384513 6.9) - 15.529388(1.2) + +multithreading off + +2(5 0.7) - 0.000002(-5.6) +4(25 1.4) - 0.000015(-4.8) +8(113 2.1) - 0.000082(-4.1) +16(481 2.7) - 0.000412(-3.4) +32(1985 3.3) - 0.002318(-2.6) +64(8065 3.9) - 0.008659(-2.1) +128(32513 4.5) - 0.040512(-1.4) +256(130561 5.1) - 0.178425(-0.7) +512(523265 5.7) - 0.827264(-0.1) +1024(2095105 6.3) - 3.611125(0.6) +2048(8384513 6.9) - 19.434681(1.3) + +i64 + multithreading on -2(5 0.7) - 0.000006(-5.2) -4(25 1.4) - 0.000040(-4.4) -8(113 2.1) - 0.000199(-3.7) -16(481 2.7) - 0.001071(-3.0) -32(1985 3.3) - 0.005099(-2.3) -64(8065 3.9) - 0.019657(-1.7) -128(32513 4.5) - 0.077459(-1.1) -256(130561 5.1) - 0.350502(-0.5) -512(523265 5.7) - 1.699005(0.2) -1024(2095105 6.3) - 7.520601(0.9) -2048(8384513 6.9) - 30.258030(1.5) +2(5 0.7) - 0.000003(-5.6) +4(25 1.4) - 0.000017(-4.8) +8(113 2.1) - 0.000099(-4.0) +16(481 2.7) - 0.000501(-3.3) +32(1985 3.3) - 0.002752(-2.6) +64(8065 3.9) - 0.008616(-2.1) +128(32513 4.5) - 0.035981(-1.4) +256(130561 5.1) - 0.158665(-0.8) +512(523265 5.7) - 0.724087(-0.1) +1024(2095105 6.3) - 3.127404(0.5) +2048(8384513 6.9) - 21.165892(1.3) multithreading off -2(5 0.7) - 0.000006(-5.2) -4(25 1.4) - 0.000035(-4.5) -8(113 2.1) - 0.000194(-3.7) -16(481 2.7) - 0.001069(-3.0) -32(1985 3.3) - 0.005088(-2.3) -64(8065 3.9) - 0.021487(-1.7) -128(32513 4.5) - 0.095950(-1.0) -256(130561 5.1) - 0.441111(-0.4) -512(523265 5.7) - 2.146555(0.3) -1024(2095105 6.3) - 9.435037(1.0) -2048(8384513 6.9) - 38.408757(1.6) +2(5 0.7) - 0.000003(-5.6) +4(25 1.4) - 0.000017(-4.8) +8(113 2.1) - 0.000099(-4.0) +16(481 2.7) - 0.000498(-3.3) +32(1985 3.3) - 0.002733(-2.6) +64(8065 3.9) - 0.010388(-2.0) +128(32513 4.5) - 0.050085(-1.3) +256(130561 5.1) - 0.219622(-0.7) +512(523265 5.7) - 1.011703(0.0) +1024(2095105 6.3) - 4.393880(0.6) +2048(8384513 6.9) - 26.690416(1.4) */ // A grid of overlapping squares forming a simple checkerboard pattern. diff --git a/performance/rust_app/src/test/test_1_not_overlap.rs b/performance/rust_app/src/test/test_1_not_overlap.rs index 9189786..b3b91df 100644 --- a/performance/rust_app/src/test/test_1_not_overlap.rs +++ b/performance/rust_app/src/test/test_1_not_overlap.rs @@ -12,33 +12,93 @@ pub(crate) struct NotOverlapTest; // 1 // Union: +i16 + +multithreading on + +5 - 0.000001 +25 - 0.000005 +113 - 0.000024 +481 - 0.000138 +1985 - 0.000908 +8065 - 0.002176 +32513 - 0.008377 +130561 - 0.034438 +523265 - 0.158055 +2095105 - 0.687777 + +multithreading off + +5 - 0.000001 +25 - 0.000005 +113 - 0.000024 +481 - 0.000136 +1985 - 0.000901 +8065 - 0.002461 +32513 - 0.011272 +130561 - 0.048214 +523265 - 0.220364 +2095105 - 0.927191 + +i32 + +multithreading on + +5 - 0.000001 +25 - 0.000005 +113 - 0.000024 +481 - 0.000132 +1985 - 0.000908 +8065 - 0.002322 +32513 - 0.008956 +130561 - 0.036901 +523265 - 0.177118 +2095105 - 0.745187 +8384513 - 3.282416 + +multithreading off + +5 - 0.000001 +25 - 0.000005 +113 - 0.000024 +481 - 0.000133 +1985 - 0.000912 +8065 - 0.002725 +32513 - 0.012319 +130561 - 0.053420 +523265 - 0.240664 +2095105 - 1.037521 +8384513 - 4.580672 + +i64 + multithreading on -5 - 0.000003 -25 - 0.000016 -113 - 0.000066 -481 - 0.000371 -1985 - 0.001692 -8065 - 0.005704 -32513 - 0.026937 -130561 - 0.106385 -523265 - 0.549426 -2095105 - 2.380725 -8384513 - 10.040016 +5 - 0.000001 +25 - 0.000006 +113 - 0.000028 +481 - 0.000151 +1985 - 0.001053 +8065 - 0.002579 +32513 - 0.010409 +130561 - 0.044266 +523265 - 0.217189 +2095105 - 0.920391 +8384513 - 3.948376 multithreading off -5 - 0.000003 -25 - 0.000013 -113 - 0.000064 -481 - 0.000368 -1985 - 0.001671 -8065 - 0.006311 -32513 - 0.030795 -130561 - 0.133008 -523265 - 0.680722 -2095105 - 3.050487 -8384513 - 12.764988 +5 - 0.000001 +25 - 0.000006 +113 - 0.000027 +481 - 0.000152 +1985 - 0.001046 +8065 - 0.003178 +32513 - 0.014719 +130561 - 0.065100 +523265 - 0.301577 +2095105 - 1.300260 +8384513 - 5.667204 */ diff --git a/performance/rust_app/src/test/test_2_lines_net.rs b/performance/rust_app/src/test/test_2_lines_net.rs index f7c9084..85afa38 100644 --- a/performance/rust_app/src/test/test_2_lines_net.rs +++ b/performance/rust_app/src/test/test_2_lines_net.rs @@ -12,61 +12,93 @@ pub(crate) struct LinesNetTest; // 2 // Intersection: +i16 + +multithreading on + +4 - 0.000002 +8 - 0.000006 +16 - 0.000021 +32 - 0.000092 +64 - 0.000403 +128 - 0.001716 +256 - 0.007047 +512 - 0.030516 +1024 - 0.130099 +2048 - 0.569064 + +multithreading off + +4 - 0.000002 +8 - 0.000006 +16 - 0.000020 +32 - 0.000091 +64 - 0.000392 +128 - 0.001717 +256 - 0.008280 +512 - 0.037633 +1024 - 0.168466 +2048 - 0.751282 + +i32 + +multithreading on + +4 - 0.000002 +8 - 0.000006 +16 - 0.000020 +32 - 0.000087 +64 - 0.000423 +128 - 0.001829 +256 - 0.007510 +512 - 0.032208 +1024 - 0.145159 +2048 - 0.622211 +4096 - 2.687778 + +multithreading off + +4 - 0.000002 +8 - 0.000006 +16 - 0.000020 +32 - 0.000087 +64 - 0.000425 +128 - 0.001791 +256 - 0.008733 +512 - 0.039941 +1024 - 0.181488 +2048 - 0.806123 +4096 - 3.557761 + +i64 + multithreading on -4 - 0.000005 -8 - 0.000019 -16 - 0.000057 -32 - 0.000227 -64 - 0.000989 -128 - 0.004524 -256 - 0.021169 -512 - 0.093545 -1024 - 0.407744 -2048 - 1.617059 -4096 - 6.436703 +4 - 0.000002 +8 - 0.000007 +16 - 0.000025 +32 - 0.000114 +64 - 0.000497 +128 - 0.002118 +256 - 0.008971 +512 - 0.041422 +1024 - 0.181670 +2048 - 0.780686 +4096 - 3.220613 multithreading off -4 - 0.000005 -8 - 0.000016 -16 - 0.000053 -32 - 0.000218 -64 - 0.000981 -128 - 0.004478 -256 - 0.021773 -512 - 0.105066 -1024 - 0.469201 -2048 - 1.982992 -4096 - 8.215361 - -geom multithreading off - -4 - 0.000006 -8 - 0.000016 -16 - 0.000050 -32 - 0.000196 -64 - 0.001032 -128 - 0.003914 -256 - 0.018113 -512 - 0.088561 -1024 - 0.371023 -2048 - 1.676831 -4096 - 7.055219 - -// geom swipe line - -4 - 0.000005 -8 - 0.000014 -16 - 0.000050 -32 - 0.000191 -64 - 0.000852 -128 - 0.003730 -256 - 0.017368 -512 - 0.082651 -1024 - 0.379062 -2048 - 1.638863 -4096 - 6.566427 +4 - 0.000002 +8 - 0.000007 +16 - 0.000025 +32 - 0.000113 +64 - 0.000486 +128 - 0.002161 +256 - 0.010305 +512 - 0.048515 +1024 - 0.219835 +2048 - 0.991209 +4096 - 4.420209 */ diff --git a/performance/rust_app/src/test/test_3_spiral.rs b/performance/rust_app/src/test/test_3_spiral.rs index 6ead021..0276079 100644 --- a/performance/rust_app/src/test/test_3_spiral.rs +++ b/performance/rust_app/src/test/test_3_spiral.rs @@ -13,52 +13,108 @@ pub(crate) struct SpiralTest; // 3 // Intersection: +i16 multithreading on -2 - 0.000002 -4 - 0.000005 -8 - 0.000010 -16 - 0.000022 -32 - 0.000053 -64 - 0.000138 -128 - 0.000318 -256 - 0.000698 -512 - 0.001734 -1024 - 0.003680 -2048 - 0.008326 -4096 - 0.011125 -8192 - 0.019165 -16384 - 0.041279 -32768 - 0.082171 -65536 - 0.179237 -131072 - 0.341959 -262144 - 0.785235 -524288 - 1.345209 -1048576 - 3.039988 +2 - 0.000001 +4 - 0.000002 +8 - 0.000005 +16 - 0.000007 +32 - 0.000015 +64 - 0.000032 +128 - 0.000079 +256 - 0.000249 +512 - 0.000715 +1024 - 0.001833 +2048 - 0.004278 +4096 - 0.004283 +8192 - 0.008659 +16384 - 0.017107 +32768 - 0.033244 +65536 - 0.066839 +131072 - 0.131752 +262144 - 0.291700 +524288 - 0.455038 +1048576 - 0.945685 + +multithreading off + +2 - 0.000001 +4 - 0.000002 +8 - 0.000005 +16 - 0.000007 +32 - 0.000015 +64 - 0.000031 +128 - 0.000082 +256 - 0.000258 +512 - 0.000736 +1024 - 0.001867 +2048 - 0.004070 +4096 - 0.005567 +8192 - 0.010481 +16384 - 0.022516 +32768 - 0.045402 +65536 - 0.095105 +131072 - 0.198492 +262144 - 0.431128 +524288 - 0.661296 +1048576 - 1.435810 + +i32 + +multithreading on + +2 - 0.000001 +4 - 0.000002 +8 - 0.000005 +16 - 0.000010 +32 - 0.000020 +64 - 0.000042 +128 - 0.000104 +256 - 0.000245 +512 - 0.000748 +1024 - 0.001984 +2048 - 0.004483 +4096 - 0.006270 +8192 - 0.009434 +16384 - 0.019526 +32768 - 0.034397 +65536 - 0.074229 +131072 - 0.146095 +262144 - 0.350941 +524288 - 0.744520 +1048576 - 1.626030 + +multithreading off + +2 - 0.000001 +4 - 0.000002 +8 - 0.000005 +16 - 0.000010 +32 - 0.000021 +64 - 0.000043 +128 - 0.000092 +256 - 0.000298 +512 - 0.000790 +1024 - 0.002041 +2048 - 0.004207 +4096 - 0.007974 +8192 - 0.012796 +16384 - 0.028001 +32768 - 0.050997 +65536 - 0.113348 +131072 - 0.223653 +262144 - 0.541526 +524288 - 1.034178 +1048576 - 2.303664 + +i64 + +multithreading on multithreading off -2 - 0.000003 -4 - 0.000005 -8 - 0.000010 -16 - 0.000022 -32 - 0.000054 -64 - 0.000138 -128 - 0.000316 -256 - 0.000727 -512 - 0.001767 -1024 - 0.003605 -2048 - 0.008468 -4096 - 0.010696 -8192 - 0.019332 -16384 - 0.041967 -32768 - 0.078037 -65536 - 0.175368 -131072 - 0.323782 -262144 - 0.823820 -524288 - 1.425306 -1048576 - 3.029298 */ // Two irregular self-intersecting polygons are generated, the vertices of which are defined by a fixed radius and angle. diff --git a/performance/rust_app/src/test/test_4_windows.rs b/performance/rust_app/src/test/test_4_windows.rs index 2d21532..a56df86 100644 --- a/performance/rust_app/src/test/test_4_windows.rs +++ b/performance/rust_app/src/test/test_4_windows.rs @@ -11,31 +11,83 @@ pub(crate) struct WindowsTest; // 4 // Difference: +i16 + +// multithreading on +8 - 0.000003 +32 - 0.000009 +128 - 0.000038 +512 - 0.000204 +2048 - 0.001129 +8192 - 0.002633 +32768 - 0.010163 +131072 - 0.045737 +524288 - 0.199035 +2097152 - 0.909131 + +// multithreading off +8 - 0.000003 +32 - 0.000009 +128 - 0.000038 +512 - 0.000205 +2048 - 0.001127 +8192 - 0.003188 +32768 - 0.013917 +131072 - 0.063098 +524288 - 0.277322 +2097152 - 1.211561 + +i32 + +// multithreading on +8 - 0.000002 +32 - 0.000009 +128 - 0.000038 +512 - 0.000194 +2048 - 0.001138 +8192 - 0.002809 +32768 - 0.010892 +131072 - 0.049875 +524288 - 0.224817 +2097152 - 1.007965 + +// multithreading off +8 - 0.000003 +32 - 0.000008 +128 - 0.000038 +512 - 0.000195 +2048 - 0.001133 +8192 - 0.003421 +32768 - 0.015584 +131072 - 0.069925 +524288 - 0.312258 +2097152 - 1.382459 + +i64 + // multithreading on -8 - 0.000006 -32 - 0.000024 -128 - 0.000102 -512 - 0.000554 -2048 - 0.002276 -8192 - 0.007199 -32768 - 0.036992 -131072 - 0.159541 -524288 - 0.701553 -2097152 - 2.941974 -8388608 - 12.287573 +8 - 0.000003 +32 - 0.000010 +128 - 0.000043 +512 - 0.000224 +2048 - 0.001340 +8192 - 0.003138 +32768 - 0.012617 +131072 - 0.061721 +524288 - 0.267889 +2097152 - 1.175990 // multithreading off -8 - 0.000006 -32 - 0.000022 -128 - 0.000099 -512 - 0.000546 -2048 - 0.002241 -8192 - 0.008248 -32768 - 0.042241 -131072 - 0.202890 -524288 - 0.880672 -2097152 - 3.708358 -8388608 - 15.611615 +8 - 0.000003 +32 - 0.000010 +128 - 0.000043 +512 - 0.000225 +2048 - 0.001338 +8192 - 0.004013 +32768 - 0.018626 +131072 - 0.088967 +524288 - 0.384434 +2097152 - 1.709374 */ diff --git a/performance/rust_app/src/test/test_5_nested_squares.rs b/performance/rust_app/src/test/test_5_nested_squares.rs index 39f0984..e251eb5 100644 --- a/performance/rust_app/src/test/test_5_nested_squares.rs +++ b/performance/rust_app/src/test/test_5_nested_squares.rs @@ -12,43 +12,71 @@ pub(crate) struct CrossTest; // 5 // Union: +i16 + +// multithreading on +4 - 0.000004 +8 - 0.000007 +16 - 0.000015 +32 - 0.000034 +64 - 0.000083 +128 - 0.000228 +256 - 0.000698 +512 - 0.002015 +1024 - 0.005271 +2048 - 0.007070 +4096 - 0.058529 + +// multithreading off +4 - 0.000004 +8 - 0.000007 +16 - 0.000015 +32 - 0.000034 +64 - 0.000082 +128 - 0.000224 +256 - 0.000651 +512 - 0.002065 +1024 - 0.005356 +2048 - 0.009238 +4096 - 0.057300 + +i32 + // multithreading on -4 - 0.000011 -8 - 0.000023 -16 - 0.000042 -32 - 0.000091 -64 - 0.000220 -128 - 0.000625 -256 - 0.002053 -512 - 0.005073 -1024 - 0.013714 -2048 - 0.021411 -4096 - 0.048197 -8192 - 0.157209 -16384 - 0.347116 -32768 - 1.280415 -65536 - 2.305738 -131072 - 9.871802 -262144 - 16.526387 +4 - 0.000004 +8 - 0.000008 +16 - 0.000015 +32 - 0.000033 +64 - 0.000080 +128 - 0.000214 +256 - 0.000672 +512 - 0.002109 +1024 - 0.005426 +2048 - 0.007757 +4096 - 0.014683 +8192 - 0.046014 +16384 - 0.087646 +32768 - 0.322707 +65536 - 0.653741 +131072 - 2.410910 // multithreading off -4 - 0.000010 -8 - 0.000018 -16 - 0.000039 -32 - 0.000086 -64 - 0.000215 -128 - 0.000620 -256 - 0.002037 -512 - 0.004784 -1024 - 0.013536 -2048 - 0.031879 -4096 - 0.066320 -8192 - 0.253849 -16384 - 0.448397 -32768 - 1.870073 -65536 - 3.989876 -131072 - 15.874618 -262144 - 31.306648 +4 - 0.000004 +8 - 0.000007 +16 - 0.000015 +32 - 0.000033 +64 - 0.000079 +128 - 0.000213 +256 - 0.000659 +512 - 0.002060 +1024 - 0.005223 +2048 - 0.011609 +4096 - 0.023309 +8192 - 0.081856 +16384 - 0.181416 +32768 - 0.667394 +65536 - 1.401006 +131072 - 5.445065 */ From 2403b718f54322ea744acb910a229c081bbe7dd2 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Fri, 22 May 2026 10:27:42 +0300 Subject: [PATCH 20/36] fix --- iOverlay/src/build/boolean.rs | 7 +++---- iOverlay/src/core/edge_overlay.rs | 3 +-- iOverlay/src/core/extract.rs | 14 +++----------- iOverlay/src/core/graph.rs | 3 +-- iOverlay/src/core/overlay.rs | 15 +++++---------- iOverlay/src/core/simplify.rs | 25 ++++--------------------- iOverlay/src/float/overlay.rs | 6 +++--- iOverlay/src/mesh/outline/offset.rs | 2 +- iOverlay/src/mesh/stroke/offset.rs | 4 ++-- iOverlay/src/split/cross_solver.rs | 10 ++-------- iOverlay/src/split/grid_layout.rs | 12 +++++------- iOverlay/src/string/extract.rs | 3 +-- iOverlay/src/string/overlay.rs | 8 ++------ iOverlay/src/string/split.rs | 10 +++++----- iOverlay/src/vector/extract.rs | 14 +++----------- 15 files changed, 41 insertions(+), 95 deletions(-) diff --git a/iOverlay/src/build/boolean.rs b/iOverlay/src/build/boolean.rs index 334e1af..01b8da5 100644 --- a/iOverlay/src/build/boolean.rs +++ b/iOverlay/src/build/boolean.rs @@ -20,7 +20,6 @@ use crate::segm::segment::{ use crate::segm::winding::WindingCount; use alloc::vec::Vec; use i_float::int::number::int::IntNumber; -use i_float::int::number::wide_int::WideIntNumber; use i_key_sort::sort::key::SortKey; use i_shape::util::reserve::Reserve; use i_tree::Expiration; @@ -34,7 +33,7 @@ where pub(crate) fn build_boolean_all( &mut self, fill_rule: FillRule, - options: IntOverlayOptions<::UInt>, + options: IntOverlayOptions, solver: &Solver, segments: &[Segment], ) -> OverlayGraph<'_, I, D> { @@ -48,7 +47,7 @@ where &mut self, fill_rule: FillRule, overlay_rule: OverlayRule, - options: IntOverlayOptions<::UInt>, + options: IntOverlayOptions, solver: &Solver, segments: &[Segment], ) -> OverlayGraph<'_, I, D> { @@ -83,7 +82,7 @@ where #[inline] fn boolean_graph( &mut self, - options: IntOverlayOptions<::UInt>, + options: IntOverlayOptions, solver: &Solver, ) -> OverlayGraph<'_, I, D> { self.build_nodes_and_connect_links(solver); diff --git a/iOverlay/src/core/edge_overlay.rs b/iOverlay/src/core/edge_overlay.rs index 0272a0d..c06b918 100644 --- a/iOverlay/src/core/edge_overlay.rs +++ b/iOverlay/src/core/edge_overlay.rs @@ -13,7 +13,6 @@ use crate::split::solver::SplitSolver; use crate::vector::edge::{DataVectorEdge, DataVectorShape}; use alloc::vec::Vec; use i_float::int::number::int::IntNumber; -use i_float::int::number::wide_int::WideIntNumber; use i_float::int::point::IntPoint; use i_key_sort::sort::key::SortKey; use i_tree::{Expiration, LayoutNumber}; @@ -27,7 +26,7 @@ pub struct InputEdge { pub struct EdgeOverlay { pub solver: Solver, - pub options: IntOverlayOptions<::UInt>, + pub options: IntOverlayOptions, pub boolean_buffer: Option>, segments: Vec>, split_solver: SplitSolver, diff --git a/iOverlay/src/core/extract.rs b/iOverlay/src/core/extract.rs index 030488a..e23cd1b 100644 --- a/iOverlay/src/core/extract.rs +++ b/iOverlay/src/core/extract.rs @@ -311,21 +311,13 @@ impl StartPathData { } pub(crate) trait GraphContour { - fn validate( - &mut self, - min_output_area: ::UInt, - preserve_output_collinear: bool, - ) -> (bool, bool); + fn validate(&mut self, min_output_area: I::WideUInt, preserve_output_collinear: bool) -> (bool, bool); fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize; } impl GraphContour for IntContour { #[inline] - fn validate( - &mut self, - min_output_area: ::UInt, - preserve_output_collinear: bool, - ) -> (bool, bool) { + fn validate(&mut self, min_output_area: I::WideUInt, preserve_output_collinear: bool) -> (bool, bool) { let is_modified = if !preserve_output_collinear { self.simplify_contour() } else { @@ -336,7 +328,7 @@ impl GraphContour for IntContour { return (false, is_modified); } - if min_output_area == ::UInt::ZERO { + if min_output_area == I::WideUInt::ZERO { return (true, is_modified); } let area = self.unsafe_area(); diff --git a/iOverlay/src/core/graph.rs b/iOverlay/src/core/graph.rs index d57121c..10087c9 100644 --- a/iOverlay/src/core/graph.rs +++ b/iOverlay/src/core/graph.rs @@ -7,7 +7,6 @@ use crate::build::builder::GraphNode; use crate::core::overlay::IntOverlayOptions; use alloc::vec::Vec; use i_float::int::number::int::IntNumber; -use i_float::int::number::wide_int::WideIntNumber; /// A representation of geometric shapes organized for efficient boolean operations. /// @@ -16,7 +15,7 @@ use i_float::int::number::wide_int::WideIntNumber; /// Use `OverlayGraph` to perform boolean operations on the geometric shapes you've added to an `Overlay`, after it has processed the shapes according to the specified build and overlay rules. /// [More information](https://ishape-rust.github.io/iShape-js/overlay/overlay_graph/overlay_graph.html) about Overlay Graph. pub struct OverlayGraph<'a, I: IntNumber, D = ()> { - pub(crate) options: IntOverlayOptions<::UInt>, + pub(crate) options: IntOverlayOptions, pub(crate) nodes: &'a [OverlayNode], pub(crate) links: &'a [OverlayLink], } diff --git a/iOverlay/src/core/overlay.rs b/iOverlay/src/core/overlay.rs index a4bff10..49a23a0 100644 --- a/iOverlay/src/core/overlay.rs +++ b/iOverlay/src/core/overlay.rs @@ -16,7 +16,6 @@ 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::number::wide_int::WideIntNumber; use i_float::int::point::IntPoint; use i_key_sort::sort::key::SortKey; use i_shape::int::count::PointsCount; @@ -68,7 +67,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 solver: Solver, - pub options: IntOverlayOptions<::UInt>, + pub options: IntOverlayOptions, pub boolean_buffer: Option>, pub(crate) segments: Vec>, pub(crate) split_solver: SplitSolver, @@ -98,11 +97,7 @@ where /// - `capacity`: The initial capacity for storing edge data. Ideally, this should be set to the sum of the edges of all shapes to be added to the overlay, ensuring efficient data management. /// - `options`: Adjust custom behavior. /// - `solver`: Type of solver to use. - pub fn new_custom( - capacity: usize, - options: IntOverlayOptions<::UInt>, - solver: Solver, - ) -> Self { + pub fn new_custom(capacity: usize, options: IntOverlayOptions, solver: Solver) -> Self { Self { solver, options, @@ -131,7 +126,7 @@ where pub fn with_contour_custom( subj: &[IntPoint], clip: &[IntPoint], - options: IntOverlayOptions<::UInt>, + options: IntOverlayOptions, solver: Solver, ) -> Self { let mut overlay = Self::new_custom(subj.len() + clip.len(), options, solver); @@ -158,7 +153,7 @@ where pub fn with_contours_custom( subj: &[IntContour], clip: &[IntContour], - options: IntOverlayOptions<::UInt>, + options: IntOverlayOptions, solver: Solver, ) -> Self { let mut overlay = Self::new_custom(subj.points_count() + clip.points_count(), options, solver); @@ -185,7 +180,7 @@ where pub fn with_shapes_options( subj: &[IntShape], clip: &[IntShape], - options: IntOverlayOptions<::UInt>, + options: IntOverlayOptions, solver: Solver, ) -> Self { let mut overlay = Self::new_custom(subj.points_count() + clip.points_count(), options, solver); diff --git a/iOverlay/src/core/simplify.rs b/iOverlay/src/core/simplify.rs index c47dc60..29f1557 100644 --- a/iOverlay/src/core/simplify.rs +++ b/iOverlay/src/core/simplify.rs @@ -7,7 +7,6 @@ use crate::core::overlay::ContourDirection::Clockwise; use crate::core::overlay::{IntOverlayOptions, Overlay, ShapeType}; use crate::core::overlay_rule::OverlayRule; use crate::i_float::int::number::int::IntNumber; -use crate::i_float::int::number::wide_int::WideIntNumber; use crate::i_float::int::point::IntPoint; use alloc::vec; use i_key_sort::sort::key::SortKey; @@ -34,11 +33,7 @@ pub trait Simplify { /// - Each path `Vec` is a sequence of points, forming a closed path. /// /// Note: Outer boundary paths have a **main_direction** order, and holes have an opposite to **main_direction** order. - fn simplify( - &self, - fill_rule: FillRule, - options: IntOverlayOptions<::UInt>, - ) -> IntShapes; + fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes; } impl Simplify for [IntPoint] @@ -46,11 +41,7 @@ where I: IntNumber + Expiration + LayoutNumber + SortKey, { #[inline] - fn simplify( - &self, - fill_rule: FillRule, - options: IntOverlayOptions<::UInt>, - ) -> IntShapes { + fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { match Overlay::new_custom(self.len(), options, Default::default()).simplify_contour(self, fill_rule) { Some(shapes) => shapes, None => vec![vec![self.to_vec()]], @@ -63,11 +54,7 @@ where I: IntNumber + Expiration + LayoutNumber + SortKey, { #[inline] - fn simplify( - &self, - fill_rule: FillRule, - options: IntOverlayOptions<::UInt>, - ) -> IntShapes { + fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { match Overlay::new_custom(self.len(), options, Default::default()).simplify_shape(self, fill_rule) { Some(shapes) => shapes, None => vec![self.to_vec()], @@ -80,11 +67,7 @@ where I: IntNumber + Expiration + LayoutNumber + SortKey, { #[inline] - fn simplify( - &self, - fill_rule: FillRule, - options: IntOverlayOptions<::UInt>, - ) -> IntShapes { + fn simplify(&self, fill_rule: FillRule, options: IntOverlayOptions) -> IntShapes { Overlay::new_custom(self.points_count(), options, Default::default()).simplify_shapes(self, fill_rule) } } diff --git a/iOverlay/src/float/overlay.rs b/iOverlay/src/float/overlay.rs index 0f9d036..52b48d2 100644 --- a/iOverlay/src/float/overlay.rs +++ b/iOverlay/src/float/overlay.rs @@ -469,7 +469,7 @@ impl OverlayOptions { pub(crate) fn int_with_adapter>( &self, adapter: &FloatPointAdapter, - ) -> IntOverlayOptions<::UInt> { + ) -> IntOverlayOptions { IntOverlayOptions { preserve_input_collinear: self.preserve_input_collinear, output_direction: self.output_direction, @@ -479,12 +479,12 @@ impl OverlayOptions { } } - pub(crate) fn int_default(&self) -> IntOverlayOptions<::UInt> { + pub(crate) fn int_default(&self) -> IntOverlayOptions { IntOverlayOptions { preserve_input_collinear: self.preserve_input_collinear, output_direction: self.output_direction, preserve_output_collinear: self.preserve_output_collinear, - min_output_area: ::UInt::ZERO, + min_output_area: I::WideUInt::ZERO, ogc: self.ogc, } } diff --git a/iOverlay/src/mesh/outline/offset.rs b/iOverlay/src/mesh/outline/offset.rs index 7424698..deaaf8b 100644 --- a/iOverlay/src/mesh/outline/offset.rs +++ b/iOverlay/src/mesh/outline/offset.rs @@ -486,7 +486,7 @@ where for path in source.iter_paths() { let area = path.unsafe_int_area(&self.adapter); - if area.unsigned_abs() <= <::UInt as UIntNumber>::from_u64(1) { + if area.unsigned_abs() <= ::from_u64(1) { // ignore degenerate paths continue; } diff --git a/iOverlay/src/mesh/stroke/offset.rs b/iOverlay/src/mesh/stroke/offset.rs index eee953a..25359a1 100644 --- a/iOverlay/src/mesh/stroke/offset.rs +++ b/iOverlay/src/mesh/stroke/offset.rs @@ -508,7 +508,7 @@ where options: OverlayOptions, ) -> Shapes

{ let ir = self.adapter.round_len_to_int(self.r).wide().unsigned_abs(); - if ir <= <::UInt as UIntNumber>::from_u64(1) { + if ir <= I::WideUInt::ONE { // offset is too small return vec![]; } @@ -549,7 +549,7 @@ where output: &mut FloatFlatContoursBuffer

, ) { let ir = self.adapter.round_len_to_int(self.r).wide().unsigned_abs(); - if ir <= <::UInt as UIntNumber>::from_u64(1) { + if ir <= I::WideUInt::ONE { // offset is too small output.clear_and_reserve(0, 0); return; diff --git a/iOverlay/src/split/cross_solver.rs b/iOverlay/src/split/cross_solver.rs index 455e0ce..993f221 100644 --- a/iOverlay/src/split/cross_solver.rs +++ b/iOverlay/src/split/cross_solver.rs @@ -294,14 +294,8 @@ impl CrossSolver { let uxy_b = xy_b.unsigned_abs(); let udiv = div.unsigned_abs(); - let kx = <::UInt as UIntNumber>::Product::multiply( - a1x.unsigned_abs(), - uxy_b, - ); - let ky = <::UInt as UIntNumber>::Product::multiply( - a1y.unsigned_abs(), - uxy_b, - ); + let kx = ::Product::multiply(a1x.unsigned_abs(), uxy_b); + let ky = ::Product::multiply(a1y.unsigned_abs(), uxy_b); let ux = kx.divide_with_rounding(udiv); let uy = ky.divide_with_rounding(udiv); diff --git a/iOverlay/src/split/grid_layout.rs b/iOverlay/src/split/grid_layout.rs index 6e34a3b..8492541 100644 --- a/iOverlay/src/split/grid_layout.rs +++ b/iOverlay/src/split/grid_layout.rs @@ -112,23 +112,21 @@ impl FragmentBuffer { let is_inc = s.a.y <= s.b.y; - let width = (s.b.x.wide() - s.a.x.wide()).to_uint(); + let width = (s.b.x - s.a.x).to_uint(); let height = (s.b.y.wide() - s.a.y.wide()).unsigned_abs(); let log = (width * height).ilog2(); - let p = <::UInt as UIntNumber>::LAST_BIT_INDEX - log; + let p = I::WideUInt::LAST_BIT_INDEX - log; let k = (height << p) / width; - let mut w = (self.layout.pos(i0 + 1).wide() - s.a.x.wide()).to_uint(); - - let one = <::UInt as UIntNumber>::ONE; - let dw = one << self.layout.power; + let mut w = (self.layout.pos(i0 + 1) - s.a.x).to_uint(); + let dw = I::WideUInt::ONE << self.layout.power; for i in i0..i1 { let h_min = (w * k) >> p; let mut h_max = h_min; while h_max * width < height * w { - h_max += one; + h_max += I::WideUInt::ONE; } let max_x = x0 + I::from_uint(w); diff --git a/iOverlay/src/string/extract.rs b/iOverlay/src/string/extract.rs index 7450d47..0d063d5 100644 --- a/iOverlay/src/string/extract.rs +++ b/iOverlay/src/string/extract.rs @@ -8,7 +8,6 @@ use crate::string::split::{BinStore, Split}; use alloc::vec; use alloc::vec::Vec; use i_float::int::number::int::IntNumber; -use i_float::int::number::wide_int::WideIntNumber; use i_key_sort::sort::key::SortKey; use i_shape::int::path::{ContourExtension, IntPath}; use i_shape::int::shape::IntShapes; @@ -44,7 +43,7 @@ impl StringGraph<'_, I> { pub fn extract_shapes_custom( &self, string_rule: StringRule, - options: IntOverlayOptions<::UInt>, + options: IntOverlayOptions, ) -> IntShapes { let clockwise = options.output_direction == ContourDirection::Clockwise; let mut fills = self.filter(string_rule); diff --git a/iOverlay/src/string/overlay.rs b/iOverlay/src/string/overlay.rs index 5e18ca9..62d9e4c 100644 --- a/iOverlay/src/string/overlay.rs +++ b/iOverlay/src/string/overlay.rs @@ -15,7 +15,6 @@ use crate::string::line::IntLine; use alloc::vec::Vec; use core::cmp::Ordering; use i_float::int::number::int::IntNumber; -use i_float::int::number::wide_int::WideIntNumber; use i_float::int::point::IntPoint; use i_key_sort::sort::key::SortKey; use i_shape::int::count::PointsCount; @@ -24,7 +23,7 @@ use i_shape::int::shape::{IntContour, IntShape}; use i_tree::{Expiration, LayoutNumber}; pub struct StringOverlay { - pub options: IntOverlayOptions<::UInt>, + pub options: IntOverlayOptions, pub(super) segments: Vec>, pub(crate) split_solver: SplitSolver, pub(crate) graph_builder: GraphBuilder, I>, @@ -51,10 +50,7 @@ where /// This pre-allocation helps in optimizing memory usage and performance. /// - `capacity`: The initial capacity for storing edge data. Ideally, this should be set to the sum of the edges of all shapes to be added to the overlay, ensuring efficient data management. /// - `options`: Adjust custom behavior. - pub fn with_options( - capacity: usize, - options: IntOverlayOptions<::UInt>, - ) -> Self { + pub fn with_options(capacity: usize, options: IntOverlayOptions) -> Self { Self { options, segments: Vec::with_capacity(capacity), diff --git a/iOverlay/src/string/split.rs b/iOverlay/src/string/split.rs index ef623ee..c032ef6 100644 --- a/iOverlay/src/string/split.rs +++ b/iOverlay/src/string/split.rs @@ -10,7 +10,7 @@ use i_shape::util::reserve::Reserve; pub(super) trait Split { fn split_loops( self, - min_area: ::UInt, + min_area: I::WideUInt, contour_buffer: &mut IntContour, bin_store: &mut BinStore, ) -> Vec @@ -21,7 +21,7 @@ pub(super) trait Split { impl Split for IntContour { fn split_loops( self, - min_area: ::UInt, + min_area: I::WideUInt, contour_buffer: &mut IntContour, bin_store: &mut BinStore, ) -> Vec { @@ -162,13 +162,13 @@ impl BinStore { } trait ValidateArea { - fn validate_area(&self, min_area: ::UInt) -> bool; + fn validate_area(&self, min_area: I::WideUInt) -> bool; } impl ValidateArea for IntContour { #[inline] - fn validate_area(&self, min_area: ::UInt) -> bool { - if min_area == ::UInt::ZERO { + fn validate_area(&self, min_area: I::WideUInt) -> bool { + if min_area == I::WideUInt::ZERO { return true; } let abs_area = self.unsafe_area().unsigned_abs() >> 1; diff --git a/iOverlay/src/vector/extract.rs b/iOverlay/src/vector/extract.rs index 7faa07a..aa6e44a 100644 --- a/iOverlay/src/vector/extract.rs +++ b/iOverlay/src/vector/extract.rs @@ -312,21 +312,13 @@ fn is_sorted(segments: &[IdSegment]) -> bool { } trait DataGraphContour { - fn validate( - &mut self, - min_output_area: ::UInt, - preserve_output_collinear: bool, - ) -> (bool, bool); + fn validate(&mut self, min_output_area: I::WideUInt, preserve_output_collinear: bool) -> (bool, bool); fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize; } impl DataGraphContour for DataVectorPath { #[inline] - fn validate( - &mut self, - min_output_area: ::UInt, - preserve_output_collinear: bool, - ) -> (bool, bool) { + fn validate(&mut self, min_output_area: I::WideUInt, preserve_output_collinear: bool) -> (bool, bool) { let is_modified = if !preserve_output_collinear { self.simplify_contour() } else { @@ -337,7 +329,7 @@ impl DataGraphContour for DataVectorPath return (false, is_modified); } - if min_output_area == ::UInt::ZERO { + if min_output_area == I::WideUInt::ZERO { return (true, is_modified); } From 52df21e465f73b1da51bcf21ae61fec9742eca4a Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Fri, 22 May 2026 11:54:35 +0300 Subject: [PATCH 21/36] fix empty segments after merge --- iOverlay/src/split/solver.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/iOverlay/src/split/solver.rs b/iOverlay/src/split/solver.rs index d1f09aa..77b215b 100644 --- a/iOverlay/src/split/solver.rs +++ b/iOverlay/src/split/solver.rs @@ -40,8 +40,11 @@ where segments.sort_by_ab(solver.is_parallel_sort_allowed()); let any_merged = segments.merge_if_needed(); - let any_intersection = self.split(segments, solver); + if !segments.is_empty() { + return true; + } + let any_intersection = self.split(segments, solver); any_merged | any_intersection } From 51dc099e136dd5617c964865b0570912e0bfc013 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Fri, 22 May 2026 11:54:48 +0300 Subject: [PATCH 22/36] add tests --- iOverlay/tests/crash_tests.rs | 33 ++++ iOverlay/tests/dynamic_tests.rs | 336 ++++++++++++++++++++++---------- 2 files changed, 271 insertions(+), 98 deletions(-) create mode 100644 iOverlay/tests/crash_tests.rs diff --git a/iOverlay/tests/crash_tests.rs b/iOverlay/tests/crash_tests.rs new file mode 100644 index 0000000..bb7b21d --- /dev/null +++ b/iOverlay/tests/crash_tests.rs @@ -0,0 +1,33 @@ +#[cfg(test)] +mod tests { + use i_overlay::core::fill_rule::FillRule; + use i_overlay::core::overlay::{Overlay, ShapeType}; + use i_overlay::core::overlay_rule::OverlayRule; + use i_shape::base::data::Shape; + use i_shape::int_shape; + use i_overlay::core::solver::{Precision, Solver, Strategy}; + + #[test] + fn test_00() { + let subj: Shape<_> = int_shape![ + [[0i16, 0], [0, 4], [3, -5]], + [[0, 0], [1, 7], [2, -8]], + [[0, 0], [4, -4], [5, 7]], + ]; + + let solver = Solver { + strategy: Strategy::List, + precision: Precision { start: 0, progression: 1 }, + multithreading: None, + }; + + let mut overlay = Overlay::new_custom(4, Default::default(), solver); + overlay.add_contours(&subj, ShapeType::Subject); + if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { + graph.validate(); + let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); + assert!(!result.is_empty()); + } + } + +} diff --git a/iOverlay/tests/dynamic_tests.rs b/iOverlay/tests/dynamic_tests.rs index e537648..ebdd5b7 100644 --- a/iOverlay/tests/dynamic_tests.rs +++ b/iOverlay/tests/dynamic_tests.rs @@ -1,6 +1,8 @@ #[cfg(test)] 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::overlay::{Overlay, ShapeType}; use i_overlay::core::overlay_rule::OverlayRule; @@ -9,20 +11,33 @@ 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; 3] = [Solver::LIST, Solver::TREE, Solver::AUTO]; + const SOLVERS: [Solver; 4] = [Solver::LIST, Solver::TREE, Solver::FRAG, Solver::AUTO]; + + trait TestInt: IntNumber + Expiration + LayoutNumber + SortKey {} + + impl TestInt for I where I: IntNumber + Expiration + LayoutNumber + SortKey {} + #[test] fn test_0() { - let clip = create_star(1.0, 2.0, 7, 0.0); + test_0_as::(); + test_0_as::(); + test_0_as::(); + } + + fn test_0_as() { + let scale = scale_for::(2.0); + let clip = create_star::(1.0, 2.0, 7, 0.0, scale); for &solver in SOLVERS.iter() { let mut r = 0.9; while r < 1.2 { let mut a = 0.0; while a < 2.0 * PI { - let subj = create_star(1.0, r, 7, a); + let subj = create_star::(1.0, r, 7, a, scale); if let Some(graph) = Overlay::with_contours_custom(&subj, &clip, Default::default(), solver) @@ -32,58 +47,79 @@ mod tests { let result = graph.extract_shapes(OverlayRule::Union, &mut Default::default()); assert!(!result.is_empty()); } - a += 0.005 + a += 0.01 } - r += 0.01 + r += 0.02 } } } #[test] fn test_1() { - let clip = create_star(200.0, 30.0, 7, 0.0); + test_1_as::(); + test_1_as::(); + test_1_as::(); + } + + fn test_1_as() { + let scale = scale_for::(200.0); + let clip = create_star::(200.0, 30.0, 7, 0.0, scale); for &solver in SOLVERS.iter() { let mut a = 0.0; while a < 4.0 * PI { - let subj = create_star(200.0, 30.0, 7, a); + let subj = create_star::(200.0, 30.0, 7, a, scale); if let Some(graph) = Overlay::with_contours_custom(&subj, &clip, Default::default(), solver) .build_graph_view(FillRule::NonZero) { graph.validate(); let _ = graph.extract_shapes(OverlayRule::Xor, &mut Default::default()); } - a += 0.001 + a += 0.005 } } } #[test] fn test_2() { - let clip = create_star(202.5, 33.75, 24, 0.0); + test_2_as::(); + test_2_as::(); + test_2_as::(); + } + + fn test_2_as() { + let scale = scale_for::(202.5); + let clip = create_star::(202.5, 33.75, 24, 0.0, scale); for &solver in SOLVERS.iter() { let mut a = 0.0; while a < 2.0 * PI { - let subj = create_star(202.5, 33.75, 24, a); + let subj = create_star::(202.5, 33.75, 24, a, scale); if let Some(graph) = Overlay::with_contours_custom(&subj, &clip, Default::default(), solver) .build_graph_view(FillRule::NonZero) { graph.validate(); let _ = graph.extract_shapes(OverlayRule::Xor, &mut Default::default()); } - a += 0.001 + a += 0.005 } } } #[test] fn test_3() { - let clip = create_star(100.0, 10.0, 17, 0.0); + test_3_as::(); + test_3_as::(); + test_3_as::(); + } + + fn test_3_as() { + let scale = scale_for::(100.0); + let clip = create_star::(100.0, 10.0, 17, 0.0, scale); for &solver in SOLVERS.iter() { let mut a = 0.0; while a < 4.0 * PI { - let subj = create_star(100.0, 10.0, 17, a); + let subj = create_star::(100.0, 10.0, 17, a, scale); if let Some(graph) = Overlay::with_contours_custom(&subj, &clip, Default::default(), solver) .build_graph_view(FillRule::NonZero) { @@ -97,12 +133,19 @@ mod tests { #[test] fn test_4() { - let clip = create_star(202.5, 33.75, 24, 0.0); + test_4_as::(); + test_4_as::(); + test_4_as::(); + } + + fn test_4_as() { + let scale = scale_for::(202.5); + let clip = create_star::(202.5, 33.75, 24, 0.0, scale); for &solver in SOLVERS.iter() { let mut a = -0.000_001; while a < 0.000_001 { - let subj = create_star(202.5, 33.75, 24, a); + let subj = create_star::(202.5, 33.75, 24, a, scale); if let Some(graph) = Overlay::with_contours_custom(&subj, &clip, Default::default(), solver) .build_graph_view(FillRule::NonZero) { @@ -116,9 +159,16 @@ mod tests { #[test] fn test_5() { - let clip = create_star(202.5, 33.75, 24, 0.0); + test_5_as::(); + test_5_as::(); + test_5_as::(); + } + + fn test_5_as() { + let scale = scale_for::(202.5); + let clip = create_star::(202.5, 33.75, 24, 0.0, scale); let a = -1E-6; - let subj = create_star(202.5, 33.75, 24, a); + let subj = create_star::(202.5, 33.75, 24, a, scale); // println!("subj {:?}", subj); for &solver in SOLVERS.iter() { @@ -126,20 +176,26 @@ mod tests { .build_graph_view(FillRule::NonZero) { graph.validate(); - let result = graph.extract_shapes(OverlayRule::Xor, &mut Default::default()); - assert!(!result.is_empty()); + let _ = graph.extract_shapes(OverlayRule::Xor, &mut Default::default()); } } } #[test] fn test_6() { - let clip = create_star(100.0, 50.0, 24, 0.0); + test_6_as::(); + test_6_as::(); + test_6_as::(); + } + + fn test_6_as() { + let scale = scale_for::(100.0); + let clip = create_star::(100.0, 50.0, 24, 0.0, scale); for &solver in SOLVERS.iter() { let mut a = -0.000_001; while a < 0.000_001 { - let subj = create_star(100.0, 50.0, 24, a); + let subj = create_star::(100.0, 50.0, 24, a, scale); if let Some(graph) = Overlay::with_contours_custom(&subj, &clip, Default::default(), solver) .build_graph_view(FillRule::NonZero) { @@ -153,26 +209,41 @@ mod tests { #[test] fn test_7() { + test_7_as::(); + test_7_as::(); + test_7_as::(); + } + + fn test_7_as() { let n = 1010; - let subj_paths = random_polygon(1_000_000.0, 0.0, n); + let scale = scale_for::(1_000_000.0); + let subj_paths = random_polygon::(1_000_000.0, 0.0, n, scale); - let mut overlay = Overlay::new(n); - overlay.add_contour(&subj_paths, ShapeType::Subject); + for &solver in SOLVERS.iter() { + let mut overlay = Overlay::new_custom(n, Default::default(), solver); + overlay.add_contour(&subj_paths, ShapeType::Subject); - if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { - graph.validate(); - let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); - assert!(!result.is_empty()); + if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { + graph.validate(); + let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); + assert!(!result.is_empty()); + } } } #[test] fn test_8() { + test_8_as::(); + test_8_as::(); + test_8_as::(); + } + + fn test_8_as() { for &solver in SOLVERS.iter() { let mut r = 0.004; while r < 1.0 { for n in 5..10 { - let subj_paths = random_polygon(r, 0.0, n); + let subj_paths = random_polygon::(r, 0.0, n, scale_for::(r)); let mut overlay = Overlay::new_custom(n, Default::default(), solver); overlay.add_contour(&subj_paths, ShapeType::Subject); @@ -190,15 +261,22 @@ mod tests { #[test] fn test_9() { + test_9_as::(); + test_9_as::(); + test_9_as::(); + } + + fn test_9_as() { let s = 0.02; let r0 = s * 1.0; - let clip = create_star(r0, s * 2.0, 4, 0.0); + let scale = scale_for::(s * 2.0); + let clip = create_star::(r0, s * 2.0, 4, 0.0, scale); for &solver in SOLVERS.iter() { let mut r = s * 0.9; while r < 1.2 * s { let mut a = 0.0; while a < 2.0 * PI { - let subj = create_star(r0, r, 4, a); + let subj = create_star::(r0, r, 4, a, scale); if let Some(graph) = Overlay::with_contours_custom(&subj, &clip, Default::default(), solver) .build_graph_view(FillRule::NonZero) @@ -216,11 +294,18 @@ mod tests { #[test] fn test_10() { + test_10_as::(); + test_10_as::(); + test_10_as::(); + } + + fn test_10_as() { let solver = Solver::AUTO; - let clip = create_star(1.0, 2.0, 7, 0.0); + let scale = scale_for::(2.0); + let clip = create_star::(1.0, 2.0, 7, 0.0, scale); let a = 0.440_000_000_000_000_3; let r = 1.01; - let subj = create_star(1.0, r, 7, a); + let subj = create_star::(1.0, r, 7, a, scale); if let Some(graph) = Overlay::with_contours_custom(&subj, &clip, Default::default(), solver) .build_graph_view(FillRule::NonZero) @@ -233,114 +318,154 @@ mod tests { #[test] fn test_11() { + test_11_as::(); + test_11_as::(); + test_11_as::(); + } + + fn test_11_as() { let n = 6; - for _ in 0..10000 { - let subj_path = random_polygon(100.0, 0.0, n); - let clip_path = random_polygon(100.0, 0.5 * PI, n); - let mut overlay = Overlay::new(2 * n); + let scale = scale_for::(100.0); + for &solver in SOLVERS.iter() { + for _ in 0..2000 { + let subj_path = random_polygon::(100.0, 0.0, n, scale); + let clip_path = random_polygon::(100.0, 0.5 * PI, n, scale); + let mut overlay = Overlay::new_custom(2 * n, Default::default(), solver); - overlay.add_contour(&subj_path, ShapeType::Subject); - overlay.add_contour(&clip_path, ShapeType::Clip); + overlay.add_contour(&subj_path, ShapeType::Subject); + overlay.add_contour(&clip_path, ShapeType::Clip); - if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { - graph.validate(); - let result = graph.extract_shapes(OverlayRule::Union, &mut Default::default()); - assert!(!result.is_empty()); + if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { + graph.validate(); + let result = graph.extract_shapes(OverlayRule::Union, &mut Default::default()); + assert!(!result.is_empty()); + } } } } #[test] fn test_12() { + test_12_as::(); + test_12_as::(); + test_12_as::(); + } + + fn test_12_as() { let n = 5; - for _ in 0..10000 { - let subj_path = random(10, n); - let mut overlay = Overlay::new(2 * n); - overlay.add_contour(&subj_path, ShapeType::Subject); - if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { - graph.validate(); - let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); - assert!(!result.is_empty()); + for &solver in SOLVERS.iter() { + for _ in 0..10000 { + let subj_path = random::(10, n); + let mut overlay = Overlay::new_custom(2 * n, Default::default(), solver); + overlay.add_contour(&subj_path, ShapeType::Subject); + if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { + graph.validate(); + let _ = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); + } } } } #[test] fn test_13() { + test_13_as::(); + test_13_as::(); + test_13_as::(); + } + + fn test_13_as() { let n = 5; - for i in 1..50000 { - let r = i as f64; - let subj_path = random_float(r, n); - let mut overlay = FloatOverlay::with_subj(&subj_path); - if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { - graph.graph.validate(); - let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); - assert!(!result.is_empty()); + for &solver in SOLVERS.iter() { + for i in 1..50000 { + let r = i as f64; + let subj_path = random_float(r, n); + let mut overlay = + FloatOverlay::<_, I>::from_subj_custom(&subj_path, Default::default(), solver); + if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { + graph.graph.validate(); + let _ = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); + } } } } #[test] fn test_14() { - let p = IntPoint::new(0, 0); + test_14_as::(); + // test_14_as::(); + // test_14_as::(); + } + + fn test_14_as() { + let p = point::(0, 0); let mut rng = rand::rng(); let paths_count = 3; let mut subj_paths = Vec::with_capacity(paths_count); - for _ in 0..1_000_000 { - subj_paths.clear(); - let x_range = 0..=8; - let y_range = -8..=8; - for _ in 0..paths_count { - let ax = rng.random_range(x_range.clone()); - let ay = rng.random_range(y_range.clone()); - let bx = rng.random_range(x_range.clone()); - let by = rng.random_range(y_range.clone()); - subj_paths.push(vec![p, IntPoint::new(ax, ay), IntPoint::new(bx, by)]); - } + for &solver in SOLVERS.iter() { + for _ in 0..100_000 { + subj_paths.clear(); + let x_range = 0..=8; + let y_range = -8..=8; + for _ in 0..paths_count { + let ax = rng.random_range(x_range.clone()); + let ay = rng.random_range(y_range.clone()); + let bx = rng.random_range(x_range.clone()); + let by = rng.random_range(y_range.clone()); + subj_paths.push(vec![p, point::(ax, ay), point::(bx, by)]); + } - let mut overlay = Overlay::new(4); - overlay.add_contours(&subj_paths, ShapeType::Subject); - if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { - graph.validate(); - let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); - assert!(!result.is_empty()); + let mut overlay = Overlay::new_custom(4, Default::default(), solver); + overlay.add_contours(&subj_paths, ShapeType::Subject); + if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { + graph.validate(); + let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); + assert!(!result.is_empty()); + } } } } #[test] fn test_15() { + test_15_as::(); + test_15_as::(); + test_15_as::(); + } + + fn test_15_as() { let subj_paths = [ - vec![IntPoint::new(0, 0), IntPoint::new(1, 6), IntPoint::new(6, 4)], - vec![IntPoint::new(0, 0), IntPoint::new(6, 5), IntPoint::new(2, -2)], - vec![IntPoint::new(0, 0), IntPoint::new(3, -1), IntPoint::new(1, 3)], + vec![point::(0, 0), point(1, 6), point(6, 4)], + vec![point::(0, 0), point(6, 5), point(2, -2)], + vec![point::(0, 0), point(3, -1), point(1, 3)], ]; - let mut overlay = Overlay::new(4); - overlay.add_contours(&subj_paths, ShapeType::Subject); - if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { - graph.validate(); - let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); - assert!(!result.is_empty()); + for &solver in SOLVERS.iter() { + let mut overlay = Overlay::new_custom(4, Default::default(), solver); + overlay.add_contours(&subj_paths, ShapeType::Subject); + if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { + graph.validate(); + let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); + assert!(!result.is_empty()); + } } } - fn create_star(r0: f64, r1: f64, count: usize, angle: f64) -> IntShape { + fn create_star(r0: f64, r1: f64, count: usize, angle: f64, scale: f64) -> IntShape { let da = PI / count as f64; let mut a = angle; let mut points = Vec::new(); - let sr0 = r0 * 1024.0; - let sr1 = r1 * 1024.0; + let sr0 = r0 * scale; + let sr1 = r1 * scale; for _ in 0..count { - let xr0 = (sr0 * a.cos()) as i32; - let yr0 = (sr0 * a.sin()) as i32; + let xr0 = I::from_float(sr0 * a.cos()); + let yr0 = I::from_float(sr0 * a.sin()); a += da; - let xr1 = (sr1 * a.cos()) as i32; - let yr1 = (sr1 * a.sin()) as i32; + let xr1 = I::from_float(sr1 * a.cos()); + let yr1 = I::from_float(sr1 * a.sin()); a += da; @@ -351,25 +476,25 @@ mod tests { [points].to_vec() } - fn random_polygon(radius: f64, angle: f64, n: usize) -> IntPath { + fn random_polygon(radius: f64, angle: f64, n: usize, scale: f64) -> IntPath { let mut result = Vec::with_capacity(n); let da: f64 = PI * 0.7; let mut a: f64 = angle; - let r = 1024.0 * radius; + let r = scale * radius; for _ in 0..n { let (sin, cos) = a.sin_cos(); let x = r * cos; let y = r * sin; - result.push(IntPoint::new(x as i32, y as i32)); + result.push(IntPoint::new(I::from_float(x), I::from_float(y))); a += da; } result } - fn random(radius: i32, n: usize) -> IntPath { + fn random(radius: i32, n: usize) -> IntPath { let a = radius / 2; let range = -a..=a; let mut points = Vec::with_capacity(n); @@ -377,12 +502,27 @@ mod tests { for _ in 0..n { let x = rng.random_range(range.clone()); let y = rng.random_range(range.clone()); - points.push(IntPoint { x, y }) + points.push(point(x, y)) } points } + fn point(x: i32, y: i32) -> IntPoint { + IntPoint { + x: I::from_float(x as f64), + y: I::from_float(y as f64), + } + } + + fn scale_for(max_abs_coord: f64) -> f64 { + if max_abs_coord <= 0.0 { + return 1024.0; + } + + 1024.0_f64.min(I::MAX.to_f64() * 0.25 / max_abs_coord) + } + fn random_float(radius: f64, n: usize) -> Path<[f64; 2]> { let a = 0.5 * radius; let range = -a..=a; From 9c8892c95a9c899b4e833253d6acb242a942b81a Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Fri, 22 May 2026 15:58:48 +0300 Subject: [PATCH 23/36] fix empty segmeents --- iOverlay/src/split/solver.rs | 2 +- iOverlay/tests/crash_tests.rs | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/iOverlay/src/split/solver.rs b/iOverlay/src/split/solver.rs index 77b215b..ffbbe10 100644 --- a/iOverlay/src/split/solver.rs +++ b/iOverlay/src/split/solver.rs @@ -40,7 +40,7 @@ where segments.sort_by_ab(solver.is_parallel_sort_allowed()); let any_merged = segments.merge_if_needed(); - if !segments.is_empty() { + if segments.is_empty() { return true; } diff --git a/iOverlay/tests/crash_tests.rs b/iOverlay/tests/crash_tests.rs index bb7b21d..44c89b4 100644 --- a/iOverlay/tests/crash_tests.rs +++ b/iOverlay/tests/crash_tests.rs @@ -30,4 +30,26 @@ mod tests { } } + #[test] + fn test_01() { + let subj: Shape<_> = int_shape![ + [[0i32, 0], [0, 4], [3, -5]], + [[0, 0], [1, 7], [2, -8]], + [[0, 0], [4, -4], [5, 7]], + ]; + + let solver = Solver { + strategy: Strategy::List, + precision: Precision { start: 0, progression: 1 }, + multithreading: None, + }; + + let mut overlay = Overlay::new_custom(4, Default::default(), solver); + overlay.add_contours(&subj, ShapeType::Subject); + if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { + graph.validate(); + let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); + assert!(!result.is_empty()); + } + } } From 05d3fda1b6756dae701af90bea87dea51bfce602 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Fri, 22 May 2026 21:30:39 +0300 Subject: [PATCH 24/36] empty segments fix --- iOverlay/src/split/grid_layout.rs | 6 +- iOverlay/src/split/solver_tree.rs | 16 ++-- iOverlay/tests/crash_tests.rs | 118 ++++++++++++++++++++++++++---- iOverlay/tests/dynamic_tests.rs | 74 ++++++------------- 4 files changed, 141 insertions(+), 73 deletions(-) diff --git a/iOverlay/src/split/grid_layout.rs b/iOverlay/src/split/grid_layout.rs index 8492541..7e26d99 100644 --- a/iOverlay/src/split/grid_layout.rs +++ b/iOverlay/src/split/grid_layout.rs @@ -256,7 +256,7 @@ pub(super) struct GridLayout { impl GridLayout { #[inline] pub(super) fn index(&self, x: I) -> usize { - ((x - self.min_x) >> self.power).to_usize() + ((x.wide() - self.min_x.wide()) >> self.power).to_usize() } #[inline] @@ -284,8 +284,8 @@ impl GridLayout { } fn with_min_max(min_x: I, max_x: I, max_power: u32) -> Option { - let dx = max_x - min_x; - if dx < I::FOUR { + let dx = max_x.wide() - min_x.wide(); + if dx < I::Wide::FOUR { return None; } let log = dx.ilog2(); diff --git a/iOverlay/src/split/solver_tree.rs b/iOverlay/src/split/solver_tree.rs index 696fbdd..8e60d6b 100644 --- a/iOverlay/src/split/solver_tree.rs +++ b/iOverlay/src/split/solver_tree.rs @@ -37,7 +37,11 @@ where segments: &mut Vec>, solver: &Solver, ) -> bool { - let range: SegRange = segments.ver_range().into(); + let range: SegRange = if let Some(range) = segments.ver_range() { + range.into() + } else { + return false; + }; let mut tree: SegExpTree> = if let Some(tree) = SegExpTree::new(range) { tree } else { @@ -104,14 +108,14 @@ impl From> for SegRange { trait VerticalRange { type Int: IntNumber; - fn ver_range(&self) -> LineRange; + fn ver_range(&self) -> Option>; } impl VerticalRange for Vec> { type Int = I; - fn ver_range(&self) -> LineRange { - let mut min_y = self[0].x_segment.a.y; + fn ver_range(&self) -> Option> { + let mut min_y = self.first()?.x_segment.a.y; let mut max_y = min_y; for edge in self.iter() { @@ -121,10 +125,10 @@ impl VerticalRange for Vec> { max_y = max_y.max(edge.x_segment.b.y); } - LineRange { + Some(LineRange { min: min_y, max: max_y, - } + }) } } diff --git a/iOverlay/tests/crash_tests.rs b/iOverlay/tests/crash_tests.rs index 44c89b4..2f51d54 100644 --- a/iOverlay/tests/crash_tests.rs +++ b/iOverlay/tests/crash_tests.rs @@ -1,11 +1,27 @@ #[cfg(test)] 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::overlay::{Overlay, ShapeType}; use i_overlay::core::overlay_rule::OverlayRule; - use i_shape::base::data::Shape; - use i_shape::int_shape; use i_overlay::core::solver::{Precision, Solver, Strategy}; + use i_overlay::float::overlay::FloatOverlay; + 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 {} + const SOLVERS: [Solver; 4] = [Solver::LIST, Solver::TREE, Solver::FRAG, Solver::AUTO]; + + fn point(x: i32, y: i32) -> IntPoint { + IntPoint { + x: I::from_float(x as f64), + y: I::from_float(y as f64), + } + } #[test] fn test_00() { @@ -17,7 +33,10 @@ mod tests { let solver = Solver { strategy: Strategy::List, - precision: Precision { start: 0, progression: 1 }, + precision: Precision { + start: 0, + progression: 1, + }, multithreading: None, }; @@ -32,24 +51,97 @@ mod tests { #[test] fn test_01() { - let subj: Shape<_> = int_shape![ - [[0i32, 0], [0, 4], [3, -5]], - [[0, 0], [1, 7], [2, -8]], - [[0, 0], [4, -4], [5, 7]], + let subj = [ + [-117.04171489206965, 1820.3621519926919], + [4619.6817058891429, -2133.11539650432], + [1902.5599837294722, -133.53167784432389], + [-3572.1275050425684, 3909.4677532724309], + [3047.0491344383845, -4087.6336157702817], ]; let solver = Solver { - strategy: Strategy::List, - precision: Precision { start: 0, progression: 1 }, + strategy: Strategy::Frag, + precision: Precision { + start: 0, + progression: 1, + }, multithreading: None, }; - let mut overlay = Overlay::new_custom(4, Default::default(), solver); - overlay.add_contours(&subj, ShapeType::Subject); + let mut overlay = FloatOverlay::<_, i16>::from_subj_custom(&subj, Default::default(), solver); + if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { + graph.graph.validate(); + let _ = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); + } + } + + #[test] + fn test_02() { + test_02_as::(); + test_02_as::(); + test_02_as::(); + } + + fn test_02_as() { + let subj_paths = [ + vec![point::(0, 0), point(1, 6), point(6, 4)], + vec![point::(0, 0), point(6, 5), point(2, -2)], + vec![point::(0, 0), point(3, -1), point(1, 3)], + ]; + for &solver in SOLVERS.iter() { + let mut overlay = Overlay::new_custom(4, Default::default(), solver); + overlay.add_contours(&subj_paths, ShapeType::Subject); + if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { + graph.validate(); + let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); + assert!(!result.is_empty()); + } + } + } + + #[test] + fn test_03() { + let subj: Path<_> = int_path![ + [3, 4], [5, 0], [3, 3], [4, 2], [5, -2] + ]; + + let solver = Solver { + strategy: Strategy::Tree, + precision: Precision { + start: 0, + progression: 1, + }, + multithreading: None, + }; + + let mut overlay = Overlay::new_custom(10, Default::default(), solver); + overlay.add_contour(&subj, ShapeType::Subject); if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { graph.validate(); - let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); - assert!(!result.is_empty()); + let _ = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); + } + } + + #[test] + fn test_04() { + let subj: Path> = int_path![ + [-4, -2], [1, -3], [-1, 3], [1, -4], [4, -3] + ]; + + let solver = Solver { + strategy: Strategy::Tree, + precision: Precision { + start: 0, + progression: 1, + }, + multithreading: None, + }; + + let mut overlay = Overlay::new_custom(10, Default::default(), solver); + overlay.add_contour(&subj, ShapeType::Subject); + if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { + graph.validate(); + let _ = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); } } } diff --git a/iOverlay/tests/dynamic_tests.rs b/iOverlay/tests/dynamic_tests.rs index ebdd5b7..bc97df4 100644 --- a/iOverlay/tests/dynamic_tests.rs +++ b/iOverlay/tests/dynamic_tests.rs @@ -21,7 +21,6 @@ mod tests { impl TestInt for I where I: IntNumber + Expiration + LayoutNumber + SortKey {} - #[test] fn test_0() { test_0_as::(); @@ -347,8 +346,8 @@ mod tests { #[test] fn test_12() { test_12_as::(); - test_12_as::(); - test_12_as::(); + // test_12_as::(); + // test_12_as::(); } fn test_12_as() { @@ -375,16 +374,14 @@ mod tests { fn test_13_as() { let n = 5; - for &solver in SOLVERS.iter() { - for i in 1..50000 { - let r = i as f64; - let subj_path = random_float(r, n); - let mut overlay = - FloatOverlay::<_, I>::from_subj_custom(&subj_path, Default::default(), solver); - if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { - graph.graph.validate(); - let _ = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); - } + for i in 1..10000 { + let r = i as f64; + let subj_path = random_float(r, n); + let mut overlay = + FloatOverlay::<_, I>::from_subj_custom(&subj_path, Default::default(), Default::default()); + if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { + graph.graph.validate(); + let _ = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); } } } @@ -392,8 +389,8 @@ mod tests { #[test] fn test_14() { test_14_as::(); - // test_14_as::(); - // test_14_as::(); + test_14_as::(); + test_14_as::(); } fn test_14_as() { @@ -401,45 +398,20 @@ mod tests { let mut rng = rand::rng(); let paths_count = 3; let mut subj_paths = Vec::with_capacity(paths_count); - for &solver in SOLVERS.iter() { - for _ in 0..100_000 { - subj_paths.clear(); - let x_range = 0..=8; - let y_range = -8..=8; - for _ in 0..paths_count { - let ax = rng.random_range(x_range.clone()); - let ay = rng.random_range(y_range.clone()); - let bx = rng.random_range(x_range.clone()); - let by = rng.random_range(y_range.clone()); - subj_paths.push(vec![p, point::(ax, ay), point::(bx, by)]); - } - let mut overlay = Overlay::new_custom(4, Default::default(), solver); - overlay.add_contours(&subj_paths, ShapeType::Subject); - if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { - graph.validate(); - let result = graph.extract_shapes(OverlayRule::Subject, &mut Default::default()); - assert!(!result.is_empty()); - } + for _ in 0..100_000 { + subj_paths.clear(); + let x_range = 0..=8; + let y_range = -8..=8; + for _ in 0..paths_count { + let ax = rng.random_range(x_range.clone()); + let ay = rng.random_range(y_range.clone()); + let bx = rng.random_range(x_range.clone()); + let by = rng.random_range(y_range.clone()); + subj_paths.push(vec![p, point::(ax, ay), point::(bx, by)]); } - } - } - #[test] - fn test_15() { - test_15_as::(); - test_15_as::(); - test_15_as::(); - } - - fn test_15_as() { - let subj_paths = [ - vec![point::(0, 0), point(1, 6), point(6, 4)], - vec![point::(0, 0), point(6, 5), point(2, -2)], - vec![point::(0, 0), point(3, -1), point(1, 3)], - ]; - for &solver in SOLVERS.iter() { - let mut overlay = Overlay::new_custom(4, Default::default(), solver); + let mut overlay = Overlay::new_custom(4, Default::default(), Default::default()); overlay.add_contours(&subj_paths, ShapeType::Subject); if let Some(graph) = overlay.build_graph_view(FillRule::NonZero) { graph.validate(); From 7324143a5b5dc8c061640ff56abbb3087e1afcd7 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Fri, 22 May 2026 22:32:30 +0300 Subject: [PATCH 25/36] fix div cell --- iOverlay/src/split/grid_layout.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/iOverlay/src/split/grid_layout.rs b/iOverlay/src/split/grid_layout.rs index 7e26d99..10930ea 100644 --- a/iOverlay/src/split/grid_layout.rs +++ b/iOverlay/src/split/grid_layout.rs @@ -125,8 +125,10 @@ impl FragmentBuffer { for i in i0..i1 { let h_min = (w * k) >> p; let mut h_max = h_min; - while h_max * width < height * w { - h_max += I::WideUInt::ONE; + + let hw = height * w; + if h_max * width < hw { + h_max = (hw + width - I::WideUInt::ONE) / width; } let max_x = x0 + I::from_uint(w); From 4457b956592837b25bd76f2e83fb12e40631c360 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Fri, 22 May 2026 22:33:55 +0300 Subject: [PATCH 26/36] reduce test step --- iOverlay/tests/dynamic_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOverlay/tests/dynamic_tests.rs b/iOverlay/tests/dynamic_tests.rs index bc97df4..570dcf1 100644 --- a/iOverlay/tests/dynamic_tests.rs +++ b/iOverlay/tests/dynamic_tests.rs @@ -125,7 +125,7 @@ mod tests { graph.validate(); let _ = graph.extract_shapes(OverlayRule::Xor, &mut Default::default()); } - a += 0.001 + a += 0.005 } } } From 447e512a98d6b2106ed5cb7c4b94043e9b4c8acb Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Fri, 22 May 2026 22:34:24 +0300 Subject: [PATCH 27/36] update tests results --- .../rust_app/src/test/test_0_checkerboard.rs | 4 - .../rust_app/src/test/test_3_spiral.rs | 98 +++++++++++----- .../src/test/test_5_nested_squares.rs | 39 +++++++ .../rust_app/src/test/test_6_corrosion.rs | 96 ++++++++++++---- .../rust_app/src/test/test_7_concentric.rs | 106 ++++++++++++------ .../rust_app/src/test/test_8_wind_mill.rs | 95 ++++++++++++---- 6 files changed, 326 insertions(+), 112 deletions(-) diff --git a/performance/rust_app/src/test/test_0_checkerboard.rs b/performance/rust_app/src/test/test_0_checkerboard.rs index bd44d41..8228e2d 100644 --- a/performance/rust_app/src/test/test_0_checkerboard.rs +++ b/performance/rust_app/src/test/test_0_checkerboard.rs @@ -54,7 +54,6 @@ multithreading on 256(130561 5.1) - 0.125711(-0.9) 512(523265 5.7) - 0.592768(-0.2) 1024(2095105 6.3) - 2.610431(0.4) -2048(8384513 6.9) - 15.529388(1.2) multithreading off @@ -68,7 +67,6 @@ multithreading off 256(130561 5.1) - 0.178425(-0.7) 512(523265 5.7) - 0.827264(-0.1) 1024(2095105 6.3) - 3.611125(0.6) -2048(8384513 6.9) - 19.434681(1.3) i64 @@ -84,7 +82,6 @@ multithreading on 256(130561 5.1) - 0.158665(-0.8) 512(523265 5.7) - 0.724087(-0.1) 1024(2095105 6.3) - 3.127404(0.5) -2048(8384513 6.9) - 21.165892(1.3) multithreading off @@ -98,7 +95,6 @@ multithreading off 256(130561 5.1) - 0.219622(-0.7) 512(523265 5.7) - 1.011703(0.0) 1024(2095105 6.3) - 4.393880(0.6) -2048(8384513 6.9) - 26.690416(1.4) */ // A grid of overlapping squares forming a simple checkerboard pattern. diff --git a/performance/rust_app/src/test/test_3_spiral.rs b/performance/rust_app/src/test/test_3_spiral.rs index 0276079..777287f 100644 --- a/performance/rust_app/src/test/test_3_spiral.rs +++ b/performance/rust_app/src/test/test_3_spiral.rs @@ -22,21 +22,20 @@ multithreading on 8 - 0.000005 16 - 0.000007 32 - 0.000015 -64 - 0.000032 -128 - 0.000079 -256 - 0.000249 -512 - 0.000715 -1024 - 0.001833 -2048 - 0.004278 -4096 - 0.004283 -8192 - 0.008659 -16384 - 0.017107 -32768 - 0.033244 -65536 - 0.066839 -131072 - 0.131752 -262144 - 0.291700 -524288 - 0.455038 -1048576 - 0.945685 +64 - 0.000031 +128 - 0.000076 +256 - 0.000279 +512 - 0.000764 +1024 - 0.001962 +2048 - 0.003950 +4096 - 0.004374 +8192 - 0.008446 +16384 - 0.017347 +32768 - 0.033183 +65536 - 0.065167 +131072 - 0.131789 +262144 - 0.301777 +524288 - 0.456391 multithreading off @@ -46,20 +45,19 @@ multithreading off 16 - 0.000007 32 - 0.000015 64 - 0.000031 -128 - 0.000082 -256 - 0.000258 -512 - 0.000736 -1024 - 0.001867 -2048 - 0.004070 -4096 - 0.005567 -8192 - 0.010481 -16384 - 0.022516 -32768 - 0.045402 -65536 - 0.095105 -131072 - 0.198492 -262144 - 0.431128 -524288 - 0.661296 -1048576 - 1.435810 +128 - 0.000086 +256 - 0.000251 +512 - 0.000698 +1024 - 0.001818 +2048 - 0.004124 +4096 - 0.005286 +8192 - 0.010566 +16384 - 0.022061 +32768 - 0.045513 +65536 - 0.094961 +131072 - 0.201415 +262144 - 0.431597 +524288 - 0.666660 i32 @@ -84,7 +82,6 @@ multithreading on 131072 - 0.146095 262144 - 0.350941 524288 - 0.744520 -1048576 - 1.626030 multithreading off @@ -107,14 +104,53 @@ multithreading off 131072 - 0.223653 262144 - 0.541526 524288 - 1.034178 -1048576 - 2.303664 i64 multithreading on +2 - 0.000001 +4 - 0.000003 +8 - 0.000008 +16 - 0.000016 +32 - 0.000033 +64 - 0.000069 +128 - 0.000154 +256 - 0.000380 +512 - 0.001008 +1024 - 0.002594 +2048 - 0.005705 +4096 - 0.005941 +8192 - 0.010722 +16384 - 0.023228 +32768 - 0.042546 +65536 - 0.091817 +131072 - 0.192015 +262144 - 0.449105 +524288 - 0.922172 + multithreading off +2 - 0.000001 +4 - 0.000003 +8 - 0.000007 +16 - 0.000016 +32 - 0.000034 +64 - 0.000071 +128 - 0.000159 +256 - 0.000376 +512 - 0.001018 +1024 - 0.002658 +2048 - 0.005736 +4096 - 0.009687 +8192 - 0.017713 +16384 - 0.037901 +32768 - 0.075060 +65536 - 0.165686 +131072 - 0.336338 +262144 - 0.780771 +524288 - 1.536626 + */ // Two irregular self-intersecting polygons are generated, the vertices of which are defined by a fixed radius and angle. diff --git a/performance/rust_app/src/test/test_5_nested_squares.rs b/performance/rust_app/src/test/test_5_nested_squares.rs index e251eb5..4764c21 100644 --- a/performance/rust_app/src/test/test_5_nested_squares.rs +++ b/performance/rust_app/src/test/test_5_nested_squares.rs @@ -78,6 +78,45 @@ i32 65536 - 1.401006 131072 - 5.445065 +i64 + +// multithreading on +4 - 0.000004 +8 - 0.000009 +16 - 0.000018 +32 - 0.000040 +64 - 0.000097 +128 - 0.000253 +256 - 0.000756 +512 - 0.002833 +1024 - 0.007585 +2048 - 0.009880 +4096 - 0.018457 +8192 - 0.059750 +16384 - 0.122264 +32768 - 0.431346 +65536 - 0.933473 +131072 - 3.822923 + +// multithreading off + +4 - 0.000005 +8 - 0.000008 +16 - 0.000018 +32 - 0.000040 +64 - 0.000096 +128 - 0.000250 +256 - 0.000760 +512 - 0.002837 +1024 - 0.007613 +2048 - 0.015553 +4096 - 0.031292 +8192 - 0.113196 +16384 - 0.245353 +32768 - 0.961019 +65536 - 2.059438 +131072 - 8.138039 + */ // A series of concentric squares, each progressively larger than the last. diff --git a/performance/rust_app/src/test/test_6_corrosion.rs b/performance/rust_app/src/test/test_6_corrosion.rs index 11e4add..fcc34a7 100644 --- a/performance/rust_app/src/test/test_6_corrosion.rs +++ b/performance/rust_app/src/test/test_6_corrosion.rs @@ -13,31 +13,83 @@ pub(crate) struct CorrosionTest; // 6 // Difference: +i16 + +// multithreading on +1 - 0.000006 +2 - 0.000018 +4 - 0.000082 +8 - 0.000578 +16 - 0.003789 +32 - 0.007703 +64 - 0.030151 +128 - 0.134113 +256 - 0.553680 +512 - 2.020489 + +// multithreading off +1 - 0.000006 +2 - 0.000018 +4 - 0.000082 +8 - 0.000583 +16 - 0.003799 +32 - 0.009568 +64 - 0.041341 +128 - 0.209341 +256 - 0.878897 +512 - 2.869231 + +i32 + +// multithreading on +1 - 0.000007 +2 - 0.000024 +4 - 0.000103 +8 - 0.000645 +16 - 0.004095 +32 - 0.008387 +64 - 0.033293 +128 - 0.133794 +256 - 0.594231 +512 - 2.297538 + +// multithreading off +1 - 0.000007 +2 - 0.000023 +4 - 0.000104 +8 - 0.000636 +16 - 0.004134 +32 - 0.011785 +64 - 0.050564 +128 - 0.199536 +256 - 0.812732 +512 - 3.383901 + +i64 + // multithreading on -1 - 0.000010 -2 - 0.000061 -4 - 0.000364 -8 - 0.001869 -16 - 0.004424 -32 - 0.017941 -64 - 0.079459 -128 - 0.326245 -256 - 1.313516 -512 - 5.392524 -1024 - 22.228494 +1 - 0.000009 +2 - 0.000036 +4 - 0.000158 +8 - 0.000914 +16 - 0.005314 +32 - 0.010206 +64 - 0.042668 +128 - 0.175621 +256 - 0.737713 +512 - 3.101692 // multithreading off -1 - 0.000010 -2 - 0.000064 -4 - 0.000381 -8 - 0.001897 -16 - 0.005981 -32 - 0.023521 -64 - 0.106998 -128 - 0.439589 -256 - 1.756946 -512 - 7.186862 -1024 - 30.818670 +1 - 0.000009 +2 - 0.000036 +4 - 0.000159 +8 - 0.000918 +16 - 0.005234 +32 - 0.016559 +64 - 0.072355 +128 - 0.304704 +256 - 1.280368 +512 - 5.351266 */ // A series of concentric squares, each progressively larger than the last. diff --git a/performance/rust_app/src/test/test_7_concentric.rs b/performance/rust_app/src/test/test_7_concentric.rs index 41355a7..fa0457f 100644 --- a/performance/rust_app/src/test/test_7_concentric.rs +++ b/performance/rust_app/src/test/test_7_concentric.rs @@ -13,43 +13,83 @@ pub(crate) struct ConcentricTest; // 7 // Difference: +i16 + +// multithreading on +1 - 0.000007 +2 - 0.000020 +4 - 0.000082 +8 - 0.000610 +16 - 0.005260 +32 - 0.013028 +64 - 0.053713 +128 - 0.318682 +256 - 1.384741 +512 - 6.981723 + +// multithreading off +1 - 0.000007 +2 - 0.000020 +4 - 0.000083 +8 - 0.000569 +16 - 0.005350 +32 - 0.009203 +64 - 0.035059 +128 - 0.170760 +256 - 0.712446 +512 - 3.317967 + +i32 + +// multithreading on +1 - 0.000010 +2 - 0.000028 +4 - 0.000113 +8 - 0.000660 +16 - 0.004171 +32 - 0.008281 +64 - 0.034357 +128 - 0.134210 +256 - 0.563524 +512 - 2.367582 + +// multithreading off +1 - 0.000010 +2 - 0.000029 +4 - 0.000112 +8 - 0.000709 +16 - 0.004211 +32 - 0.012234 +64 - 0.052943 +128 - 0.223251 +256 - 0.955805 +512 - 3.692530 + +i64 + // multithreading on 1 - 0.000016 -2 - 0.000086 -4 - 0.000407 -8 - 0.001839 -16 - 0.008672 -32 - 0.028811 -64 - 0.090131 -128 - 0.368713 -256 - 1.456724 -512 - 6.657090 -1024 - 31.702080 +2 - 0.000049 +4 - 0.000189 +8 - 0.000955 +16 - 0.005389 +32 - 0.010158 +64 - 0.042141 +128 - 0.171089 +256 - 0.726401 +512 - 3.116469 // multithreading off -1 - 0.000015 -2 - 0.000086 -4 - 0.000402 -8 - 0.001852 -16 - 0.008529 -32 - 0.035403 -64 - 0.122799 -128 - 0.504404 -256 - 2.038331 -512 - 10.096450 -1024 - 50.159095 - - -1 - 0.000015 -2 - 0.000086 -4 - 0.000399 -8 - 0.001818 -16 - 0.007582 -32 - 0.036797 -64 - 0.133616 -128 - 0.536633 -256 - 2.157370 -512 - 10.505163 +1 - 0.000016 +2 - 0.000049 +4 - 0.000186 +8 - 0.001010 +16 - 0.005452 +32 - 0.017694 +64 - 0.075916 +128 - 0.325436 +256 - 1.386162 +512 - 5.722743 */ diff --git a/performance/rust_app/src/test/test_8_wind_mill.rs b/performance/rust_app/src/test/test_8_wind_mill.rs index 3571e5b..dc01b7b 100644 --- a/performance/rust_app/src/test/test_8_wind_mill.rs +++ b/performance/rust_app/src/test/test_8_wind_mill.rs @@ -14,31 +14,82 @@ pub(crate) struct WindMillTest; // 7 // Difference: +i16 + +// multithreading on +1 - 0.000004 +2 - 0.000012 +4 - 0.000049 +8 - 0.000239 +16 - 0.001321 +32 - 0.003630 +64 - 0.013392 +128 - 0.055149 +256 - 0.245865 + +// multithreading off +1 - 0.000004 +2 - 0.000012 +4 - 0.000049 +8 - 0.000241 +16 - 0.001326 +32 - 0.004098 +64 - 0.018836 +128 - 0.080683 +256 - 0.375508 + +i32 + // multithreading on -1 - 0.000007 -2 - 0.000028 -4 - 0.000136 -8 - 0.000702 -16 - 0.003199 -32 - 0.010574 -64 - 0.049129 -128 - 0.202190 -256 - 0.887956 -512 - 3.830878 -1024 - 15.643612 +1 - 0.000004 +2 - 0.000013 +4 - 0.000048 +8 - 0.000229 +16 - 0.001354 +32 - 0.003816 +64 - 0.015072 +128 - 0.061520 +256 - 0.280049 +512 - 1.243484 // multithreading off -1 - 0.000007 -2 - 0.000027 -4 - 0.000136 -8 - 0.000708 -16 - 0.003072 -32 - 0.010975 -64 - 0.054829 -128 - 0.243694 -256 - 1.180819 -512 - 4.130558 -1024 - 17.840079 +1 - 0.000004 +2 - 0.000012 +4 - 0.000049 +8 - 0.000231 +16 - 0.001354 +32 - 0.004408 +64 - 0.021355 +128 - 0.090989 +256 - 0.416754 +512 - 1.820984 + +i64 + +// multithreading on +1 - 0.000005 +2 - 0.000014 +4 - 0.000058 +8 - 0.000284 +16 - 0.001662 +32 - 0.004458 +64 - 0.018935 +128 - 0.077800 +256 - 0.349065 +512 - 1.508555 + +// multithreading off +1 - 0.000005 +2 - 0.000014 +4 - 0.000059 +8 - 0.000281 +16 - 0.001637 +32 - 0.005425 +64 - 0.026436 +128 - 0.115288 +256 - 0.513712 +512 - 2.269812 + */ impl WindMillTest { From 5dff0d3ebd529816656a3f6729b6d16eab8afb0e Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sat, 23 May 2026 16:12:44 +0300 Subject: [PATCH 28/36] update readme --- iOverlay/README.md | 11 ++- iOverlay/readme/average_relative_time.svg | 97 +++++++++++++++++++ .../rust_app/src/test/test_0_checkerboard.rs | 7 +- .../rust_app/src/test/test_1_not_overlap.rs | 6 +- .../rust_app/src/test/test_2_lines_net.rs | 6 +- .../rust_app/src/test/test_3_spiral.rs | 6 +- .../rust_app/src/test/test_4_windows.rs | 5 +- .../src/test/test_5_nested_squares.rs | 6 +- .../rust_app/src/test/test_6_corrosion.rs | 6 +- .../rust_app/src/test/test_7_concentric.rs | 6 +- .../rust_app/src/test/test_8_wind_mill.rs | 5 +- 11 files changed, 134 insertions(+), 27 deletions(-) create mode 100644 iOverlay/readme/average_relative_time.svg diff --git a/iOverlay/README.md b/iOverlay/README.md index 12b2dc9..0e996b2 100644 --- a/iOverlay/README.md +++ b/iOverlay/README.md @@ -70,9 +70,16 @@ iOverlay powers polygon boolean operations in [geo](https://github.com/georust/g   ## Performance -iOverlay is optimized for large and complex inputs while preserving robust geometry semantics. The benchmark report compares iOverlay to other polygon overlay engines with a focus on throughput and performance. +iOverlay supports: -See the detailed report: [Performance Comparison](https://ishape-rust.github.io/iShape-js/overlay/performance/performance.html) +- `i16`/`i32`/`i64` math solvers +- `on`/`off` multithreading feature + +Average relative time for iOverlay Rust solvers + +For bigger data sets, the math engine and multithreading mode have a larger impact on runtime. Results below were measured on Apple M4, 24 GB. + +See the detailed reports: [Performance Comparison](https://ishape-rust.github.io/iShape-js/overlay/performance/performance.html) and [Rust Solver Benchmarks](https://ishape-rust.github.io/iShape-js/overlay/performance/rust_i_overlay.html)   ## Getting Started diff --git a/iOverlay/readme/average_relative_time.svg b/iOverlay/readme/average_relative_time.svg new file mode 100644 index 0000000..56a16a1 --- /dev/null +++ b/iOverlay/readme/average_relative_time.svg @@ -0,0 +1,97 @@ + + Average Relative Time + Geometric mean relative time across benchmark rows where every solver has a result. i32 off is the baseline. + + + Average relative time, i32 off = 1.00x + + + 0.50 + + 0.75 + + 1.00 + + 1.25 + + + i16 off + + 0.92x + + i32 off + + 1.00x + + i64 off + + 1.30x + + i16 on + + 0.85x + + i32 on + + 0.86x + + i64 on + + 1.08x + + Lower is faster. Apple M4, 24 GB. + diff --git a/performance/rust_app/src/test/test_0_checkerboard.rs b/performance/rust_app/src/test/test_0_checkerboard.rs index 8228e2d..adeef5b 100644 --- a/performance/rust_app/src/test/test_0_checkerboard.rs +++ b/performance/rust_app/src/test/test_0_checkerboard.rs @@ -9,8 +9,9 @@ use std::time::Instant; pub(crate) struct CheckerboardTest; /* -// test 0 -// Xor: +test 0 +CheckerboardTest +Xor: i16 @@ -101,7 +102,7 @@ multithreading off impl CheckerboardTest { pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64) { // 1000 - if Util::skip_if_out_of_range::(n, 30 * n + 20) { + if Util::skip_if_out_of_range::(n, 15 * n + 10) { return; } diff --git a/performance/rust_app/src/test/test_1_not_overlap.rs b/performance/rust_app/src/test/test_1_not_overlap.rs index b3b91df..d447e08 100644 --- a/performance/rust_app/src/test/test_1_not_overlap.rs +++ b/performance/rust_app/src/test/test_1_not_overlap.rs @@ -8,9 +8,9 @@ use std::time::Instant; pub(crate) struct NotOverlapTest; /* - -// 1 -// Union: +test 1 +NotOverlapTest +Union: i16 diff --git a/performance/rust_app/src/test/test_2_lines_net.rs b/performance/rust_app/src/test/test_2_lines_net.rs index 85afa38..3d98e92 100644 --- a/performance/rust_app/src/test/test_2_lines_net.rs +++ b/performance/rust_app/src/test/test_2_lines_net.rs @@ -8,9 +8,9 @@ use std::time::Instant; pub(crate) struct LinesNetTest; /* - -// 2 -// Intersection: +test 2 +LinesNetTest +Intersection: i16 diff --git a/performance/rust_app/src/test/test_3_spiral.rs b/performance/rust_app/src/test/test_3_spiral.rs index 777287f..7c2e8b8 100644 --- a/performance/rust_app/src/test/test_3_spiral.rs +++ b/performance/rust_app/src/test/test_3_spiral.rs @@ -9,9 +9,9 @@ use std::time::Instant; pub(crate) struct SpiralTest; /* - -// 3 -// Intersection: +test 3 +SpiralTest +Intersection: i16 diff --git a/performance/rust_app/src/test/test_4_windows.rs b/performance/rust_app/src/test/test_4_windows.rs index a56df86..5c460c4 100644 --- a/performance/rust_app/src/test/test_4_windows.rs +++ b/performance/rust_app/src/test/test_4_windows.rs @@ -8,8 +8,9 @@ use std::time::Instant; pub(crate) struct WindowsTest; /* -// 4 -// Difference: +test 4 +WindowsTest +Difference: i16 diff --git a/performance/rust_app/src/test/test_5_nested_squares.rs b/performance/rust_app/src/test/test_5_nested_squares.rs index 4764c21..de2565a 100644 --- a/performance/rust_app/src/test/test_5_nested_squares.rs +++ b/performance/rust_app/src/test/test_5_nested_squares.rs @@ -8,9 +8,9 @@ use std::time::Instant; pub(crate) struct CrossTest; /* - -// 5 -// Union: +test 5 +CrossTest +Union: i16 diff --git a/performance/rust_app/src/test/test_6_corrosion.rs b/performance/rust_app/src/test/test_6_corrosion.rs index fcc34a7..b7d7c95 100644 --- a/performance/rust_app/src/test/test_6_corrosion.rs +++ b/performance/rust_app/src/test/test_6_corrosion.rs @@ -9,9 +9,9 @@ use std::time::Instant; pub(crate) struct CorrosionTest; /* - -// 6 -// Difference: +test 6 +CorrosionTest +Difference: i16 diff --git a/performance/rust_app/src/test/test_7_concentric.rs b/performance/rust_app/src/test/test_7_concentric.rs index fa0457f..680c7d6 100644 --- a/performance/rust_app/src/test/test_7_concentric.rs +++ b/performance/rust_app/src/test/test_7_concentric.rs @@ -9,9 +9,9 @@ use std::time::Instant; pub(crate) struct ConcentricTest; /* - -// 7 -// Difference: +test 7 +ConcentricTest +Difference: i16 diff --git a/performance/rust_app/src/test/test_8_wind_mill.rs b/performance/rust_app/src/test/test_8_wind_mill.rs index dc01b7b..c318272 100644 --- a/performance/rust_app/src/test/test_8_wind_mill.rs +++ b/performance/rust_app/src/test/test_8_wind_mill.rs @@ -11,8 +11,9 @@ use std::time::Instant; pub(crate) struct WindMillTest; /* -// 7 -// Difference: +test 8 +WindMillTest +Difference: i16 From f1bcd8c94c78cf5daef2b22620bd4f3c1f486f93 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Mon, 25 May 2026 10:20:00 +0300 Subject: [PATCH 29/36] fix area sign --- iOverlay/src/core/overlay.rs | 32 ++++++++++++++--------------- iOverlay/src/mesh/outline/offset.rs | 2 +- iOverlay/src/split/grid_layout.rs | 8 ++++---- iOverlay/tests/direction_tests.rs | 16 +++++++-------- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/iOverlay/src/core/overlay.rs b/iOverlay/src/core/overlay.rs index 49a23a0..7074f3e 100644 --- a/iOverlay/src/core/overlay.rs +++ b/iOverlay/src/core/overlay.rs @@ -475,7 +475,7 @@ mod tests { let shape = &result[0]; assert_eq!(shape.len(), 1); assert_eq!(shape[0].len(), 4); - assert_eq!(shape[0].area(), -100i64); + assert_eq!(shape[0].area(), 100i64); } #[test] @@ -502,7 +502,7 @@ mod tests { let shape = &result[0]; assert_eq!(shape.len(), 1); assert_eq!(shape[0].len(), 4); - assert_eq!(shape[0].area(), -100i64); + assert_eq!(shape[0].area(), 100i64); } #[test] @@ -529,9 +529,9 @@ mod tests { let shape = &result[0]; assert_eq!(shape.len(), 2); assert_eq!(shape[0].len(), 4); - assert_eq!(shape[0].area(), -16i64); + assert_eq!(shape[0].area(), 16i64); assert_eq!(shape[1].len(), 4); - assert_eq!(shape[1].area(), 4i64); + assert_eq!(shape[1].area(), -4i64); } #[test] @@ -557,7 +557,7 @@ mod tests { assert_eq!(result.len(), 1); let shape = &result[0]; assert_eq!(shape.len(), 1); - assert_eq!(shape[0].area(), -10i64); + assert_eq!(shape[0].area(), 10i64); } #[test] @@ -589,7 +589,7 @@ mod tests { assert_eq!(result.len(), 1); let shape = &result[0]; assert_eq!(shape.len(), 1); - assert_eq!(shape[0].area(), -17i64); + assert_eq!(shape[0].area(), 17i64); } #[test] @@ -615,7 +615,7 @@ mod tests { assert_eq!(result.len(), 1); let shape = &result[0]; assert_eq!(shape.len(), 1); - assert_eq!(shape[0].area(), -16i64); + assert_eq!(shape[0].area(), 16i64); } #[test] @@ -653,7 +653,7 @@ mod tests { assert_eq!(result.len(), 1); let shape = &result[0]; assert_eq!(shape.len(), 1); - assert_eq!(shape[0].area(), -27i64); + assert_eq!(shape[0].area(), 27i64); } #[test] @@ -679,7 +679,7 @@ mod tests { assert_eq!(result.len(), 1); let shape = &result[0]; assert_eq!(shape.len(), 1); - assert_eq!(shape[0].area(), -12i64); + assert_eq!(shape[0].area(), 12i64); } #[test] @@ -705,7 +705,7 @@ mod tests { assert_eq!(result.len(), 1); let shape = &result[0]; assert_eq!(shape.len(), 1); - assert_eq!(shape[0].area(), -4i64); + assert_eq!(shape[0].area(), 4i64); } #[test] @@ -731,7 +731,7 @@ mod tests { assert_eq!(result.len(), 2); assert_eq!(result[0].len(), 1); assert_eq!(result[1].len(), 1); - assert_eq!(result.area(), -4i64); + assert_eq!(result.area(), 4i64); } #[test] @@ -756,7 +756,7 @@ mod tests { assert_eq!(result.len(), 1); assert_eq!(result[0].len(), 1); - assert_eq!(result.area(), -6i64); + assert_eq!(result.area(), 6i64); } #[test] @@ -781,7 +781,7 @@ mod tests { assert_eq!(result.len(), 1); assert_eq!(result[0].len(), 1); - assert_eq!(result.area(), -14i64); + assert_eq!(result.area(), 14i64); } #[test] @@ -806,7 +806,7 @@ mod tests { assert_eq!(result.len(), 1); assert_eq!(result[0].len(), 1); - assert_eq!(result.area(), -25i64); + assert_eq!(result.area(), 25i64); } #[test] @@ -831,7 +831,7 @@ mod tests { assert_eq!(result.len(), 1); assert_eq!(result[0].len(), 1); - assert_eq!(result.area(), -25i64); + assert_eq!(result.area(), 25i64); } #[test] @@ -848,7 +848,7 @@ mod tests { assert_eq!(result.len(), 1); assert_eq!(result[0].len(), 1); - assert_eq!(result.area(), -8i64); + assert_eq!(result.area(), 8i64); } #[test] diff --git a/iOverlay/src/mesh/outline/offset.rs b/iOverlay/src/mesh/outline/offset.rs index deaaf8b..7e731bb 100644 --- a/iOverlay/src/mesh/outline/offset.rs +++ b/iOverlay/src/mesh/outline/offset.rs @@ -494,7 +494,7 @@ where offset_overlay.clear(); segments.clear(); - let contour_fill_rule = if area < I::Wide::ZERO { + let contour_fill_rule = if area > I::Wide::ZERO { offset_overlay.options.output_direction = ContourDirection::CounterClockwise; segments.reserve(self.outer_builder.capacity(path.len())); self.outer_builder.build(path, &self.adapter, &mut segments); diff --git a/iOverlay/src/split/grid_layout.rs b/iOverlay/src/split/grid_layout.rs index 10930ea..cf4bb5f 100644 --- a/iOverlay/src/split/grid_layout.rs +++ b/iOverlay/src/split/grid_layout.rs @@ -1563,10 +1563,10 @@ mod tests { let a2 = Triangle::area_two(p2, segment.b, segment.a); let a3 = Triangle::area_two(p3, segment.b, segment.a); - assert!(a0 >= 0); - assert!(a1 >= 0); - assert!(a2 >= 0); - assert!(a3 >= 0); + assert!(a0 <= 0); + assert!(a1 <= 0); + assert!(a2 <= 0); + assert!(a3 <= 0); } #[inline] diff --git a/iOverlay/tests/direction_tests.rs b/iOverlay/tests/direction_tests.rs index 2a9658d..0c9cda4 100644 --- a/iOverlay/tests/direction_tests.rs +++ b/iOverlay/tests/direction_tests.rs @@ -35,10 +35,10 @@ mod tests { }; let r0 = &path.simplify(FillRule::NonZero, op0)[0][0]; - debug_assert!(r0.area_two() < 0i64); + assert!(r0.area_two() > 0i64); let r1 = &path.simplify(FillRule::NonZero, op1)[0][0]; - debug_assert!(r1.area_two() > 0i64); + assert!(r1.area_two() < 0i64); } #[test] @@ -75,12 +75,12 @@ mod tests { }; let r0 = &path.simplify(FillRule::NonZero, op0)[0]; - debug_assert!(r0[0].area_two() < 0i64); - debug_assert!(r0[1].area_two() > 0i64); + assert!(r0[0].area_two() > 0i64); + assert!(r0[1].area_two() < 0i64); let r1 = &path.simplify(FillRule::NonZero, op1)[0]; - debug_assert!(r1[0].area_two() > 0i64); - debug_assert!(r1[1].area_two() < 0i64); + assert!(r1[0].area_two() < 0i64); + assert!(r1[1].area_two() > 0i64); } #[test] @@ -102,7 +102,7 @@ mod tests { // test default behavior let r = Overlay::with_contours(&path, &[]).overlay(OverlayRule::Subject, FillRule::NonZero); - debug_assert!(r[0][0].area_two() < 0i64); - debug_assert!(r[0][1].area_two() > 0i64); + assert!(r[0][0].area_two() > 0i64); + assert!(r[0][1].area_two() < 0i64); } } From 606ca51c1c7c55641fb4500aa10797a763f2f77e Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Fri, 29 May 2026 18:15:28 +0300 Subject: [PATCH 30/36] remove unused traits --- iOverlay/src/core/edge_data.rs | 6 +++--- iOverlay/tests/crash_tests.rs | 8 ++------ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/iOverlay/src/core/edge_data.rs b/iOverlay/src/core/edge_data.rs index 161c1a9..90da444 100644 --- a/iOverlay/src/core/edge_data.rs +++ b/iOverlay/src/core/edge_data.rs @@ -16,14 +16,14 @@ pub trait OverlayEdgeData: Copy + PartialEq + Send + Sync fn merge(ctx: EdgeDataMerge) -> Self; } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone)] pub struct EdgeDataSplit { pub a: IntPoint, pub p: IntPoint, pub b: IntPoint, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone)] pub struct EdgeDataMerge { pub lhs_data: D, pub lhs_count: C, @@ -34,7 +34,7 @@ pub struct EdgeDataMerge { impl OverlayEdgeData for () where - C: Copy + Send + Sync, + C: Send + Sync, { #[inline(always)] fn merge(_: EdgeDataMerge) -> Self {} diff --git a/iOverlay/tests/crash_tests.rs b/iOverlay/tests/crash_tests.rs index 2f51d54..ea1a39c 100644 --- a/iOverlay/tests/crash_tests.rs +++ b/iOverlay/tests/crash_tests.rs @@ -101,9 +101,7 @@ mod tests { #[test] fn test_03() { - let subj: Path<_> = int_path![ - [3, 4], [5, 0], [3, 3], [4, 2], [5, -2] - ]; + let subj: Path<_> = int_path![[3, 4], [5, 0], [3, 3], [4, 2], [5, -2]]; let solver = Solver { strategy: Strategy::Tree, @@ -124,9 +122,7 @@ mod tests { #[test] fn test_04() { - let subj: Path> = int_path![ - [-4, -2], [1, -3], [-1, 3], [1, -4], [4, -3] - ]; + let subj: Path> = int_path![[-4, -2], [1, -3], [-1, 3], [1, -4], [4, -3]]; let solver = Solver { strategy: Strategy::Tree, From 045f71dd2424fb515fcc3801a358a97753877faa Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Fri, 29 May 2026 20:52:14 +0300 Subject: [PATCH 31/36] clean api --- iOverlay/src/bind/segment.rs | 2 +- iOverlay/src/core/extract.rs | 2 +- iOverlay/src/core/graph.rs | 1 - iOverlay/src/core/link.rs | 2 +- iOverlay/src/geom/id_point.rs | 2 +- iOverlay/src/geom/line_range.rs | 2 +- iOverlay/src/mesh/outline/section.rs | 2 +- iOverlay/src/mesh/stroke/builder_cap.rs | 1 - iOverlay/src/mesh/stroke/section.rs | 2 +- iOverlay/src/split/fragment.rs | 2 +- iOverlay/src/split/grid_layout.rs | 1 - iOverlay/src/split/solver.rs | 1 - iOverlay/src/split/solver_tree.rs | 2 +- iOverlay/src/string/split.rs | 4 ++-- iOverlay/src/vector/simplify.rs | 2 +- 15 files changed, 12 insertions(+), 16 deletions(-) diff --git a/iOverlay/src/bind/segment.rs b/iOverlay/src/bind/segment.rs index 289427d..1ee7ed8 100644 --- a/iOverlay/src/bind/segment.rs +++ b/iOverlay/src/bind/segment.rs @@ -36,7 +36,7 @@ impl ContourIndex { } } -#[derive(Debug, Clone, Copy)] +#[derive(Clone, Copy)] pub(crate) struct IdSegment { pub(crate) contour_index: ContourIndex, pub(crate) v_segment: VSegment, diff --git a/iOverlay/src/core/extract.rs b/iOverlay/src/core/extract.rs index e23cd1b..85d0c33 100644 --- a/iOverlay/src/core/extract.rs +++ b/iOverlay/src/core/extract.rs @@ -23,7 +23,7 @@ use i_shape::util::reserve::Reserve; use i_tree::Expiration; #[repr(u8)] -#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] +#[derive(Copy, Clone, PartialEq, Default)] pub(crate) enum VisitState { #[default] Unvisited = 0, diff --git a/iOverlay/src/core/graph.rs b/iOverlay/src/core/graph.rs index 10087c9..0df6a07 100644 --- a/iOverlay/src/core/graph.rs +++ b/iOverlay/src/core/graph.rs @@ -20,7 +20,6 @@ pub struct OverlayGraph<'a, I: IntNumber, D = ()> { pub(crate) links: &'a [OverlayLink], } -#[derive(Debug)] pub(crate) enum OverlayNode { Bridge([usize; 2]), Cross(Vec), diff --git a/iOverlay/src/core/link.rs b/iOverlay/src/core/link.rs index 7c2120c..53fa93b 100644 --- a/iOverlay/src/core/link.rs +++ b/iOverlay/src/core/link.rs @@ -5,7 +5,7 @@ use crate::segm::segment::SegmentFill; use alloc::vec::Vec; use i_float::int::number::int::IntNumber; -#[derive(Debug, Clone, Copy)] +#[derive(Clone)] pub(crate) struct OverlayLink { pub(crate) a: IdPoint, pub(crate) b: IdPoint, diff --git a/iOverlay/src/geom/id_point.rs b/iOverlay/src/geom/id_point.rs index 127536b..d2f9988 100644 --- a/iOverlay/src/geom/id_point.rs +++ b/iOverlay/src/geom/id_point.rs @@ -1,7 +1,7 @@ use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; -#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, PartialEq, Clone, Copy)] pub(crate) struct IdPoint { pub(crate) id: usize, pub(crate) point: IntPoint, diff --git a/iOverlay/src/geom/line_range.rs b/iOverlay/src/geom/line_range.rs index 92f3a6e..f6bb758 100644 --- a/iOverlay/src/geom/line_range.rs +++ b/iOverlay/src/geom/line_range.rs @@ -1,6 +1,6 @@ use i_float::int::number::int::IntNumber; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq)] pub(crate) struct LineRange { pub(crate) min: I, pub(crate) max: I, diff --git a/iOverlay/src/mesh/outline/section.rs b/iOverlay/src/mesh/outline/section.rs index 2525748..9f95ad5 100644 --- a/iOverlay/src/mesh/outline/section.rs +++ b/iOverlay/src/mesh/outline/section.rs @@ -4,7 +4,7 @@ use i_float::float::compatible::FloatPointCompatible; use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; -#[derive(Debug, Clone)] +#[derive(Clone)] pub(super) struct OffsetSection { pub(super) a: IntPoint, pub(super) b: IntPoint, diff --git a/iOverlay/src/mesh/stroke/builder_cap.rs b/iOverlay/src/mesh/stroke/builder_cap.rs index 2e0ce90..d580a4f 100644 --- a/iOverlay/src/mesh/stroke/builder_cap.rs +++ b/iOverlay/src/mesh/stroke/builder_cap.rs @@ -13,7 +13,6 @@ use i_float::float::rect::FloatRect; use i_float::float::vector::FloatPointMath; use i_float::int::number::int::IntNumber; -#[derive(Debug, Clone)] pub(super) struct CapBuilder

{ points: Option>, } diff --git a/iOverlay/src/mesh/stroke/section.rs b/iOverlay/src/mesh/stroke/section.rs index 2f7a448..d647b0f 100644 --- a/iOverlay/src/mesh/stroke/section.rs +++ b/iOverlay/src/mesh/stroke/section.rs @@ -7,7 +7,7 @@ use i_float::float::compatible::FloatPointCompatible; use i_float::float::vector::FloatPointMath; use i_float::int::number::int::IntNumber; -#[derive(Debug, Clone)] +#[derive(Clone)] pub(super) struct Section { pub(super) a: P, pub(super) b: P, diff --git a/iOverlay/src/split/fragment.rs b/iOverlay/src/split/fragment.rs index c3626c1..5bab356 100644 --- a/iOverlay/src/split/fragment.rs +++ b/iOverlay/src/split/fragment.rs @@ -2,7 +2,7 @@ use crate::geom::x_segment::XSegment; use i_float::int::number::int::IntNumber; use i_float::int::rect::IntRect; -#[derive(Debug, Clone)] +#[derive(Clone)] pub(super) struct Fragment { pub(super) index: usize, pub(super) rect: IntRect, diff --git a/iOverlay/src/split/grid_layout.rs b/iOverlay/src/split/grid_layout.rs index cf4bb5f..f56f25f 100644 --- a/iOverlay/src/split/grid_layout.rs +++ b/iOverlay/src/split/grid_layout.rs @@ -8,7 +8,6 @@ use i_float::int::number::uint::UIntNumber; use i_float::int::number::wide_int::WideIntNumber; use i_float::int::rect::IntRect; -#[derive(Debug, Clone)] pub(super) struct BorderVSegment { pub(super) id: usize, pub(super) x: I, diff --git a/iOverlay/src/split/solver.rs b/iOverlay/src/split/solver.rs index ffbbe10..a07bef7 100644 --- a/iOverlay/src/split/solver.rs +++ b/iOverlay/src/split/solver.rs @@ -12,7 +12,6 @@ use i_float::int::number::int::IntNumber; use i_key_sort::sort::key::SortKey; use i_tree::{Expiration, LayoutNumber}; -#[derive(Clone)] pub(crate) struct SplitSolver { pub(super) marks: Vec>, } diff --git a/iOverlay/src/split/solver_tree.rs b/iOverlay/src/split/solver_tree.rs index 8e60d6b..c0c1af5 100644 --- a/iOverlay/src/split/solver_tree.rs +++ b/iOverlay/src/split/solver_tree.rs @@ -14,7 +14,7 @@ use i_tree::seg::exp::{SegExpCollection, SegRange}; use i_tree::seg::tree::SegExpTree; use i_tree::{Expiration, LayoutNumber}; -#[derive(Debug, Clone, Copy)] +#[derive(Clone, Copy)] struct IdSegment { id: usize, x_segment: XSegment, diff --git a/iOverlay/src/string/split.rs b/iOverlay/src/string/split.rs index c032ef6..9484b16 100644 --- a/iOverlay/src/string/split.rs +++ b/iOverlay/src/string/split.rs @@ -64,13 +64,13 @@ impl Split for IntContour { } } -#[derive(Debug, Clone, Copy)] +#[derive(Clone)] struct PointItem { point: IntPoint, pos: usize, } -#[derive(Debug, Clone, Copy)] +#[derive(Clone)] struct Bin { offset: usize, data: usize, diff --git a/iOverlay/src/vector/simplify.rs b/iOverlay/src/vector/simplify.rs index e1e6d09..d19194b 100644 --- a/iOverlay/src/vector/simplify.rs +++ b/iOverlay/src/vector/simplify.rs @@ -269,7 +269,7 @@ mod tests { use alloc::vec; use i_float::int_pnt; - #[derive(Clone, Copy, Debug, PartialEq, Eq)] + #[derive(Clone, Copy, PartialEq)] enum TestData { A, B, From e242b7c312a156a57dc90c1ba1d519bdd8c60b56 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Fri, 29 May 2026 21:59:34 +0300 Subject: [PATCH 32/36] update edge data api --- iOverlay/src/core/edge_data.rs | 12 +++-- iOverlay/src/core/edge_overlay.rs | 31 +++++++++-- iOverlay/src/segm/build.rs | 11 +++- iOverlay/src/segm/merge.rs | 45 ++++++++++++---- iOverlay/src/segm/segment.rs | 18 +++---- iOverlay/src/split/solver.rs | 76 ++++++++++++++++++--------- iOverlay/src/split/solver_fragment.rs | 5 +- iOverlay/src/split/solver_list.rs | 5 +- iOverlay/src/split/solver_tree.rs | 5 +- iOverlay/src/vector/edge.rs | 13 ++++- iOverlay/src/vector/extract.rs | 47 ++++++++++++++--- iOverlay/src/vector/simplify.rs | 4 +- iOverlay/tests/edge_overlay_tests.rs | 4 +- 13 files changed, 202 insertions(+), 74 deletions(-) diff --git a/iOverlay/src/core/edge_data.rs b/iOverlay/src/core/edge_data.rs index 90da444..e0f2f6a 100644 --- a/iOverlay/src/core/edge_data.rs +++ b/iOverlay/src/core/edge_data.rs @@ -3,17 +3,19 @@ use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; pub trait OverlayEdgeData: Copy + PartialEq + Send + Sync { + type Store: Default; + #[inline(always)] - fn reversed(self) -> Self { + fn reversed(self, _store: &mut Self::Store) -> Self { self } #[inline(always)] - fn split(self, _ctx: EdgeDataSplit) -> (Self, Self) { + fn split(self, _ctx: EdgeDataSplit, _store: &mut Self::Store) -> (Self, Self) { (self, self) } - fn merge(ctx: EdgeDataMerge) -> Self; + fn merge(ctx: EdgeDataMerge, store: &mut Self::Store) -> Self; } #[derive(Debug, Clone)] @@ -36,6 +38,8 @@ impl OverlayEdgeData for () where C: Send + Sync, { + type Store = (); + #[inline(always)] - fn merge(_: EdgeDataMerge) -> Self {} + fn merge(_: EdgeDataMerge, _: &mut Self::Store) -> Self {} } diff --git a/iOverlay/src/core/edge_overlay.rs b/iOverlay/src/core/edge_overlay.rs index c06b918..654b797 100644 --- a/iOverlay/src/core/edge_overlay.rs +++ b/iOverlay/src/core/edge_overlay.rs @@ -28,6 +28,7 @@ pub struct EdgeOverlay { pub solver: Solver, pub options: IntOverlayOptions, pub boolean_buffer: Option>, + data_store: D::Store, segments: Vec>, split_solver: SplitSolver, graph_builder: GraphBuilder, @@ -43,6 +44,7 @@ where solver: Default::default(), options: IntOverlayOptions::keep_output_points(), boolean_buffer: None, + data_store: Default::default(), segments: Vec::with_capacity(capacity), split_solver: SplitSolver::new(), graph_builder: GraphBuilder::::new(), @@ -56,7 +58,12 @@ where let (direct, invert) = ShapeCountBoolean::with_shape_type(shape_type); self.segments.push(Segment::with_ab_and_data( - edge.a, edge.b, direct, invert, edge.data, + edge.a, + edge.b, + direct, + invert, + edge.data, + &mut self.data_store, )); } @@ -69,12 +76,25 @@ where } } + pub fn data_store(&self) -> &D::Store { + &self.data_store + } + + pub fn data_store_mut(&mut self) -> &mut D::Store { + &mut self.data_store + } + + pub fn into_data_store(self) -> D::Store { + self.data_store + } + pub fn build_vectors( &mut self, overlay_rule: OverlayRule, fill_rule: FillRule, ) -> Vec> { - self.split_solver.split_segments(&mut self.segments, &self.solver); + self.split_solver + .split_segments_with_store(&mut self.segments, &self.solver, &mut self.data_store); if self.segments.is_empty() { return Vec::new(); } @@ -87,7 +107,7 @@ where &self.solver, &self.segments, ) - .extract_vectors() + .extract_vectors_with_store(&mut self.data_store) } pub fn build_vector_shapes( @@ -95,7 +115,8 @@ where overlay_rule: OverlayRule, fill_rule: FillRule, ) -> Vec> { - self.split_solver.split_segments(&mut self.segments, &self.solver); + self.split_solver + .split_segments_with_store(&mut self.segments, &self.solver, &mut self.data_store); if self.segments.is_empty() { return Vec::new(); } @@ -111,7 +132,7 @@ where &self.solver, &self.segments, ) - .extract_vector_shapes(overlay_rule, &mut buffer); + .extract_vector_shapes_with_store(overlay_rule, &mut buffer, &mut self.data_store); self.boolean_buffer = Some(buffer); diff --git a/iOverlay/src/segm/build.rs b/iOverlay/src/segm/build.rs index c7aaf6b..dab8b9e 100644 --- a/iOverlay/src/segm/build.rs +++ b/iOverlay/src/segm/build.rs @@ -148,7 +148,14 @@ impl Segment { impl> Segment { #[inline] - pub(crate) fn with_ab_and_data(p0: IntPoint, p1: IntPoint, direct: C, invert: C, data: D) -> Self { + pub(crate) fn with_ab_and_data( + p0: IntPoint, + p1: IntPoint, + direct: C, + invert: C, + data: D, + store: &mut D::Store, + ) -> Self { if p0 < p1 { Self { x_segment: XSegment { a: p0, b: p1 }, @@ -159,7 +166,7 @@ impl> Segment { Self { x_segment: XSegment { a: p1, b: p0 }, count: invert, - data: data.reversed(), + data: data.reversed(store), } } } diff --git a/iOverlay/src/segm/merge.rs b/iOverlay/src/segm/merge.rs index 7061954..66a6d60 100644 --- a/iOverlay/src/segm/merge.rs +++ b/iOverlay/src/segm/merge.rs @@ -4,12 +4,26 @@ use crate::segm::winding::WindingCount; use alloc::vec::Vec; use i_float::int::number::int::IntNumber; -pub(crate) trait ShapeSegmentsMerge { +pub(crate) trait ShapeSegmentsMerge +where + C: WindingCount, + D: OverlayEdgeData, +{ + #[cfg(test)] fn merge_if_needed(&mut self) -> bool; + fn merge_if_needed_with_store(&mut self, store: &mut D::Store) -> bool; } -impl> ShapeSegmentsMerge for Vec> { +impl> ShapeSegmentsMerge + for Vec> +{ + #[cfg(test)] fn merge_if_needed(&mut self) -> bool { + let mut store = D::Store::default(); + self.merge_if_needed_with_store(&mut store) + } + + fn merge_if_needed_with_store(&mut self, store: &mut D::Store) -> bool { if self.len() < 2 { return false; } @@ -18,7 +32,7 @@ impl> ShapeSegmentsMerge fo for i in 1..self.len() { let this = &self[i].x_segment; if prev.eq(this) { - let new_len = merge(self, i); + let new_len = merge(self, i, store); self.truncate(new_len); return true; } @@ -32,6 +46,7 @@ impl> ShapeSegmentsMerge fo fn merge>( segments: &mut [Segment], after: usize, + store: &mut D::Store, ) -> usize { let mut i = after; let mut j = i - 1; @@ -42,13 +57,16 @@ fn merge>( let lhs_count = prev.count; let rhs_count = segments[i].count; let out_count = lhs_count.add(rhs_count); - prev.data = D::merge(EdgeDataMerge { - lhs_data: prev.data, - lhs_count, - rhs_data: segments[i].data, - rhs_count, - out_count, - }); + prev.data = D::merge( + EdgeDataMerge { + lhs_data: prev.data, + lhs_count, + rhs_data: segments[i].data, + rhs_count, + out_count, + }, + store, + ); prev.count = out_count; } else { if prev.count.is_not_empty() { @@ -75,6 +93,13 @@ mod tests { use alloc::vec; use i_float::int::point::IntPoint; + impl Segment { + fn create_and_validate(a: IntPoint, b: IntPoint, count: C) -> Self { + let mut store = (); + Self::create_and_validate_with_data(a, b, count, (), &mut store) + } + } + #[test] fn test_merge_if_needed_empty() { let mut segments: Vec> = Vec::new(); diff --git a/iOverlay/src/segm/segment.rs b/iOverlay/src/segm/segment.rs index 944f7ad..4c4efcf 100644 --- a/iOverlay/src/segm/segment.rs +++ b/iOverlay/src/segm/segment.rs @@ -28,17 +28,15 @@ pub(crate) struct Segment { pub(crate) data: D, } -impl Segment { - #[inline(always)] - #[allow(dead_code)] - pub(crate) fn create_and_validate(a: IntPoint, b: IntPoint, count: C) -> Self { - Self::create_and_validate_with_data(a, b, count, ()) - } -} - impl> Segment { #[inline(always)] - pub(crate) fn create_and_validate_with_data(a: IntPoint, b: IntPoint, count: C, data: D) -> Self { + pub(crate) fn create_and_validate_with_data( + a: IntPoint, + b: IntPoint, + count: C, + data: D, + store: &mut D::Store, + ) -> Self { if a < b { Self { x_segment: XSegment { a, b }, @@ -49,7 +47,7 @@ impl> Segment { Self { x_segment: XSegment { a: b, b: a }, count: count.invert(), - data: data.reversed(), + data: data.reversed(store), } } } diff --git a/iOverlay/src/split/solver.rs b/iOverlay/src/split/solver.rs index a07bef7..6da3267 100644 --- a/iOverlay/src/split/solver.rs +++ b/iOverlay/src/split/solver.rs @@ -32,18 +32,29 @@ where &mut self, segments: &mut Vec>, solver: &Solver, + ) -> bool { + let mut store = D::Store::default(); + self.split_segments_with_store(segments, solver, &mut store) + } + + #[inline] + pub(crate) fn split_segments_with_store>( + &mut self, + segments: &mut Vec>, + solver: &Solver, + store: &mut D::Store, ) -> bool { if segments.is_empty() { return false; } segments.sort_by_ab(solver.is_parallel_sort_allowed()); - let any_merged = segments.merge_if_needed(); + let any_merged = segments.merge_if_needed_with_store(store); if segments.is_empty() { return true; } - let any_intersection = self.split(segments, solver); + let any_intersection = self.split(segments, solver, store); any_merged | any_intersection } @@ -52,19 +63,20 @@ where &mut self, segments: &mut Vec>, solver: &Solver, + store: &mut D::Store, ) -> bool { let is_list = solver.is_list_split(segments); let snap_radius = solver.snap_radius(); if is_list { - return self.list_split(snap_radius, segments, solver); + return self.list_split(snap_radius, segments, solver, store); } let is_fragmentation = solver.is_fragmentation_required(segments); if is_fragmentation { - self.fragment_split(snap_radius, segments, solver) + self.fragment_split(snap_radius, segments, solver, store) } else { - self.tree_split(snap_radius, segments, solver) + self.tree_split(snap_radius, segments, solver, store) } } @@ -149,6 +161,7 @@ where segments: &mut Vec>, reusable_buffer: &mut Vec>, solver: &Solver, + store: &mut D::Store, ) { self.marks .sort_by_index_and_point(solver.is_parallel_sort_allowed(), reusable_buffer); @@ -181,13 +194,16 @@ where if start + 1 == i { // single split - let (d0, d1) = data.split(EdgeDataSplit { - a: x_seg.a, - p: m0.point, - b: x_seg.b, - }); - *s0 = Segment::create_and_validate_with_data(x_seg.a, m0.point, count, d0); - let s1 = Segment::create_and_validate_with_data(m0.point, x_seg.b, count, d1); + let (d0, d1) = data.split( + EdgeDataSplit { + a: x_seg.a, + p: m0.point, + b: x_seg.b, + }, + store, + ); + *s0 = Segment::create_and_validate_with_data(x_seg.a, m0.point, count, d0, store); + let s1 = Segment::create_and_validate_with_data(m0.point, x_seg.b, count, d1, store); segments.push(s1); continue; @@ -198,33 +214,41 @@ where Self::sort_sub_marks(sub_marks, x_seg); let m0 = sub_marks[0]; - let (d0, mut rest_data) = data.split(EdgeDataSplit { - a: x_seg.a, - p: m0.point, - b: x_seg.b, - }); - *s0 = Segment::create_and_validate_with_data(x_seg.a, m0.point, count, d0); + let (d0, mut rest_data) = data.split( + EdgeDataSplit { + a: x_seg.a, + p: m0.point, + b: x_seg.b, + }, + store, + ); + *s0 = Segment::create_and_validate_with_data(x_seg.a, m0.point, count, d0, store); let mut p0 = m0.point; for mi in sub_marks.iter().skip(1) { - let (di, next_data) = rest_data.split(EdgeDataSplit { - a: p0, - p: mi.point, - b: x_seg.b, - }); - segments.push(Segment::create_and_validate_with_data(p0, mi.point, count, di)); + let (di, next_data) = rest_data.split( + EdgeDataSplit { + a: p0, + p: mi.point, + b: x_seg.b, + }, + store, + ); + segments.push(Segment::create_and_validate_with_data( + p0, mi.point, count, di, store, + )); rest_data = next_data; p0 = mi.point; } segments.push(Segment::create_and_validate_with_data( - p0, x_seg.b, count, rest_data, + p0, x_seg.b, count, rest_data, store, )); } segments.sort_by_ab(solver.is_parallel_sort_allowed()); - segments.merge_if_needed(); + segments.merge_if_needed_with_store(store); } #[inline] diff --git a/iOverlay/src/split/solver_fragment.rs b/iOverlay/src/split/solver_fragment.rs index 093edda..6a228ac 100644 --- a/iOverlay/src/split/solver_fragment.rs +++ b/iOverlay/src/split/solver_fragment.rs @@ -22,12 +22,13 @@ where snap_radius: SnapRadius, segments: &mut Vec>, solver: &Solver, + store: &mut D::Store, ) -> bool { let layout = if let Some(layout) = GridLayout::new(segments.iter().map(|it| it.x_segment), segments.len()) { layout } else { - return self.tree_split(snap_radius, segments, solver); + return self.tree_split(snap_radius, segments, solver, store); }; let mut reusable_buffer = Vec::new(); @@ -72,7 +73,7 @@ where any_intersection = true; buffer.clear(); - self.apply(segments, &mut reusable_buffer, solver); + self.apply(segments, &mut reusable_buffer, solver, store); snap_radius.increment(); } diff --git a/iOverlay/src/split/solver_list.rs b/iOverlay/src/split/solver_list.rs index 65d20cc..794081c 100644 --- a/iOverlay/src/split/solver_list.rs +++ b/iOverlay/src/split/solver_list.rs @@ -18,6 +18,7 @@ where snap_radius: SnapRadius, segments: &mut Vec>, solver: &Solver, + store: &mut D::Store, ) -> bool { let mut need_to_fix = true; @@ -53,13 +54,13 @@ where return any_intersection; } any_intersection = true; - self.apply(segments, &mut reusable_buffer, solver); + self.apply(segments, &mut reusable_buffer, solver, store); snap_radius.increment(); if need_to_fix && !solver.is_list_split(segments) { // finish with tree solver if edges is become large - self.tree_split(snap_radius, segments, solver); + self.tree_split(snap_radius, segments, solver, store); return true; } } diff --git a/iOverlay/src/split/solver_tree.rs b/iOverlay/src/split/solver_tree.rs index c0c1af5..1f69555 100644 --- a/iOverlay/src/split/solver_tree.rs +++ b/iOverlay/src/split/solver_tree.rs @@ -36,6 +36,7 @@ where snap_radius: SnapRadius, segments: &mut Vec>, solver: &Solver, + store: &mut D::Store, ) -> bool { let range: SegRange = if let Some(range) = segments.ver_range() { range.into() @@ -45,7 +46,7 @@ where let mut tree: SegExpTree> = if let Some(tree) = SegExpTree::new(range) { tree } else { - return self.list_split(snap_radius, segments, solver); + return self.list_split(snap_radius, segments, solver, store); }; let mut reusable_buffer = Vec::new(); @@ -86,7 +87,7 @@ where any_intersection = true; tree.clear(); - self.apply(segments, &mut reusable_buffer, solver); + self.apply(segments, &mut reusable_buffer, solver, store); snap_radius.increment(); } diff --git a/iOverlay/src/vector/edge.rs b/iOverlay/src/vector/edge.rs index 4f5b817..09971cf 100644 --- a/iOverlay/src/vector/edge.rs +++ b/iOverlay/src/vector/edge.rs @@ -40,10 +40,21 @@ pub struct DataVectorEdge { impl DataVectorEdge { pub(crate) fn new(fill: SideFill, a: IntPoint, b: IntPoint, data: D) -> Self { + let mut store = D::Store::default(); + Self::new_with_store(fill, a, b, data, &mut store) + } + + pub(crate) fn new_with_store( + fill: SideFill, + a: IntPoint, + b: IntPoint, + data: D, + store: &mut D::Store, + ) -> Self { let (fill, data) = if a < b { (fill, data) } else { - (fill.reverse(), data.reversed()) + (fill.reverse(), data.reversed(store)) }; Self { a, b, fill, data } diff --git a/iOverlay/src/vector/extract.rs b/iOverlay/src/vector/extract.rs index aa6e44a..c6553ce 100644 --- a/iOverlay/src/vector/extract.rs +++ b/iOverlay/src/vector/extract.rs @@ -35,6 +35,16 @@ where &self, overlay_rule: OverlayRule, buffer: &mut BooleanExtractionBuffer, + ) -> Vec> { + let mut store = D::Store::default(); + self.extract_vector_shapes_with_store(overlay_rule, buffer, &mut store) + } + + pub fn extract_vector_shapes_with_store( + &self, + overlay_rule: OverlayRule, + buffer: &mut BooleanExtractionBuffer, + store: &mut D::Store, ) -> Vec> { let clockwise = self.options.output_direction == ContourDirection::Clockwise; self.links @@ -70,7 +80,7 @@ where let start_data = StartVectorPathData::new(direction, link, left_top_link); let mut contour = - self.find_vector_contour(start_data, direction, visited_state, &mut buffer.visited); + self.find_vector_contour(start_data, direction, visited_state, &mut buffer.visited, store); let (is_valid, is_modified) = contour.validate( self.options.min_output_area, self.options.preserve_output_collinear, @@ -125,6 +135,7 @@ where clockwise: bool, visited_state: VisitState, visited: &mut [VisitState], + store: &mut D::Store, ) -> DataVectorPath { let mut link_id = start_data.link_id; let mut node_id = start_data.node_id; @@ -133,11 +144,12 @@ where visited.visit_edge(link_id, visited_state); let mut contour = DataVectorPath::new(); - contour.push(DataVectorEdge::new( + contour.push(DataVectorEdge::new_with_store( start_data.fill, start_data.a, start_data.b, start_data.data, + store, )); // Find a closed tour @@ -149,7 +161,7 @@ where // traversal helpers, so this stays in-bounds. self.links.get_unchecked(link_id) }; - node_id = contour.push_node_and_get_other(link, node_id); + node_id = contour.push_node_and_get_other(link, node_id, store); visited.visit_edge(link_id, visited_state); } @@ -160,9 +172,16 @@ where impl OverlayGraph<'_, I, D> { pub fn extract_vectors(&self) -> Vec> { + let mut store = D::Store::default(); + self.extract_vectors_with_store(&mut store) + } + + pub fn extract_vectors_with_store(&self, store: &mut D::Store) -> Vec> { self.links .iter() - .map(|link| DataVectorEdge::new(link.fill, link.a.point, link.b.point, link.data)) + .map(|link| { + DataVectorEdge::new_with_store(link.fill, link.a.point, link.b.point, link.data, store) + }) .collect() } } @@ -313,7 +332,12 @@ fn is_sorted(segments: &[IdSegment]) -> bool { trait DataGraphContour { fn validate(&mut self, min_output_area: I::WideUInt, preserve_output_collinear: bool) -> (bool, bool); - fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize; + fn push_node_and_get_other( + &mut self, + link: &OverlayLink, + node_id: usize, + store: &mut D::Store, + ) -> usize; } impl DataGraphContour for DataVectorPath { @@ -341,21 +365,28 @@ impl DataGraphContour for DataVectorPath } #[inline] - fn push_node_and_get_other(&mut self, link: &OverlayLink, node_id: usize) -> usize { + fn push_node_and_get_other( + &mut self, + link: &OverlayLink, + node_id: usize, + store: &mut D::Store, + ) -> usize { if link.a.id == node_id { - self.push(DataVectorEdge::new( + self.push(DataVectorEdge::new_with_store( link.fill, link.a.point, link.b.point, link.data, + store, )); link.b.id } else { - self.push(DataVectorEdge::new( + self.push(DataVectorEdge::new_with_store( link.fill, link.b.point, link.a.point, link.data, + store, )); link.a.id } diff --git a/iOverlay/src/vector/simplify.rs b/iOverlay/src/vector/simplify.rs index d19194b..4e47ab8 100644 --- a/iOverlay/src/vector/simplify.rs +++ b/iOverlay/src/vector/simplify.rs @@ -279,7 +279,9 @@ mod tests { where C: Copy + Send + Sync, { - fn merge(ctx: EdgeDataMerge) -> Self { + type Store = (); + + fn merge(ctx: EdgeDataMerge, _: &mut Self::Store) -> Self { if ctx.lhs_data == ctx.rhs_data { ctx.lhs_data } else { diff --git a/iOverlay/tests/edge_overlay_tests.rs b/iOverlay/tests/edge_overlay_tests.rs index 83d9230..cb93743 100644 --- a/iOverlay/tests/edge_overlay_tests.rs +++ b/iOverlay/tests/edge_overlay_tests.rs @@ -25,7 +25,9 @@ mod tests { } impl OverlayEdgeData for EdgeColor { - fn merge(ctx: EdgeDataMerge) -> Self { + type Store = (); + + fn merge(ctx: EdgeDataMerge, _: &mut Self::Store) -> Self { let subj = if ctx.lhs_data.subj == ctx.rhs_data.subj { ctx.lhs_data.subj } else if ctx.rhs_count.subj == 0 || ctx.lhs_count.subj == 0 { From 7b33a776ae13043c4d5e32a8a1555f0f3a95bb64 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sun, 31 May 2026 22:03:44 +0300 Subject: [PATCH 33/36] fix init --- iOverlay/src/core/edge_overlay.rs | 25 +++++++++++++---------- iOverlay/src/mesh/outline/offset.rs | 2 +- iOverlay/src/mesh/stroke/offset.rs | 2 +- iOverlay/src/segm/boolean.rs | 16 +++++++++++++++ iOverlay/src/segm/build.rs | 27 ------------------------- iOverlay/src/segm/mod.rs | 4 ++-- iOverlay/src/segm/segment.rs | 31 +++++++++++++++++++++++++++++ iOverlay/src/segm/string.rs | 20 +++++++++++++++++++ iOverlay/src/segm/winding.rs | 2 ++ 9 files changed, 88 insertions(+), 41 deletions(-) diff --git a/iOverlay/src/core/edge_overlay.rs b/iOverlay/src/core/edge_overlay.rs index 654b797..0296863 100644 --- a/iOverlay/src/core/edge_overlay.rs +++ b/iOverlay/src/core/edge_overlay.rs @@ -8,7 +8,6 @@ use crate::core::overlay_rule::OverlayRule; use crate::core::solver::Solver; use crate::segm::boolean::ShapeCountBoolean; use crate::segm::segment::Segment; -use crate::segm::winding::WindingCount; use crate::split::solver::SplitSolver; use crate::vector::edge::{DataVectorEdge, DataVectorShape}; use alloc::vec::Vec; @@ -52,19 +51,15 @@ where } pub fn add_edge(&mut self, edge: InputEdge, shape_type: ShapeType) { - if edge.a == edge.b { - return; - } - - let (direct, invert) = ShapeCountBoolean::with_shape_type(shape_type); - self.segments.push(Segment::with_ab_and_data( + if let Some(segment) = Segment::try_ab_and_data( edge.a, edge.b, - direct, - invert, + shape_type, edge.data, &mut self.data_store, - )); + ) { + self.segments.push(segment); + } } pub fn add_edges(&mut self, edges: It, shape_type: ShapeType) @@ -139,3 +134,13 @@ where shapes } } + +impl EdgeOverlay +where + I: IntNumber + Expiration + LayoutNumber + SortKey, + D: OverlayEdgeData, +{ + pub fn edges(&self) -> impl Iterator; 2]> + '_ { + self.segments.iter().map(|e| [e.x_segment.a, e.x_segment.b]) + } +} diff --git a/iOverlay/src/mesh/outline/offset.rs b/iOverlay/src/mesh/outline/offset.rs index 7e731bb..37f3766 100644 --- a/iOverlay/src/mesh/outline/offset.rs +++ b/iOverlay/src/mesh/outline/offset.rs @@ -461,7 +461,7 @@ where fn apply_scale(&mut self, scale: f64) -> Result<(), FixedScaleOverlayError> { let s = P::Scalar::from_float(scale); - self.adapter = FloatPointAdapter::try_with_scale(self.adapter.rect().clone(), s)?; + self.adapter = FloatPointAdapter::try_with_scale(*self.adapter.rect(), s)?; Ok(()) } diff --git a/iOverlay/src/mesh/stroke/offset.rs b/iOverlay/src/mesh/stroke/offset.rs index 25359a1..150e525 100644 --- a/iOverlay/src/mesh/stroke/offset.rs +++ b/iOverlay/src/mesh/stroke/offset.rs @@ -497,7 +497,7 @@ where } fn apply_scale(&mut self, scale: P::Scalar) -> Result<(), FixedScaleOverlayError> { - self.adapter = FloatPointAdapter::try_with_scale(self.adapter.rect().clone(), scale)?; + self.adapter = FloatPointAdapter::try_with_scale(*self.adapter.rect(), scale)?; Ok(()) } diff --git a/iOverlay/src/segm/boolean.rs b/iOverlay/src/segm/boolean.rs index 57487d8..572594b 100644 --- a/iOverlay/src/segm/boolean.rs +++ b/iOverlay/src/segm/boolean.rs @@ -33,6 +33,22 @@ impl WindingCount for ShapeCountBoolean { } } + #[inline(always)] + fn direct_count(shape_type: ShapeType) -> Self { + match shape_type { + ShapeType::Subject => ShapeCountBoolean::SUBJ_DIRECT, + ShapeType::Clip => ShapeCountBoolean::CLIP_DIRECT, + } + } + + #[inline(always)] + fn invert_count(shape_type: ShapeType) -> Self { + match shape_type { + ShapeType::Subject => ShapeCountBoolean::SUBJ_INVERT, + ShapeType::Clip => ShapeCountBoolean::CLIP_INVERT, + } + } + #[inline(always)] fn add(self, count: Self) -> Self { let subj = self.subj + count.subj; diff --git a/iOverlay/src/segm/build.rs b/iOverlay/src/segm/build.rs index dab8b9e..5293fbe 100644 --- a/iOverlay/src/segm/build.rs +++ b/iOverlay/src/segm/build.rs @@ -1,4 +1,3 @@ -use crate::core::edge_data::OverlayEdgeData; use crate::core::overlay::ShapeType; use crate::geom::x_segment::XSegment; use crate::segm::segment::Segment; @@ -146,32 +145,6 @@ impl Segment { } } -impl> Segment { - #[inline] - pub(crate) fn with_ab_and_data( - p0: IntPoint, - p1: IntPoint, - direct: C, - invert: C, - data: D, - store: &mut D::Store, - ) -> Self { - if p0 < p1 { - Self { - x_segment: XSegment { a: p0, b: p1 }, - count: direct, - data, - } - } else { - Self { - x_segment: XSegment { a: p1, b: p0 }, - count: invert, - data: data.reversed(store), - } - } - } -} - #[cfg(test)] mod tests { use crate::core::overlay::ShapeType; diff --git a/iOverlay/src/segm/mod.rs b/iOverlay/src/segm/mod.rs index 5f58287..a1204dc 100644 --- a/iOverlay/src/segm/mod.rs +++ b/iOverlay/src/segm/mod.rs @@ -1,7 +1,7 @@ pub mod boolean; pub(crate) mod build; pub(crate) mod merge; -pub(crate) mod segment; pub(crate) mod sort; +pub(crate) mod winding; pub mod string; -pub mod winding; +pub mod segment; \ No newline at end of file diff --git a/iOverlay/src/segm/segment.rs b/iOverlay/src/segm/segment.rs index 4c4efcf..07dffaf 100644 --- a/iOverlay/src/segm/segment.rs +++ b/iOverlay/src/segm/segment.rs @@ -4,6 +4,8 @@ use crate::segm::winding::WindingCount; use core::cmp::Ordering; use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; +use crate::core::overlay::ShapeType; +use crate::segm::boolean::ShapeCountBoolean; pub type SegmentFill = u8; @@ -53,6 +55,35 @@ impl> Segment { } } +impl> Segment { + #[inline] + pub(crate) fn try_ab_and_data( + a: IntPoint, + b: IntPoint, + shape_type: ShapeType, + data: D, + store: &mut D::Store, + ) -> Option { + match a.cmp(&b) { + Ordering::Equal => None, + Ordering::Less => Some( + Self { + x_segment: XSegment { a, b }, + count: ShapeCountBoolean::direct_count(shape_type), + data, + } + ), + Ordering::Greater => Some( + Self { + x_segment: XSegment { a: b, b: a }, + count: ShapeCountBoolean::invert_count(shape_type), + data: data.reversed(store), + } + ) + } + } +} + impl PartialEq for Segment { #[inline(always)] fn eq(&self, other: &Self) -> bool { diff --git a/iOverlay/src/segm/string.rs b/iOverlay/src/segm/string.rs index 4ce95d4..c42ac57 100644 --- a/iOverlay/src/segm/string.rs +++ b/iOverlay/src/segm/string.rs @@ -46,6 +46,26 @@ impl WindingCount for ShapeCountString { } } + fn direct_count(shape_type: ShapeType) -> Self { + match shape_type { + ShapeType::Subject => Self { subj: 1, clip: 0 }, + ShapeType::Clip => Self { + subj: 0, + clip: STRING_FORWARD_CLIP, + }, + } + } + + fn invert_count(shape_type: ShapeType) -> Self { + match shape_type { + ShapeType::Subject => Self { subj: -1, clip: 0 }, + ShapeType::Clip => Self { + subj: 0, + clip: STRING_BACK_CLIP, + }, + } + } + #[inline(always)] fn add(self, count: Self) -> Self { let subj = self.subj + count.subj; diff --git a/iOverlay/src/segm/winding.rs b/iOverlay/src/segm/winding.rs index 9f800a5..110a970 100644 --- a/iOverlay/src/segm/winding.rs +++ b/iOverlay/src/segm/winding.rs @@ -7,6 +7,8 @@ where fn is_not_empty(&self) -> bool; fn new(subj: i32, clip: i32) -> Self; fn with_shape_type(shape_type: ShapeType) -> (Self, Self); + fn direct_count(shape_type: ShapeType) -> Self; + fn invert_count(shape_type: ShapeType) -> Self; fn add(self, count: Self) -> Self; fn invert(self) -> Self; } From 44cfaf652f35b2bb0db7cac245cf44647df4e221 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sun, 31 May 2026 22:06:00 +0300 Subject: [PATCH 34/36] fmt --- iOverlay/src/core/edge_overlay.rs | 10 +++------- iOverlay/src/segm/mod.rs | 4 ++-- iOverlay/src/segm/segment.rs | 28 ++++++++++++---------------- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/iOverlay/src/core/edge_overlay.rs b/iOverlay/src/core/edge_overlay.rs index 0296863..342089b 100644 --- a/iOverlay/src/core/edge_overlay.rs +++ b/iOverlay/src/core/edge_overlay.rs @@ -51,13 +51,9 @@ where } pub fn add_edge(&mut self, edge: InputEdge, shape_type: ShapeType) { - if let Some(segment) = Segment::try_ab_and_data( - edge.a, - edge.b, - shape_type, - edge.data, - &mut self.data_store, - ) { + if let Some(segment) = + Segment::try_ab_and_data(edge.a, edge.b, shape_type, edge.data, &mut self.data_store) + { self.segments.push(segment); } } diff --git a/iOverlay/src/segm/mod.rs b/iOverlay/src/segm/mod.rs index a1204dc..98b4e74 100644 --- a/iOverlay/src/segm/mod.rs +++ b/iOverlay/src/segm/mod.rs @@ -1,7 +1,7 @@ pub mod boolean; pub(crate) mod build; pub(crate) mod merge; +pub mod segment; pub(crate) mod sort; -pub(crate) mod winding; pub mod string; -pub mod segment; \ No newline at end of file +pub(crate) mod winding; diff --git a/iOverlay/src/segm/segment.rs b/iOverlay/src/segm/segment.rs index 07dffaf..1c0f688 100644 --- a/iOverlay/src/segm/segment.rs +++ b/iOverlay/src/segm/segment.rs @@ -1,11 +1,11 @@ use crate::core::edge_data::OverlayEdgeData; +use crate::core::overlay::ShapeType; use crate::geom::x_segment::XSegment; +use crate::segm::boolean::ShapeCountBoolean; use crate::segm::winding::WindingCount; use core::cmp::Ordering; use i_float::int::number::int::IntNumber; use i_float::int::point::IntPoint; -use crate::core::overlay::ShapeType; -use crate::segm::boolean::ShapeCountBoolean; pub type SegmentFill = u8; @@ -66,20 +66,16 @@ impl> Segment Option { match a.cmp(&b) { Ordering::Equal => None, - Ordering::Less => Some( - Self { - x_segment: XSegment { a, b }, - count: ShapeCountBoolean::direct_count(shape_type), - data, - } - ), - Ordering::Greater => Some( - Self { - x_segment: XSegment { a: b, b: a }, - count: ShapeCountBoolean::invert_count(shape_type), - data: data.reversed(store), - } - ) + Ordering::Less => Some(Self { + x_segment: XSegment { a, b }, + count: ShapeCountBoolean::direct_count(shape_type), + data, + }), + Ordering::Greater => Some(Self { + x_segment: XSegment { a: b, b: a }, + count: ShapeCountBoolean::invert_count(shape_type), + data: data.reversed(store), + }), } } } From b88950d0752f8cfd8075b43c9b76a69ac8657003 Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Sun, 31 May 2026 22:26:58 +0300 Subject: [PATCH 35/36] up ver --- iOverlay/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOverlay/Cargo.toml b/iOverlay/Cargo.toml index be74749..61cba1b 100644 --- a/iOverlay/Cargo.toml +++ b/iOverlay/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "i_overlay" -version = "6.0.1" +version = "7.0.0" authors = ["Nail Sharipov "] edition = "2024" rust-version = "1.88" From 3d1997a9a45011e6cf4f6112b493d32ddb87ad3e Mon Sep 17 00:00:00 2001 From: Nail Sharipov Date: Mon, 1 Jun 2026 11:35:38 +0300 Subject: [PATCH 36/36] fix dep --- iOverlay/Cargo.toml | 18 +++++++++--------- iOverlay/README.md | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/iOverlay/Cargo.toml b/iOverlay/Cargo.toml index 61cba1b..dd848bb 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 = "^2.0.0" } -#i_shape = { version = "^2.0.0" } -i_tree = { version = "^0.18.0", path = "../../iTree" } +i_float = { version = "^3.0.0" } +i_shape = { version = "^3.0.0" } +i_tree = { version = "^0.19.0" } i_key_sort = { version = "^0.10.1" } -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 = "^2.0.0", features = ["serde"] } -#i_shape = { version = "^2.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"] } diff --git a/iOverlay/README.md b/iOverlay/README.md index 0e996b2..09503e3 100644 --- a/iOverlay/README.md +++ b/iOverlay/README.md @@ -77,7 +77,7 @@ iOverlay supports: Average relative time for iOverlay Rust solvers -For bigger data sets, the math engine and multithreading mode have a larger impact on runtime. Results below were measured on Apple M4, 24 GB. +For bigger data sets, the math engine and multithreading mode have a larger impact on runtime. See the detailed reports: [Performance Comparison](https://ishape-rust.github.io/iShape-js/overlay/performance/performance.html) and [Rust Solver Benchmarks](https://ishape-rust.github.io/iShape-js/overlay/performance/rust_i_overlay.html)