Skip to content

Commit aba9f18

Browse files
committed
Improve intersection candidate finding
1 parent 0af8bc6 commit aba9f18

File tree

4 files changed

+363
-31
lines changed

4 files changed

+363
-31
lines changed

libraries/path-bool/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ mod parsing {
99
mod util {
1010
pub(crate) mod aabb;
1111
pub(crate) mod epsilons;
12+
pub(crate) mod grid;
1213
pub(crate) mod math;
1314
pub(crate) mod quad_tree;
15+
pub(crate) mod rtree;
1416
}
1517
mod path;
1618
#[cfg(test)]

libraries/path-bool/src/path_boolean.rs

+42-31
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ new_key_type! {
6565

6666
use crate::aabb::{bounding_box_around_point, bounding_box_max_extent, expand_bounding_box, extend_bounding_box, merge_bounding_boxes, Aabb};
6767
use crate::epsilons::Epsilons;
68+
use crate::grid::{BitVec, Grid};
6869
use crate::intersection_path_segment::{path_segment_intersection, segments_equal};
6970
use crate::path::Path;
7071
use crate::path_cubic_segment_self_intersection::path_cubic_segment_self_intersection;
@@ -431,29 +432,42 @@ fn split_at_self_intersections(edges: &mut Vec<MajorGraphEdgeStage1>) {
431432
/// A tuple containing:
432433
/// * A vector of split edges (MajorGraphEdgeStage2).
433434
/// * An optional overall bounding box (AaBb) for all edges.
434-
fn split_at_intersections(edges: &[MajorGraphEdgeStage1]) -> (Vec<MajorGraphEdgeStage1>, Option<Aabb>) {
435-
if edges.is_empty() {
436-
return (Vec::new(), None);
437-
}
438-
435+
fn split_at_intersections(edges: &[MajorGraphEdgeStage1]) -> Vec<MajorGraphEdgeStage1> {
439436
// Step 1: Add bounding boxes to edges
440437
let with_bounding_box: Vec<MajorGraphEdgeStage2> = edges.iter().map(|(seg, parent)| (*seg, *parent, seg.approx_bounding_box())).collect();
441438
// Step 2: Calculate total bounding box
442-
let total_bounding_box = with_bounding_box.iter().fold(Default::default(), |acc, (_, _, bb)| merge_bounding_boxes(&acc, &bb));
439+
let total_bounding_box = with_bounding_box.iter().fold(Default::default(), |acc, (_, _, bb)| merge_bounding_boxes(&acc, bb));
440+
441+
let max_extent = bounding_box_max_extent(&total_bounding_box);
442+
let cell_size = max_extent / (edges.len() as f64).sqrt();
443+
444+
// Step 3: Create grid for efficient intersection checks
445+
let mut grid = Grid::new(cell_size, edges.len());
443446

444447
// Step 3: Create edge tree for efficient intersection checks
445-
let mut edge_tree = QuadTree::new(total_bounding_box, INTERSECTION_TREE_DEPTH, 8);
448+
// let mut edge_tree = QuadTree::new(total_bounding_box, INTERSECTION_TREE_DEPTH, 16);
449+
// let mut rtree = crate::util::rtree::RTree::new(24);
446450

447-
let mut splits_per_edge: HashMap<usize, Vec<f64>> = HashMap::default();
451+
let mut splits_per_edge: Vec<Vec<f64>> = vec![Vec::new(); edges.len()];
448452

449-
fn add_split(splits_per_edge: &mut HashMap<usize, Vec<f64>>, i: usize, t: f64) {
450-
splits_per_edge.entry(i).or_default().push(t);
453+
fn add_split(splits_per_edge: &mut [Vec<f64>], i: usize, t: f64) {
454+
splits_per_edge[i].push(t);
451455
}
456+
// let mut candidates = Vec::with_capacity(8);
457+
let mut candidates = BitVec::new(edges.len());
452458

453459
// Step 4: Find intersections and record split points
454460
for (i, edge) in with_bounding_box.iter().enumerate() {
455-
let candidates = edge_tree.find(&edge.2);
456-
for &j in &candidates {
461+
// let candidates = edge_tree.find(&edge.2);
462+
// let mut quad_candidates: Vec<_> = quad_candidates.into_iter().collect();
463+
// quad_candidates.sort_unstable();
464+
// let mut candidates = rtree.query(&edge.2);
465+
// candidates.sort_unstable();
466+
// assert_eq!(candidates, quad_candidates);
467+
candidates.clear();
468+
grid.query(&edge.2, &mut candidates);
469+
470+
for j in candidates.iter_set_bits() {
457471
let candidate: &(PathSegment, u8) = &edges[j];
458472
let include_endpoints = edge.1 != candidate.1 || !(candidate.0.end().abs_diff_eq(edge.0.start(), EPS.point) || candidate.0.start().abs_diff_eq(edge.0.end(), EPS.point));
459473
let intersection = path_segment_intersection(&edge.0, &candidate.0, include_endpoints, &EPS);
@@ -462,14 +476,16 @@ fn split_at_intersections(edges: &[MajorGraphEdgeStage1]) -> (Vec<MajorGraphEdge
462476
add_split(&mut splits_per_edge, j, t1);
463477
}
464478
}
465-
edge_tree.insert(edge.2, i);
479+
grid.insert(&edge.2, i);
480+
// edge_tree.insert(edge.2, i);
481+
// rtree.insert(edge.2, i);
466482
}
467483

468484
// Step 5: Apply splits to create new edges
469485
let mut new_edges = Vec::new();
470486

471487
for (i, (seg, parent, _)) in with_bounding_box.into_iter().enumerate() {
472-
if let Some(splits) = splits_per_edge.get(&i) {
488+
if let Some(splits) = splits_per_edge.get(i) {
473489
let mut splits = splits.clone();
474490
splits.sort_by(|a, b| a.partial_cmp(b).unwrap());
475491
let mut tmp_seg = seg;
@@ -496,7 +512,7 @@ fn split_at_intersections(edges: &[MajorGraphEdgeStage1]) -> (Vec<MajorGraphEdge
496512
}
497513
}
498514

499-
(new_edges, Some(total_bounding_box))
515+
new_edges
500516
}
501517

502518
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -539,7 +555,7 @@ fn round_point(point: DVec2) -> I64Vec2 {
539555
}
540556

541557
// TODO: Using 32bit values here might lead to incorrect results when the values collide. Even though this is very unlikely we should think about this case
542-
fn find_vertices(edges: &[MajorGraphEdgeStage1], total_bounding_box: Aabb) -> MajorGraph {
558+
fn find_vertices(edges: &[MajorGraphEdgeStage1]) -> MajorGraph {
543559
let mut graph = MajorGraph {
544560
edges: SlotMap::with_capacity_and_key(edges.len() * 2),
545561
vertices: SlotMap::with_capacity_and_key(edges.len()),
@@ -1729,21 +1745,19 @@ impl Display for BooleanError {
17291745
pub fn path_boolean(a: &Path, a_fill_rule: FillRule, b: &Path, b_fill_rule: FillRule, op: PathBooleanOperation) -> Result<Vec<Path>, BooleanError> {
17301746
let mut unsplit_edges: Vec<MajorGraphEdgeStage1> = a.iter().map(segment_to_edge(1)).chain(b.iter().map(segment_to_edge(2))).flatten().collect();
17311747

1748+
if unsplit_edges.is_empty() {
1749+
return Ok(Vec::new());
1750+
}
17321751
split_at_self_intersections(&mut unsplit_edges);
17331752

1734-
let (split_edges, total_bounding_box) = split_at_intersections(&unsplit_edges);
1753+
let split_edges = split_at_intersections(&unsplit_edges);
17351754

17361755
#[cfg(feature = "logging")]
17371756
for (edge, _) in split_edges.iter() {
17381757
eprintln!("{}", path_to_path_data(&vec![*edge], 0.001));
17391758
}
17401759

1741-
let total_bounding_box = match total_bounding_box {
1742-
Some(bb) => bb,
1743-
None => return Ok(Vec::new()), // Input geometry is empty
1744-
};
1745-
1746-
let major_graph = find_vertices(&split_edges, total_bounding_box);
1760+
let major_graph = find_vertices(&split_edges);
17471761

17481762
#[cfg(feature = "logging")]
17491763
eprintln!("Major graph:");
@@ -1829,10 +1843,7 @@ mod tests {
18291843
#[test]
18301844
fn test_split_at_intersections() {
18311845
let unsplit_edges = unsplit_edges();
1832-
let (split_edges, total_bounding_box) = split_at_intersections(&unsplit_edges);
1833-
1834-
// Check that we have a valid bounding box
1835-
assert!(total_bounding_box.is_some());
1846+
let split_edges = split_at_intersections(&unsplit_edges);
18361847

18371848
// Check that we have more edges after splitting (due to intersections)
18381849
assert!(split_edges.len() >= unsplit_edges.len());
@@ -1859,8 +1870,8 @@ mod tests {
18591870
fn test_compute_minor() {
18601871
// Set up the initial graph
18611872
let unsplit_edges = unsplit_edges();
1862-
let (split_edges, total_bounding_box) = split_at_intersections(&unsplit_edges);
1863-
let major_graph = find_vertices(&split_edges, total_bounding_box.unwrap());
1873+
let split_edges = split_at_intersections(&unsplit_edges);
1874+
let major_graph = find_vertices(&split_edges);
18641875

18651876
// Compute minor graph
18661877
let minor_graph = compute_minor(&major_graph);
@@ -1914,8 +1925,8 @@ mod tests {
19141925
fn test_sort_outgoing_edges_by_angle() {
19151926
// Set up the initial graph
19161927
let unsplit_edges = unsplit_edges();
1917-
let (split_edges, total_bounding_box) = split_at_intersections(&unsplit_edges);
1918-
let major_graph = find_vertices(&split_edges, total_bounding_box.unwrap());
1928+
let split_edges = split_at_intersections(&unsplit_edges);
1929+
let major_graph = find_vertices(&split_edges);
19191930
let mut minor_graph = compute_minor(&major_graph);
19201931

19211932
// Print initial state

libraries/path-bool/src/util/grid.rs

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
use crate::aabb::Aabb;
2+
use glam::{DVec2, IVec2};
3+
use rustc_hash::FxHashMap;
4+
use smallvec::SmallVec;
5+
6+
pub(crate) struct Grid {
7+
cell_size: f64,
8+
cells: FxHashMap<IVec2, SmallVec<[usize; 6]>>,
9+
}
10+
11+
impl Grid {
12+
pub(crate) fn new(cell_size: f64, edges: usize) -> Self {
13+
Grid {
14+
cell_size,
15+
cells: FxHashMap::with_capacity_and_hasher(edges, Default::default()),
16+
}
17+
}
18+
19+
pub(crate) fn insert(&mut self, bbox: &Aabb, index: usize) {
20+
let min_cell = self.point_to_cell(bbox.min());
21+
let max_cell = self.point_to_cell(bbox.max());
22+
23+
for i in min_cell.x..=max_cell.x {
24+
for j in min_cell.y..=max_cell.y {
25+
self.cells.entry((i, j).into()).or_default().push(index);
26+
}
27+
}
28+
}
29+
30+
pub(crate) fn query(&self, bbox: &Aabb, result: &mut BitVec) {
31+
let min_cell = self.point_to_cell(bbox.min());
32+
let max_cell = self.point_to_cell(bbox.max());
33+
34+
for i in min_cell.x..=max_cell.x {
35+
for j in min_cell.y..=max_cell.y {
36+
if let Some(indices) = self.cells.get(&(i, j).into()) {
37+
for &index in indices {
38+
result.set(index);
39+
}
40+
}
41+
}
42+
}
43+
// result.sort_unstable();
44+
// result.dedup();
45+
}
46+
47+
fn point_to_cell(&self, point: DVec2) -> IVec2 {
48+
(point / self.cell_size).as_ivec2()
49+
}
50+
}
51+
52+
use std::mem::size_of;
53+
54+
pub struct BitVec {
55+
data: Vec<u64>,
56+
capacity: usize,
57+
}
58+
59+
impl BitVec {
60+
pub fn new(capacity: usize) -> Self {
61+
let num_words = (capacity + 63) / 64;
62+
BitVec { data: vec![0; num_words], capacity }
63+
}
64+
65+
pub fn set(&mut self, index: usize) {
66+
let word_index = index / 64;
67+
let bit_index = index % 64;
68+
self.data[word_index] |= 1u64 << bit_index;
69+
}
70+
71+
pub fn clear(&mut self) {
72+
self.data.fill(0);
73+
}
74+
75+
pub fn iter_set_bits(&self) -> BitVecIterator {
76+
BitVecIterator {
77+
bit_vec: self,
78+
current_word: self.data[0],
79+
word_index: 0,
80+
}
81+
}
82+
}
83+
84+
pub struct BitVecIterator<'a> {
85+
bit_vec: &'a BitVec,
86+
current_word: u64,
87+
word_index: usize,
88+
}
89+
90+
impl<'a> Iterator for BitVecIterator<'a> {
91+
type Item = usize;
92+
93+
fn next(&mut self) -> Option<Self::Item> {
94+
while self.word_index < self.bit_vec.data.len() {
95+
if self.current_word == 0 {
96+
self.word_index += 1;
97+
if self.word_index == self.bit_vec.data.len() {
98+
return None;
99+
}
100+
self.current_word = self.bit_vec.data[self.word_index];
101+
continue;
102+
}
103+
let tz = self.current_word.trailing_zeros() as usize;
104+
self.current_word ^= 1 << tz;
105+
106+
let result = self.word_index * 64 + tz;
107+
108+
return Some(result);
109+
}
110+
None
111+
}
112+
}
113+
114+
#[cfg(test)]
115+
mod tests {
116+
use super::*;
117+
118+
#[test]
119+
fn test_bitvec() {
120+
let mut bv = BitVec::new(200);
121+
bv.set(5);
122+
bv.set(64);
123+
bv.set(128);
124+
bv.set(199);
125+
126+
let set_bits: Vec<usize> = bv.iter_set_bits().collect();
127+
assert_eq!(set_bits, vec![5, 64, 128, 199]);
128+
}
129+
}

0 commit comments

Comments
 (0)