use alloc::vec::Vec;
use crate::paint::Point;
use thorvg_sys as sys;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PathCommand {
Close,
MoveTo,
LineTo,
CubicTo,
}
impl PathCommand {
#[must_use]
pub const fn points_consumed(self) -> usize {
match self {
Self::Close => 0,
Self::MoveTo | Self::LineTo => 1,
Self::CubicTo => 3,
}
}
#[allow(clippy::cast_possible_truncation)]
pub(crate) fn to_raw(self) -> sys::Tvg_Path_Command {
match self {
Self::Close => sys::TVG_PATH_COMMAND_CLOSE as sys::Tvg_Path_Command,
Self::MoveTo => sys::TVG_PATH_COMMAND_MOVE_TO as sys::Tvg_Path_Command,
Self::LineTo => sys::TVG_PATH_COMMAND_LINE_TO as sys::Tvg_Path_Command,
Self::CubicTo => sys::TVG_PATH_COMMAND_CUBIC_TO as sys::Tvg_Path_Command,
}
}
#[allow(clippy::unnecessary_cast)]
pub(crate) fn from_raw(c: sys::Tvg_Path_Command) -> Option<Self> {
const CLOSE: u32 = sys::TVG_PATH_COMMAND_CLOSE as u32;
const MOVE_TO: u32 = sys::TVG_PATH_COMMAND_MOVE_TO as u32;
const LINE_TO: u32 = sys::TVG_PATH_COMMAND_LINE_TO as u32;
const CUBIC_TO: u32 = sys::TVG_PATH_COMMAND_CUBIC_TO as u32;
match u32::from(c) {
CLOSE => Some(Self::Close),
MOVE_TO => Some(Self::MoveTo),
LINE_TO => Some(Self::LineTo),
CUBIC_TO => Some(Self::CubicTo),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Path {
pub commands: Vec<PathCommand>,
pub points: Vec<Point>,
}
impl Path {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn command_count(&self) -> usize {
self.commands.len()
}
#[must_use]
pub fn point_count(&self) -> usize {
self.points.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.commands.is_empty()
}
#[must_use]
pub fn segments(&self) -> Segments<'_> {
Segments {
commands: self.commands.iter(),
points: self.points.iter(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Segment {
Close,
MoveTo(Point),
LineTo(Point),
CubicTo {
c1: Point,
c2: Point,
end: Point,
},
}
pub struct Segments<'a> {
commands: core::slice::Iter<'a, PathCommand>,
points: core::slice::Iter<'a, Point>,
}
impl Iterator for Segments<'_> {
type Item = Segment;
fn next(&mut self) -> Option<Self::Item> {
Some(match *self.commands.next()? {
PathCommand::Close => Segment::Close,
PathCommand::MoveTo => Segment::MoveTo(*self.points.next()?),
PathCommand::LineTo => Segment::LineTo(*self.points.next()?),
PathCommand::CubicTo => Segment::CubicTo {
c1: *self.points.next()?,
c2: *self.points.next()?,
end: *self.points.next()?,
},
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.commands.len()))
}
}
impl core::iter::FusedIterator for Segments<'_> {}
impl core::fmt::Debug for Segments<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Segments").finish_non_exhaustive()
}
}