Combine: a cut keeps a body joined from separate pieces in one body (#2247)

Thanks @H1D.
This commit is contained in:
Artem Sushchev authored and GitHub committed 2026-10-18 06:02:44 -04:00
1 parent 5eb7640b54
commit a4c4b411e5
3 files changed
+53 -8

No files matched your search

+10 -6
View File
@@ -1764,8 +1764,9 @@ fn apply_traced(state: &mut ModelState, f: &Feature, tools: Vec<Traced>, op: Ope
Operation::Cut | Operation::Intersect => {
let bop = if op == Operation::Cut { BoolOp::Cut } else { BoolOp::Intersect };
let tools = if op == Operation::Cut { clear_tools_as_one(tools) } else { tools };
let affected: std::collections::BTreeSet<String> =
target_idx.iter().filter_map(|i| state.bodies.get(*i).map(|b| b.name.clone())).collect();
// Each target's pieces before the cut: a body joined from separate pieces stays one.
let affected: std::collections::BTreeMap<String, usize> =
target_idx.iter().filter_map(|i| state.bodies.get(*i)).map(|b| (b.name.clone(), b.body.lumps().map_or(1, |l| l.len()))).collect();
let mut remove = Vec::new();
for &ti in &target_idx {
let Some(mut acc) = state.bodies.get(ti).map(|b| Some(b.traced())) else { continue };
@@ -1793,16 +1794,19 @@ fn apply_traced(state: &mut ModelState, f: &Feature, tools: Vec<Traced>, op: Ope
let live: std::collections::BTreeSet<&str> = state.bodies.iter().map(|b| b.name.as_str()).collect();
state.threads.retain(|t| live.contains(t.body.as_str()));
// A cut can leave a body in pieces: each piece becomes its own body ("Bar", "Bar (1)"),
// numbered on past names already taken (a body cut in pieces before).
// numbered on past names already taken (a body cut in pieces before). Only a cut that
// made more pieces than the body had splits it: the pieces of a join stay together.
// Not yet: a cut that splits one piece of such a body still splits all of them; keeping
// the untouched ones together needs each new piece traced to the piece it came from.
let mut taken: std::collections::BTreeSet<String> = state.bodies.iter().map(|b| b.name.clone()).collect();
let mut split: Vec<ModelBody> = Vec::new();
for mb in std::mem::take(&mut state.bodies) {
if !affected.contains(&mb.name) || state.sheets.iter().any(|s| s.body == mb.name) {
let Some(&had) = affected.get(&mb.name).filter(|_| !state.sheets.iter().any(|s| s.body == mb.name)) else {
split.push(mb);
continue;
}
};
let pieces = mb.body.lumps().unwrap_or_else(|_| vec![mb.body.clone()]);
if pieces.len() < 2 {
if pieces.len() <= had {
split.push(mb);
continue;
}