parent
a4235a1243
commit
8018ab1523
15 changed files with 478 additions and 6 deletions
|
|
@ -4,6 +4,22 @@ use acadrust::Handle;
|
|||
use iced::Task;
|
||||
|
||||
impl OpenCADStudio {
|
||||
/// Apply LIMCHECK/PLIMCHECK to a point before an interactive command
|
||||
/// consumes it. LIMITS itself must be able to redefine a rectangle beyond
|
||||
/// the old boundary, so it is the sole bypass.
|
||||
pub(super) fn command_point_allowed(&mut self, i: usize, point: glam::DVec3) -> bool {
|
||||
let checks_limits = self.tabs[i]
|
||||
.active_cmd
|
||||
.as_ref()
|
||||
.is_some_and(|command| command.name() != "LIMITS")
|
||||
&& self.tabs[i].scene.drawing_limit_check_enabled();
|
||||
if checks_limits && !self.tabs[i].scene.point_inside_drawing_limits(point) {
|
||||
self.command_line.push_error("Outside limits.");
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Drive the active command's step machine with one [`StepInput`], then
|
||||
/// apply the result. This is the single entry point every input source
|
||||
/// (command line, headless, dynamic input, plugin API, viewport) funnels
|
||||
|
|
@ -19,6 +35,11 @@ impl OpenCADStudio {
|
|||
}
|
||||
}
|
||||
let i = self.active_tab;
|
||||
if let StepInput::Point(point) = &input {
|
||||
if !self.command_point_allowed(i, *point) {
|
||||
return Task::none();
|
||||
}
|
||||
}
|
||||
let ctrl = self.ctrl_down;
|
||||
let shift = self.shift_down;
|
||||
let result: Option<CmdResult> = {
|
||||
|
|
@ -229,6 +250,9 @@ impl OpenCADStudio {
|
|||
None => coord,
|
||||
},
|
||||
};
|
||||
if !self.command_point_allowed(i, wcs) {
|
||||
return;
|
||||
}
|
||||
self.last_point = Some(wcs);
|
||||
self.push_ucs_to_cmd(i);
|
||||
let _ = self.feed_command(StepInput::Point(wcs));
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -323,6 +323,9 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
|||
if let Some((base, dir)) = self.otrack_active {
|
||||
if let Some(dist) = crate::app::expr_eval::eval_number(text.trim()) {
|
||||
let pt = base + dir * dist;
|
||||
if !self.command_point_allowed(i, pt) {
|
||||
return Task::none();
|
||||
}
|
||||
self.last_point = Some(pt);
|
||||
self.dyn_user_reshaped = false;
|
||||
self.sync_dyn_fields();
|
||||
|
|
@ -365,6 +368,9 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
|||
}
|
||||
}
|
||||
};
|
||||
if !self.command_point_allowed(i, wcs_pt) {
|
||||
return Task::none();
|
||||
}
|
||||
self.last_point = Some(wcs_pt);
|
||||
self.dyn_user_reshaped = false;
|
||||
self.sync_dyn_fields();
|
||||
|
|
|
|||
|
|
@ -484,6 +484,9 @@ impl OpenCADStudio {
|
|||
{
|
||||
if let Some(dist) = crate::app::expr_eval::eval_number(text.trim()) {
|
||||
let pt = base + dir * dist;
|
||||
if !self.command_point_allowed(i, pt) {
|
||||
return Some(Task::none());
|
||||
}
|
||||
self.last_point = Some(pt);
|
||||
for f in self.tabs[i].dyn_fields.iter_mut() {
|
||||
f.buffer = None;
|
||||
|
|
@ -547,6 +550,9 @@ impl OpenCADStudio {
|
|||
});
|
||||
}
|
||||
let pt = self.dyn_resolve_point()?;
|
||||
if !self.command_point_allowed(i, pt) {
|
||||
return Some(Task::none());
|
||||
}
|
||||
self.last_point = Some(pt);
|
||||
self.dyn_user_reshaped = false;
|
||||
self.sync_dyn_fields();
|
||||
|
|
|
|||
|
|
@ -2383,6 +2383,8 @@ impl OpenCADStudio {
|
|||
self.command_line.push_info("Select a tangent object.");
|
||||
None
|
||||
}
|
||||
} else if !self.command_point_allowed(i, world_pt) {
|
||||
None
|
||||
} else {
|
||||
// A scalar typed into the dynamic-input box but not
|
||||
// yet confirmed with Enter is applied before the
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ impl OpenCADStudio {
|
|||
),
|
||||
origin,
|
||||
axes,
|
||||
limits: tab.scene.grid_limits_for_viewport(handle),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
|
|
|||
Loading…
Reference in a new issue