Audit the held flags against the fork's source; tabbed plate and mitred rounds parity

Reading the fork at SMLIB_REV finds `perspective_sphere_offsets` is there (709c5be: the cull projects
each sphere's own radius from its centre), so it goes on. The new
`gap_samples_use_trimmed_spans` and `twist_simple_duw_sign` are held: upstream's defects are still in
the fork.

The tabbed plate and #1997's rounded box are compared with the C++ kernel. `sweeps::List` is visible
to the crate for that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MNXLXkZLQ6T7TSi4wswcmk
This commit is contained in:
brett19
2026-10-31 02:37:40 -07:00
parent 03470c3df3
commit f01b877570
4 changed files with 122 additions and 7 deletions
+8 -4
View File
@@ -250,6 +250,7 @@ pub(crate) fn fixes() -> UpstreamFixes {
f.planar_path_sweep_statuses = true;
f.planar_path_profile_placement_statuses = true;
f.fillet_trim_status = true;
f.perspective_sphere_offsets = true;
f
}
@@ -258,8 +259,8 @@ pub(crate) fn fixes() -> UpstreamFixes {
/// (solidcore#1664, WR-563), `bspline_trim_keeps_rational_end_spans` (solidcore#1711, WR-575) and
/// `fillet_blend_curve_revolved_on_arc` (solidcore#1759, WR-581, an own design waiting for the
/// fork's fix) have no fork commit. `temporary_radius_setup_errors` (solidcore#644, #458), `ellipse_step_interval_checks`
/// (solidcore#686, #428) and `perspective_sphere_offsets` (solidcore#684, WR-608, #525) are the
/// port's own fixes with no fork commit; the C++ build keeps upstream's behaviour there.
/// (solidcore#686, #428) are the port's own fixes with no fork commit; the C++ build keeps
/// upstream's behaviour there.
/// `corner_patch_face_side_twist` (WR-613, fork 7fd9fb0) ports a fork commit that is not
/// published.
///
@@ -290,7 +291,9 @@ pub(crate) fn fixes() -> UpstreamFixes {
/// no tangent is no tangency), `fillet_edge_curve_domain_statuses` (`sm_CheckEdgeCurveDomains`: a
/// failed trim keeps the edge's curve), `offset_tangent_gap_statuses` (`HaveTangentGaps`: a failed
/// sample shows no gap) and `ssi_alignment_statuses` (`ALIGN_DIR_WITH_PREVIOUS_POINT`: no angle, no
/// alignment). `translation_sweep_regularize` (solidcore#1937) has no fork commit.
/// alignment). `translation_sweep_regularize` (solidcore#1937) has no fork commit, and the fork keeps
/// upstream's `gap_samples_use_trimmed_spans` (WR-89: `BuildSampleSet` spans from the first knot) and
/// `twist_simple_duw_sign` (WR-46: `EvaluateSimple`'s Duw y is -per cos).
#[cfg(test)]
const HELD_BACK: &[&str] = &[
"precise_properties_trim_base_v",
@@ -298,7 +301,6 @@ const HELD_BACK: &[&str] = &[
"fillet_blend_curve_revolved_on_arc",
"temporary_radius_setup_errors",
"ellipse_step_interval_checks",
"perspective_sphere_offsets",
"corner_patch_face_side_twist",
"boolean_union_refuses_solid_leak",
"curve_loop_start_after_previous_end",
@@ -316,6 +318,8 @@ const HELD_BACK: &[&str] = &[
"offset_tangent_gap_statuses",
"ssi_alignment_statuses",
"translation_sweep_regularize",
"gap_samples_use_trimmed_spans",
"twist_simple_duw_sign",
];
fn fresh() -> Kernel {
+69
View File
@@ -894,3 +894,72 @@ fn rounds_on_the_871_part() {
assert!(crefused.is_err());
}
}
/// The fork's `tabbed_plate_rounds_mitred_on_the_arcs_continuation`: a plate 2 thick, its outline an
/// R25 arc to the sides x = +-4 of a tab whose top is 4 wide with R2 corners, every top edge of the
/// outline rounded r 0.5. The arc's round and the tab side's are mitred on the arc wall's
/// continuation. Removed: the outline's area less the area of the outline moved in by
/// d = r (1 - cos phi), at the height r sin phi into the round (midpoint rule, 8192 steps; the area
/// by Green's theorem). With SMLib built, the C++ kernel gives the same.
#[test]
fn tabbed_plate_rounds_mitred_on_the_arcs_continuation() {
use super::sweep::extrude;
use crate::sweeps::{Curve, DraftCorner};
const Z: [f64; 3] = [0.0, 0.0, 1.0];
let theta = (4.0_f64 / 25.0).acos();
let y = 609.0_f64.sqrt();
let cy = 280.0 / 9.0;
let line = |a: [f64; 2], b: [f64; 2]| Curve::Line { start: [a[0], a[1], 0.0], end: [b[0], b[1], 0.0] };
let arc = |c: [f64; 2], radius, start, end| Curve::Arc { center: [c[0], c[1], 0.0], axis: Z, reference: [1.0, 0.0, 0.0], radius, start, end };
let outline = [
arc([0.0, 0.0], 25.0, PI - theta, 2.0 * PI + theta),
line([4.0, y], [4.0, cy]),
arc([2.0, cy], 2.0, 0.0, PI / 2.0),
line([2.0, cy + 2.0], [-2.0, cy + 2.0]),
arc([-2.0, cy], 2.0, PI / 2.0, PI),
line([-4.0, cy], [-4.0, y]),
];
let area = |da: f64, dt: f64| {
let (big, rho) = (25.0 - da, 2.0 - dt);
let tab = rho * rho * PI + 2.0 * rho * (2.0 + cy) + 4.0 * (cy + rho);
let yc = (big * big - (4.0 - dt) * (4.0 - dt)).sqrt();
let at = yc.atan2(4.0 - dt);
0.5 * (big * big * (PI + 2.0 * at) + tab) + (4.0 - dt) * (cy - yc)
};
let (r, dphi) = (0.5, (PI / 2.0) / 8192.0);
let want: f64 = (0..8192)
.map(|i| {
let phi = (f64::from(i) + 0.5) * dphi;
let d = r * (1.0 - phi.cos());
(area(0.0, 0.0) - area(d, d)) * r * phi.cos() * dphi
})
.sum();
let top = [
[0.0, -25.0, 2.0],
[4.0, 27.8945, 2.0],
[3.41421356, 32.5253247, 2.0],
[0.0, cy + 2.0, 2.0],
[-3.41421356, 32.5253247, 2.0],
[-4.0, 27.8945, 2.0],
];
let plate = extrude(&outline, Z, 2.0, 0.0, DraftCorner::default(), true).unwrap();
assert!(rel(volume(&plate, 1e-8).unwrap(), 2.0 * area(0.0, 0.0)) < 1e-10);
let round = blend(&plate, Blend::Round, &top, &[r; 6], TOL, &[], &[], Corners::Filled).unwrap();
let removed = volume(&plate, 1e-8).unwrap() - volume(&round, 1e-8).unwrap();
assert!(rel(removed, want) < 1e-6, "removed {removed}, expected {want}");
let (rec, _) = native(&to_arrays(&round).unwrap()).unwrap();
rec.validate().unwrap();
assert!(super::is_manifold_solid(&round).unwrap());
assert!(super::check(&round, crate::check::CheckLevel::All).unwrap().is_valid());
#[cfg(feature = "native")]
{
use crate::{Brep, ffi};
crate::init().unwrap();
let list = crate::sweeps::List::of(&outline).unwrap();
let cpp = ffi::sweeps::extrude(&list.raw(), &Z, 2.0, 0.0, ffi::sweeps::DRAFT_CORNER_CURVATURE, true, &mut [f64::NAN; 3]).unwrap();
let cround = ffi::ops::blend(ffi::ops::BLEND_ROUND, Brep::made(cpp).cpp().unwrap(), &top, &[r; 6], TOL).unwrap();
let cv = ffi::volume(&cround, 1e-8).unwrap();
assert!(rel(volume(&round, 1e-8).unwrap(), cv) < 1e-9);
assert_eq!(counts(&round).unwrap(), ffi::counts(&cround).unwrap().map(|x| usize::try_from(x).unwrap()));
}
}
+42
View File
@@ -834,3 +834,45 @@ fn offset_faces_close_a_step_wall() {
assert!(rel(got, v) < 1e-9 && rel(area, a) < 1e-9, "d {d}: V {got} A {area}");
}
}
/// Rig case shell/4492's rounded box: four mitred rounds, r 3, come off a 40 x 30 x 10 box's top
/// together and follow it. From height 0 the outline is the 34 x 24 top moved out by the round's
/// w(z) = sqrt(9 - (z - axis)^2), or 3 below the axis. With the fork's values at +19, and a body
/// whose rounds go all the way down at -7. With SMLib built, the C++ kernel gives the same.
#[test]
fn mitred_rounds_follow_a_moved_top() {
use super::blend::{Corners, blend};
use crate::Blend;
let edges = [[20.0, 0.0, 10.0], [20.0, 30.0, 10.0], [0.0, 15.0, 10.0], [40.0, 15.0, 10.0]];
let top = [20.0, 15.0, 10.0];
let rounded = blend(&make_box([0.0; 3], [40.0, 30.0, 10.0]).unwrap(), Blend::Round, &edges, &[3.0; 4], TOL, &[], &[], Corners::Filled).unwrap();
let analytic = |d: f64| {
let (height, axis, n) = (10.0 + d, 7.0 + d, 20000);
(0..n)
.map(|i| {
let z = height * (f64::from(i) + 0.5) / f64::from(n);
let w = if z < axis { 3.0 } else { (9.0 - (z - axis) * (z - axis)).max(0.0).sqrt() };
(34.0 + 2.0 * w) * (24.0 + 2.0 * w) * height / f64::from(n)
})
.sum::<f64>()
};
let moves = [(19.0, [10, 20, 12]), (1.0, [10, 20, 12]), (-1.0, [10, 20, 12]), (-6.9, [10, 20, 12]), (-7.0, [6, 12, 8])];
for (d, n) in moves {
let moved = offset_faces(&rounded, &[top], 0.1, d).unwrap();
let v = volume(&moved, 1e-8).unwrap();
assert_eq!(counts(&moved).unwrap(), n, "d {d}");
assert!(rel(v, analytic(d)) < 1e-6, "d {d}: {v} vs {}", analytic(d));
}
#[cfg(feature = "native")]
{
use crate::ffi;
crate::init().unwrap();
let b = ffi::make_box(&[0.0; 3], &[40.0, 30.0, 10.0]).unwrap();
let round = ffi::ops::blend(ffi::ops::BLEND_ROUND, &b, &edges, &[3.0; 4], TOL).unwrap();
for (d, _) in moves {
let cpp = ffi::ops::pick_op(ffi::ops::Pick::OffsetFaces, &round, &[top], 0.1, d).unwrap();
let ours = offset_faces(&rounded, &[top], 0.1, d).unwrap();
assert!(rel(volume(&ours, 1e-8).unwrap(), ffi::volume(&cpp, 1e-8).unwrap()) < 1e-9, "d {d}");
}
}
}
+3 -3
View File
@@ -99,13 +99,13 @@ pub const MAX_CURVES: usize = ffi::sweeps::CURVE_MAX_CURVES;
/// The curves as the C API's curve list.
#[derive(Default)]
struct List {
pub(crate) struct List {
kinds: Vec<i64>,
reals: Vec<f64>,
}
impl List {
fn of<'a>(curves: impl IntoIterator<Item = &'a Curve>) -> Result<List> {
pub(crate) fn of<'a>(curves: impl IntoIterator<Item = &'a Curve>) -> Result<List> {
let mut l = List::default();
for c in curves {
l.push(c)?;
@@ -150,7 +150,7 @@ impl List {
Ok(())
}
fn raw(&self) -> ffi::sweeps::List<'_> {
pub(crate) fn raw(&self) -> ffi::sweeps::List<'_> {
ffi::sweeps::List::new(&self.kinds, &self.reals)
}
}