mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-29 05:16:49 -06:00
THICKNESS: Format > Thickness, and new 2D objects take the current thickness
New lines, arcs, circles, points and lightweight polylines (for example PLINE, POLYGON, DONUT and BOUNDARY) now take the THICKNESS system variable as their 3D thickness. THICKNESS could already be set with SETVAR and with the Properties palette when nothing is selected, but new objects were always created with a thickness of 0, apart from RECTANG's own Thickness option. RECTANG keeps that option, and single-line text does not take THICKNESS, as in AutoCAD. The new THICKNESS command is on Format > Thickness. Typed or picked from the menu, it asks "Enter new value for THICKNESS <0.0000>:"; Enter keeps the current value, and a negative value is accepted. docs/parity-checklist.md is regenerated.
This commit is contained in:
@@ -1631,10 +1631,9 @@ pub(crate) fn run_rectang2(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let first = if centered { a - Vec2::new(w / 2.0, h / 2.0).rotate(rot) } else { a };
|
||||
let k = rect_kind(first, w, h, rot, fillet, ch, width, f64_or(p, "elevation", 0.0));
|
||||
let hd = s.add_entity(k)?;
|
||||
// A rectangle has its own thickness option and does not take THICKNESS.
|
||||
let th = f64_or(p, "thickness", 0.0);
|
||||
if th != 0.0 {
|
||||
s.doc_mut()?.modify_entity(hd, |e| e.common.thickness = th)?;
|
||||
}
|
||||
s.doc_mut()?.modify_entity(hd, |e| e.common.thickness = th)?;
|
||||
added(hd)
|
||||
}
|
||||
|
||||
@@ -1683,10 +1682,8 @@ impl RectM2 {
|
||||
}
|
||||
let first = self.origin(w, h).unwrap_or(a);
|
||||
let hd = s.add_entity(rect_kind(first, w, h, self.rotation, self.fillet, self.chamfer, self.width, self.elevation))?;
|
||||
if self.thickness != 0.0 {
|
||||
let t = self.thickness;
|
||||
s.doc_mut()?.modify_entity(hd, |e| e.common.thickness = t)?;
|
||||
}
|
||||
let t = self.thickness;
|
||||
s.doc_mut()?.modify_entity(hd, |e| e.common.thickness = t)?;
|
||||
Ok(Step::Done)
|
||||
}
|
||||
/// The rectangle's local origin (the corner `rect_kind` draws from) for a size: the first
|
||||
|
||||
@@ -32,6 +32,11 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
CommandSpec::new("lweight", "Lineweight...", run_lweight).menu(&["Format", "Lineweight..."]).alias(&["lw", "lineweight"]).params("{lineweight: mm | ByLayer}"),
|
||||
CommandSpec::new("ltscale", "Linetype Scale", run_ltscale).alias(&["lts"]).params("{scale}").interactive(|_| Ok(Box::new(LtscaleM))),
|
||||
CommandSpec::new("units", "Units...", run_units).menu(&["Format", "Units..."]).alias(&["un"]).params("{lunits?: 1..5, luprec?: 0..8, aunits?: 0..4, auprec?: 0..8, insunits?}"),
|
||||
CommandSpec::new("thickness", "Thickness", run_thickness)
|
||||
.menu(&["Format", "Thickness"])
|
||||
.params("{thickness} (THICKNESS: the 3D thickness given to new lines, arcs, circles, points and polylines; may be negative) → {thickness}")
|
||||
.transparent()
|
||||
.interactive(|_| Ok(Box::new(ThicknessM))),
|
||||
CommandSpec::new("limits", "Drawing Limits", run_limits).menu(&["Format", "Drawing Limits"]).params("{min: [x,y], max: [x,y]}"),
|
||||
CommandSpec::new("style", "Text Style...", run_style)
|
||||
.menu(&["Format", "Text Style..."])
|
||||
@@ -534,6 +539,42 @@ impl Interactive for LtscaleM {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_thickness(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let t = f64_req("thickness", p, "thickness")?;
|
||||
if !t.is_finite() || t.abs() > 1e12 {
|
||||
return Err(bad("thickness", "thickness is out of range"));
|
||||
}
|
||||
s.doc_mut()?.header.set_f64("THICKNESS", t);
|
||||
Ok(json!({ "thickness": t }))
|
||||
}
|
||||
|
||||
/// Format > Thickness and typed THICKNESS: "Enter new value for THICKNESS <0.0000>:".
|
||||
struct ThicknessM;
|
||||
|
||||
impl Interactive for ThicknessM {
|
||||
fn name(&self) -> &'static str {
|
||||
"THICKNESS"
|
||||
}
|
||||
fn prompt(&self, s: &Session) -> Prompt {
|
||||
let cur = s.doc().map(|d| d.header.f64("THICKNESS", 0.0)).unwrap_or(0.0);
|
||||
Prompt::new("Enter new value for THICKNESS", Accept::NUMBER).default(format!("{cur:.4}"))
|
||||
}
|
||||
fn input(&mut self, s: &mut Session, i: Input) -> Result<Step> {
|
||||
let text = match i {
|
||||
Input::Enter | Input::Cancel => return Ok(Step::Done),
|
||||
Input::Text(t) => t,
|
||||
_ => return Ok(Step::Continue),
|
||||
};
|
||||
match super::machines::number(&text).map(|t| run_thickness(s, &json!({ "thickness": t }))) {
|
||||
Some(Ok(_)) => Ok(Step::Done),
|
||||
_ => {
|
||||
s.echo("Requires a number.");
|
||||
Ok(Step::Continue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_units(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let d = s.doc_mut()?;
|
||||
for (k, lo, hi) in [("lunits", 1, 5), ("luprec", 0, 8), ("aunits", 0, 4), ("auprec", 0, 8), ("insunits", 0, 24)] {
|
||||
|
||||
@@ -885,6 +885,19 @@ impl Session {
|
||||
if d.annotative_by_style(&kind) {
|
||||
common.anno_scales = vec![d.current_anno_scale()];
|
||||
}
|
||||
// Lines, arcs, circles, points, polylines and 2D solids take the current 3D thickness
|
||||
// (THICKNESS). RECTANG sets its own, and text never takes it.
|
||||
if matches!(
|
||||
kind,
|
||||
EntityKind::Line(_)
|
||||
| EntityKind::Arc(_)
|
||||
| EntityKind::Circle(_)
|
||||
| EntityKind::Point(_)
|
||||
| EntityKind::LwPolyline(_)
|
||||
| EntityKind::Solid(_)
|
||||
) {
|
||||
common.thickness = Some(d.header.f64("THICKNESS", 0.0)).filter(|t| t.is_finite()).unwrap_or(0.0);
|
||||
}
|
||||
let h = self.doc_mut()?.add(&space, common, kind)?;
|
||||
Ok(h)
|
||||
}
|
||||
|
||||
@@ -1309,6 +1309,58 @@ fn typed_ltscale_asks_for_the_scale() {
|
||||
assert_eq!(s.doc().unwrap().header.f64("LTSCALE", 0.0), 0.1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_thickness_asks_for_the_thickness() {
|
||||
let mut s = Session::new();
|
||||
s.start("thickness").unwrap();
|
||||
assert!(s.prompt_text().contains("Enter new value for THICKNESS <0.0000>"), "{}", s.prompt_text());
|
||||
// After text that is not a number, the prompt asks again.
|
||||
s.cmdline("thick").unwrap();
|
||||
assert!(s.current_prompt().is_some(), "asked again");
|
||||
// A negative thickness is accepted.
|
||||
s.cmdline("-1.5").unwrap();
|
||||
assert!(s.current_prompt().is_none());
|
||||
assert_eq!(s.doc().unwrap().header.f64("THICKNESS", 0.0), -1.5);
|
||||
// Typed, Enter keeps the current thickness.
|
||||
s.cmdline("thickness").unwrap();
|
||||
assert!(s.prompt_text().contains("<-1.5000>"), "{}", s.prompt_text());
|
||||
s.cmdline("").unwrap();
|
||||
assert!(s.current_prompt().is_none());
|
||||
assert_eq!(s.doc().unwrap().header.f64("THICKNESS", 0.0), -1.5);
|
||||
assert!(s.execute("thickness", &json!({"thickness": "thick"})).is_err());
|
||||
assert!(s.execute("thickness", &json!({"thickness": 1e13})).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_objects_take_the_current_thickness() {
|
||||
let mut s = Session::new();
|
||||
s.execute("thickness", &json!({"thickness": 2.5})).unwrap();
|
||||
s.execute("line", &json!({"points": [[0, 0], [10, 0]]})).unwrap();
|
||||
s.execute("arc", &json!({"center": [0, 0], "radius": 3, "start": 0, "end": 90})).unwrap();
|
||||
s.execute("circle", &json!({"center": [0, 0], "radius": 5})).unwrap();
|
||||
s.execute("point", &json!({"at": [1, 1]})).unwrap();
|
||||
s.execute("pline", &json!({"vertices": [[0, 0], [5, 0], [5, 5]]})).unwrap();
|
||||
s.execute("polygon", &json!({"sides": 6, "center": [20, 20], "radius": 2})).unwrap();
|
||||
s.execute("donut", &json!({"center": [30, 30], "inside": 1, "outside": 2})).unwrap();
|
||||
let thick: Vec<(&str, f64)> = s.doc().unwrap().model.iter().map(|e| (e.kind.type_name(), e.common.thickness)).collect();
|
||||
assert_eq!(thick.len(), 7, "{thick:?}");
|
||||
assert!(thick.iter().all(|(_, t)| *t == 2.5), "{thick:?}");
|
||||
// RECTANG keeps its own thickness option, and text does not take THICKNESS.
|
||||
s.execute("rectang", &json!({"p1": [0, 0], "p2": [4, 4]})).unwrap();
|
||||
assert_eq!(s.doc().unwrap().model.last().unwrap().common.thickness, 0.0);
|
||||
s.execute("rectang", &json!({"p1": [0, 0], "p2": [4, 4], "thickness": 1})).unwrap();
|
||||
assert_eq!(s.doc().unwrap().model.last().unwrap().common.thickness, 1.0);
|
||||
let before = s.doc().unwrap().model.len();
|
||||
s.cmdline("rectang 0,0 4,4").unwrap();
|
||||
assert_eq!(s.doc().unwrap().model.len(), before + 1);
|
||||
assert_eq!(s.doc().unwrap().model.last().unwrap().common.thickness, 0.0);
|
||||
s.execute("text", &json!({"at": [0, 0], "text": "A"})).unwrap();
|
||||
assert_eq!(s.doc().unwrap().model.last().unwrap().common.thickness, 0.0);
|
||||
// An ellipse has no thickness.
|
||||
s.execute("ellipse", &json!({"center": [0, 0], "major": [4, 0], "ratio": 0.5})).unwrap();
|
||||
assert_eq!(s.doc().unwrap().model.last().unwrap().common.thickness, 0.0);
|
||||
}
|
||||
|
||||
fn setvar_prompt_open(s: &Session) -> bool {
|
||||
s.running.as_ref().is_some_and(|r| r.id == "setvar")
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
# Parity checklist
|
||||
|
||||
> **Generated:** 2026-10-15 by `cargo xtask parity`. Do not edit by hand.
|
||||
> **Generated:** 2026-10-16 by `cargo xtask parity`. Do not edit by hand.
|
||||
> **Generated:** 2026-10-24 by `cargo xtask parity`. Do not edit by hand.
|
||||
|
||||
Menu-breadth parity: a reference menu item counts as covered when a CADCraft command is registered under the same menu label. It measures breadth, not depth; see [target-app-parity.md](target-app-parity.md) for the weighted assessment.
|
||||
|
||||
**Menu breadth: 283 / 491 items (58%)** · 427 registered commands.
|
||||
**Menu breadth: 288 / 491 items (59%)** · 434 registered commands.
|
||||
|
||||
| Menu | Items | Covered | % |
|
||||
|---|---|---|---|
|
||||
@@ -13,8 +12,8 @@ Menu-breadth parity: a reference menu item counts as covered when a CADCraft com
|
||||
| Edit | 13 | 11 | 85% |
|
||||
| View | 75 | 31 | 41% |
|
||||
| Insert | 11 | 5 | 45% |
|
||||
| Format | 27 | 25 | 93% |
|
||||
| Tools | 85 | 60 | 71% |
|
||||
| Format | 27 | 26 | 96% |
|
||||
| Tools | 85 | 64 | 75% |
|
||||
| Draw | 87 | 55 | 63% |
|
||||
| Dimension | 28 | 28 | 100% |
|
||||
| Modify | 113 | 52 | 46% |
|
||||
@@ -85,7 +84,6 @@ Menu-breadth parity: a reference menu item counts as covered when a CADCraft com
|
||||
- Insert > ACIS File...
|
||||
- Insert > Drawing Exchange Binary...
|
||||
- Format > Transparency
|
||||
- Format > Thickness
|
||||
- Tools > Spelling
|
||||
- Tools > Inquiry > Volume
|
||||
- Tools > Update Fields
|
||||
@@ -101,13 +99,8 @@ Menu-breadth parity: a reference menu item counts as covered when a CADCraft com
|
||||
- Tools > New UCS > Z
|
||||
- Tools > Named UCS...
|
||||
- Tools > Customize > Interface (CUI)...
|
||||
- Tools > Customize > Edit Command Aliases (PGP)...
|
||||
- Tools > Import Settings
|
||||
- Tools > Export Settings
|
||||
- Tools > Express Tools > Convert Text to Mtext
|
||||
- Tools > Express Tools > Break-line Symbol
|
||||
- Tools > Express Tools > Move/Copy/Rotate
|
||||
- Tools > Express Tools > Explode Attributes to Text
|
||||
- Tools > Express Tools > Replace Block...
|
||||
- Tools > Express Tools > Super Hatch > Image...
|
||||
- Tools > Express Tools > Super Hatch > Xref...
|
||||
|
||||
Reference in New Issue
Block a user