From 356c8b5a7b5549458620ec98e0d05ebe44976902 Mon Sep 17 00:00:00 2001 From: ramox81 <184937705+ramox81@users.noreply.github.com> Date: Fri, 11 Sep 2026 12:57:58 +0300 Subject: [PATCH] Keep multiple polyline editing active across selected joins --- src/app/command_driver.rs | 30 ++++++++++++++++++++++++++++++ src/modules/draw/modify/pedit.rs | 21 +++++++++++++++++---- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/app/command_driver.rs b/src/app/command_driver.rs index aed88b26a..0017ad2b2 100644 --- a/src/app/command_driver.rs +++ b/src/app/command_driver.rs @@ -3216,6 +3216,36 @@ impl OpenCADStudio { apply_pedit, convert_to_polyline, PeditOp, }; match &op { + PeditOp::JoinSelection(handles) => { + let mut available = handles.iter().filter_map(|handle| { + if self.tabs[i].scene.is_layer_locked(*handle) { return None; } + self.tabs[i].scene.document.get_entity(*handle).cloned().map(|entity| (*handle, entity)) + }).collect::>(); + let mut changes = Vec::new(); + while !available.is_empty() { + let (source_handle, source) = available.remove(0); + let candidates = available.iter().map(|(handle, entity)| (*handle, entity)).collect::>(); + if let Some((mut result, consumed)) = crate::modules::draw::modify::join::join_to_source(&source, &candidates) { + *result.common_mut() = source.common().clone(); + available.retain(|(handle, _)| !consumed.contains(handle)); + changes.push((source_handle, result, consumed)); + } + } + if !changes.is_empty() { + self.push_undo_snapshot(i, "PEDIT"); + for (source, replacement, consumed) in changes { + self.tabs[i].scene.erase_entities(&consumed); + self.tabs[i].scene.update_entity(replacement.clone()); + if let Some(command) = self.tabs[i].active_cmd.as_mut() { + for old in consumed { command.on_entity_replaced(old, &[source]); } + command.inject_picked_entity(replacement); + } + } + if let Some(command) = self.tabs[i].active_cmd.as_mut() { command.on_pedit_applied(); } + self.tabs[i].dirty = true; + self.refresh_properties(); + } + } PeditOp::Multiple(handles, operation) => { let replacements: Vec<_> = handles.iter().filter(|handle| !self.tabs[i].scene.is_layer_locked(**handle)).filter_map(|handle| { let original = self.tabs[i].scene.document.get_entity(*handle)?; diff --git a/src/modules/draw/modify/pedit.rs b/src/modules/draw/modify/pedit.rs index 4450f0313..963d9d350 100644 --- a/src/modules/draw/modify/pedit.rs +++ b/src/modules/draw/modify/pedit.rs @@ -31,6 +31,7 @@ enum Mode { PickTarget, MultipleGather, MultipleConvert, + MultipleJoin, Options, /// Picked a Line/Arc: asking "Turn it into one? [Yes/No]". ConvertPrompt(Handle), @@ -234,6 +235,7 @@ impl CadCommand for PeditCommand { t!("PEDIT Select polyline (or a line/arc to convert) or [Multiple]:").into_owned() } Mode::MultipleGather => format!("PEDIT Select objects ({} selected, Enter when done):", self.multiple.len()), + Mode::MultipleJoin => "PEDIT Enter fuzz distance <0>:".to_string(), Mode::MultipleConvert => t!("PEDIT Convert lines and arcs to polylines [Yes/No] :").into_owned(), Mode::ConvertPrompt(_) => t!( "PEDIT Object is not a polyline. Turn it into one? [Yes/No] :" @@ -392,6 +394,8 @@ impl CadCommand for PeditCommand { mesh_closed: None, }, ); + let mut seen = std::collections::HashSet::new(); + self.multiple.retain(|handle| seen.insert(*handle)); self.target = Some(nh); self.mode = Mode::Options; } @@ -433,6 +437,13 @@ impl CadCommand for PeditCommand { if matches!(up.as_str(), "M" | "MULTIPLE") { self.mode = Mode::MultipleGather; Some(CmdResult::NeedPoint) } else { None } } Mode::MultipleGather => None, + Mode::MultipleJoin => { + let fuzz = text.trim().parse::().ok()?; + if fuzz != 0.0 { return Some(CmdResult::ReportError("PEDIT: only zero fuzz distance is currently supported.".to_string())); } + self.pending_multiple = Some(self.multiple.clone()); + self.mode = Mode::Options; + return Some(CmdResult::PeditOp { handle: self.target?, op: PeditOp::JoinSelection(self.multiple.clone()) }); + }, Mode::MultipleConvert => { match up.as_str() { "Y" | "YES" | "" => { @@ -443,8 +454,8 @@ impl CadCommand for PeditCommand { "N" | "NO" => { self.multiple.retain(|handle| self.info.get(&handle.value()).is_some_and(|info| info.is_poly)); self.target = self.multiple.first().copied(); - self.mode = Mode::Options; - Some(if self.target.is_some() { CmdResult::NeedPoint } else { CmdResult::Cancel }) + self.mode = if self.target.is_some() { Mode::Options } else { Mode::PickTarget }; + Some(CmdResult::NeedPoint) } _ => Some(CmdResult::NeedPoint), } @@ -665,7 +676,7 @@ impl CadCommand for PeditCommand { Some(CmdResult::NeedPoint) } "J" | "JOIN" => { - self.mode = Mode::JoinGather(vec![handle]); + self.mode = if self.multiple.is_empty() { Mode::JoinGather(vec![handle]) } else { Mode::MultipleJoin }; Some(CmdResult::NeedPoint) } "F" | "FIT" => Some(CmdResult::PeditOp { @@ -751,6 +762,7 @@ impl CadCommand for PeditCommand { CmdResult::NeedPoint } Mode::MultipleConvert => self.on_text_input("Y").unwrap_or(CmdResult::NeedPoint), + Mode::MultipleJoin => self.on_text_input("0").unwrap_or(CmdResult::NeedPoint), Mode::PolyWidthStart(_, width) | Mode::PolyWidthEnd(_, width) => { let value = width.to_string(); self.on_text_input(&value).unwrap_or(CmdResult::NeedPoint) } Mode::PolyVertex(_) | Mode::PolyRange { .. } => self.on_text_input("N").unwrap_or(CmdResult::NeedPoint), Mode::PolyMove(_) | Mode::PolyInsert(_) => { self.mode = Mode::Options; CmdResult::NeedPoint } @@ -788,6 +800,7 @@ impl CadCommand for PeditCommand { #[derive(Clone)] pub enum PeditOp { Multiple(Vec, Box), + JoinSelection(Vec), SetClosed(bool), SetWidth(f64), SetVertexWidth { index: usize, start: f64, end: f64 }, @@ -853,7 +866,7 @@ pub fn edit_vertex_range(entity: &EntityType, first: usize, last: usize, split: pub fn apply_pedit(entity: &mut EntityType, op: &PeditOp) -> bool { match op { - PeditOp::Multiple(_, _) | PeditOp::VertexRange { .. } => false, + PeditOp::Multiple(_, _) | PeditOp::JoinSelection(_) | PeditOp::VertexRange { .. } => false, PeditOp::SetVertexWidth { index, start, end } => { if !start.is_finite() || !end.is_finite() || *start < 0.0 || *end < 0.0 { return false; } match entity {