@@ -78,33 +78,28 @@ struct WindingNumber {
7878}
7979
8080impl linesweeper:: topology:: WindingNumber for WindingNumber {
81- type Tag = usize ;
81+ type Tag = ( usize , usize ) ;
8282
83- fn single ( tag : usize , positive : bool ) -> Self {
84- let mut elems = SmallVec :: with_capacity ( tag) ;
85- let sign = if positive { 1 } else { -1 } ;
86- for _ in 0 ..tag {
87- elems. push ( 0 ) ;
88- }
89- elems. push ( sign) ;
83+ fn single ( ( tag, out_of) : ( usize , usize ) , positive : bool ) -> Self {
84+ let mut elems = SmallVec :: with_capacity ( out_of) ;
85+ elems. resize ( out_of, 0 ) ;
86+ elems[ tag] = if positive { 1 } else { -1 } ;
9087 Self { elems }
9188 }
9289}
9390
9491impl std:: ops:: AddAssign for WindingNumber {
9592 fn add_assign ( & mut self , rhs : Self ) {
96- if self . elems . len ( ) < rhs. elems . len ( ) {
97- self . elems . resize ( rhs . elems . len ( ) , 0 ) ;
93+ if rhs. elems . is_empty ( ) {
94+ return ;
9895 }
99-
100- for ( me, them) in self . elems . iter_mut ( ) . zip ( & rhs. elems ) {
101- * me += * them;
96+ if self . elems . is_empty ( ) {
97+ self . elems = rhs. elems ;
98+ } else {
99+ for ( me, them) in self . elems . iter_mut ( ) . zip ( & rhs. elems ) {
100+ * me += * them;
101+ }
102102 }
103-
104- // Removing trailing zeros normalizes the representation so that the derived
105- // PartialEq works. (Alternatively, we could write our own PartialEq.)
106- let trailing_zeros = self . elems . iter ( ) . rev ( ) . take_while ( |w| * * w == 0 ) . count ( ) ;
107- self . elems . truncate ( self . elems . len ( ) - trailing_zeros) ;
108103 }
109104}
110105
@@ -119,50 +114,63 @@ impl std::ops::Add for WindingNumber {
119114
120115impl WindingNumber {
121116 fn is_inside ( & self , op : BooleanOperation ) -> bool {
117+ let is_in = |w : & i16 | * w != 0 ;
118+ let is_out = |w : & i16 | * w == 0 ;
122119 match op {
123- BooleanOperation :: Union => self . elems . iter ( ) . any ( |w| * w != 0 ) ,
124- BooleanOperation :: SubtractFront => self . elems . first ( ) . is_some_and ( |w| * w != 0 ) && self . elems . iter ( ) . skip ( 1 ) . all ( |w| * w == 0 ) ,
125- BooleanOperation :: SubtractBack => self . elems . last ( ) . is_some_and ( |w| * w != 0 ) && self . elems . iter ( ) . rev ( ) . skip ( 1 ) . all ( |w| * w == 0 ) ,
126- BooleanOperation :: Intersect => self . elems . iter ( ) . all ( |w| * w != 0 ) ,
127- BooleanOperation :: Difference => self . elems . iter ( ) . any ( |w| * w != 0 ) && !self . elems . iter ( ) . all ( |w| * w != 0 ) ,
120+ BooleanOperation :: Union => self . elems . iter ( ) . any ( is_in ) ,
121+ BooleanOperation :: SubtractFront => self . elems . first ( ) . is_some_and ( is_in ) && self . elems . iter ( ) . skip ( 1 ) . all ( is_out ) ,
122+ BooleanOperation :: SubtractBack => self . elems . last ( ) . is_some_and ( is_in ) && self . elems . iter ( ) . rev ( ) . skip ( 1 ) . all ( is_out ) ,
123+ BooleanOperation :: Intersect => ! self . elems . is_empty ( ) && self . elems . iter ( ) . all ( is_in ) ,
124+ BooleanOperation :: Difference => self . elems . iter ( ) . any ( is_in ) && !self . elems . iter ( ) . all ( is_in ) ,
128125 }
129126 }
130127}
131128
132- fn boolean_operation_on_vector_table < ' a > ( vector : impl DoubleEndedIterator < Item = TableRowRef < ' a , Vector > > , boolean_operation : BooleanOperation ) -> Table < Vector > {
129+ fn boolean_operation_on_vector_table < ' a > ( vector : impl DoubleEndedIterator < Item = TableRowRef < ' a , Vector > > + Clone , boolean_operation : BooleanOperation ) -> Table < Vector > {
133130 const EPSILON : f64 = 1e-5 ;
134131 let mut table = Table :: new ( ) ;
135- let mut alpha_blending = None ;
136- let mut source_node_id = None ;
137132 let mut paths = Vec :: new ( ) ;
138- for v in vector {
139- if alpha_blending. is_none ( ) {
140- alpha_blending = Some ( * v. alpha_blending ) ;
141- source_node_id = Some ( * v. source_node_id ) ;
142- }
133+ let mut row = TableRow :: < Vector > :: default ( ) ;
134+
135+ // How should we style the result? The previous implementation copied it
136+ // from either the first or the last row, depending on the boolean op.
137+ // We copy that behaviour, although I'm not sure what motivated it.
138+ let copy_from = if matches ! ( boolean_operation, BooleanOperation :: SubtractFront ) {
139+ vector. clone ( ) . next ( )
140+ } else {
141+ vector. clone ( ) . next_back ( )
142+ } ;
143+ if let Some ( copy_from) = copy_from {
144+ row. alpha_blending = * copy_from. alpha_blending ;
145+ row. source_node_id = * copy_from. source_node_id ;
146+ row. element . style = copy_from. element . style . clone ( ) ;
147+ row. element . upstream_data = copy_from. element . upstream_data . clone ( ) ;
148+ }
143149
150+ for v in vector {
144151 paths. push ( to_bez_path ( v. element , * v. transform ) ) ;
145152 }
146153
147- log:: warn !( "boolean op {boolean_operation:?} on paths:" ) ;
154+ log:: debug !( "boolean op {boolean_operation:?} on paths:" ) ;
148155 for p in & paths {
149- log:: warn !( "{}" , p. to_svg( ) ) ;
156+ log:: debug !( "{}" , p. to_svg( ) ) ;
150157 }
151158
152- // unwrap: Topology::from_paths only errors on a non-closed path, and our paths are closed by construction.
153- let top = Topology :: < WindingNumber > :: from_paths ( paths. iter ( ) . enumerate ( ) . map ( |( idx, path) | ( path, idx) ) , EPSILON ) . unwrap ( ) ;
159+ // unwrap: Topology::from_paths only errors on a non-closed path, and our paths are closed because `push_subpath`
160+ // always makes closed subpaths.
161+ let top = Topology :: < WindingNumber > :: from_paths ( paths. iter ( ) . enumerate ( ) . map ( |( idx, path) | ( path, ( idx, paths. len ( ) ) ) ) , EPSILON ) . unwrap ( ) ;
154162 let contours = top. contours ( |w| w. is_inside ( boolean_operation) ) ;
155163
156- log:: warn !( "boolean op output paths:" ) ;
164+ log:: debug !( "boolean op output paths:" ) ;
157165 for c in contours. contours ( ) {
158- log:: warn !( "{}" , c. path. to_svg( ) ) ;
166+ log:: debug !( "{}" , c. path. to_svg( ) ) ;
159167 }
160- table . push ( TableRow {
161- element : from_bez_paths ( contours. contours ( ) . map ( |c| & c. path ) ) ,
162- transform : DAffine2 :: IDENTITY ,
163- alpha_blending : alpha_blending . unwrap_or_default ( ) ,
164- source_node_id : source_node_id . unwrap_or_default ( ) ,
165- } ) ;
168+
169+ for subpath in from_bez_paths ( contours. contours ( ) . map ( |c| & c. path ) ) {
170+ row . element . append_subpath ( subpath , false ) ;
171+ }
172+
173+ table . push ( row ) ;
166174 table
167175}
168176
@@ -275,7 +283,7 @@ fn push_subpath(path: &mut BezPath, subpath: &Subpath<PointId>, transform: DAffi
275283 let transform = Affine :: new ( transform. to_cols_array ( ) ) ;
276284 let mut first = true ;
277285
278- for seg in subpath. iter ( ) {
286+ for seg in subpath. iter_closed ( ) {
279287 if first {
280288 first = false ;
281289 path. move_to ( transform * seg. start ( ) ) ;
@@ -284,7 +292,7 @@ fn push_subpath(path: &mut BezPath, subpath: &Subpath<PointId>, transform: DAffi
284292 }
285293}
286294
287- fn from_bez_paths < ' a > ( paths : impl Iterator < Item = & ' a BezPath > ) -> Vector {
295+ fn from_bez_paths < ' a > ( paths : impl Iterator < Item = & ' a BezPath > ) -> Vec < Subpath < PointId > > {
288296 let mut all_subpaths = Vec :: new ( ) ;
289297
290298 for path in paths {
@@ -323,7 +331,7 @@ fn from_bez_paths<'a>(paths: impl Iterator<Item = &'a BezPath>) -> Vector {
323331 }
324332 }
325333
326- Vector :: from_subpaths ( all_subpaths, false )
334+ all_subpaths
327335}
328336
329337type Path = Vec < path_bool:: PathSegment > ;
0 commit comments