feat(commands): step the remaining inquiry / solid commands (bucket E)

- ValuePromptCommand: CAL/QUICKCALC/QC (expression), FIND, FINDALL (search).
- SelectThenKeywordCommand (select the solid, then axis + value): SECTION,
  SLICE, 3DMIRROR, 3DROTATE. SLICE's optional TOP/BOTTOM stays typeable.
- TwoValuePromptCommand: PYRAMID (radius + height; sides typeable).

Left as-is by design: UNDO (bare already undoes one step — a correct default,
not a usage line); POLYSOLID (select a polyline then width + height — needs a
two-value-after-selection form) and 3DALIGN (six 3-D points) stay typeable.
This commit is contained in:
Hakan Seven committed 2026-07-16 14:43:34 +03:00
1 parent f0f9a57bf6
commit 49c10bc806
2 files changed
+101 -30

No files matched your search

+75 -21
View File
@@ -838,11 +838,17 @@ impl OpenCADStudio {
}
// PYRAMID <radius> <height> [sides] — create an n-sided pyramid mesh.
cmd if cmd == "PYRAMID"
|| cmd.starts_with("PYRAMID ")
|| cmd == "PYR"
|| cmd.starts_with("PYR ") =>
{
"PYRAMID" | "PYR" => {
use crate::command::TwoValuePromptCommand;
let c = TwoValuePromptCommand::new(
"PYRAMID",
"PYRAMID base radius:",
"PYRAMID height (add sides by typing a 3rd number):",
);
self.command_line.push_info(&c.prompt());
self.tabs[i].active_cmd = Some(Box::new(c));
}
cmd if cmd.starts_with("PYRAMID ") || cmd.starts_with("PYR ") => {
let nums: Vec<f64> = cmd
.split_whitespace()
.skip(1)
@@ -857,7 +863,23 @@ impl OpenCADStudio {
}
// SECTION [X|Y|Z] <value> — draw the cross-section outline of the solid.
cmd if cmd == "SECTION" || cmd.starts_with("SECTION ") => {
"SECTION" => {
use crate::command::SelectThenKeywordCommand;
let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
let c = SelectThenKeywordCommand::new(
"SECTION",
"SECTION cutting-plane axis [X / Y / Z]:",
vec![
("X", "X", Some("SECTION offset along X:")),
("Y", "Y", Some("SECTION offset along Y:")),
("Z", "Z", Some("SECTION offset along Z:")),
],
has_sel,
);
self.command_line.push_info(&c.prompt());
self.tabs[i].active_cmd = Some(Box::new(c));
}
cmd if cmd.starts_with("SECTION ") => {
let parts: Vec<String> = cmd
.split_whitespace()
.skip(1)
@@ -903,11 +925,19 @@ impl OpenCADStudio {
}
// 3DMIRROR [X|Y|Z] — add a mirror of the selected solid across a plane.
cmd if cmd == "3DMIRROR"
|| cmd == "MIRROR3D"
|| cmd.starts_with("3DMIRROR ")
|| cmd.starts_with("MIRROR3D ") =>
{
"3DMIRROR" | "MIRROR3D" => {
use crate::command::SelectThenKeywordCommand;
let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
let c = SelectThenKeywordCommand::new(
"3DMIRROR",
"3DMIRROR mirror plane [X / Y / Z]:",
vec![("X", "X", None), ("Y", "Y", None), ("Z", "Z", None)],
has_sel,
);
self.command_line.push_info(&c.prompt());
self.tabs[i].active_cmd = Some(Box::new(c));
}
cmd if cmd.starts_with("3DMIRROR ") || cmd.starts_with("MIRROR3D ") => {
let parts: Vec<String> = cmd
.split_whitespace()
.skip(1)
@@ -928,11 +958,23 @@ impl OpenCADStudio {
}
// 3DROTATE [X|Y|Z] <angle> — rotate the selected solid about an axis.
cmd if cmd == "3DROTATE"
|| cmd == "ROTATE3D"
|| cmd.starts_with("3DROTATE ")
|| cmd.starts_with("ROTATE3D ") =>
{
"3DROTATE" | "ROTATE3D" => {
use crate::command::SelectThenKeywordCommand;
let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
let c = SelectThenKeywordCommand::new(
"3DROTATE",
"3DROTATE rotation axis [X / Y / Z]:",
vec![
("X", "X", Some("3DROTATE angle in degrees:")),
("Y", "Y", Some("3DROTATE angle in degrees:")),
("Z", "Z", Some("3DROTATE angle in degrees:")),
],
has_sel,
);
self.command_line.push_info(&c.prompt());
self.tabs[i].active_cmd = Some(Box::new(c));
}
cmd if cmd.starts_with("3DROTATE ") || cmd.starts_with("ROTATE3D ") => {
let parts: Vec<String> = cmd
.split_whitespace()
.skip(1)
@@ -954,11 +996,23 @@ impl OpenCADStudio {
// SLICE [X|Y|Z] <value> [TOP|BOTTOM] — cut the selected solid with an
// axis-aligned plane, keeping the lower half by default.
cmd if cmd == "SLICE"
|| cmd == "SL"
|| cmd.starts_with("SLICE ")
|| cmd.starts_with("SL ") =>
{
"SLICE" | "SL" => {
use crate::command::SelectThenKeywordCommand;
let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
let c = SelectThenKeywordCommand::new(
"SLICE",
"SLICE cutting-plane axis [X / Y / Z] (add TOP/BOTTOM by typing):",
vec![
("X", "X", Some("SLICE offset along X:")),
("Y", "Y", Some("SLICE offset along Y:")),
("Z", "Z", Some("SLICE offset along Z:")),
],
has_sel,
);
self.command_line.push_info(&c.prompt());
self.tabs[i].active_cmd = Some(Box::new(c));
}
cmd if cmd.starts_with("SLICE ") || cmd.starts_with("SL ") => {
let parts: Vec<String> = cmd
.split_whitespace()
.skip(1)
+26 -9
View File
@@ -55,10 +55,13 @@ impl OpenCADStudio {
// CAL / QUICKCALC <expression> — evaluate an arithmetic expression
// (+ - * /, parentheses, unary minus, decimals).
cmd if cmd == "CAL"
|| cmd == "QUICKCALC"
|| cmd == "QC"
|| cmd.starts_with("CAL ")
"CAL" | "QUICKCALC" | "QC" => {
use crate::command::ValuePromptCommand;
let c = ValuePromptCommand::new("CAL", "CAL expression (e.g. (2+3)*4):");
self.command_line.push_info(&c.prompt());
self.tabs[self.active_tab].active_cmd = Some(Box::new(c));
}
cmd if cmd.starts_with("CAL ")
|| cmd.starts_with("QUICKCALC ")
|| cmd.starts_with("QC ") =>
{
@@ -922,11 +925,25 @@ impl OpenCADStudio {
// FIND <search> — list all Text/MText/Dimension containing <search>
// FIND <search> REPLACE <rep> — replace first occurrence (case-insensitive)
// FINDALL <search> REPLACE <rep> — replace all occurrences
cmd if cmd == "FIND"
|| cmd.starts_with("FIND ")
|| cmd == "FINDALL"
|| cmd.starts_with("FINDALL ") =>
{
"FIND" => {
use crate::command::ValuePromptCommand;
let c = ValuePromptCommand::new(
"FIND",
"FIND text to find (add REPLACE <text> by typing):",
);
self.command_line.push_info(&c.prompt());
self.tabs[self.active_tab].active_cmd = Some(Box::new(c));
}
"FINDALL" => {
use crate::command::ValuePromptCommand;
let c = ValuePromptCommand::new(
"FINDALL",
"FINDALL text to find (add REPLACE <text> by typing):",
);
self.command_line.push_info(&c.prompt());
self.tabs[self.active_tab].active_cmd = Some(Box::new(c));
}
cmd if cmd.starts_with("FIND ") || cmd.starts_with("FINDALL ") => {
let all_mode = cmd.starts_with("FINDALL");
let rest = cmd.split_once(' ').map(|(_, r)| r.trim()).unwrap_or("");