Skip to content

Commit 1bd729e

Browse files
committed
WIP
1 parent b4b33b7 commit 1bd729e

File tree

2 files changed

+245
-12
lines changed

2 files changed

+245
-12
lines changed

src/lib.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@ mod rounded_rect_radii;
108108
mod shape;
109109
pub mod simplify;
110110
mod size;
111-
#[cfg(feature = "std")]
112-
mod svg;
111+
//#[cfg(feature = "std")]
112+
//mod svg;
113113
mod translate_scale;
114114
mod vec2;
115115

@@ -131,7 +131,7 @@ pub use crate::rounded_rect::*;
131131
pub use crate::rounded_rect_radii::*;
132132
pub use crate::shape::*;
133133
pub use crate::size::*;
134-
#[cfg(feature = "std")]
135-
pub use crate::svg::*;
134+
//#[cfg(feature = "std")]
135+
//pub use crate::svg::*;
136136
pub use crate::translate_scale::*;
137137
pub use crate::vec2::*;

src/paths/svg.rs

+241-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
//! 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};
33
use anyhow::{anyhow, Result};
4-
use std::{fmt, ops::Deref, vec};
4+
use std::{fmt, io, iter, mem, ops::Deref, slice, vec};
55

66
pub use self::one_vec::*;
77

8+
type OneIter<'a, T> = iter::Chain<iter::Once<&'a T>, slice::Iter<'a, T>>;
9+
810
/// An SVG path
911
///
1012
/// A path *MUST* begin with a `MoveTo` element. For this and other invalid inputs, we return
@@ -250,7 +252,7 @@ impl Path {
250252
}
251253

252254
/// 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 {
254256
let mut iter = self.elements.iter();
255257
if let Some(el) = iter.next() {
256258
el.write(f)?;
@@ -261,6 +263,18 @@ impl Path {
261263
}
262264
Ok(())
263265
}
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+
}
264278
}
265279

266280
impl Deref for Path {
@@ -271,6 +285,12 @@ impl Deref for Path {
271285
}
272286
}
273287

288+
impl fmt::Display for Path {
289+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
290+
self.fmt(f)
291+
}
292+
}
293+
274294
impl Shape for Path {
275295
type PathElementsIter<'iter> = PathElementsIter<'iter>;
276296

@@ -281,6 +301,8 @@ impl Shape for Path {
281301
path_start_point: Point::ZERO,
282302
current_point: Point::ZERO,
283303
current_bearing: 0.,
304+
state: IterState::None,
305+
seen_moveto: false,
284306
}
285307
}
286308

@@ -353,11 +375,11 @@ impl PathEl {
353375
}
354376
PathEl::SmoothCubicTo(cubic_tos) => {
355377
write!(f, "S")?;
356-
cubic_tos.write_spaced(CubicTo::write_vals, f)?;
378+
cubic_tos.write_spaced(SmoothCubicTo::write_vals, f)?;
357379
}
358380
PathEl::SmoothCubicToRel(cubic_tos) => {
359381
write!(f, "s")?;
360-
cubic_tos.write_spaced(CubicTo::write_vals, f)?;
382+
cubic_tos.write_spaced(SmoothCubicTo::write_vals, f)?;
361383
}
362384
PathEl::QuadTo(quad_tos) => {
363385
write!(f, "Q")?;
@@ -375,8 +397,14 @@ impl PathEl {
375397
write!(f, "t")?;
376398
quad_tos.write_spaced(write_point, f)?;
377399
}
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+
}
380408
PathEl::Bearing(bearing) => {
381409
write!(f, "B{bearing}",)?;
382410
}
@@ -418,6 +446,22 @@ impl QuadTo {
418446
}
419447
}
420448

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+
421465
/// An iterator over the path elements of an SVG path.
422466
///
423467
/// This structure could be `Copy`, but we don't implement it to avoid hidden clones.
@@ -433,13 +477,156 @@ pub struct PathElementsIter<'iter> {
433477
current_point: Point,
434478
/// The current bearing.
435479
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+
}
436609
}
437610

438611
impl<'iter> Iterator for PathElementsIter<'iter> {
439612
type Item = KurboPathEl;
440613

441614
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
443630
}
444631
}
445632

@@ -454,24 +641,70 @@ mod one_vec {
454641
/// A vector that has at least 1 element.
455642
///
456643
/// 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.
457647
#[derive(Debug, Clone)]
458648
pub struct OneVec<T> {
649+
/// The first, required element in the `OneVec`.
459650
pub first: T,
651+
/// The second and subsequent elements in this `OneVec` (all optional).
460652
pub rest: Vec<T>,
461653
}
462654

463655
impl<T> OneVec<T> {
656+
/// Create a `OneVec` from a single element.
464657
pub fn single(val: T) -> Self {
465658
Self {
466659
first: val,
467660
rest: vec![],
468661
}
469662
}
470663

664+
/// Iterate over the values in this `OneVec`.
665+
///
666+
/// The iterator is statically guaranteed to produce at least one element.
471667
pub fn iter(&self) -> iter::Chain<iter::Once<&T>, slice::Iter<'_, T>> {
472668
self.into_iter()
473669
}
474670

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+
475708
/// Write out the vector with spaces between each element
476709
pub(crate) fn write_spaced(
477710
&self,

0 commit comments

Comments
 (0)