The query.face_p4 shim (and query.face_p3, which includes it) puts the exact
database record before an analytic face surface (sc_analytic_export.h) and an
ellipse/circle edge curve (sc_exact_curve_export.h); the shared topology.face_p2
importer now reads those heads and rebuilds the geometry with the ported readers.
Before, the Rust side ran ClassifyLoops on the B-spline parts of the SmCylinder and
its circle edges. That flipped a loop classification in gen0360 on macOS (Apple
libm moved the C++ side) and in the two scale-1e-3 known mismatches gen0157 and
gen0300 on every platform. All three pass now and are named regressions; the two
known-mismatch entries are removed. query.face_p3 compares 197 more cases with
values (faces/edges that were inexact_geometry before).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TYEpC4tUKxEP3MCaFL5Kz2
SmFace::ClassifyLoops still returned Unported for a target loop that crosses
no seam, although SmCurveClassification::GetLoopCrossings is ported. Call it
with the target loops, the face props and the bisector's classification, as
upstream does; its status stays ignored.
The query.face_p4 runner now also exports the face props the bisector and
GetLoopCrossings read (zone tolerance, no-vertex poles, pole points, missing
seam direction). Faces whose surface or loop edge curves the importer cannot
reproduce exactly report inexact_geometry on both sides when ClassifyLoops
may evaluate them, decided from the export as unit query.face_p3 does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The shim's run_op now starts ClassifyLoops' out-params from
SM_HNF_NO_LOOP_TREE_ROOT and SM_CD_LAMINA, the values the runner's
initial_out gives the Rust side, so the SM_ERR no-loops early return
compares equal.
- Add 41 adversarial fixture cases (4 regressions for the no-loops path).
- classify_loops borrows the face props' done touch list instead of
cloning it per target loop.
difftest: 500 cases, 395 pass, 0 failed, 105 deferred (query.face_p3),
--perturb 395/395; --green 232 units, 0 failed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8ERxmTxLqSjFQKckiXC5a
Conflicts: PR #85 (intersect.curve_class_p2) and #65 landed on main, so main's
versions are kept; heal::face_props lists ClassifyLoops and ReSet as wired.
LoopObject::as_loop/as_vertex implemented on the handles added on main
(HealLoopHandle, SeamObject, test and runner handles). The shim's B-rep export
copy follows topology_face_p2.cpp (GetEdges registered first, vertex-use lists,
version 3 shell state). GetLoopCrossings stays UNPORTED: it is reached only after
the bisector (query.face_p3) and needs its curve handles.
query.face_p4: 459 cases, 359 pass, 0 failed, 100 deferred (query.face_p3);
--perturb 359/359; --green 224 units, 0 failed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8ERxmTxLqSjFQKckiXC5a
Wires UNPORTED(query.face_p4) in heal::face_props::set_props_stage3 (status
ignored as upstream) and gives the heal.face_props runner's Rust B-rep the
shim's single vertex loop, which the wiring made visible.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BY8UpXKuMci15rAWGXsbsm