feat: HATCHEDIT command to edit existing hatch pattern, scale, and angle

Add HATCHEDIT (HE alias) command. Works on pre-selected hatch or via
entity pick. Accepts text options: P <pattern>, S <scale>, A <angle>.
Enter applies changes; rebuilds hatch entity and GPU model via add_hatch.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Sonnet 4.6 committed 2026-04-07 11:08:51 +03:00
1 parent 8078c4c9da
commit f6d716dfc5
7 files changed
+206 -2

No files matched your search

+131
View File
@@ -0,0 +1,131 @@
// HATCHEDIT — edit an existing hatch entity's pattern, scale, or angle.
//
// Workflow:
// 1. Pick or pre-select a Hatch entity.
// 2. Enter options:
// P <name> — change pattern (ANSI31, SOLID, etc.)
// S <value> — change scale
// A <degrees> — change angle
// Press Enter to apply changes.
use acadrust::Handle;
use glam::Vec3;
use crate::command::{CadCommand, CmdResult};
enum HatcheditStep {
PickHatch,
EditOptions {
handle: Handle,
name: String,
scale: f32,
angle: f32,
},
}
pub struct HatcheditCommand {
step: HatcheditStep,
}
impl HatcheditCommand {
pub fn new() -> Self {
Self { step: HatcheditStep::PickHatch }
}
pub fn with_handle(handle: Handle, name: String, scale: f32, angle: f32) -> Self {
Self {
step: HatcheditStep::EditOptions { handle, name, scale, angle },
}
}
}
impl CadCommand for HatcheditCommand {
fn name(&self) -> &'static str { "HATCHEDIT" }
fn prompt(&self) -> String {
match &self.step {
HatcheditStep::PickHatch => "HATCHEDIT Select hatch:".into(),
HatcheditStep::EditOptions { name, scale, angle, .. } => format!(
"HATCHEDIT Pattern:{name} Scale:{scale:.4} Angle:{angle:.1} \
[P <pat> / S <scale> / A <angle> | Enter to apply]:"
),
}
}
fn needs_entity_pick(&self) -> bool {
matches!(self.step, HatcheditStep::PickHatch)
}
fn on_entity_pick(&mut self, handle: Handle, _pt: Vec3) -> CmdResult {
if handle.is_null() { return CmdResult::NeedPoint; }
// Actual hatch model retrieval happens in commands.rs dispatch.
// Store handle; name/scale/angle filled in by dispatch.
self.step = HatcheditStep::EditOptions {
handle,
name: String::new(),
scale: 1.0,
angle: 0.0,
};
CmdResult::NeedPoint
}
fn wants_text_input(&self) -> bool {
matches!(self.step, HatcheditStep::EditOptions { .. })
}
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
let (handle, name, scale, angle) = match &mut self.step {
HatcheditStep::EditOptions { handle, name, scale, angle } => {
(*handle, name, scale, angle)
}
_ => return None,
};
let text = text.trim().to_uppercase();
if text.is_empty() {
// Apply and exit
return Some(CmdResult::HatcheditApply {
handle,
name: name.clone(),
scale: *scale,
angle: *angle,
});
}
// Parse option: P/S/A followed by value
if let Some(rest) = text.strip_prefix('P') {
let n = rest.trim().to_string();
if !n.is_empty() { *name = n; }
return Some(CmdResult::NeedPoint);
}
if let Some(rest) = text.strip_prefix('S') {
if let Ok(v) = rest.trim().replace(',', ".").parse::<f32>() {
if v > 0.0 { *scale = v; }
}
return Some(CmdResult::NeedPoint);
}
if let Some(rest) = text.strip_prefix('A') {
if let Ok(v) = rest.trim().replace(',', ".").parse::<f32>() {
*angle = v;
}
return Some(CmdResult::NeedPoint);
}
// Unrecognized — stay and re-prompt
Some(CmdResult::NeedPoint)
}
fn on_point(&mut self, _pt: Vec3) -> CmdResult { CmdResult::NeedPoint }
fn on_enter(&mut self) -> CmdResult {
// Enter without text → apply current settings
let (handle, name, scale, angle) = match &self.step {
HatcheditStep::EditOptions { handle, name, scale, angle } => {
(*handle, name.clone(), *scale, *angle)
}
_ => return CmdResult::Cancel,
};
CmdResult::HatcheditApply { handle, name, scale, angle }
}
fn on_escape(&mut self) -> CmdResult { CmdResult::Cancel }
}
+1
View File
@@ -3,6 +3,7 @@ pub mod attdef;
pub mod circle;
pub mod donut;
pub mod ellipse;
pub mod hatchedit;
pub mod hatch;
pub mod line;
pub mod mline;