diff --git a/crates/engine/src/cmd/draw.rs b/crates/engine/src/cmd/draw.rs index 538e53c5..430e47e3 100644 --- a/crates/engine/src/cmd/draw.rs +++ b/crates/engine/src/cmd/draw.rs @@ -780,6 +780,9 @@ impl Interactive for CircleM { Ok(Step::Continue) } Input::Point(p) => { + if self.mode == 0 && self.pts.first().is_some_and(|c| c.near(p, 1e-12)) { + return Err(crate::EngineError::Other("Value must be positive and nonzero.".into())); + } self.pts.push(p); let done = match self.mode { 0 if self.pts.len() == 2 => { diff --git a/crates/engine/tests/draw_prompts.rs b/crates/engine/tests/draw_prompts.rs index 9ddecdb4..4535b1be 100644 --- a/crates/engine/tests/draw_prompts.rs +++ b/crates/engine/tests/draw_prompts.rs @@ -44,6 +44,25 @@ fn spline_rejects_text_and_switches_method() { assert!(sp.fit.is_empty() && sp.control.len() == 4); } +#[test] +fn circle_radius_point_on_the_centre_is_refused() { + for input in ["circle 2,3 2,3", "circle 2,3 d 2,3"] { + let mut s = Session::new(); + s.cmdline(input).unwrap(); + assert_eq!(s.doc().unwrap().model.len(), 0, "{input}: no circle"); + assert!(logged(&s, "Value must be positive and nonzero."), "{input}: {:?}", s.log); + assert!(s.running.is_some(), "{input}: still at the radius prompt"); + s.cmdline("4").unwrap(); + assert!(s.running.is_none(), "{input}: a positive value finishes"); + assert_eq!(s.doc().unwrap().model.len(), 1, "{input}: one circle"); + let want = if input.contains(" d ") { 2.0 } else { 4.0 }; + assert!( + matches!(last(&s), EntityKind::Circle(c) if c.center.xy().near(Vec2::new(2.0, 3.0), 1e-9) && (c.radius - want).abs() < 1e-9), + "{input}: the centre is kept" + ); + } +} + /// The two lowest vertices of a closed polyline share their y (the bottom edge is horizontal). fn bottom_y(k: &EntityKind) -> Option { let EntityKind::LwPolyline(p) = k else { return None };