mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-30 22:06:49 -06:00
* PEDIT Multiple at the command line (#2182) `M` at "Select polyline" answered that Multiple was only available through the JSON form. It now works as in AutoCAD: select objects (polylines, lines and arcs), turn the lines and arcs into polylines on Yes (left out on No), then each option applies to all of them: Close, Open, Width, Fit, Spline, Decurve, Ltype gen and Reverse. Join joins the selected objects with each other, ends within a fuzz distance, and reports how many it added. Undo takes the last option back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Geometry parity status line for PEDIT Multiple Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * PEDIT Multiple: objects selected first, and Decurve after Fit or Spline Several objects selected before PEDIT go straight into Multiple, as in AutoCAD, and M keeps a selection made first. Decurve gives back each polyline's vertices from before its first Fit or Spline, as single-polyline PEDIT does; a Join drops that record for the joined polylines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * PEDIT Multiple: the Decurve record follows Undo and survives a Join that joins nothing Each Undo step keeps the record of the vertices from before Fit or Spline, so Fit, Join, Undo, Decurve gives them back; a Join drops the record only for a polyline that took on segments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * PEDIT: Yes/No asked again on another answer; Decurve keeps a later Width The convert questions (single and Multiple) take Y/Yes, N/No or Enter and ask again for any other answer instead of treating it as Yes. Width clears the per-vertex widths of the vertices kept for Decurve too, so Decurve after Fit or Spline and Width keeps the new width. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
198 lines
7.0 KiB
Rust
198 lines
7.0 KiB
Rust
//! #2182: PEDIT Multiple at the command line, as AutoCAD: select several objects, turn lines and
|
|
//! arcs into polylines, then each option applies to all of them; Join joins them with each other.
|
|
|
|
use cadcraft_engine::Session;
|
|
use cadcraft_engine::doc::EntityKind;
|
|
use serde_json::json;
|
|
|
|
fn polylines(s: &Session) -> Vec<(usize, bool, f64)> {
|
|
s.doc()
|
|
.unwrap()
|
|
.model
|
|
.iter()
|
|
.filter_map(|e| if let EntityKind::LwPolyline(p) = &e.kind { Some((p.vertices.len(), p.closed, p.const_width)) } else { None })
|
|
.collect()
|
|
}
|
|
|
|
fn count(s: &Session) -> usize {
|
|
s.doc().unwrap().model.len()
|
|
}
|
|
|
|
fn lines(s: &mut Session, pts: &[[f64; 4]]) {
|
|
for p in pts {
|
|
s.execute("line", &json!({ "points": [[p[0], p[1]], [p[2], p[3]]] })).unwrap();
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn join_turns_four_lines_into_one_closed_polyline() {
|
|
let mut s = Session::new();
|
|
lines(&mut s, &[[0.0, 0.0, 10.0, 0.0], [10.0, 0.0, 10.0, 5.0], [10.0, 5.0, 0.0, 5.0], [0.0, 5.0, 0.0, 0.0]]);
|
|
for t in ["PEDIT", "M", "ALL", "", "Y", "J", "", ""] {
|
|
s.cmdline(t).unwrap();
|
|
}
|
|
assert!(s.running.is_none(), "{}", s.prompt_text());
|
|
assert_eq!(polylines(&s), vec![(4, true, 0.0)]);
|
|
assert_eq!(count(&s), 1);
|
|
assert!(s.log.iter().any(|l| l.contains("3 segments added to polyline")), "{:?}", s.log);
|
|
}
|
|
|
|
#[test]
|
|
fn join_bridges_gaps_within_the_fuzz_distance_only() {
|
|
// Two chains with a 0.5 gap between them.
|
|
let gap = |s: &mut Session| lines(s, &[[0.0, 0.0, 10.0, 0.0], [10.5, 0.0, 20.0, 0.0]]);
|
|
let mut s = Session::new();
|
|
gap(&mut s);
|
|
for t in ["PE", "M", "ALL", "", "Y", "J", "0", ""] {
|
|
s.cmdline(t).unwrap();
|
|
}
|
|
assert_eq!(polylines(&s).len(), 2, "no fuzz: still two");
|
|
let mut s = Session::new();
|
|
gap(&mut s);
|
|
for t in ["PE", "M", "ALL", "", "Y", "J", "1", ""] {
|
|
s.cmdline(t).unwrap();
|
|
}
|
|
assert_eq!(polylines(&s).len(), 1, "a fuzz of 1 joins them");
|
|
}
|
|
|
|
#[test]
|
|
fn options_apply_to_all_and_undo_takes_one_back() {
|
|
let mut s = Session::new();
|
|
s.cmdline("pline 0,0 10,0 10,10").unwrap();
|
|
s.cmdline("").unwrap();
|
|
s.cmdline("pline 20,0 30,0 30,10").unwrap();
|
|
s.cmdline("").unwrap();
|
|
for t in ["PEDIT", "M", "ALL", "", "W", "0.5", "C"] {
|
|
s.cmdline(t).unwrap();
|
|
}
|
|
assert_eq!(polylines(&s), vec![(3, true, 0.5), (3, true, 0.5)], "width and close on both");
|
|
s.cmdline("U").unwrap();
|
|
assert_eq!(polylines(&s), vec![(3, false, 0.5), (3, false, 0.5)], "Undo opens them again");
|
|
s.cmdline("").unwrap();
|
|
assert!(s.running.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn lines_left_out_when_not_converted() {
|
|
let mut s = Session::new();
|
|
s.cmdline("pline 0,0 10,0 10,10").unwrap();
|
|
s.cmdline("").unwrap();
|
|
lines(&mut s, &[[20.0, 0.0, 30.0, 0.0]]);
|
|
for t in ["PEDIT", "M", "ALL", "", "N", "C", ""] {
|
|
s.cmdline(t).unwrap();
|
|
}
|
|
assert_eq!(polylines(&s), vec![(3, true, 0.0)], "only the polyline is edited");
|
|
assert!(s.doc().unwrap().model.iter().any(|e| matches!(e.kind, EntityKind::Line(_))), "the line stays a line");
|
|
}
|
|
|
|
#[test]
|
|
fn objects_selected_first_go_straight_to_multiple() {
|
|
let mut s = Session::new();
|
|
s.cmdline("pline 0,0 10,0 10,10").unwrap();
|
|
s.cmdline("").unwrap();
|
|
s.cmdline("pline 20,0 30,0 30,10").unwrap();
|
|
s.cmdline("").unwrap();
|
|
let all: Vec<_> = s.doc().unwrap().model.iter().map(|e| e.handle).collect();
|
|
s.set_selection(all);
|
|
for t in ["PEDIT", "W", "0.5", ""] {
|
|
s.cmdline(t).unwrap();
|
|
}
|
|
assert_eq!(polylines(&s), vec![(3, false, 0.5), (3, false, 0.5)], "no second selection asked for");
|
|
assert!(s.running.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn decurve_after_fit_gives_back_the_vertices() {
|
|
let mut s = Session::new();
|
|
s.cmdline("pline 0,0 10,0 10,10 20,10").unwrap();
|
|
s.cmdline("").unwrap();
|
|
s.cmdline("pline 0,20 10,20 10,30").unwrap();
|
|
s.cmdline("").unwrap();
|
|
for t in ["PEDIT", "M", "ALL", "", "S"] {
|
|
s.cmdline(t).unwrap();
|
|
}
|
|
assert!(polylines(&s).iter().all(|p| p.0 > 4), "Spline adds vertices: {:?}", polylines(&s));
|
|
s.cmdline("D").unwrap();
|
|
assert_eq!(polylines(&s), vec![(4, false, 0.0), (3, false, 0.0)], "Decurve gives back each one's vertices");
|
|
s.cmdline("").unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn decurve_still_gives_back_the_vertices_after_a_join_that_joins_nothing_or_is_undone() {
|
|
let mut s = Session::new();
|
|
s.cmdline("pline 0,0 10,0 10,10 20,10").unwrap();
|
|
s.cmdline("").unwrap();
|
|
s.cmdline("pline 0,20 10,20 10,30").unwrap();
|
|
s.cmdline("").unwrap();
|
|
// Join with nothing in reach joins nothing: Decurve still gives back the vertices.
|
|
for t in ["PEDIT", "M", "ALL", "", "S", "J", "0", "D"] {
|
|
s.cmdline(t).unwrap();
|
|
}
|
|
assert_eq!(polylines(&s), vec![(4, false, 0.0), (3, false, 0.0)], "after a Join that joined nothing");
|
|
// Spline, a Join that joins them, Undo: Decurve gives back the vertices again.
|
|
for t in ["S", "J", "100", "U", "D"] {
|
|
s.cmdline(t).unwrap();
|
|
}
|
|
assert_eq!(polylines(&s), vec![(4, false, 0.0), (3, false, 0.0)], "after an undone Join");
|
|
s.cmdline("").unwrap();
|
|
}
|
|
|
|
fn widths(s: &Session) -> Vec<f64> {
|
|
s.doc()
|
|
.unwrap()
|
|
.model
|
|
.iter()
|
|
.filter_map(|e| if let EntityKind::LwPolyline(p) = &e.kind { Some(p) } else { None })
|
|
.flat_map(|p| p.vertices.iter().flat_map(|v| [v.start_width, v.end_width]))
|
|
.collect()
|
|
}
|
|
|
|
#[test]
|
|
fn convert_asks_again_for_an_answer_that_is_not_yes_or_no() {
|
|
let mut s = Session::new();
|
|
lines(&mut s, &[[0.0, 0.0, 10.0, 0.0]]);
|
|
for t in ["PEDIT", "M", "ALL", ""] {
|
|
s.cmdline(t).unwrap();
|
|
}
|
|
s.cmdline("foo").unwrap();
|
|
assert!(s.prompt_text().contains("Convert"), "{}", s.prompt_text());
|
|
assert_eq!(polylines(&s), vec![], "nothing converted on a wrong answer");
|
|
s.cmdline("Y").unwrap();
|
|
assert_eq!(polylines(&s).len(), 1);
|
|
s.cmdline("").unwrap();
|
|
|
|
// Single-polyline PEDIT asks again too.
|
|
lines(&mut s, &[[0.0, 5.0, 10.0, 5.0]]);
|
|
let l = s.doc().unwrap().model.last().unwrap().handle;
|
|
s.set_selection(vec![l]);
|
|
s.cmdline("PEDIT").unwrap();
|
|
s.cmdline("foo").unwrap();
|
|
assert!(s.prompt_text().contains("turn it into one"), "{}", s.prompt_text());
|
|
s.cmdline("N").unwrap();
|
|
assert!(s.running.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn decurve_after_width_keeps_the_new_width() {
|
|
let mut s = Session::new();
|
|
for t in ["PLINE", "0,0", "W", "0", "2", "10,0", "10,10", "20,10", ""] {
|
|
s.cmdline(t).unwrap();
|
|
}
|
|
assert!(widths(&s).iter().any(|w| *w > 0.0), "{:?}", widths(&s));
|
|
for t in ["PEDIT", "M", "ALL", "", "S", "W", "0", "D", ""] {
|
|
s.cmdline(t).unwrap();
|
|
}
|
|
assert!(widths(&s).iter().all(|w| *w == 0.0), "Multiple: {:?}", widths(&s));
|
|
|
|
let mut s = Session::new();
|
|
for t in ["PLINE", "0,0", "W", "0", "2", "10,0", "10,10", "20,10", ""] {
|
|
s.cmdline(t).unwrap();
|
|
}
|
|
let h = s.doc().unwrap().model.last().unwrap().handle;
|
|
s.set_selection(vec![h]);
|
|
for t in ["PEDIT", "S", "W", "0", "D", ""] {
|
|
s.cmdline(t).unwrap();
|
|
}
|
|
assert!(widths(&s).iter().all(|w| *w == 0.0), "single: {:?}", widths(&s));
|
|
}
|