use super::blend_mode::BlendMode; use super::folder_layer::FolderLayer; use super::image_layer::ImageLayer; use super::shape_layer::ShapeLayer; use super::style::{PathStyle, RenderData}; use super::text_layer::TextLayer; use super::vector::subpath::Subpath; use crate::intersection::Quad; use crate::layers::text_layer::FontCache; use crate::DocumentError; use crate::LayerId; use core::fmt; use glam::{DAffine2, DMat2, DVec2}; use serde::{Deserialize, Serialize}; use std::fmt::Write; #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] /// Represents different types of layers. pub enum LayerDataType { /// A layer that wraps a [FolderLayer] struct. Folder(FolderLayer), /// A layer that wraps a [ShapeLayer] struct. Shape(ShapeLayer), /// A layer that wraps a [TextLayer] struct. Text(TextLayer), /// A layer that wraps an [ImageLayer] struct. Image(ImageLayer), } impl LayerDataType { pub fn inner(&self) -> &dyn LayerData { match self { LayerDataType::Shape(s) => s, LayerDataType::Folder(f) => f, LayerDataType::Text(t) => t, LayerDataType::Image(i) => i, } } pub fn inner_mut(&mut self) -> &mut dyn LayerData { match self { LayerDataType::Shape(s) => s, LayerDataType::Folder(f) => f, LayerDataType::Text(t) => t, LayerDataType::Image(i) => i, } } } #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] pub enum LayerDataTypeDiscriminant { Folder, Shape, Text, Image, } impl fmt::Display for LayerDataTypeDiscriminant { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let name = match self { LayerDataTypeDiscriminant::Folder => "Folder", LayerDataTypeDiscriminant::Shape => "Shape", LayerDataTypeDiscriminant::Text => "Text", LayerDataTypeDiscriminant::Image => "Image", }; formatter.write_str(name) } } impl From<&LayerDataType> for LayerDataTypeDiscriminant { fn from(data: &LayerDataType) -> Self { use LayerDataType::*; match data { Folder(_) => LayerDataTypeDiscriminant::Folder, Shape(_) => LayerDataTypeDiscriminant::Shape, Text(_) => LayerDataTypeDiscriminant::Text, Image(_) => LayerDataTypeDiscriminant::Image, } } } /// Defines shared behavior for every layer type. pub trait LayerData { /// Render the layer as an SVG tag to a given string. /// /// # Example /// ``` /// # use graphite_graphene::layers::shape_layer::ShapeLayer; /// # use graphite_graphene::layers::style::{Fill, PathStyle, ViewMode, RenderData}; /// # use graphite_graphene::layers::layer_info::LayerData; /// # use std::collections::HashMap; /// /// let mut shape = ShapeLayer::rectangle(PathStyle::new(None, Fill::None)); /// let mut svg = String::new(); /// /// // Render the shape without any transforms, in normal view mode /// # let font_cache = Default::default(); /// let render_data = RenderData::new(ViewMode::Normal, &font_cache, None, false); /// shape.render(&mut svg, &mut String::new(), &mut vec![], render_data); /// /// assert_eq!( /// svg, /// "\ /// \ /// " /// ); /// ``` fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, render_data: RenderData); /// Determine the layers within this layer that intersect a given quad. /// # Example /// ``` /// # use graphite_graphene::layers::shape_layer::ShapeLayer; /// # use graphite_graphene::layers::style::{Fill, PathStyle, ViewMode}; /// # use graphite_graphene::layers::layer_info::LayerData; /// # use graphite_graphene::intersection::Quad; /// # use glam::f64::{DAffine2, DVec2}; /// # use std::collections::HashMap; /// /// let mut shape = ShapeLayer::ellipse(PathStyle::new(None, Fill::None)); /// let shape_id = 42; /// let mut svg = String::new(); /// /// let quad = Quad::from_box([DVec2::ZERO, DVec2::ONE]); /// let mut intersections = vec![]; /// /// shape.intersects_quad(quad, &mut vec![shape_id], &mut intersections, &Default::default()); /// /// assert_eq!(intersections, vec![vec![shape_id]]); /// ``` fn intersects_quad(&self, quad: Quad, path: &mut Vec, intersections: &mut Vec>, font_cache: &FontCache); // TODO: this doctest fails because 0 != 1e-32, maybe assert difference < epsilon? /// Calculate the bounding box for the layer's contents after applying a given transform. /// # Example /// ```no_run /// # use graphite_graphene::layers::shape_layer::ShapeLayer; /// # use graphite_graphene::layers::style::{Fill, PathStyle}; /// # use graphite_graphene::layers::layer_info::LayerData; /// # use glam::f64::{DAffine2, DVec2}; /// # use std::collections::HashMap; /// let shape = ShapeLayer::ellipse(PathStyle::new(None, Fill::None)); /// /// // Calculate the bounding box without applying any transformations. /// // (The identity transform maps every vector to itself.) /// let transform = DAffine2::IDENTITY; /// let bounding_box = shape.bounding_box(transform, &Default::default()); /// /// assert_eq!(bounding_box, Some([DVec2::ZERO, DVec2::ONE])); /// ``` fn bounding_box(&self, transform: glam::DAffine2, font_cache: &FontCache) -> Option<[DVec2; 2]>; } impl LayerData for LayerDataType { fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, render_data: RenderData) { self.inner_mut().render(svg, svg_defs, transforms, render_data) } fn intersects_quad(&self, quad: Quad, path: &mut Vec, intersections: &mut Vec>, font_cache: &FontCache) { self.inner().intersects_quad(quad, path, intersections, font_cache) } fn bounding_box(&self, transform: glam::DAffine2, font_cache: &FontCache) -> Option<[DVec2; 2]> { self.inner().bounding_box(transform, font_cache) } } #[derive(Serialize, Deserialize)] #[serde(remote = "glam::DAffine2")] struct DAffine2Ref { pub matrix2: DMat2, pub translation: DVec2, } /// Utility function for providing a default boolean value to serde. #[inline(always)] fn return_true() -> bool { true } #[derive(Debug, PartialEq, Deserialize, Serialize)] pub struct Layer { /// Whether the layer is currently visible or hidden. pub visible: bool, /// The user-given name of the layer. pub name: Option, /// The type of layer, such as folder or shape. pub data: LayerDataType, /// A transformation applied to the layer (translation, rotation, scaling, and shear). #[serde(with = "DAffine2Ref")] pub transform: glam::DAffine2, /// The center of transformations like rotation or scaling with the shift key. /// This is in local space (so the layer's transform should be applied). pub pivot: DVec2, /// The cached SVG thumbnail view of the layer. #[serde(skip)] pub thumbnail_cache: String, /// The cached SVG render of the layer. #[serde(skip)] pub cache: String, /// The cached definition(s) used by the layer's SVG tag, placed at the top in the SVG defs tag. #[serde(skip)] pub svg_defs_cache: String, /// Whether or not the [Cache](Layer::cache) and [Thumbnail Cache](Layer::thumbnail_cache) need to be updated. #[serde(skip, default = "return_true")] pub cache_dirty: bool, /// The blend mode describing how this layer should composite with others underneath it. pub blend_mode: BlendMode, /// The opacity, in the range of 0 to 1. pub opacity: f64, } impl Layer { pub fn new(data: LayerDataType, transform: [f64; 6]) -> Self { Self { visible: true, name: None, data, transform: glam::DAffine2::from_cols_array(&transform), pivot: DVec2::splat(0.5), cache: String::new(), thumbnail_cache: String::new(), svg_defs_cache: String::new(), cache_dirty: true, blend_mode: BlendMode::Normal, opacity: 1., } } /// Iterate over the layers encapsulated by this layer. /// If the [Layer type](Layer::data) is not a folder, the only item in the iterator will be the layer itself. /// If the [Layer type](Layer::data) wraps a [Folder](LayerDataType::Folder), the iterator will recursively yield all the layers contained in the folder as well as potential sub-folders. /// /// # Example /// ``` /// # use graphite_graphene::layers::shape_layer::ShapeLayer; /// # use graphite_graphene::layers::layer_info::Layer; /// # use graphite_graphene::layers::style::PathStyle; /// # use graphite_graphene::layers::folder_layer::FolderLayer; /// let mut root_folder = FolderLayer::default(); /// /// // Add a shape to the root folder /// let child_1: Layer = ShapeLayer::rectangle(PathStyle::default()).into(); /// root_folder.add_layer(child_1.clone(), None, -1); /// /// // Add a folder containing another shape to the root layer /// let mut child_folder = FolderLayer::default(); /// let grandchild: Layer = ShapeLayer::rectangle(PathStyle::default()).into(); /// child_folder.add_layer(grandchild.clone(), None, -1); /// let child_2: Layer = child_folder.into(); /// root_folder.add_layer(child_2.clone(), None, -1); /// let root: Layer = root_folder.into(); /// /// let mut iter = root.iter(); /// assert_eq!(iter.next(), Some(&root)); /// assert_eq!(iter.next(), Some(&child_2)); /// assert_eq!(iter.next(), Some(&grandchild)); /// assert_eq!(iter.next(), Some(&child_1)); /// assert_eq!(iter.next(), None); /// ``` pub fn iter(&self) -> LayerIter<'_> { LayerIter { stack: vec![self] } } pub fn render(&mut self, transforms: &mut Vec, svg_defs: &mut String, render_data: RenderData) -> &str { if !self.visible { return ""; } transforms.push(self.transform); if let Some(viewport_bounds) = render_data.culling_bounds { if let Some(bounding_box) = self .data .bounding_box(transforms.iter().cloned().reduce(|a, b| a * b).unwrap_or(DAffine2::IDENTITY), render_data.font_cache) { let is_overlapping = viewport_bounds[0].x < bounding_box[1].x && bounding_box[0].x < viewport_bounds[1].x && viewport_bounds[0].y < bounding_box[1].y && bounding_box[0].y < viewport_bounds[1].y; if !is_overlapping { transforms.pop(); self.cache.clear(); self.cache_dirty = true; return ""; } } } if self.cache_dirty { self.thumbnail_cache.clear(); self.svg_defs_cache.clear(); self.data.render(&mut self.thumbnail_cache, &mut self.svg_defs_cache, transforms, render_data); self.cache.clear(); let _ = writeln!(self.cache, r#"{}"#, self.blend_mode.to_svg_style_name(), self.opacity, self.thumbnail_cache.as_str() ); self.cache_dirty = false; } transforms.pop(); svg_defs.push_str(&self.svg_defs_cache); self.cache.as_str() } pub fn intersects_quad(&self, quad: Quad, path: &mut Vec, intersections: &mut Vec>, font_cache: &FontCache) { if !self.visible { return; } let transformed_quad = self.transform.inverse() * quad; self.data.intersects_quad(transformed_quad, path, intersections, font_cache) } /// Compute the bounding box of the layer after applying a transform to it. /// /// # Example /// ``` /// # use graphite_graphene::layers::shape_layer::ShapeLayer; /// # use graphite_graphene::layers::layer_info::Layer; /// # use graphite_graphene::layers::style::PathStyle; /// # use glam::DVec2; /// # use glam::f64::DAffine2; /// # use std::collections::HashMap; /// // Create a rectangle with the default dimensions, from `(0|0)` to `(1|1)` /// let layer: Layer = ShapeLayer::rectangle(PathStyle::default()).into(); /// /// // Apply the Identity transform, which leaves the points unchanged /// assert_eq!( /// layer.aabb_for_transform(DAffine2::IDENTITY, &Default::default()), /// Some([DVec2::ZERO, DVec2::ONE]), /// ); /// /// // Apply a transform that scales every point by a factor of two /// let transform = DAffine2::from_scale(DVec2::ONE * 2.); /// assert_eq!( /// layer.aabb_for_transform(transform, &Default::default()), /// Some([DVec2::ZERO, DVec2::ONE * 2.]), /// ); pub fn aabb_for_transform(&self, transform: DAffine2, font_cache: &FontCache) -> Option<[DVec2; 2]> { self.data.bounding_box(transform, font_cache) } pub fn aabb(&self, font_cache: &FontCache) -> Option<[DVec2; 2]> { self.aabb_for_transform(self.transform, font_cache) } pub fn bounding_transform(&self, font_cache: &FontCache) -> DAffine2 { let scale = match self.aabb_for_transform(DAffine2::IDENTITY, font_cache) { Some([a, b]) => { let dimensions = b - a; DAffine2::from_scale(dimensions) } _ => DAffine2::IDENTITY, }; self.transform * scale } pub fn layerspace_pivot(&self, font_cache: &FontCache) -> DVec2 { let [min, max] = self.aabb_for_transform(DAffine2::IDENTITY, font_cache).unwrap_or([DVec2::ZERO, DVec2::ONE]); self.pivot * (max - min) + min } /// Get a mutable reference to the Folder wrapped by the layer. /// This operation will fail if the [Layer type](Layer::data) is not `LayerDataType::Folder`. pub fn as_folder_mut(&mut self) -> Result<&mut FolderLayer, DocumentError> { match &mut self.data { LayerDataType::Folder(f) => Ok(f), _ => Err(DocumentError::NotAFolder), } } pub fn as_subpath(&self) -> Option<&Subpath> { match &self.data { LayerDataType::Shape(s) => Some(&s.shape), _ => None, } } pub fn as_subpath_copy(&self) -> Option { match &self.data { LayerDataType::Shape(s) => Some(s.shape.clone()), _ => None, } } pub fn as_subpath_mut(&mut self) -> Option<&mut Subpath> { match &mut self.data { LayerDataType::Shape(s) => Some(&mut s.shape), _ => None, } } /// Get a reference to the Folder wrapped by the layer. /// This operation will fail if the [Layer type](Layer::data) is not `LayerDataType::Folder`. pub fn as_folder(&self) -> Result<&FolderLayer, DocumentError> { match &self.data { LayerDataType::Folder(f) => Ok(f), _ => Err(DocumentError::NotAFolder), } } /// Get a mutable reference to the Text element wrapped by the layer. /// This operation will fail if the [Layer type](Layer::data) is not `LayerDataType::Text`. pub fn as_text_mut(&mut self) -> Result<&mut TextLayer, DocumentError> { match &mut self.data { LayerDataType::Text(t) => Ok(t), _ => Err(DocumentError::NotText), } } /// Get a reference to the Text element wrapped by the layer. /// This operation will fail if the [Layer type](Layer::data) is not `LayerDataType::Text`. pub fn as_text(&self) -> Result<&TextLayer, DocumentError> { match &self.data { LayerDataType::Text(t) => Ok(t), _ => Err(DocumentError::NotText), } } /// Get a mutable reference to the Image element wrapped by the layer. /// This operation will fail if the [Layer type](Layer::data) is not `LayerDataType::Image`. pub fn as_image_mut(&mut self) -> Result<&mut ImageLayer, DocumentError> { match &mut self.data { LayerDataType::Image(img) => Ok(img), _ => Err(DocumentError::NotAnImage), } } /// Get a reference to the Image element wrapped by the layer. /// This operation will fail if the [Layer type](Layer::data) is not `LayerDataType::Image`. pub fn as_image(&self) -> Result<&ImageLayer, DocumentError> { match &self.data { LayerDataType::Image(img) => Ok(img), _ => Err(DocumentError::NotAnImage), } } pub fn style(&self) -> Result<&PathStyle, DocumentError> { match &self.data { LayerDataType::Shape(s) => Ok(&s.style), LayerDataType::Text(t) => Ok(&t.path_style), _ => Err(DocumentError::NotAShape), } } pub fn style_mut(&mut self) -> Result<&mut PathStyle, DocumentError> { match &mut self.data { LayerDataType::Shape(s) => Ok(&mut s.style), LayerDataType::Text(t) => Ok(&mut t.path_style), _ => Err(DocumentError::NotAShape), } } } impl Clone for Layer { fn clone(&self) -> Self { Self { visible: self.visible, name: self.name.clone(), data: self.data.clone(), transform: self.transform, pivot: self.pivot, cache: String::new(), thumbnail_cache: String::new(), svg_defs_cache: String::new(), cache_dirty: true, blend_mode: self.blend_mode, opacity: self.opacity, } } } impl From for Layer { fn from(from: FolderLayer) -> Layer { Layer::new(LayerDataType::Folder(from), DAffine2::IDENTITY.to_cols_array()) } } impl From for Layer { fn from(from: ShapeLayer) -> Layer { Layer::new(LayerDataType::Shape(from), DAffine2::IDENTITY.to_cols_array()) } } impl From for Layer { fn from(from: TextLayer) -> Layer { Layer::new(LayerDataType::Text(from), DAffine2::IDENTITY.to_cols_array()) } } impl From for Layer { fn from(from: ImageLayer) -> Layer { Layer::new(LayerDataType::Image(from), DAffine2::IDENTITY.to_cols_array()) } } impl<'a> IntoIterator for &'a Layer { type Item = &'a Layer; type IntoIter = LayerIter<'a>; fn into_iter(self) -> Self::IntoIter { self.iter() } } /// An iterator over the layers encapsulated by this layer. /// See [Layer::iter] for more information. #[derive(Debug, Default)] pub struct LayerIter<'a> { pub stack: Vec<&'a Layer>, } impl<'a> Iterator for LayerIter<'a> { type Item = &'a Layer; fn next(&mut self) -> Option { match self.stack.pop() { Some(layer) => { if let LayerDataType::Folder(folder) = &layer.data { let layers = folder.layers(); self.stack.extend(layers); }; Some(layer) } None => None, } } }