Compare commits

...

12 Commits

Author SHA1 Message Date
bherbruck 2e8847512b Ladder golden: 004-housing as on main again
The housing's change at the new pin (81 faces, 15111.545 mm3 against its answer
15138.533, from solidcore#2735) is a regression the gate should show, not record:
filed as tangency-labs/solidcore#2908.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 01:29:50 -05:00
bherbruck 0fc45a1b07 Known failures: the solidcore changes behind the new pin's regressions, bisected
Bisected over solidcore's first-parent history 5d2635d36..9856ffee5 with SolidCraft's fixes:
the rim cut's extra face comes with the exact surface extension (a6314d4b3 or f4e0ec2d0,
solidcore#2769), the basin's shell with the offset's exact extension margin (4d2b4d35a or
95192d385), and the ladder housing's change with solidcore#2735, which makes the M16 groove
cut grazing the boss's root round valid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 00:48:37 -05:00
bherbruck 4ea1435f7c Known failures: fillet_into_an_earlier_round passes on main with the new pin
The merge queue's run (this pin on today's main) builds the r3 round into the r2 round
within the closed form's tolerance, so the line #11875 added goes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:57:01 -05:00
bherbruck 18c8e31397 Known failures: three tests the new pin breaks
The rim cut after the far-end round (one face more), the basin's shell (status 1001) and
the whole-body intersection's small round (no closed outline). The first two pass at
5d2635d36, so they come from solidcore after it; the third fails at 5d2635d36 too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:57:00 -05:00
bherbruck 180510cdf4 Reference tests: 77-fillet-variable-linear passes at 9856ffee5
The variable round r 2 to 5 along a box edge builds its closed form at the new pin, as
blend_variable::tests::variable_radius_on_a_box_edge does since #11875's pin: its
known_failure goes. The other 89 reference cases are as before (87 pass, 2 known).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:57:00 -05:00
bherbruck 7da12cb981 Known failures: drop the seven tests 9856ffee5 fixes; ladder housing recorded
Pass at the new pin, off the list: the four issue_851 two-round cases,
a_round_next_to_a_round_of_another_radius, a_propagated_chain_that_cannot_build_says_how_to_blend_the_edge_alone
and fillet_takes_exactly_the_picked_edges (the smooth-vertex End solidcore#2364 brought is gone).
The ladder golden is main's again for 07-fillets/012 and 021, which build as at 8ddce9016;
11-cumulative-parts/004-housing is recorded failing at the new pin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:57:00 -05:00
bherbruck add3e2d2c1 smlib: the two held-back fixes, measured again at 9856ffee5
fillet_refuse_rounds_breaking_through now refuses one fillet rig case that builds with it
off (1668-fillet-both-sides-of-thin-wall-r7, closed form 39509.938), down from 11 at
8ddce9016; the other 270 cases come out the same. fillet_round_ends_on_outline fails the
same tests with the same numbers as at 5d2635d36. Both stay off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:57:00 -05:00
bherbruck 5d8bd85b7d Fillet rig: four cases #11875 listed pass at 9856ffee5
1080-fillet-notched-block-top-outline-r8, 1207-fillet-box-end-edge-into-unblended-round-partial-chain,
5158-fillet-adjacent-plate-edges-as-thick-as-plate and
every-fillet-after-fillet-curvature-front-then-side-r3-partial-chain meet their continuity
again: the smooth-vertex End solidcore#2364 brought is gone from them at the new pin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:57:00 -05:00
bherbruck f0645730d9 Kernel cases: match the reorder refusal's new wording (same change as #12392)
#12171 reworded the refusal ("moving Notch to position 0 breaks Notch ..."), so the
history case fails on every pin bump, which runs every rig area. This is raykholo's
one-line change from #12392, identical, so either can land first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:57:00 -05:00
bherbruck ba2408d6f4 smlib: mitred rounds follow a top moved down 9, which solidcore now builds
At 9856ffee5 solidcore puts the four r 3 rounds back when the top of the 40 x 30 x 10
box moves down 9 (refused before, solidcore#2020): 6 faces and the closed form's volume
within 1e-6, as at -8 and -9.5. The test takes -9 with the other moves instead of
expecting the refusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:57:00 -05:00
bherbruck 124aa4cccd Known failures and the ladder golden as #11875 measured them at 5d2635d36
jyviko's lists for the pin before this one: the six tests and the rig case 5d2635d36
fixed are off the lists, its regressions are on them with the solidcore PR that made
each, and the ladder's 07-fillets/012 and 021 are re-recorded. Re-measured at 9856ffee5
in the commits after this one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:57:00 -05:00
bherbruck c47669d406 solidcore pin: 8ddce9016 → 9856ffee5, solidcore and solidcore-record at one rev
Both crates now come from the same solidcore commit. Builds on #11875 (jyviko), which
moved the pin to 5d2635d36: its compile fixes (the per-kernel blend serial ranges go,
solidcore#2480 keys the station memo by definition) and its fix choices carry over.

Compile fixes for the newer API:
- Offset refusals are typed (solidcore#2654): a fold is OffsetFailure::Folds, walls
  touching and a vanishing edge are read from the error's kind.
- The fitted surface extension is gone (solidcore a5de86aaf, issue #2730): extend_sheet takes solidcore's
  exact extension (extended_by_distance) instead of solving for the distance to ask.

Flags: the six solidcore retired into its defaults leave fixes() (fillet_blend_corner,
fillet_round_on_round_as_definition, fillet_nx1_tangent_side_edge_same_convexity,
gap_array_append_updates_longest, masked_array_uses_bit_shift,
hash_table_grow_preserves_removed_records); the two new ones are on
(brep_ray_classifies_current_trims, revolution_frame_from_profile_origin).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:57:00 -05:00
12 changed files with 96 additions and 210 deletions
Generated
+3 -2
View File
@@ -4051,15 +4051,16 @@ dependencies = [
[[package]]
name = "solidcore"
version = "1.0.1"
source = "git+https://github.com/tangency-labs/solidcore?rev=8ddce901671f1cebd9214a31d21895d725e886c8#8ddce901671f1cebd9214a31d21895d725e886c8"
source = "git+https://github.com/tangency-labs/solidcore?rev=9856ffee5ab778c6b49f24716e82305683eda1fb#9856ffee5ab778c6b49f24716e82305683eda1fb"
dependencies = [
"serde",
"thiserror 2.0.21",
]
[[package]]
name = "solidcore-record"
version = "0.1.0"
source = "git+https://github.com/tangency-labs/solidcore?rev=f5e8e9a29e0c9b7cf6c4040a5bc8878382b3c62f#f5e8e9a29e0c9b7cf6c4040a5bc8878382b3c62f"
source = "git+https://github.com/tangency-labs/solidcore?rev=9856ffee5ab778c6b49f24716e82305683eda1fb#9856ffee5ab778c6b49f24716e82305683eda1fb"
dependencies = [
"serde",
"serde_json",
+2 -2
View File
@@ -36,10 +36,10 @@ solidcraft-3dx-client = { path = "crates/threedx-client" }
# move the rev on its own commit. For a
# local checkout, add `[patch."https://github.com/tangency-labs/solidcore"] solidcore = { path =
# "../solidcore/crates/solidcore" }` to a `.cargo/config.toml` outside the repository.
solidcore = { git = "https://github.com/tangency-labs/solidcore", rev = "8ddce901671f1cebd9214a31d21895d725e886c8" }
solidcore = { git = "https://github.com/tangency-labs/solidcore", rev = "9856ffee5ab778c6b49f24716e82305683eda1fb" }
# Geometry as data (solidcore's layers §1, "Record"): the B-rep record, its structural validation
# and the native body file. Pinned like solidcore; for a local checkout, patch it as above.
solidcore-record = { git = "https://github.com/tangency-labs/solidcore", rev = "f5e8e9a29e0c9b7cf6c4040a5bc8878382b3c62f" }
solidcore-record = { git = "https://github.com/tangency-labs/solidcore", rev = "9856ffee5ab778c6b49f24716e82305683eda1fb" }
# test tooling: a dev-dependency only (xtask/src/layers.rs)
solidcraft-testkit = { path = "crates/testkit" }
+32 -26
View File
@@ -6,7 +6,6 @@
//! fork fixed in its own sources; each one turned on here says which fork commit it matches.
use std::panic::{AssertUnwindSafe, catch_unwind};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Mutex, PoisonError};
use solidcore::geom::attribute::{AttributeStore, ObjectKey};
@@ -153,7 +152,6 @@ pub(crate) fn fixes() -> UpstreamFixes {
f.fillet_point_step_per_point = true;
f.analytic_copy_keeps_parameterization = true;
f.fillet_point_bigger_step_retry = true;
f.fillet_through_extended_rail_end = true;
f.precise_properties_body_length_scale = true;
f.offset_extension_from_base = true;
f.corner_blend_stays_inside_neighbours = true;
@@ -230,7 +228,6 @@ pub(crate) fn fixes() -> UpstreamFixes {
f.fillet_cleanup_removes_input_slits = true;
f.fillet_rail_end_on_continued_surface = true;
f.glue_vertices_joins_every_region = true;
f.fillet_nx1_tangent_side_edge_same_convexity = true;
f.fillet_removal_takes_ring_fillets = true;
// The fork's commits that reached `SMLIB_REV` at eaa998a (tangency-labs/smlib#68, #69, #70).
f.fillet_trim_inside_by_best_dot = true;
@@ -274,7 +271,6 @@ pub(crate) fn fixes() -> UpstreamFixes {
f.offset_keep_cavity_regions = true;
f.offset_four_face_vertex_split = true;
f.gap_samples_non_bspline_v_stride = true;
f.gap_array_append_updates_longest = true;
f.imprint_skips_collapsed_definition = true;
f.precise_properties_trim_base_v = true;
f.fillet_blend_curve_revolved_on_arc = true;
@@ -319,7 +315,6 @@ pub(crate) fn fixes() -> UpstreamFixes {
f.circle_silhouette_perspective_center = true;
f.ellipse_convert_to_2d_uses_both_axes = true;
f.circle_offset_antiparallel_normal = true;
f.masked_array_uses_bit_shift = true;
f.global_solver_identical_prefers_larger_when_maximizing = true;
f.surface_continuity_checks_last_v_knot = true;
f.surface_point_solver_propagates_bbox_failure = true;
@@ -346,7 +341,6 @@ pub(crate) fn fixes() -> UpstreamFixes {
f.surface_parallel_to_vector_checks = true;
f.fillet_round_ends_at_tangent_continuation = true;
f.geometric_g2_continuity = true;
f.hash_table_grow_preserves_removed_records = true;
f.bend_volume_bbox_contains_wide_angle_and_inf_u = true;
f.bend_volume_trim_param_box_across_period = true;
f.point_set_tight_pbox_projects_points = true;
@@ -377,7 +371,6 @@ pub(crate) fn fixes() -> UpstreamFixes {
f.fillet_section_end_trim_at_creases = true;
f.fillet_refuse_mixed_corner_crossing_rails = true;
f.fillet_keep_features = true;
f.fillet_blend_corner = true;
f.fillet_exact_arc_sections = true;
f.fillet_exact_arc_spans = true;
// Held back: see `HELD_BACK`.
@@ -385,11 +378,22 @@ pub(crate) fn fixes() -> UpstreamFixes {
f.cone_unbounded_parameterized_as_nurb = true;
f.ellipse_open_arc_keeps_end_angle = true;
f.fillet_round_as_definition = true;
f.fillet_round_on_round_as_definition = true;
f.fillet_overflow_crease_along_edge = true;
f.plane_first_torus_section_exact = true;
f.keep_exact_pcurves = true;
f.shell_one_construction = true;
f.offset_extends_ball_patches_as_themselves = true;
f.approximate_surface_checks_every_row = true;
f.topological_vertex_has_only_edge_uses = true;
f.planar_section_face_tolerance = true;
f.face_swap_uv_transposes_trims = true;
f.closed_smooth_pole_tangent_limit = true;
// Held back: see `HELD_BACK`.
f.fillet_round_ends_on_outline = false;
f.fillet_setback_beside_curved_faces = true;
f.fillet_chamfer_corner = true;
f.brep_ray_classifies_current_trims = true;
f.revolution_frame_from_profile_origin = true;
f
}
@@ -399,29 +403,31 @@ pub(crate) fn fixes() -> UpstreamFixes {
/// the fork doesn't have stays on, and where solidcore builds what SMLib refuses, a result right
/// against its closed form is a win.
///
/// `fillet_refuse_rounds_breaking_through` (WR-707, solidcore#2311): sound geometry refused. With it
/// on, 11 rig cases that pass with it off, each graded against its closed form or invariants, are
/// refused as breaking through the part (190-fillet-disc-rim-past-its-2mm-wall fails status 1001
/// instead): every-hole-top-rim-r12-past-plate-bottom, 1080-chamfer-runs-into-openings-cut-by-them
/// (3350, its closed form), 1080-fillet-notched-block-top-outline-r8,
/// 324-fillet-over-hole-cut-by-overlong-cylinder, 324-fillet-over-hole-overlap,
/// 5158-fillet-adjacent-plate-edges-as-thick-as-plate and the sloped and equal-slope basin rims of
/// #1080 and #5835. A fix under the same flag is in progress on solidcore.
/// `fillet_refuse_rounds_breaking_through` (WR-707, solidcore#2311): sound geometry refused. At
/// solidcore 9856ffee5, with it on, the fillet rig's 1668-fillet-both-sides-of-thin-wall-r7 is
/// refused as breaking through the wall into the hollow behind it ("the largest radius that fits is
/// 6.828 mm"), where with it off it builds the closed form, 39509.938 (at 8ddce9016 it refused 11
/// rig cases that pass with it off; at this pin the other 270 fillet rig cases come out as with it
/// off).
///
/// `fillet_round_ends_on_outline` (WR-730, solidcore#2541): volumes wrong against their closed
/// forms, and rounds too big for their faces built instead of refused. With it on, each of these
/// passes with it off: `fillet_over_a_cliff` measures 721.832 against the closed form's 693.205;
/// `a_round_wider_than_its_faces_overflows_onto_its_tangent_sides` 6125.719 against 6107.529;
/// `taper_follows_the_room_along_the_edge` 4210.926 against 4195.579; the round larger than the
/// cube in `hostile_inputs_are_errors` builds (4773.168 against the 1094.853 its refusal leaves),
/// as do the fillets the CLI's `run_strict` and `json_out` tests need to fail; and
/// `a_corner_round_wider_than_its_faces_overflows_and_takes_the_corners_other_edges`,
/// `overflow_goes_first`, `corners_refuse_what_they_cannot_build` and
/// `fillet_edge_removed_by_a_larger_overflowing_round_is_noted` are refused (too large, no room,
/// status 1001), where with it off they build. Measured again at solidcore 9856ffee5: the same.
#[cfg(test)]
const HELD_BACK: &[&str] = &["fillet_refuse_rounds_breaking_through"];
const HELD_BACK: &[&str] = &["fillet_refuse_rounds_breaking_through", "fillet_round_ends_on_outline"];
/// How far apart two kernels' procedural blend serials start. A body keeps the serials of the
/// kernel that made it and may be used on any other, whose memo of solved stations is keyed by
/// serial alone: the ranges keep one definition's stations from answering for another's. A serial
/// only names a definition, so which range one comes from changes no answer.
pub(crate) const BLEND_SERIALS_PER_KERNEL: u64 = 1 << 40;
/// A new kernel. Each takes the next range of blend serials, the first starting at 0.
/// A new kernel, with this backend's fixes.
fn fresh() -> Kernel {
static MADE: AtomicU64 = AtomicU64::new(0);
let mut k = Kernel::new(&KernelOptions::default());
k.fixes = fixes();
k.blend_last_serial.set(MADE.fetch_add(1, Ordering::Relaxed).wrapping_mul(BLEND_SERIALS_PER_KERNEL));
k
}
+14 -11
View File
@@ -18,6 +18,8 @@
use solidcore::EntityRef;
use solidcore::SolidError;
use solidcore::error::offset::OffsetFailure;
use solidcore::error::{ErrorKind, Reason};
use solidcore::geom::attribute::{Attribute, Behavior, ids};
use solidcore::geom::axis2_placement::Axis2Placement;
use solidcore::geom::object::Shape;
@@ -29,7 +31,6 @@ use solidcore::nurbs::b_spline_curve::ControlPointForm;
use solidcore::nurbs::b_spline_surface::p1::SurfParamType;
use solidcore::nurbs::b_spline_surface_n_lib::p2::OrientType;
use solidcore::nurbs::knots::Side;
use solidcore::offset::construction::ShellRefusal;
use solidcore::offset::offset_executive::p2::{OffsetOptions, ShellOptions, local_operation_simple, offset_brep_simple, shell_brep_simple};
use solidcore::offset::offset_faces::{OffsetFacesExecutive, OffsetFacesRefusal};
use solidcore::query::api_queries::{face_bounding_box, face_classify_uv, face_closest_point_in_shape, face_internal_point};
@@ -50,21 +51,23 @@ type Out<T> = std::result::Result<T, Error>;
/// `SM_ERR_INVALID_INPUT`.
const INVALID_INPUT: i32 = 1007;
/// `SM_ERR_OFFSET_WALLS_TOUCH` (fork a759622), which `status` (smlib_shim.h) reports as
/// `SMLIB_ERR_WALLS_TOUCH`.
const OFFSET_WALLS_TOUCH: i32 = 1042;
/// `status` (smlib_shim.h): an offset whose walls touch is [`Error::WallsTouch`]; a vertex moved
/// past an edge's other end (`SM_ERR_OFFSET_EDGE_VANISHES`, fork 34318f4) is
/// `SMLIB_ERR_UNSUPPORTED`; a wall whose face's offset folds, which solidcore's one construction
/// refuses (`fixes.shell_one_construction`, solidcore#2367), is [`Error::WallFolds`] with the
/// largest wall it names; any other status as the blends pass it on.
/// largest wall it names; any other status as the blends pass it on. solidcore names an offset's
/// refusals by their kind and reason (its errors in its own terms, solidcore#2654), whichever
/// status the pinned C++ gives them.
fn status(e: SolidError) -> Error {
match e {
SolidError::Upstream { code: OFFSET_WALLS_TOUCH, .. } => Error::WallsTouch,
SolidError::ShellRefused { refusal: ShellRefusal::Fold, size, .. } => Error::WallFolds { largest: size },
e if e.is_offset_edge_vanishes() => Error::Unsupported,
e => blend_status(e),
if let SolidError::Failed(f) = &e
&& let Reason::Offset(OffsetFailure::Folds { largest, .. }) = f.reason
{
return Error::WallFolds { largest: Some(largest) };
}
match e.kind() {
ErrorKind::OffsetWallsTouch => Error::WallsTouch,
ErrorKind::OffsetEdgeVanishes => Error::Unsupported,
_ => blend_status(e),
}
}
+3 -4
View File
@@ -546,9 +546,8 @@ fn offset_faces_close_a_step_wall() {
/// 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 bodies
/// whose rounds go all the way down from -7 and are cut short from -7.5 (solidcore#2027). At -9
/// solidcore refuses to put the rounds back (solidcore#2020, open): SolidCraft says it does not
/// make that move.
/// whose rounds go all the way down from -7 and are cut short from -7.5 (solidcore#2027). At -9,
/// which solidcore refused before 9856ffee5 (solidcore#2020), it builds the closed form too.
#[test]
fn mitred_rounds_follow_a_moved_top() {
use super::blend::{Corners, blend};
@@ -574,6 +573,7 @@ fn mitred_rounds_follow_a_moved_top() {
(-7.0, [6, 12, 8]),
(-7.5, [6, 12, 8]),
(-8.0, [6, 12, 8]),
(-9.0, [6, 12, 8]),
(-9.5, [6, 12, 8]),
];
for (d, n) in moves {
@@ -582,7 +582,6 @@ fn mitred_rounds_follow_a_moved_top() {
assert_eq!(counts(&moved).unwrap(), n, "d {d}");
assert!(rel(v, analytic(d)) < 1e-6, "d {d}: {v} vs {}", analytic(d));
}
assert!(matches!(offset_faces(&rounded, &[top], 0.1, -9.0), Err(Error::Unsupported)));
}
/// `moved_faces`: drafts so small they leave a plane or cylinder where it was still moved a face
+23 -140
View File
@@ -24,21 +24,20 @@
use std::sync::PoisonError;
use solidcore::EntityRef;
use solidcore::curve::curve::Curve;
use solidcore::geom::extent2d::Extent2d;
use solidcore::geom::geom_utility::p2::ContinuityType;
use solidcore::geom::object::Shape;
use solidcore::geom::tol::USE_DEFAULT;
use solidcore::geom::vector2d::Point2;
use solidcore::geom::vector3d::{Point3, Vec3};
use solidcore::kernel::Kernel;
use solidcore::nurbs::b_spline_surface::p1::SurfParamType;
use solidcore::nurbs::b_spline_surface_n_lib::p2::EvaluateFlags;
use solidcore::nurbs::b_spline_surface_n_lib::p2::OrientType;
use solidcore::query::api_queries::{edge_closest_point, face_closest_point_in_shape, face_internal_point};
use solidcore::query::brep::{BrepQuery, LiveBrepObjects};
use solidcore::surface::cone::p1::Cone;
use solidcore::surface::extend::Continuity;
use solidcore::surface::side::BoxSide;
use solidcore::surface::surface::Surface;
use solidcore::surface::surface::SurfaceKind;
use solidcore::topology::brep::p1 as brep_p1;
@@ -54,6 +53,7 @@ use solidcore::topology::face::{p1 as face_p1, p2 as face_p2, p4 as face_p4};
use solidcore::topology::loopuse::{loop_edges, loop_loopuses, notify_ref};
use solidcore::topology::topology_traverser::Marks;
use solidcore::topology::{Brep, BrepId, EdgeId, FaceId, LoopId, edge};
use solidcore::{EntityRef, SolidError};
use super::blend::{p3, status};
use super::sweep::build_faces;
@@ -454,17 +454,17 @@ pub(crate) fn untrim_face(body: &Body, point: [f64; 3], tol: f64, holes: Option<
/// grows in u), and whether it is at the box's max.
#[derive(Clone, Copy)]
struct Side {
which: SurfParamType,
which: BoxSide,
u: bool,
high: bool,
}
/// `SIDES`.
const SIDES: [Side; 4] = [
Side { which: SurfParamType::UMin, u: true, high: false },
Side { which: SurfParamType::UMax, u: true, high: true },
Side { which: SurfParamType::VMin, u: false, high: false },
Side { which: SurfParamType::VMax, u: false, high: true },
Side { which: BoxSide::UMin, u: true, high: false },
Side { which: BoxSide::UMax, u: true, high: true },
Side { which: BoxSide::VMin, u: false, high: false },
Side { which: BoxSide::VMax, u: false, high: true },
];
/// `SAMPLES`.
@@ -546,55 +546,6 @@ fn side_edge(k: &Kernel, b: &Brep, surf: &Surface, dom: &Extent2d, s: Side, edge
e.filter(|_| d <= on)
}
/// `across`: how far `ext` (the face's surface extended past side `s` of its box `dom`, on the
/// same parameters) runs past that side, measured across it through the side's middle, by
/// 16-point Gauss-Legendre quadrature on 64 pieces. Negative when it can't be evaluated.
fn across(k: &Kernel, ext: &Surface, dom: &Extent2d, s: Side) -> f64 {
const X: [f64; 8] = [
0.0950125098376374,
0.2816035507792589,
0.4580167776572274,
0.6178762444026438,
0.755404408355003,
0.8656312023878318,
0.9445750230732326,
0.9894009349916499,
];
const W: [f64; 8] = [
0.1894506104550685,
0.1826034150449236,
0.1691565193950025,
0.1495959888165767,
0.1246289712555339,
0.0951585116824928,
0.0622535239386479,
0.0271524594117541,
];
const PIECES: usize = 64;
let Ok(bx) = ext.natural_uv_domain_with_nurb(k) else { return -1.0 };
let mid = on_side(dom, s, 0.5);
let from = if s.u { mid.x } else { mid.y };
let to = match (s.u, s.high) {
(true, true) => bx.max().x,
(true, false) => bx.min().x,
(false, true) => bx.max().y,
(false, false) => bx.min().y,
};
let h = (to - from) / PIECES as f64;
let mut length = 0.0;
for piece in 0..PIECES {
let c = from + (piece as f64 + 0.5) * h;
for i in 0..16 {
let (x, w) = if i < 8 { (-X[i], W[i]) } else { (X[i - 8], W[i - 8]) };
let t = c + 0.5 * h * x;
let uv = if s.u { Point2::new(t, mid.y) } else { Point2::new(mid.x, t) };
let Some((_, du, dv)) = eval(k, ext, uv) else { return -1.0 };
length += 0.5 * h.abs() * w * if s.u { du } else { dv }.length();
}
}
length
}
/// `cone_continued`: a cone's or cylinder's face continued naturally `distance` past side `s` of
/// its box `dom`, as the analytic itself over a wider STEP domain (degrees round the axis,
/// height along it). `None` where the cone can't be widened so (more than a turn, or past the
@@ -656,82 +607,16 @@ fn same_patch(k: &Kernel, p: &Surface, q: &Surface, tol: f64) -> bool {
}
/// `extended_by`: the surface `surf` (whose face is its whole box `dom`) extended past side `s` as
/// `ct` says, so that it runs `distance` past that side measured across it ([`across`]), within
/// 1e-9 of it. `CreateExtendedSurface` takes its distance as an estimate, and how far it then runs
/// is increasing in what it is asked: a bracketed solve on that (each ask scaled by the distance
/// over what it gave until two asks fall either side, then Illinois false position). An extension
/// that moves the face's side off its parameters, or a target not met, is [`Error::NotExact`],
/// never the surface as it came out.
fn extended_by(k: &Kernel, surf: &Surface, dom: &Extent2d, s: Side, ct: ContinuityType, distance: f64, model_tol: f64) -> Result<Surface> {
const BRACKET_TRIES: usize = 40;
const SOLVE_TRIES: usize = 100;
let tol = 1e-9 * distance;
// How far asking `ask` runs, and the surface it makes.
let try_ask = |ask: f64| -> Result<(Surface, f64)> {
let tried = surf.create_extended_surface_dir(k, s.which, ask, ct).map_err(status)?;
// The face's side stays where it was on the extended surface's parameters.
let mid = on_side(dom, s, 0.5);
let stays = matches!((point_at(k, surf, mid), point_at(k, &tried, mid)), (Some(was), Some(now)) if (was - now).length() <= model_tol);
if !stays {
return Err(Error::NotExact);
}
let got = across(k, &tried, dom, s);
if got.is_finite() && got > 0.0 { Ok((tried, got)) } else { Err(Error::NotExact) }
};
// Asks that run short of and past `distance`, by f = got - distance.
let (mut short, mut past) = (None, None);
let mut ask = distance;
for _ in 0..BRACKET_TRIES {
let (made, got) = try_ask(ask)?;
let f = got - distance;
if f.abs() <= tol {
return Ok(made);
}
if f < 0.0 {
short = Some((ask, f));
} else {
past = Some((ask, f));
}
if short.is_some() && past.is_some() {
break;
}
ask *= distance / got;
/// `how` says, so that it runs `distance` past that side measured across it, exactly: solidcore's
/// one extension ([`Surface::extended_by_distance`]), which keeps the source's own part bit for bit
/// and meets the distance within 1e-9 of it. A distance it cannot meet exactly is
/// [`Error::NotExact`], never a surface that falls short or runs past.
fn extended_by(k: &Kernel, surf: &Surface, dom: &Extent2d, s: Side, how: Continuity, distance: f64) -> Result<Surface> {
match surf.extended_by_distance(k, dom, s.which, how, distance) {
Ok(e) => Ok(e.surface),
Err(SolidError::ExtendRefused { .. } | SolidError::NotConverged { .. }) => Err(Error::NotExact),
Err(e) => Err(status(e)),
}
let (Some((mut lo, mut flo)), Some((mut hi, mut fhi))) = (short, past) else { return Err(Error::NotExact) };
// Illinois: false position, halving the value at the end that stays when the same end moves
// twice running.
#[derive(PartialEq)]
enum Moved {
Neither,
Lo,
Hi,
}
let mut moved = Moved::Neither;
for _ in 0..SOLVE_TRIES {
let x = hi - fhi * (hi - lo) / (fhi - flo);
if !(x > lo.min(hi) && x < lo.max(hi)) {
break;
}
let (made, got) = try_ask(x)?;
let f = got - distance;
if f.abs() <= tol {
return Ok(made);
}
if f < 0.0 {
(lo, flo) = (x, f);
if moved == Moved::Lo {
fhi *= 0.5;
}
moved = Moved::Lo;
} else {
(hi, fhi) = (x, f);
if moved == Moved::Hi {
flo *= 0.5;
}
moved = Moved::Hi;
}
}
Err(Error::NotExact)
}
/// `smlib_extend_sheet`: the one-face sheet `sheet`, whose face is its surface's whole parameter
@@ -787,15 +672,13 @@ pub(crate) fn extend_sheet(sheet: &Body, point: [f64; 3], tol: f64, distance: f6
if (nl.x - dl.x).abs() > ptol || (nl.y - dl.y).abs() > ptol || (nh.x - dh.x).abs() > ptol || (nh.y - dh.y).abs() > ptol {
return Ok(Err(Error::Unsupported));
}
let ct = match how {
Continuation::Tangent => ContinuityType::G1,
Continuation::Curvature => ContinuityType::G1G2,
Continuation::Natural => ContinuityType::CInfinity,
let continuity = match how {
Continuation::Tangent => Continuity::Tangent,
Continuation::Curvature => Continuity::Curvature,
Continuation::Natural => Continuity::Natural,
};
// CreateExtendedSurface takes its distance as an estimate (a natural parabola falls 2.5%
// short, a G2 run-off of a circle runs more than twice as far): the distance asked of it
// is solved for until the extension measures `distance` across the side's middle.
let extended = match extended_by(k, &surf, &dom, grow, ct, distance, model_tol) {
// The extension runs `distance` across the side, exactly (solidcore's one extension).
let extended = match extended_by(k, &surf, &dom, grow, continuity, distance) {
Ok(e) => e,
Err(e) => return Ok(Err(e)),
};
+1 -1
View File
@@ -55,7 +55,7 @@ pub enum Error {
/// middle of the edge it was found for, and why there is no room, when the refusal names them.
TooLarge { size: f64, kind: FitKind, at: Option<[f64; 3]>, why: Option<RoomReason> },
/// A shell, offset or thicken whose wall is thicker than a face curves: the face's offset
/// folds (solidcore's `ShellRefused`, `ShellRefusal::Fold`). The largest wall that works,
/// folds (solidcore's `OffsetFailure::Folds`). The largest wall that works,
/// when solidcore names one.
WallFolds { largest: Option<f64> },
/// SMLib cannot build this exactly yet (a variable radius whose law has a corner).
-13
View File
@@ -486,19 +486,6 @@ fn bodies_move_between_kernels_and_calls_run_side_by_side() {
}
}
/// Each kernel has its own range of blend serials: a body keeps the serials of the kernel that
/// made it, and another kernel's memo of solved stations must not take them for its own.
#[test]
fn kernels_never_issue_the_same_blend_serial() {
let range = core::BLEND_SERIALS_PER_KERNEL as usize;
let ranges = core::run("outer", |outer| {
let inner = core::run("inner", |inner| Ok(inner.blend_last_serial.get() as usize)).unwrap_or_default();
Ok([outer.blend_last_serial.get() as usize / range, inner / range])
});
let [outer, inner] = ranges.unwrap();
assert_ne!(outer, inner);
}
/// A panic in a call running beside another costs neither: the panicking call's kernel is thrown
/// away and the other call and later ones go on.
#[test]
+14 -7
View File
@@ -8,32 +8,39 @@
# and junit-gpu-1..4), run `cargo xtask known-failing --junit <dir>`, then drop each FIXED line and
# add each REGRESSION line with its cause, from the test's failure message.
solidcraft-cli::action_matrix::action_matrix_sample 3 baseline actions fail, the same 3 as on C++: 2 shells (Bad Loop Orient on issue-616-lid-ribs; status 1001 on filleted-shelled-box), 1 fillet (SmSurfOfRevolution Test #7 on oblique_cut_of_cylinder)
solidcraft-cli::model_cache::a_later_round_after_opening_blends_the_saved_rounds_again the later r3 round at the r2 mitre builds solidcore's 10-face corner (7928.743041), the test wants SMLib f916a8d's setback corner (smlib#104), which solidcore ports in solidcore#2310: 15 faces, 7928.142563938
solidcraft-cli::model_cache::a_later_round_after_opening_blends_the_saved_rounds_again the later r3 round at the r2 mitre builds 7927.724283 since solidcore#2382 (a9ef4c707, the least-certifying setback scale); the test wants SMLib f916a8d's setback corner (smlib#104), 15 faces, 7928.142563938, which solidcore#2369 built (7928.142564)
solidcraft-cli::naming_corpus::corpus_designs_name_alike_every_build_and_fail_where_a_reference_is_gone oracle/83-boundary-loop-thicken does not load: step 8, thicken status 1001, as on C++
solidcraft-doc::eval::cost_tests::a_tap_marking_searches_only_the_bodies_near_it main's mesh-based version (#10303) not taken; the exact replacement is to come
solidcraft-doc::eval::cost_tests::a_tap_marking_still_searches_the_bodies_within_its_reach main's mesh-based version (#10303) not taken; the exact replacement is to come
solidcraft-engine::boolean_regression_tests::box_intersect_splits_the_end_off_a_raked_wedge_after_its_end_rounds the wedge's end rounds fail status 1001 since solidcore#2480 (819a5ad3a, the procedural blend record); passes at 7906d0d0c
solidcraft-engine::cmd::edit::tests::inserting_variable_fillet_control_preserves_parameters_at_existing_positions solidcore refuses a variable fillet whose law has a corner (solidcore#430)
solidcraft-engine::cmd::file::tests::each_format_has_an_export_command_of_its_own the box's planar faces are meshed into 124 triangles, not 12 (the STL is 6284 bytes, the test counts 84 + 50 x 12), as on SMLib
solidcraft-engine::cmd::sketch_project::tests::whole_body_intersect_keeps_a_small_round_between_short_walls the whole-body intersection gives no closed outline (0 profiles, 1 wanted) on solidcore between 8ddce9016 and 5d2635d36: it fails at 5d2635d36 and at 9856ffee5
solidcraft-engine::extent_tests::cut_to_the_far_side_of_a_curved_wall main's mesh-based version (#8634) not taken; the exact replacement is to come
solidcraft-engine::face_name_tests::a_join_that_makes_the_body_bigger_leaves_round_walls_it_never_touched_alone main's edge lookup by a stored point (#11286) finds the edge on the display mesh (edge_near on the body's mesh), not taken; the exact replacement is to come
solidcraft-engine::hole_face_tests::a_hole_wider_than_the_part_says_it_removes_all_of_it a 40 mm hole at (10, 10) touches the plate's edge y = 30 at one point; the kernel parts the plate there into 2 bodies, the test wants truck's 1, as on SMLib
solidcraft-engine::measure_centre_tests::a_fillet_round_a_boss_reports_its_radius the round at the foot of a boss is not an analytic torus of minor radius 3, so no face of the body is (the test finds none to measure), as on SMLib
solidcraft-engine::pleat_regressions::mirrored_pleat_joins_into_a_thick_case_wall FilletRearOuter and FilletRearInner fail status 1001 since solidcore#2480 (819a5ad3a, the procedural blend record); passes at 7906d0d0c
solidcraft-engine::pleat_regressions::pleat_built_at_the_far_end_joins_the_finished_case FilletRearOuter and FilletRearInner fail status 1001 since solidcore#2480 (819a5ad3a, the procedural blend record); passes at 7906d0d0c
solidcraft-engine::tests::a_later_round_at_a_mitred_corner_is_blended_with_the_rounds_there the later r3 round at the r2 mitre builds 7927.724283 since solidcore#2382 (a9ef4c707, the least-certifying setback scale); 7928.142564 at solidcore#2369 (the fork's setback corner, smlib#104); the test wants the old 10-face corner, 7928.743041
solidcraft-engine::tests::chord_and_variable_fillets its 2 → 4 → 2 waist: solidcore refuses a variable fillet whose law has a corner (solidcore#430)
solidcraft-engine::tests::fillet_fit_taper_closes_to_a_point fillet status 1001 on the taper fit (radius 16 closing to 0), as on C++
solidcraft-engine::tests::mirror_placed::dragging_a_mirrored_part_meshes_its_copies_once a copy of a copy drawn from its first copy's triangles (#10101) is triangulated differently from a fresh mesh of it, where truck's meshes are the same under a rigid move, as on SMLib
solidcraft-engine::tests::shell_tangent_chain_that_opens_every_face_explains_how_to_keep_walls the 2 mm shell of the box with every edge rounded r 1.5, its front face open, fails SM_ERR_INVALID_INPUT at solidcore 8ddce9016: the wall is thicker than the rounds and their Ball corners (BallPatch, fillet_blend_corner), whose inward offsets collapse
solidcraft-engine::tests::pleat_join::the_pleats_join_at_every_wall_from_2_4_to_2_6_mm at 2.5 mm FilletRearOuter and FilletRearInner fail status 1001 since solidcore#2480 (819a5ad3a, the procedural blend record); passes at 7906d0d0c
solidcraft-engine::tests::rim_cut::rim_cut_after_the_round_on_the_other_end the window cut after the far-end round adds 4 faces, not 3 (16, want 15), since solidcore's exact surface extension (a6314d4b3 or f4e0ec2d0, solidcore#2769); passes at the merge before, 9e343e179
solidcraft-engine::tests::robustness::a_boundary_found_across_a_seam_is_not_used seed 20's fillet fails status 1001 since solidcore#2480 (819a5ad3a, the procedural blend record); passes at 7906d0d0c
solidcraft-kernel::blend::corner_tests::a_round_ending_in_an_earlier_round_matches_both_together fillet status 1001 on the round that ends in the earlier one, as on C++
solidcraft-kernel::blend::corner_tests::corner_blends_hold_at_any_scale at 0.01x the arc case's rounds remove 72.783 (scaled back) against 72.713 at 1x, as on C++
solidcraft-kernel::blend_corner::tests::issue2422_vertical_after_separate_bottom_rounds the vertical edge after the separate bottom rounds is refused as smooth ("its faces meet tangentially, so there is nothing to round"): the rounds leave it tangent at solidcore 8ddce9016
solidcraft-kernel::blend_corner::tests::three_rounds_at_a_corner the corner's variable-radius transition between rounds of different radii meets its neighbours at 0.144 degrees once its sections are the balls' touch circles (fillet_gn_section_generator, solidcore#2262; 0 with the great circles), as on SMLib d01d7b0; the corner step replaces that transition
solidcraft-kernel::blend_variable::tests::variable_radius_on_a_box_edge the variable round r 2 to 5 along a box edge fails status 1001 at solidcore 8ddce9016
solidcraft-kernel::blend_smlib::tests::smooth_edges_and_unfinished_tangent_chains_are_refused_saying_why the r1 top round alone measures 7957.723 against the closed form 7957.505 since solidcore#2364 (845bdb4e0, the smooth-vertex End); passes at fb937dd8d
solidcraft-kernel::blend_variable::tests::variable_radius_with_a_corner_through_a_middle_point solidcore refuses a variable fillet whose law has a corner (solidcore#430)
solidcraft-kernel::blend_wrong_way_tests::a_chamfer_that_would_grow_the_part_on_a_convex_edge_is_refused at solidcore 8ddce9016 the 0.5 chamfer of the ball cut's rim is no longer refused as too large: the session builds no face (made no change), its tangent chain running into an unblended edge
solidcraft-kernel::corner_g2::tests::a_long_setback_keeps_the_g2_patch_inside_the_box at set backs 11 to 15 the G2 corner patch bulges past the box, as on C++
solidcraft-kernel::corner_g2::tests::curvature_rounds_meet_at_a_g2_corner the corner between curvature rounds jumps 2.07 in curvature over 538 stations on solidcore; the test wants it curvature continuous, as SMLib f916a8d's setback corner (smlib#104), which solidcore ports in solidcore#2310 builds it
solidcraft-kernel::corner_g2::tests::the_box_corner_g2_patch_is_a_closed_solid_curvature_continuous_to_its_neighbours the fitted G2 corner patch bulges 1.7e-5 past the box, as on C++
solidcraft-kernel::fillet_setback_tests::a_setback_not_past_the_radius_is_refused_naming_the_least solidcore builds setback 5.5 (15 faces) where SMLib f916a8d's setback corner (smlib#104), which solidcore ports in solidcore#2310 refuses it naming the least that builds; nothing names the least setback on solidcore until then
solidcraft-kernel::modify_history_tests::fillet_variable_and_chord the variable round r 2 to 5 along the cube's edge fails status 1001 at solidcore 8ddce9016, as variable_radius_on_a_box_edge
solidcraft-kernel::native::tests::box_and_fillet_have_stable_wire_records the box-edge round's wire record differs from the fixture at solidcore 8ddce9016 (a value 3 is 5): the fixture is re-blessed (NATIVE_BREP_BLESS=1) once the change is shown right
solidcraft-kernel::tests::fillet_partial_mixed_arc_and_line_perimeter_run the partial arc-and-line run fails status 1001 since solidcore#2480 (819a5ad3a, the procedural blend record); passes at 7906d0d0c
solidcraft-kernel::tests::fillet_tabbed_plate_outer_loop_and_partial_selection_after_hole the tabbed plate's outer-loop rounds fail status 1001 since solidcore#2480 (819a5ad3a, the procedural blend record); passes at 7906d0d0c
solidcraft-kernel::tests::shell_of_a_basin_past_its_rim_keeps_the_rim_solid the shell of the basin fails status 1001 since the offset's exact extension margin (4d2b4d35a or 95192d385, solidcore#2769); passes at a5de86aaf
solidcraft-smlib::core::blend_tests::tabbed_plate_rounds_mitred_on_the_arcs_continuation the rounds fail status 1001 since solidcore#2480 (819a5ad3a, the procedural blend record); passes at 7906d0d0c
solidcraft-smlib::ops::tests::a_variable_round_with_a_corner_in_its_law_is_its_balls_union solidcore refuses a variable fillet whose law has a corner (solidcore#430)
solidcraft-ui-egui::preselect_tests::variable_fillet_round_trip the dialog's variable round fails status 1001 at solidcore 8ddce9016, as variable_radius_on_a_box_edge
+3 -2
View File
@@ -1,6 +1,6 @@
# The fillets cases that fail today on solidcore, one a line: `<case> <why it fails today>`. Sorted.
# The kernel rig gate fails on a case that fails and is not listed, and on a listed case that passes.
# Baseline: solidcore pin 8ddce9016, tests/kernel-cases run by `cargo xtask kernel-cases`, x86_64 Linux.
# Baseline: solidcore pin 9856ffee5, tests/kernel-cases run by `cargo xtask kernel-cases`, x86_64 Linux.
1080-fillet-equal-slope-basin-1-mm-rim-loop-4 [fail] continuity_order: got {'body': 'Slab', 'edges': 'blend', 'order': 'g0', want >= g1 at every blend seam
1080-fillet-sloped-basin-1-mm-rim-r4-2-edges [fail] continuity_order: got {'body': 'Slab', 'edges': 'blend', 'order': 'g0', want >= g1 at every blend seam
1080-fillet-sloped-basin-1-mm-rim-r4-4-edges [fail] continuity_order: got {'body': 'Slab', 'edges': 'blend', 'order': 'g0', want >= g1 at every blend seam
@@ -16,9 +16,10 @@ cov-fillet-g2-mixed-corner [unsupported] refused as on SMLib d01d7b0: a G2 corne
cov-fillet-g2-oblique-three-edge-corner [unsupported] refused on solidcore: its three edges meet at different wedge angles, and one curvature fillet takes the first edge's equal-area thumbweight for every edge (solidcore#2250, issuecomment-6563419897); SMLib d01d7b0 fails it too
cov-fillet-g2-pocket-corner [fail] continuity_order: G1 at one seam where the corner patch meets a contact face (g2 wanted), as on SMLib d01d7b0
cov-junction-boss-tilt-20-root-round [fail] fillet status 1001 on a cylinder tilted 20 degrees into the plate, as on SMLib
cov-junction-boss-tilt-40-root-round [fail] fillet status 1001 on a cylinder tilted 40 degrees into the plate, as on SMLib
cov-junction-tee-equal-round [fail] the result is not a closed solid, as on SMLib: equal pipes' intersection is two plane ellipses crossing, a corner the old corner code cannot join (the corner step)
cov-junction-y-junction-120-round [unsupported] refused as on SMLib: the faces between the three intersection curves narrow to nothing at the two triple points, so the room check trips (the corner step)
every-block-cylinder-tangent-concave-arc-r2-partial-chain [fail] continuity_order: g0, a contact seam meets at 90 degrees, want >= g1 at every blend seam since solidcore#2364 (845bdb4e0, the smooth-vertex End); passes at fb937dd8d
report-drafted-peg-front-and-concave-rounds-together-r2 [unsupported] step 13 (solid.fillet): invalid input: not supported yet: the fillet meets a corner of this kind, which SMLib's blends do not handle yet (where its ed
report-fillet-curvature-three-edges-at-bar-corner [fail] continuity_order: G1 at one seam where the corner patch meets a contact face (g2 wanted), as on SMLib d01d7b0 (see KNOWN_FAILING)
report-fillet-curvature-three-edges-at-bar-corner-solver-residues [fail] continuity_order: G1 at one seam where the corner patch meets a contact face (g2 wanted), as on SMLib d01d7b0 (see KNOWN_FAILING)
report-fillet-curvature-three-edges-at-bar-corner-solver-residues-outside-setback [unsupported] the corner fill is refused (misses its neighbours' curvature by 4.02e-3, tolerance 2.5e-3) since solidcore#2369 (23a97889b, the setback corner); built the closed form's 9526.705048 at 8ddce9016
@@ -4,5 +4,5 @@
"source": "invariants",
"method": "the cut cannot find its sketch there: the move breaks a feature that worked and is refused",
"outcome": "refused",
"refusal": "moving Notch there breaks Notch"
"refusal": "breaks Notch"
}
@@ -1,6 +1,5 @@
{
"source": "analytic",
"known_failure": "the variable round r 2 to 5 along a box edge fails status 1001 at solidcore 8ddce9016 (as blend_variable::tests::variable_radius_on_a_box_edge in tests/KNOWN_FAILING_TESTS)",
"method": "box 40x30x20 with its top front edge (y=0, z=20) rounded at a radius running straight from 2 (x=0) to 5 (x=40). The round is the union of the balls rolled along the edge, ball s touching both faces at r(s), the law continued past the edge's ends along its end slopes: each section loses the corner points no ball reaches, up to rho(x, t), the nearest entry into a ball along the ray from the edge at the angle t. V, moments and areas integrated by Gauss-Legendre panels in x and t (tools/oracle-analytic/cases_fillets.py)",
"body_count": 1,
"volume_mm3": 23888.613525715446,