This commit is contained in:
parent
a983cf1ed0
commit
74cbdf7e2f
|
|
@ -1171,6 +1171,8 @@ fn run_breakat(s: &mut Session, p: &Value) -> Result<Value> {
|
|||
pub(crate) fn join(s: &mut Session, hs: &[Handle]) -> Result<Option<Handle>> {
|
||||
let d = s.doc()?;
|
||||
let mut segs: Vec<Segment> = Vec::new();
|
||||
// Source entity of each segment in `segs`, so only entities that end up in the chain are removed.
|
||||
let mut owners: Vec<Handle> = Vec::new();
|
||||
let mut common = None;
|
||||
for h in hs {
|
||||
let Some(e) = d.entity(*h) else { continue };
|
||||
|
|
@ -1181,12 +1183,14 @@ pub(crate) fn join(s: &mut Session, hs: &[Handle]) -> Result<Option<Handle>> {
|
|||
EntityKind::LwPolyline(p) if !p.closed => segs.extend(Polyline { vertices: p.vertices.clone(), closed: false }.segments()),
|
||||
_ => {}
|
||||
}
|
||||
owners.resize(segs.len(), *h);
|
||||
}
|
||||
if segs.len() < 2 {
|
||||
return Err(EngineError::Other("Select at least two objects to join.".into()));
|
||||
}
|
||||
// Chain greedily from the first segment.
|
||||
let mut chain = vec![segs.remove(0)];
|
||||
let mut used = vec![owners.remove(0)];
|
||||
let tol = 1e-6;
|
||||
loop {
|
||||
let start = chain.first().map(Segment::start).unwrap_or_default();
|
||||
|
|
@ -1197,6 +1201,7 @@ pub(crate) fn join(s: &mut Session, hs: &[Handle]) -> Result<Option<Handle>> {
|
|||
break;
|
||||
};
|
||||
let s0 = segs.remove(i);
|
||||
used.push(owners.remove(i));
|
||||
if s0.start().near(end, tol) {
|
||||
chain.push(s0);
|
||||
} else if s0.end().near(end, tol) {
|
||||
|
|
@ -1227,7 +1232,7 @@ pub(crate) fn join(s: &mut Session, hs: &[Handle]) -> Result<Option<Handle>> {
|
|||
}
|
||||
let space = s.space();
|
||||
let doc = s.doc_mut()?;
|
||||
for h in hs {
|
||||
for h in hs.iter().filter(|h| used.contains(*h)) {
|
||||
doc.remove_entity(*h);
|
||||
}
|
||||
let nh = doc.new_handle();
|
||||
|
|
@ -1318,7 +1323,11 @@ impl SelectThen {
|
|||
}
|
||||
Op::Join => {
|
||||
match join(s, &self.objs) {
|
||||
Ok(_) => s.echo(format!("{} objects joined into 1 polyline", self.objs.len())),
|
||||
Ok(_) => {
|
||||
// Objects outside the joined chain are kept, so count only the ones JOIN consumed.
|
||||
let n = s.doc().map(|d| self.objs.iter().filter(|h| d.entity(**h).is_none()).count()).unwrap_or(0);
|
||||
s.echo(format!("{n} objects joined into 1 polyline"))
|
||||
}
|
||||
Err(e) => s.echo(e.to_string()),
|
||||
}
|
||||
Ok(Step::Done)
|
||||
|
|
|
|||
|
|
@ -94,6 +94,23 @@ fn pedit_json_options() {
|
|||
assert!(s.execute("pedit", &json!({"handle": c, "option": "close"})).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn join_keeps_selected_objects_outside_the_chain() {
|
||||
let mut s = Session::new();
|
||||
let a = h(&s.execute("line", &json!({"points": [[0, 0], [5, 0]]})).unwrap());
|
||||
let b = h(&s.execute("line", &json!({"points": [[5, 0], [5, 5]]})).unwrap());
|
||||
let far = h(&s.execute("line", &json!({"points": [[50, 50], [60, 60]]})).unwrap());
|
||||
let c = h(&s.execute("circle", &json!({"center": [50, 50], "radius": 2})).unwrap());
|
||||
let r = s.execute("join", &json!({"handles": [a, b, far, c]})).unwrap();
|
||||
let joined = r["handle"].as_str().unwrap().to_string();
|
||||
assert_eq!(poly(&kind(&s, &joined)).vertices.len(), 3);
|
||||
let d = s.doc().unwrap();
|
||||
assert!(d.entity(Handle::parse_hex(&a).unwrap()).is_none());
|
||||
assert!(d.entity(Handle::parse_hex(&b).unwrap()).is_none());
|
||||
assert!(d.entity(Handle::parse_hex(&far).unwrap()).is_some(), "unconnected line stays");
|
||||
assert!(d.entity(Handle::parse_hex(&c).unwrap()).is_some(), "unrelated circle stays");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pedit_command_line() {
|
||||
let mut s = Session::new();
|
||||
|
|
|
|||
Loading…
Reference in New Issue