diff --git a/src/entities/curve.rs b/src/entities/curve.rs index 0eede62e8..6a083fca0 100644 --- a/src/entities/curve.rs +++ b/src/entities/curve.rs @@ -114,9 +114,14 @@ pub fn entity_spatial_measurement(entity: &EntityType) -> Option { if polyline.flags.spline_fit { - let degree = match polyline.smooth_type as i16 { 5 => 2, 6 => 3, _ => return None }; - let controls: Vec<_> = polyline.vertices.iter().filter(|vertex| vertex.flags & 16 != 0) - .map(|vertex| [vertex.position.x, vertex.position.y, vertex.position.z]).collect(); + use acadrust::entities::polyline3d::SmoothSurfaceType; + let degree = match polyline.smooth_type { + SmoothSurfaceType::QuadraticBSpline => 2, + SmoothSurfaceType::CubicBSpline | SmoothSurfaceType::Bezier => 3, + SmoothSurfaceType::None => return None, + }; + let controls: Vec<_> = crate::entities::polyline::polyline3d_controls(polyline) + .into_iter().map(|vertex| xyz(vertex.position)).collect(); let curve = NurbsCurve3::from_control_polygon(degree, &controls, polyline.is_closed())?; return ArcLengthCurve3::from_nurbs(curve); } @@ -127,7 +132,7 @@ pub fn entity_spatial_measurement(entity: &EntityType) -> Option { if crate::entities::spline::uses_fit_method(spline) { - let points: Vec<_> = spline.fit_points.iter().copied().map(xyz).collect(); + let mut points: Vec<_> = spline.fit_points.iter().copied().map(xyz).collect(); let parameterization = match spline.knot_parameterization { 1 => Parameterization::Centripetal, 2 => Parameterization::Uniform, _ => Parameterization::Chord, @@ -135,9 +140,15 @@ pub fn entity_spatial_measurement(entity: &EntityType) -> Option 1e-18).then_some(value) + }; + NurbsCurve3::interpolate_fit(&points, tangent(spline.begin_tangent), + tangent(spline.end_tangent), parameterization)? } } else { let controls: Vec<_> = spline.control_points.iter().copied().map(xyz).collect(); @@ -730,6 +741,42 @@ mod tests { assert!(entity_curve(&EntityType::Spline(spline)).is_none()); } + #[test] + fn spatial_polyline_measurement_uses_unflagged_fit_controls() { + use acadrust::entities::polyline3d::SmoothSurfaceType; + use acadrust::entities::{Polyline3D, Vertex3DPolyline}; + + let mut polyline = Polyline3D::new(); + polyline.flags.spline_fit = true; + polyline.smooth_type = SmoothSurfaceType::Bezier; + polyline.vertices = vec![ + Vertex3DPolyline::from_xyz(0.0, 0.0, 0.0), + Vertex3DPolyline::from_xyz(1.0, 2.0, 1.0), + Vertex3DPolyline::from_xyz(3.0, 1.0, 3.0), + Vertex3DPolyline::from_xyz(5.0, 4.0, 2.0), + ]; + + let curve = entity_spatial_measurement(&EntityType::Polyline3D(polyline)).unwrap(); + assert!(curve.length().is_finite() && curve.length() > 0.0); + } + + #[test] + fn spatial_fit_spline_accepts_unset_tangents_and_closes() { + let mut spline = SplineEnt::default(); + spline.degree = 3; + spline.flags.closed = true; + spline.fit_points = vec![ + v3(0.0, 0.0, 0.0), + v3(2.0, 0.0, 1.0), + v3(2.0, 2.0, 3.0), + v3(0.0, 2.0, 1.0), + ]; + + let curve = entity_spatial_measurement(&EntityType::Spline(spline)).unwrap(); + assert!(curve.is_closed()); + assert!(curve.length().is_finite() && curve.length() > 0.0); + } + fn hints(snap: &CurveSnap, want: SnapHint) -> Vec { snap.snap_pts .iter() diff --git a/src/entities/polyline.rs b/src/entities/polyline.rs index 5b23a3ead..fcbdedfa6 100644 --- a/src/entities/polyline.rs +++ b/src/entities/polyline.rs @@ -900,7 +900,7 @@ pub(crate) fn polyline3d_control_vertex_count(pl: &Polyline3D) -> usize { polyline3d_control_indices(pl).len() } -fn polyline3d_controls(pl: &Polyline3D) -> Vec { +pub(crate) fn polyline3d_controls(pl: &Polyline3D) -> Vec { polyline3d_control_indices(pl) .into_iter() .filter_map(|index| pl.vertices.get(index).cloned())