Skip to content

fix: prepare face geometry for shell ray queries - #613

Merged
github-merge-queue[bot] merged 14 commits into
tangency-labs:mainfrom
henlamp:codex/primitive-integration
Oct 24, 2026
Merged

github-merge-queue[bot] merged 14 commits into
tangency-labs:mainfrom
henlamp:codex/primitive-integration

Conversation

@henlamp

@henlamp henlamp commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Shell rays can reach faces without UV trims, then revisit earlier candidates after preparation raises tolerances. Final region nesting during stitching can also omit geometry constructed under notification identities.

Keep production ray solves in flight while lazy face queries update the original B-rep. Use the prepared trim cache before boundary classification and forward construction identities into the final stitching query. Add native comparisons of reachable geometry, tolerances, stored trims and pyramid miss/side/apex rays, including the translated tall-pyramid regression. Expose an explicit construction tolerance for the pole-free sphere so the example can use strict analytic checks without changing the default tolerance.

Uses the merged #616 and #618 implementations and removes the corresponding duplicate workarounds. Includes only the region-nesting ray entry needed from #619; its remaining entrypoints stay separate.

Validation on 3b617cac8: twelve affected native C++ differential suites (13,380 cases: 13,041 pass, 315 deferred, 24 native rejections) have no failures and bit-identical compared values. Deferrals and native input rejections are reported separately. Focused kernel tests: 43 passed. Kernel Clippy, formatting and WebAssembly check passed. All seven complete example solids are checked against analytic properties with the existing 1e-9 relative gate; partial sphere/torus construction remains outside these seven.

Latest integration: main d51978934 is merged in c28753855; formatting and strict kernel Clippy pass after the merge. The targeted follow-up comparisons below ran on 32f82cc96 before this integration.

Local reference: macOS arm64, pinned C9 SMLib, floating-point contraction disabled. The full Linux/GCC suite has not been rerun for this revision.

An additional sweep regression run was stopped during generated-input construction before comparisons; it is not counted in the twelve completed suites.

CI follow-up: export p10 regions in owning-list order after changing the infinite region, and bind lazily generated UV trim owners under the construction identities. Both CI failures and all 90 spine-doubled fixtures match C++ exactly locally; the shared p6 exporter remains at 588 pass, 0 fail, 7 deferred. The full local p10 rerun is incomplete.

henlamp and others added 3 commits October 23, 2026 11:26
Bind shell ray solves to lazy UV trims and owning-face caches. Preserve
the construction notification keys when translating pole-free spheres.

Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Count reachable B-rep lists and restore curve owners from the native face fixture. Let sweep coverage accept successful kernel calls.

Co-Authored-By: Codex <noreply@openai.com>
@tdamsma

tdamsma commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Machine-generated (Claude Fable 5.1 coordinator, Claude Opus 5.5 review agent) on behalf of @tdamsma.

Read-only review of head 7c29dc0 against upstream. Verdict: approve with fixes. Hard rules are clean (no unwrap/indexing/unsafe, ordered collections, wasm-safe, no tolerance change, no case removed) and the pole-free sphere fix is correct: transform_with_notifications(..., None, true, Some(keys)) matches CreateSphereNoPole's pBrep->Transform(sRef) order (vertices, curves, surfaces); the old plain transform matched owners by kernel identity, which never equals the construction keys.

Four things to fix before ready:

  1. Restart vs single pass (topology/shell.rs:1147-1167). SmShell::RayFire (SmShell.cpp:592) runs the solve once and creates missing trims and face caches lazily as it goes. The PR stops at the first missing piece, prepares it and reruns the whole solve. Preparation can change tolerances (SmEdgeuse.cpp:2299/2498 SetTolerance(dError*1.01); SmFace.cpp:7965 CreateUVTrimCurves(bAdjustTolerances=TRUE)), so the C++'s early solve ran with the old tolerances and the Rust rerun with the new ones. Exact primitives agree; toleranced or imported B-reps may not. Either snapshot tolerances around prepare and return a distinct error when any changed, or record the deviation in plan/findings.md and the ray.rs header.
  2. Prepared cache not used in classification (intersect/topology_solver/p1.rs:2027-2045). face_point_classify never passes RayPreparation.surface_caches, so curved faces skip SmFace::PointClassify's cached shortcut (SmFace.cpp ~12060, note [B537] where the shortcut and the full path disagree). Pass it in.
  3. Evidence is not CI-reproducible. The difftest records for primitive B-reps are counts only (primitive_api_primitives.rs:343, primitive_primitive_creation_p1.rs:~420) and the PR changes nothing under crates/difftest or harness/, so the "6,142 compared values" came from an uncommitted local harness. Please commit the richer records (vertex positions, sampled geometry, trim presence, edge tolerances, a non-origin sphere, shell ray cases); the sphere translation bug was invisible to difftest before. Also "Linux x86_64 under Rosetta" is not the parity reference (Linux x86-64 GNU, g++); quote CI's --green summary lines instead.
  4. Unported as a control signal. "Prepare this" is signalled with Unported tagged surface.trim_srf_cache / topology.edgeuse_p3 (both partial units; the trim-cache string is not a C++ symbol, and there is no // UNPORTED comment). If one escapes the loop a failure is counted as deferred. Use a dedicated signal. Termination is sound but the bound is once per edge-use/face (quadratic worst case), not "once"; say so.

Nits: surface/surface/p8.rs:1468-1474 skips the rebuild SmSurfaceCache::GetTree (SmSurfaceCache.cpp:5452) does for an unfinished face classification; the face cache is rebuilt per ray_fire and copied per line intersection with an O(N²) handle lookup; prepare passes no notification environment; ray.rs lacks the Rust | Upstream header table; the pyramid test labels uncompared trim counts "Native results"; a test re-asserts a stub into green curve.curve_p2. Follow-up outside this PR: proto_topology/p1/api_transform.rs:132 uses plain transform and likely has the same key hazard.

Partial units whose deferred cases will now run further and should be checked in CI: topology.shell/brep_p9/p6/p4, primitive.api_primitives, primitive_creation_p1, sweep.api_brep, and every difftest that builds curved primitives as fixtures. Full notes: solidcore-notes/claude-review-pr613.md on the maintainer's machine; happy to paste any section.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RvuXmr7S5RoUXxVohC1giE

@tdamsma

tdamsma commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Machine-generated (OpenAI Codex coordinator and agents) on behalf of @tdamsma.

@henlamp, we have an unpublished branch covering the same face-cache dependency through a single mutable solve context. We are holding that overlapping implementation to avoid competing with your PR. Its audit also found the prepared trim-cache fast path and live tolerance reads need attention.

A useful independent contribution would be CI-visible primitive geometry, trim/tolerance and shell-ray comparisons, extending the counts-only records. #615 already adds sampled geometry for primitive p3. Are you already extending the primitive API/p1 records for this PR? If not, we can prepare that test-only follow-up and keep kernel changes coordinated here.

@erlis

erlis commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Two things from the UV-trim work for the cases #541 moved to the read-only GetOrCreateUVTrimCurve refusal:

  1. brep_p6 regression with this branch. On main 10a9297 + this branch, topology.brep_p6 gets 2 failures: cube_faces_6 and cube_faces_6_reversed (586 pass, 2 failed, 7 deferred). The cause is the harness, not the retry. The brep_p6 import leaves edge curves unowned, so the point-edge solve skips the edge the C++ hits first, and Rust classifies (and trims) a face the C++ prunes. difftest: own imported geometry under the harness's keys (brep_p6, merge_p2) #618 fixes the import. With main + this branch + difftest: own imported geometry under the harness's keys (brep_p6, merge_p2) #618, brep_p6 has 588 pass, 0 failed, 7 deferred (topology.edge).

  2. Other entry points. I prototyped the same lazy retry at the other places that reach the refusal and measured every one against the C++. In each, creating the visited edge use's trim and solving again matches exactly (0 failures):

    • brep::p2::ray_fire: brep_p7 3 and brep_p6;
    • BrepQuery::classify's boundary solve: query.brep 2;
    • the topology intersector's brep_curve_solve sites: topology_intersector_p2 1;
    • the query::api_queries callers: api_queries 26.

    This PR wires RayPreparation into shell::ray_fire only; brep::p2::ray_fire and BrepQuery::classify pass preparation: None. I'd rather reuse your RayPreparation / QueryTopology::Requested than add a second mechanism. Would a follow-up on top of this branch work for you, extending it to those entry points? I'll keep it out of your files where I can. If you'd rather fold them in here, say so and I'll send the measurements instead.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
henlamp and others added 7 commits October 23, 2026 15:29
Use the live curve natural interval for duplicate comparisons while retaining the caller handles. Cover subdomains, distinct identities and missing geometry.

Co-Authored-By: Codex <noreply@openai.com>
Expose an explicit pole-free sphere tolerance and use precise property integration for the unchanged analytic gate. Verify all seven complete examples, sphere scaling and translation, UV-edge agreement and tolerance validation.

Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Use live topology reads and cache-first face classification while retaining exact evaluation geometry. Forward constructor identities into stitching region queries and compare primitive geometry, tolerances, trims and rays against SMLib.

Co-Authored-By: Codex <noreply@openai.com>
henlamp and others added 2 commits October 23, 2026 22:38
Export regions in native owning-list order after ResetInfiniteRegion and bind lazy UV trim owners to the construction identities. The 90 spine-doubled fixtures, including both CI failures, compare exactly against C++; the shared p6 exporter stays unchanged.

Co-Authored-By: Codex <noreply@openai.com>
@erlis

erlis commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Linux/GCC full --green sweep of c287538 against its base main d519789 (every green/partial unit, one process per unit): 0 failures.

  • 366 cases deferred → pass (43 of them the new integration_* cases), 45 move to a later deferral, nothing regresses.
  • topology.brep_p10: closure_inf_spine_doubled and review_spine_doubled_m1_closure_True pass (1156 pass, 0 failed). On c97f821 they had failed here too; 32f82cc fixes them on Linux as well.
  • The primitive_creation_p1 linear_circle_cap1_distance2 / linear_analytic_circle_cap1_distance2 failures I saw on 7c29dc0 are gone.
  • UV-trim refusals (SmEdgeuse::GetOrCreateUVTrimCurve (unprepared read-only topology)): 98 cases on main, 30 with this PR (query.api_queries 27, query.brep 2, boolean.topology_intersector_p2 1). wire: lazy UV trims for brep ray fires, BrepQuery::classify, the topology intersector and api_queries #619 will move the remaining B-rep entries onto this PR's single-pass path once it lands.

🤖 Generated with Claude Code

brett19 added a commit to brett19/solidcore-fork that referenced this pull request Oct 23, 2026
tangency-labs#613's shell ray face preparation and tangency-labs#639's forwarded face hooks each
leave these nine cases deferred; together the cases run and their region
nesting differs from C++ (skin_concentric_loops, skin_primitive_annular,
topology.brep_p6 make_face tangency-labs#89..95). Probes rule out tangency-labs#639's mutable face
queries and tangency-labs#613's cache-first point classification as the cause on
their own. List them as known mismatches with a findings entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brett19 added a commit to brett19/solidcore-fork that referenced this pull request Oct 23, 2026
…ll ray face geometry branch

Only conflict: plan/units/topology.brep_p10.toml notes; kept tangency-labs#679's
UpdateVertexEdgeIntervals update and tangency-labs#613's update text. Code merged
cleanly (topology/brep/p5.rs included).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-merge-queue
github-merge-queue Bot merged commit ae47be0 into tangency-labs:main Oct 24, 2026
7 checks passed
brett19 pushed a commit to brett19/solidcore-fork that referenced this pull request Oct 24, 2026
…e-geometry-on-main

fix: prepare face geometry for shell ray queries (stacked on tangency-labs#613, merged with main)
erlis added a commit to erlis/solidcore that referenced this pull request Oct 24, 2026
topology::brep::p2::ray_fire and BrepQuery::classify's boundary solve
classified faces read-only, so an edge use without a UV trim stopped them
at SmEdgeuse::GetOrCreateUVTrimCurve (unprepared read-only topology),
where upstream makes the trim inside the loop walk. Both now solve through
query::brep::ray::solve_mutable_brep_point (tangency-labs#613's single-pass live solve
on the caller's B-rep), as region nesting already does. A classifier's
shared target keeps the read-only boundary solve: its copy is not the
caller's B-rep.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants