feat(units): read back every form the drawing writes

The readout learned to write lengths as 5'-6 1/2" and angles as
N 45d0'0" E, but typed input was still a bare parse::<f64>() — a
coordinate could be displayed and not entered. Give the formatters
their inverses.

parse_length takes feet and inches separated by a dash, a space, or
nothing, with the closing quote optional, and whole-plus-fraction in
any of those forms: 5', 60", 5'-9", 5'9-1/2, 9-1/2, 1/2. A leading
minus is a sign; the one inside 5'-9" is a separator, since after feet
there is nothing left to subtract from.

parse_angle reads the mark that says which convention an angle is in —
g for grads, r for radians, d/'/" for degrees-minutes-seconds, compass
letters for a surveyor's bearing — and reads a bare number in whichever
convention the drawing is set to.

ANGBASE and ANGDIR now apply, but only where they mean something. Which
way a thing points is measured from the drawing's zero and runs the way
it says; how wide an arc opens is the same number whatever zero is
counted from. format_direction and parse_direction are the pair for
directions, and the polar readout and polar input both go through them,
so the bearing shown is the bearing accepted. format_angle stays for
sizes, where applying a base angle would corrupt the value.

The command prompts follow the same rule, each classified rather than
swept: OFFSET distance, FILLET radius, CHAMFER distances, ARRAY and
MINSERT spacing, TRACE width, HELIX, and EXTRUDE height read lengths;
the four dimension text-rotation prompts, ARRAY's total angle and
INSERT's rotation read angles. MLINE's scale and INSERT's scale factor
are left alone — a multiplier has no unit to be written in. INSERT read
both through one parse; they are now separate.

The typed variants also take a decimal comma, which a single-value
prompt has no other use for. parse_length itself does not: inside a
coordinate the comma separates the axes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 5 committed 2026-08-07 23:46:49 +03:00
1 parent 523b85f42b
commit 6cbec232bb
18 files changed
+298 -58

No files matched your search

+16 -8
View File
@@ -220,16 +220,24 @@ impl CadCommand for InsertBlockCommand {
}
Step::FillAttr { .. } => Some(self.accept_attr_value(text)),
Step::Point { .. } => {
// Typing the value awaited after a Scale / Rotate keyword.
// Typing the value awaited after a Scale / Rotate keyword. The
// two read differently: a scale is a plain multiplier with no
// unit to write it in, while a rotation is an angle and takes
// whichever convention the drawing is set to.
if let Some(kind) = self.awaiting {
if let Ok(v) = text.trim().parse::<f64>() {
match kind {
AwaitKind::Scale if v != 0.0 => {
self.x_scale = v;
self.y_scale = v;
match kind {
AwaitKind::Scale => {
if let Ok(v) = text.trim().parse::<f64>() {
if v != 0.0 {
self.x_scale = v;
self.y_scale = v;
}
}
}
AwaitKind::Rotation => {
if let Some(v) = crate::entities::common::parse_typed_angle(text) {
self.rotation_rad = v;
}
AwaitKind::Scale => {}
AwaitKind::Rotation => self.rotation_rad = v.to_radians(),
}
}
self.awaiting = None;
+2 -2
View File
@@ -228,12 +228,12 @@ impl CadCommand for MinsertCommand {
}
}
ParamIdx::RowSpacing => {
if let Ok(v) = typed.parse::<f64>() {
if let Some(v) = crate::entities::common::parse_typed_length(typed) {
self.row_spacing = v;
}
}
ParamIdx::ColumnSpacing => {
if let Ok(v) = typed.parse::<f64>() {
if let Some(v) = crate::entities::common::parse_typed_length(typed) {
self.column_spacing = v;
}
}
+1 -3
View File
@@ -77,9 +77,7 @@ impl CadCommand for ExtrudeCommand {
self.step == ExtrudeStep::Height
}
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
text.trim()
.parse::<f64>()
.ok()
crate::entities::common::parse_typed_length(text)
.filter(|&h| h.abs() > 1e-6)
.map(|h| CmdResult::ExtrudeEntity {
handle: self.target_handle,