5151//! This approach allows for efficient and accurate boolean operations, even on
5252//! complex paths with many intersections or self-intersections.
5353
54+ #![ allow( dead_code, unused_imports) ]
55+
5456new_key_type ! {
5557 pub struct MajorVertexKey ;
5658 pub struct MajorEdgeKey ;
@@ -74,6 +76,8 @@ use crate::path_segment::PathSegment;
7476use crate :: path_to_path_data;
7577
7678use glam:: { BVec2 , DVec2 , I64Vec2 } ;
79+ use kurbo:: BezPath ;
80+ use linesweeper:: topology:: { BinaryWindingNumber , Topology } ;
7781use roots:: { Roots , find_roots_cubic} ;
7882use rustc_hash:: FxHashMap as HashMap ;
7983use rustc_hash:: FxHashSet as HashSet ;
@@ -1694,96 +1698,58 @@ impl Display for BooleanError {
16941698/// - Issues arise in determining the nesting structure of the paths.
16951699#[ inline( never) ]
16961700pub fn path_boolean ( a : & Path , a_fill_rule : FillRule , b : & Path , b_fill_rule : FillRule , op : PathBooleanOperation ) -> Result < Vec < Path > , BooleanError > {
1697- let mut unsplit_edges: Vec < MajorGraphEdgeStage1 > = a. iter ( ) . map ( segment_to_edge ( 1 ) ) . chain ( b. iter ( ) . map ( segment_to_edge ( 2 ) ) ) . flatten ( ) . collect ( ) ;
1698-
1699- if unsplit_edges. is_empty ( ) {
1700- return Ok ( Vec :: new ( ) ) ;
1701- }
1702- split_at_self_intersections ( & mut unsplit_edges) ;
1703-
1704- let split_edges = split_at_intersections ( & unsplit_edges) ;
1705-
1706- #[ cfg( feature = "logging" ) ]
1707- for ( edge, _) in split_edges. iter ( ) {
1708- eprintln ! ( "{}" , path_to_path_data( & vec![ * edge] , 0.001 ) ) ;
1709- }
1710-
1711- let major_graph = find_vertices ( & split_edges) ;
1712-
1713- #[ cfg( feature = "logging" ) ]
1714- eprintln ! ( "Major graph:" ) ;
1715- #[ cfg( feature = "logging" ) ]
1716- eprintln ! ( "{}" , major_graph_to_dot( & major_graph) ) ;
1717-
1718- let mut minor_graph = compute_minor ( & major_graph) ;
1719-
1720- #[ cfg( feature = "logging" ) ]
1721- eprintln ! ( "Minor graph:" ) ;
1722- #[ cfg( feature = "logging" ) ]
1723- eprintln ! ( "{}" , minor_graph_to_dot( & minor_graph. edges) ) ;
1724-
1725- remove_dangling_edges ( & mut minor_graph) ;
1726- #[ cfg( feature = "logging" ) ]
1727- eprintln ! ( "After removing dangling edges:" ) ;
1728- #[ cfg( feature = "logging" ) ]
1729- eprintln ! ( "{}" , minor_graph_to_dot( & minor_graph. edges) ) ;
1730-
1731- #[ cfg( feature = "logging" ) ]
1732- for ( key, edge) in minor_graph. edges . iter ( ) {
1733- eprintln ! ( "{key:?}:\n {}" , path_to_path_data( & edge. segments. to_vec( ) , 0.001 ) ) ;
1734- }
1735- #[ cfg( feature = "logging" ) ]
1736- for vertex in minor_graph. vertices . values ( ) {
1737- eprintln ! ( "{vertex:?}" ) ;
1738- }
1739- sort_outgoing_edges_by_angle ( & mut minor_graph) ;
1740- #[ cfg( feature = "logging" ) ]
1741- for vertex in minor_graph. vertices . values ( ) {
1742- eprintln ! ( "{vertex:?}" ) ;
1743- }
1701+ let a = path_to_kurbo ( a) ;
1702+ let b = path_to_kurbo ( b) ;
17441703
1745- for ( edge_key , edge ) in & minor_graph . edges {
1746- assert ! ( minor_graph . vertices . contains_key ( edge . incident_vertices [ 0 ] ) , "Edge {edge_key:?} has invalid start vertex" ) ;
1747- assert ! ( minor_graph . vertices . contains_key ( edge . incident_vertices [ 1 ] ) , "Edge {edge_key:?} has invalid end vertex" ) ;
1748- assert ! ( edge . twin . is_some ( ) , "Edge {edge_key:?} should have a twin" ) ;
1749- let twin = & minor_graph . edges [ edge . twin . unwrap ( ) ] ;
1750- assert_eq ! ( twin . twin . unwrap ( ) , edge_key , "Twin relationship should be symmetrical for edge {edge_key:?}" ) ;
1751- }
1752-
1753- let mut dual_graph = compute_dual ( & minor_graph ) ? ;
1704+ let inside = | windings : BinaryWindingNumber | -> bool {
1705+ let in_a = match a_fill_rule {
1706+ FillRule :: NonZero => windings . shape_a != 0 ,
1707+ FillRule :: EvenOdd => windings . shape_a % 2 == 1 ,
1708+ } ;
1709+ let in_b = match b_fill_rule {
1710+ FillRule :: NonZero => windings . shape_b != 0 ,
1711+ FillRule :: EvenOdd => windings . shape_b % 2 == 1 ,
1712+ } ;
17541713
1755- let nesting_trees = compute_nesting_tree ( & mut dual_graph) ;
1714+ match op {
1715+ PathBooleanOperation :: Union => in_a || in_b,
1716+ PathBooleanOperation :: Difference => in_a && !in_b,
1717+ PathBooleanOperation :: Intersection => in_a && in_b,
1718+ PathBooleanOperation :: Exclusion => in_a != in_b,
1719+ PathBooleanOperation :: Division => unimplemented ! ( ) ,
1720+ PathBooleanOperation :: Fracture => unimplemented ! ( ) ,
1721+ }
1722+ } ;
1723+ let top = Topology :: from_paths_binary ( & a, & b, 1e-5 ) . map_err ( |_| BooleanError :: MultipleOuterFaces ) ?;
1724+ let contours = top. contours ( inside) ;
1725+ Ok ( contours. contours ( ) . map ( |c| path_from_kurbo ( & c. path ) ) . collect ( ) )
1726+ }
17561727
1757- #[ cfg( feature = "logging" ) ]
1758- for tree in & nesting_trees {
1759- eprintln ! ( "nesting_trees: {tree:?}" ) ;
1728+ fn seg_to_kurbo ( seg : & PathSegment ) -> kurbo:: PathSeg {
1729+ let p = |dv : & DVec2 | kurbo:: Point :: new ( dv. x , dv. y ) ;
1730+ match seg {
1731+ PathSegment :: Line ( a, b) => kurbo:: Line :: new ( p ( a) , p ( b) ) . into ( ) ,
1732+ PathSegment :: Quadratic ( a, b, c) => kurbo:: QuadBez :: new ( p ( a) , p ( b) , p ( c) ) . into ( ) ,
1733+ PathSegment :: Cubic ( a, b, c, d) => kurbo:: CubicBez :: new ( p ( a) , p ( b) , p ( c) , p ( d) ) . into ( ) ,
1734+ PathSegment :: Arc ( ..) => unimplemented ! ( ) ,
17601735 }
1736+ }
17611737
1762- let DualGraph { edges, vertices, .. } = & dual_graph;
1763-
1764- #[ cfg( feature = "logging" ) ]
1765- eprintln ! ( "Dual Graph:" ) ;
1766- #[ cfg( feature = "logging" ) ]
1767- eprintln ! ( "{}" , dual_graph_to_dot( & dual_graph. components, edges) ) ;
1768-
1769- let mut flags = new_hash_map ( vertices. len ( ) ) ;
1770- flag_faces ( & nesting_trees, a_fill_rule, b_fill_rule, edges, vertices, & mut flags) ;
1738+ fn path_to_kurbo ( p : & Path ) -> kurbo:: BezPath {
1739+ BezPath :: from_path_segments ( p. iter ( ) . map ( seg_to_kurbo) )
1740+ }
17711741
1772- #[ cfg( feature = "logging" ) ]
1773- for ( face, flag) in & flags {
1774- eprintln ! ( "{:?}: {:b}" , face. 0 , flag) ;
1742+ fn seg_from_kurbo ( seg : kurbo:: PathSeg ) -> PathSegment {
1743+ let d = |p : kurbo:: Point | DVec2 :: new ( p. x , p. y ) ;
1744+ match seg {
1745+ kurbo:: PathSeg :: Line ( l) => PathSegment :: Line ( d ( l. p0 ) , d ( l. p1 ) ) ,
1746+ kurbo:: PathSeg :: Quad ( q) => PathSegment :: Quadratic ( d ( q. p0 ) , d ( q. p1 ) , d ( q. p2 ) ) ,
1747+ kurbo:: PathSeg :: Cubic ( c) => PathSegment :: Cubic ( d ( c. p0 ) , d ( c. p1 ) , d ( c. p2 ) , d ( c. p3 ) ) ,
17751748 }
1749+ }
17761750
1777- let predicate = OPERATION_PREDICATES [ op as usize ] ;
1778-
1779- match op {
1780- PathBooleanOperation :: Division | PathBooleanOperation :: Fracture => Ok ( dump_faces ( & nesting_trees, predicate, edges, vertices, & flags) ) ,
1781- _ => {
1782- let mut selected_faces: Vec < DualVertexKey > = get_selected_faces ( & predicate, & flags) . collect ( ) ;
1783- selected_faces. sort_unstable ( ) ;
1784- Ok ( vec ! [ walk_faces( & selected_faces, edges, vertices) . collect( ) ] )
1785- }
1786- }
1751+ fn path_from_kurbo ( p : & BezPath ) -> Path {
1752+ p. segments ( ) . map ( seg_from_kurbo) . collect ( )
17871753}
17881754
17891755#[ cfg( test) ]
0 commit comments