From f9b27e7b62c260ed831f64248686dee7df6f680e Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Wed, 24 Jun 2026 14:01:42 +0300 Subject: [PATCH] fix(viewport): aim paper-space viewports at the absolute drawing centre MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Floating viewports rendered blank on UTM drawings. camera_for_viewport centred both the saved-view overlap test and the auto-fit target on the ORIGIN — correct only while world_offset re-centred the model there. With world_offset removed the model sits at absolute (~5.7e6) coordinates, so a stale (0,0,0) saved view failed the overlap test AND the auto-fit aimed at empty origin, leaving the viewport empty. Both now use the absolute drawing centre from model_space_extents, so saved views that frame the model are kept and the auto-fit falls back onto the real content. Co-Authored-By: Claude Opus 4.8 --- src/scene/mod.rs | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 6391d4f75..abbd4abf8 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -7561,6 +7561,18 @@ impl Scene { let saved_h = vp.view_height.abs(); let aspect_d = (vp.width / vp.height.max(1.0)).max(1e-9); let cluster_half = self.local_extent_max.max(1.0) as f64; + // Absolute drawing centre. Geometry now reaches the scene at absolute + // (UTM) coordinates — the old code centred the overlap test and the + // auto-fit on the origin, which was right only while world_offset + // re-centred the model there. Without it a UTM drawing sits ~5.7e6 away, + // so a stale `(0,0,0)` saved view failed the overlap test AND the + // auto-fit aimed at empty origin → blank viewports. + let (cx, cy) = self + .model_space_extents() + .map(|(mn, mx)| { + (((mn.x + mx.x) * 0.5) as f64, ((mn.y + mx.y) * 0.5) as f64) + }) + .unwrap_or((0.0, 0.0)); if let Some(cam) = self.camera_from_view( vp.view_direction, @@ -7576,20 +7588,20 @@ impl Scene { let half_h = saved_h * 0.5; let half_w = half_h * aspect_d; let (tx, ty) = (cam.target.x as f64, cam.target.y as f64); - let overlaps = tx + half_w >= -cluster_half - && tx - half_w <= cluster_half - && ty + half_h >= -cluster_half - && ty - half_h <= cluster_half; + let overlaps = tx + half_w >= cx - cluster_half + && tx - half_w <= cx + cluster_half + && ty + half_h >= cy - cluster_half + && ty - half_h <= cy + cluster_half; if overlaps { return Some(cam); } } - // Auto-fit: aim at the content cluster, drop the stale view_center. + // Auto-fit: aim at the content cluster centre, drop the stale view_center. let fit_h = cluster_half * 2.0 * 1.05; let tgt = acadrust::types::Vector3 { - x: [0.0_f64; 3][0], - y: [0.0_f64; 3][1], + x: cx, + y: cy, z: vp.view_target.z, }; self.camera_from_view(