From 5ed5f55dfb6cf918e2e1993bf71390d8c475656f Mon Sep 17 00:00:00 2001 From: Luke Franceschini Date: Fri, 21 Jun 2024 02:45:57 -0400 Subject: [PATCH] Fix bounding boxes of VectorData with rotations in several nodes (#1792) The VectorData::bounding_box() function does not apply the VectorData's transform, and bounding_box_with_transform() must be used instead of applying the transform to the computed bounding box. It may make sense in the future to move the current version of bounding_box to a private method bounding_box_no_transform() and have the public bounding_box call bounding_box_with_transform so that external code doesn't need to know about how the VectorData stores it's data. This fixes the following nodes: - Repeat - Circular Repeat - Bounding Box Also add a test to the bounding box node for this situation. --- node-graph/gcore/src/vector/vector_nodes.rs | 25 +++++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/node-graph/gcore/src/vector/vector_nodes.rs b/node-graph/gcore/src/vector/vector_nodes.rs index 86e50ec5..ed1a8ac3 100644 --- a/node-graph/gcore/src/vector/vector_nodes.rs +++ b/node-graph/gcore/src/vector/vector_nodes.rs @@ -76,7 +76,9 @@ fn repeat_vector_data(vector_data: VectorData, direction: DVec2, angle: f64, ins // Repeat the vector data let mut result = VectorData::empty(); - let Some(bounding_box) = vector_data.bounding_box() else { return vector_data }; + let Some(bounding_box) = vector_data.bounding_box_with_transform(vector_data.transform) else { + return vector_data; + }; let center = (bounding_box[0] + bounding_box[1]) / 2.; for i in 0..instances { @@ -108,7 +110,9 @@ fn circular_repeat_vector_data(vector_data: VectorData, angle_offset: f64, radiu let mut result = VectorData::empty(); - let Some(bounding_box) = vector_data.bounding_box() else { return vector_data }; + let Some(bounding_box) = vector_data.bounding_box_with_transform(vector_data.transform) else { + return vector_data; + }; let center = (bounding_box[0] + bounding_box[1]) / 2.; let base_transform = DVec2::new(0., radius) - center; @@ -128,11 +132,8 @@ pub struct BoundingBoxNode; #[node_macro::node_fn(BoundingBoxNode)] fn generate_bounding_box(vector_data: VectorData) -> VectorData { - let bounding_box = vector_data.bounding_box().unwrap(); - VectorData::from_subpath(Subpath::new_rect( - vector_data.transform.transform_point2(bounding_box[0]), - vector_data.transform.transform_point2(bounding_box[1]), - )) + let bounding_box = vector_data.bounding_box_with_transform(vector_data.transform).unwrap(); + VectorData::from_subpath(Subpath::new_rect(bounding_box[0], bounding_box[1])) } #[derive(Debug, Clone, Copy)] @@ -652,6 +653,16 @@ mod test { assert_eq!(bounding_box.region_bezier_paths().count(), 1); let subpath = bounding_box.region_bezier_paths().next().unwrap().1; assert_eq!(&subpath.anchors()[..4], &[DVec2::NEG_ONE, DVec2::new(1., -1.), DVec2::ONE, DVec2::new(-1., 1.),]); + + // test a VectorData with non-zero rotation + let mut square = VectorData::from_subpath(Subpath::new_rect(DVec2::NEG_ONE, DVec2::ONE)); + square.transform *= DAffine2::from_angle(core::f64::consts::FRAC_PI_4); + let bounding_box = BoundingBoxNode.eval(square); + assert_eq!(bounding_box.region_bezier_paths().count(), 1); + let subpath = bounding_box.region_bezier_paths().next().unwrap().1; + let sqrt2 = core::f64::consts::SQRT_2; + let sqrt2_bounding_box = [DVec2::new(-sqrt2, -sqrt2), DVec2::new(sqrt2, -sqrt2), DVec2::new(sqrt2, sqrt2), DVec2::new(-sqrt2, sqrt2)]; + assert!(subpath.anchors()[..4].iter().zip(sqrt2_bounding_box).all(|(p1, p2)| p1.abs_diff_eq(p2, f64::EPSILON))); } #[tokio::test] async fn copy_to_points() {