use crate::boolean_ops::composite_boolean_operation; use crate::intersection::Quad; use crate::layers::folder_layer::FolderLayer; use crate::layers::image_layer::ImageLayer; use crate::layers::layer_info::{Layer, LayerData, LayerDataType, LayerDataTypeDiscriminant}; use crate::layers::nodegraph_layer::NodeGraphFrameLayer; use crate::layers::shape_layer::ShapeLayer; use crate::layers::style::RenderData; use crate::layers::text_layer::{Font, FontCache, TextLayer}; use crate::{DocumentError, DocumentResponse, Operation}; use graphene_std::vector::subpath::Subpath; use glam::{DAffine2, DVec2}; use serde::{Deserialize, Serialize}; use std::cell::RefCell; use std::cmp::max; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; /// A number that identifies a layer. /// This does not technically need to be unique globally, only within a folder. pub type LayerId = u64; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Document { /// The root layer, usually a [FolderLayer](layers::folder_layer::FolderLayer) that contains all other [Layers](layers::layer_info::Layer). pub root: Layer, /// The state_identifier serves to provide a way to uniquely identify a particular state that the document is in. /// This identifier is not a hash and is not guaranteed to be equal for equivalent documents. #[serde(skip)] pub state_identifier: DefaultHasher, } impl PartialEq for Document { fn eq(&self, other: &Self) -> bool { self.state_identifier.finish() == other.state_identifier.finish() } } impl Default for Document { fn default() -> Self { Self { root: Layer::new(LayerDataType::Folder(FolderLayer::default()), DAffine2::IDENTITY.to_cols_array()), state_identifier: DefaultHasher::new(), } } } impl Document { /// Wrapper around render, that returns the whole document as a Response. pub fn render_root(&mut self, render_data: RenderData) -> String { // Render and append to the defs section let mut svg_defs = String::from(""); self.root.render(&mut vec![], &mut svg_defs, render_data); svg_defs.push_str(""); // Append the cached rendered SVG svg_defs.push_str(&self.root.cache); svg_defs } /// Renders everything below the given layer contained within its parent folder. pub fn render_layers_below(&mut self, below_layer_path: &[LayerId], render_data: RenderData) -> Option { // Split the path into the layer ID and its parent folder let (layer_id_to_render_below, parent_folder_path) = below_layer_path.split_last()?; // Note: it is bad practice to directly clone and modify the document structure, this is a temporary hack until this whole system is replaced by the node graph let mut temp_subset_folder = self.layer_mut(parent_folder_path).ok()?.clone(); if let LayerDataType::Folder(ref mut folder) = temp_subset_folder.data { // Remove the upper layers to leave behind the lower subset for rendering let count_of_layers_below = folder.layer_ids.iter().position(|id| id == layer_id_to_render_below).unwrap(); folder.layer_ids.truncate(count_of_layers_below); folder.layers.truncate(count_of_layers_below); // Render and append to the defs section let mut svg_defs = String::from(""); temp_subset_folder.render(&mut vec![], &mut svg_defs, render_data); svg_defs.push_str(""); // Append the cached rendered SVG svg_defs.push_str(&temp_subset_folder.cache); Some(svg_defs) } else { None } } /// Renders a layer and its children pub fn render_layer(&mut self, layer_path: &[LayerId], render_data: RenderData) -> Option { // Note: it is bad practice to directly clone and modify the document structure, this is a temporary hack until this whole system is replaced by the node graph let mut temp_clone = self.layer_mut(layer_path).ok()?.clone(); // Render and append to the defs section let mut svg_defs = String::from(""); temp_clone.render(&mut vec![], &mut svg_defs, render_data); svg_defs.push_str(""); // Append the cached rendered SVG svg_defs.push_str(&temp_clone.cache); Some(svg_defs) } pub fn current_state_identifier(&self) -> u64 { self.state_identifier.finish() } /// Checks whether each layer under `path` intersects with the provided `quad` and adds all intersection layers as paths to `intersections`. pub fn intersects_quad(&self, quad: Quad, path: &mut Vec, intersections: &mut Vec>, font_cache: &FontCache) { self.layer(path).unwrap().intersects_quad(quad, path, intersections, font_cache); } /// Checks whether each layer under the root path intersects with the provided `quad` and returns the paths to all intersecting layers. pub fn intersects_quad_root(&self, quad: Quad, font_cache: &FontCache) -> Vec> { let mut intersections = Vec::new(); self.intersects_quad(quad, &mut vec![], &mut intersections, font_cache); intersections } /// Returns a reference to the requested folder. Fails if the path does not exist, /// or if the requested layer is not of type folder. pub fn folder(&self, path: impl AsRef<[LayerId]>) -> Result<&FolderLayer, DocumentError> { let mut root = &self.root; for id in path.as_ref() { root = root.as_folder()?.layer(*id).ok_or_else(|| DocumentError::LayerNotFound(path.as_ref().into()))?; } root.as_folder() } /// Returns a mutable reference to the requested folder. Fails if the path does not exist, /// or if the requested layer is not of type folder. /// If you manually edit the folder you have to set the cache_dirty flag yourself. fn folder_mut(&mut self, path: &[LayerId]) -> Result<&mut FolderLayer, DocumentError> { let mut root = &mut self.root; for id in path { root = root.as_folder_mut()?.layer_mut(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?; } root.as_folder_mut() } /// Returns a reference to the layer or folder at the path. pub fn layer(&self, path: &[LayerId]) -> Result<&Layer, DocumentError> { if path.is_empty() { return Ok(&self.root); } let (path, id) = split_path(path)?; self.folder(path)?.layer(id).ok_or_else(|| DocumentError::LayerNotFound(path.into())) } /// Returns a mutable reference to the layer or folder at the path. pub fn layer_mut(&mut self, path: &[LayerId]) -> Result<&mut Layer, DocumentError> { if path.is_empty() { return Ok(&mut self.root); } let (path, id) = split_path(path)?; self.folder_mut(path)?.layer_mut(id).ok_or_else(|| DocumentError::LayerNotFound(path.into())) } /// Returns vector `Shape`s for each specified in `paths`. /// If any path is not a shape, or does not exist, `DocumentError::InvalidPath` is returned. fn transformed_shapes(&self, paths: &[Vec]) -> Result, DocumentError> { let mut shapes: Vec = Vec::new(); let undo_viewport = self.root.transform.inverse(); for path in paths { match (self.multiply_transforms(path), &self.layer(path)?.data) { (Ok(shape_transform), LayerDataType::Shape(shape)) => { let mut new_shape = shape.clone(); new_shape.shape.apply_affine(undo_viewport * shape_transform); shapes.push(new_shape); } (Ok(_), _) => return Err(DocumentError::InvalidPath), (Err(err), _) => return Err(err), } } Ok(shapes) } /// Return a copy of all [Subpath]s currently in the document. pub fn all_subpaths(&self) -> Vec { self.root.iter().flat_map(|layer| layer.as_subpath_copy()).collect::>() } /// Returns references to all [Subpath]s currently in the document. pub fn all_subpaths_ref(&self) -> Vec<&Subpath> { self.root.iter().flat_map(|layer| layer.as_subpath()).collect::>() } /// Returns a reference to the requested [Subpath] by providing a path to its owner layer. pub fn subpath_ref<'a>(&'a self, path: &[LayerId]) -> Option<&'a Subpath> { self.layer(path).ok()?.as_subpath() } /// Returns a mutable reference of the requested [Subpath] by providing a path to its owner layer. pub fn subpath_mut<'a>(&'a mut self, path: &'a [LayerId]) -> Option<&'a mut Subpath> { self.layer_mut(path).ok()?.as_subpath_mut() } /// Set a [Subpath] at the specified path. pub fn set_subpath(&mut self, path: &[LayerId], shape: Subpath) { let layer = self.layer_mut(path); if let Ok(layer) = layer { if let LayerDataType::Shape(shape_layer) = &mut layer.data { shape_layer.shape = shape; // Is this needed? layer.cache_dirty = true; } } } /// Set [Subpath]s for multiple paths at once. pub fn set_subpaths<'a>(&'a mut self, paths: impl Iterator, shapes: Vec) { paths.zip(shapes).for_each(|(path, shape)| self.set_subpath(path, shape)); } pub fn common_layer_path_prefix<'a>(&self, layers: impl Iterator) -> &'a [LayerId] { layers.reduce(|a, b| &a[..a.iter().zip(b.iter()).take_while(|&(a, b)| a == b).count()]).unwrap_or_default() } /// Filters out the non folders from an iterator of paths. /// Takes and Iterator over &[LayerId] or &Vec. pub fn folders<'a, T>(&'a self, layers: impl Iterator + 'a) -> impl Iterator + 'a where T: AsRef<[LayerId]> + std::cmp::Ord + 'a, { layers.filter(|layer| self.is_folder(layer.as_ref())) } /// Returns the shallowest folder given the selection, even if the selection doesn't contain any folders pub fn shallowest_common_folder<'a>(&self, layers: impl Iterator) -> Result<&'a [LayerId], DocumentError> { let common_prefix_of_path = self.common_layer_path_prefix(layers); Ok(match self.layer(common_prefix_of_path)?.data { LayerDataType::Folder(_) => common_prefix_of_path, _ => &common_prefix_of_path[..common_prefix_of_path.len() - 1], }) } /// Returns all folders that are not contained in any other of the given folders /// Takes and Iterator over &[LayerId] or &Vec. pub fn shallowest_folders<'a, T>(&'a self, layers: impl Iterator) -> Vec where T: AsRef<[LayerId]> + std::cmp::Ord + 'a, { Self::shallowest_unique_layers(self.folders(layers)) } /// Returns all layers that are not contained in any other of the given folders /// Takes and Iterator over &[LayerId] or &Vec. pub fn shallowest_unique_layers<'a, T>(layers: impl Iterator) -> Vec where T: AsRef<[LayerId]> + std::cmp::Ord + 'a, { let mut sorted_layers: Vec<_> = layers.collect(); sorted_layers.sort(); // Sorting here creates groups of similar UUID paths sorted_layers.dedup_by(|a, b| a.as_ref().starts_with(b.as_ref())); sorted_layers } /// Deepest to shallowest (longest to shortest path length) /// Takes and Iterator over &[LayerId] or &Vec. pub fn sorted_folders_by_depth<'a, T>(&'a self, layers: impl Iterator) -> Vec where T: AsRef<[LayerId]> + std::cmp::Ord + 'a, { let mut folders: Vec<_> = self.folders(layers).collect(); folders.sort_by_key(|a| std::cmp::Reverse(a.as_ref().len())); folders } pub fn folder_children_paths(&self, path: &[LayerId]) -> Vec> { if let Ok(folder) = self.folder(&path) { folder.list_layers().iter().map(|f| [path, &[*f]].concat()).collect() } else { vec![] } } pub fn is_folder(&self, path: impl AsRef<[LayerId]>) -> bool { return self.folder(path.as_ref()).is_ok(); } // Determines which layer is closer to the root, if path_a return true, if path_b return false // Answers the question: Is A closer to the root than B? pub fn layer_closer_to_root(&self, path_a: &[u64], path_b: &[u64]) -> bool { // Convert UUIDs to indices let indices_for_path_a = self.indices_for_path(path_a).unwrap(); let indices_for_path_b = self.indices_for_path(path_b).unwrap(); let longest = max(indices_for_path_a.len(), indices_for_path_b.len()); for i in 0..longest { // usize::MAX becomes negative one here, sneaky. So folders are compared as [X, -1]. This is intentional. let index_a = *indices_for_path_a.get(i).unwrap_or(&usize::MAX) as i32; let index_b = *indices_for_path_b.get(i).unwrap_or(&usize::MAX) as i32; // At the point at which the two paths first differ, compare to see which is closer to the root if index_a != index_b { // If index_a is smaller, index_a is closer to the root return index_a < index_b; } } false } // Is the target layer between a <-> b layers, inclusive pub fn layer_is_between(&self, target: &[u64], path_a: &[u64], path_b: &[u64]) -> bool { // If the target is the root, it isn't between if target.is_empty() { return false; } // This function is inclusive, so we consider path_a, path_b to be between themselves if target == path_a || target == path_b { return true; }; // These can't both be true and be between two values let layer_vs_a = self.layer_closer_to_root(target, path_a); let layer_vs_b = self.layer_closer_to_root(target, path_b); // To be in-between you need to be above A and below B or vice versa layer_vs_a != layer_vs_b } /// Given a path to a layer, returns a vector of the indices in the layer tree /// These indices can be used to order a list of layers pub fn indices_for_path(&self, path: &[LayerId]) -> Result, DocumentError> { let mut root = self.root.as_folder()?; let mut indices = vec![]; let (path, layer_id) = split_path(path)?; // TODO: appears to be n^2? should we maintain a lookup table? for id in path { let pos = root.layer_ids.iter().position(|x| *x == *id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?; indices.push(pos); root = root.folder(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?; } indices.push(root.layer_ids.iter().position(|x| *x == layer_id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?); Ok(indices) } /// Replaces the layer at the specified `path` with `layer`. pub fn set_layer(&mut self, path: &[LayerId], layer: Layer, insert_index: isize) -> Result<(), DocumentError> { let mut folder = self.root.as_folder_mut()?; let mut layer_id = None; if let Ok((path, id)) = split_path(path) { layer_id = Some(id); self.mark_as_dirty(path)?; folder = self.folder_mut(path)?; if let Some(folder_layer) = folder.layer_mut(id) { *folder_layer = layer; return Ok(()); } } folder.add_layer(layer, layer_id, insert_index).ok_or(DocumentError::IndexOutOfBounds)?; Ok(()) } /// Visit each layer recursively, marks all children as dirty pub fn mark_children_as_dirty(layer: &mut Layer) -> bool { match layer.data { LayerDataType::Folder(ref mut folder) => { for sub_layer in folder.layers_mut() { if Document::mark_children_as_dirty(sub_layer) { layer.cache_dirty = true; } } } _ => layer.cache_dirty = true, } layer.cache_dirty } /// Adds a new layer to the folder specified by `path`. /// Passing a negative `insert_index` indexes relative to the end. /// -1 is equivalent to adding the layer to the top. pub fn add_layer(&mut self, path: &[LayerId], layer: Layer, insert_index: isize) -> Result { let folder = self.folder_mut(path)?; folder.add_layer(layer, None, insert_index).ok_or(DocumentError::IndexOutOfBounds) } /// Deletes the layer specified by `path`. pub fn delete(&mut self, path: &[LayerId]) -> Result<(), DocumentError> { let (path, id) = split_path(path)?; self.mark_as_dirty(path)?; self.folder_mut(path)?.remove_layer(id) } pub fn visible_layers(&self, path: &mut Vec, paths: &mut Vec>) -> Result<(), DocumentError> { if !self.layer(path)?.visible { return Ok(()); } if let Ok(folder) = self.folder(&path) { for layer in folder.layer_ids.iter() { path.push(*layer); self.visible_layers(path, paths)?; path.pop(); } } else { paths.push(path.clone()); } Ok(()) } pub fn viewport_bounding_box(&self, path: &[LayerId], font_cache: &FontCache) -> Result, DocumentError> { let layer = self.layer(path)?; let transform = self.multiply_transforms(path)?; Ok(layer.data.bounding_box(transform, font_cache)) } pub fn bounding_box_and_transform(&self, path: &[LayerId], font_cache: &FontCache) -> Result, DocumentError> { let layer = self.layer(path)?; let transform = self.multiply_transforms(&path[..path.len() - 1])?; Ok(layer.data.bounding_box(layer.transform, font_cache).map(|bounds| (bounds, transform))) } /// Compute the center of transformation multiplied with `Document::multiply_transforms`. pub fn pivot(&self, path: &[LayerId], font_cache: &FontCache) -> Option { let layer = self.layer(path).ok()?; Some(self.multiply_transforms(path).unwrap_or_default().transform_point2(layer.layerspace_pivot(font_cache))) } pub fn visible_layers_bounding_box(&self, font_cache: &FontCache) -> Option<[DVec2; 2]> { let mut paths = vec![]; self.visible_layers(&mut vec![], &mut paths).ok()?; self.combined_viewport_bounding_box(paths.iter().map(|x| x.as_slice()), font_cache) } pub fn combined_viewport_bounding_box<'a>(&self, paths: impl Iterator, font_cache: &FontCache) -> Option<[DVec2; 2]> { let boxes = paths.filter_map(|path| self.viewport_bounding_box(path, font_cache).ok()?); boxes.reduce(|a, b| [a[0].min(b[0]), a[1].max(b[1])]) } /// Mark the layer at the provided path, as well as all the folders containing it, as dirty. pub fn mark_upstream_as_dirty(&mut self, path: &[LayerId]) -> Result<(), DocumentError> { let mut root = &mut self.root; root.cache_dirty = true; for id in path { root = root.as_folder_mut()?.layer_mut(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?; root.cache_dirty = true; } Ok(()) } pub fn mark_downstream_as_dirty(&mut self, path: &[LayerId]) -> Result<(), DocumentError> { let mut layer = self.layer_mut(path)?; layer.cache_dirty = true; let mut path = path.to_vec(); let len = path.len(); path.push(0); if let Some(ids) = layer.as_folder().ok().map(|f| f.layer_ids.clone()) { for id in ids { path[len] = id; self.mark_downstream_as_dirty(&path)? } } Ok(()) } /// For the purposes of rendering, this invalidates the render cache for the layer so it must be re-rendered next time. pub fn mark_as_dirty(&mut self, path: &[LayerId]) -> Result<(), DocumentError> { self.mark_upstream_as_dirty(path)?; Ok(()) } /// Marks all decendants of the specified [Layer] of a specific [LayerDataType] as dirty fn mark_layers_of_type_as_dirty(root: &mut Layer, data_type: LayerDataTypeDiscriminant) -> bool { if let LayerDataType::Folder(folder) = &mut root.data { let mut dirty = false; for layer in folder.layers_mut() { dirty = Self::mark_layers_of_type_as_dirty(layer, data_type) || dirty; } root.cache_dirty = dirty; } if LayerDataTypeDiscriminant::from(&root.data) == data_type { root.cache_dirty = true; if let LayerDataType::Text(text) = &mut root.data { text.cached_path = None; } } root.cache_dirty } /// Marks all layers in the [Document] of a specific [LayerDataType] as dirty pub fn mark_all_layers_of_type_as_dirty(&mut self, data_type: LayerDataTypeDiscriminant) -> bool { Self::mark_layers_of_type_as_dirty(&mut self.root, data_type) } pub fn transforms(&self, path: &[LayerId]) -> Result, DocumentError> { let mut root = &self.root; let mut transforms = vec![self.root.transform]; for id in path { if let Ok(folder) = root.as_folder() { root = folder.layer(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?; } transforms.push(root.transform); } Ok(transforms) } pub fn multiply_transforms(&self, path: &[LayerId]) -> Result { let mut root = &self.root; let mut trans = self.root.transform; for id in path { if let Ok(folder) = root.as_folder() { root = folder.layer(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?; } trans = trans * root.transform; } Ok(trans) } pub fn generate_transform_across_scope(&self, from: &[LayerId], to: Option) -> Result { let from_rev = self.multiply_transforms(from)?; let scope = to.unwrap_or(DAffine2::IDENTITY); Ok(scope * from_rev) } pub fn transform_relative_to_scope(&mut self, layer: &[LayerId], scope: Option, transform: DAffine2) -> Result<(), DocumentError> { let to = self.generate_transform_across_scope(&layer[..layer.len() - 1], scope)?; let layer = self.layer_mut(layer)?; layer.transform = to.inverse() * transform * to * layer.transform; Ok(()) } pub fn set_transform_relative_to_scope(&mut self, layer: &[LayerId], scope: Option, transform: DAffine2) -> Result<(), DocumentError> { let to = self.generate_transform_across_scope(&layer[..layer.len() - 1], scope)?; let layer = self.layer_mut(layer)?; layer.transform = to.inverse() * transform; Ok(()) } pub fn generate_transform_relative_to_viewport(&self, from: &[LayerId]) -> Result { self.generate_transform_across_scope(from, None) } pub fn apply_transform_relative_to_viewport(&mut self, layer: &[LayerId], transform: DAffine2) -> Result<(), DocumentError> { self.transform_relative_to_scope(layer, None, transform) } pub fn set_transform_relative_to_viewport(&mut self, layer: &[LayerId], transform: DAffine2) -> Result<(), DocumentError> { self.set_transform_relative_to_scope(layer, None, transform) } /// Mutate the document by applying the `operation` to it. If the operation necessitates a /// reaction from the frontend, responses may be returned. pub fn handle_operation(&mut self, operation: Operation, font_cache: &FontCache) -> Result>, DocumentError> { use DocumentResponse::*; operation.pseudo_hash().hash(&mut self.state_identifier); let responses = match operation { Operation::AddEllipse { path, insert_index, transform, style } => { let layer = Layer::new(LayerDataType::Shape(ShapeLayer::ellipse(style)), transform); self.set_layer(&path, layer, insert_index)?; Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(&path)].concat()) } Operation::AddRect { path, insert_index, transform, style } => { let layer = Layer::new(LayerDataType::Shape(ShapeLayer::rectangle(style)), transform); self.set_layer(&path, layer, insert_index)?; Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(&path)].concat()) } Operation::AddLine { path, insert_index, transform, style } => { let layer = Layer::new(LayerDataType::Shape(ShapeLayer::line(style)), transform); self.set_layer(&path, layer, insert_index)?; Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(&path)].concat()) } Operation::AddText { path, insert_index, transform, text, style, size, font_name, font_style, } => { let font = Font::new(font_name, font_style); let layer_text = TextLayer::new(text, style, size, font, font_cache); let layer_data = LayerDataType::Text(layer_text); let layer = Layer::new(layer_data, transform); self.set_layer(&path, layer, insert_index)?; Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(&path)].concat()) } Operation::AddImage { path, transform, insert_index, image_data, mime, } => { let image_data = std::sync::Arc::new(image_data); let layer = Layer::new(LayerDataType::Image(ImageLayer::new(mime, image_data)), transform); self.set_layer(&path, layer, insert_index)?; Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(&path)].concat()) } Operation::AddNodeGraphFrame { path, insert_index, transform, network, } => { let layer = Layer::new(LayerDataType::NodeGraphFrame(NodeGraphFrameLayer { network, ..Default::default() }), transform); self.set_layer(&path, layer, insert_index)?; Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(&path)].concat()) } Operation::SetNodeGraphFrameImageData { layer_path, image_data } => { let layer = self.layer_mut(&layer_path).expect("Setting NodeGraphFrame image data for invalid layer"); if let LayerDataType::NodeGraphFrame(node_graph_frame) = &mut layer.data { let image_data = std::sync::Arc::new(image_data); node_graph_frame.image_data = Some(crate::layers::nodegraph_layer::ImageData { image_data }); } else { panic!("Incorrectly trying to set image data for a layer that is not an NodeGraphFrame layer type"); } Some(vec![LayerChanged { path: layer_path.clone() }]) } Operation::SetLayerPreserveAspect { layer_path, preserve_aspect } => { if let Ok(layer) = self.layer_mut(&layer_path) { layer.preserve_aspect = preserve_aspect; } Some(vec![LayerChanged { path: layer_path.clone() }]) } Operation::SetTextEditability { path, editable } => { self.layer_mut(&path)?.as_text_mut()?.editable = editable; self.mark_as_dirty(&path)?; Some(vec![DocumentChanged]) } Operation::SetTextContent { path, new_text } => { // Not using Document::layer_mut is necessary because we also need to borrow the font cache let mut current_folder = &mut self.root; let (layer_path, id) = split_path(&path)?; for id in layer_path { current_folder = current_folder.as_folder_mut()?.layer_mut(*id).ok_or_else(|| DocumentError::LayerNotFound(layer_path.into()))?; } current_folder .as_folder_mut()? .layer_mut(id) .ok_or_else(|| DocumentError::LayerNotFound(path.clone()))? .as_text_mut()? .update_text(new_text, font_cache); self.mark_as_dirty(&path)?; Some([vec![DocumentChanged], update_thumbnails_upstream(&path)].concat()) } Operation::AddNgon { path, insert_index, transform, style, sides, } => { let layer = Layer::new(LayerDataType::Shape(ShapeLayer::ngon(sides, style)), transform); self.set_layer(&path, layer, insert_index)?; Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(&path)].concat()) } Operation::AddShape { path, transform, insert_index, style, subpath, } => { let shape = ShapeLayer::new(subpath, style); self.set_layer(&path, Layer::new(LayerDataType::Shape(shape), transform), insert_index)?; Some([vec![DocumentChanged, CreatedLayer { path }]].concat()) } Operation::AddPolyline { path, insert_index, points, transform, style, } => { let points: Vec = points.iter().map(|&it| it.into()).collect(); self.set_layer(&path, Layer::new(LayerDataType::Shape(ShapeLayer::poly_line(points, style)), transform), insert_index)?; Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(&path)].concat()) } Operation::BooleanOperation { operation, selected } => { let mut responses = Vec::new(); if selected.len() > 1 { let new_shapes = composite_boolean_operation(operation, &mut self.transformed_shapes(&selected)?.into_iter().rev().map(RefCell::new).collect())?; for path in selected { self.delete(&path)?; responses.push(DocumentResponse::DeletedLayer { path }) } for new_shape in new_shapes { let new_id = self.add_layer(&[], Layer::new(LayerDataType::Shape(new_shape), DAffine2::IDENTITY.to_cols_array()), -1)?; responses.push(DocumentResponse::CreatedLayer { path: vec![new_id] }) } } Some([vec![DocumentChanged, DocumentResponse::FolderChanged { path: vec![] }], responses].concat()) } Operation::AddSpline { path, insert_index, points, transform, style, } => { let points: Vec = points.iter().map(|&it| it.into()).collect(); self.set_layer(&path, Layer::new(LayerDataType::Shape(ShapeLayer::spline(points, style)), transform), insert_index)?; Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(&path)].concat()) } Operation::DeleteLayer { path } => { fn aggregate_deletions(folder: &FolderLayer, path: &mut Vec, responses: &mut Vec) { for (id, layer) in folder.layer_ids.iter().zip(folder.layers()) { path.push(*id); responses.push(DocumentResponse::DeletedLayer { path: path.clone() }); if let LayerDataType::Folder(f) = &layer.data { aggregate_deletions(f, path, responses); } path.pop(); } } let mut responses = Vec::new(); if let Ok(folder) = self.folder(&path) { aggregate_deletions(folder, &mut path.clone(), &mut responses) }; self.delete(&path)?; let (folder, _) = split_path(path.as_slice()).unwrap_or((&[], 0)); responses.extend([DocumentChanged, DeletedLayer { path: path.clone() }, FolderChanged { path: folder.to_vec() }]); responses.extend(update_thumbnails_upstream(folder)); Some(responses) } Operation::InsertLayer { destination_path, layer, insert_index, } => { let (folder_path, layer_id) = split_path(&destination_path)?; let folder = self.folder_mut(folder_path)?; folder.add_layer(*layer, Some(layer_id), insert_index).ok_or(DocumentError::IndexOutOfBounds)?; self.mark_as_dirty(&destination_path)?; fn aggregate_insertions(folder: &FolderLayer, path: &mut Vec, responses: &mut Vec) { for (id, layer) in folder.layer_ids.iter().zip(folder.layers()) { path.push(*id); responses.push(DocumentResponse::CreatedLayer { path: path.clone() }); if let LayerDataType::Folder(f) = &layer.data { aggregate_insertions(f, path, responses); } path.pop(); } } let mut responses = Vec::new(); if let Ok(folder) = self.folder(&destination_path) { aggregate_insertions(folder, &mut destination_path.as_slice().to_vec(), &mut responses) }; responses.extend([DocumentChanged, CreatedLayer { path: destination_path.clone() }, FolderChanged { path: folder_path.to_vec() }]); responses.extend(update_thumbnails_upstream(&destination_path)); Some(responses) } Operation::DuplicateLayer { path } => { let layer = self.layer(&path)?.clone(); let (folder_path, _) = split_path(path.as_slice()).unwrap_or((&[], 0)); let folder = self.folder_mut(folder_path)?; if let Some(new_layer_id) = folder.add_layer(layer, None, -1) { let new_path = [folder_path, &[new_layer_id]].concat(); self.mark_as_dirty(folder_path)?; Some( [ vec![DocumentChanged, CreatedLayer { path: new_path }, FolderChanged { path: folder_path.to_vec() }], update_thumbnails_upstream(path.as_slice()), ] .concat(), ) } else { return Err(DocumentError::IndexOutOfBounds); } } Operation::ModifyFont { path, font_family, font_style, size } => { // Not using Document::layer_mut is necessary because we also need to borrow the font cache let mut current_folder = &mut self.root; let (folder_path, id) = split_path(&path)?; for id in folder_path { current_folder = current_folder.as_folder_mut()?.layer_mut(*id).ok_or_else(|| DocumentError::LayerNotFound(folder_path.into()))?; } let layer_mut = current_folder.as_folder_mut()?.layer_mut(id).ok_or_else(|| DocumentError::LayerNotFound(folder_path.into()))?; let text = layer_mut.as_text_mut()?; text.font = Font::new(font_family, font_style); text.size = size; text.cached_path = Some(text.generate_path(text.load_face(font_cache))); self.mark_as_dirty(&path)?; Some([vec![DocumentChanged, LayerChanged { path: path.clone() }], update_thumbnails_upstream(&path)].concat()) } Operation::RenameLayer { layer_path: path, new_name: name } => { self.layer_mut(&path)?.name = Some(name); Some(vec![LayerChanged { path }]) } Operation::CreateFolder { path } => { self.set_layer(&path, Layer::new(LayerDataType::Folder(FolderLayer::default()), DAffine2::IDENTITY.to_cols_array()), -1)?; self.mark_as_dirty(&path)?; Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(&path)].concat()) } Operation::TransformLayer { path, transform } => { let layer = self.layer_mut(&path).unwrap(); let transform = DAffine2::from_cols_array(&transform) * layer.transform; layer.transform = transform; self.mark_as_dirty(&path)?; Some([vec![DocumentChanged], update_thumbnails_upstream(&path)].concat()) } Operation::TransformLayerInViewport { path, transform } => { let transform = DAffine2::from_cols_array(&transform); self.apply_transform_relative_to_viewport(&path, transform)?; self.mark_as_dirty(&path)?; Some([vec![DocumentChanged], update_thumbnails_upstream(&path)].concat()) } Operation::SetLayerBlobUrl { layer_path, blob_url, resolution } => { let layer = self.layer_mut(&layer_path).unwrap_or_else(|_| panic!("Blob url for invalid layer with path '{:?}'", layer_path)); match &mut layer.data { LayerDataType::Image(image) => { image.blob_url = Some(blob_url); image.dimensions = resolution.into(); } LayerDataType::NodeGraphFrame(node_graph_frame) => { node_graph_frame.blob_url = Some(blob_url); node_graph_frame.dimensions = resolution.into(); } _ => panic!("Incorrectly trying to set the image blob URL for a layer that is not an Image, NodeGraphFrame or Imaginate layer type"), } self.mark_as_dirty(&layer_path)?; Some([vec![DocumentChanged, LayerChanged { path: layer_path.clone() }], update_thumbnails_upstream(&layer_path)].concat()) } Operation::ClearBlobURL { path } => { let layer = self.layer_mut(&path).expect("Clearing node graph image for invalid layer"); match &mut layer.data { LayerDataType::NodeGraphFrame(node_graph) => { node_graph.image_data = None; node_graph.blob_url = None; } e => panic!("Incorrectly trying to clear the blob URL for layer of type {}", LayerDataTypeDiscriminant::from(&*e)), } self.mark_as_dirty(&path)?; Some([vec![DocumentChanged, LayerChanged { path: path.clone() }], update_thumbnails_upstream(&path)].concat()) } Operation::SetPivot { layer_path, pivot } => { let layer = self.layer_mut(&layer_path).expect("Setting pivot for invalid layer"); layer.pivot = pivot.into(); self.mark_as_dirty(&layer_path)?; Some([vec![DocumentChanged, LayerChanged { path: layer_path.clone() }], update_thumbnails_upstream(&layer_path)].concat()) } Operation::SetLayerTransformInViewport { path, transform } => { let transform = DAffine2::from_cols_array(&transform); self.set_transform_relative_to_viewport(&path, transform)?; self.mark_as_dirty(&path)?; Some([vec![DocumentChanged], update_thumbnails_upstream(&path)].concat()) } Operation::SetShapePath { path, subpath } => { self.mark_as_dirty(&path)?; if let LayerDataType::Shape(shape) = &mut self.layer_mut(&path)?.data { shape.shape = subpath; } Some(vec![DocumentChanged, LayerChanged { path }]) } Operation::InsertManipulatorGroup { layer_path, manipulator_group, after_id, } => { if let Ok(Some(shape)) = self.layer_mut(&layer_path).map(|layer| layer.as_subpath_mut()) { shape.manipulator_groups_mut().insert(manipulator_group, after_id); self.mark_as_dirty(&layer_path)?; } Some([update_thumbnails_upstream(&layer_path), vec![DocumentChanged, LayerChanged { path: layer_path }]].concat()) } Operation::PushManipulatorGroup { layer_path, manipulator_group } => { if let Ok(Some(shape)) = self.layer_mut(&layer_path).map(|layer| layer.as_subpath_mut()) { shape.manipulator_groups_mut().push(manipulator_group); self.mark_as_dirty(&layer_path)?; } Some([update_thumbnails_upstream(&layer_path), vec![DocumentChanged, LayerChanged { path: layer_path }]].concat()) } Operation::PushFrontManipulatorGroup { layer_path, manipulator_group } => { if let Ok(Some(shape)) = self.layer_mut(&layer_path).map(|layer| layer.as_subpath_mut()) { shape.manipulator_groups_mut().push_front(manipulator_group); self.mark_as_dirty(&layer_path)?; } Some([update_thumbnails_upstream(&layer_path), vec![DocumentChanged, LayerChanged { path: layer_path }]].concat()) } Operation::RemoveManipulatorGroup { layer_path, id } => { if let Ok(Some(shape)) = self.layer_mut(&layer_path).map(|layer| layer.as_subpath_mut()) { shape.manipulator_groups_mut().remove(id); self.mark_as_dirty(&layer_path)?; } Some([update_thumbnails_upstream(&layer_path), vec![DocumentChanged, LayerChanged { path: layer_path }]].concat()) } Operation::MoveManipulatorPoint { layer_path, id, manipulator_type: control_type, position, } => { if let Ok(Some(shape)) = self.layer_mut(&layer_path).map(|layer| layer.as_subpath_mut()) { if let Some(manipulator_group) = shape.manipulator_groups_mut().by_id_mut(id) { manipulator_group.set_point_position(control_type as usize, position.into()); self.mark_as_dirty(&layer_path)?; } } Some([update_thumbnails_upstream(&layer_path), vec![DocumentChanged, LayerChanged { path: layer_path }]].concat()) } Operation::SetManipulatorPoints { layer_path, id, manipulator_type, position, } => { if let Ok(Some(shape)) = self.layer_mut(&layer_path).map(|layer| layer.as_subpath_mut()) { if let Some(manipulator_group) = shape.manipulator_groups_mut().by_id_mut(id) { if let Some(position) = position { manipulator_group.set_point_position(manipulator_type as usize, position.into()); } else { manipulator_group.points[manipulator_type] = None; } self.mark_as_dirty(&layer_path)?; } } Some([update_thumbnails_upstream(&layer_path), vec![DocumentChanged, LayerChanged { path: layer_path }]].concat()) } Operation::RemoveManipulatorPoint { layer_path, id, manipulator_type: control_type, } => { if let Ok(Some(shape)) = self.layer_mut(&layer_path).map(|layer| layer.as_subpath_mut()) { if let Some(manipulator_group) = shape.manipulator_groups_mut().by_id_mut(id) { manipulator_group.points[control_type as usize] = None; self.mark_as_dirty(&layer_path)?; } } Some([update_thumbnails_upstream(&layer_path), vec![DocumentChanged, LayerChanged { path: layer_path }]].concat()) } Operation::TransformLayerInScope { path, transform, scope } => { let transform = DAffine2::from_cols_array(&transform); let scope = DAffine2::from_cols_array(&scope); self.transform_relative_to_scope(&path, Some(scope), transform)?; self.mark_as_dirty(&path)?; Some([vec![DocumentChanged], update_thumbnails_upstream(&path)].concat()) } Operation::SetLayerTransformInScope { path, transform, scope } => { let transform = DAffine2::from_cols_array(&transform); let scope = DAffine2::from_cols_array(&scope); self.set_transform_relative_to_scope(&path, Some(scope), transform)?; self.mark_as_dirty(&path)?; Some([vec![DocumentChanged], update_thumbnails_upstream(&path)].concat()) } Operation::TransformLayerScaleAroundPivot { path, scale_factor } => { let layer = self.layer_mut(&path)?; let offset = DAffine2::from_translation(-layer.pivot); let scale = DAffine2::from_scale(scale_factor.into()); let offset_back = DAffine2::from_translation(layer.pivot); layer.transform = layer.transform * offset_back * scale * offset; self.mark_as_dirty(&path)?; Some([vec![DocumentChanged], update_thumbnails_upstream(&path)].concat()) } Operation::SetLayerScaleAroundPivot { path, new_scale } => { let layer = self.layer_mut(&path)?; let matrix = layer.transform.to_cols_array(); let old_scale = (matrix[0], matrix[3]); let scale_factor = DVec2::from(new_scale) / DVec2::from(old_scale); let offset = DAffine2::from_translation(-layer.pivot); let scale = DAffine2::from_scale(scale_factor); let offset_back = DAffine2::from_translation(layer.pivot); layer.transform = layer.transform * offset_back * scale * offset; self.mark_as_dirty(&path)?; Some([vec![DocumentChanged], update_thumbnails_upstream(&path)].concat()) } Operation::SetLayerTransform { path, transform } => { let transform = DAffine2::from_cols_array(&transform); let layer = self.layer_mut(&path)?; layer.transform = transform; self.mark_as_dirty(&path)?; Some([vec![DocumentChanged], update_thumbnails_upstream(&path)].concat()) } Operation::ToggleLayerVisibility { path } => { self.mark_as_dirty(&path)?; let layer = self.layer_mut(&path)?; layer.visible = !layer.visible; Some([vec![DocumentChanged], update_thumbnails_upstream(&path)].concat()) } Operation::SetLayerVisibility { path, visible } => { self.mark_as_dirty(&path)?; let layer = self.layer_mut(&path)?; layer.visible = visible; Some([vec![DocumentChanged], update_thumbnails_upstream(&path)].concat()) } Operation::SetLayerName { path, name } => { self.mark_as_dirty(&path)?; let mut layer = self.layer_mut(&path)?; layer.name = if name.as_str() == "" { None } else { Some(name) }; Some(vec![LayerChanged { path }]) } Operation::SetLayerBlendMode { path, blend_mode } => { self.mark_as_dirty(&path)?; self.layer_mut(&path)?.blend_mode = blend_mode; Some([vec![DocumentChanged], update_thumbnails_upstream(&path)].concat()) } Operation::SetLayerOpacity { path, opacity } => { self.mark_as_dirty(&path)?; self.layer_mut(&path)?.opacity = opacity; Some([vec![DocumentChanged], update_thumbnails_upstream(&path)].concat()) } Operation::SetLayerStyle { path, style } => { let layer = self.layer_mut(&path)?; match &mut layer.data { LayerDataType::Shape(s) => s.style = style, LayerDataType::Text(text) => text.path_style = style, _ => return Err(DocumentError::NotAShape), } self.mark_as_dirty(&path)?; Some([vec![DocumentChanged, LayerChanged { path: path.clone() }], update_thumbnails_upstream(&path)].concat()) } Operation::SetLayerStroke { path, stroke } => { let layer = self.layer_mut(&path)?; layer.style_mut()?.set_stroke(stroke); self.mark_as_dirty(&path)?; Some([vec![DocumentChanged], update_thumbnails_upstream(&path)].concat()) } Operation::SetLayerFill { path, fill } => { let layer = self.layer_mut(&path)?; layer.style_mut()?.set_fill(fill); self.mark_as_dirty(&path)?; Some([vec![DocumentChanged], update_thumbnails_upstream(&path)].concat()) } // We may not want the concept of selection here. For now leaving though. Operation::SelectManipulatorPoints { layer_path, point_ids, add } => { let layer = self.layer_mut(&layer_path)?; if let Some(shape) = layer.as_subpath_mut() { if !add { shape.clear_selected_manipulator_groups(); } shape.select_points(&point_ids, true); } Some(vec![LayerChanged { path: layer_path.clone() }]) } Operation::DeselectManipulatorPoints { layer_path, point_ids } => { let layer = self.layer_mut(&layer_path)?; if let Some(shape) = layer.as_subpath_mut() { shape.select_points(&point_ids, false); } Some(vec![LayerChanged { path: layer_path.clone() }]) } Operation::DeselectAllManipulatorPoints { layer_path } => { let layer = self.layer_mut(&layer_path)?; if let Some(shape) = layer.as_subpath_mut() { shape.clear_selected_manipulator_groups(); } Some(vec![LayerChanged { path: layer_path.clone() }]) } Operation::DeleteSelectedManipulatorPoints { layer_paths } => { let mut responses = vec![]; for layer_path in layer_paths { let layer = self.layer_mut(&layer_path)?; if let Some(shape) = layer.as_subpath_mut() { // Delete the selected points. shape.delete_selected(); // Delete the layer if there are no longer any manipulator groups if (shape.manipulator_groups().len() - 1) == 0 { self.delete(&layer_path)?; responses.push(DocumentChanged); responses.push(DocumentResponse::DeletedLayer { path: layer_path }); return Ok(Some(responses)); } // If we still have manipulator groups, update the layer and thumbnails self.mark_as_dirty(&layer_path)?; responses.push(DocumentChanged); responses.push(LayerChanged { path: layer_path.clone() }); responses.append(&mut update_thumbnails_upstream(&layer_path)); } } Some(responses) } Operation::MoveSelectedManipulatorPoints { layer_path, delta, mirror_distance } => { if let Ok(viewspace) = self.generate_transform_relative_to_viewport(&layer_path) { let objectspace = &viewspace.inverse(); let delta = objectspace.transform_vector2(DVec2::new(delta.0, delta.1)); let layer = self.layer_mut(&layer_path)?; if let Some(shape) = layer.as_subpath_mut() { shape.move_selected(delta, mirror_distance); } } self.mark_as_dirty(&layer_path)?; Some([vec![DocumentChanged, LayerChanged { path: layer_path.clone() }], update_thumbnails_upstream(&layer_path)].concat()) } Operation::SetManipulatorHandleMirroring { layer_path, id, mirror_angle } => { if let Ok(Some(shape)) = self.layer_mut(&layer_path).map(|layer| layer.as_subpath_mut()) { if let Some(manipulator_group) = shape.manipulator_groups_mut().by_id_mut(id) { manipulator_group.editor_state.mirror_angle_between_handles = mirror_angle; self.mark_as_dirty(&layer_path)?; } } Some([update_thumbnails_upstream(&layer_path), vec![DocumentChanged, LayerChanged { path: layer_path }]].concat()) } Operation::SetSelectedHandleMirroring { layer_path, toggle_angle } => { let layer = self.layer_mut(&layer_path)?; if let Some(shape) = layer.as_subpath_mut() { for manipulator_group in shape.selected_manipulator_groups_any_points_mut() { manipulator_group.toggle_mirroring(toggle_angle); } } // This does nothing visually so we don't need to send any messages None } }; Ok(responses) } } fn split_path(path: &[LayerId]) -> Result<(&[LayerId], LayerId), DocumentError> { let (id, path) = path.split_last().ok_or(DocumentError::InvalidPath)?; Ok((path, *id)) } fn update_thumbnails_upstream(path: &[LayerId]) -> Vec { let length = path.len(); let mut responses = Vec::with_capacity(length); for i in 0..length { responses.push(DocumentResponse::LayerChanged { path: path[0..(length - i)].to_vec() }); } responses } pub fn pick_layer_safe_imaginate_resolution(layer: &Layer, font_cache: &FontCache) -> (u64, u64) { let layer_bounds = layer.bounding_transform(font_cache); let layer_bounds_size = (layer_bounds.transform_vector2((1., 0.).into()).length(), layer_bounds.transform_vector2((0., 1.).into()).length()); pick_safe_imaginate_resolution(layer_bounds_size) } pub fn pick_safe_imaginate_resolution((width, height): (f64, f64)) -> (u64, u64) { const MAX_RESOLUTION: u64 = 1000 * 1000; let mut scale_factor = 1.; let round_to_increment = |size: f64| (size / 64.).round() as u64 * 64; loop { let possible_solution = (round_to_increment(width * scale_factor), round_to_increment(height * scale_factor)); if possible_solution.0 * possible_solution.1 <= MAX_RESOLUTION { return possible_solution; } scale_factor -= 0.1; } }