From cd17096f653e0744c40293e96b995ad27645852c Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Thu, 18 Jun 2026 22:05:25 +0300 Subject: [PATCH] feat(io): persist grid/snap display state to the active VPort Mirror the in-app grid and grid-snap toggles (plus the adaptive grid spacing) onto the *Active VPort entries so a saved DWG/DXF reflects what the user actually has on, instead of always writing VPort defaults. The flags are stamped when the toggles change and again before a save, and carried across the camera-sync VPort rebuild so they survive an orbit/pan. Only grid_on, snap_on and grid_spacing have an OCS counterpart; the other VPort view fields (view twist, clip planes, lens length, etc.) have no in-app source and stay at their faithful defaults. Co-Authored-By: Claude Opus 4.8 --- src/app/update.rs | 20 ++++++++++++++++++++ src/scene/mod.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/app/update.rs b/src/app/update.rs index d7b03e217..a93028a2e 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -342,6 +342,19 @@ impl OpenCADStudio { self.persist_settings_if_changed(); } + /// Mirror the current grid/snap toggles onto tab `i`'s `*Active` VPort + /// entries so a save writes the real display state rather than VPort + /// defaults (#121). Cheap; safe to call every frame. + fn sync_vport_display(&mut self, i: usize) { + let grid_on = self.show_grid; + let snap_on = + self.snapper.snap_enabled && self.snapper.is_on(crate::snap::SnapType::Grid); + let grid_spacing = self.snapper.grid_spacing as f64; + self.tabs[i] + .scene + .set_active_vport_display(grid_on, snap_on, grid_spacing); + } + fn update_inner(&mut self, msg: Message) -> Task { match msg { Message::Tick(t) => { @@ -924,6 +937,10 @@ impl OpenCADStudio { Message::SaveFile => { let i = self.active_tab; + // Stamp the live grid/snap toggles onto the VPort so the file + // reflects them even if they came from settings with no + // in-session toggle (#121). + self.sync_vport_display(i); // Native: save straight to the known path. Web has no path // (downloads instead), so always go through the Save dialog. #[cfg(not(target_arch = "wasm32"))] @@ -4295,14 +4312,17 @@ impl OpenCADStudio { // ── Snap / mode toggles ─────────────────────────────────────── Message::ToggleSnapEnabled => { self.snapper.toggle_global(); + self.sync_vport_display(self.active_tab); Task::none() } Message::ToggleGridSnap => { self.snapper.toggle(crate::snap::SnapType::Grid); + self.sync_vport_display(self.active_tab); Task::none() } Message::ToggleGrid => { self.show_grid ^= true; + self.sync_vport_display(self.active_tab); Task::none() } Message::ToggleOrtho => { diff --git a/src/scene/mod.rs b/src/scene/mod.rs index b08ba97b4..bf9b8a31f 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -5573,6 +5573,15 @@ impl Scene { } let table_handle = self.document.vports.handle(); + // Carry the grid/snap display state from the existing `*Active` entry + // across the rebuild — these flags track the in-app toggles and must + // not reset to VPort defaults each camera sync. (#121) + let display = self + .document + .vports + .iter() + .find(|v| v.name == "*Active") + .map(|v| (v.grid_on, v.snap_on, v.grid_spacing, v.snap_spacing, v.snap_base)); let preserved_vps: Vec = self .document .vports @@ -5604,11 +5613,32 @@ impl Scene { let (ll, ur) = Self::tile_rect_to_vport(tile.rect); let mut entry = self.vport_from_camera("*Active", &tile.camera, ll, ur); entry.render_mode = tile.render_mode; + if let Some((grid_on, snap_on, grid_spacing, snap_spacing, snap_base)) = display { + entry.grid_on = grid_on; + entry.snap_on = snap_on; + entry.grid_spacing = grid_spacing; + entry.snap_spacing = snap_spacing; + entry.snap_base = snap_base; + } entry.handle = self.document.allocate_handle(); self.document.vports.add_allow_duplicate(entry); } } + /// Stamp the grid/snap display state onto every `*Active` VPort entry so a + /// saved file reflects the in-app grid/snap toggles (otherwise other CAD + /// apps read VPort defaults). `grid_spacing` is applied to both axes and + /// mirrored to the snap spacing. (#121) + pub fn set_active_vport_display(&mut self, grid_on: bool, snap_on: bool, grid_spacing: f64) { + let spacing = acadrust::types::Vector2 { x: grid_spacing, y: grid_spacing }; + for vp in self.document.vports.iter_mut().filter(|v| v.name == "*Active") { + vp.grid_on = grid_on; + vp.snap_on = snap_on; + vp.grid_spacing = spacing; + vp.snap_spacing = spacing; + } + } + /// Set the paper-space camera from the sheet viewport's stored view. /// Returns true if a valid sheet viewport was found and the camera was set. ///