mirror of
https://github.com/storytold/cadcraft.git
synced 2026-11-11 16:32:22 -06:00
Typed at the command line or run from a script, DIMTEDIT ran the JSON form at once and failed with "select dimensions". It now asks "Select dimension", then "Specify new location for dimension text or [Left/Right/Center/Home/Angle]": a point moves the text there (the text follows the cursor in the preview), Left/Right/Center justify it, Home restores the default position and Angle asks for the text angle. A single preselected dimension is taken at once. Each answer runs the existing JSON form on the picked dimension. DIMTED is its alias. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1418130d45
commit
409968ecbb
@@ -99,7 +99,9 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
.params("{handles?}"),
|
||||
CommandSpec::new("dimtedit.right", "Right", |s, p| run_tedit(s, p, "right")).menu(&["Dimension", "Align Text", "Right"]).params("{handles?}"),
|
||||
CommandSpec::new("dimtedit", "Dimension Text Edit", run_dimtedit)
|
||||
.params("{handles?, at?: [x,y], mode?: home|angle|left|center|right, angle?}"),
|
||||
.alias(&["dimted"])
|
||||
.params("{handles?, at?: [x,y], mode?: home|angle|left|center|right, angle?}")
|
||||
.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)}"),
|
||||
@@ -1684,6 +1686,9 @@ impl Interactive for MLeaderM {
|
||||
}
|
||||
}
|
||||
|
||||
#[path = "annotate_tedit.rs"]
|
||||
mod tedit;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "annotate_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
//! DIMTEDIT at the command line: "Select dimension", then a new text location or
|
||||
//! Left/Right/Center/Home/Angle. Each answer runs the JSON form on the picked dimension.
|
||||
|
||||
use cadcraft_doc::{EntityKind, Handle};
|
||||
use cadcraft_geom::Vec2;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::{dims_of, run_dimtedit, v3};
|
||||
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct TEditM {
|
||||
dim: Option<Handle>,
|
||||
asking_angle: bool,
|
||||
}
|
||||
|
||||
impl TEditM {
|
||||
fn run(&self, s: &mut Session, mut p: Value) -> Result<Step> {
|
||||
let Some(h) = self.dim else { return Ok(Step::Cancel) };
|
||||
if let Some(o) = p.as_object_mut() {
|
||||
o.insert("handles".into(), json!([h.hex()]));
|
||||
}
|
||||
run_dimtedit(s, &p)?;
|
||||
Ok(Step::Done)
|
||||
}
|
||||
}
|
||||
|
||||
impl Interactive for TEditM {
|
||||
fn name(&self) -> &'static str {
|
||||
"DIMTEDIT"
|
||||
}
|
||||
fn begin(&mut self, s: &mut Session) -> Result<Step> {
|
||||
// A single preselected dimension is taken at once.
|
||||
if s.settings.pickfirst
|
||||
&& let [h] = dims_of(s, &s.selection())[..]
|
||||
{
|
||||
self.dim = Some(h);
|
||||
}
|
||||
Ok(Step::Continue)
|
||||
}
|
||||
fn prompt(&self, _s: &Session) -> Prompt {
|
||||
if self.dim.is_none() {
|
||||
Prompt::new("Select dimension", Accept::SELECT)
|
||||
} else if self.asking_angle {
|
||||
Prompt::new("Specify angle for dimension text", Accept::NUMBER)
|
||||
} else {
|
||||
Prompt::new("Specify new location for dimension text", Accept::POINT).kw(&["Left", "Right", "Center", "Home", "Angle"])
|
||||
}
|
||||
}
|
||||
fn input(&mut self, s: &mut Session, i: Input) -> Result<Step> {
|
||||
if self.dim.is_none() {
|
||||
return Ok(match i {
|
||||
Input::Pick(hs) => {
|
||||
match dims_of(s, &hs).first() {
|
||||
Some(h) => self.dim = Some(*h),
|
||||
None if hs.is_empty() => {}
|
||||
None => s.echo("Object selected is not a dimension."),
|
||||
}
|
||||
Step::Continue
|
||||
}
|
||||
Input::Enter | Input::Cancel => Step::Cancel,
|
||||
_ => Step::Continue,
|
||||
});
|
||||
}
|
||||
if self.asking_angle {
|
||||
return match i {
|
||||
Input::Text(t) => match s.angle_settings().direction(&t) {
|
||||
Some(a) => self.run(s, json!({ "mode": "angle", "angle": a.to_degrees() })),
|
||||
None => {
|
||||
s.echo("Requires a valid angle.");
|
||||
Ok(Step::Continue)
|
||||
}
|
||||
},
|
||||
Input::Enter | Input::Cancel => Ok(Step::Cancel),
|
||||
_ => Ok(Step::Continue),
|
||||
};
|
||||
}
|
||||
match i {
|
||||
Input::Point(p) => self.run(s, json!({ "at": [p.x, p.y] })),
|
||||
Input::Keyword(k) if k == "Angle" => {
|
||||
self.asking_angle = true;
|
||||
Ok(Step::Continue)
|
||||
}
|
||||
Input::Keyword(k) => self.run(s, json!({ "mode": k.to_ascii_lowercase() })),
|
||||
Input::Enter | Input::Cancel => Ok(Step::Cancel),
|
||||
_ => Ok(Step::Continue),
|
||||
}
|
||||
}
|
||||
/// The text follows the cursor.
|
||||
fn preview(&self, s: &Session, c: Vec2) -> Vec<EntityKind> {
|
||||
if self.asking_angle {
|
||||
return Vec::new();
|
||||
}
|
||||
let Some(EntityKind::Dimension(mut d)) = self.dim.and_then(|h| s.doc().ok()?.entity(h).map(|e| e.kind.clone())) else { return Vec::new() };
|
||||
d.text_mid = v3(c);
|
||||
d.user_text_pos = true;
|
||||
d.block = None;
|
||||
vec![EntityKind::Dimension(d)]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
//! DIMTEDIT at the command line and in scripts: "Select dimension", then a new text location or
|
||||
//! Left/Right/Center/Home/Angle.
|
||||
|
||||
use cadcraft_engine::Session;
|
||||
use cadcraft_engine::doc::{Dimension, EntityKind};
|
||||
use cadcraft_engine::geom::Vec2;
|
||||
|
||||
fn the_dim(s: &Session) -> Dimension {
|
||||
s.doc()
|
||||
.unwrap()
|
||||
.model
|
||||
.iter()
|
||||
.find_map(|e| match &e.kind {
|
||||
EntityKind::Dimension(d) => Some(d.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dimtedit_moves_justifies_rotates_and_homes_typed_text() {
|
||||
let mut s = Session::new();
|
||||
s.script("dimlinear 0,0 10,0 5,3\n").unwrap();
|
||||
// Pick the dimension on its dimension line, then a new text location.
|
||||
s.script("dimtedit 2,3\n").unwrap();
|
||||
let p = s.prompt_text();
|
||||
assert!(p.contains("Specify new location for dimension text") && p.contains("[Left/Right/Center/Home/Angle]"), "{p}");
|
||||
s.script("7,6\n").unwrap();
|
||||
assert!(s.running.is_none());
|
||||
let d = the_dim(&s);
|
||||
assert!(d.user_text_pos && d.text_mid.xy().dist(Vec2::new(7.0, 6.0)) < 1e-9, "{:?}", d.text_mid);
|
||||
|
||||
s.script("dimtedit 2,3 h\n").unwrap();
|
||||
assert!(!the_dim(&s).user_text_pos, "Home puts the text back");
|
||||
|
||||
s.script("dimtedit 2,3 l\n").unwrap();
|
||||
assert_eq!(the_dim(&s).overrides.get("textJust").and_then(|v| v.as_i64()), Some(1));
|
||||
|
||||
s.script("dimtedit 2,3 a 30\n").unwrap();
|
||||
assert!(s.running.is_none());
|
||||
assert!((the_dim(&s).text_rotation - 30f64.to_radians()).abs() < 1e-9);
|
||||
|
||||
// Something other than a dimension is refused and the prompt stays.
|
||||
s.script("line 20,20 30,20\n\ndimtedit 25,20\n").unwrap();
|
||||
assert!(s.prompt_text().contains("Select dimension"), "{}", s.prompt_text());
|
||||
s.script("2,3 c\n").unwrap();
|
||||
assert!(s.running.is_none());
|
||||
assert_eq!(the_dim(&s).overrides.get("textJust").and_then(|v| v.as_i64()), Some(0));
|
||||
}
|
||||
Reference in New Issue
Block a user