diff --git a/core/document/src/document.rs b/core/document/src/document.rs index 17b1baac..e12575db 100644 --- a/core/document/src/document.rs +++ b/core/document/src/document.rs @@ -141,7 +141,7 @@ impl Document { } /// Returns a reference to the layer or folder at the path. Does not return an error for root - pub fn document_layer(&mut self, path: &[LayerId]) -> Result<&Layer, DocumentError> { + pub fn document_layer(&self, path: &[LayerId]) -> Result<&Layer, DocumentError> { if path.is_empty() { return Ok(&self.root); } @@ -227,13 +227,20 @@ impl Document { Ok(()) } - pub fn layer_axis_aligned_bounding_box(&self, path: &[LayerId]) -> Result<[DVec2; 2], DocumentError> { - let layer = self.layer(path)?; - Ok(layer.bounding_box(self.root.transform * layer.transform, layer.style)) + pub fn layer_axis_aligned_bounding_box(&self, path: &[LayerId]) -> Result, DocumentError> { + // TODO: Replace with functions of the transform api + if let &[] = path { + // Special case for root. Root's local is the documents global, so we avoid transforming its transform by itself. + self.layer_local_bounding_box(path) + } else { + let layer = self.document_layer(path)?; + Ok(layer.bounding_box(self.root.transform * layer.transform, layer.style)) + } } - pub fn layer_local_bounding_box(&self, path: &[LayerId]) -> Result<[DVec2; 2], DocumentError> { - let layer = self.layer(path)?; + pub fn layer_local_bounding_box(&self, path: &[LayerId]) -> Result, DocumentError> { + // TODO: Replace with functions of the transform api + let layer = self.document_layer(path)?; Ok(layer.bounding_box(layer.transform, layer.style)) } diff --git a/core/document/src/layers/folder.rs b/core/document/src/layers/folder.rs index 983482ea..5ca3754b 100644 --- a/core/document/src/layers/folder.rs +++ b/core/document/src/layers/folder.rs @@ -101,6 +101,35 @@ impl Folder { _ => None, } } + + pub fn bounding_box(&self, transform: glam::DAffine2) -> Option<[DVec2; 2]> { + let mut layers_non_empty_bounding_boxes = self.layers.iter().filter_map(|layer| layer.bounding_box(transform * layer.transform, layer.style)).peekable(); + + if layers_non_empty_bounding_boxes.peek().is_none() { + return None; + } + + let mut x_min = f64::MAX; + let mut y_min = f64::MAX; + let mut x_max = f64::MIN; + let mut y_max = f64::MIN; + + for [bounding_box_min, bounding_box_max] in layers_non_empty_bounding_boxes { + if bounding_box_min.x < x_min { + x_min = bounding_box_min.x + } + if bounding_box_min.y < y_min { + y_min = bounding_box_min.y + } + if bounding_box_max.x > x_max { + x_max = bounding_box_max.x + } + if bounding_box_max.y > y_max { + y_max = bounding_box_max.y + } + } + return Some([DVec2::new(x_min, y_min), DVec2::new(x_max, y_max)]); + } } impl Default for Folder { diff --git a/core/document/src/layers/mod.rs b/core/document/src/layers/mod.rs index 5d1d1e96..ed1d46c8 100644 --- a/core/document/src/layers/mod.rs +++ b/core/document/src/layers/mod.rs @@ -175,8 +175,12 @@ impl Layer { self.data.to_kurbo_path(self.transform, self.style) } - pub fn bounding_box(&self, transform: glam::DAffine2, style: style::PathStyle) -> [DVec2; 2] { - self.data.bounding_box(transform, style) + pub fn bounding_box(&self, transform: glam::DAffine2, style: style::PathStyle) -> Option<[DVec2; 2]> { + if let Ok(folder) = self.as_folder() { + folder.bounding_box(transform) + } else { + Some(self.data.bounding_box(transform, style)) + } } pub fn as_folder_mut(&mut self) -> Result<&mut Folder, DocumentError> {