mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-04 19:15:57 +00:00
SMLib export: a NURBS edge's interval a rounding past its knots is the NURBS's own
SMLib can give a NURBS edge an interval that starts or ends a rounding past its
curve's knots: the end of the variable round r1 -> r5 run on into a box corner's
R5 round (ladder 07-fillets/012), trimmed by the R5 cylinder, started 7e-15 before
the first knot, and the record failed validation ("parameter interval outside
the natural domain"). Such an interval is moved onto the knots, as a face's
domain side is onto its surface's natural range (`onto`); farther past them is
still an error. SMLib 7e917f0 (c0d74ee..6e77631: the trace's reach set before
the corners) moved these ends by 1e-11, which is how the case regressed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -531,11 +531,14 @@ fn curves(flat: View, n_edges: usize) -> Res<(Vec<(b::Curve, Option<b::Curve>, [
|
||||
}
|
||||
}
|
||||
CURVE_NURBS => {
|
||||
let c = next_nurbs()?;
|
||||
let c = next_nurbs()?.record();
|
||||
// A NURBS edge stays the NURBS SMLib holds, its trims on its own parameter: SMLib
|
||||
// gives lines, circles and ellipses as their own kinds, and nothing is recognised here.
|
||||
// SMLib's interval may start or end a rounding past its knots (a variable round's
|
||||
// end trimmed by a cylinder, ladder 07-fillets/012: 7e-15 before the first knot).
|
||||
kept += 1;
|
||||
(c.record(), None, r, Some([1.0, 0.0]))
|
||||
let r = onto(r, c.natural());
|
||||
(c, None, r, Some([1.0, 0.0]))
|
||||
}
|
||||
other => return Err(unsupported(format!("SMLib curve kind {other}"))),
|
||||
});
|
||||
@@ -626,8 +629,8 @@ fn mapped(trim: &b::Curve2, same: bool, [scale, offset]: [f64; 2], [t0, t1]: [f6
|
||||
})
|
||||
}
|
||||
|
||||
/// A domain side moved onto the natural range where it overshoots it by rounding (a sphere's
|
||||
/// latitudes, a cone's apex).
|
||||
/// A domain side or an edge's interval moved onto the natural range where it overshoots it by
|
||||
/// rounding (a sphere's latitudes, a cone's apex, a NURBS edge's end knots).
|
||||
fn onto(r: [f64; 2], natural: b::Natural) -> [f64; 2] {
|
||||
match natural {
|
||||
b::Natural::Range(lo, hi) => {
|
||||
|
||||
@@ -227,6 +227,49 @@ mod native_tests {
|
||||
assert!((got - want).abs() < 1e-6 * want, "volume {got} vs {want}");
|
||||
}
|
||||
|
||||
/// The variable round r1 -> r5 along a box's top back edge run on into the R5 round of its
|
||||
/// corner (ladder 07-fillets/012): its end trimmed by the R5 cylinder is a NURBS edge, whose
|
||||
/// interval SMLib gave 7e-15 before the first knot. The record's interval is the NURBS's own
|
||||
/// where it overshoots it by rounding, and the record validates; farther past it stays an error.
|
||||
#[test]
|
||||
fn a_nurbs_edge_interval_a_rounding_past_its_knots_is_moved_onto_them() {
|
||||
let block = Brep::make_box([0.0; 3], [60.0, 40.0, 20.0]).unwrap();
|
||||
let rounded = block.fillet(&[[60.0, 40.0, 10.0]], 0.1, 5.0).unwrap();
|
||||
let r = rounded.fillet_variable([27.5, 40.0, 20.0], 0.1, [0.0, 40.0, 20.0], 1, &[0.0, 0.0, 1.0, 1.0], &[1.0, 5.0]).unwrap();
|
||||
let arrays = r.arrays().unwrap();
|
||||
let kinds = arrays.ints.get(&EDGE_CURVE_TYPE).cloned().unwrap_or_default();
|
||||
let counts = arrays.ints.get(&EDGE_NURBS_VERTEX_COUNT).cloned().unwrap_or_default();
|
||||
let orders = arrays.ints.get(&EDGE_NURBS_ORDER).cloned().unwrap_or_default();
|
||||
let forms = arrays.ints.get(&EDGE_NURBS_FORM).cloned().unwrap_or_default();
|
||||
let knots = arrays.reals.get(&EDGE_NURBS_KNOTS).cloned().unwrap_or_default();
|
||||
// Each NURBS block's first and last knots of its domain, in edge order.
|
||||
let (mut at, mut block_of) = (0usize, Vec::new());
|
||||
for (n, o) in counts.iter().zip(&orders) {
|
||||
let (n, o) = (*n as usize, *o as usize);
|
||||
block_of.push((knots[at + o - 1], knots[at + n]));
|
||||
at += n + o;
|
||||
}
|
||||
let mut nurbs = 0usize;
|
||||
let mut edge = None;
|
||||
for (e, kind) in kinds.iter().enumerate() {
|
||||
let has_block = *kind == CURVE_NURBS || forms.get(e) == Some(&1);
|
||||
if *kind == CURVE_NURBS && edge.is_none() {
|
||||
edge = Some((e, block_of[nurbs]));
|
||||
}
|
||||
nurbs += usize::from(has_block);
|
||||
}
|
||||
let (e, (first, last)) = edge.expect("the round's end on the cylinder is a NURBS edge");
|
||||
let shifted = |by: f64| {
|
||||
let mut a = arrays.clone();
|
||||
a.reals.get_mut(&EDGE_RANGE).unwrap()[2 * e] = first - by;
|
||||
a
|
||||
};
|
||||
let (record, _) = native(&shifted(7e-15)).unwrap();
|
||||
record.validate().unwrap();
|
||||
assert!(record.edges.iter().any(|x| x.interval[0] == first && x.interval[1] <= last), "an interval on the NURBS's first knot");
|
||||
assert!(native(&shifted(1e-3)).map(|(r, _)| r.validate()).map_or(true, |v| v.is_err()), "a real overshoot is not moved");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hostile_arrays_are_errors_never_panics() {
|
||||
let good = Brep::make_box([0.0; 3], [1.0, 2.0, 3.0]).unwrap().arrays().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user