Remove all references to legacy layers (#1523)

* Remove visible field from LegacyLayer

* Remove LegacyLayer wrapper around LegacyLayerType

* Remove FolderLegacyLayer and LayerLegacyLayer wrappers around their data

* Remove legacy layers
This commit is contained in:
Keavon Chambers
2023-12-19 20:50:45 -08:00
parent 9a7d7de8fa
commit dcd38f2e4c
26 changed files with 211 additions and 1078 deletions
-107
View File
@@ -1,7 +1,4 @@
use crate::document_metadata::{is_artboard, DocumentMetadata, LayerNodeIdentifier};
use crate::layers::folder_layer::FolderLegacyLayer;
use crate::layers::layer_info::{LegacyLayer, LegacyLayerType};
use crate::DocumentError;
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeNetwork, NodeOutput};
use graphene_core::renderer::ClickTarget;
@@ -23,9 +20,6 @@ pub type LayerId = u64;
pub struct Document {
#[serde(default)]
pub document_network: NodeNetwork,
/// The root layer, usually a [FolderLegacyLayer](layers::folder_layer::FolderLegacyLayer) that contains all other [LegacyLayers](layers::layer_info::LegacyLayer).
#[serde(skip)]
pub root: LegacyLayer,
/// 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)]
@@ -43,11 +37,6 @@ impl PartialEq for Document {
impl Default for Document {
fn default() -> Self {
Self {
root: LegacyLayer {
name: None,
visible: true,
data: LegacyLayerType::Folder(FolderLegacyLayer::default()),
},
state_identifier: DefaultHasher::new(),
document_network: {
use graph_craft::document::{value::TaggedValue, NodeInput};
@@ -156,100 +145,4 @@ impl Document {
pub fn current_state_identifier(&self) -> u64 {
self.state_identifier.finish()
}
/// 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<&FolderLegacyLayer, 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.
fn folder_mut(&mut self, path: &[LayerId]) -> Result<&mut FolderLegacyLayer, 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<&LegacyLayer, 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 LegacyLayer, 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()))
}
pub fn common_layer_path_prefix<'a>(&self, layers: impl Iterator<Item = &'a [LayerId]>) -> &'a [LayerId] {
layers.reduce(|a, b| &a[..a.iter().zip(b.iter()).take_while(|&(a, b)| a == b).count()]).unwrap_or_default()
}
/// 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<Item = &'a [LayerId]>) -> Result<&'a [LayerId], DocumentError> {
let common_prefix_of_path = self.common_layer_path_prefix(layers);
Ok(match self.layer(common_prefix_of_path)?.data {
LegacyLayerType::Folder(_) => common_prefix_of_path,
_ => &common_prefix_of_path[..common_prefix_of_path.len() - 1],
})
}
/// Returns all layers that are not contained in any other of the given folders
/// Takes and Iterator over &[LayerId] or &Vec<LayerId>.
pub fn shallowest_unique_layers<'a, T>(layers: impl Iterator<Item = T>) -> Vec<T>
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
}
/// 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<Vec<usize>, 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 = match root.layer(*id) {
Some(LegacyLayer {
data: LegacyLayerType::Folder(folder),
..
}) => Some(folder),
_ => None,
}
.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)
}
}
fn split_path(path: &[LayerId]) -> Result<(&[LayerId], LayerId), DocumentError> {
let (id, path) = path.split_last().ok_or(DocumentError::InvalidPath)?;
Ok((path, *id))
}
+10 -18
View File
@@ -154,24 +154,20 @@ impl DocumentMetadata {
// selected layer modifications
impl DocumentMetadata {
#[must_use]
pub fn retain_selected_nodes(&mut self, f: impl FnMut(&NodeId) -> bool) -> SelectionChanged {
pub fn retain_selected_nodes(&mut self, f: impl FnMut(&NodeId) -> bool) {
self.selected_nodes.retain(f);
SelectionChanged
}
#[must_use]
pub fn set_selected_nodes(&mut self, new: Vec<NodeId>) -> SelectionChanged {
pub fn set_selected_nodes(&mut self, new: Vec<NodeId>) {
self.selected_nodes = new;
SelectionChanged
}
#[must_use]
pub fn add_selected_nodes(&mut self, iter: impl IntoIterator<Item = NodeId>) -> SelectionChanged {
pub fn add_selected_nodes(&mut self, iter: impl IntoIterator<Item = NodeId>) {
self.selected_nodes.extend(iter);
SelectionChanged
}
#[must_use]
pub fn clear_selected_nodes(&mut self) -> SelectionChanged {
self.set_selected_nodes(Vec::new())
pub fn clear_selected_nodes(&mut self) {
self.set_selected_nodes(Vec::new());
}
/// Loads the structure of layer nodes from a node graph.
@@ -374,8 +370,8 @@ impl LayerNodeIdentifier {
#[track_caller]
pub fn new(node_id: NodeId, network: &NodeNetwork) -> Self {
debug_assert!(
is_layer_node(node_id, network),
"Layer identifier constructed from non layer node {node_id}: {:#?}",
node_id == LayerNodeIdentifier::ROOT.to_node() || network.nodes.get(&node_id).is_some_and(|node| node.is_layer()),
"Layer identifier constructed from non-layer node {node_id}: {:#?}",
network.nodes.get(&node_id)
);
Self::new_unchecked(node_id)
@@ -633,10 +629,6 @@ pub struct NodeRelations {
last_child: Option<LayerNodeIdentifier>,
}
fn is_layer_node(node: NodeId, network: &NodeNetwork) -> bool {
node == LayerNodeIdentifier::ROOT.to_node() || network.nodes.get(&node).is_some_and(|node| node.is_layer())
}
#[test]
fn test_tree() {
let mut document_metadata = DocumentMetadata::default();
@@ -1,27 +0,0 @@
use super::layer_info::LegacyLayer;
use crate::document::LayerId;
use crate::DocumentError;
use serde::{Deserialize, Serialize};
/// A layer that encapsulates other layers, including potentially more folders.
/// The contained layers are rendered in the same order they are stored.
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Default)]
pub struct FolderLegacyLayer {
/// The IDs of the [Layer]s contained within the Folder
pub layer_ids: Vec<LayerId>,
/// The [Layer]s contained in the folder
pub layers: Vec<LegacyLayer>,
}
impl FolderLegacyLayer {
pub fn layer(&self, layer_id: LayerId) -> Option<&LegacyLayer> {
let index = self.layer_ids.iter().position(|x| *x == layer_id).ok_or_else(|| DocumentError::LayerNotFound([layer_id].into())).ok()?;
Some(&self.layers[index])
}
pub fn layer_mut(&mut self, layer_id: LayerId) -> Option<&mut LegacyLayer> {
let index = self.layer_ids.iter().position(|x| *x == layer_id).ok_or_else(|| DocumentError::LayerNotFound([layer_id].into())).ok()?;
Some(&mut self.layers[index])
}
}
-124
View File
@@ -1,124 +0,0 @@
use super::folder_layer::FolderLegacyLayer;
use super::layer_layer::LayerLegacyLayer;
use crate::DocumentError;
use core::fmt;
use serde::{Deserialize, Serialize};
// ===============
// LegacyLayerType
// ===============
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
/// Represents different types of layers.
pub enum LegacyLayerType {
/// A layer that wraps a [FolderLegacyLayer] struct.
Folder(FolderLegacyLayer),
/// A layer that wraps an [LayerLegacyLayer] struct.
Layer(LayerLegacyLayer),
}
impl Default for LegacyLayerType {
fn default() -> Self {
LegacyLayerType::Layer(Default::default())
}
}
// =========================
// LayerDataTypeDiscriminant
// =========================
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, specta::Type)]
pub enum LayerDataTypeDiscriminant {
Folder,
Layer,
}
impl fmt::Display for LayerDataTypeDiscriminant {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
LayerDataTypeDiscriminant::Folder => write!(f, "Folder"),
LayerDataTypeDiscriminant::Layer => write!(f, "Layer"),
}
}
}
impl From<&LegacyLayerType> for LayerDataTypeDiscriminant {
fn from(data: &LegacyLayerType) -> Self {
use LegacyLayerType::*;
match data {
Folder(_) => LayerDataTypeDiscriminant::Folder,
Layer(_) => LayerDataTypeDiscriminant::Layer,
}
}
}
// ===========
// LegacyLayer
// ===========
#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
pub struct LegacyLayer {
/// The user-given name of the layer.
pub name: Option<String>,
/// Whether the layer is currently visible or hidden.
pub visible: bool,
/// The type of layer, such as folder or shape.
pub data: LegacyLayerType,
}
impl LegacyLayer {
/// 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](LegacyLayerType::Folder), the iterator will recursively yield all the layers contained in the folder as well as potential sub-folders.
pub fn iter(&self) -> LayerIter<'_> {
LayerIter { stack: vec![self] }
}
/// Get a mutable reference to the Folder wrapped by the layer.
/// This operation will fail if the [Layer type](Layer::data) is not `LegacyLayerType::Folder`.
pub fn as_folder_mut(&mut self) -> Result<&mut FolderLegacyLayer, DocumentError> {
match &mut self.data {
LegacyLayerType::Folder(f) => Ok(f),
_ => Err(DocumentError::NotFolder),
}
}
/// Get a reference to the Folder wrapped by the layer.
/// This operation will fail if the [Layer type](Layer::data) is not `LegacyLayerType::Folder`.
pub fn as_folder(&self) -> Result<&FolderLegacyLayer, DocumentError> {
match &self.data {
LegacyLayerType::Folder(f) => Ok(f),
_ => Err(DocumentError::NotFolder),
}
}
}
// =========
// LayerIter
// =========
/// 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 LegacyLayer>,
}
impl<'a> Iterator for LayerIter<'a> {
type Item = &'a LegacyLayer;
fn next(&mut self) -> Option<Self::Item> {
match self.stack.pop() {
Some(layer) => {
if let LegacyLayerType::Folder(folder) = &layer.data {
let layers = folder.layers.as_slice();
self.stack.extend(layers);
};
Some(layer)
}
None => None,
}
}
}
-11
View File
@@ -1,11 +0,0 @@
use serde::{Deserialize, Serialize};
// ================
// LayerLegacyLayer
// ================
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
pub struct LayerLegacyLayer {
/// The document node network that this layer contains
pub network: graph_craft::document::NodeNetwork,
}
-20
View File
@@ -1,20 +0,0 @@
//! # Layers
//! A document consists of a set of [Layers](layer_info::Layer).
//! Layers allow the user to mutate part of the document while leaving the rest unchanged.
//! There are currently these different types of layers:
//! * [Folder layers](folder_layer::FolderLegacyLayer), which encapsulate sub-layers
//! * [Layer layers](layer_layer::LayerLegacyLayer), which contain a node graph layer
//!
//! Refer to the module-level documentation for detailed information on each layer.
//!
//! ## Overlapping layers
//! Layers are rendered on top of each other.
//! When different layers overlap, they are blended together according to the [BlendMode](blend_mode::BlendMode)
//! using the CSS [`mix-blend-mode`](https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode) property and the layer opacity.
/// Contains the [FolderLegacyLayer](folder_layer::FolderLegacyLayer) type that encapsulates other layers, including more folders.
pub mod folder_layer;
/// Contains the base [Layer](layer_info::Layer) type, an abstraction over the different types of layers.
pub mod layer_info;
/// Contains the [LayerLegacyLayer](nodegraph_layer::LayerLegacyLayer) type that contains a node graph.
pub mod layer_layer;
-14
View File
@@ -1,16 +1,2 @@
// `macro_use` puts the log macros (`error!`, `warn!`, `debug!`, `info!` and `trace!`) in scope for the crate
// #[macro_use]
extern crate log;
pub mod document;
pub mod document_metadata;
pub mod layers;
/// A set of different errors that can occur when using this crate.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DocumentError {
LayerNotFound(Vec<document::LayerId>),
InvalidPath,
NotFolder,
InvalidFile(String),
}