use yog_data::Schema;
use crate::math::Vec2;
use crate::rect::Rect;
#[derive(Schema, Debug, Clone, Copy, PartialEq)]
#[data(tag = "s")]
pub enum Seg {
Move { x: f32, y: f32 },
Line { x: f32, y: f32 },
Quad { x1: f32, y1: f32, x: f32, y: f32 },
Cubic { x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32 },
Close,
}
pub fn bounds(path: &[Seg], r: f32) -> Option<Rect> {
let points = path.iter().flat_map(|s| match *s {
Seg::Move { x, y } | Seg::Line { x, y } => vec![Vec2::new(x, y)],
Seg::Quad { x1, y1, x, y } => vec![Vec2::new(x1, y1), Vec2::new(x, y)],
Seg::Cubic { x1, y1, x2, y2, x, y } => vec![Vec2::new(x1, y1), Vec2::new(x2, y2), Vec2::new(x, y)],
Seg::Close => vec![],
});
Rect::around(points).map(|b| b.outset(r))
}
pub fn flatten(path: &[Seg], to: impl Fn(Vec2) -> Vec2) -> Vec<Vec<Vec2>> {
let mut out = Vec::new();
let mut cur: Vec<Vec2> = Vec::new();
let mut at = Vec2::ZERO;
for seg in path {
match *seg {
Seg::Move { x, y } => {
if cur.len() > 1 {
out.push(std::mem::take(&mut cur));
}
at = to(Vec2::new(x, y));
cur = vec![at];
}
Seg::Line { x, y } => {
at = to(Vec2::new(x, y));
cur.push(at);
}
Seg::Quad { x1, y1, x, y } => {
let (c, p, from) = (to(Vec2::new(x1, y1)), to(Vec2::new(x, y)), at);
let n = steps(from.distance(c) + c.distance(p));
for i in 1..=n {
let t = i as f32 / n as f32;
cur.push(from.lerp(c, t).lerp(c.lerp(p, t), t));
}
at = p;
}
Seg::Cubic { x1, y1, x2, y2, x, y } => {
let (c1, c2, p, from) = (to(Vec2::new(x1, y1)), to(Vec2::new(x2, y2)), to(Vec2::new(x, y)), at);
let n = steps(from.distance(c1) + c1.distance(c2) + c2.distance(p));
for i in 1..=n {
let t = i as f32 / n as f32;
let (a, b, c) = (from.lerp(c1, t), c1.lerp(c2, t), c2.lerp(p, t));
cur.push(a.lerp(b, t).lerp(b.lerp(c, t), t));
}
at = p;
}
Seg::Close => {
if let Some(&first) = cur.first() {
if at != first {
cur.push(first);
}
at = first;
}
if cur.len() > 1 {
out.push(std::mem::take(&mut cur));
}
cur = vec![at];
}
}
}
if cur.len() > 1 {
out.push(cur);
}
out
}
fn steps(len: f32) -> usize {
(len / 2.0).ceil().clamp(1.0, 64.0) as usize
}