use super::command::{Command, PointsCommands, Verb};
use super::geometry::{Point, Transform};
use super::path_builder::PathBuilder;
use super::segment::segments;
use super::svg_parser::SvgCommands;
#[cfg(feature = "eval")]
use super::stroke::stroke_into;
#[cfg(feature = "eval")]
use super::style::*;
#[cfg(feature = "eval")]
use super::geometry::{Bounds, BoundsBuilder};
#[cfg(feature = "eval")]
use super::path_builder::TransformSink;
use crate::lib::Vec;
pub trait PathData {
type Commands: Iterator<Item = Command> + Clone;
fn commands(&self) -> Self::Commands;
fn copy_to(&self, sink: &mut impl PathBuilder) {
for cmd in self.commands() {
use Command::*;
match cmd {
MoveTo(p) => sink.move_to(p),
LineTo(p) => sink.line_to(p),
QuadTo(c, p) => sink.quad_to(c, p),
CurveTo(c1, c2, p) => sink.curve_to(c1, c2, p),
Close => sink.close(),
};
}
}
}
pub fn length(data: impl PathData, transform: Option<Transform>) -> f32 {
let data = data.commands();
let mut length = 0.;
if let Some(transform) = transform {
for s in segments(data.map(|cmd| cmd.transform(&transform)), false) {
length += s.length();
}
} else {
for s in segments(data, false) {
length += s.length();
}
}
length
}
#[cfg(feature = "eval")]
pub fn bounds<'a>(
data: impl PathData,
style: impl Into<Style<'a>>,
transform: Option<Transform>,
) -> Bounds {
let style = style.into();
let mut bounds = BoundsBuilder::new();
apply(data, style, transform, &mut bounds);
bounds.build()
}
#[cfg(feature = "eval")]
pub fn apply<'a>(
data: impl PathData,
style: impl Into<Style<'a>>,
transform: Option<Transform>,
sink: &mut impl PathBuilder,
) -> Fill {
let style = style.into();
match style {
Style::Fill(fill) => {
if let Some(transform) = transform {
let mut transform_sink = TransformSink { sink, transform };
data.copy_to(&mut transform_sink);
fill
} else {
data.copy_to(sink);
fill
}
}
Style::Stroke(stroke) => {
if let Some(transform) = transform {
if stroke.scale {
let mut transform_sink = TransformSink { sink, transform };
stroke_into(data.commands(), &stroke, &mut transform_sink);
} else {
stroke_into(
data.commands().map(|cmd| cmd.transform(&transform)),
&stroke,
sink,
);
}
} else {
stroke_into(data.commands(), &stroke, sink);
}
Fill::NonZero
}
}
}
impl<T> PathData for &'_ T
where
T: PathData,
{
type Commands = T::Commands;
fn commands(&self) -> Self::Commands {
T::commands(*self)
}
#[inline(always)]
fn copy_to(&self, sink: &mut impl PathBuilder) {
T::copy_to(*self, sink)
}
}
impl<'a> PathData for &'a str {
type Commands = SvgCommands<'a>;
fn commands(&self) -> Self::Commands {
SvgCommands::new(self)
}
}
impl<'a> PathData for (&'a [Point], &'a [Verb]) {
type Commands = PointsCommands<'a>;
fn commands(&self) -> Self::Commands {
PointsCommands::new(self.0, self.1)
}
#[inline(always)]
fn copy_to(&self, sink: &mut impl PathBuilder) {
self.commands().copy_to(sink);
}
}
impl<'a> PathData for &'a [Command] {
type Commands = core::iter::Copied<core::slice::Iter<'a, Command>>;
fn commands(&self) -> Self::Commands {
self.iter().copied()
}
}
impl<'a> PathData for &'a Vec<Command> {
type Commands = core::iter::Copied<core::slice::Iter<'a, Command>>;
fn commands(&self) -> Self::Commands {
self.iter().copied()
}
}