use crate::types::Point;
use super::format::{PathFormatter, SvgPathFormat};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CubicSegment {
pub from: Point,
pub ctrl1: Point,
pub ctrl2: Point,
pub to: Point,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PathCommand {
MoveTo(Point),
LineTo(Point),
CubicTo {
ctrl1: Point,
ctrl2: Point,
to: Point,
},
Close,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct SmoothPath {
commands: Vec<PathCommand>,
}
impl SmoothPath {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn commands(&self) -> &[PathCommand] {
&self.commands
}
#[must_use]
pub fn cubics(&self) -> Vec<CubicSegment> {
let mut cubics = Vec::new();
let mut current = None;
let mut subpath_start = None;
for command in &self.commands {
match *command {
PathCommand::MoveTo(point) => {
current = Some(point);
subpath_start = Some(point);
}
PathCommand::LineTo(point) => {
current = Some(point);
}
PathCommand::CubicTo { ctrl1, ctrl2, to } => {
if let Some(from) = current {
cubics.push(CubicSegment {
from,
ctrl1,
ctrl2,
to,
});
}
current = Some(to);
}
PathCommand::Close => {
current = subpath_start;
}
}
}
cubics
}
pub fn move_to(&mut self, point: Point) {
self.commands.push(PathCommand::MoveTo(point));
}
pub fn line_to(&mut self, point: Point) {
self.commands.push(PathCommand::LineTo(point));
}
pub fn cubic_to(&mut self, ctrl1: Point, ctrl2: Point, to: Point) {
self.commands
.push(PathCommand::CubicTo { ctrl1, ctrl2, to });
}
pub fn close(&mut self) {
self.commands.push(PathCommand::Close);
}
pub fn export_with<F>(&self, formatter: &F) -> F::Output
where
F: PathFormatter + ?Sized,
{
formatter.format(self.commands())
}
#[must_use]
pub fn to_svg_path(&self) -> String {
self.export_with(&SvgPathFormat::default())
}
#[must_use]
pub fn to_svg_path_with_precision(&self, precision: usize) -> String {
self.export_with(&SvgPathFormat::new(precision))
}
}