feat: Spline support for TRIM, EXTEND, OFFSET, BREAK, and LENGTHEN
Add shared spline_ops module with: - spline_to_bspline / bspline_to_spline: acadrust ↔ truck BSplineCurve conversion - spline_sample_xy: sample DXF-XY projection at N+1 points with parameters - spline_nearest_t: nearest-parameter search via coarse+bisection - spline_pts_wire: world-space wire points for WireModel preview TRIM: Spline entities can now be used as cutting boundaries (Geo::Spline sampled into polyline segments) and as entities being trimmed; uses BSplineCurve::cut() to produce surviving pieces. EXTEND: extend_spline() shoots a tangent ray from the nearest endpoint and appends a control point at the first boundary hit. OFFSET: offset_spline() offsets each sample point along the local perpendicular normal and rebuilds a new spline from the offset control pts. BREAK: break_spline() uses spline_nearest_t() + BSplineCurve::cut() to split the spline at one or two click points. LENGTHEN: lengthen_spline() uses numerical arc-length integration + bisection to cut the spline at the parameter corresponding to the new requested length. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
71667cfafe
commit
083eb5cbb5
7 changed files with 591 additions and 11 deletions
|
|
@ -7,7 +7,9 @@
|
|||
//
|
||||
// BREAK @ (at-sign as second point) → Break at a single point (splits without gap).
|
||||
|
||||
use acadrust::entities::{Arc as ArcEnt, Ellipse as EllipseEnt, Line as LineEnt, LwPolyline};
|
||||
use acadrust::entities::{Arc as ArcEnt, Ellipse as EllipseEnt, Line as LineEnt, LwPolyline, Spline as SplineEnt};
|
||||
use truck_modeling::base::{BoundedCurve, Cut};
|
||||
use crate::modules::home::modify::spline_ops::{bspline_to_spline, spline_nearest_t, spline_to_bspline};
|
||||
use acadrust::types::Vector3;
|
||||
use acadrust::{EntityType, Handle};
|
||||
use glam::Vec3;
|
||||
|
|
@ -39,6 +41,7 @@ pub fn break_entity(entity: &EntityType, p1: Vec3, p2: Vec3) -> Option<Vec<Entit
|
|||
EntityType::Circle(c) => Some(break_circle(c, p1, p2)),
|
||||
EntityType::LwPolyline(p) => Some(break_lwpolyline(p, p1, p2)),
|
||||
EntityType::Ellipse(e) => Some(break_ellipse(e, p1, p2)),
|
||||
EntityType::Spline(s) => Some(break_spline(s, p1, p2)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -271,6 +274,52 @@ fn vec3_to_v3(v: Vec3) -> Vector3 {
|
|||
Vector3::new(v.x as f64, v.y as f64, v.z as f64)
|
||||
}
|
||||
|
||||
fn break_spline(spl: &SplineEnt, p1: Vec3, p2: Vec3) -> Vec<EntityType> {
|
||||
// Find the two nearest parameters to p1 and p2 (DXF XY: world x, z).
|
||||
let t1 = match spline_nearest_t(spl, p1.x as f64, p1.z as f64) {
|
||||
Some(t) => t,
|
||||
None => return vec![EntityType::Spline(spl.clone())],
|
||||
};
|
||||
let t2 = match spline_nearest_t(spl, p2.x as f64, p2.z as f64) {
|
||||
Some(t) => t,
|
||||
None => return vec![EntityType::Spline(spl.clone())],
|
||||
};
|
||||
|
||||
let bs = match spline_to_bspline(spl) {
|
||||
Some(b) => b,
|
||||
None => return vec![EntityType::Spline(spl.clone())],
|
||||
};
|
||||
let (t0, t_end) = bs.range_tuple();
|
||||
|
||||
let (ta, tb) = if t1 <= t2 { (t1, t2) } else { (t2, t1) };
|
||||
|
||||
// Single-point break (ta ≈ tb): split into two segments at that point.
|
||||
if (tb - ta).abs() < 1e-9 {
|
||||
let mut piece = bs.clone();
|
||||
let right = piece.cut(ta);
|
||||
return vec![
|
||||
EntityType::Spline(bspline_to_spline(&piece, spl)),
|
||||
EntityType::Spline(bspline_to_spline(&right, spl)),
|
||||
];
|
||||
}
|
||||
|
||||
// Two-point break: keep [t0..ta] and [tb..t_end], discard middle.
|
||||
let mut result = vec![];
|
||||
// Left piece [t0, ta]
|
||||
if ta - t0 > 1e-9 {
|
||||
let mut left = bs.clone();
|
||||
let _right = left.cut(ta); // left = [t0, ta]
|
||||
result.push(EntityType::Spline(bspline_to_spline(&left, spl)));
|
||||
}
|
||||
// Right piece [tb, t_end]
|
||||
if t_end - tb > 1e-9 {
|
||||
let mut full = bs.clone();
|
||||
let right = full.cut(tb); // right = [tb, t_end]
|
||||
result.push(EntityType::Spline(bspline_to_spline(&right, spl)));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
// ── CadCommand (simplified — break logic via CmdResult::BreakEntity) ───────
|
||||
|
||||
/// Thin wrapper for commands.rs to register the break command using the
|
||||
|
|
|
|||
Loading…
Reference in a new issue