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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Run fmt check
working-directory: iOverlay
run: cargo fmt --all -- --check
- name: Run tests
working-directory: iOverlay
run: cargo test
Expand Down
2 changes: 2 additions & 0 deletions iOverlay/rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
edition = "2024"
max_width = 110
2 changes: 1 addition & 1 deletion iOverlay/src/bind/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pub(crate) mod segment;
pub(crate) mod solver;
pub(crate) mod segment;
4 changes: 2 additions & 2 deletions iOverlay/src/bind/segment.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use alloc::vec::Vec;
use crate::geom::v_segment::VSegment;
use crate::vector::edge::{VectorEdge, VectorPath};
use alloc::vec::Vec;
use i_float::int::point::IntPoint;
use i_shape::int::path::IntPath;

Expand Down Expand Up @@ -139,4 +139,4 @@ impl IdSegments for VectorPath {
inner(self.iter().rev().copied(), buffer, id_data, x_min, x_max);
}
}
}
}
45 changes: 7 additions & 38 deletions iOverlay/src/bind/solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@ impl ShapeBinder {
j += 1
}

let target_id =
scan_list.first_less(anchor.v_segment.a.x, ContourIndex::EMPTY, anchor.v_segment);
let target_id = scan_list.first_less(anchor.v_segment.a.x, ContourIndex::EMPTY, anchor.v_segment);
let parent_index = if target_id.is_hole() {
// index is a hole index
// at this moment this hole parent is known
Expand All @@ -108,12 +107,7 @@ impl ShapeBinder {

pub(crate) trait JoinHoles {
fn join_unsorted_holes(&mut self, holes: Vec<IntContour>, clockwise: bool);
fn join_sorted_holes(
&mut self,
holes: Vec<IntContour>,
anchors: Vec<IdSegment>,
clockwise: bool,
);
fn join_sorted_holes(&mut self, holes: Vec<IntContour>, anchors: Vec<IdSegment>, clockwise: bool);
fn scan_join(&mut self, holes: Vec<IntPath>, hole_segments: Vec<IdSegment>, clockwise: bool);
}

Expand Down Expand Up @@ -146,12 +140,7 @@ impl JoinHoles for Vec<IntShape> {
}

#[inline]
fn join_sorted_holes(
&mut self,
holes: Vec<IntContour>,
anchors: Vec<IdSegment>,
clockwise: bool,
) {
fn join_sorted_holes(&mut self, holes: Vec<IntContour>, anchors: Vec<IdSegment>, clockwise: bool) {
if self.is_empty() || holes.is_empty() {
return;
}
Expand All @@ -175,23 +164,11 @@ impl JoinHoles for Vec<IntShape> {
let capacity = self.iter().fold(0, |s, it| s + it[0].len()) / 2;
let mut segments = Vec::with_capacity(capacity);
for (i, shape) in self.iter().enumerate() {
shape[0].append_id_segments(
&mut segments,
ContourIndex::new_shape(i),
x_min,
x_max,
clockwise,
);
shape[0].append_id_segments(&mut segments, ContourIndex::new_shape(i), x_min, x_max, clockwise);
}

for (i, hole) in holes.iter().enumerate() {
hole.append_id_segments(
&mut segments,
ContourIndex::new_hole(i),
x_min,
x_max,
clockwise,
);
hole.append_id_segments(&mut segments, ContourIndex::new_hole(i), x_min, x_max, clockwise);
}

segments.sort_by_a_then_by_angle();
Expand Down Expand Up @@ -322,16 +299,8 @@ mod tests {
];

let holes = vec![
vec![
IntPoint::new(2, 3),
IntPoint::new(4, 4),
IntPoint::new(4, 3),
],
vec![
IntPoint::new(2, 3),
IntPoint::new(4, 2),
IntPoint::new(3, 1),
],
vec![IntPoint::new(2, 3), IntPoint::new(4, 4), IntPoint::new(4, 3)],
vec![IntPoint::new(2, 3), IntPoint::new(4, 2), IntPoint::new(3, 1)],
];

shapes.join_unsorted_holes(holes, false);
Expand Down
109 changes: 64 additions & 45 deletions iOverlay/src/build/boolean.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,44 @@
use alloc::vec::Vec;
use i_shape::util::reserve::Reserve;
use crate::segm::boolean::ShapeCountBoolean;
use crate::core::link::OverlayLinkFilter;
use crate::core::graph::OverlayNode;
use crate::core::fill_rule::FillRule;
use crate::core::solver::Solver;
use crate::build::builder::{FillStrategy, GraphBuilder, InclusionFilterStrategy};
use crate::core::extract::VisitState;
use crate::core::fill_rule::FillRule;
use crate::core::graph::OverlayGraph;
use crate::core::graph::OverlayNode;
use crate::core::link::OverlayLink;
use crate::core::link::OverlayLinkFilter;
use crate::core::overlay::IntOverlayOptions;
use crate::core::overlay_rule::OverlayRule;
use crate::segm::segment::{Segment, SegmentFill, ALL, BOTH_BOTTOM, BOTH_TOP, CLIP_BOTH, CLIP_BOTTOM, CLIP_TOP, SUBJ_BOTH, SUBJ_BOTTOM, SUBJ_TOP};
use crate::core::solver::Solver;
use crate::segm::boolean::ShapeCountBoolean;
use crate::segm::segment::{
ALL, BOTH_BOTTOM, BOTH_TOP, CLIP_BOTH, CLIP_BOTTOM, CLIP_TOP, SUBJ_BOTH, SUBJ_BOTTOM, SUBJ_TOP, Segment,
SegmentFill,
};
use crate::segm::winding::WindingCount;
use alloc::vec::Vec;
use i_shape::util::reserve::Reserve;

impl GraphBuilder<ShapeCountBoolean, OverlayNode> {
#[inline]
pub(crate) fn build_boolean_all(&mut self,
fill_rule: FillRule,
options: IntOverlayOptions,
solver: &Solver,
segments: &[Segment<ShapeCountBoolean>],
pub(crate) fn build_boolean_all(
&mut self,
fill_rule: FillRule,
options: IntOverlayOptions,
solver: &Solver,
segments: &[Segment<ShapeCountBoolean>],
) -> OverlayGraph<'_> {
self.build_boolean_fills(fill_rule, solver, segments);
self.build_links_all(segments);
self.boolean_graph(options, solver)
}

#[inline]
pub(crate) fn build_boolean_overlay(&mut self,
fill_rule: FillRule,
overlay_rule: OverlayRule,
options: IntOverlayOptions,
solver: &Solver,
segments: &[Segment<ShapeCountBoolean>],
pub(crate) fn build_boolean_overlay(
&mut self,
fill_rule: FillRule,
overlay_rule: OverlayRule,
options: IntOverlayOptions,
solver: &Solver,
segments: &[Segment<ShapeCountBoolean>],
) -> OverlayGraph<'_> {
self.build_boolean_fills(fill_rule, solver, segments);
match overlay_rule {
Expand All @@ -49,7 +54,12 @@ impl GraphBuilder<ShapeCountBoolean, OverlayNode> {
}

#[inline]
fn build_boolean_fills(&mut self, fill_rule: FillRule, solver: &Solver, segments: &[Segment<ShapeCountBoolean>]) {
fn build_boolean_fills(
&mut self,
fill_rule: FillRule,
solver: &Solver,
segments: &[Segment<ShapeCountBoolean>],
) {
match fill_rule {
FillRule::EvenOdd => self.build_fills_with_strategy::<EvenOddStrategy>(solver, segments),
FillRule::NonZero => self.build_fills_with_strategy::<NonZeroStrategy>(solver, segments),
Expand All @@ -64,7 +74,7 @@ impl GraphBuilder<ShapeCountBoolean, OverlayNode> {
OverlayGraph {
nodes: &self.nodes,
links: &self.links,
options
options,
}
}
}
Expand All @@ -76,10 +86,7 @@ struct NegativeStrategy;

impl FillStrategy<ShapeCountBoolean> for EvenOddStrategy {
#[inline(always)]
fn add_and_fill(
this: ShapeCountBoolean,
bot: ShapeCountBoolean,
) -> (ShapeCountBoolean, SegmentFill) {
fn add_and_fill(this: ShapeCountBoolean, bot: ShapeCountBoolean) -> (ShapeCountBoolean, SegmentFill) {
let top = bot.add(this);
let subj_top = 1 & top.subj as SegmentFill;
let subj_bot = 1 & bot.subj as SegmentFill;
Expand All @@ -94,10 +101,7 @@ impl FillStrategy<ShapeCountBoolean> for EvenOddStrategy {

impl FillStrategy<ShapeCountBoolean> for NonZeroStrategy {
#[inline(always)]
fn add_and_fill(
this: ShapeCountBoolean,
bot: ShapeCountBoolean,
) -> (ShapeCountBoolean, SegmentFill) {
fn add_and_fill(this: ShapeCountBoolean, bot: ShapeCountBoolean) -> (ShapeCountBoolean, SegmentFill) {
let top = bot.add(this);
let subj_top = (top.subj != 0) as SegmentFill;
let subj_bot = (bot.subj != 0) as SegmentFill;
Expand All @@ -112,10 +116,7 @@ impl FillStrategy<ShapeCountBoolean> for NonZeroStrategy {

impl FillStrategy<ShapeCountBoolean> for PositiveStrategy {
#[inline(always)]
fn add_and_fill(
this: ShapeCountBoolean,
bot: ShapeCountBoolean,
) -> (ShapeCountBoolean, SegmentFill) {
fn add_and_fill(this: ShapeCountBoolean, bot: ShapeCountBoolean) -> (ShapeCountBoolean, SegmentFill) {
let top = bot.add(this);
let subj_top = (top.subj > 0) as SegmentFill;
let subj_bot = (bot.subj > 0) as SegmentFill;
Expand All @@ -130,10 +131,7 @@ impl FillStrategy<ShapeCountBoolean> for PositiveStrategy {

impl FillStrategy<ShapeCountBoolean> for NegativeStrategy {
#[inline(always)]
fn add_and_fill(
this: ShapeCountBoolean,
bot: ShapeCountBoolean,
) -> (ShapeCountBoolean, SegmentFill) {
fn add_and_fill(this: ShapeCountBoolean, bot: ShapeCountBoolean) -> (ShapeCountBoolean, SegmentFill) {
let top = bot.add(this);
let subj_top = (top.subj < 0) as SegmentFill;
let subj_bot = (bot.subj < 0) as SegmentFill;
Expand Down Expand Up @@ -307,37 +305,58 @@ impl OverlayLinkFilter for [OverlayLink] {

#[inline]
fn filter_subject(links: &[OverlayLink]) -> Vec<VisitState> {
links.iter().map(|link| VisitState::new(!link.fill.is_subject())).collect()
links
.iter()
.map(|link| VisitState::new(!link.fill.is_subject()))
.collect()
}

#[inline]
fn filter_clip(links: &[OverlayLink]) -> Vec<VisitState> {
links.iter().map(|link| VisitState::new(!link.fill.is_clip())).collect()
links
.iter()
.map(|link| VisitState::new(!link.fill.is_clip()))
.collect()
}

#[inline]
fn filter_intersect(links: &[OverlayLink]) -> Vec<VisitState> {
links.iter().map(|link| VisitState::new(!link.fill.is_intersect())).collect()
links
.iter()
.map(|link| VisitState::new(!link.fill.is_intersect()))
.collect()
}

#[inline]
fn filter_union(links: &[OverlayLink]) -> Vec<VisitState> {
links.iter().map(|link| VisitState::new(!link.fill.is_union())).collect()
links
.iter()
.map(|link| VisitState::new(!link.fill.is_union()))
.collect()
}

#[inline]
fn filter_difference(links: &[OverlayLink]) -> Vec<VisitState> {
links.iter().map(|link| VisitState::new(!link.fill.is_difference())).collect()
links
.iter()
.map(|link| VisitState::new(!link.fill.is_difference()))
.collect()
}

#[inline]
fn filter_inverse_difference(links: &[OverlayLink]) -> Vec<VisitState> {
links.iter().map(|link| VisitState::new(!link.fill.is_inverse_difference())).collect()
links
.iter()
.map(|link| VisitState::new(!link.fill.is_inverse_difference()))
.collect()
}

#[inline]
fn filter_xor(links: &[OverlayLink]) -> Vec<VisitState> {
links.iter().map(|link| VisitState::new(!link.fill.is_xor())).collect()
links
.iter()
.map(|link| VisitState::new(!link.fill.is_xor()))
.collect()
}

#[inline]
Expand Down Expand Up @@ -401,4 +420,4 @@ fn filter_xor_into(links: &[OverlayLink], buffer: &mut Vec<VisitState>) {
for link in links.iter() {
buffer.push(VisitState::new(!link.fill.is_xor()));
}
}
}
19 changes: 13 additions & 6 deletions iOverlay/src/build/builder.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
use alloc::vec::Vec;
use crate::core::link::OverlayLink;
use crate::core::solver::Solver;
use crate::geom::end::End;
use crate::geom::id_point::IdPoint;
use crate::geom::v_segment::VSegment;
use crate::segm::segment::{NONE, Segment, SegmentFill};
use crate::segm::winding::WindingCount;
use crate::util::log::Int;
use alloc::vec::Vec;
use i_float::triangle::Triangle;
use i_shape::util::reserve::Reserve;
use i_tree::key::exp::KeyExpCollection;
use i_tree::key::list::KeyExpList;
use i_tree::key::tree::KeyExpTree;
use crate::core::link::OverlayLink;
use crate::geom::id_point::IdPoint;

pub(super) trait FillStrategy<C> {
fn add_and_fill(this: C, bot: C) -> (C, SegmentFill);
Expand All @@ -35,7 +35,6 @@ pub(crate) struct GraphBuilder<C, N> {
}

impl<C: WindingCount, N: GraphNode> GraphBuilder<C, N> {

#[inline]
pub(crate) fn new() -> Self {
Self {
Expand All @@ -49,7 +48,11 @@ impl<C: WindingCount, N: GraphNode> GraphBuilder<C, N> {
}

#[inline]
pub(super) fn build_fills_with_strategy<F: FillStrategy<C>>(&mut self, solver: &Solver, segments: &[Segment<C>]) {
pub(super) fn build_fills_with_strategy<F: FillStrategy<C>>(
&mut self,
solver: &Solver,
segments: &[Segment<C>],
) {
let count = segments.len();
if solver.is_list_fill(segments) {
let capacity = count.log2_sqrt().max(4) * 2;
Expand All @@ -65,7 +68,11 @@ impl<C: WindingCount, N: GraphNode> GraphBuilder<C, N> {
}

#[inline]
fn build_fills<F: FillStrategy<C>, S: KeyExpCollection<VSegment, i32, C>>(&mut self, scan_list: &mut S, segments: &[Segment<C>]) {
fn build_fills<F: FillStrategy<C>, S: KeyExpCollection<VSegment, i32, C>>(
&mut self,
scan_list: &mut S,
segments: &[Segment<C>],
) {
let mut node = Vec::with_capacity(4);

let n = segments.len();
Expand Down
Loading