diff --git a/src/app/view.rs b/src/app/view.rs index 302fd3fd1..808c3181c 100644 --- a/src/app/view.rs +++ b/src/app/view.rs @@ -2,6 +2,7 @@ use super::document::DocumentTab; use super::helpers::grid_plane_from_camera; use super::history::history_dropdown_labels; use super::{Message, OpenCADStudio}; +use crate::command::DynField; use crate::scene::grip::{grips_to_screen, grips_to_screen_paper}; use crate::scene::paper_canvas::PaperCanvas; use crate::scene::viewport_pane::{PaperViewportPane, ViewportPane}; @@ -463,18 +464,40 @@ impl OpenCADStudio { }; // Dynamic input overlay — shown when a command is active and DYN is on. + // The label adapts to what the command is currently asking for: + // a point shows coordinates (or polar distance+angle once a base + // point exists); a radius/length prompt shows a single distance; + // an angle prompt shows degrees. let dyn_input_overlay: Option> = if self.dyn_input && tab.active_cmd.is_some() { let w = tab.last_cursor_world; - let label = if let Some(base) = self.last_point { - // Show relative distance + angle when we have a base point. - let dx = (w.x - base.x) as f64; - let dy = (w.z - base.z) as f64; - let dist = (dx * dx + dy * dy).sqrt(); - let ang = dy.atan2(dx).to_degrees(); - format!("d={:.3} <{:.1}°", dist, ang) - } else { - format!("X:{:.3} Y:{:.3}", w.x, w.z) + let field = tab + .active_cmd + .as_ref() + .map(|c| c.dyn_field()) + .unwrap_or(DynField::Point); + // World is Z-up: the drawing plane is X/Y, so screen "up" + // maps to world Y — use w.y, not w.z (issue #27). + let label = match (field, self.last_point) { + (DynField::Distance, Some(base)) => { + let dx = (w.x - base.x) as f64; + let dy = (w.y - base.y) as f64; + format!("d={:.3}", (dx * dx + dy * dy).sqrt()) + } + (DynField::Angle, Some(base)) => { + let dx = (w.x - base.x) as f64; + let dy = (w.y - base.y) as f64; + format!("<{:.1}°", dy.atan2(dx).to_degrees().rem_euclid(360.0)) + } + (_, Some(base)) => { + // Point with a base: polar distance + angle. + let dx = (w.x - base.x) as f64; + let dy = (w.y - base.y) as f64; + let dist = (dx * dx + dy * dy).sqrt(); + let ang = dy.atan2(dx).to_degrees().rem_euclid(360.0); + format!("d={:.3} <{:.1}°", dist, ang) + } + (_, None) => format!("X:{:.3} Y:{:.3}", w.x, w.y), }; Some(overlay::dynamic_input_overlay( tab.last_cursor_screen, diff --git a/src/command/mod.rs b/src/command/mod.rs index fb69fcffe..5d9043923 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -179,6 +179,23 @@ pub enum CmdResult { AttreqNeeded { block_name: String }, } +/// What kind of value the active command is currently asking for. Drives +/// the dynamic-input overlay so the tooltip shows the relevant quantity +/// (coordinates for a point pick, a single length for a radius/distance +/// prompt, degrees for an angle prompt). +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +pub enum DynField { + /// A position — X/Y coordinates (or distance+angle relative to the + /// last point). The default for every command step. + #[default] + Point, + /// A single linear distance (radius, length, offset) measured from + /// the last point. + Distance, + /// An angle, shown in degrees, measured from the last point. + Angle, +} + // ── Trait ───────────────────────────────────────────────────────────────── /// An interactive CAD command that collects user input step-by-step. @@ -316,6 +333,14 @@ pub trait CadCommand: Send { /// that need to read/modify it (e.g. DIMTEDIT, MLEADERADD, MLEADERREMOVE). /// Default: no-op. fn inject_picked_entity(&mut self, _entity: acadrust::EntityType) {} + + /// What the command is asking for at this step, used to label the + /// dynamic-input overlay. Default is a point pick; commands waiting + /// on a radius/length return `Distance` and angle prompts return + /// `Angle`. + fn dyn_field(&self) -> DynField { + DynField::Point + } } // ── Autocomplete registry ───────────────────────────────────────────────── diff --git a/src/modules/home/draw/circle.rs b/src/modules/home/draw/circle.rs index 408fe80ad..c6f0ecf64 100644 --- a/src/modules/home/draw/circle.rs +++ b/src/modules/home/draw/circle.rs @@ -11,7 +11,7 @@ use acadrust::types::Vector3; use acadrust::{Circle, EntityType}; -use crate::command::{CadCommand, CmdResult, TangentObject}; +use crate::command::{CadCommand, CmdResult, DynField, TangentObject}; use crate::modules::home::defaults; use crate::modules::IconKind; use crate::scene::wire_model::WireModel; @@ -183,6 +183,12 @@ impl CadCommand for CircleCommand { } None } + fn dyn_field(&self) -> DynField { + match self.step { + StepCR::Center => DynField::Point, + StepCR::Radius(_) => DynField::Distance, + } + } } // ── Command: Center, Diameter ────────────────────────────────────────────── diff --git a/src/modules/home/modify/rotate.rs b/src/modules/home/modify/rotate.rs index 278bcc46f..c0a708b7f 100644 --- a/src/modules/home/modify/rotate.rs +++ b/src/modules/home/modify/rotate.rs @@ -9,7 +9,7 @@ use acadrust::Handle; use glam::Vec3; -use crate::command::{CadCommand, CmdResult, EntityTransform}; +use crate::command::{CadCommand, CmdResult, DynField, EntityTransform}; use crate::modules::home::defaults; use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::scene::wire_model::WireModel; @@ -187,4 +187,11 @@ impl CadCommand for RotateCommand { )); out } + + fn dyn_field(&self) -> DynField { + match self.step { + Step::Angle { .. } => DynField::Angle, + _ => DynField::Point, + } + } }