diff --git a/editor/src/consts.rs b/editor/src/consts.rs index 4f3412133d..2f4e3ab313 100644 --- a/editor/src/consts.rs +++ b/editor/src/consts.rs @@ -1,5 +1,4 @@ use graphene::color::Color; -use graphene::layers::text_layer::Font; // Viewport pub const VIEWPORT_ZOOM_WHEEL_RATE: f64 = 1. / 600.; diff --git a/editor/src/document/artboard_message_handler.rs b/editor/src/document/artboard_message_handler.rs index 32a1d30424..b4ca9886ec 100644 --- a/editor/src/document/artboard_message_handler.rs +++ b/editor/src/document/artboard_message_handler.rs @@ -87,7 +87,7 @@ impl MessageHandler for ArtboardMessageHandler { } else { responses.push_back( FrontendMessage::UpdateDocumentArtboards { - svg: self.artboards_graphene_document.render_root(ViewMode::Normal, font_cache), + svg: self.artboards_graphene_document.render_root(ViewMode::Normal, font_cache, None), } .into(), ); diff --git a/editor/src/document/document_message_handler.rs b/editor/src/document/document_message_handler.rs index aeb6fae42f..7bb5fe5050 100644 --- a/editor/src/document/document_message_handler.rs +++ b/editor/src/document/document_message_handler.rs @@ -873,8 +873,7 @@ impl MessageHandler { - self.overlays_message_handler.process_action(message, (self.overlays_visible, font_cache), responses); - // responses.push_back(OverlaysMessage::RenderOverlays.into()); + self.overlays_message_handler.process_action(message, (self.overlays_visible, font_cache, ipp), responses); } #[remain::unsorted] TransformLayers(message) => { @@ -1060,7 +1059,7 @@ impl MessageHandler file_name + file_suffix, }; - let rendered = self.graphene_document.render_root(self.view_mode, font_cache); + let rendered = self.graphene_document.render_root(self.view_mode, font_cache, None); let document = format!( r#"{}{}"#, bbox[0].x, bbox[0].y, size.x, size.y, size.x, size.y, "\n", rendered @@ -1100,7 +1099,6 @@ impl MessageHandler { - let _ = self.graphene_document.render_root(self.view_mode, font_cache); let affected_layer_path = affected_folder_path; responses.extend([LayerChanged { affected_layer_path }.into(), DocumentStructureChanged.into()]); } @@ -1220,7 +1218,7 @@ impl MessageHandler { responses.push_back( FrontendMessage::UpdateDocumentArtwork { - svg: self.graphene_document.render_root(self.view_mode, font_cache), + svg: self.graphene_document.render_root(self.view_mode, font_cache, Some(ipp.document_bounds())), } .into(), ); diff --git a/editor/src/document/layer_panel.rs b/editor/src/document/layer_panel.rs index eb6ff31406..b96cb5c713 100644 --- a/editor/src/document/layer_panel.rs +++ b/editor/src/document/layer_panel.rs @@ -27,7 +27,7 @@ pub fn layer_panel_entry(layer_metadata: &LayerMetadata, transform: DAffine2, la let mut thumbnail = String::new(); let mut svg_defs = String::new(); - layer.data.clone().render(&mut thumbnail, &mut svg_defs, &mut vec![transform], ViewMode::Normal, font_cache); + layer.data.clone().render(&mut thumbnail, &mut svg_defs, &mut vec![transform], ViewMode::Normal, font_cache, None); let transform = transform.to_cols_array().iter().map(ToString::to_string).collect::>().join(","); let thumbnail = if let [(x_min, y_min), (x_max, y_max)] = arr.as_slice() { format!( diff --git a/editor/src/document/overlays_message_handler.rs b/editor/src/document/overlays_message_handler.rs index 91ea809fbb..cd4adc3304 100644 --- a/editor/src/document/overlays_message_handler.rs +++ b/editor/src/document/overlays_message_handler.rs @@ -1,3 +1,4 @@ +use crate::input::InputPreprocessorMessageHandler; use crate::message_prelude::*; use graphene::document::Document as GrapheneDocument; @@ -9,9 +10,9 @@ pub struct OverlaysMessageHandler { pub overlays_graphene_document: GrapheneDocument, } -impl MessageHandler for OverlaysMessageHandler { +impl MessageHandler for OverlaysMessageHandler { #[remain::check] - fn process_action(&mut self, message: OverlaysMessage, (overlays_visible, font_cache): (bool, &FontCache), responses: &mut VecDeque) { + fn process_action(&mut self, message: OverlaysMessage, (overlays_visible, font_cache, ipp): (bool, &FontCache, &InputPreprocessorMessageHandler), responses: &mut VecDeque) { use OverlaysMessage::*; #[remain::sorted] @@ -31,7 +32,7 @@ impl MessageHandler for OverlaysMessageHand responses.push_back( FrontendMessage::UpdateDocumentOverlays { svg: if overlays_visible { - self.overlays_graphene_document.render_root(ViewMode::Normal, font_cache) + self.overlays_graphene_document.render_root(ViewMode::Normal, font_cache, Some(ipp.document_bounds())) } else { String::from("") }, diff --git a/editor/src/input/input_preprocessor_message_handler.rs b/editor/src/input/input_preprocessor_message_handler.rs index 331145327d..d5bd6c19dc 100644 --- a/editor/src/input/input_preprocessor_message_handler.rs +++ b/editor/src/input/input_preprocessor_message_handler.rs @@ -6,6 +6,8 @@ use crate::message_prelude::*; #[doc(inline)] pub use graphene::DocumentResponse; +use glam::DVec2; + #[derive(Debug, Default)] pub struct InputPreprocessorMessageHandler { pub keyboard: KeyStates, @@ -152,4 +154,9 @@ impl InputPreprocessorMessageHandler { responses.push_back(InputMapperMessage::KeyDown(key).into()); } } + + pub fn document_bounds(&self) -> [DVec2; 2] { + // ipp bounds are relative to the entire screen + [(0., 0.).into(), self.viewport_bounds.bottom_right - self.viewport_bounds.top_left] + } } diff --git a/graphene/src/document.rs b/graphene/src/document.rs index b6e59dfef4..7304500b00 100644 --- a/graphene/src/document.rs +++ b/graphene/src/document.rs @@ -42,10 +42,10 @@ impl Default for Document { impl Document { /// Wrapper around render, that returns the whole document as a Response. - pub fn render_root(&mut self, mode: ViewMode, font_cache: &FontCache) -> String { + pub fn render_root(&mut self, mode: ViewMode, font_cache: &FontCache, culling_bounds: Option<[DVec2; 2]>) -> String { let mut svg_defs = String::from(""); - self.root.render(&mut vec![], mode, &mut svg_defs, font_cache); + self.root.render(&mut vec![], mode, &mut svg_defs, font_cache, culling_bounds); svg_defs.push_str(""); diff --git a/graphene/src/layers/folder_layer.rs b/graphene/src/layers/folder_layer.rs index 71825819a4..6bbdd0b19e 100644 --- a/graphene/src/layers/folder_layer.rs +++ b/graphene/src/layers/folder_layer.rs @@ -22,9 +22,9 @@ pub struct FolderLayer { } impl LayerData for FolderLayer { - fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, view_mode: ViewMode, font_cache: &FontCache) { + fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, view_mode: ViewMode, font_cache: &FontCache, culling_bounds: Option<[DVec2; 2]>) { for layer in &mut self.layers { - let _ = writeln!(svg, "{}", layer.render(transforms, view_mode, svg_defs, font_cache)); + let _ = writeln!(svg, "{}", layer.render(transforms, view_mode, svg_defs, font_cache, culling_bounds)); } } diff --git a/graphene/src/layers/image_layer.rs b/graphene/src/layers/image_layer.rs index 7a1ae7a466..df023da606 100644 --- a/graphene/src/layers/image_layer.rs +++ b/graphene/src/layers/image_layer.rs @@ -24,7 +24,7 @@ pub struct ImageLayer { } impl LayerData for ImageLayer { - fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec, view_mode: ViewMode, _font_cache: &FontCache) { + fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec, view_mode: ViewMode, _font_cache: &FontCache, _culling_bounds: Option<[DVec2; 2]>) { let transform = self.transform(transforms, view_mode); let inverse = transform.inverse(); diff --git a/graphene/src/layers/layer_info.rs b/graphene/src/layers/layer_info.rs index 9d7dca52ee..2b605fd4b4 100644 --- a/graphene/src/layers/layer_info.rs +++ b/graphene/src/layers/layer_info.rs @@ -61,7 +61,7 @@ pub trait LayerData { /// let mut svg = String::new(); /// /// // Render the shape without any transforms, in normal view mode - /// shape.render(&mut svg, &mut String::new(), &mut vec![], ViewMode::Normal, &Default::default()); + /// shape.render(&mut svg, &mut String::new(), &mut vec![], ViewMode::Normal, &Default::default(), None); /// /// assert_eq!( /// svg, @@ -70,7 +70,7 @@ pub trait LayerData { /// " /// ); /// ``` - fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, view_mode: ViewMode, font_cache: &FontCache); + fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, view_mode: ViewMode, font_cache: &FontCache, culling_bounds: Option<[DVec2; 2]>); /// Determine the layers within this layer that intersect a given quad. /// # Example @@ -117,8 +117,8 @@ pub trait LayerData { } impl LayerData for LayerDataType { - fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, view_mode: ViewMode, font_cache: &FontCache) { - self.inner_mut().render(svg, svg_defs, transforms, view_mode, font_cache) + fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, view_mode: ViewMode, font_cache: &FontCache, viewport_bounds: Option<[DVec2; 2]>) { + self.inner_mut().render(svg, svg_defs, transforms, view_mode, font_cache, viewport_bounds) } fn intersects_quad(&self, quad: Quad, path: &mut Vec, intersections: &mut Vec>, font_cache: &FontCache) { @@ -223,16 +223,29 @@ impl Layer { LayerIter { stack: vec![self] } } - pub fn render(&mut self, transforms: &mut Vec, view_mode: ViewMode, svg_defs: &mut String, font_cache: &FontCache) -> &str { + pub fn render(&mut self, transforms: &mut Vec, view_mode: ViewMode, svg_defs: &mut String, font_cache: &FontCache, culling_bounds: Option<[DVec2; 2]>) -> &str { if !self.visible { return ""; } + transforms.push(self.transform); + if let Some(viewport_bounds) = culling_bounds { + if let Some(bounding_box) = self.data.bounding_box(transforms.iter().cloned().reduce(|a, b| a * b).unwrap_or(DAffine2::IDENTITY), font_cache) { + let is_overlapping = + viewport_bounds[0].x < bounding_box[1].x && bounding_box[0].x < viewport_bounds[1].x && viewport_bounds[0].y < bounding_box[1].y && bounding_box[0].y < viewport_bounds[1].y; + if !is_overlapping { + transforms.pop(); + self.cache.clear(); + self.cache_dirty = true; + return ""; + } + } + } + if self.cache_dirty { - transforms.push(self.transform); self.thumbnail_cache.clear(); self.svg_defs_cache.clear(); - self.data.render(&mut self.thumbnail_cache, &mut self.svg_defs_cache, transforms, view_mode, font_cache); + self.data.render(&mut self.thumbnail_cache, &mut self.svg_defs_cache, transforms, view_mode, font_cache, culling_bounds); self.cache.clear(); let _ = writeln!(self.cache, r#", view_mode: ViewMode, _font_cache: &FontCache) { + fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, view_mode: ViewMode, _font_cache: &FontCache, _culling_bounds: Option<[DVec2; 2]>) { let mut path = self.path.clone(); let kurbo::Rect { x0, y0, x1, y1 } = path.bounding_box(); @@ -89,7 +90,7 @@ impl ShapeLayer { (_, -1) => 0, (_, x) => (transforms.len() as i32 - x).max(0) as usize, }; - transforms.iter().skip(start).cloned().reduce(|a, b| a * b).unwrap_or(DAffine2::IDENTITY) + transforms.iter().skip(start).fold(DAffine2::IDENTITY, |a, b| a * *b) } pub fn from_bez_path(bez_path: BezPath, style: PathStyle, closed: bool) -> Self { diff --git a/graphene/src/layers/text_layer.rs b/graphene/src/layers/text_layer.rs index 5b88f55a16..1829866c57 100644 --- a/graphene/src/layers/text_layer.rs +++ b/graphene/src/layers/text_layer.rs @@ -37,7 +37,7 @@ pub struct TextLayer { } impl LayerData for TextLayer { - fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, view_mode: ViewMode, font_cache: &FontCache) { + fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec, view_mode: ViewMode, font_cache: &FontCache, _culling_bounds: Option<[DVec2; 2]>) { let transform = self.transform(transforms, view_mode); let inverse = transform.inverse();