From f80130560edd32a0a9181d3ad5a1b83fe936d61a Mon Sep 17 00:00:00 2001 From: Christopher Overbeck Date: Tue, 15 Sep 2026 12:32:57 -0500 Subject: [PATCH] Regenerate viewport dimensions when the viewport view changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Panning, zooming, twisting or rescaling a layout viewport moves model geometry across the sheet without touching that geometry, so nothing in the ordinary entity-change path noticed and the dimensions drawn through the viewport stayed where they were — detached from what they measure. `Scene::notify_viewport_changed` announces the viewport itself as a modified entity. That is all it takes: every trans-space reference carries the viewport handle as the first link of its chain, so the dependency lookup in `refresh_associative_dimensions` already finds the associations from there, and the refresh re-places and re-measures them. Called from each mutator that changes the paper<->model mapping: pan, zoom, fit-to-bounds, view normalisation on entering MSPACE, the view-direction/twist snaps, the viewcube camera path, and both annotation-scale setters. Co-Authored-By: Claude Fable 5.1 --- src/scene/camera_ops.rs | 3 +++ src/scene/mod.rs | 8 ++++++++ src/scene/mspace.rs | 23 +++++++++++++++++++++-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/scene/camera_ops.rs b/src/scene/camera_ops.rs index 4c0118256..e17ebfd7f 100644 --- a/src/scene/camera_ops.rs +++ b/src/scene/camera_ops.rs @@ -567,6 +567,9 @@ impl Scene { viewport.view_target.x = center.x; viewport.view_target.y = center.y; viewport.view_target.z = center.z; + // Panning a viewport moves model geometry across the sheet; the + // dimensions drawn through it follow. + self.notify_viewport_changed(handle); } else { self.camera.borrow_mut().target = center; } diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 452e6f822..bcb966d38 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -5296,6 +5296,10 @@ impl Scene { self.document .set_viewport_annotation_scale(viewport, scale_handle); + // Viewport scale changed: the dimensions drawn on the sheet + // through it measure the same model geometry at a new paper + // size and have to be re-placed. + self.notify_viewport_changed(viewport); self.resident_wire_sets.borrow_mut().clear(); self.bump_geometry(); @@ -5349,6 +5353,10 @@ impl Scene { vp.view_height = vp.height / factor; self.document .set_viewport_annotation_scale(viewport, scale_handle); + // Viewport scale changed: the dimensions drawn on the sheet + // through it measure the same model geometry at a new paper + // size and have to be re-placed. + self.notify_viewport_changed(viewport); self.resident_wire_sets.borrow_mut().clear(); self.bump_geometry(); true diff --git a/src/scene/mspace.rs b/src/scene/mspace.rs index dc16c99f4..97814e4c7 100644 --- a/src/scene/mspace.rs +++ b/src/scene/mspace.rs @@ -259,6 +259,9 @@ impl Scene { vp.view_height = eff_h; } } + // A viewport's view changed, so every dimension drawn on the + // sheet through it has to be re-placed and re-measured. + self.notify_viewport_changed(vp_handle); } /// Fit model-space bounds into the active floating viewport without moving @@ -297,6 +300,7 @@ impl Scene { viewport.custom_scale = viewport.height / viewport.view_height; } } + self.notify_viewport_changed(viewport_handle); self.camera_generation += 1; true } @@ -348,6 +352,9 @@ impl Scene { vp.view_target.y += model_delta.y as f64; vp.view_target.z += model_delta.z as f64; } + // A viewport's view changed, so every dimension drawn on the + // sheet through it has to be re-placed and re-measured. + self.notify_viewport_changed(vp_handle); } /// Zoom the active viewport's model-space view by `steps` notches. @@ -397,6 +404,9 @@ impl Scene { } } } + // A viewport's view changed, so every dimension drawn on the + // sheet through it has to be re-placed and re-measured. + self.notify_viewport_changed(vp_handle); } /// Orbit the active viewport's view direction by the given screen-pixel delta. @@ -474,6 +484,9 @@ impl Scene { vp.view_direction.z = dir.z as f64; vp.twist_angle = twist; } + // Twisting a viewport rotates model geometry on the sheet; the + // dimensions drawn through it follow. + self.notify_viewport_changed(vp_handle); } /// Mutate the active viewport's camera through a closure, then re-encode the @@ -502,6 +515,7 @@ impl Scene { .normalize_or(glam::Vec3::Y); let roll = up0.cross(desired_up).dot(dir).atan2(up0.dot(desired_up)); let twist = -roll as f64; + let mut changed = false; if let Some(acadrust::EntityType::Viewport(vp)) = self.document.get_entity_mut(vp_handle) { if vp.status.locked { return false; @@ -514,9 +528,14 @@ impl Scene { tmp.projection == view::camera::Projection::Perspective; vp.lens_length = (12.0 / (tmp.fov_y * 0.5).tan().max(1e-6)) as f64; - return true; + changed = true; } - false + if changed { + // The view (and with it the sheet placement of everything drawn + // through this viewport) moved. + self.notify_viewport_changed(vp_handle); + } + changed } /// Render mode of the active paper-space viewport, or `None` when no