@@ -238,33 +238,6 @@ impl fmt::Debug for Path {
238238    } 
239239} 
240240
241- impl  fmt:: Display  for  Path  { 
242-     fn  fmt ( & self ,  fmt :  & mut  fmt:: Formatter < ' _ > )  -> fmt:: Result  { 
243-         struct  FormatterWrite < ' a ,  ' b >  { 
244-             fmt :  & ' a  mut  fmt:: Formatter < ' b > , 
245-         } 
246- 
247-         impl  std:: io:: Write  for  FormatterWrite < ' _ ,  ' _ >  { 
248-             fn  write ( & mut  self ,  buf :  & [ u8 ] )  -> std:: io:: Result < usize >  { 
249-                 self . fmt 
250-                     . write_str ( 
251-                         std:: str:: from_utf8 ( buf) 
252-                             . expect ( "Path generated non utf8 svg representation" ) , 
253-                     ) 
254-                     . map_err ( |err| std:: io:: Error :: new ( std:: io:: ErrorKind :: Other ,  err) ) ?; 
255-                 Ok ( buf. len ( ) ) 
256-             } 
257- 
258-             fn  flush ( & mut  self )  -> std:: io:: Result < ( ) >  { 
259-                 Ok ( ( ) ) 
260-             } 
261-         } 
262- 
263-         self . write_svg_path ( FormatterWrite  {  fmt } ) 
264-             . map_err ( |_| std:: fmt:: Error ) 
265-     } 
266- } 
267- 
268241impl  Path  { 
269242    pub  fn  new ( segments :  Vec < Segment > ,  subpaths :  Vec < usize > ,  closed :  Vec < bool > )  -> Self  { 
270243        Self  { 
@@ -554,38 +527,7 @@ impl Path {
554527
555528    /// Save path in SVG path format. 
556529pub  fn  write_svg_path ( & self ,  mut  out :  impl  Write )  -> std:: io:: Result < ( ) >  { 
557-         for  subpath in  self . subpaths ( )  { 
558-             write ! ( out,  "M{:?} " ,  subpath. start( ) ) ?; 
559-             let  mut  segment_type:  Option < u8 >  = None ; 
560-             for  segment in  subpath. segments ( ) . iter ( )  { 
561-                 match  segment { 
562-                     Segment :: Line ( line)  => { 
563-                         if  segment_type. replace ( b'L' )  != Some ( b'L' )  { 
564-                             out. write_all ( b"L" ) ?; 
565-                         } 
566-                         write ! ( out,  "{:?} " ,  line. end( ) ) ?; 
567-                     } 
568-                     Segment :: Quad ( quad)  => { 
569-                         let  [ _,  p1,  p2]  = quad. points ( ) ; 
570-                         if  segment_type. replace ( b'Q' )  != Some ( b'Q' )  { 
571-                             out. write_all ( b"Q" ) ?; 
572-                         } 
573-                         write ! ( out,  "{:?} {:?} " ,  p1,  p2) ?; 
574-                     } 
575-                     Segment :: Cubic ( cubic)  => { 
576-                         let  [ _,  p1,  p2,  p3]  = cubic. points ( ) ; 
577-                         if  segment_type. replace ( b'C' )  != Some ( b'C' )  { 
578-                             out. write_all ( b"C" ) ?; 
579-                         } 
580-                         write ! ( out,  "{:?} {:?} {:?} " ,  p1,  p2,  p3) ?; 
581-                     } 
582-                 } 
583-             } 
584-             if  subpath. is_closed ( )  { 
585-                 out. write_all ( b"Z" ) ?; 
586-             } 
587-         } 
588-         Ok ( ( ) ) 
530+         write ! ( out,  "{}" ,  self ) 
589531    } 
590532
591533    /// Load path from SVG path representation 
@@ -597,12 +539,26 @@ impl Path {
597539        Ok ( builder. build ( ) ) 
598540    } 
599541
542+     pub  fn  display ( & self ,  relative :  bool ,  tr :  Transform )  -> PathSvgDisplay < ' _ >  { 
543+         PathSvgDisplay  { 
544+             path :  self , 
545+             relative, 
546+             tr, 
547+         } 
548+     } 
549+ 
600550    /// Returns struct that prints command per line on debug formatting. 
601551pub  fn  verbose_debug ( & self )  -> PathVerboseDebug < ' _ >  { 
602552        PathVerboseDebug  {  path :  self  } 
603553    } 
604554} 
605555
556+ impl  fmt:: Display  for  Path  { 
557+     fn  fmt ( & self ,  out :  & mut  fmt:: Formatter < ' _ > )  -> fmt:: Result  { 
558+         write ! ( out,  "{}" ,  self . display( false ,  Transform :: identity( ) ) ) 
559+     } 
560+ } 
561+ 
606562pub  struct  PathVerboseDebug < ' a >  { 
607563    path :  & ' a  Path , 
608564} 
@@ -620,6 +576,106 @@ impl fmt::Debug for PathVerboseDebug<'_> {
620576    } 
621577} 
622578
579+ pub  struct  PathSvgDisplay < ' a >  { 
580+     path :  & ' a  Path , 
581+     relative :  bool , 
582+     tr :  Transform , 
583+ } 
584+ 
585+ impl  PathSvgDisplay < ' _ >  { 
586+     // round floating point to only contain specified amount of digits 
587+     fn  round_significant ( x :  f64 ,  digits :  usize )  -> f64  { 
588+         if  x == 0.  || digits == 0  { 
589+             0. 
590+         }  else  { 
591+             let  shift = digits as  i32  - x. abs ( ) . log10 ( ) . ceil ( )  as  i32 ; 
592+             let  shift_factor = 10_f64 . powi ( shift) ; 
593+ 
594+             ( x *  shift_factor) . round ( )  / shift_factor
595+         } 
596+     } 
597+ 
598+     fn  fmt_point ( 
599+         & self , 
600+         out :  & mut  fmt:: Formatter < ' _ > , 
601+         point :  Point , 
602+         previous :  Option < Point > , 
603+         precision :  usize , 
604+         sep :  bool , 
605+     )  -> Result < Point ,  fmt:: Error >  { 
606+         let  point_tr = self . tr . apply ( point) ; 
607+         let  point = previous. map_or_else ( || point_tr,  |point_prev| point_tr - point_prev) ; 
608+ 
609+         let  x = Self :: round_significant ( point. x ( ) ,  precision) ; 
610+         let  y = Self :: round_significant ( point. y ( ) ,  precision) ; 
611+         let  eps = 10_f64 . powi ( -( precision as  i32 ) ) ; 
612+         if  sep && x >= 0.0  { 
613+             write ! ( out,  " " ) ?; 
614+         } 
615+         if  x. abs ( )  < eps { 
616+             write ! ( out,  "0" ) ?; 
617+         }  else  { 
618+             write ! ( out,  "{x}" ) ?; 
619+         } 
620+         if  y >= 0.0  { 
621+             write ! ( out,  "," ) ?; 
622+         } 
623+         if  y. abs ( )  < eps { 
624+             write ! ( out,  "0" ) ?; 
625+         }  else  { 
626+             write ! ( out,  "{y}" ) ?; 
627+         } 
628+         Ok ( point_tr) 
629+     } 
630+ } 
631+ 
632+ impl  fmt:: Display  for  PathSvgDisplay < ' _ >  { 
633+     fn  fmt ( & self ,  out :  & mut  fmt:: Formatter < ' _ > )  -> fmt:: Result  { 
634+         let  precision = out. precision ( ) . unwrap_or ( 4 ) ; 
635+         let  mut  previous:  Option < Point >  = None ; 
636+         for  subpath in  self . path . subpaths ( )  { 
637+             out. write_str ( if  self . relative  && previous. is_some ( )  { 
638+                 "m" 
639+             }  else  { 
640+                 "M" 
641+             } ) ?; 
642+             let  point_last = self . fmt_point ( out,  subpath. start ( ) ,  previous,  precision,  false ) ?; 
643+             if  self . relative  { 
644+                 previous = Some ( point_last) ; 
645+             } 
646+ 
647+             for  segment in  subpath. segments ( ) . iter ( )  { 
648+                 let  point_last = match  segment { 
649+                     Segment :: Line ( line)  => { 
650+                         out. write_str ( if  self . relative  {  "l"  }  else  {  "L"  } ) ?; 
651+                         self . fmt_point ( out,  line. end ( ) ,  previous,  precision,  false ) ?
652+                     } 
653+                     Segment :: Quad ( quad)  => { 
654+                         out. write_str ( if  self . relative  {  "q"  }  else  {  "Q"  } ) ?; 
655+                         let  [ _,  p1,  p2]  = quad. points ( ) ; 
656+                         self . fmt_point ( out,  p1,  previous,  precision,  false ) ?; 
657+                         self . fmt_point ( out,  p2,  previous,  precision,  true ) ?
658+                     } 
659+                     Segment :: Cubic ( cubic)  => { 
660+                         out. write_str ( if  self . relative  {  "c"  }  else  {  "C"  } ) ?; 
661+                         let  [ _,  p1,  p2,  p3]  = cubic. points ( ) ; 
662+                         self . fmt_point ( out,  p1,  previous,  precision,  false ) ?; 
663+                         self . fmt_point ( out,  p2,  previous,  precision,  true ) ?; 
664+                         self . fmt_point ( out,  p3,  previous,  precision,  true ) ?
665+                     } 
666+                 } ; 
667+                 if  self . relative  { 
668+                     previous = Some ( point_last) ; 
669+                 } 
670+             } 
671+             if  subpath. is_closed ( )  { 
672+                 write ! ( out,  "Z" ) ?; 
673+             } 
674+         } 
675+         Ok ( ( ) ) 
676+     } 
677+ } 
678+ 
623679pub  struct  PathIter < ' a >  { 
624680    path :  & ' a  Path , 
625681    index :  usize , 
@@ -1250,4 +1306,21 @@ mod tests {
12501306        assert_path_eq ( & path_reference,  & path) ; 
12511307        Ok ( ( ) ) 
12521308    } 
1309+ 
1310+     #[ test]  
1311+     fn  test_display ( )  -> Result < ( ) ,  SvgParserError >  { 
1312+         let  path:  Path  = SQUIRREL . parse ( ) ?; 
1313+ 
1314+         let  path_display = format ! ( "{}" ,  path) ; 
1315+         assert_path_eq ( & path,  & path_display. parse ( ) ?) ; 
1316+         let  path_reference = "M12,1C9.79,1 8,2.31 8,3.92C8,5.86 8.5,6.95 8,10C8,5.5 5.23,3.66 4,3.66C4.05,3.16 3.52,3 3.52,3C3.52,3 3.3,3.11 3.22,3.34C2.95,3.03 2.66,3.07 2.66,3.07L2.53,3.65C2.53,3.65 0.7,4.29 0.68,6.87C0.88,7.2 2.21,7.47 3.15,7.3C4.04,7.35 3.82,8.09 3.62,8.29C2.78,9.13 2,8 1,8C0,8 0,9 1,9C2,9 2,10 4,10C0.91,11.2 4,14 4,14L3,14C2,14 2,15 2,15L8,15C11,15 13,14 13,11.53C13,10.68 12.57,9.74 12,9C10.89,7.54 12.23,6.32 13,7C13.77,7.68 16,8 16,5C16,2.79 14.21,1 12,1ZM2.5,6C2.22,6 2,5.78 2,5.5C2,5.22 2.22,5 2.5,5C2.78,5 3,5.22 3,5.5C3,5.78 2.78,6 2.5,6Z" ; 
1317+         assert_eq ! ( path_reference,  path_display. as_str( ) ) ; 
1318+ 
1319+         let  path_relative = format ! ( "{}" ,  path. display( true ,  Transform :: identity( ) ) ) ; 
1320+         assert_path_eq ( & path,  & path_relative. parse ( ) ?) ; 
1321+         let  path_reference = "M12,1c-2.21,0-4,1.31-4,2.92c0,1.94 0.5,3.03 0,6.08c0-4.5-2.77-6.34-4-6.34c0.05-0.5-0.48-0.66-0.48-0.66c0,0-0.22,0.11-0.3,0.34c-0.27-0.31-0.56-0.27-0.56-0.27l-0.13,0.58c0,0-1.83,0.64-1.85,3.22c0.2,0.33 1.53,0.6 2.47,0.43c0.89,0.05 0.67,0.79 0.47,0.99c-0.84,0.84-1.62-0.29-2.62-0.29c-1,0-1,1 0,1c1,0 1,1 3,1c-3.09,1.2 0,4 0,4l-1,0c-1,0-1,1-1,1l6,0c3,0 5-1 5-3.47c0-0.85-0.43-1.79-1-2.53c-1.11-1.46 0.23-2.68 1-2c0.77,0.68 3,1 3-2c0-2.21-1.79-4-4-4Zm-9.5,5c-0.28,0-0.5-0.22-0.5-0.5c0-0.28 0.22-0.5 0.5-0.5c0.28,0 0.5,0.22 0.5,0.5c0,0.28-0.22,0.5-0.5,0.5Z" ; 
1322+         assert_eq ! ( path_reference,  path_relative. as_str( ) ) ; 
1323+ 
1324+         Ok ( ( ) ) 
1325+     } 
12531326} 
0 commit comments