feat(view): implement LIMITS command

Closes #478
This commit is contained in:
Hakan Seven 2026-07-27 16:15:28 +03:00
commit 8018ab1523
15 changed files with 478 additions and 6 deletions

View file

@ -692,9 +692,9 @@ impl OpenCADStudio {
self.command_line.push_output("Zoom Out");
}
// ZOOM ALL — fit all entities (same as EXTENTS for now)
// ZOOM ALL — fit the configured drawing limits.
"ZOOM ALL" | "ZOOM A" | "ZA" => {
self.tabs[i].scene.fit_all();
self.tabs[i].scene.fit_all_with_limits();
self.command_line.push_output("Zoom All");
}

View file

@ -142,6 +142,62 @@ impl OpenCADStudio {
return Some(Task::done(Message::ToggleViewCube));
}
// ── LIMITS — drawing/grid boundary for the active space ─────────────
"LIMITS" => {
use crate::modules::view::limits::LimitsCommand;
let (min, max) = self.tabs[i]
.scene
.current_drawing_limits()
.unwrap_or((glam::DVec2::ZERO, glam::DVec2::new(12.0, 9.0)));
let command = LimitsCommand::new(min, max);
self.command_line.push_info(&command.prompt());
self.tabs[i].active_cmd = Some(Box::new(command));
}
"LIMITS ON" | "LIMITS OFF" => {
let enabled = cmd.ends_with("ON");
if self.tabs[i].scene.drawing_limit_check_enabled() != enabled {
self.push_undo_snapshot(i, "LIMITS");
self.tabs[i].scene.set_drawing_limit_check(enabled);
self.tabs[i].dirty = true;
}
self.command_line.push_output(if enabled {
"Limits checking ON."
} else {
"Limits checking OFF."
});
}
cmd if cmd.starts_with("LIMITS SET ") => {
let tokens: Vec<&str> = cmd["LIMITS SET ".len()..].split_whitespace().collect();
let values: Result<Vec<f64>, _> =
tokens.iter().map(|value| value.parse()).collect();
let Ok(values) = values else {
self.command_line
.push_error("LIMITS: four numeric coordinates required.");
return Some(Task::none());
};
if tokens.len() != 4 || !values.iter().all(|value| value.is_finite()) {
self.command_line
.push_error("LIMITS: four finite numeric coordinates required.");
} else {
let first = glam::DVec2::new(values[0], values[1]);
let opposite = glam::DVec2::new(values[2], values[3]);
let min = first.min(opposite);
let max = first.max(opposite);
if min.x == max.x || min.y == max.y {
self.command_line
.push_error("LIMITS: corners must define a non-zero area.");
} else {
self.push_undo_snapshot(i, "LIMITS");
self.tabs[i].scene.set_current_drawing_limits(min, max);
self.tabs[i].dirty = true;
self.command_line.push_output(&format!(
"Drawing limits: {:.4},{:.4} to {:.4},{:.4}.",
min.x, min.y, max.x, max.y
));
}
}
}
// ── PROPERTIES — toggle Properties panel visibility ──────────────────
"PROPERTIES" | "PROPS" => {
return Some(Task::done(Message::ToggleProperties));

View file

@ -90,8 +90,14 @@ impl OpenCADStudio {
.borrow_mut()
.right_click_entered = false;
// A fresh command starts at the polar/cartesian default — clear
// any `,`-driven reshape from a previous command (#35).
// any `,`-driven reshape and locked dynamic-input values from a
// previous command. Otherwise a bare Enter on the first point prompt
// can commit that stale coordinate instead of accepting the command's
// default (LIMITS then compares an unintended lower-left point with
// the displayed default upper-right).
self.dyn_user_reshaped = false;
self.tabs[i].dyn_fields.clear();
self.tabs[i].dyn_active = 0;
if let Some(path_str) = cmd.strip_prefix("OPEN_RECENT:") {
let path = PathBuf::from(path_str);
@ -208,6 +214,7 @@ impl OpenCADStudio {
let i = self.active_tab;
if self.tabs[i].active_cmd.is_some() {
self.tabs[i].last_cmd = Some(cmd.to_string());
self.sync_dyn_fields();
self.focus_cmd_input()
} else {
Task::none()