From 093c87d08a59605b0a7567cd2d686797e5718b78 Mon Sep 17 00:00:00 2001 From: Books Date: Sun, 11 Oct 2026 03:57:13 +1100 Subject: [PATCH] DIMANGULAR: accept polyline segments (fixes #10) (#15) Picking a side of a rectangle or a PLINE segment was rejected with "Select a line or an arc." because only LINE and ARC entities were accepted. The picked object is now resolved to the segment under the cursor: straight polyline segments work like lines and arc segments like arcs. --- crates/engine/src/cmd/annotate.rs | 23 +++++++++++++++++------ crates/engine/src/cmd/annotate_tests.rs | 25 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/crates/engine/src/cmd/annotate.rs b/crates/engine/src/cmd/annotate.rs index a925085..ee09c34 100644 --- a/crates/engine/src/cmd/annotate.rs +++ b/crates/engine/src/cmd/annotate.rs @@ -760,6 +760,19 @@ fn pick_at(s: &Session, p: Vec2) -> Option { crate::select::pick(s.doc().ok()?, &s.space(), p, ap) } +/// The straight or curved piece of `h` picked at `p`: a line, an arc, or the polyline segment nearest `p`. +fn segment_at(s: &Session, h: Handle, p: Vec2) -> Option { + match &s.doc().ok()?.entity(h)?.kind { + EntityKind::Line(l) => Some(Segment::Line(cadcraft_geom::Line::new(l.a.xy(), l.b.xy()))), + EntityKind::Arc(a) => Some(Segment::Arc { arc: cadcraft_geom::Arc::new(a.center.xy(), a.radius, a.start, a.end), ccw: true }), + EntityKind::LwPolyline(pl) => cadcraft_geom::Polyline { vertices: pl.vertices.clone(), closed: pl.closed } + .segments() + .into_iter() + .min_by(|x, y| x.closest(p).dist(p).total_cmp(&y.closest(p).dist(p))), + _ => None, + } +} + struct LinearM { aligned: bool, pts: Vec, @@ -1003,15 +1016,13 @@ impl Interactive for AngularM { s.echo("*Invalid selection*"); return Ok(Step::Continue); }; - let kind = s.doc()?.entity(h).map(|e| e.kind.clone()); - match kind { - Some(EntityKind::Arc(a)) if self.first.is_none() => { - let g = cadcraft_geom::Arc::new(a.center.xy(), a.radius, a.start, a.end); + match segment_at(s, h, p) { + Some(Segment::Arc { arc: g, .. }) if self.first.is_none() => { self.arc = Some((g.center, g.start_point(), g.end_point())); } - Some(EntityKind::Line(l)) => { + Some(Segment::Line(l)) => { self.handles.push(h); - let seg = (l.a.xy(), l.b.xy()); + let seg = (l.a, l.b); if self.first.is_none() { self.first = Some(seg); } else { diff --git a/crates/engine/src/cmd/annotate_tests.rs b/crates/engine/src/cmd/annotate_tests.rs index f644acd..02a117b 100644 --- a/crates/engine/src/cmd/annotate_tests.rs +++ b/crates/engine/src/cmd/annotate_tests.rs @@ -143,6 +143,31 @@ fn angular_between_lines_follows_rotation() { assert_eq!(cadcraft_render::dimension_in(s.doc().unwrap(), &dim(&s, d)).value, "45°"); } +#[test] +fn interactive_angular_picks_polyline_segments() { + // Rectangle sides are polyline segments, not lines (issue #10). + let mut s = Session::new(); + s.cmdline("rectang 0,0 10,5").unwrap(); + s.cmdline("dimangular").unwrap(); + s.cmdline("5,0").unwrap(); + s.cmdline("0,2").unwrap(); + s.cmdline("2,2").unwrap(); + assert!(s.running.is_none()); + let d = s.last_dim.unwrap(); + assert_eq!(cadcraft_render::dimension_in(s.doc().unwrap(), &dim(&s, d)).value, "90°"); + // A polyline arc segment works like an arc. + s.cmdline("pline 20,0 30,0 a 30,10").unwrap(); + s.cmdline("").unwrap(); + s.cmdline("dimangular").unwrap(); + let (cx, cy) = (30.0 + 5.0 * std::f64::consts::FRAC_1_SQRT_2, 5.0 - 5.0 * std::f64::consts::FRAC_1_SQRT_2); + s.cmdline(&format!("{cx},{cy}")).unwrap(); + s.cmdline("40,5").unwrap(); + assert!(s.running.is_none()); + let d2 = s.last_dim.unwrap(); + assert_ne!(d, d2); + assert_eq!(cadcraft_render::dimension_in(s.doc().unwrap(), &dim(&s, d2)).value, "180°"); +} + #[test] fn erase_disassociate_and_reassociate() { let mut s = Session::new();