Populate layer entry cache (#437)

* Populate layer entry cache

* Serialize the DocumentMessageHandler

* Fix restoring of collapsed/expanded state, add iter impl for Layer, and clean up layer_data() functions

* Fixed bug with CreateEmptyLayer revealed by test

Co-authored-by: Keavon Chambers <keavon@keavon.com>
Co-authored-by: otdavies <oliver@psyfer.io>
This commit is contained in:
0HyperCube 2021-12-29 04:32:44 +00:00 committed by Keavon Chambers
parent 3eb915eaee
commit fd1ddfc41e
10 changed files with 156 additions and 83 deletions

14
Cargo.lock generated
View File

@ -101,6 +101,7 @@ dependencies = [
"log", "log",
"rand_chacha", "rand_chacha",
"serde", "serde",
"serde_json",
"spin", "spin",
"thiserror", "thiserror",
] ]
@ -113,7 +114,6 @@ dependencies = [
"kurbo", "kurbo",
"log", "log",
"serde", "serde",
"serde_json",
] ]
[[package]] [[package]]
@ -157,9 +157,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "0.4.8" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
@ -274,9 +274,9 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.5" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]] [[package]]
name = "scoped-tls" name = "scoped-tls"
@ -324,9 +324,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.68" version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",

View File

@ -15,6 +15,7 @@ log = "0.4"
bitflags = "1.2.1" bitflags = "1.2.1"
thiserror = "1.0.24" thiserror = "1.0.24"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }
graphite-proc-macros = { path = "../proc-macros" } graphite-proc-macros = { path = "../proc-macros" }
glam = { version="0.17", features = ["serde"] } glam = { version="0.17", features = ["serde"] }
rand_chacha = "0.3.1" rand_chacha = "0.3.1"

View File

@ -4,6 +4,7 @@ use std::collections::VecDeque;
pub use super::layer_panel::*; pub use super::layer_panel::*;
use super::movement_handler::{MovementMessage, MovementMessageHandler}; use super::movement_handler::{MovementMessage, MovementMessageHandler};
use super::transform_layer_handler::{TransformLayerMessage, TransformLayerMessageHandler}; use super::transform_layer_handler::{TransformLayerMessage, TransformLayerMessageHandler};
use super::vectorize_layerdata;
use crate::consts::DEFAULT_DOCUMENT_NAME; use crate::consts::DEFAULT_DOCUMENT_NAME;
use crate::consts::{ASYMPTOTIC_EFFECT, FILE_EXPORT_SUFFIX, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING}; use crate::consts::{ASYMPTOTIC_EFFECT, FILE_EXPORT_SUFFIX, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING};
@ -58,16 +59,21 @@ pub struct VectorManipulatorShape {
pub transform: DAffine2, pub transform: DAffine2,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DocumentMessageHandler { pub struct DocumentMessageHandler {
pub graphene_document: GrapheneDocument, pub graphene_document: GrapheneDocument,
#[serde(skip)]
pub document_undo_history: Vec<DocumentSave>, pub document_undo_history: Vec<DocumentSave>,
#[serde(skip)]
pub document_redo_history: Vec<DocumentSave>, pub document_redo_history: Vec<DocumentSave>,
pub saved_document_identifier: u64, pub saved_document_identifier: u64,
pub name: String, pub name: String,
#[serde(with = "vectorize_layerdata")]
pub layer_data: HashMap<Vec<LayerId>, LayerData>, pub layer_data: HashMap<Vec<LayerId>, LayerData>,
layer_range_selection_reference: Vec<LayerId>, layer_range_selection_reference: Vec<LayerId>,
#[serde(skip)]
movement_handler: MovementMessageHandler, movement_handler: MovementMessageHandler,
#[serde(skip)]
transform_layer_handler: TransformLayerMessageHandler, transform_layer_handler: TransformLayerMessageHandler,
pub snapping_enabled: bool, pub snapping_enabled: bool,
pub view_mode: ViewMode, pub view_mode: ViewMode,
@ -115,6 +121,7 @@ pub enum DocumentMessage {
ToggleLayerVisibility(Vec<LayerId>), ToggleLayerVisibility(Vec<LayerId>),
FlipSelectedLayers(FlipAxis), FlipSelectedLayers(FlipAxis),
ToggleLayerExpansion(Vec<LayerId>), ToggleLayerExpansion(Vec<LayerId>),
SetLayerExpansion(Vec<LayerId>, bool),
FolderChanged(Vec<LayerId>), FolderChanged(Vec<LayerId>),
LayerChanged(Vec<LayerId>), LayerChanged(Vec<LayerId>),
DocumentStructureChanged, DocumentStructureChanged,
@ -157,30 +164,27 @@ impl From<DocumentOperation> for Message {
} }
impl DocumentMessageHandler { impl DocumentMessageHandler {
pub fn serialize_document(&self) -> String {
let val = serde_json::to_string(self);
// We fully expect the serialization to succeed
val.unwrap()
}
pub fn deserialize_document(serialized_content: &str) -> Result<Self, DocumentError> {
log::info!("Deserialising: {:?}", serialized_content);
serde_json::from_str(serialized_content).map_err(|e| DocumentError::InvalidFile(e.to_string()))
}
pub fn with_name(name: String, ipp: &InputPreprocessor) -> Self { pub fn with_name(name: String, ipp: &InputPreprocessor) -> Self {
let mut document = Self { let mut document = Self { name, ..Self::default() };
graphene_document: GrapheneDocument::default(), document.graphene_document.root.transform = document.layer_data(&[]).calculate_offset_transform(ipp.viewport_bounds.size() / 2., 0.);
document_undo_history: Vec::new(),
document_redo_history: Vec::new(),
saved_document_identifier: 0,
name,
layer_data: vec![(vec![], LayerData::new(true))].into_iter().collect(),
layer_range_selection_reference: Vec::new(),
movement_handler: MovementMessageHandler::default(),
transform_layer_handler: TransformLayerMessageHandler::default(),
snapping_enabled: true,
view_mode: ViewMode::default(),
};
document.graphene_document.root.transform = document.layerdata(&[]).calculate_offset_transform(ipp.viewport_bounds.size() / 2., 0.);
document document
} }
pub fn with_name_and_content(name: String, serialized_content: String, ipp: &InputPreprocessor) -> Result<Self, EditorError> { pub fn with_name_and_content(name: String, serialized_content: String) -> Result<Self, EditorError> {
let mut document = Self::with_name(name, ipp); match Self::deserialize_document(&serialized_content) {
let internal_document = GrapheneDocument::with_content(&serialized_content); Ok(mut document) => {
match internal_document { document.name = name;
Ok(handle) => {
document.graphene_document = handle;
Ok(document) Ok(document)
} }
Err(DocumentError::InvalidFile(msg)) => Err(EditorError::Document(msg)), Err(DocumentError::InvalidFile(msg)) => Err(EditorError::Document(msg)),
@ -199,7 +203,7 @@ impl DocumentMessageHandler {
if self.graphene_document.layer(path).ok()?.overlay { if self.graphene_document.layer(path).ok()?.overlay {
return None; return None;
} }
self.layer_data(path).selected = true; self.layer_data_mut(path).selected = true;
let data = self.layer_panel_entry(path.to_vec()).ok()?; let data = self.layer_panel_entry(path.to_vec()).ok()?;
(!path.is_empty()).then(|| FrontendMessage::UpdateLayer { data }.into()) (!path.is_empty()).then(|| FrontendMessage::UpdateLayer { data }.into())
} }
@ -244,12 +248,8 @@ impl DocumentMessageHandler {
shapes.collect::<Vec<VectorManipulatorShape>>() shapes.collect::<Vec<VectorManipulatorShape>>()
} }
pub fn layerdata(&self, path: &[LayerId]) -> &LayerData { pub fn create_layer_data(&mut self, path: &[LayerId]) {
self.layer_data.get(path).expect("Layerdata does not exist") self.layer_data.insert(path.to_vec(), LayerData::new(true));
}
pub fn layerdata_mut(&mut self, path: &[LayerId]) -> &mut LayerData {
self.layer_data.entry(path.to_vec()).or_insert_with(|| LayerData::new(true))
} }
pub fn selected_layers(&self) -> impl Iterator<Item = &[LayerId]> { pub fn selected_layers(&self) -> impl Iterator<Item = &[LayerId]> {
@ -265,7 +265,7 @@ impl DocumentMessageHandler {
LayerDataType::Shape(_) => (), LayerDataType::Shape(_) => (),
LayerDataType::Folder(ref folder) => { LayerDataType::Folder(ref folder) => {
path.push(*id); path.push(*id);
if self.layerdata(path).expanded { if self.layer_data(path).expanded {
structure.push(space); structure.push(space);
self.serialize_structure(folder, structure, data, path); self.serialize_structure(folder, structure, data, path);
space = 0; space = 0;
@ -352,8 +352,18 @@ impl DocumentMessageHandler {
self.layers_sorted(Some(false)) self.layers_sorted(Some(false))
} }
pub fn layer_data(&mut self, path: &[LayerId]) -> &mut LayerData { pub fn layer_data(&self, path: &[LayerId]) -> &LayerData {
layer_data(&mut self.layer_data, path) self.layer_data.get(path).expect("Layerdata does not exist")
}
pub fn layer_data_mut(&mut self, path: &[LayerId]) -> &mut LayerData {
Self::layer_data_mut_no_borrow_self(&mut self.layer_data, path)
}
pub fn layer_data_mut_no_borrow_self<'a>(layer_data: &'a mut HashMap<Vec<LayerId>, LayerData>, path: &[LayerId]) -> &'a mut LayerData {
layer_data
.get_mut(path)
.unwrap_or_else(|| panic!("Layer data cannot be found because the path {:?} does not exist", path))
} }
pub fn backup(&mut self, responses: &mut VecDeque<Message>) { pub fn backup(&mut self, responses: &mut VecDeque<Message>) {
@ -431,7 +441,7 @@ impl DocumentMessageHandler {
} }
pub fn layer_panel_entry(&mut self, path: Vec<LayerId>) -> Result<LayerPanelEntry, EditorError> { pub fn layer_panel_entry(&mut self, path: Vec<LayerId>) -> Result<LayerPanelEntry, EditorError> {
let data: LayerData = *layer_data(&mut self.layer_data, &path); let data: LayerData = *self.layer_data_mut(&path);
let layer = self.graphene_document.layer(&path)?; let layer = self.graphene_document.layer(&path)?;
let entry = layer_panel_entry(&data, self.graphene_document.multiply_transforms(&path)?, layer, path); let entry = layer_panel_entry(&data, self.graphene_document.multiply_transforms(&path)?, layer, path);
Ok(entry) Ok(entry)
@ -442,27 +452,23 @@ impl DocumentMessageHandler {
pub fn layer_panel(&mut self, path: &[LayerId]) -> Result<Vec<LayerPanelEntry>, EditorError> { pub fn layer_panel(&mut self, path: &[LayerId]) -> Result<Vec<LayerPanelEntry>, EditorError> {
let folder = self.graphene_document.folder(path)?; let folder = self.graphene_document.folder(path)?;
let paths: Vec<Vec<LayerId>> = folder.layer_ids.iter().map(|id| [path, &[*id]].concat()).collect(); let paths: Vec<Vec<LayerId>> = folder.layer_ids.iter().map(|id| [path, &[*id]].concat()).collect();
let data: Vec<LayerData> = paths.iter().map(|path| *layer_data(&mut self.layer_data, path)).collect(); let entries = paths.iter().rev().filter_map(|path| self.layer_panel_entry_from_path(path)).collect();
let folder = self.graphene_document.folder(path)?;
let entries = folder
.layers()
.iter()
.zip(paths.iter().zip(data))
.rev()
.filter(|(layer, _)| !layer.overlay)
.map(|(layer, (path, data))| {
layer_panel_entry(
&data,
self.graphene_document
.generate_transform_across_scope(path, Some(self.graphene_document.root.transform.inverse()))
.unwrap(),
layer,
path.to_vec(),
)
})
.collect();
Ok(entries) Ok(entries)
} }
pub fn layer_panel_entry_from_path(&self, path: &[LayerId]) -> Option<LayerPanelEntry> {
let layer_data = self.layer_data(path);
let transform = self
.graphene_document
.generate_transform_across_scope(path, Some(self.graphene_document.root.transform.inverse()))
.ok()?;
let layer = self.graphene_document.layer(path).ok()?;
match layer.overlay {
true => None,
false => Some(layer_panel_entry(layer_data, transform, layer, path.to_vec())),
}
}
} }
impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHandler { impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHandler {
@ -471,7 +477,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
match message { match message {
Movement(message) => self Movement(message) => self
.movement_handler .movement_handler
.process_action(message, (layer_data(&mut self.layer_data, &[]), &self.graphene_document, ipp), responses), .process_action(message, (Self::layer_data_mut_no_borrow_self(&mut self.layer_data, &[]), &self.graphene_document, ipp), responses),
TransformLayers(message) => self TransformLayers(message) => self
.transform_layer_handler .transform_layer_handler
.process_action(message, (&mut self.layer_data, &mut self.graphene_document, ipp), responses), .process_action(message, (&mut self.layer_data, &mut self.graphene_document, ipp), responses),
@ -521,7 +527,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
}; };
responses.push_back( responses.push_back(
FrontendMessage::SaveDocument { FrontendMessage::SaveDocument {
document: self.graphene_document.serialize_document(), document: self.serialize_document(),
name, name,
} }
.into(), .into(),
@ -530,8 +536,8 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
CreateEmptyFolder(mut path) => { CreateEmptyFolder(mut path) => {
let id = generate_uuid(); let id = generate_uuid();
path.push(id); path.push(id);
self.layerdata_mut(&path).expanded = true; responses.push_back(DocumentOperation::CreateFolder { path: path.clone() }.into());
responses.push_back(DocumentOperation::CreateFolder { path }.into()) responses.push_back(DocumentMessage::SetLayerExpansion(path, true).into());
} }
GroupSelectedLayers => { GroupSelectedLayers => {
let selected_layers = self.selected_layers(); let selected_layers = self.selected_layers();
@ -574,7 +580,12 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
responses.push_back(DocumentOperation::ToggleLayerVisibility { path }.into()); responses.push_back(DocumentOperation::ToggleLayerVisibility { path }.into());
} }
ToggleLayerExpansion(path) => { ToggleLayerExpansion(path) => {
self.layer_data(&path).expanded ^= true; self.layer_data_mut(&path).expanded ^= true;
responses.push_back(DocumentStructureChanged.into());
responses.push_back(LayerChanged(path).into())
}
SetLayerExpansion(path, is_expanded) => {
self.layer_data_mut(&path).expanded = is_expanded;
responses.push_back(DocumentStructureChanged.into()); responses.push_back(DocumentStructureChanged.into());
responses.push_back(LayerChanged(path).into()) responses.push_back(LayerChanged(path).into())
} }
@ -621,7 +632,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
} else { } else {
if ctrl { if ctrl {
// Toggle selection when holding ctrl // Toggle selection when holding ctrl
let layer = self.layerdata_mut(&selected); let layer = self.layer_data_mut(&selected);
layer.selected = !layer.selected; layer.selected = !layer.selected;
responses.push_back(LayerChanged(selected.clone()).into()); responses.push_back(LayerChanged(selected.clone()).into());
} else { } else {
@ -734,7 +745,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
} }
.into(), .into(),
); );
let root_layerdata = self.layerdata(&[]); let root_layerdata = self.layer_data(&[]);
let scale = 0.5 + ASYMPTOTIC_EFFECT + root_layerdata.scale * SCALE_EFFECT; let scale = 0.5 + ASYMPTOTIC_EFFECT + root_layerdata.scale * SCALE_EFFECT;
let viewport_size = ipp.viewport_bounds.size(); let viewport_size = ipp.viewport_bounds.size();

View File

@ -95,7 +95,7 @@ impl DocumentsMessageHandler {
} }
// TODO Fix how this doesn't preserve tab order upon loading new document from file>load // TODO Fix how this doesn't preserve tab order upon loading new document from file>load
fn load_document(&mut self, new_document: DocumentMessageHandler, document_id: u64, replace_first_empty: bool, responses: &mut VecDeque<Message>) { fn load_document(&mut self, mut new_document: DocumentMessageHandler, document_id: u64, replace_first_empty: bool, responses: &mut VecDeque<Message>) {
// Special case when loading a document on an empty page // Special case when loading a document on an empty page
if replace_first_empty && self.active_document().is_unmodified_default() { if replace_first_empty && self.active_document().is_unmodified_default() {
responses.push_back(DocumentsMessage::CloseDocument(self.active_document_id).into()); responses.push_back(DocumentsMessage::CloseDocument(self.active_document_id).into());
@ -110,6 +110,15 @@ impl DocumentsMessageHandler {
self.document_ids.push(document_id); self.document_ids.push(document_id);
} }
responses.extend(
new_document
.layer_data
.keys()
.filter_map(|path| new_document.layer_panel_entry_from_path(path))
.map(|entry| FrontendMessage::UpdateLayer { data: entry }.into())
.collect::<Vec<_>>(),
);
self.documents.insert(document_id, new_document); self.documents.insert(document_id, new_document);
// Send the new list of document tab names // Send the new list of document tab names
@ -273,7 +282,7 @@ impl MessageHandler<DocumentsMessage, &InputPreprocessor> for DocumentsMessageHa
document, document,
document_is_saved, document_is_saved,
} => { } => {
let document = DocumentMessageHandler::with_name_and_content(document_name, document, ipp); let document = DocumentMessageHandler::with_name_and_content(document_name, document);
match document { match document {
Ok(mut document) => { Ok(mut document) => {
document.set_save_state(document_is_saved); document.set_save_state(document_is_saved);
@ -307,7 +316,7 @@ impl MessageHandler<DocumentsMessage, &InputPreprocessor> for DocumentsMessageHa
let document = self.documents.get(&id).unwrap(); let document = self.documents.get(&id).unwrap();
responses.push_back( responses.push_back(
FrontendMessage::AutoSaveDocument { FrontendMessage::AutoSaveDocument {
document: document.graphene_document.serialize_document(), document: document.serialize_document(),
details: FrontendDocumentDetails { details: FrontendDocumentDetails {
is_saved: document.is_saved(), is_saved: document.is_saved(),
id, id,

View File

@ -35,10 +35,6 @@ impl LayerData {
} }
} }
pub fn layer_data<'a>(layer_data: &'a mut HashMap<Vec<LayerId>, LayerData>, path: &[LayerId]) -> &'a mut LayerData {
layer_data.get_mut(path).expect(&format!("Layer data cannot be found because the path {:?} does not exist", path))
}
pub fn layer_panel_entry(layer_data: &LayerData, transform: DAffine2, layer: &Layer, path: Vec<LayerId>) -> LayerPanelEntry { pub fn layer_panel_entry(layer_data: &LayerData, transform: DAffine2, layer: &Layer, path: Vec<LayerId>) -> LayerPanelEntry {
let layer_type: LayerType = (&layer.data).into(); let layer_type: LayerType = (&layer.data).into();
let name = layer.name.clone().unwrap_or_else(|| format!("Unnamed {}", layer_type)); let name = layer.name.clone().unwrap_or_else(|| format!("Unnamed {}", layer_type));

View File

@ -3,6 +3,7 @@ mod document_message_handler;
pub mod layer_panel; pub mod layer_panel;
mod movement_handler; mod movement_handler;
mod transform_layer_handler; mod transform_layer_handler;
mod vectorize_layerdata;
#[doc(inline)] #[doc(inline)]
pub use document_file::LayerData; pub use document_file::LayerData;

View File

@ -0,0 +1,25 @@
/// Necessary because serde can't serialize hashmaps when the keys don't implement display.
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::iter::FromIterator;
pub fn serialize<'a, T, K, V, S>(target: T, ser: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: IntoIterator<Item = (&'a K, &'a V)>,
K: Serialize + 'a,
V: Serialize + 'a,
{
let container: Vec<_> = target.into_iter().collect();
serde::Serialize::serialize(&container, ser)
}
pub fn deserialize<'de, T, K, V, D>(des: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
T: FromIterator<(K, V)>,
K: Deserialize<'de>,
V: Deserialize<'de>,
{
let container: Vec<_> = serde::Deserialize::deserialize(des)?;
Ok(T::from_iter(container.into_iter()))
}

View File

@ -17,5 +17,4 @@ kurbo = { git = "https://github.com/linebender/kurbo.git", features = [
"serde", "serde",
] } ] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }
glam = { version = "0.17", features = ["serde"] } glam = { version = "0.17", features = ["serde"] }

View File

@ -34,10 +34,6 @@ impl Default for Document {
} }
impl Document { impl Document {
pub fn with_content(serialized_content: &str) -> Result<Self, DocumentError> {
serde_json::from_str(serialized_content).map_err(|e| DocumentError::InvalidFile(e.to_string()))
}
/// Wrapper around render, that returns the whole document as a Response. /// Wrapper around render, that returns the whole document as a Response.
pub fn render_root(&mut self, mode: ViewMode) -> String { pub fn render_root(&mut self, mode: ViewMode) -> String {
self.root.render(&mut vec![], mode); self.root.render(&mut vec![], mode);
@ -48,12 +44,6 @@ impl Document {
self.state_identifier.finish() self.state_identifier.finish()
} }
pub fn serialize_document(&self) -> String {
let val = serde_json::to_string(self);
// We fully expect the serialization to succeed
val.unwrap()
}
/// Checks whether each layer under `path` intersects with the provided `quad` and adds all intersection layers as paths to `intersections`. /// 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<LayerId>, intersections: &mut Vec<Vec<LayerId>>) { pub fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>) {
self.layer(path).unwrap().intersects_quad(quad, path, intersections); self.layer(path).unwrap().intersects_quad(quad, path, intersections);

View File

@ -105,6 +105,10 @@ impl Layer {
} }
} }
pub fn iter(&self) -> LayerIter<'_> {
LayerIter { stack: vec![self] }
}
pub fn render(&mut self, transforms: &mut Vec<DAffine2>, view_mode: ViewMode) -> &str { pub fn render(&mut self, transforms: &mut Vec<DAffine2>, view_mode: ViewMode) -> &str {
if !self.visible { if !self.visible {
return ""; return "";
@ -179,3 +183,40 @@ impl Clone for Layer {
} }
} }
} }
impl<'a> IntoIterator for &'a Layer {
type Item = &'a Layer;
type IntoIter = LayerIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Debug)]
pub struct LayerIter<'a> {
pub stack: Vec<&'a Layer>,
}
impl Default for LayerIter<'_> {
fn default() -> Self {
Self { stack: vec![] }
}
}
impl<'a> Iterator for LayerIter<'a> {
type Item = &'a Layer;
fn next(&mut self) -> Option<Self::Item> {
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,
}
}
}