Skip to content

port: topology.face_p4 - #239

Merged
bherbruck merged 2 commits into
tangency-labs:mainfrom
denelson1-dot:unit/topology.face_p4
Oct 21, 2026
Merged

bherbruck merged 2 commits into
tangency-labs:mainfrom
denelson1-dot:unit/topology.face_p4

Conversation

@denelson1-dot

@denelson1-dot denelson1-dot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Update (2026-10-21): the port itself landed on main through batch #364, at the reviewed head 590caa76c. Against main, this PR now changes only two prose lines: the topology.face_p4 notes in plan/modules.toml (the 1118 zone-tolerance deferrals are tagged geom.tol) and the face_p4 line in plan/STATUS.md (main's unit totals). The rest of this description documents the port as it landed.

Takes over the claim in #134 (no commits since 10-16; see the comment there).

Ports the 10 SmFace members of topology.face_p4 into topology::face::p4:

  • GetBrep, GetEdges, GetVertices
  • GetUpwardLoopuses, GetUpwardVertexuses, GetUpwardEdgeuses
  • IsConnectedTo, GlobalLineIntersect
  • FindVertexSector, HasSeamProblem
PARTIAL topology.face_p4 cases=5506 passed=3957 failed=0 deferred=1549 max_rel_err=0e0 with_values=3957 no_values=0 blocked_by=curve.crv_on_surf(80),geom.tol(1118),intersect.curve_class_p2(50),intersect.surface_drop_lib_p3(9),topology.face_p2(292)
SENSITIVE topology.face_p4 perturbed=3957 detected=3957 unperturbed=0 deferred=1549
PARTIAL topology.trimming_tools cases=3263 passed=2850 failed=0 deferred=413 max_rel_err=0e0

Back-edges. 26 of the 44 UNPORTED(topology.face_p4) sites are wired (28 came with this PR; 18 more arrived as main merged #196, #145, #130, #155 and the #259 batch):

  • GetEdges in brep_cache, trimming_tools, face_p1, heal_data_p3, geom.tol and query.face_p2
  • GetVertices, GetBrep and the upward-use getters in face_p1
  • GlobalLineIntersect in is_inner_shell, which classifies on a copy of the B-rep
  • GetBrep / GetVertices / GetEdges in heal.stitch_p1, topology.brep_p5, brep_p7 and brep_p10; FindVertexSector in topology.brep_p9
  • the upward-use getters, outer-loop GetEdges and GetBrep in topology.face_p3

The existing partial ports topology_traverser::face_edges and query::face::p2::face_vertices now delegate to the full ones. The other 18 take generic handles that can't reach a B-rep, or, in brep_p5's FindVertexSector, have no kernel to pass; each is listed in modules.toml.

In the other direction, HasSeamProblem now calls intersect.curve_class_p4's GetLoopCrossings (merged in #259). The runner's classification objects answer part 5's vertex_face_gap (#85) and #137's topology_entity / topology_handle, so the 50 periodic seam cases run up to curve_class_p4's own intersect.curve_class_p2 stubs.

Second review's should-fixes. has_seam_problem gives the V seam its own temporaries (2/3), and SeamCheck returns both seams' curves, because upstream's classifications own them. is_inner_shell runs all its rays on one copy of the B-rep instead of cloning it for every ray/face pair.

query.face_p3 (#132, merged since). face_p4's two calls into it are now wired: sm_FaceClassifyUVLine for GlobalLineIntersect's range solutions, using face_p2's UV line handles (now pub(crate)), and SectorClassifyCurveOn for FindVertexSector. face_p4 no longer has any UNPORTED markers of its own. Its 80 query.face_p3 deferrals now defer on curve.crv_on_surf inside SectorClassifyCurveOn. The duplicate ClassifyProblem co-port is dropped in favour of query::face::p1's (re-exported).

Review fixes (bherbruck, at c0c4707): GlobalLineIntersect and FindVertexSector now convert their tolerance as SmZoneTol3d does (fabs, SM_USE_DEFAULT), and the runner has 810 new tolerance line cases; FindVertexSector's interval keeps the natural interval when SetMinMax refuses a reversed pair; the IsConnectedTo shell / region null checks (port and shim) now match upstream's dereferences.

Fewer passes after #269. Since #147 (merged in batch #269), the surface cache's copy keeps the surface's owner. Its subdivision test now reaches SmTol::GetZoneTol3d of the face-owned surface, UNPORTED(surface.surface_cache_p2), which main now tags geom.tol. So 329 cases that used to pass, matching the system tolerance only by coincidence, now defer. That is main's change, not this PR's.

Other units after the wiring (0 failed throughout): topology.face_p3 823 passed (654 on main without this PR), topology.trimming_tools 2850 (was 2842; the shim now runs CreateUVTrimCurves, which Rust no longer defers, and both sides compare the UV trim curves), topology.topology_traverser 895 (was 889), heal.stitch_p1 234 (was 232). In offset.offset_geometry_creation, UpdateFaceSurfaceDomain now runs past the face_p3 getters. On a face imported as its B-spline part, it defers under harness:analytic-geometry-export, as FaceuseOffset already does: its virtual surface calls and the face surface the record compares would be the B-spline kind's, not the analytic SmSurfOfRevolution (after main's exact SmSurfOfRevolution import, only inexact faces defer there: 585 passed, 565 on main). In topology.brep_p9, MakeVertexLoop and MakeEdgeInFaceWithVU now run past the face_p4 / face_p3 calls they used to defer at. That exposed three runner gaps, and 8 cases failed on CI:

  • the Rust side didn't record MakeEdgeInFaceWithVU's edge, loop and face;
  • the Rust-only Face::loops / Loop::edges caches that the importer leaves empty were being compared;
  • a deleted loop stays in the Rust arena as a reset record, while C++ exports a fresh walk.

The runner now records the outputs, empties those caches before comparing, and compares only the reachable topology (graph_reachable). Result: 681 passed, 669 on main.

In face_p1, SwapUV used to discard GetBrep()'s result; a face without a B-rep now reports the null dereference that upstream would hit.

HasSeamProblem ↔ SetProps_Stage2. Upstream they call each other. A new FaceProps::set_props_stage2_with takes the seam call as a callback, which has_seam_problem supplies. The seam curves are ClassObject::temporary handles, as in curve_class::p8.

trimming_tools. Wiring GetEdges makes ClearUVTrimCurves runnable in Rust, so the shim now runs it and the runner compares the resulting UV trim curves. Before, it was a guarded "not run" case.

Upstream defects (plan/upstream-issues.md):

  • WR-176: HasSeamProblem returns SM_SUCCESS (1000) as its SmBoolean, so it is always TRUE on the face-properties path. Found by the difftest.
  • WR-177: FindVertexSector's block-scoped sSurfDomain is empty on later passes.
  • UB-180: GlobalLineIntersect reads sCC[0] of an empty classification.

x86-64 fix. The first CI run failed 326 GlobalLineIntersect cases on x86-64 only. The shim built the line from SmPoint3d p(ad.num(), ad.num(), ad.num()). C++ leaves the order in which those arguments are evaluated unspecified, and x86-64 g++ reads them right to left, so the C++ side intersected the line through (z, y, x). The shim now reads each value into a local first (plan/findings.md, General). The counts above are from x86-64 (Linux Mint 22.3 / Ubuntu 24.04, GCC 13.3, as CI), and the committed report reproduces byte for byte.

Verification (x86-64, on d27e87a, main through 7b57d8d): fmt, rustfmt of the include!d runners, clippy (workspace, --features difftest/cpp), cargo test -p solidcore (1551 passed), wasm32 and --green (237 units, 116 passed, 121 partial, 0 failed).

🤖 Generated with Claude Code

bluecmd added a commit to bluecmd/solidcore that referenced this pull request Oct 19, 2026
…otes and report

- WR-180/181 collided with UB-180/181 of open PRs tangency-labs#229/tangency-labs#239 (one UB/WR
  number space): now WR-186/187, reserved for tangency-labs#205 in the swarm ledger.
- offset.offset_executive_p1: probe_piecewise_empty defers at query.brep now
  that PiecewiseMerge's stitch is wired; the report and notes say so. 0 other
  changes per case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VjjfMoEv8cPgHcyNPF2hvE
@bluecmd bluecmd mentioned this pull request Oct 19, 2026
@bherbruck bherbruck mentioned this pull request Oct 19, 2026
denelson1-dot added a commit to denelson1-dot/solidcore that referenced this pull request Oct 19, 2026
… notes)

- rustfmt the include!-ed runner and FFI unit files.
- surface::surface::p4: the face_p2 porter's NOTE lists
  query::face::p1::point_3d_classify (SmFace.cpp:12801) among the callers that
  switch the cache's point test off; the module Deviations say so.
- The six UNPORTED(query.face_p1) sites on opaque face handles
  (boolean::topology_intersector::p1, intersect::curve_class::p7,
  intersect::topology_solver::p1, geom::object) are DISPATCH GAPs, as in
  lift_to_face; their deferral labels are unchanged.
- ClassifyProblem: name PR tangency-labs#239 (topology.face_p4) next to PR tangency-labs#132 in the
  module table, findings and the query.face_p1 / query.face_p3 notes.
- Point3d FFI guard: note it is stricter than the C++ needs and should be
  narrowed once those cases compare.
- mass.integrator runner: a comment instead of the unused-name binding.

Difftest unchanged (0 failed; --green 210 units, 113 pass, 97 partial).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
denelson1-dot added a commit to denelson1-dot/solidcore that referenced this pull request Oct 19, 2026
…ut range)

- plan/modules.toml: the query.face_p3 note names PR tangency-labs#132 and tangency-labs#239 once, in
  one sentence; the topology.shell note says the face-use box is wired.
- geom::tol: SmTol::GetScaledZero(SmRegion/SmShell) no longer carries
  UNPORTED(query.face_p1); the comment says their box callees exist in
  query::face::p1 and where to port them when a caller needs them.
- query.face_p1 runner: curve cases outside the shim's inputs (kind, non-finite
  data, mask outside 0..=7) are rejected on both sides, as the FFI does.

Difftest unchanged: query.face_p1 1535/689, SENSITIVE 1535/1535; --green 213
units, 114 pass, 99 partial, 0 failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 19, 2026
@denelson1-dot
denelson1-dot marked this pull request as ready for review October 19, 2026 20:28
@0xlillith

Copy link
Copy Markdown
Contributor

I am preparing to reuse this port in #101 after the #259 integration, rather than duplicating the face traversal implementation. I reviewed head 71ab11c47c7e74316ff552ae313f44481a8774da against that combined tree and found three integration details to preserve:

  • The face/p3.rs now supplied by port: topology.face_p3 #139 retains five UNPORTED(topology.face_p4) helpers, including GetUpwardLoopuses on CreateUVTrimCurves' path. This PR's p1 wiring does not remove that p3 boundary. I have prepared delegations to your getters with the original B-rep and native default filters; they still need an executed integration gate.
  • p4::face_surface and has_seam_problem use Surface::clone(). In the port: query.brep #101/port: query.face_p1 #238 implementation that clone clears owner identity (and some concrete copies can adjust NURBS state), while later code calls surface.face() for tolerance lookup. I have prepared an exact owner-preserving evaluation copy for the mutable path and a direct borrow for HasSeamProblem.
  • The new traverser line_parameters clones the B-rep before global_line_intersect. The mutable query/cache work in port: query.brep #101 needs those writes on the original B-rep, so this hunk needs a separate caller/context reconciliation.

I am closing the already-prepared #259/#149 integration first, then can bind the face integration to a tested head. The 92 directed Point3D fixtures in #101 remain deferred in the published checkpoint; I am not claiming that moving the first dependency boundary proves their numerical parity. Your reported counts remain evidence for your head, separate from our upcoming combined run.

@0xlillith

Copy link
Copy Markdown
Contributor

Two output-contract issues are present in 71ab11c47c7e74316ff552ae313f44481a8774da. These are findings from the Rust and pinned C++ source review; they are not new differential results.

  1. The seam classifications outlive their curve storage. In topology/face/p4.rs::classify_seam (lines 1345–1369 and 1394 at that head), seam and seam_uv are local values borrowed by WithSeam, but the returned classification contains only their handles. set_is_curve_owned(true) stores a flag; it does not retain those Rust curves. The U and V calls also reuse temporaries 0/1. Native SmFace::HasSeamProblem transfers the actual curve into the caller's U classification and separately into V (source/SMLib/src/SmFace.cpp at c9c5979f, lines 15686–15691 and 15833–15838). The Rust outputs need backing storage that survives the call and distinct resolvable handles.

  2. Error returns discard outputs already written by the native function. Native resets the caller's classifications and seam enums on entry (15587–15592), then publishes each classification before CurveOnClassify and GetLoopCrossings (U: 15686–15717; V: 15833–15865). Rust keeps the result local and assigns U/V only after classify_seam succeeds. A deferred/error return therefore loses the classification already created; a later V error also loses earlier U output. An additive caller-owned / *_into API needs to preserve those writes together with the curve storage. Reset should use the existing classification's native reset semantics, rather than construct a fresh classification.

Our integration has not wired the remaining p1 seam callers through this API. I am separating the FaceProps/trait adaptations from the ownership and partial-output correction so that the latter can be reviewed explicitly. The current GetLoopCrossings stub limits successful periodic execution; it does not establish that the output lifetime is correct.

@bherbruck bherbruck left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking this one over and for the detailed write-up. The back-edge list and the WR-176 / WR-177 / UB-180 notes made the review much quicker. Reviewed at c0c470736. The new head 7e3d3005b only merges main (#160, #219) and STATUS.md, and leaves this unit's files unchanged, so everything below applies to it.

What passed (x86-64, main through cfa9d068, merge-tree with origin/main clean):

  • cargo fmt --all --check, clippy -D warnings (plain and --features solidcore-ffi/cpp,difftest/cpp), cargo test -p solidcore (1440 passed), wasm32 check.
  • --unit topology.face_p4 reproduces your numbers exactly: 4696 cases, 3807 pass, 889 deferred, 0 failed.
  • --green: 223 units, 114 passed, 109 partial, 0 failed.
  • I also ran 23,040 extra adversarial cases through the C++ (10 .smb files with and without rebuilt trim curves, faces 0-3): lines with tiny (1e-9) and huge (1e9) vectors, far-away points, reversed, zero-width and 1e-12 intervals, tolerances 0 / 1e-15 / 10 / negative; sectors with tolerances 0 / 1e-15 / 100 / -1 over every orientation and validate / use_uv / uv_ok combination; every forced-periodicity seam combination; every IsConnectedTo slot; every list getter with out-of-range loop selectors. No crashes or panics on either side. 9,612 pass, 13,406 defer (as expected, mostly SectorClassifyCurveOn), and 22 fail, all on the same cause (must-fix 2).
adversarial (not committed): cases=23040 passed=9612 deferred=13406 failed=22 (all: GlobalLineIntersect with a negative tolerance on a planar face)
PARTIAL topology.face_p4 cases=4696 passed=3807 failed=0 deferred=889 max_rel_err=0e0
PARTIAL topology.trimming_tools cases=3263 passed=2850 failed=0 deferred=413 max_rel_err=0e0
PARTIAL topology.face_p3 cases=1781 passed=823 failed=0 deferred=958 max_rel_err=0e0
PARTIAL topology.topology_traverser cases=909 passed=895 failed=0 deferred=14 max_rel_err=0e0
PARTIAL heal.stitch_p1 cases=300 passed=234 failed=0 deferred=66 max_rel_err=0e0
PARTIAL offset.offset_geometry_creation cases=861 passed=438 failed=0 deferred=423 max_rel_err=0e0
difftest: 223 unit(s), 114 passed, 109 partial, 0 failed

The rest of the port reads faithfully. I checked GetBrep, the five list getters, IsConnectedTo's short-circuit order, the GlobalLineIntersect removal/insertion index logic, the FindVertexSector shortcuts, the 3D pole/seam branch (including the block-scoped sSurfDomain), the binormal skip, and the whole HasSeamProblem torus case against SmFace.cpp. The HasSeamProblem ↔ SetProps_Stage2 recursion stops at depth 2 because set_props_stage2_with sets Stage2A before the seam call. The two UNPORTED(query.face_p3) stubs are tagged correctly (query.face_p3 is todo), and the code after them is ported. I found no overlapping co-ports in the other open PRs.

Must-fix

  1. FindVertexSector's interval swaps where upstream keeps the natural interval. crates/solidcore/src/topology/face/p4.rs:846-851 builds ivl with Extent1d::from_min_max, which swaps reversed ends. Upstream (SmFace.cpp:9496-9500) starts from sIvl = cr3DCurve.GetNaturalInterval() and calls sIvl.SetMinMax(..). That call fails on min > max and leaves sIvl as the natural interval (SmExtent1d.cpp:247-252). The shim reaches this already: at_start=0 with opposite=0, or at_start=1 with opposite=1, gives reversed ends. Upstream then passes the whole natural interval to SectorClassifyCurveOn, but the port passes the swapped edge interval. Every such case defers today, so the difftest can't see it yet. It will diverge once query.face_p3 lands. Fix: let mut ivl = natural; let _ = ivl.set_min_max(a, b); (status ignored, as upstream), with a Deviations note like the ones in surface/cone/p1.rs:341 and surface/surf_of_revolution/p3.rs:601.

  2. SmZoneTol3d by-value parameters are not converted. GlobalLineIntersect takes SmZoneTol3d sSrcZoneTol3d (SmFace.cpp:11700) and FindVertexSector takes SmZoneTol3d sZoneTol3d (SmFace.cpp:9420). Passing a double through those parameters runs SmZoneTol3d(double): SM_USE_DEFAULT becomes the system zone tolerance, then fabs (SmTol.h:143-171, ported as geom::tol::zone_tol3d). p4.rs:427 and p4.rs:599 take the raw f64. Concrete case: plane_square.smb, face 0, Line { p: [0.2, 0.1, 1], v: [0, 0, -1], tol: Some(-1e-6) }. The C++ returns one solution, t=1, uv=(4.9, 4.8), because the plane gets 1e-6. The port returns none, because dist > -1e-6 rejects it. The same happens for 22 of the adversarial line cases on plane_square, disk, box_with_large_fillet and Mouse. Fix: apply tol::zone_tol3d(k, ..) at entry in both functions, as blend/hcr.rs:347 does, and add a negative-tolerance line case to the runner.

  3. rustfmt on the include!d runner. rustfmt --edition 2021 --check crates/difftest/src/units/topology_face_p4.rs reports 15 hunks: the use solidcore::topology::{..} list and several struct patterns in op_args / rust_record. They look like they were formatted at a wider max_width. cargo fmt skips include!d unit files, so CI does not catch this. Running rustfmt --edition 2021 crates/difftest/src/units/topology_face_p4.rs fixes it. The FFI unit file is already clean.

Should-fix / nits

  • IsConnectedTo with a shell target: a face use without shell is just "not equal" upstream (pFaceuse1->GetShell() == target, no dereference). Only the region branch dereferences it. faceuse_shell (p4.rs:290) errors for both, and the shim's guard (topology_face_p4.cpp:237) skips both, plus a null fu2 when fu1 already matches. On valid B-reps this can't happen, but the guard could be narrowed to what actually crashes.
  • 2D curves in FindVertexSector: lifting a 2D curve needs a SurfaceKind::BSpline here (p4.rs:831), but upstream's SetAll takes any SmSurface. A Plane face therefore gets InvalidInput. The shim always passes the edge's 3D curve, so this branch isn't compared. A line in the Deviations header would help the next porter.
  • 18 UNPORTED(topology.face_p4) markers stay after the unit becomes partial. They are listed in modules.toml with reasons, which is fine for now. Once a classification-object mapper exists, the face_p1.rs:244 / trimming_tools.rs:393 / heal/face_props.rs:1081 HasSeamProblem sites look like the next candidates.

🤖 Generated with Claude Code

@0xlillith

Copy link
Copy Markdown
Contributor

Rechecked 7e243c54cb71bf5c18b65be966bba4bcdf536348. I am retaining the new zone_tol3d, rejected-SetMinMax interval and shell-target fixes in the integration preparation.

One precision correction to my earlier seam comment: the native assignment does more than transfer the seam handle. At the pinned c9c5979f, SmCurveClassification::operator= deep-copies an owned curve (SmCurveClass.cpp:5030–5040), but preserves the source UV pointers (m_cpUVCurve1 at 5043 and m_cpUVCurve2 at 5055). The seam path therefore needs both retained source/UV storage and a distinct resolvable copied curve. Aliasing the source and only setting the owned flag does not reproduce that contract.

The storage and partial-output issues from the previous comment remain in this head: classify_seam still returns handles for local seam/UV values, U/V reuse the same temporary IDs, and Result<SeamCheck> loses earlier native writes on an error. The traverser also still runs global_line_intersect on b.clone(), dropping the original B-rep's mutable state.

I have prepared a separate operation-owned registry and additive *_into API for review in #101. That preparation explicitly stops at the missing owner representation in CrvOnSurf::Copy; retaining a buffer alone is not sufficient to claim periodic-seam parity. It has not passed a combined execution gate yet.

The useful regressions here should resolve the returned curve and UV handles after the call, retain distinct U/V and repeated-call identities, inspect the outputs after a later U/V error, and verify cache/trim writes on the original B-rep. These are source-review findings against the pinned native implementation, separate from the passing CI reported for this PR.

@tangency-monitor

Copy link
Copy Markdown

Thanks for the quick turnaround, and for merging #132 and wiring query.face_p3 before I'd even posted. I reviewed head 75257ef08. That's your merge of main through 5119dff7 on top of 7e243c54c. merge-tree with today's origin/main (e8919e9c) is clean.

Earlier must-fix items: all three are fixed.

  1. FindVertexSector interval: ivl now starts from the natural interval, and set_min_max's status is ignored, as upstream does (SmFace.cpp:9496-9500). Deviations note added.
  2. SmZoneTol3d by-value parameters: tol::zone_tol3d now runs at entry to both global_line_intersect and find_vertex_sector. The 810 new tolerance line cases cover it, and so do mine below (negative, 0, 1e-15, 10, SM_USE_DEFAULT; 0 failed).
  3. rustfmt: rustfmt --edition 2021 --check passes on both the include!d runner and the FFI unit file.

The should-fix items were addressed as well: the IsConnectedTo shell/region guards now match upstream's dereferences in both the port and the shim, and the 2D-curve FindVertexSector limitation is documented.

New since 7e243c54c: the two UNPORTED(query.face_p3) stubs are wired to query::face::p3::{face_classify_uv_line, sector_classify_curve_on}. I checked them against SmFace.cpp:11760 and 9964-9997. The sm_FaceClassifyUVLine status is honoured as upstream (a failure skips the range, UB-180 still errors), the edge uses are SAME/OPPOSITE, and the curve, sIvl, sZoneTol3d, singularity flag/UV and bUVSpaceOkay are passed through. UvLineObjects is reused from face_p2, not duplicated, and ClassifyProblem is re-exported from query.face_p1's co-port. No UNPORTED site in this PR now points at a green or partial unit.

What passed (x86-64):

  • cargo fmt --all --check, clippy -D warnings (plain and --features solidcore-ffi/cpp,difftest/cpp), cargo test -p solidcore (1517 passed), wasm32 check.
  • --unit topology.face_p4 reproduces your numbers exactly (5506 / 3957 / 1549 / 0).
  • --green: 234 units, 114 passed, 120 partial, 0 failed.
  • 30,978 extra adversarial cases run through the C++: 15 .smb files, with and without rebuilt trim curves, faces 0-3, plus plane faces on unit, 1e-12 and 1e3 domains. They cover every list getter with loop selectors -1/0/5/99, every IsConnectedTo slot with indices 0/1/1000/-5, and lines with 1e-9 / 1e9 vectors and far-away origins. Intervals were reversed, zero-width and 1e-12, with tolerances None / 0 / negative / 10 / SM_USE_DEFAULT / 1e-15. Sectors used sampled flag combinations with tolerances None / negative / SM_USE_DEFAULT, and seams every forced-periodicity combination. 0 failed, no crashes or panics. With SectorClassifyCurveOn wired, 3,651 sector cases now compare values; they all deferred before.
adversarial (not committed): cases=30978 passed=17651 deferred=13327 failed=0
  by op: connected 5280 pass | lists 4800 pass | line 2344 pass, 7736 deferred | sector 3651 pass, 4995 deferred | seam 1568 pass, 592 deferred
PARTIAL topology.face_p4 cases=5506 passed=3957 failed=0 deferred=1549 max_rel_err=0e0
PARTIAL topology.trimming_tools cases=3263 passed=2850 failed=0 deferred=413 max_rel_err=0e0
PARTIAL topology.face_p3 cases=1781 passed=823 failed=0 deferred=958 max_rel_err=0e0
PARTIAL query.face_p3 cases=1199 passed=523 failed=0 deferred=676 max_rel_err=0e0
PARTIAL topology.topology_traverser cases=909 passed=895 failed=0 deferred=14 max_rel_err=0e0
PARTIAL heal.stitch_p1 cases=300 passed=234 failed=0 deferred=66 max_rel_err=0e0
PARTIAL offset.offset_geometry_creation cases=861 passed=438 failed=0 deferred=423 max_rel_err=0e0
difftest: 234 unit(s), 114 passed, 120 partial, 0 failed

No must-fix items remain. It's good to merge once CI is green (it's green on 75257ef08).

Should-fix (follow-up is fine)

  • Seam classifications outlive their curves (as @0xlillith noted). classify_seam (p4.rs, the WithSeam block) records the seam and its UV curve as ClassObject::temporary(0/1) handles. The Curves themselves are locals, so the class_u / class_v that has_seam_problem returns (and FaceProps::crv_class_u/v keep) name curves nobody can resolve after the call. U and V also share the same two handles. Upstream hands the owned seam curve to rCrvClassU / rCrvClassV (SmFace.cpp:15687-15691, 15834-15838; operator= deep-copies an owned curve, SmCurveClass.cpp:5030-5040). No ported consumer resolves those curves today (heal_data_p3 and MoveSeam read intervals and topology only), so nothing compares wrong. A later caller would get None, though, or a different curve if its mapper uses temporaries 0/1 itself. A cheap fix: return the seam curves in SeamCheck (seam_u, seam_uv_u, seam_v, seam_uv_v) and give V its own temporaries (2/3). Otherwise, add a Deviations line saying the handles are only valid during the call.
  • line_parameters in topology_traverser.rs clones the whole B-rep for every ray/face pair. One scratch clone made before the ray loop in is_inner_shell would avoid rays × faces copies. It is also closer to upstream, whose cache writes carry over from ray to ray.

🤖 Generated with Claude Code

@tangency-monitor

Copy link
Copy Markdown

Thanks for the follow-ups since 75257ef08. I reviewed head 590caa76c, which is your merge of main 7b57d8de (#351). Most of the work was on 70f8177e and 089fc96ad. Between those heads and 590caa76c, the PR's own diff under crates/ and harness/ changed only in the topology.brep_p9 runner commit (9e8365139). Main's #344 and #351 came in through clean merges. merge-tree with today's origin/main (7b57d8de) is clean.

Earlier should-fix items: both are fixed (452293ca9).

  1. Seam curves outlive the call. SeamCheck now returns seam_u / seam_v (curve and UV curve). V gets its own temporaries (2/3) instead of sharing 0/1 with U. The Deviations line says the copies SetProps_Stage2 keeps hold handles only. This matches upstream's owned seam curves (SmFace.cpp:15687-15691, 15834-15838).
  2. One B-rep copy per is_inner_shell. line_parameters now takes &mut Brep, and is_inner_shell clones once before the ray loop. Cache updates carry over from ray to ray as they do upstream, and this is documented.

New since then: the topology.brep_p9 runner (9e8365139) now records op 5's edge/loop/face. Before, it recorded -1s, while the shim already exported fresh.find(...) for them. It compares reachable topology through Canon::reachable / graph_reachable, because the arena keeps reset records that the C++ fresh walk never sees. It also empties the Rust-only Face::loops / Loop::edges before comparing, since the importer leaves them empty on the C++ side. All three are harness-only changes. Canon::of / graph keep their behaviour for the other runners, and boolean.merge_p1 is unchanged (206 pass / 0 failed).

What passed (x86-64):

  • cargo fmt --all --check passes. rustfmt --edition 2021 --check passes on the include!d topology_face_p4 runner and FFI files and on the topology_brep_p9 and boolean_merge_p1 runners. The topology_trimming_tools runner's 15 diffs are already on main, so none are new.
  • clippy -D warnings passes, both plain and with --features solidcore-ffi/cpp,difftest/cpp.
  • cargo test -p solidcore: 1551 passed. The wasm32 check passes.
  • --unit topology.face_p4 reproduces your numbers exactly (5506 / 3957 / 1549 / 0). I also re-ran every touched unit; all are listed below with 0 failed.
  • --green: 235 units / 0 failed on 70f8177e, and 237 units / 0 failed on 089fc96ad. On 590caa76c I re-ran fmt, clippy, tests, wasm and the touched units listed below.
  • 31,278 extra adversarial cases ran through the C++ on this round, with 0 failed and no crashes or panics:
    • 30,978 topology.face_p4 cases: the 15 .smb files, with and without rebuilt trim curves, faces 0-3, plus plane faces on unit, 1e-12 and 1e3 domains. They cover every list getter and IsConnectedTo slot with out-of-range indices, and lines with 1e-9 / 1e9 directions and far origins. Intervals were reversed, zero-width or 1e-12, with tolerances None / 0 / negative / 10 / SM_USE_DEFAULT / 1e-15. Sectors used sampled flag combinations, and seams every forced-periodicity combination.
    • 300 new IsInnerShell cases (topology_traverser op 7): all 15 files, rebuilt or not, target shells 0-3 and 99, repeat 1 and 2. They exercise the one-copy change; 120 compare values.
adversarial face_p4 (not committed): PARTIAL cases=30978 passed=17651 failed=0 deferred=13327 max_rel_err=0e0
adversarial is_inner_shell (not committed): PARTIAL cases=300 passed=120 failed=0 deferred=180 max_rel_err=0e0
PARTIAL topology.face_p4 cases=5506 passed=3957 failed=0 deferred=1549 max_rel_err=0e0
PARTIAL topology.brep_p9 cases=684 passed=681 failed=0 deferred=3 max_rel_err=0e0
PARTIAL topology.brep_p10 cases=1404 passed=972 failed=0 deferred=432 max_rel_err=0e0
PARTIAL topology.brep_p7 cases=316 passed=237 failed=0 deferred=79 max_rel_err=0e0
PARTIAL topology.brep_p5 cases=797 passed=744 failed=0 deferred=53 max_rel_err=0e0
PARTIAL boolean.merge_p1 cases=248 passed=206 failed=0 deferred=42 max_rel_err=0e0
PARTIAL topology.trimming_tools cases=3263 passed=2850 failed=0 deferred=413 max_rel_err=0e0
PARTIAL topology.topology_traverser cases=909 passed=895 failed=0 deferred=14 max_rel_err=0e0
PARTIAL topology.face_p3 cases=1781 passed=823 failed=0 deferred=958 max_rel_err=0e0
PARTIAL offset.offset_geometry_creation cases=861 passed=572 failed=0 deferred=289 max_rel_err=0e0
PARTIAL heal.stitch_p1 cases=300 passed=234 failed=0 deferred=66 max_rel_err=0e0
difftest: 237 unit(s), 115 passed, 122 partial, 0 failed   (089fc96ad)

No must-fix items. It's good to merge once CI is green (it's green on 590caa76c).

Nits (optional)

  • topology.topology_traverser is now CI's longest difftest unit: 382 s on 70f8177e, compared with about 250 s for edgeuse_p3, which led on main. The extra time comes from the six IsInnerShell cases that now run instead of deferring. Fine for now; if it grows, a --filter split or fewer samples per face in the fixture would keep the wall time down.
  • The topology.face_p4 notes attribute its 1118 zone-tolerance deferrals to surface.surface_cache_p2, but the report's blocked_by names geom.tol(1118). That's the owner tag of SmTol::GetZoneTol3d(const SmObject*) on main. One phrase in the notes would make them line up.

🤖 Generated with Claude Code

…m.tol on main

Review nit (tangency-monitor, at 590caa7): the notes named
surface.surface_cache_p2 while the report's blocked_by says geom.tol, the
owner tag of SmTol::GetZoneTol3d(const SmObject*) on main. Regenerated
reports; STATUS unit totals (237: 116 green, 121 partial).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
denelson1-dot pushed a commit to denelson1-dot/solidcore that referenced this pull request Oct 21, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pfe7dodPG5rpuN9B8QcnuX
denelson1-dot added a commit to denelson1-dot/solidcore that referenced this pull request Oct 21, 2026
, tangency-labs#239) into unit/blend.fillet_solver_p3

Conflict: the offset.offset_geometry_creation notes (main's topology.face_p4
sentence and this branch's tangency-labs#192 one were appended at the same place; main's
taken, this branch's re-added with fresh counts after --green) and its
generated report (main's taken; regenerated by --green).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bluecmd added a commit to bluecmd/solidcore that referenced this pull request Oct 21, 2026
…s#239 topology.face_p4, tangency-labs#229 boolean.merge_p2) into fix/topology.brep_p2-delta-minors

Conflict: plan/modules.toml (offset.offset_geometry_creation): main's notes
with this branch's issue tangency-labs#76 link re-applied. boolean/merge/p3.rs takes
main's side: tangency-labs#229 wired topology.brep_p7's merge_copy_faces, which uses the
notification environment, so this branch's comments on the CopyFaces merge
branch no longer held. The branch now carries only the issue tangency-labs#76 links.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bluecmd added a commit to bluecmd/solidcore that referenced this pull request Oct 21, 2026
…rsection-loop

Brings in batch tangency-labs#364 (tangency-labs#239 topology.face_p4, tangency-labs#229 boolean.merge_p2). The
merge_p4 note keeps this branch's text, with main's appended tangency-labs#229 sentence
added. The p2 report is regenerated on the merged tree: 905 cases, 728 pass, 0
fail, 177 deferred. Against the previous head, 3 CreateUVTrimCurves deferrals
move from topology.face_p4 (GetUpwardLoopuses, now ported) to topology.face_p2
(GetTolerance), and the notes' breakdown is updated. merge_p4 is identical to main.
No newly unblocked stub is wired here (merge-only round).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bluecmd added a commit to bluecmd/solidcore that referenced this pull request Oct 21, 2026
…topology.face_p4, tangency-labs#229 boolean.merge_p2) into unit/topology.brep_p3

Conflicts:
- difftest boolean_merge_p1 runner: main's flagged Canon walk (of / reachable /
  of_native, with the unreachable tail); graph() uses its reachable mode (the stricter
  criterion of d1ae46e), graph_reachable() is kept for its callers, the
  unreached_records diagnostic and the regression test are kept.
- topology/brep/p7.rs: main's shorter unported list plus tangency-labs#204's wire-edges bullet.
- four reports: main's; plan/modules.toml: main's notes with tangency-labs#204's UPDATEs appended.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bluecmd added a commit to bluecmd/solidcore that referenced this pull request Oct 21, 2026
…logy.face_p4, tangency-labs#229 boolean.merge_p2) into unit/topology.brep_p7-curve-class

Conflicts: crates/solidcore/src/topology/brep/p7.rs (module docs: main's CopyFaces /
boolean.merge_p2 sentence and this PR's curve classification wiring; the stand-in
interval stubs main still described are gone) and plan/modules.toml (topology.brep_p7
notes: main's text with tangency-labs#229's update, then this PR's sentences, plus a merge-round
sentence). Gated: cargo check (workspace, features cpp), fmt, clippy (both), cargo test
-p difftest --features cpp (177 passed), 1557 tests, wasm; topology.brep_p7 338/233/0/105
(main 316/237/79: only this PR's 26 curve cases differ, deferred at ReSet), perturb
233/233, FUZZ-PASS 2000.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tangency-monitor

Copy link
Copy Markdown

Thanks for the follow-up commit. I reviewed head d27e87aa3.

Context: the port itself landed on main through #364, at the reviewed head 590caa76c. That batch did not include d27e87aa3, so this PR now carries only that commit.

What's left in this PR: compared with origin/main (ca3bbd2bc), merge-tree is clean, and merging changes exactly two prose lines:

  • the topology.face_p4 notes in plan/modules.toml
  • the face_p4 line in plan/STATUS.md

Both reports this commit regenerates (topology.face_p4.json, offset.offset_geometry_creation.json) already match main's copies byte for byte, because #364 regenerated them. No code, shim, runner or fixture changes. That means fmt, clippy, tests, wasm and difftest on the merged tree are the same as on main, so I didn't run extra adversarial cases this round.

Earlier nit: fixed. The notes now credit the 1118 zone-tolerance deferrals to geom.tol. That matches the report's deferred_by_unit and the UNPORTED(geom.tol): SmTol::GetZoneTol3d(const SmObject*) markers on main (surface/surface_cache/p1.rs:2422, surface/surface_cache/p2.rs:471, surface/trim_srf_cache.rs:1088).

topology.face_p4 report (main == this head): PARTIAL cases=5506 passed=3957 failed=0 deferred=1549 max_rel_err=0e0
deferred_by_unit: geom.tol 1118, topology.face_p2 292, curve.crv_on_surf 80, intersect.curve_class_p2 50, intersect.surface_drop_lib_p3 9
CI on d27e87aa3: clippy, deny, difftest, fmt, progress, publish-dry-run, test, wasm all pass

There are no must-fix items. It's good to merge; CI is already green on this head.

Nit (optional)

  • The STATUS.md line ends with "237 units, 116 green and 121 partial". main is now at 238 units (116 green, 122 partial, after port: boolean.merge_p2 #229). You could update the count or leave it as a snapshot from when the unit landed.

🤖 Generated with Claude Code

…gency-labs#229) into unit/topology.face_p4

plan/STATUS.md: main's unit totals (238: 116 green, 122 partial), review nit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tdamsma added a commit to tdamsma/solidcore that referenced this pull request Oct 21, 2026
STATUS keeps both lines; offset.offset_geometry_creation takes main's notes
(tangency-labs#239 face_p4) plus this PR's re-run (595 pass / 279 deferred), report
regenerated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9QwJFBTKLZ9FFK3nVd1X4
@bherbruck
bherbruck added this pull request to the merge queue Oct 21, 2026
Merged via the queue into tangency-labs:main with commit 3e88ab9 Oct 21, 2026
8 checks passed
froglemonade2 added a commit to froglemonade2/solidcore that referenced this pull request Oct 21, 2026
 topology.face_p4, tangency-labs#229 boolean.merge_p2)

Co-Authored-By: Claude Opus 5.5 <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