Class-A surfacing 3/3: blend curves, curved-edge fillets, the G2 corner and continuity analysis (#1642)
Thanks @midasdf.
This commit is contained in:
1 parent
3126962cd4
commit
bbd4f29ebb
43 files changed
+4753
-132
No files matched your search
@@ -1,6 +1,6 @@
|
||||
use std::f64::consts::{FRAC_PI_2, PI};
|
||||
|
||||
use solidcraft_geom::Vec2;
|
||||
use solidcraft_geom::{Seg2, Vec2};
|
||||
|
||||
use crate::*;
|
||||
|
||||
@@ -921,3 +921,49 @@ fn projected_boundary_keeps_its_pieces_when_only_stations_go() {
|
||||
assert!(sk.update_link(&j, &smaller).unwrap());
|
||||
assert_eq!(sk.link_curves(&j).len(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_degree_nine_control_spline_stays_exact_and_closes_a_profile_exactly() {
|
||||
// A G4 blend curve is one degree-9 Bézier; a sketch keeps it as a control spline whose exact
|
||||
// piece is that Bézier, and profile measurement must keep using the curve, not its chords.
|
||||
let mut sk = Sketch::new();
|
||||
let bump =
|
||||
[v(0.0, 0.0), v(1.5, 4.0), v(3.0, 7.0), v(5.0, 9.5), v(7.5, 11.0), v(12.5, 11.0), v(15.0, 9.5), v(17.0, 7.0), v(18.5, 4.0), v(20.0, 0.0)];
|
||||
let pts: Vec<usize> = bump.iter().map(|q| sk.add_point(*q, None).unwrap()).collect();
|
||||
let s = sk.add_curve(CurveKind::Spline { pts: pts.clone(), control: true, degree: 9, periodic: false }, None).unwrap();
|
||||
let pieces = sk.exact_pieces(s);
|
||||
assert_eq!(pieces.len(), 1, "{pieces:?}");
|
||||
let Seg2::Bezier { n, p } = pieces[0] else { panic!("{:?}", pieces[0]) };
|
||||
assert_eq!(n, 9, "the highest degree a sketch piece can carry");
|
||||
for (i, q) in bump.iter().enumerate() {
|
||||
assert!(p[i].dist(*q) < 1e-12, "control {i}");
|
||||
}
|
||||
// Evaluation and the exact piece agree at the spline's real degree (a degree clamp would show
|
||||
// here), and a line's own exact piece still comes back as a line.
|
||||
let exact = pieces[0];
|
||||
for k in 0..=10 {
|
||||
let t = k as f64 / 10.0;
|
||||
assert!(crate::curves::spline_point(&bump, true, 9, t).dist(exact.point_at(t)) < 1e-9, "t={t}");
|
||||
}
|
||||
let l = sk.add_line(v(-5.0, 0.0), v(-2.0, 1.0), None, None, None).unwrap();
|
||||
assert!(matches!(sk.exact_pieces(l).as_slice(), [Seg2::Line { .. }]));
|
||||
// Close the loop: the region's area is the shoelace integral of the exact pieces, which a fine
|
||||
// sampling of the piece reproduces (a chord approximation would miss the bump's sagitta).
|
||||
sk.add_line(v(20.0, 0.0), v(0.0, 0.0), Some(pts[9]), Some(pts[0]), None).unwrap();
|
||||
let ps = find_profiles(&sk);
|
||||
assert_eq!(ps.len(), 1, "{ps:?}");
|
||||
let n = 20_000;
|
||||
let mut sampled = 0.0;
|
||||
for i in 0..n {
|
||||
let (t0, t1) = (i as f64 / n as f64, (i + 1) as f64 / n as f64);
|
||||
let (a, b) = (exact.point_at(t0), exact.point_at(t1));
|
||||
sampled += 0.5 * a.cross(b);
|
||||
}
|
||||
sampled -= 0.5 * v(20.0, 0.0).cross(v(0.0, 0.0));
|
||||
assert!((ps[0].area - sampled.abs()).abs() < 1e-5, "{} vs {sampled}", ps[0].area);
|
||||
assert!((ps[0].area - 100.0).abs() > 1e-3, "the region is not its chord: {}", ps[0].area);
|
||||
// A degree above the cap is still refused.
|
||||
let mut high: Vec<usize> = pts.clone();
|
||||
high.push(sk.add_point(v(21.0, -2.0), None).unwrap());
|
||||
assert!(sk.add_curve(CurveKind::Spline { pts: high, control: true, degree: 10, periodic: false }, None).is_err());
|
||||
}
|
||||
Reference in new issue
Block a user