// WIPEOUT command — create a rectangular or polygonal masking area. // // Modes: // WIPEOUT (default): two-corner rectangular wipeout // WIPEOUT P: polygonal wipeout (pick corners, Enter to close) use acadrust::entities::Wipeout; use acadrust::types::Vector3; use acadrust::EntityType; use glam::Vec3; use crate::command::{CadCommand, CmdResult}; use crate::scene::wire_model::WireModel; pub struct WipeoutCommand { mode: WipeoutMode, first: Option, poly_pts: Vec, } enum WipeoutMode { Rectangular, Polygonal, } impl WipeoutCommand { pub fn new_rectangular() -> Self { Self { mode: WipeoutMode::Rectangular, first: None, poly_pts: vec![] } } pub fn new_polygonal() -> Self { Self { mode: WipeoutMode::Polygonal, first: None, poly_pts: vec![] } } } impl CadCommand for WipeoutCommand { fn name(&self) -> &'static str { "WIPEOUT" } fn prompt(&self) -> String { match &self.mode { WipeoutMode::Rectangular => { if self.first.is_none() { "WIPEOUT Specify first corner:".into() } else { "WIPEOUT Specify opposite corner:".into() } } WipeoutMode::Polygonal => { if self.poly_pts.is_empty() { "WIPEOUT (Polygon) Specify first point:".into() } else { format!("WIPEOUT (Polygon) Specify next point ({} pts, Enter to close):", self.poly_pts.len()) } } } } fn on_point(&mut self, pt: Vec3) -> CmdResult { match &self.mode { WipeoutMode::Rectangular => { if let Some(p1) = self.first { let entity = make_rect_wipeout(p1, pt); CmdResult::CommitAndExit(entity) } else { self.first = Some(pt); CmdResult::NeedPoint } } WipeoutMode::Polygonal => { self.poly_pts.push(pt); CmdResult::NeedPoint } } } fn on_enter(&mut self) -> CmdResult { match &self.mode { WipeoutMode::Polygonal if self.poly_pts.len() >= 3 => { let entity = make_poly_wipeout(&self.poly_pts); CmdResult::CommitAndExit(entity) } _ => CmdResult::Cancel, } } fn on_mouse_move(&mut self, pt: Vec3) -> Option { match &self.mode { WipeoutMode::Rectangular => { let p1 = self.first?; let min = p1.min(pt); let max = p1.max(pt); Some(WireModel { name: "wipeout_preview".into(), points: vec![ [min.x, min.y, min.z], [max.x, min.y, min.z], [max.x, max.y, max.z], [min.x, max.y, max.z], [min.x, min.y, min.z], ], color: WireModel::CYAN, selected: false, pattern_length: 0.0, pattern: [0.0; 8], line_weight_px: 1.0, snap_pts: vec![], tangent_geoms: vec![], key_vertices: vec![], }) } WipeoutMode::Polygonal => { if self.poly_pts.is_empty() { return None; } let mut pts: Vec<[f32; 3]> = self.poly_pts.iter().map(|p| [p.x, p.y, p.z]).collect(); pts.push([pt.x, pt.y, pt.z]); pts.push([self.poly_pts[0].x, self.poly_pts[0].y, self.poly_pts[0].z]); Some(WireModel { name: "wipeout_poly_preview".into(), points: pts, color: WireModel::CYAN, selected: false, pattern_length: 0.0, pattern: [0.0; 8], line_weight_px: 1.0, snap_pts: vec![], tangent_geoms: vec![], key_vertices: vec![], }) } } } } fn make_rect_wipeout(p1: Vec3, p2: Vec3) -> EntityType { // Y-up world: X→DXF X, Z→DXF Y, Y→DXF Z let c1 = Vector3::new( p1.x.min(p2.x) as f64, p1.z.min(p2.z) as f64, p1.y as f64, ); let c2 = Vector3::new( p1.x.max(p2.x) as f64, p1.z.max(p2.z) as f64, p1.y as f64, ); EntityType::Wipeout(Wipeout::from_corners(c1, c2)) } fn make_poly_wipeout(pts: &[Vec3]) -> EntityType { use acadrust::types::Vector2; let z = pts[0].y as f64; let verts: Vec = pts.iter() .map(|p| Vector2::new(p.x as f64, p.z as f64)) .collect(); EntityType::Wipeout(Wipeout::polygonal(&verts, z)) }