fix: honor GRIDUNIT X/Y spacing and LIMITS option in grid display

The viewport grid ignored the configured spacing and forced square grids: compute_grid_step hardcoded a 1.0 base, a single shared step drove both line families (including the tangent derivative used for gap measurement), and the display always clipped to LIMITS despite the beyond-Limits option.

Grid spacing now scales per-axis from GRIDUNIT via compute_grid_steps (adaptive 5x growth, fixed passthrough), GridParams carries step_x/step_y, snap tolerances follow the visible steps, and the LIMITS clip is skipped when beyond-Limits is set. Also fixes a per-axis overflow fallback that returned the X base for Y. Spacing state (X/Y, adaptive, beyond-Limits) is stored on the app and persisted to UserSettings at defaults; dialog wiring follows separately.
This commit is contained in:
Karim Jerbi committed 2026-09-17 13:52:37 +01:00
1 parent bb29b987c2
commit 05f462f294
7 files changed
+246 -73

No files matched your search

+11
View File
@@ -542,6 +542,13 @@ pub(super) struct OpenCADStudio {
snap_angle_deg: f32,
/// Show grid lines in the viewport (F7).
show_grid: bool,
/// GRIDUNIT X/Y display spacing backing the DSettings grid-resize inputs.
pub grid_spacing_x: f32,
pub grid_spacing_y: f32,
/// Adaptive grid: scale GRIDUNIT up by 5x steps to stay readable.
pub grid_adaptive: bool,
/// Display the grid beyond LIMITS (infinite) instead of clipping to them.
pub grid_beyond_limits: bool,
/// Dynamic input overlay (F12): show coordinate tooltip near cursor.
dyn_input: bool,
/// Currently visible page in the application Options dialog.
@@ -3806,6 +3813,10 @@ impl OpenCADStudio {
iso_plane: settings::IsoPlane::Left,
snap_angle_deg: 0.0,
show_grid: false,
grid_spacing_x: 10.0,
grid_spacing_y: 10.0,
grid_adaptive: true,
grid_beyond_limits: true,
dyn_input: true,
options_tab: crate::ui::window::options::OptionsTab::General,
spacemouse: {
+19
View File
@@ -459,6 +459,17 @@ pub struct UserSettings {
pub snap_spacing_x: f32,
#[serde(default = "default_snap_spacing")]
pub snap_spacing_y: f32,
/// GRIDUNIT X/Y display spacing (grid resizing). Falls back to 10.
#[serde(default = "default_snap_spacing")]
pub grid_spacing_x: f32,
#[serde(default = "default_snap_spacing")]
pub grid_spacing_y: f32,
/// Adaptive grid scaling (default on).
#[serde(default = "default_true")]
pub grid_adaptive: bool,
/// Display grid beyond LIMITS (default on, matches dialog).
#[serde(default = "default_true")]
pub grid_beyond_limits: bool,
/// Most-recently-inserted block names, most recent first, capped to 20.
/// Used to rank INSERT suggestions without touching the drawing file.
#[serde(default)]
@@ -485,6 +496,10 @@ fn default_snap_spacing() -> f32 {
10.0
}
fn default_true() -> bool {
true
}
/// Clamp a snap spacing to the positive range the dialog accepts.
pub fn sanitize_snap_spacing(v: f32) -> f32 {
if v.is_finite() && v > 0.0 && v <= 1e9 {
@@ -599,6 +614,10 @@ impl Default for UserSettings {
commandline_fade_ms: 3000,
snap_spacing_x: 10.0,
snap_spacing_y: 10.0,
grid_spacing_x: 10.0,
grid_spacing_y: 10.0,
grid_adaptive: true,
grid_beyond_limits: true,
block_mru: Vec::new(),
block_freq: std::collections::HashMap::new(),
}
+8
View File
@@ -531,6 +531,10 @@ impl OpenCADStudio {
),
snap_spacing_x: self.snapper.snap_spacing_x,
snap_spacing_y: self.snapper.snap_spacing_y,
grid_spacing_x: self.grid_spacing_x,
grid_spacing_y: self.grid_spacing_y,
grid_adaptive: self.grid_adaptive,
grid_beyond_limits: self.grid_beyond_limits,
block_mru: self.block_mru.clone(),
block_freq: self.block_freq.clone(),
}
@@ -635,6 +639,10 @@ impl OpenCADStudio {
crate::app::settings::sanitize_snap_spacing(s.snap_spacing_x);
self.snapper.snap_spacing_y =
crate::app::settings::sanitize_snap_spacing(s.snap_spacing_y);
self.grid_spacing_x = crate::app::settings::sanitize_snap_spacing(s.grid_spacing_x);
self.grid_spacing_y = crate::app::settings::sanitize_snap_spacing(s.grid_spacing_y);
self.grid_adaptive = s.grid_adaptive;
self.grid_beyond_limits = s.grid_beyond_limits;
// Block usage: clone but cap to sane sizes (MRU 20, freq map 200)
self.block_mru = s.block_mru.iter().take(20).cloned().collect();
self.block_freq = s
+20 -5
View File
@@ -1924,18 +1924,25 @@ impl OpenCADStudio {
// content), so snap / hit-test / preview run exactly like the
// main model view — no paper projection, tracks pan/zoom/twist.
let cursor_world = self.cursor_model_point(i, &edit_cam, p, bounds);
let (view_rot, eye, grid_spacing) = match &edit_cam {
let (gx, gy, adaptive) = (self.grid_spacing_x, self.grid_spacing_y, self.grid_adaptive);
let visible_step = |distance: f32, fov_y: f32| {
let (sx, sy) = crate::ui::overlay::compute_grid_steps(
gx, gy, distance, fov_y, bounds, adaptive,
);
sx.max(sy)
};
let (view_rot, eye, grid_spacing) = match &edit_cam {
Some(cam) => (
cam.view_proj_rte(bounds),
cam.eye(),
crate::ui::overlay::compute_grid_step(cam.distance, cam.fov_y, bounds),
visible_step(cam.distance, cam.fov_y),
),
None => {
let cam = self.tabs[i].scene.camera.borrow();
(
cam.view_proj_rte(bounds),
cam.eye(),
crate::ui::overlay::compute_grid_step(cam.distance, cam.fov_y, bounds),
visible_step(cam.distance, cam.fov_y),
)
}
};
@@ -2788,18 +2795,26 @@ impl OpenCADStudio {
// Object/grid snap, same path as an entity grip or command drag: the
// dragged UCS point sticks to endpoints/midpoints/grid under the cursor,
// and the snap marker is published via `snap_result`.
let (dgx, dgy, dadaptive) =
(self.grid_spacing_x, self.grid_spacing_y, self.grid_adaptive);
let dvisible_step = |distance: f32, fov_y: f32| {
let (sx, sy) = crate::ui::overlay::compute_grid_steps(
dgx, dgy, distance, fov_y, bounds, dadaptive,
);
sx.max(sy)
};
let (view_rot, eye, grid_spacing) = match &edit_cam {
Some(cam) => (
cam.view_proj_rte(bounds),
cam.eye(),
crate::ui::overlay::compute_grid_step(cam.distance, cam.fov_y, bounds),
dvisible_step(cam.distance, cam.fov_y),
),
None => {
let cam = self.tabs[i].scene.camera.borrow();
(
cam.view_proj_rte(bounds),
cam.eye(),
crate::ui::overlay::compute_grid_step(cam.distance, cam.fov_y, bounds),
dvisible_step(cam.distance, cam.fov_y),
)
}
};
+19 -6
View File
@@ -443,18 +443,31 @@ impl OpenCADStudio {
);
axes = (ux.as_vec3(), uy.as_vec3(), uz.as_vec3());
}
// BUG FIX: the display step used to ignore GRIDUNIT entirely
// (hardcoded 1.0 base). It now resizes from the DSettings
// grid spacing, and honors "Display grid beyond Limits".
let (step_x, step_y) = crate::ui::overlay::compute_grid_steps(
self.grid_spacing_x,
self.grid_spacing_y,
cam.distance,
cam.fov_y,
bounds,
self.grid_adaptive,
);
let limits = if self.grid_beyond_limits {
None
} else {
tab.scene.grid_limits_for_viewport(handle)
};
crate::ui::overlay::GridParams {
view_rot: cam.view_proj_rte(bounds),
eye: cam.eye(),
bounds,
step: crate::ui::overlay::compute_grid_step(
cam.distance,
cam.fov_y,
bounds,
),
step_x,
step_y,
origin,
axes,
limits: tab.scene.grid_limits_for_viewport(handle),
limits,
}
})
.collect();