From 30b6441ef9c1074c8829ce13747cd1322c5950d8 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Fri, 11 Sep 2026 13:33:17 +0300 Subject: [PATCH] Complete bounded spline polyline conversion --- Cargo.lock | 4 +- Cargo.toml | 4 +- src/modules/draw/modify/splinedit.rs | 91 +++++++++++++++++++++++++++- 3 files changed, 94 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d894754b2..e52a93f49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -894,7 +894,7 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "cadkernel" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=979e668a2a81ef28147b694510b05f6e87bb96c5#979e668a2a81ef28147b694510b05f6e87bb96c5" +source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=125cfe4f956d1f16c795cc13a653cd0a3aecb9e1#125cfe4f956d1f16c795cc13a653cd0a3aecb9e1" dependencies = [ "acadrust", "cavalier_contours", @@ -905,7 +905,7 @@ dependencies = [ [[package]] name = "cadkernel-constraints" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=979e668a2a81ef28147b694510b05f6e87bb96c5#979e668a2a81ef28147b694510b05f6e87bb96c5" +source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=125cfe4f956d1f16c795cc13a653cd0a3aecb9e1#125cfe4f956d1f16c795cc13a653cd0a3aecb9e1" dependencies = [ "nalgebra", ] diff --git a/Cargo.toml b/Cargo.toml index 1046cf65a..32d92345a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,8 +31,8 @@ rfd = "0.17" clap = { version = "4", features = ["derive", "string"] } env_logger = "0.11" acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "b49cbbfc49ba267712045831a601d2f3d0ed386a", features = ["serde"] } -cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "979e668a2a81ef28147b694510b05f6e87bb96c5", features = ["acis", "offset"] } -cadkernel-constraints = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "979e668a2a81ef28147b694510b05f6e87bb96c5" } +cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "125cfe4f956d1f16c795cc13a653cd0a3aecb9e1", features = ["acis", "offset"] } +cadkernel-constraints = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "125cfe4f956d1f16c795cc13a653cd0a3aecb9e1" } dwg-thumbnailer = { path = "crates/dwg-thumbnailer" } flate2 = "1" image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] } diff --git a/src/modules/draw/modify/splinedit.rs b/src/modules/draw/modify/splinedit.rs index d1191b45f..6664f3889 100644 --- a/src/modules/draw/modify/splinedit.rs +++ b/src/modules/draw/modify/splinedit.rs @@ -205,7 +205,7 @@ impl CadCommand for SplineditCommand { match self.step { Step::SelectSpline => crate::t!("SPLINEDIT Select spline:").into_owned(), Step::Options if self.closed() => crate::t!("SPLINEDIT [Open/Move vertex/Refine/rEverse/convert to Polyline/Undo/eXit] :").into_owned(), - Step::Options => crate::t!("SPLINEDIT [Close/Join/Move vertex/Refine/rEverse/Undo/eXit] :").into_owned(), + Step::Options => crate::t!("SPLINEDIT [Close/Join/Move vertex/Refine/rEverse/convert to Polyline/Undo/eXit] :").into_owned(), Step::PolylinePrecision => crate::t!("SPLINEDIT Specify precision 0-99 <10> (straight segments):").into_owned(), Step::Join => crate::t!("SPLINEDIT Select any open curves to join to source:").into_owned(), Step::Refine => crate::t!("SPLINEDIT [Add/Delete/Elevate order/Move/Weight/eXit] :").into_owned(), @@ -674,6 +674,95 @@ mod tests { assert_eq!(command.picked_vertex(DVec3::new(-0.49, 0.0, 0.0)), Some(0)); assert_eq!(command.picked_vertex(DVec3::new(0.0, 0.5, 0.0)), None); } + + #[test] + fn planar_polyline_conversion_honors_source_deletion_policy() { + let handle = Handle::new(41); + let mut spline = control_spline( + &[ + [0.0, 0.0, 2.0], + [1.0, 2.0, 2.0], + [3.0, 0.0, 2.0], + ], + 2, + ); + spline.common.handle = handle; + spline.flags.planar = true; + let mut command = SplineditCommand::new().with_delete_source(false); + command.inject_picked_entity(EntityType::Spline(spline)); + command.on_entity_pick(handle, DVec3::ZERO); + let CmdResult::ReplaceMany(replacements, additions) = command.convert_polyline(10) else { + panic!("expected a polyline conversion"); + }; + assert!(replacements.is_empty()); + let [EntityType::LwPolyline(polyline)] = additions.as_slice() else { + panic!("expected a lightweight polyline"); + }; + assert!(polyline.common.handle.is_null()); + assert_eq!(polyline.elevation, 2.0); + assert!(polyline.vertices.len() > 2); + } + + #[test] + fn nonplanar_spline_converts_to_a_spatial_polyline() { + let spline = control_spline( + &[ + [0.0, 0.0, 0.0], + [1.0, 0.0, 1.0], + [2.0, 1.0, 0.0], + [3.0, 0.0, 2.0], + ], + 3, + ); + let mut command = selected_command(spline); + let CmdResult::ReplaceMany(replacements, additions) = command.convert_polyline(10) else { + panic!("expected a polyline conversion"); + }; + assert!(additions.is_empty()); + let [(_, entities)] = replacements.as_slice() else { + panic!("expected one source replacement"); + }; + let [EntityType::Polyline3D(polyline)] = entities.as_slice() else { + panic!("expected a spatial polyline"); + }; + assert!(polyline.vertices.len() > 2); + } + + #[test] + fn closed_conversion_closes_once_and_invalid_precision_retries() { + let curve = cadkernel::space::NurbsCurve3::from_control_polygon( + 2, + &[ + [0.0, 0.0, 0.0], + [2.0, 0.0, 0.0], + [2.0, 2.0, 0.0], + [0.0, 2.0, 0.0], + ], + true, + ) + .unwrap(); + let mut spline = control_spline(curve.control_points(), curve.degree()); + spline.knots = curve.knots().to_vec(); + spline.weights = curve.weights().to_vec(); + spline.flags.closed = true; + spline.flags.periodic = true; + let mut command = selected_command(spline); + command.on_text_input("POLYLINE"); + assert!(matches!(command.on_text_input("100"), Some(CmdResult::ReportError(_)))); + assert!(matches!(command.step, Step::PolylinePrecision)); + let CmdResult::ReplaceMany(replacements, _) = command.on_enter() else { + panic!("expected the default conversion"); + }; + let [(_, entities)] = replacements.as_slice() else { + panic!("expected one source replacement"); + }; + let [EntityType::LwPolyline(polyline)] = entities.as_slice() else { + panic!("expected a lightweight polyline"); + }; + assert!(polyline.is_closed); + assert!(polyline.vertices.len() >= 4); + assert_ne!(polyline.vertices.first().unwrap().location, polyline.vertices.last().unwrap().location); + } } // ── Autocomplete registry ─────────────────────────────────