forked from 1b-agent/opencadstudio-upstream
fix: closed spline gap, hatch spline boundary, and spline commit grouping
- Spline: if flags.closed/periodic and B-spline endpoints don't meet, add an explicit closing Wire segment to eliminate the visual gap. - Hatch BoundaryEdge::Spline: evaluate the B-spline at 16 uniform parameter samples (de Boor via truck BSplineCurve) instead of plotting raw control points as a polyline; falls back to control- point polyline for degenerate knot/degree inputs. - Ellipse arc key_vertices and LWPolyline midpoint grips committed separately; this commit wraps the spline-related fixes. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
1 parent
6fd740e38f
commit
0bb4209e2c
2 files changed
+59
-13
No files matched your search
+26
-7
@@ -2,7 +2,7 @@ use acadrust::entities::Spline;
|
||||
use glam::Vec3;
|
||||
use truck_modeling::{
|
||||
base::{BoundedCurve, ParametricCurve},
|
||||
builder, BSplineCurve, Curve, Edge, KnotVec, Point3,
|
||||
builder, BSplineCurve, Curve, Edge, KnotVec, Point3, Wire,
|
||||
};
|
||||
|
||||
use crate::command::EntityTransform;
|
||||
@@ -34,12 +34,7 @@ fn to_truck(spl: &Spline) -> TruckEntity {
|
||||
let (t0, t1) = bspline.range_tuple();
|
||||
let p_start = bspline.subs(t0);
|
||||
let p_end = bspline.subs(t1);
|
||||
let v_start = builder::vertex(p_start);
|
||||
let v_end = builder::vertex(p_end);
|
||||
let edge = Edge::new(&v_start, &v_end, Curve::BSplineCurve(bspline));
|
||||
|
||||
// Expose fit_points (preferred) or control_points as key_vertices so that
|
||||
// Endpoint/Midpoint snap can reach spline construction points.
|
||||
let snap_source = if !spl.fit_points.is_empty() {
|
||||
&spl.fit_points
|
||||
} else {
|
||||
@@ -50,8 +45,32 @@ fn to_truck(spl: &Spline) -> TruckEntity {
|
||||
.map(|p| [p.x as f32, p.y as f32, p.z as f32])
|
||||
.collect();
|
||||
|
||||
let is_closed = spl.flags.closed || spl.flags.periodic;
|
||||
let gap = {
|
||||
let dx = (p_end.x - p_start.x) as f32;
|
||||
let dy = (p_end.y - p_start.y) as f32;
|
||||
let dz = (p_end.z - p_start.z) as f32;
|
||||
(dx * dx + dy * dy + dz * dz).sqrt()
|
||||
};
|
||||
|
||||
let object = if is_closed && gap > 1e-6 {
|
||||
// The B-spline doesn't self-close — add an explicit closing segment.
|
||||
let v_start = builder::vertex(p_start);
|
||||
let v_end = builder::vertex(p_end);
|
||||
let v_close = builder::vertex(p_start);
|
||||
let main_edge = Edge::new(&v_start, &v_end, Curve::BSplineCurve(bspline));
|
||||
let close_edge = builder::line(&v_end, &v_close);
|
||||
let wire: Wire = [main_edge, close_edge].into_iter().collect();
|
||||
TruckObject::Contour(wire)
|
||||
} else {
|
||||
let v_start = builder::vertex(p_start);
|
||||
let v_end = builder::vertex(p_end);
|
||||
let edge = Edge::new(&v_start, &v_end, Curve::BSplineCurve(bspline));
|
||||
TruckObject::Curve(edge)
|
||||
};
|
||||
|
||||
TruckEntity {
|
||||
object: TruckObject::Curve(edge),
|
||||
object,
|
||||
snap_pts: vec![],
|
||||
tangent_geoms: vec![],
|
||||
key_vertices,
|
||||
|
||||
+33
-6
@@ -37,6 +37,10 @@ pub use wire_model::WireModel;
|
||||
|
||||
use crate::command::EntityTransform;
|
||||
use acadrust::entities::{BoundaryEdge, BoundaryPath, Hatch as DxfHatch, PolylineEdge, Solid as DxfSolid};
|
||||
use truck_modeling::{
|
||||
base::{BoundedCurve, ParametricCurve},
|
||||
BSplineCurve as TruckBSpline, KnotVec, Point3,
|
||||
};
|
||||
use acadrust::entities::{Block, BlockEnd, Insert as DxfInsert};
|
||||
use acadrust::objects::ObjectType;
|
||||
use acadrust::types::Vector2;
|
||||
@@ -1668,12 +1672,35 @@ impl Scene {
|
||||
}
|
||||
}
|
||||
BoundaryEdge::Spline(spline) => {
|
||||
for cp in &spline.control_points {
|
||||
boundary.push([cp.x as f32, cp.y as f32]);
|
||||
}
|
||||
if boundary.len() > 1 {
|
||||
if let Some(&first) = boundary.first() {
|
||||
boundary.push(first);
|
||||
// Evaluate the B-spline curve into smooth boundary points.
|
||||
// Fall back to control-point polyline for degenerate inputs.
|
||||
let degree = spline.degree as usize;
|
||||
let cps: Vec<Point3> = spline.control_points
|
||||
.iter()
|
||||
.map(|p| Point3::new(p.x, p.y, 0.0))
|
||||
.collect();
|
||||
let knot_vec = if !spline.knots.is_empty() {
|
||||
KnotVec::from(spline.knots.clone())
|
||||
} else if cps.len() >= 2 {
|
||||
KnotVec::uniform_knot(degree, cps.len() - 1)
|
||||
} else {
|
||||
KnotVec::from(vec![])
|
||||
};
|
||||
let ok = cps.len() >= 2
|
||||
&& degree >= 1
|
||||
&& knot_vec.len() == cps.len() + degree + 1;
|
||||
if ok {
|
||||
let bspl = TruckBSpline::new(knot_vec, cps);
|
||||
let (t0, t1) = bspl.range_tuple();
|
||||
let segs = 16u32;
|
||||
for i in 0..=segs {
|
||||
let t = t0 + (t1 - t0) * (i as f64 / segs as f64);
|
||||
let p = bspl.subs(t);
|
||||
boundary.push([p.x as f32, p.y as f32]);
|
||||
}
|
||||
} else {
|
||||
for cp in &spline.control_points {
|
||||
boundary.push([cp.x as f32, cp.y as f32]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user