Fix viewport culling with nested layers (#939)
* Fix viewport culling with nested layers * Clean up naming
This commit is contained in:
parent
df44e8d978
commit
7cc1a192cb
|
|
@ -6,7 +6,6 @@ use crate::{DocumentError, LayerId};
|
|||
|
||||
use glam::DVec2;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Write;
|
||||
|
||||
/// A layer that encapsulates other layers, including potentially more folders.
|
||||
/// The contained layers are rendered in the same order they are
|
||||
|
|
@ -22,10 +21,14 @@ pub struct FolderLayer {
|
|||
}
|
||||
|
||||
impl LayerData for FolderLayer {
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData) {
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData) -> bool {
|
||||
let mut any_child_requires_refresh = false;
|
||||
for layer in &mut self.layers {
|
||||
let _ = writeln!(svg, "{}", layer.render(transforms, svg_defs, render_data));
|
||||
let (svg_value, requires_refresh) = layer.render(transforms, svg_defs, render_data);
|
||||
*svg += svg_value;
|
||||
any_child_requires_refresh = any_child_requires_refresh || requires_refresh;
|
||||
}
|
||||
!any_child_requires_refresh
|
||||
}
|
||||
|
||||
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) {
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ pub struct ImageLayer {
|
|||
}
|
||||
|
||||
impl LayerData for ImageLayer {
|
||||
fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) {
|
||||
fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) -> bool {
|
||||
let transform = self.transform(transforms, render_data.view_mode);
|
||||
let inverse = transform.inverse();
|
||||
|
||||
if !inverse.is_finite() {
|
||||
let _ = write!(svg, "<!-- SVG shape has an invalid transform -->");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
let _ = writeln!(svg, r#"<g transform="matrix("#);
|
||||
|
|
@ -53,6 +53,8 @@ impl LayerData for ImageLayer {
|
|||
self.blob_url.as_ref().unwrap_or(&String::new())
|
||||
);
|
||||
let _ = svg.write_str("</g>");
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> {
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ impl<'a> TryFrom<&'a Layer> for &'a Subpath {
|
|||
|
||||
/// Defines shared behavior for every layer type.
|
||||
pub trait LayerData {
|
||||
/// Render the layer as an SVG tag to a given string.
|
||||
/// Render the layer as an SVG tag to a given string, returning a boolean to indicate if a redraw is required next frame.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
|
@ -143,7 +143,7 @@ pub trait LayerData {
|
|||
/// </g>"
|
||||
/// );
|
||||
/// ```
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData);
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData) -> bool;
|
||||
|
||||
/// Determine the layers within this layer that intersect a given quad.
|
||||
/// # Example
|
||||
|
|
@ -190,7 +190,7 @@ pub trait LayerData {
|
|||
}
|
||||
|
||||
impl LayerData for LayerDataType {
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData) {
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData) -> bool {
|
||||
self.inner_mut().render(svg, svg_defs, transforms, render_data)
|
||||
}
|
||||
|
||||
|
|
@ -304,12 +304,15 @@ impl Layer {
|
|||
LayerIter { stack: vec![self] }
|
||||
}
|
||||
|
||||
pub fn render(&mut self, transforms: &mut Vec<DAffine2>, svg_defs: &mut String, render_data: RenderData) -> &str {
|
||||
/// Renders the layer, returning the result and if a redraw is required
|
||||
pub fn render(&mut self, transforms: &mut Vec<DAffine2>, svg_defs: &mut String, render_data: RenderData) -> (&str, bool) {
|
||||
if !self.visible {
|
||||
return "";
|
||||
return ("", false);
|
||||
}
|
||||
|
||||
transforms.push(self.transform);
|
||||
|
||||
// Skip rendering if outside the viewport bounds
|
||||
if let Some(viewport_bounds) = render_data.culling_bounds {
|
||||
if let Some(bounding_box) = self
|
||||
.data
|
||||
|
|
@ -321,15 +324,17 @@ impl Layer {
|
|||
transforms.pop();
|
||||
self.cache.clear();
|
||||
self.cache_dirty = true;
|
||||
return "";
|
||||
return ("", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut requires_redraw = false;
|
||||
|
||||
if self.cache_dirty {
|
||||
self.thumbnail_cache.clear();
|
||||
self.svg_defs_cache.clear();
|
||||
self.data.render(&mut self.thumbnail_cache, &mut self.svg_defs_cache, transforms, render_data);
|
||||
requires_redraw = self.data.render(&mut self.thumbnail_cache, &mut self.svg_defs_cache, transforms, render_data);
|
||||
|
||||
self.cache.clear();
|
||||
let _ = writeln!(self.cache, r#"<g transform="matrix("#);
|
||||
|
|
@ -346,10 +351,16 @@ impl Layer {
|
|||
|
||||
self.cache_dirty = false;
|
||||
}
|
||||
|
||||
transforms.pop();
|
||||
svg_defs.push_str(&self.svg_defs_cache);
|
||||
|
||||
self.cache.as_str()
|
||||
// If a redraw is required then set the cache to dirty.
|
||||
if requires_redraw {
|
||||
self.cache_dirty = true;
|
||||
}
|
||||
|
||||
(self.cache.as_str(), requires_redraw)
|
||||
}
|
||||
|
||||
pub fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ pub struct ImageData {
|
|||
}
|
||||
|
||||
impl LayerData for NodeGraphFrameLayer {
|
||||
fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) {
|
||||
fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) -> bool {
|
||||
let transform = self.transform(transforms, render_data.view_mode);
|
||||
let inverse = transform.inverse();
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ impl LayerData for NodeGraphFrameLayer {
|
|||
|
||||
if !inverse.is_finite() {
|
||||
let _ = write!(svg, "<!-- SVG shape has an invalid transform -->");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
let _ = writeln!(svg, r#"<g transform="matrix("#);
|
||||
|
|
@ -75,6 +75,8 @@ impl LayerData for NodeGraphFrameLayer {
|
|||
);
|
||||
|
||||
let _ = svg.write_str(r#"</g>"#);
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ pub struct ShapeLayer {
|
|||
}
|
||||
|
||||
impl LayerData for ShapeLayer {
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) {
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) -> bool {
|
||||
let mut subpath = self.shape.clone();
|
||||
|
||||
let layer_bounds = subpath.bounding_box().unwrap_or_default();
|
||||
|
|
@ -36,7 +36,7 @@ impl LayerData for ShapeLayer {
|
|||
let inverse = transform.inverse();
|
||||
if !inverse.is_finite() {
|
||||
let _ = write!(svg, "<!-- SVG shape has an invalid transform -->");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
subpath.apply_affine(transform);
|
||||
|
||||
|
|
@ -54,6 +54,8 @@ impl LayerData for ShapeLayer {
|
|||
self.style.render(render_data.view_mode, svg_defs, transform, layer_bounds, transformed_bounds)
|
||||
);
|
||||
let _ = svg.write_str("</g>");
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> {
|
||||
|
|
|
|||
|
|
@ -34,13 +34,13 @@ pub struct TextLayer {
|
|||
}
|
||||
|
||||
impl LayerData for TextLayer {
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) {
|
||||
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: RenderData) -> bool {
|
||||
let transform = self.transform(transforms, render_data.view_mode);
|
||||
let inverse = transform.inverse();
|
||||
|
||||
if !inverse.is_finite() {
|
||||
let _ = write!(svg, "<!-- SVG shape has an invalid transform -->");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
let _ = writeln!(svg, r#"<g transform="matrix("#);
|
||||
|
|
@ -85,6 +85,8 @@ impl LayerData for TextLayer {
|
|||
);
|
||||
}
|
||||
let _ = svg.write_str("</g>");
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn bounding_box(&self, transform: glam::DAffine2, font_cache: &FontCache) -> Option<[DVec2; 2]> {
|
||||
|
|
|
|||
Loading…
Reference in New Issue