forked from 1b-agent/opencadstudio-upstream
Merge pull request #1202 from Ramox81/codex/fix-fit-spline-bounds-20260911
This commit is contained in:
commit
8640634e64
3 files changed
+71
-19
No files matched your search
+2
-19
@@ -110,7 +110,7 @@ pub fn entity_curve(entity: &EntityType) -> Option<PlanarCurve> {
|
|||||||
/// Exact spatial source geometry for commands that traverse nonplanar curves.
|
/// Exact spatial source geometry for commands that traverse nonplanar curves.
|
||||||
/// Curve construction and arc-length calculations remain in the kernel.
|
/// Curve construction and arc-length calculations remain in the kernel.
|
||||||
pub fn entity_spatial_measurement(entity: &EntityType) -> Option<cadkernel::space::ArcLengthCurve3> {
|
pub fn entity_spatial_measurement(entity: &EntityType) -> Option<cadkernel::space::ArcLengthCurve3> {
|
||||||
use cadkernel::space::{ArcLengthCurve3, NurbsCurve3, Parameterization};
|
use cadkernel::space::{ArcLengthCurve3, NurbsCurve3};
|
||||||
let curve = match entity {
|
let curve = match entity {
|
||||||
EntityType::Polyline3D(polyline) => {
|
EntityType::Polyline3D(polyline) => {
|
||||||
if polyline.flags.spline_fit {
|
if polyline.flags.spline_fit {
|
||||||
@@ -132,24 +132,7 @@ pub fn entity_spatial_measurement(entity: &EntityType) -> Option<cadkernel::spac
|
|||||||
}
|
}
|
||||||
EntityType::Spline(spline) => {
|
EntityType::Spline(spline) => {
|
||||||
if crate::entities::spline::uses_fit_method(spline) {
|
if crate::entities::spline::uses_fit_method(spline) {
|
||||||
let mut points: Vec<_> = spline.fit_points.iter().copied().map(xyz).collect();
|
crate::entities::spline::fit_nurbs3(spline)?
|
||||||
let parameterization = match spline.knot_parameterization {
|
|
||||||
1 => Parameterization::Centripetal, 2 => Parameterization::Uniform,
|
|
||||||
_ => Parameterization::Chord,
|
|
||||||
};
|
|
||||||
if spline.flags.periodic {
|
|
||||||
NurbsCurve3::interpolate_periodic(&points, parameterization)?
|
|
||||||
} else {
|
|
||||||
if spline.flags.closed && points.first() != points.last() {
|
|
||||||
if let Some(first) = points.first().copied() { points.push(first); }
|
|
||||||
}
|
|
||||||
let tangent = |value: Vector3| {
|
|
||||||
let value = xyz(value);
|
|
||||||
(Vec3::from(value).length_squared() > 1e-18).then_some(value)
|
|
||||||
};
|
|
||||||
NurbsCurve3::interpolate_fit(&points, tangent(spline.begin_tangent),
|
|
||||||
tangent(spline.end_tangent), parameterization)?
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let controls: Vec<_> = spline.control_points.iter().copied().map(xyz).collect();
|
let controls: Vec<_> = spline.control_points.iter().copied().map(xyz).collect();
|
||||||
let weights = if spline.weights.is_empty() { vec![1.0; controls.len()] }
|
let weights = if spline.weights.is_empty() { vec![1.0; controls.len()] }
|
||||||
|
|||||||
@@ -21,6 +21,40 @@ pub(crate) fn shows_fit_points(spline: &Spline) -> bool {
|
|||||||
uses_fit_method(spline) && (!spline.cv_frame_visible || spline.flags.periodic)
|
uses_fit_method(spline) && (!spline.cv_frame_visible || spline.flags.periodic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn fit_nurbs3(spline: &Spline) -> Option<NurbsCurve3> {
|
||||||
|
if !uses_fit_method(spline) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut points: Vec<_> = spline.fit_points.iter()
|
||||||
|
.map(|point| [point.x, point.y, point.z]).collect();
|
||||||
|
let parameterization = match spline.knot_parameterization {
|
||||||
|
1 => Parameterization::Centripetal,
|
||||||
|
2 => Parameterization::Uniform,
|
||||||
|
_ => Parameterization::Chord,
|
||||||
|
};
|
||||||
|
if spline.flags.periodic {
|
||||||
|
return NurbsCurve3::interpolate_periodic(&points, parameterization);
|
||||||
|
}
|
||||||
|
if spline.flags.closed && points.first() != points.last() {
|
||||||
|
if let Some(first) = points.first().copied() { points.push(first); }
|
||||||
|
}
|
||||||
|
let tangent = |value: acadrust::types::Vector3| {
|
||||||
|
let value = [value.x, value.y, value.z];
|
||||||
|
(value.iter().map(|component| component * component).sum::<f64>() > 1e-18)
|
||||||
|
.then_some(value)
|
||||||
|
};
|
||||||
|
NurbsCurve3::interpolate_fit(&points, tangent(spline.begin_tangent),
|
||||||
|
tangent(spline.end_tangent), parameterization)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Conservative world bounds of a fit-method spline. Fit points are interpolation
|
||||||
|
/// constraints, not control-hull vertices, so their box can exclude the curve.
|
||||||
|
/// The shared kernel reconstructs the same spatial interpolation used by the
|
||||||
|
/// curve consumers, then its control hull bounds every point of that curve.
|
||||||
|
pub(crate) fn fit_geometry_bounds(spline: &Spline) -> Option<cadkernel::brep::Aabb> {
|
||||||
|
let curve = fit_nurbs3(spline)?;
|
||||||
|
cadkernel::brep::Aabb::around(curve.control_points().iter().copied())
|
||||||
|
}
|
||||||
fn to_render(spl: &Spline) -> RenderEntity {
|
fn to_render(spl: &Spline) -> RenderEntity {
|
||||||
let n = spl.control_points.len();
|
let n = spl.control_points.len();
|
||||||
if n < 2 {
|
if n < 2 {
|
||||||
@@ -1095,3 +1129,33 @@ impl crate::entities::traits::Transformable for Spline {
|
|||||||
apply_transform(self, t);
|
apply_transform(self, t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fit_bounds_accept_closed_curves_with_unset_tangents() {
|
||||||
|
let mut spline = Spline::default();
|
||||||
|
spline.degree = 3;
|
||||||
|
spline.flags.closed = true;
|
||||||
|
spline.control_points.push(acadrust::types::Vector3::ZERO);
|
||||||
|
spline.fit_points = vec![
|
||||||
|
acadrust::types::Vector3::new(0.0, 0.0, 0.0),
|
||||||
|
acadrust::types::Vector3::new(4.0, 0.0, 1.0),
|
||||||
|
acadrust::types::Vector3::new(4.0, 3.0, 2.0),
|
||||||
|
acadrust::types::Vector3::new(0.0, 3.0, 1.0),
|
||||||
|
];
|
||||||
|
|
||||||
|
let bounds = fit_geometry_bounds(&spline).unwrap();
|
||||||
|
for point in &spline.fit_points {
|
||||||
|
for (value, min, max) in [
|
||||||
|
(point.x, bounds.min[0], bounds.max[0]),
|
||||||
|
(point.y, bounds.min[1], bounds.max[1]),
|
||||||
|
(point.z, bounds.min[2], bounds.max[2]),
|
||||||
|
] {
|
||||||
|
assert!(value >= min - 1e-9 && value <= max + 1e-9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1731,6 +1731,11 @@ fn entity_bounds(e: &acadrust::EntityType) -> ([f64; 3], [f64; 3]) {
|
|||||||
return (bounds.min, bounds.max);
|
return (bounds.min, bounds.max);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let acadrust::EntityType::Spline(spline) = e {
|
||||||
|
if let Some(bounds) = crate::entities::spline::fit_geometry_bounds(spline) {
|
||||||
|
return (bounds.min, bounds.max);
|
||||||
|
}
|
||||||
|
}
|
||||||
let bounds = e.as_entity().bounding_box();
|
let bounds = e.as_entity().bounding_box();
|
||||||
(
|
(
|
||||||
[bounds.min.x, bounds.min.y, bounds.min.z],
|
[bounds.min.x, bounds.min.y, bounds.min.z],
|
||||||
|
|||||||
Reference in new issue
Block a user