697 lines
26 KiB
Rust
697 lines
26 KiB
Rust
use glam::{DAffine2, DVec2};
|
|
use graphene_core::renderer::ClickTarget;
|
|
use graphene_core::transform::Footprint;
|
|
use std::collections::{HashMap, HashSet};
|
|
use std::num::NonZeroU64;
|
|
|
|
use graph_craft::document::{DocumentNode, NodeId, NodeNetwork};
|
|
|
|
use graphene_core::renderer::Quad;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct DocumentMetadata {
|
|
upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>,
|
|
structure: HashMap<LayerNodeIdentifier, NodeRelations>,
|
|
artboards: HashSet<LayerNodeIdentifier>,
|
|
folders: HashSet<LayerNodeIdentifier>,
|
|
click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>,
|
|
selected_nodes: Vec<NodeId>,
|
|
/// Transform from document space to viewport space.
|
|
pub document_to_viewport: DAffine2,
|
|
}
|
|
|
|
impl Default for DocumentMetadata {
|
|
fn default() -> Self {
|
|
Self {
|
|
upstream_transforms: HashMap::new(),
|
|
click_targets: HashMap::new(),
|
|
structure: HashMap::from_iter([(LayerNodeIdentifier::ROOT, NodeRelations::default())]),
|
|
artboards: HashSet::new(),
|
|
folders: HashSet::new(),
|
|
selected_nodes: Vec::new(),
|
|
document_to_viewport: DAffine2::IDENTITY,
|
|
}
|
|
}
|
|
}
|
|
pub struct SelectionChanged;
|
|
|
|
// layer iters
|
|
impl DocumentMetadata {
|
|
/// Get the root layer from the document
|
|
pub const fn root(&self) -> LayerNodeIdentifier {
|
|
LayerNodeIdentifier::ROOT
|
|
}
|
|
|
|
pub fn all_layers(&self) -> DecendantsIter<'_> {
|
|
self.root().decendants(self)
|
|
}
|
|
|
|
pub fn all_layers_except_artboards(&self) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
|
|
self.all_layers().filter(move |layer| !self.artboards.contains(layer))
|
|
}
|
|
|
|
pub fn selected_layers(&self) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
|
|
self.all_layers().filter(|layer| self.selected_nodes.contains(&layer.to_node()))
|
|
}
|
|
|
|
pub fn selected_layers_except_artboards(&self) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
|
|
self.selected_layers().filter(move |layer| !self.artboards.contains(layer))
|
|
}
|
|
|
|
pub fn selected_layers_contains(&self, layer: LayerNodeIdentifier) -> bool {
|
|
self.selected_layers().any(|selected| selected == layer)
|
|
}
|
|
|
|
pub fn selected_nodes(&self) -> core::slice::Iter<'_, NodeId> {
|
|
self.selected_nodes.iter()
|
|
}
|
|
|
|
pub fn selected_nodes_ref(&self) -> &Vec<NodeId> {
|
|
&self.selected_nodes
|
|
}
|
|
|
|
pub fn has_selected_nodes(&self) -> bool {
|
|
!self.selected_nodes.is_empty()
|
|
}
|
|
|
|
pub fn layer_exists(&self, layer: LayerNodeIdentifier) -> bool {
|
|
self.structure.contains_key(&layer)
|
|
}
|
|
|
|
pub fn click_target(&self, layer: LayerNodeIdentifier) -> Option<&Vec<ClickTarget>> {
|
|
self.click_targets.get(&layer)
|
|
}
|
|
|
|
/// Access the [`NodeRelations`] of a layer.
|
|
fn get_relations(&self, node_identifier: LayerNodeIdentifier) -> Option<&NodeRelations> {
|
|
self.structure.get(&node_identifier)
|
|
}
|
|
|
|
/// Mutably access the [`NodeRelations`] of a layer.
|
|
fn get_structure_mut(&mut self, node_identifier: LayerNodeIdentifier) -> &mut NodeRelations {
|
|
self.structure.entry(node_identifier).or_default()
|
|
}
|
|
|
|
/// Layers excluding ones that are children of other layers in the list.
|
|
pub fn shallowest_unique_layers(&self, layers: impl Iterator<Item = LayerNodeIdentifier>) -> Vec<Vec<LayerNodeIdentifier>> {
|
|
let mut sorted_layers = layers
|
|
.map(|layer| {
|
|
let mut layer_path = layer.ancestors(self).collect::<Vec<_>>();
|
|
layer_path.reverse();
|
|
layer_path
|
|
})
|
|
.collect::<Vec<_>>();
|
|
sorted_layers.sort();
|
|
// Sorting here creates groups of similar UUID paths
|
|
sorted_layers.dedup_by(|a, b| a.starts_with(b));
|
|
sorted_layers
|
|
}
|
|
|
|
/// Ancestor that is shared by all layers and that is deepest (more nested). Default may be the root.
|
|
pub fn deepest_common_ancestor(&self, layers: impl Iterator<Item = LayerNodeIdentifier>, include_self: bool) -> Option<LayerNodeIdentifier> {
|
|
layers
|
|
.map(|layer| {
|
|
let mut layer_path = layer.ancestors(self).collect::<Vec<_>>();
|
|
layer_path.reverse();
|
|
|
|
if include_self || !self.folders.contains(&layer) {
|
|
layer_path.pop();
|
|
}
|
|
|
|
layer_path
|
|
})
|
|
.reduce(|mut a, b| {
|
|
a.truncate(a.iter().zip(b.iter()).position(|(&a, &b)| a != b).unwrap_or_else(|| a.len().min(b.len())));
|
|
a
|
|
})
|
|
.and_then(|layer| layer.last().copied())
|
|
}
|
|
|
|
pub fn active_artboard(&self) -> LayerNodeIdentifier {
|
|
self.artboards.iter().next().copied().unwrap_or(LayerNodeIdentifier::ROOT)
|
|
}
|
|
|
|
pub fn is_folder(&self, layer: LayerNodeIdentifier) -> bool {
|
|
self.folders.contains(&layer)
|
|
}
|
|
|
|
pub fn is_artboard(&self, layer: LayerNodeIdentifier) -> bool {
|
|
self.artboards.contains(&layer)
|
|
}
|
|
|
|
/// Filter out non folder layers
|
|
pub fn folders<'a>(&'a self, layers: impl Iterator<Item = LayerNodeIdentifier> + 'a) -> impl Iterator<Item = LayerNodeIdentifier> + 'a {
|
|
layers.filter(|layer| self.folders.contains(layer))
|
|
}
|
|
|
|
/// Folders sorted from most nested to least nested
|
|
pub fn folders_sorted_by_most_nested(&self, layers: impl Iterator<Item = LayerNodeIdentifier>) -> Vec<LayerNodeIdentifier> {
|
|
let mut folders: Vec<_> = self.folders(layers).collect();
|
|
folders.sort_by_cached_key(|a| std::cmp::Reverse(a.ancestors(self).count()));
|
|
folders
|
|
}
|
|
}
|
|
|
|
// selected layer modifications
|
|
impl DocumentMetadata {
|
|
#[must_use]
|
|
pub fn retain_selected_nodes(&mut self, f: impl FnMut(&NodeId) -> bool) -> SelectionChanged {
|
|
self.selected_nodes.retain(f);
|
|
SelectionChanged
|
|
}
|
|
#[must_use]
|
|
pub fn set_selected_nodes(&mut self, new: Vec<NodeId>) -> SelectionChanged {
|
|
self.selected_nodes = new;
|
|
SelectionChanged
|
|
}
|
|
#[must_use]
|
|
pub fn add_selected_nodes(&mut self, iter: impl IntoIterator<Item = NodeId>) -> SelectionChanged {
|
|
self.selected_nodes.extend(iter);
|
|
SelectionChanged
|
|
}
|
|
#[must_use]
|
|
pub fn clear_selected_nodes(&mut self) -> SelectionChanged {
|
|
self.set_selected_nodes(Vec::new())
|
|
}
|
|
|
|
/// Loads the structure of layer nodes from a node graph.
|
|
pub fn load_structure(&mut self, graph: &NodeNetwork) {
|
|
self.structure = HashMap::from_iter([(LayerNodeIdentifier::ROOT, NodeRelations::default())]);
|
|
self.folders = HashSet::new();
|
|
self.artboards = HashSet::new();
|
|
|
|
let id = graph.outputs[0].node_id;
|
|
let Some(output_node) = graph.nodes.get(&id) else {
|
|
return;
|
|
};
|
|
let Some((layer_node, node_id)) = first_child_layer(graph, output_node) else {
|
|
return;
|
|
};
|
|
let parent = LayerNodeIdentifier::ROOT;
|
|
let mut stack = vec![(layer_node, node_id, parent)];
|
|
while let Some((node, id, parent)) = stack.pop() {
|
|
let mut current = Some((node, id));
|
|
while let Some(&(current_node, current_id)) = current.as_ref() {
|
|
let current_identifier = LayerNodeIdentifier::new_unchecked(current_id);
|
|
if !self.structure.contains_key(¤t_identifier) {
|
|
parent.push_child(self, current_identifier);
|
|
|
|
if let Some((child_node, child_id)) = first_child_layer(graph, current_node) {
|
|
stack.push((child_node, child_id, current_identifier));
|
|
}
|
|
|
|
if is_artboard(current_identifier, graph) {
|
|
self.artboards.insert(current_identifier);
|
|
}
|
|
if is_folder(current_identifier, graph) {
|
|
self.folders.insert(current_identifier);
|
|
}
|
|
}
|
|
|
|
current = sibling_below(graph, current_node);
|
|
}
|
|
}
|
|
|
|
self.selected_nodes.retain(|node| graph.nodes.contains_key(node));
|
|
self.upstream_transforms.retain(|node, _| graph.nodes.contains_key(node));
|
|
self.click_targets.retain(|layer, _| self.structure.contains_key(layer));
|
|
}
|
|
}
|
|
|
|
fn first_child_layer<'a>(graph: &'a NodeNetwork, node: &DocumentNode) -> Option<(&'a DocumentNode, NodeId)> {
|
|
graph.upstream_flow_back_from_nodes(vec![node.inputs[0].as_node()?], true).find(|(node, _)| node.is_layer())
|
|
}
|
|
|
|
fn sibling_below<'a>(graph: &'a NodeNetwork, node: &DocumentNode) -> Option<(&'a DocumentNode, NodeId)> {
|
|
let construct_layer_node = &node.inputs[1];
|
|
construct_layer_node.as_node().and_then(|id| graph.nodes.get(&id).filter(|node| node.is_layer()).map(|node| (node, id)))
|
|
}
|
|
|
|
// transforms
|
|
impl DocumentMetadata {
|
|
/// Update the cached transforms of the layers
|
|
pub fn update_transforms(&mut self, new_upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>) {
|
|
self.upstream_transforms = new_upstream_transforms;
|
|
}
|
|
|
|
/// Access the cached transformation to document space from layer space
|
|
pub fn transform_to_document(&self, layer: LayerNodeIdentifier) -> DAffine2 {
|
|
self.document_to_viewport.inverse() * self.transform_to_viewport(layer)
|
|
}
|
|
|
|
pub fn transform_to_viewport(&self, layer: LayerNodeIdentifier) -> DAffine2 {
|
|
layer
|
|
.ancestors(self)
|
|
.filter_map(|layer| self.upstream_transforms.get(&layer.to_node()))
|
|
.copied()
|
|
.map(|(footprint, transform)| footprint.transform * transform)
|
|
.next()
|
|
.unwrap_or(self.document_to_viewport)
|
|
}
|
|
|
|
pub fn upstream_transform(&self, node_id: NodeId) -> DAffine2 {
|
|
self.upstream_transforms.get(&node_id).copied().map(|(_, transform)| transform).unwrap_or(DAffine2::IDENTITY)
|
|
}
|
|
|
|
pub fn downstream_transform_to_viewport(&self, layer: LayerNodeIdentifier) -> DAffine2 {
|
|
self.upstream_transforms
|
|
.get(&layer.to_node())
|
|
.copied()
|
|
.map(|(footprint, _)| footprint.transform)
|
|
.unwrap_or_else(|| self.transform_to_viewport(layer))
|
|
}
|
|
}
|
|
|
|
pub fn is_artboard(layer: LayerNodeIdentifier, network: &NodeNetwork) -> bool {
|
|
network.upstream_flow_back_from_nodes(vec![layer.to_node()], true).any(|(node, _)| node.is_artboard())
|
|
}
|
|
|
|
pub fn is_folder(layer: LayerNodeIdentifier, network: &NodeNetwork) -> bool {
|
|
network.nodes.get(&layer.to_node()).and_then(|node| node.inputs.first()).is_some_and(|input| input.as_node().is_none())
|
|
|| network
|
|
.upstream_flow_back_from_nodes(vec![layer.to_node()], true)
|
|
.skip(1)
|
|
.any(|(node, _)| node.is_artboard() || node.is_layer())
|
|
}
|
|
|
|
// click targets
|
|
impl DocumentMetadata {
|
|
/// Update the cached click targets of the layers
|
|
pub fn update_click_targets(&mut self, new_click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>) {
|
|
self.click_targets = new_click_targets;
|
|
}
|
|
|
|
/// Get the bounding box of the click target of the specified layer in the specified transform space
|
|
pub fn bounding_box_with_transform(&self, layer: LayerNodeIdentifier, transform: DAffine2) -> Option<[DVec2; 2]> {
|
|
self.click_targets
|
|
.get(&layer)?
|
|
.iter()
|
|
.filter_map(|click_target| click_target.subpath.bounding_box_with_transform(transform))
|
|
.reduce(Quad::combine_bounds)
|
|
}
|
|
|
|
/// Calculate the corners of the bounding box but with a nonzero size.
|
|
///
|
|
/// If the layer bounds are `0` in either axis then they are changed to be `1`.
|
|
pub fn nonzero_bounding_box(&self, layer: LayerNodeIdentifier) -> [DVec2; 2] {
|
|
let [bounds_min, mut bounds_max] = self.bounding_box_with_transform(layer, DAffine2::IDENTITY).unwrap_or_default();
|
|
|
|
let bounds_size = bounds_max - bounds_min;
|
|
if bounds_size.x < 1e-10 {
|
|
bounds_max.x = bounds_min.x + 1.;
|
|
}
|
|
if bounds_size.y < 1e-10 {
|
|
bounds_max.y = bounds_min.y + 1.;
|
|
}
|
|
|
|
[bounds_min, bounds_max]
|
|
}
|
|
|
|
/// Get the bounding box of the click target of the specified layer in document space
|
|
pub fn bounding_box_document(&self, layer: LayerNodeIdentifier) -> Option<[DVec2; 2]> {
|
|
self.bounding_box_with_transform(layer, self.transform_to_document(layer))
|
|
}
|
|
|
|
/// Get the bounding box of the click target of the specified layer in viewport space
|
|
pub fn bounding_box_viewport(&self, layer: LayerNodeIdentifier) -> Option<[DVec2; 2]> {
|
|
self.bounding_box_with_transform(layer, self.transform_to_viewport(layer))
|
|
}
|
|
|
|
/// Calculates the document bounds in viewport space
|
|
pub fn document_bounds_viewport_space(&self) -> Option<[DVec2; 2]> {
|
|
self.all_layers().filter_map(|layer| self.bounding_box_viewport(layer)).reduce(Quad::combine_bounds)
|
|
}
|
|
|
|
/// Calculates the document bounds in document space
|
|
pub fn document_bounds_document_space(&self, include_artboards: bool) -> Option<[DVec2; 2]> {
|
|
self.all_layers()
|
|
.filter(|&layer| include_artboards || !self.is_artboard(layer))
|
|
.filter_map(|layer| self.bounding_box_document(layer))
|
|
.reduce(Quad::combine_bounds)
|
|
}
|
|
|
|
/// Calculates the selected layer bounds in document space
|
|
pub fn selected_bounds_document_space(&self, include_artboards: bool) -> Option<[DVec2; 2]> {
|
|
self.selected_layers()
|
|
.filter(|&layer| include_artboards || !self.is_artboard(layer))
|
|
.filter_map(|layer| self.bounding_box_document(layer))
|
|
.reduce(Quad::combine_bounds)
|
|
}
|
|
|
|
pub fn layer_outline(&self, layer: LayerNodeIdentifier) -> graphene_core::vector::Subpath {
|
|
let Some(click_targets) = self.click_targets.get(&layer) else {
|
|
return graphene_core::vector::Subpath::new();
|
|
};
|
|
graphene_core::vector::Subpath::from_bezier_rs(click_targets.iter().map(|click_target| &click_target.subpath))
|
|
}
|
|
}
|
|
|
|
/// Id of a layer node
|
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
|
|
pub struct LayerNodeIdentifier(NonZeroU64);
|
|
|
|
impl Default for LayerNodeIdentifier {
|
|
fn default() -> Self {
|
|
Self::ROOT
|
|
}
|
|
}
|
|
|
|
impl core::fmt::Debug for LayerNodeIdentifier {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_tuple("LayerNodeIdentifier").field(&self.to_node()).finish()
|
|
}
|
|
}
|
|
|
|
impl core::fmt::Display for LayerNodeIdentifier {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.write_fmt(format_args!("Layer(node_id={})", self.to_node()))
|
|
}
|
|
}
|
|
|
|
impl LayerNodeIdentifier {
|
|
pub const ROOT: Self = LayerNodeIdentifier::new_unchecked(0);
|
|
|
|
/// Construct a [`LayerNodeIdentifier`] without checking if it is a layer node
|
|
pub const fn new_unchecked(node_id: NodeId) -> Self {
|
|
// Safety: will always be >=1
|
|
Self(unsafe { NonZeroU64::new_unchecked(node_id + 1) })
|
|
}
|
|
|
|
/// Construct a [`LayerNodeIdentifier`], debug asserting that it is a layer node
|
|
#[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}: {:#?}",
|
|
network.nodes.get(&node_id)
|
|
);
|
|
Self::new_unchecked(node_id)
|
|
}
|
|
|
|
pub fn from_path(path: &[u64], network: &NodeNetwork) -> Self {
|
|
Self::new(*path.last().unwrap(), network)
|
|
}
|
|
|
|
/// Access the node id of this layer
|
|
pub fn to_node(self) -> NodeId {
|
|
u64::from(self.0) - 1
|
|
}
|
|
|
|
/// Convert layer to layer path
|
|
pub fn to_path(self) -> Vec<NodeId> {
|
|
vec![self.to_node()]
|
|
}
|
|
|
|
/// Access the parent layer if possible
|
|
pub fn parent(self, document_metadata: &DocumentMetadata) -> Option<LayerNodeIdentifier> {
|
|
document_metadata.get_relations(self).and_then(|relations| relations.parent)
|
|
}
|
|
|
|
/// Access the previous sibling of this layer (up the layer tree)
|
|
pub fn previous_sibling(self, document_metadata: &DocumentMetadata) -> Option<LayerNodeIdentifier> {
|
|
document_metadata.get_relations(self).and_then(|relations| relations.previous_sibling)
|
|
}
|
|
|
|
/// Access the next sibling of this layer (down the layer tree)
|
|
pub fn next_sibling(self, document_metadata: &DocumentMetadata) -> Option<LayerNodeIdentifier> {
|
|
document_metadata.get_relations(self).and_then(|relations| relations.next_sibling)
|
|
}
|
|
|
|
/// Access the first child of this layer (top most in layer tree)
|
|
pub fn first_child(self, document_metadata: &DocumentMetadata) -> Option<LayerNodeIdentifier> {
|
|
document_metadata.get_relations(self).and_then(|relations| relations.first_child)
|
|
}
|
|
|
|
/// Access the last child of this layer (bottom most in layer tree)
|
|
pub fn last_child(self, document_metadata: &DocumentMetadata) -> Option<LayerNodeIdentifier> {
|
|
document_metadata.get_relations(self).and_then(|relations| relations.last_child)
|
|
}
|
|
|
|
/// Does the layer have children?
|
|
pub fn has_children(self, document_metadata: &DocumentMetadata) -> bool {
|
|
self.first_child(document_metadata).is_some()
|
|
}
|
|
|
|
/// Iterator over all direct children (excluding self and recursive children)
|
|
pub fn children(self, document_metadata: &DocumentMetadata) -> AxisIter {
|
|
AxisIter {
|
|
layer_node: self.first_child(document_metadata),
|
|
next_node: Self::next_sibling,
|
|
document_metadata,
|
|
}
|
|
}
|
|
|
|
/// All ancestors of this layer, including self, going to the document root
|
|
pub fn ancestors(self, document_metadata: &DocumentMetadata) -> AxisIter {
|
|
AxisIter {
|
|
layer_node: Some(self),
|
|
next_node: Self::parent,
|
|
document_metadata,
|
|
}
|
|
}
|
|
|
|
/// Iterator through all the last children, starting from self
|
|
pub fn last_children(self, document_metadata: &DocumentMetadata) -> AxisIter {
|
|
AxisIter {
|
|
layer_node: Some(self),
|
|
next_node: Self::last_child,
|
|
document_metadata,
|
|
}
|
|
}
|
|
|
|
/// Iterator through all decendants, including recursive children (not including self)
|
|
pub fn decendants(self, document_metadata: &DocumentMetadata) -> DecendantsIter {
|
|
DecendantsIter {
|
|
front: self.first_child(document_metadata),
|
|
back: self.last_child(document_metadata).and_then(|child| child.last_children(document_metadata).last()),
|
|
document_metadata,
|
|
}
|
|
}
|
|
|
|
/// Add a child towards the top of the layer tree
|
|
pub fn push_front_child(self, document_metadata: &mut DocumentMetadata, new: LayerNodeIdentifier) {
|
|
assert!(!document_metadata.structure.contains_key(&new), "Cannot add already existing layer");
|
|
let parent = document_metadata.get_structure_mut(self);
|
|
let old_first_child = parent.first_child.replace(new);
|
|
parent.last_child.get_or_insert(new);
|
|
if let Some(old_first_child) = old_first_child {
|
|
document_metadata.get_structure_mut(old_first_child).previous_sibling = Some(new);
|
|
}
|
|
document_metadata.get_structure_mut(new).next_sibling = old_first_child;
|
|
document_metadata.get_structure_mut(new).parent = Some(self);
|
|
}
|
|
|
|
/// Add a child towards the bottom of the layer tree
|
|
pub fn push_child(self, document_metadata: &mut DocumentMetadata, new: LayerNodeIdentifier) {
|
|
assert!(!document_metadata.structure.contains_key(&new), "Cannot add already existing layer");
|
|
let parent = document_metadata.get_structure_mut(self);
|
|
let old_last_child = parent.last_child.replace(new);
|
|
parent.first_child.get_or_insert(new);
|
|
if let Some(old_last_child) = old_last_child {
|
|
document_metadata.get_structure_mut(old_last_child).next_sibling = Some(new);
|
|
}
|
|
document_metadata.get_structure_mut(new).previous_sibling = old_last_child;
|
|
document_metadata.get_structure_mut(new).parent = Some(self);
|
|
}
|
|
|
|
/// Add sibling above in the layer tree
|
|
pub fn add_before(self, document_metadata: &mut DocumentMetadata, new: LayerNodeIdentifier) {
|
|
assert!(!document_metadata.structure.contains_key(&new), "Cannot add already existing layer");
|
|
document_metadata.get_structure_mut(new).next_sibling = Some(self);
|
|
document_metadata.get_structure_mut(new).parent = self.parent(document_metadata);
|
|
let old_previous_sibling = document_metadata.get_structure_mut(self).previous_sibling.replace(new);
|
|
if let Some(old_previous_sibling) = old_previous_sibling {
|
|
document_metadata.get_structure_mut(old_previous_sibling).next_sibling = Some(new);
|
|
document_metadata.get_structure_mut(new).previous_sibling = Some(old_previous_sibling);
|
|
} else if let Some(structure) = self
|
|
.parent(document_metadata)
|
|
.map(|parent| document_metadata.get_structure_mut(parent))
|
|
.filter(|structure| structure.first_child == Some(self))
|
|
{
|
|
structure.first_child = Some(new);
|
|
}
|
|
}
|
|
|
|
/// Add sibling below in the layer tree
|
|
pub fn add_after(self, document_metadata: &mut DocumentMetadata, new: LayerNodeIdentifier) {
|
|
assert!(!document_metadata.structure.contains_key(&new), "Cannot add already existing layer");
|
|
document_metadata.get_structure_mut(new).previous_sibling = Some(self);
|
|
document_metadata.get_structure_mut(new).parent = self.parent(document_metadata);
|
|
let old_next_sibling = document_metadata.get_structure_mut(self).next_sibling.replace(new);
|
|
if let Some(old_next_sibling) = old_next_sibling {
|
|
document_metadata.get_structure_mut(old_next_sibling).previous_sibling = Some(new);
|
|
document_metadata.get_structure_mut(new).next_sibling = Some(old_next_sibling);
|
|
} else if let Some(structure) = self
|
|
.parent(document_metadata)
|
|
.map(|parent| document_metadata.get_structure_mut(parent))
|
|
.filter(|structure| structure.last_child == Some(self))
|
|
{
|
|
structure.last_child = Some(new);
|
|
}
|
|
}
|
|
|
|
/// Delete layer and all children
|
|
pub fn delete(self, document_metadata: &mut DocumentMetadata) {
|
|
let previous_sibling = self.previous_sibling(document_metadata);
|
|
let next_sibling = self.next_sibling(document_metadata);
|
|
|
|
if let Some(previous_sibling) = previous_sibling.map(|node| document_metadata.get_structure_mut(node)) {
|
|
previous_sibling.next_sibling = next_sibling;
|
|
}
|
|
|
|
if let Some(next_sibling) = next_sibling.map(|node| document_metadata.get_structure_mut(node)) {
|
|
next_sibling.previous_sibling = previous_sibling;
|
|
}
|
|
let mut parent = self.parent(document_metadata).map(|parent| document_metadata.get_structure_mut(parent));
|
|
if let Some(structure) = parent.as_mut().filter(|structure| structure.first_child == Some(self)) {
|
|
structure.first_child = next_sibling;
|
|
}
|
|
if let Some(structure) = parent.as_mut().filter(|structure| structure.last_child == Some(self)) {
|
|
structure.last_child = previous_sibling;
|
|
}
|
|
|
|
let mut delete = vec![self];
|
|
delete.extend(self.decendants(document_metadata));
|
|
for node in delete {
|
|
document_metadata.structure.remove(&node);
|
|
}
|
|
}
|
|
|
|
pub fn exists(&self, document_metadata: &DocumentMetadata) -> bool {
|
|
document_metadata.get_relations(*self).is_some()
|
|
}
|
|
|
|
pub fn starts_with(&self, other: Self, document_metadata: &DocumentMetadata) -> bool {
|
|
self.ancestors(document_metadata).any(|parent| parent == other)
|
|
}
|
|
|
|
pub fn child_of_root(&self, document_metadata: &DocumentMetadata) -> Self {
|
|
self.ancestors(document_metadata)
|
|
.filter(|&layer| layer != LayerNodeIdentifier::ROOT)
|
|
.last()
|
|
.expect("There should be a layer before the root")
|
|
}
|
|
}
|
|
|
|
impl From<NodeId> for LayerNodeIdentifier {
|
|
fn from(node_id: NodeId) -> Self {
|
|
Self::new_unchecked(node_id)
|
|
}
|
|
}
|
|
|
|
impl From<LayerNodeIdentifier> for NodeId {
|
|
fn from(identifier: LayerNodeIdentifier) -> Self {
|
|
identifier.to_node()
|
|
}
|
|
}
|
|
|
|
/// Iterator over specified axis.
|
|
#[derive(Clone)]
|
|
pub struct AxisIter<'a> {
|
|
layer_node: Option<LayerNodeIdentifier>,
|
|
next_node: fn(LayerNodeIdentifier, &DocumentMetadata) -> Option<LayerNodeIdentifier>,
|
|
document_metadata: &'a DocumentMetadata,
|
|
}
|
|
|
|
impl<'a> Iterator for AxisIter<'a> {
|
|
type Item = LayerNodeIdentifier;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let layer_node = self.layer_node.take();
|
|
self.layer_node = layer_node.and_then(|node| (self.next_node)(node, self.document_metadata));
|
|
layer_node
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct DecendantsIter<'a> {
|
|
front: Option<LayerNodeIdentifier>,
|
|
back: Option<LayerNodeIdentifier>,
|
|
document_metadata: &'a DocumentMetadata,
|
|
}
|
|
|
|
impl<'a> Iterator for DecendantsIter<'a> {
|
|
type Item = LayerNodeIdentifier;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.front == self.back {
|
|
self.back = None;
|
|
self.front.take()
|
|
} else {
|
|
let layer_node = self.front.take();
|
|
if let Some(layer_node) = layer_node {
|
|
self.front = layer_node
|
|
.first_child(self.document_metadata)
|
|
.or_else(|| layer_node.ancestors(self.document_metadata).find_map(|ancestor| ancestor.next_sibling(self.document_metadata)));
|
|
}
|
|
layer_node
|
|
}
|
|
}
|
|
}
|
|
impl<'a> DoubleEndedIterator for DecendantsIter<'a> {
|
|
fn next_back(&mut self) -> Option<Self::Item> {
|
|
if self.front == self.back {
|
|
self.front = None;
|
|
self.back.take()
|
|
} else {
|
|
let layer_node = self.back.take();
|
|
if let Some(layer_node) = layer_node {
|
|
self.back = layer_node
|
|
.previous_sibling(self.document_metadata)
|
|
.and_then(|sibling| sibling.last_children(self.document_metadata).last())
|
|
.or_else(|| layer_node.parent(self.document_metadata));
|
|
}
|
|
|
|
layer_node
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, Default)]
|
|
pub struct NodeRelations {
|
|
parent: Option<LayerNodeIdentifier>,
|
|
previous_sibling: Option<LayerNodeIdentifier>,
|
|
next_sibling: Option<LayerNodeIdentifier>,
|
|
first_child: Option<LayerNodeIdentifier>,
|
|
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();
|
|
let root = document_metadata.root();
|
|
let document_metadata = &mut document_metadata;
|
|
root.push_child(document_metadata, LayerNodeIdentifier::new_unchecked(3));
|
|
assert_eq!(root.children(document_metadata).collect::<Vec<_>>(), vec![LayerNodeIdentifier::new_unchecked(3)]);
|
|
root.push_child(document_metadata, LayerNodeIdentifier::new_unchecked(6));
|
|
assert_eq!(root.children(document_metadata).map(LayerNodeIdentifier::to_node).collect::<Vec<_>>(), vec![3, 6]);
|
|
assert_eq!(root.decendants(document_metadata).map(LayerNodeIdentifier::to_node).collect::<Vec<_>>(), vec![3, 6]);
|
|
LayerNodeIdentifier::new_unchecked(3).add_after(document_metadata, LayerNodeIdentifier::new_unchecked(4));
|
|
LayerNodeIdentifier::new_unchecked(3).add_before(document_metadata, LayerNodeIdentifier::new_unchecked(2));
|
|
LayerNodeIdentifier::new_unchecked(6).add_before(document_metadata, LayerNodeIdentifier::new_unchecked(5));
|
|
LayerNodeIdentifier::new_unchecked(6).add_after(document_metadata, LayerNodeIdentifier::new_unchecked(9));
|
|
LayerNodeIdentifier::new_unchecked(6).push_child(document_metadata, LayerNodeIdentifier::new_unchecked(8));
|
|
LayerNodeIdentifier::new_unchecked(6).push_front_child(document_metadata, LayerNodeIdentifier::new_unchecked(7));
|
|
root.push_front_child(document_metadata, LayerNodeIdentifier::new_unchecked(1));
|
|
assert_eq!(root.children(document_metadata).map(LayerNodeIdentifier::to_node).collect::<Vec<_>>(), vec![1, 2, 3, 4, 5, 6, 9]);
|
|
assert_eq!(
|
|
root.decendants(document_metadata).map(LayerNodeIdentifier::to_node).collect::<Vec<_>>(),
|
|
vec![1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
);
|
|
assert_eq!(
|
|
root.decendants(document_metadata).map(LayerNodeIdentifier::to_node).rev().collect::<Vec<_>>(),
|
|
vec![9, 8, 7, 6, 5, 4, 3, 2, 1]
|
|
);
|
|
assert!(root.children(document_metadata).all(|child| child.parent(document_metadata) == Some(root)));
|
|
LayerNodeIdentifier::new_unchecked(6).delete(document_metadata);
|
|
LayerNodeIdentifier::new_unchecked(1).delete(document_metadata);
|
|
LayerNodeIdentifier::new_unchecked(9).push_child(document_metadata, LayerNodeIdentifier::new_unchecked(10));
|
|
assert_eq!(root.children(document_metadata).map(LayerNodeIdentifier::to_node).collect::<Vec<_>>(), vec![2, 3, 4, 5, 9]);
|
|
assert_eq!(root.decendants(document_metadata).map(LayerNodeIdentifier::to_node).collect::<Vec<_>>(), vec![2, 3, 4, 5, 9, 10]);
|
|
assert_eq!(root.decendants(document_metadata).map(LayerNodeIdentifier::to_node).rev().collect::<Vec<_>>(), vec![10, 9, 5, 4, 3, 2]);
|
|
}
|