From f80e9f742c8aabef44ca9ed694b3841adf1e9aa5 Mon Sep 17 00:00:00 2001 From: ramox81 <184937705+ramox81@users.noreply.github.com> Date: Fri, 11 Sep 2026 10:25:11 +0300 Subject: [PATCH] Reverse selected curves while preserving spline shape --- src/app/commands/draw.rs | 20 +++++++++++++++++--- src/modules/draw/modify/reverse.rs | 14 ++++++++------ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/app/commands/draw.rs b/src/app/commands/draw.rs index cf848ff7b..7c5ce76ce 100644 --- a/src/app/commands/draw.rs +++ b/src/app/commands/draw.rs @@ -596,9 +596,23 @@ impl OpenCADStudio { "REVERSE" => { use crate::modules::draw::modify::reverse::ReverseCommand; - let new_cmd = ReverseCommand::new(); - self.command_line.push_info(&new_cmd.prompt()); - self.tabs[i].active_cmd = Some(Box::new(new_cmd)); + if self.tabs[i].scene.selected.is_empty() { + use crate::modules::draw::select::SelectObjectsCommand; + let selection = SelectObjectsCommand::new("REVERSE"); + self.command_line.push_info(&selection.prompt()); + self.tabs[i].active_cmd = Some(Box::new(selection)); + } else { + let replacements = self.tabs[i].scene.selected_entities().into_iter() + .filter(|(handle, _)| !self.tabs[i].scene.is_layer_locked(*handle)) + .filter_map(|(handle, entity)| ReverseCommand::reversed(entity) + .map(|reversed| (handle, vec![reversed]))) + .collect::>(); + if !replacements.is_empty() { + return Some(self.apply_cmd_result(crate::command::CmdResult::ReplaceMany( + replacements, Vec::new(), + ))); + } + } } "MEASUREGEOM" => { diff --git a/src/modules/draw/modify/reverse.rs b/src/modules/draw/modify/reverse.rs index 3d7e3d51e..4e2cfdffd 100644 --- a/src/modules/draw/modify/reverse.rs +++ b/src/modules/draw/modify/reverse.rs @@ -54,7 +54,7 @@ impl ReverseCommand { } /// Build a reversed copy of `entity`, or `None` for an unsupported type. - fn reversed(entity: &EntityType) -> Option { + pub fn reversed(entity: &EntityType) -> Option { match entity { EntityType::Line(line) => { let mut out = line.clone(); @@ -176,9 +176,8 @@ fn reverse_polyline2d(pl: &acadrust::entities::Polyline2D) -> acadrust::entities out } -/// Reverse a Spline: flip control points and fit points, regenerate the -/// clamped knot vector. Mirrors `splinedit::apply_spline_op`'s REVERSE branch. -fn reverse_spline(sp: &Spline) -> Spline { +/// Reverse stored coordinates in 3D and let the kernel mirror the knot domain. +pub(super) fn reverse_spline(sp: &Spline) -> Spline { let mut out = sp.clone(); out.control_points.reverse(); out.fit_points.reverse(); @@ -186,8 +185,11 @@ fn reverse_spline(sp: &Spline) -> Spline { if out.weights.len() == out.control_points.len() { out.weights.reverse(); } - out.knots = - Spline::generate_clamped_knots(out.degree as usize, out.control_points.len()); + if let Some(curve) = super::spline_ops::spline_to_nurbs(sp) { + out.knots = curve.reversed().knots().to_vec(); + } + out.begin_tangent = acadrust::types::Vector3::new(-sp.end_tangent.x, -sp.end_tangent.y, -sp.end_tangent.z); + out.end_tangent = acadrust::types::Vector3::new(-sp.begin_tangent.x, -sp.begin_tangent.y, -sp.begin_tangent.z); out }