use crate::color::Color;
use crate::scene::{Label, Point, Segment, Transform};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Cap {
Butt,
Round,
Square,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Join {
Miter,
Round,
Bevel,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Stroke {
pub color: Color,
pub width: f64,
pub dash: Option<(Vec<f64>, f64)>,
pub cap: Cap,
pub join: Join,
pub miter_limit: f64,
pub opacity: f64,
}
pub type Stop = (f64, Color);
#[derive(Debug, Clone, PartialEq)]
pub enum Shading {
Axial { transform: Transform, stops: Vec<Stop> },
Radial { transform: Transform, focus: Point, stops: Vec<Stop> },
}
#[derive(Debug, Clone)]
pub struct TileItems(pub std::rc::Rc<Vec<Item>>);
impl PartialEq for TileItems {
fn eq(&self, other: &Self) -> bool {
std::rc::Rc::ptr_eq(&self.0, &other.0)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Paint {
Solid(Color),
Pattern {
name: String,
color: Color,
},
Tile {
items: TileItems,
origin: Point,
size: Point,
transform: Transform,
},
Shading(Shading),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Fill {
pub paint: Paint,
pub opacity: f64,
pub even_odd: bool,
}
#[derive(Debug, Clone)]
pub enum Item {
Path {
segments: Vec<Segment>,
fill: Option<Fill>,
stroke: Option<Stroke>,
},
Text {
label: Label,
place: Transform,
opacity: f64,
},
Clip {
segments: Vec<Segment>,
even_odd: bool,
},
Group {
opacity: f64,
},
Blend {
mode: String,
},
Transform {
transform: Transform,
},
Fade {
mask: Shading,
},
FadePicture {
items: Vec<Item>,
},
End,
}
fn grow(min: &mut Option<Point>, max: &mut Option<Point>, p: Point) {
*min = Some(match *min {
Some(m) => Point::new(m.x.min(p.x), m.y.min(p.y)),
None => p,
});
*max = Some(match *max {
Some(m) => Point::new(m.x.max(p.x), m.y.max(p.y)),
None => p,
});
}
#[derive(Debug, Clone, Default)]
pub struct Canvas {
pub items: Vec<Item>,
pub min: Option<Point>,
pub max: Option<Point>,
pub margin: f64,
pub local: Vec<(Option<Point>, Option<Point>)>,
pub overlay: bool,
}
impl Canvas {
pub fn include(&mut self, p: Point) {
if self.overlay || !(p.x.is_finite() && p.y.is_finite()) {
return;
}
grow(&mut self.min, &mut self.max, p);
for (lo, hi) in self.local.iter_mut() {
grow(lo, hi, p);
}
}
pub fn include_path(&mut self, segments: &[Segment]) {
for s in segments {
match s {
Segment::MoveTo(p) | Segment::LineTo(p) => self.include(*p),
Segment::CurveTo(a, b, p) => {
self.include(*a);
self.include(*b);
self.include(*p);
}
Segment::Close => {}
}
}
}
pub fn include_path_margin(&mut self, segments: &[Segment], margin: f64) {
let mut b = Canvas::default();
b.include_path(segments);
if let (Some(min), Some(max)) = (b.min, b.max) {
self.include(Point::new(min.x - margin, min.y - margin));
self.include(Point::new(max.x + margin, max.y + margin));
}
}
pub fn push(&mut self, item: Item) {
self.items.push(item);
}
}
impl Item {
pub fn translate(&mut self, d: Point) {
let t = Transform::translation(d.x, d.y);
match self {
Item::Path { segments, fill, .. } => {
*segments = transform_segments(segments, &t);
if let Some(Fill { paint: Paint::Shading(Shading::Axial { transform, .. } | Shading::Radial { transform, .. }), .. }) = fill {
*transform = t.compose(transform);
}
}
Item::Clip { segments, .. } => *segments = transform_segments(segments, &t),
Item::Text { place, .. } => *place = t.compose(place),
Item::Fade { mask: Shading::Axial { transform, .. } | Shading::Radial { transform, .. } } => *transform = t.compose(transform),
Item::Transform { transform } => *transform = t.compose(transform).compose(&Transform::translation(-d.x, -d.y)),
Item::FadePicture { items } => items.iter_mut().for_each(|i| i.translate(d)),
Item::Group { .. } | Item::Blend { .. } | Item::End => {}
}
}
pub fn transform(&mut self, t: &Transform) {
let scale = (t.a * t.d - t.b * t.c).abs().sqrt();
match self {
Item::Path { segments, fill, stroke } => {
*segments = transform_segments(segments, t);
if let Some(Fill { paint: Paint::Shading(Shading::Axial { transform, .. } | Shading::Radial { transform, .. }), .. }) = fill {
*transform = t.compose(transform);
}
if let Some(s) = stroke {
s.width *= scale;
if let Some((dash, phase)) = &mut s.dash {
dash.iter_mut().for_each(|d| *d *= scale);
*phase *= scale;
}
}
}
Item::Clip { segments, .. } => *segments = transform_segments(segments, t),
Item::Text { place, .. } => *place = t.compose(place),
Item::Fade { mask: Shading::Axial { transform, .. } | Shading::Radial { transform, .. } } => *transform = t.compose(transform),
Item::Transform { transform } => *transform = t.compose(transform).compose(&t.inverse()),
Item::FadePicture { items } => items.iter_mut().for_each(|i| i.transform(t)),
Item::Group { .. } | Item::Blend { .. } | Item::End => {}
}
}
}
pub fn transform_segments(segments: &[Segment], t: &Transform) -> Vec<Segment> {
segments
.iter()
.map(|s| match s {
Segment::MoveTo(p) => Segment::MoveTo(t.apply(*p)),
Segment::LineTo(p) => Segment::LineTo(t.apply(*p)),
Segment::CurveTo(a, b, p) => Segment::CurveTo(t.apply(*a), t.apply(*b), t.apply(*p)),
Segment::Close => Segment::Close,
})
.collect()
}