use crate::math; use skia_safe as skia; use uuid::Uuid; mod blend; mod fills; pub use blend::*; pub use fills::*; #[derive(Debug, Clone, Copy)] pub enum Kind { Rect, } pub type Color = skia::Color; #[derive(Debug, Clone, Copy)] pub struct Matrix { pub a: f32, pub b: f32, pub c: f32, pub d: f32, pub e: f32, pub f: f32, } impl Matrix { pub fn identity() -> Self { Self { a: 1., b: 0., c: 0., d: 1., e: 0., f: 0., } } } #[derive(Debug, Clone)] #[allow(dead_code)] pub struct Shape { pub id: Uuid, pub children: Vec, pub kind: Kind, pub selrect: math::Rect, pub transform: Matrix, pub rotation: f32, fills: Vec, pub blend_mode: BlendMode, pub opacity: f32, } impl Shape { pub fn new(id: Uuid) -> Self { Self { id, children: Vec::::new(), kind: Kind::Rect, selrect: math::Rect::new_empty(), transform: Matrix::identity(), rotation: 0., fills: vec![], blend_mode: BlendMode::default(), opacity: 1., } } pub fn translation(&self) -> (f32, f32) { (self.transform.e, self.transform.f) } pub fn scale(&self) -> (f32, f32) { (self.transform.a, self.transform.d) } pub fn skew(&self) -> (f32, f32) { (self.transform.c, self.transform.b) } pub fn fills(&self) -> std::slice::Iter { self.fills.iter() } pub fn add_fill(&mut self, f: Fill) { self.fills.push(f) } pub fn clear_fills(&mut self) { self.fills.clear(); } pub fn add_gradient_stop(&mut self, color: skia::Color, offset: f32) -> Result<(), String> { let fill = self.fills.last_mut().ok_or("Shape has no fills")?; let gradient = match fill { Fill::LinearGradient(g) => Ok(g), _ => Err("Active fill is not a gradient"), }?; gradient.add_stop(color, offset); Ok(()) } pub fn set_blend_mode(&mut self, mode: BlendMode) { self.blend_mode = mode; } } #[cfg(test)] mod tests { use super::*; fn any_shape() -> Shape { Shape::new(Uuid::nil()) } #[test] fn add_fill_pushes_a_new_fill() { let mut shape = any_shape(); assert_eq!(shape.fills.len(), 0); shape.add_fill(Fill::Solid(Color::TRANSPARENT)); assert_eq!(shape.fills.get(0), Some(&Fill::Solid(Color::TRANSPARENT))) } }