1
1
//! This module provides type [`Path`] representing SVG path data, and associated types.
2
- use crate :: { PathEl as KurboPathEl , Point , Shape , Vec2 } ;
2
+ use crate :: { Affine , PathEl as KurboPathEl , Point , Shape , Vec2 } ;
3
3
use anyhow:: { anyhow, Result } ;
4
- use std:: { fmt, ops:: Deref , vec} ;
4
+ use std:: { fmt, io , iter , mem , ops:: Deref , slice , vec} ;
5
5
6
6
pub use self :: one_vec:: * ;
7
7
8
+ type OneIter < ' a , T > = iter:: Chain < iter:: Once < & ' a T > , slice:: Iter < ' a , T > > ;
9
+
8
10
/// An SVG path
9
11
///
10
12
/// A path *MUST* begin with a `MoveTo` element. For this and other invalid inputs, we return
@@ -250,7 +252,7 @@ impl Path {
250
252
}
251
253
252
254
/// Write out a text representation of the string.
253
- pub fn write ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
255
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
254
256
let mut iter = self . elements . iter ( ) ;
255
257
if let Some ( el) = iter. next ( ) {
256
258
el. write ( f) ?;
@@ -261,6 +263,18 @@ impl Path {
261
263
}
262
264
Ok ( ( ) )
263
265
}
266
+
267
+ /// Write out the text representation of this path to anything implementing `io::Write`.
268
+ ///
269
+ /// `Path` also implements [`Display`][std::fmt::Display], which can be used when you need an
270
+ /// in-memory string representation (so you can e.g. `path.to_string()`).
271
+ ///
272
+ /// Note that this call will produce a lot of write calls under the hood, so it is recommended
273
+ /// to use a buffer (e.g. [`BufWriter`][std::io::BufWriter]) if your writer's
274
+ /// [`write`][io::Write::write] calls are expensive.
275
+ pub fn write_to ( & self , mut w : impl io:: Write ) -> io:: Result < ( ) > {
276
+ write ! ( w, "{}" , self )
277
+ }
264
278
}
265
279
266
280
impl Deref for Path {
@@ -271,6 +285,12 @@ impl Deref for Path {
271
285
}
272
286
}
273
287
288
+ impl fmt:: Display for Path {
289
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
290
+ self . fmt ( f)
291
+ }
292
+ }
293
+
274
294
impl Shape for Path {
275
295
type PathElementsIter < ' iter > = PathElementsIter < ' iter > ;
276
296
@@ -281,6 +301,8 @@ impl Shape for Path {
281
301
path_start_point : Point :: ZERO ,
282
302
current_point : Point :: ZERO ,
283
303
current_bearing : 0. ,
304
+ state : IterState :: None ,
305
+ seen_moveto : false ,
284
306
}
285
307
}
286
308
@@ -353,11 +375,11 @@ impl PathEl {
353
375
}
354
376
PathEl :: SmoothCubicTo ( cubic_tos) => {
355
377
write ! ( f, "S" ) ?;
356
- cubic_tos. write_spaced ( CubicTo :: write_vals, f) ?;
378
+ cubic_tos. write_spaced ( SmoothCubicTo :: write_vals, f) ?;
357
379
}
358
380
PathEl :: SmoothCubicToRel ( cubic_tos) => {
359
381
write ! ( f, "s" ) ?;
360
- cubic_tos. write_spaced ( CubicTo :: write_vals, f) ?;
382
+ cubic_tos. write_spaced ( SmoothCubicTo :: write_vals, f) ?;
361
383
}
362
384
PathEl :: QuadTo ( quad_tos) => {
363
385
write ! ( f, "Q" ) ?;
@@ -375,8 +397,14 @@ impl PathEl {
375
397
write ! ( f, "t" ) ?;
376
398
quad_tos. write_spaced ( write_point, f) ?;
377
399
}
378
- PathEl :: EllipticArc ( _) => todo ! ( ) ,
379
- PathEl :: EllipticArcRel ( _) => todo ! ( ) ,
400
+ PathEl :: EllipticArc ( arcs) => {
401
+ write ! ( f, "A" ) ?;
402
+ arcs. write_spaced ( Arc :: write_vals, f) ?;
403
+ }
404
+ PathEl :: EllipticArcRel ( arcs) => {
405
+ write ! ( f, "a" ) ?;
406
+ arcs. write_spaced ( Arc :: write_vals, f) ?;
407
+ }
380
408
PathEl :: Bearing ( bearing) => {
381
409
write ! ( f, "B{bearing}" , ) ?;
382
410
}
@@ -418,6 +446,22 @@ impl QuadTo {
418
446
}
419
447
}
420
448
449
+ impl Arc {
450
+ fn write_vals ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
451
+ write ! (
452
+ f,
453
+ "{},{} {} {},{} {},{}" ,
454
+ self . radii. x,
455
+ self . radii. y,
456
+ self . x_rotation,
457
+ self . large_arc,
458
+ self . sweep,
459
+ self . to. x,
460
+ self . to. y
461
+ )
462
+ }
463
+ }
464
+
421
465
/// An iterator over the path elements of an SVG path.
422
466
///
423
467
/// This structure could be `Copy`, but we don't implement it to avoid hidden clones.
@@ -433,13 +477,156 @@ pub struct PathElementsIter<'iter> {
433
477
current_point : Point ,
434
478
/// The current bearing.
435
479
current_bearing : f64 ,
480
+ /// This flag tracks whether we have seen a moveto command yet. It affects the behavior of
481
+ /// `MoveToRel`.
482
+ seen_moveto : bool ,
483
+ /// Iterator state machine
484
+ state : IterState < ' iter > ,
485
+ }
486
+
487
+ #[ derive( Clone ) ]
488
+ enum IterState < ' iter > {
489
+ /// We aren't part-way through any data type.
490
+ None ,
491
+ /// We're in the middle of a lineto or moveto.
492
+ ///
493
+ /// These values are actually for drawing lines to. See the spec for details of why this is the
494
+ /// state for a `MoveTo` as well.
495
+ LineTo {
496
+ transform : Affine ,
497
+ rest : & ' iter [ Point ] ,
498
+ } ,
499
+ }
500
+
501
+ impl < ' iter > PathElementsIter < ' iter > {
502
+ /// Handle the next element
503
+ ///
504
+ /// Only call this if we finished handling the previous one (i.e. `state = IterState::None`).
505
+ fn next_el ( & mut self , el : & ' iter PathEl ) -> Option < KurboPathEl > {
506
+ match el {
507
+ PathEl :: MoveTo ( points) => {
508
+ self . seen_moveto = true ;
509
+ let ( first, rest) = points. split ( ) ;
510
+ self . current_point = * first;
511
+ self . path_start_point = * first;
512
+ self . state = IterState :: LineTo {
513
+ transform : Affine :: IDENTITY ,
514
+ rest,
515
+ } ;
516
+ Some ( KurboPathEl :: MoveTo ( * first) )
517
+ }
518
+ PathEl :: MoveToRel ( points) => {
519
+ let ( & first, rest) = points. split ( ) ;
520
+ if self . seen_moveto {
521
+ let first = self . transform ( ) * first;
522
+ self . current_point = first;
523
+ self . path_start_point = first;
524
+ self . state = IterState :: LineTo {
525
+ transform : self . transform ( ) ,
526
+ rest,
527
+ } ;
528
+ Some ( KurboPathEl :: MoveTo ( first) )
529
+ } else {
530
+ self . seen_moveto = true ;
531
+ self . current_point = first;
532
+ self . path_start_point = first;
533
+ // Even though we treat the first element as absolute, we still treat
534
+ // subsequent points as `LineToRel`s. This might make a difference if the
535
+ // user added a `Bearing` before the first `MoveTo`.
536
+ self . state = IterState :: LineTo {
537
+ transform : self . transform ( ) ,
538
+ rest,
539
+ } ;
540
+ Some ( KurboPathEl :: MoveTo ( first) )
541
+ }
542
+ }
543
+ PathEl :: ClosePath => {
544
+ self . current_point = self . path_start_point ;
545
+ Some ( KurboPathEl :: ClosePath )
546
+ }
547
+ PathEl :: LineTo ( points) => {
548
+ let ( & first, rest) = points. split ( ) ;
549
+ self . current_point = first;
550
+ self . state = IterState :: LineTo {
551
+ transform : Affine :: IDENTITY ,
552
+ rest,
553
+ } ;
554
+ Some ( KurboPathEl :: LineTo ( first) )
555
+ }
556
+ PathEl :: LineToRel ( points) => {
557
+ let ( & first, rest) = points. split ( ) ;
558
+ self . current_point = self . transform ( ) * first;
559
+ self . state = IterState :: LineTo {
560
+ // TODO we can't store the transform here as it changes for each element.
561
+ // Replace it with a flag here (and in the other Rel variants) and get it
562
+ // directly where it's needed.
563
+ transform : self . transform ( ) ,
564
+ rest,
565
+ } ;
566
+ Some ( KurboPathEl :: LineTo ( first) )
567
+ }
568
+ PathEl :: Horiz ( _) => todo ! ( ) ,
569
+ PathEl :: HorizRel ( _) => todo ! ( ) ,
570
+ PathEl :: Vert ( _) => todo ! ( ) ,
571
+ PathEl :: VertRel ( _) => todo ! ( ) ,
572
+ PathEl :: CubicTo ( _) => todo ! ( ) ,
573
+ PathEl :: CubicToRel ( _) => todo ! ( ) ,
574
+ PathEl :: SmoothCubicTo ( _) => todo ! ( ) ,
575
+ PathEl :: SmoothCubicToRel ( _) => todo ! ( ) ,
576
+ PathEl :: QuadTo ( _) => todo ! ( ) ,
577
+ PathEl :: QuadToRel ( _) => todo ! ( ) ,
578
+ PathEl :: SmoothQuadTo ( _) => todo ! ( ) ,
579
+ PathEl :: SmoothQuadToRel ( _) => todo ! ( ) ,
580
+ PathEl :: EllipticArc ( _) => todo ! ( ) ,
581
+ PathEl :: EllipticArcRel ( _) => todo ! ( ) ,
582
+ PathEl :: Bearing ( _) => todo ! ( ) ,
583
+ PathEl :: BearingRel ( _) => todo ! ( ) ,
584
+ }
585
+ }
586
+
587
+ fn handle_state ( & mut self , state : IterState < ' iter > ) -> Option < KurboPathEl > {
588
+ match state {
589
+ IterState :: None => None ,
590
+ IterState :: LineTo { transform, rest } => {
591
+ // Interpret these points as `LineTo`s.
592
+ let Some ( ( & first, rest) ) = rest. split_first ( ) else {
593
+ return None ;
594
+ } ;
595
+ // Get the point in absolute coords.
596
+ let first = transform * first;
597
+ self . state = IterState :: LineTo { transform, rest } ;
598
+ self . current_point = first;
599
+ Some ( KurboPathEl :: LineTo ( first) )
600
+ }
601
+ }
602
+ }
603
+
604
+ /// Get the transform for the current start position and heading.
605
+ fn transform ( & self ) -> Affine {
606
+ // XXX I think this is correct, but not yet 100%
607
+ Affine :: translate ( self . current_point . to_vec2 ( ) ) * Affine :: rotate ( self . current_bearing )
608
+ }
436
609
}
437
610
438
611
impl < ' iter > Iterator for PathElementsIter < ' iter > {
439
612
type Item = KurboPathEl ;
440
613
441
614
fn next ( & mut self ) -> Option < Self :: Item > {
442
- todo ! ( )
615
+ loop {
616
+ // Remember to but the state back if necessary.
617
+ let existing_state = mem:: replace ( & mut self . state , IterState :: None ) ;
618
+ if let Some ( el) = self . handle_state ( existing_state) {
619
+ return Some ( el) ;
620
+ }
621
+ let Some ( ( first, rest) ) = self . path . split_first ( ) else {
622
+ break ;
623
+ } ;
624
+ self . path = rest;
625
+ if let Some ( kurbo_path) = self . next_el ( first) {
626
+ return Some ( kurbo_path) ;
627
+ }
628
+ }
629
+ None
443
630
}
444
631
}
445
632
@@ -454,24 +641,70 @@ mod one_vec {
454
641
/// A vector that has at least 1 element.
455
642
///
456
643
/// It stores the first element on the stack, and the rest on the heap.
644
+ ///
645
+ /// You can create a new `OneVec` either from the first element ([`single`][OneVec::single]) or
646
+ /// from the `TryFrom<Vec<T>>` implementation.
457
647
#[ derive( Debug , Clone ) ]
458
648
pub struct OneVec < T > {
649
+ /// The first, required element in the `OneVec`.
459
650
pub first : T ,
651
+ /// The second and subsequent elements in this `OneVec` (all optional).
460
652
pub rest : Vec < T > ,
461
653
}
462
654
463
655
impl < T > OneVec < T > {
656
+ /// Create a `OneVec` from a single element.
464
657
pub fn single ( val : T ) -> Self {
465
658
Self {
466
659
first : val,
467
660
rest : vec ! [ ] ,
468
661
}
469
662
}
470
663
664
+ /// Iterate over the values in this `OneVec`.
665
+ ///
666
+ /// The iterator is statically guaranteed to produce at least one element.
471
667
pub fn iter ( & self ) -> iter:: Chain < iter:: Once < & T > , slice:: Iter < ' _ , T > > {
472
668
self . into_iter ( )
473
669
}
474
670
671
+ /// Get the element at the given index.
672
+ ///
673
+ /// If the index is `0`, then this is guaranteed to return `Some`.
674
+ pub fn get ( & self , idx : usize ) -> Option < & T > {
675
+ if idx == 0 {
676
+ Some ( & self . first )
677
+ } else {
678
+ self . rest . get ( idx - 1 )
679
+ }
680
+ }
681
+
682
+ /// Get the element at the given index.
683
+ ///
684
+ /// If the index is `0`, then this is guaranteed to return `Some`.
685
+ pub fn get_mut ( & mut self , idx : usize ) -> Option < & mut T > {
686
+ if idx == 0 {
687
+ Some ( & mut self . first )
688
+ } else {
689
+ self . rest . get_mut ( idx - 1 )
690
+ }
691
+ }
692
+
693
+ /// Get the first element.
694
+ pub fn first ( & self ) -> & T {
695
+ & self . first
696
+ }
697
+
698
+ /// Get the first element.
699
+ pub fn first_mut ( & mut self ) -> & mut T {
700
+ & mut self . first
701
+ }
702
+
703
+ /// Splits the `OneVec` into the first element and the rest.
704
+ pub fn split ( & self ) -> ( & T , & [ T ] ) {
705
+ ( & self . first , & self . rest )
706
+ }
707
+
475
708
/// Write out the vector with spaces between each element
476
709
pub ( crate ) fn write_spaced (
477
710
& self ,
0 commit comments