mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 10:35:58 +00:00
brep: the geometry check reads a pcurve at its edge's parameter within the interval's rounding
Brep::validate lets a pcurve's interval differ from its edge's by PCURVE_INTERVAL (1e-9 of the magnitude: rounding room), but check_geometry then evaluated the pcurve at the edge's own ends and samples, which may lie just past it: "the pcurve has no point at 2.0000000000082903" on four of Poppy Humanoid's solids, circle edges whose end vertex sits on a quarter of their form, where SMLib's pcurve ends on the knot. Both of the check's pcurve readings (the use's points and the loop's area) now take the edge parameter on the pcurve's interval, through one reader. Poppy: 195 → 199 bodies, 41 → 37 solids not imported; the corpus survey and the kernel tests are as before. Test: a pcurve whose interval ends 1e-11 short of its edge's validates and checks; without the clamp, "the pcurve has no point at 1". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LUXeKMoxpM7Vuv8Rb6E3K2
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
//! an edge's for its pcurves, an edge's and its vertex's together where they meet, a vertex's on
|
||||
//! its surface. A point that cannot be evaluated is an error, never a panic.
|
||||
use super::eval::{CurveEval, SurfaceEval};
|
||||
use super::{Brep, Loop};
|
||||
use super::{Brep, Curve2, Loop};
|
||||
use crate::Vec3;
|
||||
|
||||
/// Where a record disagrees with itself.
|
||||
@@ -84,6 +84,14 @@ fn v3(p: [f64; 3]) -> Vec3 {
|
||||
fn unevaluable(record: Record, what: impl std::fmt::Display) -> Mismatch {
|
||||
Mismatch { record, problem: Problem::Unevaluable(what.to_string()) }
|
||||
}
|
||||
/// A use's pcurve at its edge's parameter `t`. The pcurve's interval is its edge's to within
|
||||
/// rounding ([`Brep::validate`], `PCURVE_INTERVAL`), so an edge parameter just past it is read at
|
||||
/// the pcurve's end.
|
||||
fn on_pcurve(pc: &Curve2, t: f64) -> Option<[f64; 2]> {
|
||||
let [a, b] = pc.interval()?;
|
||||
pc.eval(t.clamp(a.min(b), a.max(b)))
|
||||
}
|
||||
|
||||
/// `gap` within `tolerance` (a non-finite gap never is).
|
||||
fn within(record: Record, what: &'static str, gap: f64, tolerance: f64) -> Result<()> {
|
||||
if gap <= tolerance { Ok(()) } else { Err(Mismatch { record, problem: Problem::Off { what, gap, tolerance } }) }
|
||||
@@ -147,7 +155,7 @@ impl Brep {
|
||||
let [t0, t1] = e.interval;
|
||||
let mut uv = [[0.0; 2]; 3];
|
||||
for (k, t) in [t0, 0.5 * (t0 + t1), t1].into_iter().enumerate() {
|
||||
let q = u.pcurve.eval(t).ok_or_else(|| unevaluable(at, format!("the pcurve has no point at {t}")))?;
|
||||
let q = on_pcurve(&u.pcurve, t).ok_or_else(|| unevaluable(at, format!("the pcurve has no point at {t}")))?;
|
||||
let s = surface.point(q).ok_or_else(|| unevaluable(at, format!("the surface has no point at {q:?}")))?;
|
||||
let (Some(c), Some(what), Some(slot)) = (p.get(k), PCURVE_POINTS.get(k), uv.get_mut(k)) else {
|
||||
return Err(unevaluable(at, "pcurve sample"));
|
||||
@@ -181,7 +189,7 @@ impl Brep {
|
||||
for k in 0..AREA_SAMPLES {
|
||||
let f = k as f64 / AREA_SAMPLES as f64;
|
||||
let t = if u.forward { t0 + (t1 - t0) * f } else { t1 - (t1 - t0) * f };
|
||||
ring.push(u.pcurve.eval(t).ok_or_else(|| unevaluable(at, format!("the pcurve has no point at {t}")))?);
|
||||
ring.push(on_pcurve(&u.pcurve, t).ok_or_else(|| unevaluable(at, format!("the pcurve has no point at {t}")))?);
|
||||
}
|
||||
}
|
||||
let area = signed_area(&ring);
|
||||
|
||||
@@ -530,6 +530,21 @@ fn shift(c: &mut Curve2, by: [f64; 2]) {
|
||||
}
|
||||
}
|
||||
|
||||
/// A pcurve whose interval ends a rounding short of its edge's (as a kernel that snaps an edge's
|
||||
/// end to its knot hands it back) is read at its end: the record validates and checks.
|
||||
#[test]
|
||||
fn a_pcurve_a_rounding_short_of_its_edge_is_read_at_its_end() {
|
||||
let mut b = sheet();
|
||||
let Curve2::Spline { knots, .. } = pcurve_of(&mut b, 0, 0) else { panic!("a spline pcurve") };
|
||||
let end = *knots.last().unwrap();
|
||||
let short = end - 1e-11 * end.abs().max(1.0);
|
||||
for k in knots.iter_mut().filter(|k| **k == end) {
|
||||
*k = short;
|
||||
}
|
||||
b.validate().unwrap();
|
||||
b.check_geometry().unwrap();
|
||||
}
|
||||
|
||||
/// One geometric defect each, structurally valid: every one is refused, naming the record.
|
||||
#[test]
|
||||
fn geometric_defects_are_refused_by_record() {
|
||||
|
||||
Reference in New Issue
Block a user