@@ -793,6 +793,73 @@ impl Mul<&BezPath> for TranslateScale {
793793 }
794794}
795795
796+ /// Close all open subpaths in a path, expressed as iterator transform.
797+ pub ( crate ) fn close_subpaths < I > ( elements : I ) -> CloseSubpaths < I :: IntoIter >
798+ where
799+ I : IntoIterator < Item = PathEl > ,
800+ {
801+ CloseSubpaths {
802+ elements : elements. into_iter ( ) ,
803+ state : CloseSubpathState :: Start ,
804+ }
805+ }
806+
807+ /// An iterator that closes all open subpaths.
808+ ///
809+ /// This struct is created by the [`close_subpaths`] function.
810+ #[ derive( Clone ) ]
811+ pub ( crate ) struct CloseSubpaths < I : Iterator < Item = PathEl > > {
812+ elements : I ,
813+ state : CloseSubpathState ,
814+ }
815+
816+ #[ derive( Clone ) ]
817+ enum CloseSubpathState {
818+ Start ,
819+ InSubpath ,
820+ ClosedLast ,
821+ PendingMoveTo ( Point ) ,
822+ }
823+
824+ impl < I : Iterator < Item = PathEl > > Iterator for CloseSubpaths < I > {
825+ type Item = PathEl ;
826+
827+ fn next ( & mut self ) -> Option < PathEl > {
828+ match self . state {
829+ CloseSubpathState :: Start => {
830+ let el = self . elements . next ( ) ;
831+ if !matches ! ( el, Some ( PathEl :: ClosePath ) | None ) {
832+ self . state = CloseSubpathState :: InSubpath ;
833+ }
834+ el
835+ }
836+ CloseSubpathState :: InSubpath => {
837+ let el = self . elements . next ( ) ;
838+ match el {
839+ None => {
840+ self . state = CloseSubpathState :: ClosedLast ;
841+ Some ( PathEl :: ClosePath )
842+ }
843+ Some ( PathEl :: MoveTo ( point) ) => {
844+ self . state = CloseSubpathState :: PendingMoveTo ( point) ;
845+ Some ( PathEl :: ClosePath )
846+ }
847+ Some ( PathEl :: ClosePath ) => {
848+ self . state = CloseSubpathState :: Start ;
849+ el
850+ }
851+ _ => el,
852+ }
853+ }
854+ CloseSubpathState :: ClosedLast => None ,
855+ CloseSubpathState :: PendingMoveTo ( point) => {
856+ self . state = CloseSubpathState :: Start ;
857+ Some ( PathEl :: MoveTo ( point) )
858+ }
859+ }
860+ }
861+ }
862+
796863/// Transform an iterator over path elements into one over path
797864/// segments.
798865///
@@ -2183,4 +2250,62 @@ mod tests {
21832250 assert ! ( bez. contains( ( 100.0 , 299.9 ) . into( ) ) ) ;
21842251 assert ! ( bez. contains( ( 100.0 , 300.0 ) . into( ) ) ) ;
21852252 }
2253+
2254+ #[ test]
2255+ fn check_close_subpaths ( ) {
2256+ let mut bez = BezPath :: new ( ) ;
2257+ bez. move_to ( ( 10.0 , 10.0 ) ) ;
2258+ bez. line_to ( ( 100.0 , 20.0 ) ) ;
2259+ bez. line_to ( ( 60.0 , 100.0 ) ) ;
2260+ let elements = close_subpaths ( & bez) . collect :: < Vec < _ > > ( ) ;
2261+ bez. close_path ( ) ;
2262+ assert_eq ! ( & elements, bez. elements( ) ) ;
2263+
2264+ // Test implicit closepath in middle of path
2265+ let mut bez2 = BezPath :: new ( ) ;
2266+ bez2. move_to ( ( 10.0 , 10.0 ) ) ;
2267+ bez2. line_to ( ( 100.0 , 20.0 ) ) ;
2268+ bez2. line_to ( ( 60.0 , 100.0 ) ) ;
2269+ bez2. move_to ( ( 110.0 , 10.0 ) ) ;
2270+ bez2. line_to ( ( 200.0 , 20.0 ) ) ;
2271+ bez2. line_to ( ( 160.0 , 100.0 ) ) ;
2272+ let elements2 = close_subpaths ( & bez2) . collect :: < Vec < _ > > ( ) ;
2273+ let mut bez3 = BezPath :: new ( ) ;
2274+ bez3. move_to ( ( 10.0 , 10.0 ) ) ;
2275+ bez3. line_to ( ( 100.0 , 20.0 ) ) ;
2276+ bez3. line_to ( ( 60.0 , 100.0 ) ) ;
2277+ bez3. close_path ( ) ;
2278+ bez3. move_to ( ( 110.0 , 10.0 ) ) ;
2279+ bez3. line_to ( ( 200.0 , 20.0 ) ) ;
2280+ bez3. line_to ( ( 160.0 , 100.0 ) ) ;
2281+ bez3. close_path ( ) ;
2282+ assert_eq ! ( & elements2, bez3. elements( ) ) ;
2283+ }
2284+
2285+ #[ test]
2286+ fn close_subpaths_does_not_duplicate_existing_closepath ( ) {
2287+ let path = BezPath :: from_vec ( vec ! [
2288+ PathEl :: MoveTo ( ( 0.0 , 0.0 ) . into( ) ) ,
2289+ PathEl :: LineTo ( ( 10.0 , 0.0 ) . into( ) ) ,
2290+ PathEl :: LineTo ( ( 10.0 , 10.0 ) . into( ) ) ,
2291+ PathEl :: ClosePath ,
2292+ PathEl :: MoveTo ( ( 20.0 , 0.0 ) . into( ) ) ,
2293+ PathEl :: LineTo ( ( 30.0 , 0.0 ) . into( ) ) ,
2294+ ] ) ;
2295+
2296+ let closed = close_subpaths ( path. iter ( ) ) . collect :: < Vec < _ > > ( ) ;
2297+
2298+ assert_eq ! (
2299+ closed,
2300+ vec![
2301+ PathEl :: MoveTo ( ( 0.0 , 0.0 ) . into( ) ) ,
2302+ PathEl :: LineTo ( ( 10.0 , 0.0 ) . into( ) ) ,
2303+ PathEl :: LineTo ( ( 10.0 , 10.0 ) . into( ) ) ,
2304+ PathEl :: ClosePath ,
2305+ PathEl :: MoveTo ( ( 20.0 , 0.0 ) . into( ) ) ,
2306+ PathEl :: LineTo ( ( 30.0 , 0.0 ) . into( ) ) ,
2307+ PathEl :: ClosePath ,
2308+ ]
2309+ ) ;
2310+ }
21862311}
0 commit comments