diff --git a/src/modules/draw/draw/wipeout.rs b/src/modules/draw/draw/wipeout.rs index 1ea1c4d26..23c4e568e 100644 --- a/src/modules/draw/draw/wipeout.rs +++ b/src/modules/draw/draw/wipeout.rs @@ -311,7 +311,7 @@ impl CadCommand for WipeoutCommand { } fn on_undo_step(&mut self) -> Option { - if self.mode == WipeoutMode::Draw && !self.points.is_empty() { + if self.mode == WipeoutMode::Draw && self.points.len() >= 2 { Some(self.undo_point()) } else { None @@ -484,3 +484,32 @@ pub(crate) fn wipeout_from_polyline(entity: &EntityType) -> Option { // ── Autocomplete registry ───────────────────────────────── inventory::submit!(crate::command::CommandRegistration { names: &["WIPEOUT"] }); + +#[cfg(test)] +mod tests { + use super::*; + use acadrust::entities::LwPolyline; + + #[test] + fn command_undo_never_removes_the_first_point() { + let mut command = WipeoutCommand::new_polygonal(1); + assert!(matches!(command.on_point(DVec3::ZERO), CmdResult::NeedPoint)); + assert!(command.on_undo_step().is_none()); + + assert!(matches!(command.on_point(DVec3::X), CmdResult::NeedPoint)); + assert!(matches!(command.on_undo_step(), Some(CmdResult::NeedPoint))); + assert_eq!(command.points, vec![DVec3::ZERO]); + } + + #[test] + fn wide_polyline_is_not_accepted_as_a_wipeout_boundary() { + let mut polyline = LwPolyline::from_points(vec![ + Vector2::new(0.0, 0.0), + Vector2::new(2.0, 0.0), + Vector2::new(0.0, 2.0), + ]); + polyline.is_closed = true; + polyline.constant_width = 1.0; + assert!(wipeout_from_polyline(&EntityType::LwPolyline(polyline)).is_none()); + } +} diff --git a/src/modules/draw/modify/break_cmd.rs b/src/modules/draw/modify/break_cmd.rs index 0ad841512..5f00bdccd 100644 --- a/src/modules/draw/modify/break_cmd.rs +++ b/src/modules/draw/modify/break_cmd.rs @@ -502,3 +502,28 @@ impl CadCommand for BreakAtPointCommand { // ── Autocomplete registry ───────────────────────────────── inventory::submit!(crate::command::CommandRegistration { names: &["BREAKATPOINT"] }); // BreakAtPointCommand inventory::submit!(crate::command::CommandRegistration { names: &["BREAK"] }); // BreakInteractiveCommand + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn first_option_replaces_the_selection_point_and_at_reuses_it() { + let handle = Handle::new(7); + let mut command = BreakInteractiveCommand::new(); + assert!(matches!( + command.on_entity_pick(handle, DVec3::new(1.0, 2.0, 0.0)), + CmdResult::NeedPoint + )); + assert_eq!(command.options().len(), 1); + assert!(matches!(command.on_text_input("F"), Some(CmdResult::NeedPoint))); + + let replacement = DVec3::new(3.0, 4.0, 0.0); + assert!(matches!(command.on_point(replacement), CmdResult::NeedPoint)); + assert!(matches!( + command.on_text_input("@"), + Some(CmdResult::BreakEntity { handle: result, p1, p2 }) + if result == handle && p1 == replacement && p2 == replacement + )); + } +}