From e6da98ef94a28f441d83f03e2d0a2e357d511daa Mon Sep 17 00:00:00 2001 From: Alex Lytvynenko Date: Mon, 26 Oct 2026 22:00:35 +0100 Subject: [PATCH] difftest: preserve exact face classification geometry Reuse lossless analytic surface and edge-curve transport. All 1401 cases now compare values, replacing 27 both-error comparisons; retain original fixture identities, guards, and tolerances. Co-Authored-By: OpenAI GPT-6.1 --- crates/difftest/src/units/query_face_p2.rs | 23 +++++++++++----------- harness/cpp/shim/query_face_p2.cpp | 5 ++++- plan/units/query.face_p2.toml | 10 ++++++++++ 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/crates/difftest/src/units/query_face_p2.rs b/crates/difftest/src/units/query_face_p2.rs index c4fd4f8245..6065b0040f 100644 --- a/crates/difftest/src/units/query_face_p2.rs +++ b/crates/difftest/src/units/query_face_p2.rs @@ -5,13 +5,14 @@ //! file, optionally with rebuilt UV trim curves, or `CreateFaceFromSurface` over a //! B-spline surface or a real `SmPlane`), optionally overwrites stored face, edge and //! vertex tolerances, then runs `SmFace::PointClassify`, `sm_ClassifyParamOnFaces` (a -//! verbatim copy) or `SmFace::CurveOnClassify`. There is no Rust B-rep reader or builder -//! yet, so the Rust side imports the edited graph the shim exports (unit -//! `topology.face_p2`'s layout and importer: topology, tolerances, UV domains, planes and -//! B-spline surfaces, B-spline and line curves, UV trim curves) and runs the port on it. -//! A face whose surface, or one of whose loop edges' curves, is an `SmBSplineSurface` / -//! `SmBSplineCurve` subclass the export carries only as its B-spline part is not -//! compared (both sides report `inexact_geometry`). +//! verbatim copy) or `SmFace::CurveOnClassify`. The Rust side imports the edited graph +//! the shim exports using `topology.face_p2`'s importer: topology, tolerances, UV +//! domains, planes, B-splines and losslessly round-tripped analytic surfaces and EDGE +//! curves. UV trim curves retain their ordinary B-spline export. The shared exporters +//! preserve supported revolution/extrusion surfaces and ellipse/circle EDGE curves, +//! including their analytic data and flags. Unsupported subclasses and representations +//! that fail the exporters' lossless round-trip checks remain `inexact_geometry`; +//! neither engine's classification is compared for those faces. //! //! **Points.** The classified parameter point is computed from the imported B-rep //! (identically for both sides, then passed to both): a fraction of the face's UV @@ -1062,11 +1063,9 @@ fn gen_at(rng: &mut Rng) -> At { } } -/// The upstream `.smb` files whose faces the export carries exactly (B-spline surfaces -/// and curves; the other files of `topology.edgeuse_p1`'s list have surfaces of -/// revolution, extrusion, cones, spheres or tori, which the export carries only as -/// their B-spline parts). `Mouse.smb` and `box_with_large_fillet.smb` have several -/// faces (two and one of them inexact). +/// The historically preferred upstream files with mostly plain B-spline geometry. +/// Keep this selection and its random draw order stable as exact analytic transport +/// broadens coverage; each selected face still passes the runtime losslessness check. const EXACT_FILES: [&str; 7] = [ "Endcap.smb", "Mouse.smb", diff --git a/harness/cpp/shim/query_face_p2.cpp b/harness/cpp/shim/query_face_p2.cpp index d43235d1b9..3e4015111e 100644 --- a/harness/cpp/shim/query_face_p2.cpp +++ b/harness/cpp/shim/query_face_p2.cpp @@ -26,6 +26,8 @@ // Before classifying, the trimmed surface cache of each face surface involved is deleted // (SmCacheMgr::DeleteObjectsCache): reading a file can leave one, and the port has no // object caches, so both sides classify through the loops. +#include "sc_analytic_export.h" +#include "sc_exact_curve_export.h" #include "sc_shim.h" #include "support/topology_fixture.h" @@ -664,6 +666,7 @@ void export_graph(SmBrep* brep, Graph& g, std::vector& o) for (SmFace* p : g.faces) { SmSurface* s = p->*FPeek::srf(); + sc_analytic_export::put_exact_head(o, s, the_context()); put_surface(o, s); o.push_back((double)g.find(2, p->*FPeek::fu())); SmExtent2d d = p->*FPeek::dom(); @@ -719,7 +722,7 @@ void export_graph(SmBrep* brep, Graph& g, std::vector& o) for (SmTopology* t : list_of(p->GetList())) v.push_back(g.find(6, t)); put_list(o, v); - put_curve(o, p->GetCurve()); + sc_exact_curve_export::put_export_curve(o, p->GetCurve(), the_context(), put_curve); } for (SmVertexuse* p : g.vertexuses) { diff --git a/plan/units/query.face_p2.toml b/plan/units/query.face_p2.toml index 7253012bcc..141127bb0c 100644 --- a/plan/units/query.face_p2.toml +++ b/plan/units/query.face_p2.toml @@ -9,4 +9,14 @@ tests = ["prog_test:my_test_surface_closest_point", "prog_test:my_test_drop_poin status = "green" claimed_by = "" claimed_at = "" +# PR #1216: query shim now emits shared lossless analytic surface heads and exact +# ellipse/circle EDGE records; ordinary UV trim/output curve records are unchanged. +# Verified locally against Apple-Clang C++: 1401/1401 pass, all with values, +# zero failed/deferred/both-error cases, max_rel_err=0; perturbation detects 1401/1401. +# Unchanged-main baseline: 1374 with values and 27 both-error cases. +# Existing PointClassify grids/boundaries and CurveOnClassify inputs are unchanged. +# Canonical Linux CI remains the merge gate; historical notes below retain their dates. +# Unsupported subclasses and records failing shared lossless round-trip checks stay +# inexact, including revolution/extrusion forms whose required NURBs are absent or +# whose serialized representation cannot be restored identically. notes = "GREEN (2026-10-25, PR #970): all 1401 cases pass (1374 with values, 27 both_errored, 0 failed, 0 deferred, max_rel_err=0e0, --perturb SENSITIVE) with PR #754 on main; bare-surface fallback arms marked DISPATCH GAP. PARTIAL (PR #800): difftest 1401 cases (1101 fixture: grids over the 13 exactly exported faces of the upstream .smb files Endcap, Mouse, box_with_large_fillet, disk, ellips_ts2, plane_circ, plane_square; points on, near and at the ends of every trim curve at offsets 0..1e-3; target-loop selections on ellips_ts2's face with two inner loops; SM_API_test TestSmCreateRectangle's 10x6 rectangle at the origin and at 1e10 on a real SmPlane; sm_ClassifyParamOnFaces face pairs; SmProtoVertex containment at the tolerance/capture limits; CurveOnClassify; trim-cache fixtures from #826; 300 generated incl. tiny/huge B-spline and plane faces, tolerance edits and out-of-domain points), 1400 passed, 0 failed, 1 deferred (blocked_by=surface.surface_p2(1), gen0142 awaiting PR #754), bit-identical (max_rel_err 0); --perturb SENSITIVE. In PR #800: implemented topology_entity and topology_handle on Objects to resolve SmEdge::GetCurve and SmFace::Point3DClassify, matched SM_UNINIT_TOL default in CurveOnClassify, and wired face_surface_cache in Point3DClassify (query.face_p1). main CI e33985411 (2026-10-23): PARTIAL query.face_p2 cases=1344 passed=1326 failed=0 deferred=18 blocked_by=query.face_p1(4),topology.edge(14). After intersect.curve_class_p2 (PR #85) merged: CurveOnClassify cases now compared (status, modified flag, every interval and its three point classifications; line is SmBSplineCurve::CreateLineSegment on both sides): 1344 cases, 1310 pass, 0 fail, 34 deferred; --perturb 1310/1310. PARTIAL: difftest 1344 cases (1044 fixture: grids over the 13 exactly exported faces of the upstream .smb files Endcap, Mouse, box_with_large_fillet, disk, ellips_ts2, plane_circ, plane_square; points on, near and at the ends of every trim curve at offsets 0..1e-3; target-loop selections on ellips_ts2's face with two inner loops; SM_API_test TestSmCreateRectangle's 10x6 rectangle at the origin and at 1e10 on a real SmPlane; sm_ClassifyParamOnFaces face pairs; SmProtoVertex containment at the tolerance/capture limits; CurveOnClassify; 300 generated incl. tiny/huge B-spline and plane faces, tolerance edits and out-of-domain points), 1309 pass bit-identical (max_rel_err 0), 0 failed, 35 deferred (intersect.curve_class_p2 20: SmCurveClassification::ReSet, the first call of CurveOnClassify, whose body also needs curve_class_p5/p7/p9 and topology.face_p4 GetEdges; intersect.surface_drop_lib_p3 15: SmSurface::DropCurve in GetOrCreateUVTrimCurve of faces without trim curves); --perturb SENSITIVE. Not compared: PointClassify's SmTrimSrfCache branch (the shim deletes the cache a file read leaves; B-rep surfaces carry no object cache in the port) and faces with surfaces of revolution/extrusion/cones/spheres/tori (the shared export carries them as B-spline parts). Built on topology.loopuse (PR #102: loopuse::vertices). Wired: SmProtoVertex::ContainsPoint in ProtoTopologyManager::find_proto_vertex / which_pv_to_combine (topology.proto_topology_p1: 63 cases no longer deferred). Not wired, still UNPORTED(query.face_p2): surface::trim_srf_cache, intersect::ray_tracer, intersect::topology_solver p1 (callers hold object handles without a Brep) and the CurveOnClassify calls of topology::face::p2 / intersect::topology_solver p2 (behind UNPORTED(intersect.curve_class_p4)). CO-PORTED in intersect.curve_class_p5 (PR #150): CurveOnClassify vertex handle mapping -> CurveOnClassifyObjects::vertex and ClassGeometry callback bound; IntersectVertices now calls p5. Existing ReSet and classification stubs are retained. Merge of PR #65: ClassifyFaceInterval (p7) and IntersectVertices (p5) both wired; difftest then 1344 cases, 1271 passed, 0 failed, 73 deferred. [topology.edgeuse_p3 wired (PR #117): SmLoop::ContainsUVPoint takes &mut Brep and calls GetOrCreateUVTrimCurve, so the former topology.face_p2 (38) and topology.edgeuse_p3 (15) deferrals are gone: 1344 cases, 1309 pass, 0 failed, 35 deferred (intersect.curve_class_p2 20, intersect.surface_drop_lib_p3 15)]"