From fb6178b1787de3f38f87d3896cf871020ea2f899 Mon Sep 17 00:00:00 2001 From: Tanish Manku Date: Wed, 4 Nov 2026 14:31:19 -0700 Subject: [PATCH] fix(doc): a rotated MINSERT array's extents reach its side copies The extents took the first copy and the far corner copy only. Turned, the grid's other two corner copies lie outside that box, so ZOOM Extents cut them off. All four corner copies count now, and the counts are clamped to the 10,000 columns and rows the renderer draws. Found by CodeRabbit on #2756. --- crates/doc/src/extents.rs | 44 +++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/crates/doc/src/extents.rs b/crates/doc/src/extents.rs index 4f40a148c..e2a748606 100644 --- a/crates/doc/src/extents.rs +++ b/crates/doc/src/extents.rs @@ -187,10 +187,15 @@ fn bounds_in<'a>(d: &'a Drawing, e: &Entity, depth: usize, blocks: &mut HashMap< b = Bounds2::from_points(c.polygon().into_iter().map(|p| m.apply(p))); } if ins.cols > 1 || ins.rows > 1 { - let off = - Vec2::new(ins.col_spacing * f64::from(ins.cols.saturating_sub(1)), ins.row_spacing * f64::from(ins.rows.saturating_sub(1))) - .rotate(ins.rotation); - b = b.union(&Bounds2::from_points(b.corners().map(|c| c + off))); + // The grid's four corner copies (as many as are drawn): turned, the far corner + // alone misses the side ones. + let (cols, rows) = + (f64::from(ins.cols.clamp(1, 10_000).saturating_sub(1)), f64::from(ins.rows.clamp(1, 10_000).saturating_sub(1))); + let one = b; + for (c, r) in [(cols, 0.0), (0.0, rows), (cols, rows)] { + let off = Vec2::new(ins.col_spacing * c, ins.row_spacing * r).rotate(ins.rotation); + b = b.union(&Bounds2::from_points(one.corners().map(|p| p + off))); + } } } for a in &ins.attribs { @@ -522,4 +527,35 @@ mod tests { assert!(extents.width() > drawn.width()); } } + + /// A turned array of references reaches its side copies, not only the first and the last; + /// a hostile count measures only the copies that are drawn. + #[test] + fn a_rotated_array_counts_every_corner_copy() { + let mut d = Drawing::new_metric(); + let mut b = crate::Block::new("B"); + b.entities.push(Entity::new(crate::Handle(1), EntityKind::Line(crate::Line { a: Vec3::ZERO, b: Vec3::new(1.0, 0.0, 0.0) }))); + d.blocks.insert("B".into(), std::sync::Arc::new(b)); + let array = |cols: u32, rows: u32, rotation: f64| { + let ins = crate::Insert { + block: "B".into(), + scale: Vec3::new(1.0, 1.0, 1.0), + rotation, + cols, + rows, + col_spacing: 10.0, + row_spacing: 10.0, + ..crate::Insert::default() + }; + Entity::new(crate::Handle(2), EntityKind::Insert(ins)) + }; + // Turned 45°: the copies sit at (0, 0), (7.07, 7.07), (-7.07, 7.07) and (0, 14.14). + let b = entity_bounds(&d, &array(2, 2, std::f64::consts::FRAC_PI_4), 0); + let side = 10.0 * std::f64::consts::FRAC_1_SQRT_2; + assert!((b.min.x + side).abs() < 1e-9 && b.max.x >= side, "{b:?}"); + assert!((b.max.y - 2.0 * side - std::f64::consts::FRAC_1_SQRT_2).abs() < 1e-9, "{b:?}"); + // The renderer draws at most 10,000 columns. + let b = entity_bounds(&d, &array(u32::MAX, 1, 0.0), 0); + assert!((b.max.x - (10.0 * 9_999.0 + 1.0)).abs() < 1e-6, "{b:?}"); + } }