mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-14 14:26:45 -04:00
Squash of PR #577 by @MarekSpodBiedry. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
3363d4a78c
commit
d3ae8f3a8b
2 files changed
+107
-3
No files matched your search
@@ -8,7 +8,7 @@ use cadcraft_geom::{Segment, Vec2, Vec3};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::helpers::v3;
|
||||
use super::machines::SelectRun;
|
||||
use super::machines::{SelOutcome, SelectPhase, SelectRun};
|
||||
use super::*;
|
||||
use crate::{Accept, EngineError, Input, Interactive, LeaderOrder, MLeaderOptions, Prompt, Result, Session, Step};
|
||||
|
||||
@@ -106,7 +106,8 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
.interactive(|_| Ok(Box::new(tedit::TEditM::default()))),
|
||||
CommandSpec::new("dimspace", "Dimension Space", run_dimspace)
|
||||
.menu(&["Dimension", "Dimension Space"])
|
||||
.params("{base: hex, handles: [hex], spacing?: number (default DIMDLI)}"),
|
||||
.params("{base: hex, handles: [hex], spacing?: number (default DIMDLI)}")
|
||||
.interactive(|_| Ok(Box::new(DimSpaceM::default()))),
|
||||
]
|
||||
.into_iter()
|
||||
.chain(dimedit::specs())
|
||||
@@ -719,6 +720,90 @@ fn run_dimspace(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Ok(json!({ "spaced": others.len() }))
|
||||
}
|
||||
|
||||
/// DIMSPACE at the command line: select the base dimension, select the dimensions to space, then
|
||||
/// the spacing (Enter or Auto takes the DIMDLI-based default).
|
||||
#[derive(Default)]
|
||||
struct DimSpaceM {
|
||||
base: Option<Handle>,
|
||||
sel: SelectPhase,
|
||||
spacing_phase: bool,
|
||||
}
|
||||
|
||||
impl DimSpaceM {
|
||||
fn apply(&self, s: &mut Session, base: Handle, spacing: Option<f64>) -> Result<Step> {
|
||||
let mut p = serde_json::Map::new();
|
||||
p.insert("base".into(), json!(base.hex()));
|
||||
p.insert("handles".into(), json!(self.sel.picked.iter().map(|h| h.hex()).collect::<Vec<_>>()));
|
||||
if let Some(v) = spacing {
|
||||
p.insert("spacing".into(), json!(v));
|
||||
}
|
||||
let r = run_dimspace(s, &Value::Object(p))?;
|
||||
s.set_selection(Vec::new());
|
||||
s.echo(format!("{} dimension(s) spaced.", r.get("spaced").and_then(Value::as_u64).unwrap_or(0)));
|
||||
Ok(Step::Done)
|
||||
}
|
||||
}
|
||||
|
||||
impl Interactive for DimSpaceM {
|
||||
fn name(&self) -> &'static str {
|
||||
"DIMSPACE"
|
||||
}
|
||||
fn begin(&mut self, _s: &mut Session) -> Result<Step> {
|
||||
self.sel = SelectPhase::single();
|
||||
Ok(Step::Continue)
|
||||
}
|
||||
fn prompt(&self, _s: &Session) -> Prompt {
|
||||
if self.spacing_phase {
|
||||
return Prompt::new("Enter value", Accept::NUMBER).kw(&["Auto"]).default("Auto");
|
||||
}
|
||||
if self.base.is_none() {
|
||||
return Prompt::new("Select base dimension", Accept::SELECT);
|
||||
}
|
||||
Prompt::new("Select dimensions to space", Accept::SELECT)
|
||||
}
|
||||
fn input(&mut self, s: &mut Session, i: Input) -> Result<Step> {
|
||||
if self.spacing_phase {
|
||||
let Some(base) = self.base else { return Ok(Step::Cancel) };
|
||||
return match i {
|
||||
Input::Enter => self.apply(s, base, None),
|
||||
Input::Keyword(k) | Input::Text(k) if k.trim().eq_ignore_ascii_case("a") || k.trim().eq_ignore_ascii_case("auto") => {
|
||||
self.apply(s, base, None)
|
||||
}
|
||||
Input::Text(t) => match super::machines::number(&t).filter(|v| v.is_finite() && *v >= 0.0) {
|
||||
Some(v) => self.apply(s, base, Some(v)),
|
||||
None => {
|
||||
s.echo("Requires a non-negative number or Auto.");
|
||||
Ok(Step::Continue)
|
||||
}
|
||||
},
|
||||
_ => Ok(Step::Continue),
|
||||
};
|
||||
}
|
||||
match self.sel.feed(s, &i)? {
|
||||
SelOutcome::More => Ok(Step::Continue),
|
||||
SelOutcome::Empty => Ok(Step::Done),
|
||||
SelOutcome::Done(hs) => {
|
||||
if self.base.is_none() {
|
||||
match hs.first().copied().filter(|h| !dims_of(s, &[*h]).is_empty()) {
|
||||
Some(b) => {
|
||||
self.base = Some(b);
|
||||
self.sel = SelectPhase::default();
|
||||
}
|
||||
None => {
|
||||
s.echo("Select a dimension.");
|
||||
self.sel = SelectPhase::single();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.sel.picked = hs;
|
||||
self.spacing_phase = true;
|
||||
}
|
||||
Ok(Step::Continue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// DIMOVERRIDE at the command line: variable, value (repeat), Enter, then select dimensions.
|
||||
struct OverrideM {
|
||||
vars: serde_json::Map<String, Value>,
|
||||
|
||||
@@ -2,7 +2,7 @@ use cadcraft_doc::{AssocSnap, DimKind, Dimension, EntityKind, Handle};
|
||||
use cadcraft_geom::Vec2;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use crate::Session;
|
||||
use crate::{Input, Session};
|
||||
|
||||
fn h(v: &Value) -> Handle {
|
||||
Handle::parse_hex(v["handle"].as_str().unwrap()).unwrap()
|
||||
@@ -299,6 +299,25 @@ fn dimtedit_and_dimspace() {
|
||||
assert!((dim(&s, b).defpt.y - 1.5).abs() < 1e-9, "{:?}", dim(&s, b).defpt);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dimspace_prompts_for_its_inputs() {
|
||||
let mut s = Session::new();
|
||||
let a = h(&s.execute("dimlinear", &json!({ "p1": [0, 0], "p2": [10, 0], "at": [0, 5] })).unwrap());
|
||||
let b = h(&s.execute("dimlinear", &json!({ "p1": [0, 0], "p2": [20, 0], "at": [0, 9] })).unwrap());
|
||||
// The menu / typed start used to fail with "`base` dimension is required".
|
||||
s.start("dimspace").unwrap();
|
||||
assert!(s.prompt_text().contains("Select base dimension"), "{}", s.prompt_text());
|
||||
s.input(Input::Pick(vec![a])).unwrap();
|
||||
assert!(s.prompt_text().contains("Select dimensions to space"), "{}", s.prompt_text());
|
||||
s.input(Input::Pick(vec![b])).unwrap();
|
||||
s.input(Input::Enter).unwrap();
|
||||
assert!(s.prompt_text().contains("Enter value"), "{}", s.prompt_text());
|
||||
s.input(Input::Text("2".into())).unwrap();
|
||||
assert!(s.running.is_none());
|
||||
assert!((dim(&s, a).defpt.y - 5.0).abs() < 1e-9, "{:?}", dim(&s, a).defpt);
|
||||
assert!((dim(&s, b).defpt.y - 7.0).abs() < 1e-9, "{:?}", dim(&s, b).defpt);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_style_commands() {
|
||||
let mut s = Session::new();
|
||||
|
||||
Reference in new issue
Block a user