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,
}
}
}