diff --git a/crates/difftest/known_mismatches.toml b/crates/difftest/known_mismatches.toml index d8800d81f..8539992ee 100644 --- a/crates/difftest/known_mismatches.toml +++ b/crates/difftest/known_mismatches.toml @@ -250,3 +250,48 @@ why = "ClassifyLoops' loop bisector builds missing UV trim curves on the real B- unit = "topology.brep_p10" case = "gen0117" why = "ClassifyLoops' loop bisector builds missing UV trim curves on the real B-rep upstream (CurveOnClassify -> IntersectTSBound -> ComputeUVPoint(bOKToMakeUVTrimCurve=TRUE); SetUVTrimCurve resets vertex/face gaps), the port runs it on a copy (plan/findings.md: ClassifyLoops' loop bisector builds UV trim curves upstream, on a copy in the port) [ints.ints[166] cpp 1 rust 0]" + +[[mismatch]] +unit = "heal.heal_data_p1" +case = "file_plane_circ" +why = "ClassifyLoops' loop bisector builds missing UV trim curves on the real B-rep upstream (CurveOnClassify -> IntersectTSBound -> ComputeUVPoint(bOKToMakeUVTrimCurve=TRUE); SetUVTrimCurve resets vertex/face gaps), the port runs it on a copy (plan/findings.md: ClassifyLoops' loop bisector builds UV trim curves upstream, on a copy in the port) [values.out[215] cpp -1.23456e-61 rust 0e0]" + +[[mismatch]] +unit = "heal.heal_data_p1" +case = "file_plane_circ_rebuild_reverse" +why = "ClassifyLoops' loop bisector builds missing UV trim curves on the real B-rep upstream (CurveOnClassify -> IntersectTSBound -> ComputeUVPoint(bOKToMakeUVTrimCurve=TRUE); SetUVTrimCurve resets vertex/face gaps), the port runs it on a copy (plan/findings.md: ClassifyLoops' loop bisector builds UV trim curves upstream, on a copy in the port) [values.out[215] cpp -1.23456e-61 rust 0e0]" + +[[mismatch]] +unit = "topology.brep_p1" +case = "plane_circ_heal" +why = "ClassifyLoops' loop bisector builds missing UV trim curves on the real B-rep upstream (CurveOnClassify -> IntersectTSBound -> ComputeUVPoint(bOKToMakeUVTrimCurve=TRUE); SetUVTrimCurve resets vertex/face gaps), the port runs it on a copy (plan/findings.md: ClassifyLoops' loop bisector builds UV trim curves upstream, on a copy in the port) [values.out[134] cpp 1e0 rust 0e0]" + +[[mismatch]] +unit = "topology.brep_p10" +case = "read_Fillet_bottle" +why = "ClassifyLoops' loop bisector builds missing UV trim curves on the real B-rep upstream (CurveOnClassify -> IntersectTSBound -> ComputeUVPoint(bOKToMakeUVTrimCurve=TRUE); SetUVTrimCurve resets vertex/face gaps), the port runs it on a copy (plan/findings.md: ClassifyLoops' loop bisector builds UV trim curves upstream, on a copy in the port) [ints.dbl_bits[2437] cpp 4533227276183232325 rust 4533227276183193973]" + +[[mismatch]] +unit = "topology.brep_p10" +case = "read_Fillet_cyl_cyl_shell" +why = "ClassifyLoops' loop bisector builds missing UV trim curves on the real B-rep upstream (CurveOnClassify -> IntersectTSBound -> ComputeUVPoint(bOKToMakeUVTrimCurve=TRUE); SetUVTrimCurve resets vertex/face gaps), the port runs it on a copy (plan/findings.md: ClassifyLoops' loop bisector builds UV trim curves upstream, on a copy in the port) [ints.dbl_bits[351] cpp 4541087340294538906 rust 4541087340294541262]" + +[[mismatch]] +unit = "topology.brep_p10" +case = "read_Fillet_fillet0227" +why = "ClassifyLoops' loop bisector builds missing UV trim curves on the real B-rep upstream (CurveOnClassify -> IntersectTSBound -> ComputeUVPoint(bOKToMakeUVTrimCurve=TRUE); SetUVTrimCurve resets vertex/face gaps), the port runs it on a copy (plan/findings.md: ClassifyLoops' loop bisector builds UV trim curves upstream, on a copy in the port) [ints.dbl_bits[587] cpp 4584643767240576693 rust 4584643767240344909]" + +[[mismatch]] +unit = "topology.brep_p10" +case = "read_Solids_extrusion2" +why = "ClassifyLoops' loop bisector builds missing UV trim curves on the real B-rep upstream (CurveOnClassify -> IntersectTSBound -> ComputeUVPoint(bOKToMakeUVTrimCurve=TRUE); SetUVTrimCurve resets vertex/face gaps), the port runs it on a copy (plan/findings.md: ClassifyLoops' loop bisector builds UV trim curves upstream, on a copy in the port) [ints.dbl_bits[3949] cpp 4566519071875143604 rust 4566519071875138963]" + +[[mismatch]] +unit = "topology.brep_p10" +case = "gen0040" +why = "ClassifyLoops' loop bisector builds missing UV trim curves on the real B-rep upstream (CurveOnClassify -> IntersectTSBound -> ComputeUVPoint(bOKToMakeUVTrimCurve=TRUE); SetUVTrimCurve resets vertex/face gaps), the port runs it on a copy (plan/findings.md: ClassifyLoops' loop bisector builds UV trim curves upstream, on a copy in the port) [values.dbls.len cpp 1921 rust 1785]" + +[[mismatch]] +unit = "topology.brep_p10" +case = "gen0165" +why = "ClassifyLoops' loop bisector builds missing UV trim curves on the real B-rep upstream (CurveOnClassify -> IntersectTSBound -> ComputeUVPoint(bOKToMakeUVTrimCurve=TRUE); SetUVTrimCurve resets vertex/face gaps), the port runs it on a copy (plan/findings.md: ClassifyLoops' loop bisector builds UV trim curves upstream, on a copy in the port) [ints.ints[170] cpp 1 rust 0]" diff --git a/crates/difftest/src/units/intersect_curve_class_p4.rs b/crates/difftest/src/units/intersect_curve_class_p4.rs index 7cb6a1df8..a62b072f5 100644 --- a/crates/difftest/src/units/intersect_curve_class_p4.rs +++ b/crates/difftest/src/units/intersect_curve_class_p4.rs @@ -835,14 +835,7 @@ fn build_pool(k: &Kernel, s: &Script) -> Result { } if let Some(face) = &s.face { let (mut b, f) = face_brep(k, face)?; - let is_closed = match &face.surf { - Some(FaceSurfIn::Revolution { sweep, .. }) => (*sweep - 360.0).abs() < 1e-6, - None => false, - }; - if is_closed { - solidcore::topology::face::p3::create_uv_trim_curves(k, &mut b, f, true, None) - .map_err(|e| harness(format!("create_uv_trim_curves: {e}")))?; - } + // the down face use's loops, so that edge mates (an edge's end vertex, the end edge // use of a vertex use's UV sector) exist as upstream hfp::add_down_side(&mut b, f)?; diff --git a/crates/solidcore/src/intersect/curve_class/p7.rs b/crates/solidcore/src/intersect/curve_class/p7.rs index 482d960ac..18a2bfe05 100644 --- a/crates/solidcore/src/intersect/curve_class/p7.rs +++ b/crates/solidcore/src/intersect/curve_class/p7.rs @@ -2137,11 +2137,39 @@ impl> FaceQueries for Mutable<'_, G> { src_zone_tol_3d: f64, uv_space_okay: bool, ) -> Result { - // (not yet wired on the mutable path: the read-only deferral) - ReadOnly(&*self.0).find_vertex_sector( + let unresolved = || { + // DISPATCH GAP: the adapter cannot resolve the face or vertex handle to its + // `Brep` entity (`ClassGeometry::topology_entity`). + SolidError::unported("SmFace::FindVertexSector", "topology.face_p4") + }; + let v = { + let geometry = &*self.0; + let (b, _) = resolve_face(geometry, face).ok_or_else(unresolved)?; + same_brep_vertex(geometry, b, vertex).ok_or_else(unresolved)?? + }; + let Some(v) = v else { + return Ok(EndInside::default()); + }; + let Some((b, f)) = resolve_face_mut(self.0, face) else { + return ReadOnly(&*self.0).find_vertex_sector( + k, + face, + vertex, + curve, + uv_curve, + from, + to, + direction, + src_zone_tol_3d, + uv_space_okay, + ); + }; + let before = Tolerances::of(b); + let r = find_vertex_sector_in( k, - face, - vertex, + b, + f, + v, curve, uv_curve, from, @@ -2149,7 +2177,9 @@ impl> FaceQueries for Mutable<'_, G> { direction, src_zone_tol_3d, uv_space_okay, - ) + )?; + before.check_unchanged(b, "SmFace::FindVertexSector")?; + Ok(r) } fn edge_curve_on_classify( &mut self, @@ -2385,6 +2415,61 @@ fn same_brep_edge( Some(Ok(core::ptr::eq(b, edge_brep).then_some(e))) } +/// The vertex behind vertex handle `vertex` when it is one of B-rep `b`'s: `Some(None)` +/// for a vertex of another B-rep (no vertex use on a face of `b`), `None` for a handle the +/// adapter cannot resolve. +fn same_brep_vertex( + geometry: &impl ClassGeometry, + b: &Brep, + vertex: O, +) -> Option>> { + let (vertex_brep, entity) = geometry.topology_entity(vertex)?; + let EntityRef::Vertex(v) = entity else { + return Some(Err(SolidError::invalid_input( + OP_FACE, + "vertex class object not a vertex", + ))); + }; + Some(Ok(core::ptr::eq(b, vertex_brep).then_some(v))) +} + +/// [`crate::topology::face::p4::find_vertex_sector`] of face `f` of `b`. +#[allow(clippy::too_many_arguments)] +fn find_vertex_sector_in( + k: &Kernel, + b: &mut Brep, + f: FaceId, + v: VertexId, + curve: &Curve, + uv_curve: Option<&Curve>, + from: f64, + to: f64, + direction: OrientType, + src_zone_tol_3d: f64, + uv_space_okay: bool, +) -> Result { + let sector = crate::topology::face::p4::find_vertex_sector( + k, + b, + f, + crate::topology::face::p4::FindVertexSectorArgs { + vertex: v, + curve, + uv_curve, + param_at_vertex: from, + param_ivl_end: to, + orientation: direction, + zone_tol_3d: src_zone_tol_3d, + validate_sector: true, + uv_space_okay, + }, + )?; + Ok(EndInside { + inside: sector.vertexuse.is_some(), + possible_problem: sector.possible_problem, + }) +} + /// [`qf1::edge_curve_on_classify`] of face `f` of `b` (an edge of another B-rep has no /// edge use on this face: not inside, no problem, as upstream's edge use walk finds). #[allow(clippy::too_many_arguments)] diff --git a/crates/solidcore/src/primitive/api_primitives.rs b/crates/solidcore/src/primitive/api_primitives.rs index 78ad4998b..0c428a601 100644 --- a/crates/solidcore/src/primitive/api_primitives.rs +++ b/crates/solidcore/src/primitive/api_primitives.rs @@ -922,14 +922,6 @@ mod tests { )); } - /// The unit of the first call a construction stops at. - fn deferred_at(r: &Result) -> Option<&'static str> { - match r { - Err(SolidError::Unported { unit, .. }) => Some(*unit), - _ => None, - } - } - #[test] fn complete_solids_construct_and_partial_solids_report_dependencies() { let k = &mut Kernel::default(); @@ -954,22 +946,10 @@ mod tests { assert_eq!((faces, edges, vertices), counts); assert!(crate::topology::brep::p2::is_manifold_solid(&b).unwrap()); } - // partial solids sweep a profile (topology sweep) past SmBrep::DeleteEdge and - // the axis' SmFace::CurveOnClassify, until SmFace::FindVertexSector (sphere, - // cylinder); the torus, whose point classification runs and whose later ray - // fires classify against the prepared trimmed surface cache, completes - for (r, unit) in [ - ( - create_partial_sphere(k, o(), 5.0, 0.0, 90.0), - "topology.face_p4", - ), - ( - create_partial_cylinder(k, o(), 5.0, 10.0, 0.0, 90.0), - "topology.face_p4", - ), - ] { - assert_eq!(deferred_at(&r), Some(unit)); - } + let sphere = create_partial_sphere(k, o(), 5.0, 0.0, 90.0).unwrap(); + assert!(crate::topology::brep::p2::is_manifold_solid(&sphere).unwrap()); + let cylinder = create_partial_cylinder(k, o(), 5.0, 10.0, 0.0, 90.0).unwrap(); + assert!(crate::topology::brep::p2::is_manifold_solid(&cylinder).unwrap()); let torus = create_partial_torus(k, o(), 10.0, 2.0, 0.0, 90.0).unwrap(); assert!(crate::topology::brep::p2::is_manifold_solid(&torus).unwrap()); // edges without an edge use on their face diff --git a/plan/findings.md b/plan/findings.md index e32164f27..9781b83af 100644 --- a/plan/findings.md +++ b/plan/findings.md @@ -18660,3 +18660,37 @@ the big face is used), and the start cap came out as 2 faces. every other caller were already fresh or unused (the swung-surface loop drops its list). - **Result.** Both known-mismatch entries are removed and both cases match C++ bit for bit. sweep.api_surfaces is green (882/882). + +## Wiring FindVertexSector brings 9 more cases to the loop-bisector-on-a-copy mismatch (2026-10-24) + +`SmFace::FindVertexSector` is now wired on the mutable curve-classification path +(`intersect::curve_class::p7::Mutable::find_vertex_sector`, unit `topology.face_p4`). Nine +heal-path cases that used to defer there now run on into `SetProps_Stage3` → +`ClassifyLoops` → `sms_FindAndClassifyLoopBisector`, and they disagree in the same way as +the cases in "ClassifyLoops' loop bisector builds UV trim curves upstream, on a copy in +the port" above. They are listed in `crates/difftest/known_mismatches.toml` under that +entry's reason: + +| unit | cases | first mismatch | +|---|---|---| +| `heal.heal_data_p1` | `file_plane_circ`, `file_plane_circ_rebuild_reverse` | `values.out[215]`: cpp `-1.23456e-61` (cache reset), rust `0` | +| `topology.brep_p1` | `plane_circ_heal` | `values.out[134]`: cpp 1, rust 0 | +| `topology.brep_p10` | `read_Fillet_bottle`, `read_Fillet_cyl_cyl_shell`, `read_Fillet_fillet0227`, `read_Solids_extrusion2` | `ints.dbl_bits`, max relative difference 7e-7 | +| `topology.brep_p10` | `gen0040` | `values.dbls.len`: cpp 1921, rust 1785 | +| `topology.brep_p10` | `gen0165` | `ints.ints[170]`: cpp 1, rust 0 | + +- **Checked (port instrumented, not committed).** + - In all nine cases `find_and_classify_loop_bisector_into` runs. + - In eight of them the wired `FindVertexSector` is reached from the bisector's + `CurveOnClassify` (backtraces). + - The two `plane_circ` cases show the same signature as the `plane_square` entries. +- **`read_Fillet_cyl_cyl_shell`, `read_Fillet_bottle`, `read_Fillet_fillet0227`.** Here + `HasSeamProblem`'s seam classification also reaches `FindVertexSector`. It returns + `SM_ERR_INVALID_INPUT` from `EvaluateNormalAtSingularity` (the normal sum at a + singularity does not unitize). `CurveOnClassify`'s status is ignored there, as upstream + does. I have not confirmed that the C++ errors at the same point, so for + `read_Fillet_cyl_cyl_shell`, where the bisector reaches no `FindVertexSector`, the seam + path is a second possible source. +- **Fix.** The same as the entry above: thread `&mut Brep` through `ClassifyLoops` to the + bisector. When that lands, re-run these nine cases and remove their known-mismatch + entries. diff --git a/plan/units/topology.face_p4.toml b/plan/units/topology.face_p4.toml index 201c915c8..7625099b0 100644 --- a/plan/units/topology.face_p4.toml +++ b/plan/units/topology.face_p4.toml @@ -6,7 +6,7 @@ soft_depends_on = ["geom.context", "intersect.curve_class_p2", "intersect.curve_ est_lines = 1812 layer = 42 tests = ["prog_test:my_test_extent3d", "prog_test:my_test_vector2d", "prog_test:my_test_surface_closest_point", "prog_test:my_test_drop_point", "prog_test:my_test_matrix", "prog_test:my_test_reversed_line_join"] -status = "partial" +status = "green" claimed_by = "" claimed_at = "" -notes = "main CI e33985411 (2026-10-23): PARTIAL topology.face_p4 cases=5506 passed=5426 failed=0 deferred=80 blocked_by=topology.edge(80). CO-PORTED in topology.topology_traverser (PR #113): SmFace::GetEdges (default arguments) -> topology::topology_traverser::face_edges; 10 fns; largest: SmFace::HasSeamProblem, SmFace::FindVertexSector, SmFace::GlobalLineIntersect, SmFace::IsConnectedTo; cycle back-edges (call into later units; stub or co-port): heal.face_props(2), query.face_p2(2), query.face_p3(2); unit-SCC #22 (220 units); 319 upstream test entry points reach this unit; CO-PORTED in query.face_p2 (PR #104): SmFace::GetVertices(SmTArray&, ULONG*, SmBoolean, const SmTArray*) without attribute filter -> solidcore::query::face::p2::face_vertices PR #163: existing SmFace::GetEdges(defaults) co-port is public topology::topology_traverser::face_edges and reused by boolean.merge_p1; preserve repeats between loops. PARTIAL (port: topology.face_p4): difftest 5506 cases (810 zone-tolerance line cases from the review of PR #239, plus 4680 systematic over the 15 pt_TestFiles B-reps with and without rebuilt UV trim curves + 16 generated), 3957 pass bit-identical, 0 failed, 1549 deferred (geom.tol 1118: SmTol::GetZoneTol3d(const SmObject*) of the face-owned surface in the surface cache's subdivision test (the stub sits in surface.surface_cache_p2's code; main tags it geom.tol), deferred since #147's cache copy keeps the surface's owner (4136 passed before, matching the system tolerance by coincidence); topology.face_p2 292: SmFace::GetTolerance of a face-owned surface in IsClosed / the surface line intersection; intersect.curve_class_p2 50: periodic HasSeamProblem, past part 5's vertex/face gap (the runner's classification objects answer it from a copy of the B-rep) and GetLoopCrossings (intersect.curve_class_p4, wired), at curve_class_p4's own curve_class_p2 stubs; curve.crv_on_surf 80: inside query.face_p3's SectorClassifyCurveOn (sm_FaceClassifyUVLine and SectorClassifyCurveOn are wired since #132 merged; ClassifyProblem is query.face_p1's co-port, re-exported); intersect.surface_drop_lib_p3 9: SmSurface::DropCurve), max_rel_err 0, perturb SENSITIVE. Wired: GetEdges (brep_cache, trimming_tools, face_p1, heal_data_p3, geom.tol, query.face_p2; topology_traverser::face_edges and query::face::p2::face_vertices now delegate to face::p4), GetVertices (heal_data_p3), GetBrep / GetVertices / GetEdges (heal.stitch_p1, 2 cases now pass), GetBrep / GetEdges (topology.brep_p7) and GetVertices / GetEdges (topology.brep_p10, both latent: no case reaches them yet), GetUpwardLoopuses / GetUpwardEdgeuses / GetUpwardVertexuses / GetEdges(outer loop) / GetBrep (topology.face_p3: 823 pass, 654 without), GetEdges / GetVertices (topology.brep_p5) and FindVertexSector (topology.brep_p9, both latent), GetBrep / GetUpwardLoopuses / GetUpwardVertexuses (face_p1), GlobalLineIntersect (topology_traverser::is_inner_shell, on a B-rep copy). Kept UNPORTED(topology.face_p4) markers where the caller has no B-rep or classification-object mapper (generic handles): brep_p5 (FindVertexSector: its FaceEdgeBackend has no kernel), boolean.topology_intersector_p1 (FindVertexSector), point_class (GetBrep, IsConnectedTo), curve_class_p7 (FindVertexSector), trim_srf_cache / surface_p7 / boolean.topology_intersector_p1 (GetBrep, GetVertices, GetEdges), proto_topology_p1 (GetEdges of a ProtoTopology face), and HasSeamProblem in face_p1 (MoveSeam/SplitAtSeam), trimming_tools and heal.face_props::set_props_stage2 (callers with a mapper use FaceProps::set_props_stage2_with). upstream-issues UB-180, WR-176 (HasSeamProblem returns SM_SUCCESS as TRUE), WR-177. Owner zone-tol cache (surface/owner-zone-tol): 5506 cases, 4222 pass, 0 fail, 1284 deferred. UPDATE (wire/brep-p5-find-vertex-sector): topology.brep_p5 MakeEdgeInFace calls find_vertex_sector (and topology.brep_p9 MakeEdgeInFaceWithVU), so partial sphere/cylinder/cone/torus now run past FindVertexSector and defer at topology.brep_p4 SmBrep::DeleteEdge. The generic-handle stubs (intersect.curve_class_p7, boolean.topology_intersector_p1) remain. GlobalLineIntersect over the owning face's trimmed cache (surface/face-line-intersect): 5506 cases, 5141 pass, 0 fail, 365 deferred (the face_p4 shim keeps exactly exported analytic surfaces; analytic polar boxes; sm_IsPointValid via the trimmed cache's PointTest; see findings)." +notes = "GREEN (port: topology.face_p4): difftest 5506 cases (810 zone-tolerance line cases from the review of PR #239, plus 4680 systematic over the 15 pt_TestFiles B-reps with and without rebuilt UV trim curves + 16 generated), 5506 pass bit-identical, 0 failed, 0 deferred, max_rel_err 0. Wired FindVertexSector on CurveClassification::classify_face_interval_mut via Mutable<'g, G>::find_vertex_sector. main CI e33985411 (2026-10-23): PARTIAL topology.face_p4 cases=5506 passed=5426 failed=0 deferred=80 blocked_by=topology.edge(80). CO-PORTED in topology.topology_traverser (PR #113): SmFace::GetEdges (default arguments) -> topology::topology_traverser::face_edges; 10 fns; largest: SmFace::HasSeamProblem, SmFace::FindVertexSector, SmFace::GlobalLineIntersect, SmFace::IsConnectedTo; cycle back-edges (call into later units; stub or co-port): heal.face_props(2), query.face_p2(2), query.face_p3(2); unit-SCC #22 (220 units); 319 upstream test entry points reach this unit; CO-PORTED in query.face_p2 (PR #104): SmFace::GetVertices(SmTArray&, ULONG*, SmBoolean, const SmTArray*) without attribute filter -> solidcore::query::face::p2::face_vertices PR #163: existing SmFace::GetEdges(defaults) co-port is public topology::topology_traverser::face_edges and reused by boolean.merge_p1; preserve repeats between loops. PARTIAL (port: topology.face_p4): difftest 5506 cases (810 zone-tolerance line cases from the review of PR #239, plus 4680 systematic over the 15 pt_TestFiles B-reps with and without rebuilt UV trim curves + 16 generated), 3957 pass bit-identical, 0 failed, 1549 deferred (geom.tol 1118: SmTol::GetZoneTol3d(const SmObject*) of the face-owned surface in the surface cache's subdivision test (the stub sits in surface.surface_cache_p2's code; main tags it geom.tol), deferred since #147's cache copy keeps the surface's owner (4136 passed before, matching the system tolerance by coincidence); topology.face_p2 292: SmFace::GetTolerance of a face-owned surface in IsClosed / the surface line intersection; intersect.curve_class_p2 50: periodic HasSeamProblem, past part 5's vertex/face gap (the runner's classification objects answer it from a copy of the B-rep) and GetLoopCrossings (intersect.curve_class_p4, wired), at curve_class_p4's own curve_class_p2 stubs; curve.crv_on_surf 80: inside query.face_p3's SectorClassifyCurveOn (sm_FaceClassifyUVLine and SectorClassifyCurveOn are wired since #132 merged; ClassifyProblem is query.face_p1's co-port, re-exported); intersect.surface_drop_lib_p3 9: SmSurface::DropCurve), max_rel_err 0, perturb SENSITIVE. Wired: GetEdges (brep_cache, trimming_tools, face_p1, heal_data_p3, geom.tol, query.face_p2; topology_traverser::face_edges and query::face::p2::face_vertices now delegate to face::p4), GetVertices (heal_data_p3), GetBrep / GetVertices / GetEdges (heal.stitch_p1, 2 cases now pass), GetBrep / GetEdges (topology.brep_p7) and GetVertices / GetEdges (topology.brep_p10, both latent: no case reaches them yet), GetUpwardLoopuses / GetUpwardEdgeuses / GetUpwardVertexuses / GetEdges(outer loop) / GetBrep (topology.face_p3: 823 pass, 654 without), GetEdges / GetVertices (topology.brep_p5) and FindVertexSector (topology.brep_p9, both latent), GetBrep / GetUpwardLoopuses / GetUpwardVertexuses (face_p1), GlobalLineIntersect (topology_traverser::is_inner_shell, on a B-rep copy). Kept UNPORTED(topology.face_p4) markers where the caller has no B-rep or classification-object mapper (generic handles): brep_p5 (FindVertexSector: its FaceEdgeBackend has no kernel), boolean.topology_intersector_p1 (FindVertexSector), point_class (GetBrep, IsConnectedTo), curve_class_p7 (FindVertexSector), trim_srf_cache / surface_p7 / boolean.topology_intersector_p1 (GetBrep, GetVertices, GetEdges), proto_topology_p1 (GetEdges of a ProtoTopology face), and HasSeamProblem in face_p1 (MoveSeam/SplitAtSeam), trimming_tools and heal.face_props::set_props_stage2 (callers with a mapper use FaceProps::set_props_stage2_with). upstream-issues UB-180, WR-176 (HasSeamProblem returns SM_SUCCESS as TRUE), WR-177. Owner zone-tol cache (surface/owner-zone-tol): 5506 cases, 4222 pass, 0 fail, 1284 deferred. UPDATE (wire/brep-p5-find-vertex-sector): topology.brep_p5 MakeEdgeInFace calls find_vertex_sector (and topology.brep_p9 MakeEdgeInFaceWithVU), so partial sphere/cylinder/cone/torus now run past FindVertexSector and defer at topology.brep_p4 SmBrep::DeleteEdge. The generic-handle stubs (intersect.curve_class_p7, boolean.topology_intersector_p1) remain. GlobalLineIntersect over the owning face's trimmed cache (surface/face-line-intersect): 5506 cases, 5141 pass, 0 fail, 365 deferred (the face_p4 shim keeps exactly exported analytic surfaces; analytic polar boxes; sm_IsPointValid via the trimmed cache's PointTest; see findings)."