1+ use log:: warn;
12use lyon_geom:: { LineSegment , Point } ;
23
34use crate :: turtle:: elements:: { DrawCommand , FillPolygon , FillRule , Stroke } ;
@@ -161,6 +162,25 @@ pub(crate) fn into_fill_polygons(subpaths: Vec<Stroke>, fill_rule: FillRule) ->
161162 } )
162163 . collect ( ) ;
163164
165+ // TODO: EvenOdd fill for overlapping but non-nested subpaths isn't handled.
166+ // We need to XOR without flattening somehow.
167+ if fill_rule == FillRule :: EvenOdd {
168+ let bboxes: Vec < _ > = subpaths. iter ( ) . map ( |s| s. bounding_box ( ) ) . collect ( ) ;
169+ for i in 0 ..subpaths. len ( ) {
170+ for j in ( i + 1 ) ..subpaths. len ( ) {
171+ let boxes_overlap = bboxes[ i] . intersects ( & bboxes[ j] ) ;
172+ let neither_contains_the_other =
173+ !containers[ i] . contains ( & j) && !containers[ j] . contains ( & i) ;
174+ if boxes_overlap && neither_contains_the_other {
175+ warn ! (
176+ "`even-odd` fill rule with overlapping (non-nested) subpaths is not implemented! this won't be drawn correctly"
177+ ) ;
178+ break ;
179+ }
180+ }
181+ }
182+ }
183+
164184 // Classify each subpath as outer (contributes filled area) or hole (removes it).
165185 let is_outer: Vec < Option < bool > > = match fill_rule {
166186 FillRule :: EvenOdd => containers
@@ -186,7 +206,8 @@ pub(crate) fn into_fill_polygons(subpaths: Vec<Stroke>, fill_rule: FillRule) ->
186206 if winding_inside == 0 {
187207 Some ( false )
188208 } else {
189- // Ignore (why?)
209+ // Attempting to fill a region inside an already-filled region.
210+ // If `winding_inside` was zero, it would be a hole.
190211 None
191212 }
192213 }
@@ -363,4 +384,57 @@ mod tests {
363384 // they should be classified as two independent outer contours.
364385 assert_eq ! ( polygons. len( ) , 2 ) ;
365386 }
387+
388+ /// Pentagram (5-pointed star) drawn as a single self-intersecting M…Z subpath
389+ ///
390+ /// The path visits the five outer tips in "skip-one" order, crossing itself five times
391+ /// and enclosing an inner pentagon whose fill depends on the fill rule:
392+ ///
393+ /// - EvenOdd: hole in the center (incorrect currently)
394+ /// - NonZero: filled
395+ #[ test]
396+ #[ ignore = "self-intersecting single subpaths are not yet handled" ]
397+ fn test_self_intersecting_pentagram ( ) {
398+ let star = Stroke :: new (
399+ Point :: new ( 110.0 , 45.0 ) ,
400+ vec ! [
401+ DrawCommand :: LineTo {
402+ from: Point :: new( 110.0 , 45.0 ) ,
403+ to: Point :: new( 162.0 , 195.0 ) ,
404+ } ,
405+ DrawCommand :: LineTo {
406+ from: Point :: new( 162.0 , 195.0 ) ,
407+ to: Point :: new( 24.0 , 100.0 ) ,
408+ } ,
409+ DrawCommand :: LineTo {
410+ from: Point :: new( 24.0 , 100.0 ) ,
411+ to: Point :: new( 196.0 , 100.0 ) ,
412+ } ,
413+ DrawCommand :: LineTo {
414+ from: Point :: new( 196.0 , 100.0 ) ,
415+ to: Point :: new( 58.0 , 195.0 ) ,
416+ } ,
417+ DrawCommand :: LineTo {
418+ from: Point :: new( 58.0 , 195.0 ) ,
419+ to: Point :: new( 110.0 , 45.0 ) ,
420+ } ,
421+ ] ,
422+ ) ;
423+
424+ let evenodd = into_fill_polygons ( vec ! [ star. clone( ) ] , FillRule :: EvenOdd ) ;
425+ assert_eq ! ( evenodd. len( ) , 1 , "EvenOdd: expected one outer contour" ) ;
426+ assert_eq ! (
427+ evenodd[ 0 ] . holes. len( ) ,
428+ 1 ,
429+ "EvenOdd: expected one hole (the inner pentagon)"
430+ ) ;
431+
432+ let nonzero = into_fill_polygons ( vec ! [ star] , FillRule :: NonZero ) ;
433+ assert_eq ! ( nonzero. len( ) , 1 , "NonZero: expected one outer contour" ) ;
434+ assert_eq ! (
435+ nonzero[ 0 ] . holes. len( ) ,
436+ 0 ,
437+ "NonZero: expected no holes (inner pentagon is filled)"
438+ ) ;
439+ }
366440}
0 commit comments