mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-04 19:15:57 +00:00
geom: the patch fill takes the dense spline edges a blend leaves, and holds what its corners allow
- One patch has a space along u and one along v, each holding its two sides that run that way; a side's knot keeps its continuity when raised (multiplicity m + q - p), and each side is carried in exactly by interpolation at the space's Greville abscissae. - Refinement halves the spans only in the direction whose sides miss, and one patch's normal equations are banded (the shorter direction numbered fastest), so a loop with a long intersection curve refines where it needs to. - At a corner whose neighbours agree to a lower order than asked (their tangent planes, their twist between the two edges, or their forms), the stations near it ask no more than that. - A station is held to the angle tolerance beyond the edge's own lean out of its neighbour's tangent plane: a fill that holds the edge cannot come nearer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+461
-196
File diff suppressed because it is too large
Load Diff
@@ -130,7 +130,7 @@ fn jump(fill: &IsoFill, edges: &[IsoEdge], k: usize) -> f64 {
|
||||
fn a_sphere_window_on_rational_arcs_holds_each_order_asked_and_not_the_next() {
|
||||
let edges = window();
|
||||
for order in 1..=4 {
|
||||
let fill = iso_fill(&edges, &mut sphere_beside(&edges, order), &[false; 4], &spec()).unwrap();
|
||||
let fill = iso_fill(&edges, &mut sphere_beside(&edges, order), &spec()).unwrap();
|
||||
let a = fill.achieved;
|
||||
let measured: Vec<f64> = (2..=4).map(|k| jump(&fill, &edges, k)).collect();
|
||||
eprintln!(
|
||||
@@ -168,7 +168,7 @@ fn a_sphere_window_on_rational_arcs_holds_each_order_asked_and_not_the_next() {
|
||||
#[test]
|
||||
fn hostile_edges_are_refused() {
|
||||
let edges = window();
|
||||
let ok = |e: &[IsoEdge]| iso_fill(e, &mut sphere_beside(e, 2), &[false; 4], &spec());
|
||||
let ok = |e: &[IsoEdge]| iso_fill(e, &mut sphere_beside(e, 2), &spec());
|
||||
let mut bad = edges.clone();
|
||||
bad[1].weights = Some(vec![-1.0; bad[1].points.len()]);
|
||||
assert!(ok(&bad).is_err(), "a negative weight");
|
||||
@@ -197,7 +197,7 @@ fn hostile_edges_are_refused() {
|
||||
}
|
||||
Ok(got)
|
||||
};
|
||||
assert!(iso_fill(&edges, &mut too_high, &[false; 4], &spec()).is_err());
|
||||
assert!(iso_fill(&edges, &mut too_high, &spec()).is_err());
|
||||
}
|
||||
|
||||
/// The rational basis's derivatives (the Leibniz rule on `w N N = R W`) against central
|
||||
@@ -205,7 +205,7 @@ fn hostile_edges_are_refused() {
|
||||
#[test]
|
||||
fn a_rational_patch_s_derivatives_are_its_differences() {
|
||||
let edges = window();
|
||||
let q = Quilt::new(&edges, &[], 3).unwrap();
|
||||
let q = Quilt::new(&edges, &[Vec::new(), Vec::new()], 3).unwrap();
|
||||
let p = &q.patches[0];
|
||||
assert!(p.rational);
|
||||
let (u, v, h) = (0.3, 0.6, 1e-5);
|
||||
|
||||
@@ -80,6 +80,9 @@ pub struct PatchReport {
|
||||
/// corners where the neighbours themselves disagree, and anywhere.
|
||||
pub normal_deviation_deg: f64,
|
||||
pub normal_deviation_all_deg: f64,
|
||||
/// The largest angle (degrees) by which the patch's normal misses a neighbour's beyond the
|
||||
/// loop edge's own lean out of that neighbour's tangent plane (what the tolerance holds).
|
||||
pub normal_excess_deg: f64,
|
||||
/// `jumps[k]`, k = 2 … 4: the largest jump across the edges asked for order k or more, clear
|
||||
/// of the loop's corners, of the k-th derivative of the linkage curves' height above the
|
||||
/// neighbour (1/mmᵏ⁻¹): the normal curvature (k = 2), its rate of change
|
||||
@@ -154,18 +157,6 @@ fn general_fill(
|
||||
for (&(e, forward), order) in chain.iter().zip(orders) {
|
||||
beside.push(if *order == Continuity::G0 { None } else { Some(Beside::new(record, e, forward, *order, size)?) });
|
||||
}
|
||||
// Where a G1/G2 edge's neighbour and the one before disagree at their corner.
|
||||
let n = chain.len();
|
||||
let mut clash = vec![false; n];
|
||||
for (k, slot) in clash.iter_mut().enumerate() {
|
||||
let prev = (k + n - 1) % n;
|
||||
let (Some(Some(a)), Some(Some(b))) = (beside.get(prev), beside.get(k)) else { continue };
|
||||
let (Some(ea), Some(eb)) = (edges.get(prev), edges.get(k)) else { continue };
|
||||
let (end, start) = (ea.knots.last().copied().unwrap_or(0.0), eb.knots.first().copied().unwrap_or(0.0));
|
||||
if let (Some(x), Some(y)) = (a.across(record, end)?, b.across(record, start)?) {
|
||||
*slot = x.normal.dot(y.normal).abs() < (2.0 * PATCH_ANGLE_TOL_DEG).to_radians().cos();
|
||||
}
|
||||
}
|
||||
let mut across = |k: usize, ts: &[f64]| -> std::result::Result<Vec<Option<Across>>, String> {
|
||||
match beside.get(k) {
|
||||
None => Err("no such edge".into()),
|
||||
@@ -174,7 +165,7 @@ fn general_fill(
|
||||
}
|
||||
};
|
||||
let spec = IsoSpec { tol: face_tolerance(body), angle_tol: PATCH_ANGLE_TOL_DEG.to_radians(), crown, points };
|
||||
let fill = solidcraft_geom::fill_iso::iso_fill(&edges, &mut across, &clash, &spec).map_err(spline_error)?;
|
||||
let fill = solidcraft_geom::fill_iso::iso_fill(&edges, &mut across, &spec).map_err(spline_error)?;
|
||||
let made = quilt_body(body, record, chain, &fill, face_tolerance(body))?;
|
||||
let a = fill.achieved;
|
||||
let report = PatchReport {
|
||||
@@ -182,6 +173,7 @@ fn general_fill(
|
||||
gap: 0.0,
|
||||
normal_deviation_deg: a.normal_deviation.to_degrees(),
|
||||
normal_deviation_all_deg: a.normal_deviation_all.to_degrees(),
|
||||
normal_excess_deg: a.normal_excess.to_degrees(),
|
||||
jumps: a.jumps,
|
||||
point_gap: a.point_deviation,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user