use skia_safe as skia;
use crate::point::{Point, PointData, PointType, WhichHandle};
use crate::{Contour, Outline};
#[derive(Clone, Default, PartialEq)]
pub struct SkiaPaths {
pub open: Option<skia::Path>,
pub closed: Option<skia::Path>,
}
impl SkiaPaths {
pub fn combined(&self) -> skia::Path {
let mut combined = skia::Path::new();
self.open.as_ref().map(|p|combined.add_path(&p, (0., 0.), skia::path::AddPathMode::Append));
self.closed.as_ref().map(|p|combined.add_path(&p, (0., 0.), skia::path::AddPathMode::Append));
combined
}
}
impl Into<skia::Path> for SkiaPaths {
fn into(self) -> skia::Path {
self.combined()
}
}
#[derive(Copy, Clone)]
pub struct SkiaPointTransforms<'a> {
pub calc_x: &'a dyn Fn(f32) -> f32,
pub calc_y: &'a dyn Fn(f32) -> f32,
}
impl Default for SkiaPointTransforms<'_> {
fn default() -> Self {
Self {
calc_x: &|f|f,
calc_y: &|f|f
}
}
}
impl SkiaPointTransforms<'_> {
pub fn new() -> Self {
Self::default()
}
}
pub trait ToSkiaPath {
fn to_skia_path(&self, spt: Option<SkiaPointTransforms>) -> Option<skia::Path>;
}
pub trait ToSkiaPaths {
fn to_skia_paths(&self, spt: Option<SkiaPointTransforms>) -> SkiaPaths;
}
impl<PD: PointData> ToSkiaPaths for Outline<PD> {
fn to_skia_paths(&self, spt: Option<SkiaPointTransforms>) -> SkiaPaths {
let mut ret = SkiaPaths {
open: None,
closed: None
};
let mut open = skia::Path::new();
let mut closed = skia::Path::new();
for contour in self {
let firstpoint: &Point<PD> = match contour.first() {
Some(p) => p,
None => { continue } };
let skpath = contour.to_skia_path(spt).unwrap(); if firstpoint.ptype == PointType::Move {
&mut open
} else {
&mut closed
}.add_path(&skpath, (0., 0.), skia::path::AddPathMode::Append);
}
if open.count_points() > 0 {
ret.open = Some(open);
}
if closed.count_points() > 0 {
ret.closed = Some(closed);
}
ret
}
}
impl<PD: PointData> ToSkiaPath for Contour<PD> {
fn to_skia_path(&self, spt: Option<SkiaPointTransforms>) -> Option<skia::Path> {
if self.is_empty() {
return None;
}
let mut path = skia::Path::new();
let firstpoint: &Point<PD> = self.first().unwrap();
let mut prevpoint: &Point<PD> = self.first().unwrap();
let transforms = spt.unwrap_or(SkiaPointTransforms::new());
let calc_x: &dyn Fn(f32) -> f32 = transforms.calc_x;
let calc_y: &dyn Fn(f32) -> f32 = transforms.calc_y;
path.move_to((calc_x(self[0].x), calc_y(self[0].y)));
for (i, point) in self.iter().enumerate() {
if i == 0 { continue;
};
match point.ptype {
PointType::Line => {
path.line_to((calc_x(point.x), calc_y(point.y)));
}
PointType::Curve => {
let h1 = prevpoint.handle_or_colocated(WhichHandle::A, &calc_x, &calc_y);
let h2 = point.handle_or_colocated(WhichHandle::B, &calc_x, &calc_y);
path.cubic_to(h1, h2, (calc_x(point.x), calc_y(point.y)));
}
PointType::QCurve => {
let h1 = prevpoint.handle_or_colocated(WhichHandle::A, &calc_x, &calc_y);
path.quad_to(h1, (calc_x(point.x), calc_y(point.y)));
}
_ => {}
}
prevpoint = &point;
}
if firstpoint.ptype != PointType::Move {
let lastpoint = self.last().unwrap();
let h1 = lastpoint.handle_or_colocated(WhichHandle::A, &calc_x, &calc_y);
match firstpoint.ptype {
PointType::Curve => {
let h2 = firstpoint.handle_or_colocated(WhichHandle::B, &calc_x, &calc_y);
path.cubic_to(h1, h2, (calc_x(firstpoint.x), calc_y(firstpoint.y)));
}
PointType::QCurve => {
path.quad_to(h1, (calc_x(firstpoint.x), calc_y(firstpoint.y)));
}
_ => {}
}
path.close();
}
Some(path)
}
}