diff --git a/crates/difftest/src/units/boolean_merge_p1.rs b/crates/difftest/src/units/boolean_merge_p1.rs index 03f05ea3b1..dcb01c98ae 100644 --- a/crates/difftest/src/units/boolean_merge_p1.rs +++ b/crates/difftest/src/units/boolean_merge_p1.rs @@ -923,16 +923,17 @@ fn run_rust(i: &Inputs, b: &mut Brep, other: &Brep) -> Result, SolidErr 7 => { let mut other = other.clone(); // The native probe constructs and immediately destroys its shift. - let transform = |_: &mut Brep, _: bool, _: &_, _: Option<_>, _: bool| { - // UNPORTED(topology.brep_p5): SmBrep::Transform - Err(SolidError::unported( - "SmBrep::Transform", - "topology.brep_p5", - )) - }; - port::temporary_brep_transform(&k, &k, b, &other)? - .apply(b, &mut other, transform)? - .restore(Some(b), Some(&mut other), transform)?; + let shift = port::temporary_brep_transform(&k, &k, b, &other)?; + // SmBrep::Transform, both operands in the one kernel + let mut transform = + |brep: &mut Brep, _: bool, xfm: &_, scale: Option<_>, scale_tol: bool| { + solidcore::topology::brep::p5::transform(&mut k, brep, xfm, scale, scale_tol) + }; + shift.apply(b, &mut other, &mut transform)?.restore( + Some(b), + Some(&mut other), + &mut transform, + )?; } 8 | 9 => { let sec = EdgeuseId::from_index(u32::from(a) % b.edgeuses().len() as u32); diff --git a/crates/difftest/src/units/boolean_topology_intersector_p3.rs b/crates/difftest/src/units/boolean_topology_intersector_p3.rs index 92a219bd3d..d19b0de47e 100644 --- a/crates/difftest/src/units/boolean_topology_intersector_p3.rs +++ b/crates/difftest/src/units/boolean_topology_intersector_p3.rs @@ -13,7 +13,9 @@ //! Both sides produce one flat record: the pre-relation statuses, the merge's status //! and outputs (only on success: the port returns none with an error), then the state //! (counts, every vertex and edge tolerance, and the vertex, edge and face mates sorted -//! by handle). Handles are the p1 runner's `brep kind index` triples. +//! by handle). Handles are the p1 runner's `brep kind index` triples, numbered after the +//! merge by both sides' re-walk of the B-reps (the shim's `walk_all`, `Walk` here), so +//! records the merge deleted (kept unlinked in the Rust arena) are unknown on both. //! //! **Selectors.** Generated handles are selectors taken modulo the entity count of //! their kind; classification parameters are fractions of the classified edge's @@ -55,7 +57,9 @@ use solidcore::topology::edgeuse::p1 as eu1; use solidcore::topology::face::p4 as fp4; use solidcore::topology::loopuse as lu; use solidcore::topology::proto_topology::ProtoBrep; -use solidcore::topology::{Brep, EdgeId, EdgeuseId, FaceuseId, LoopuseId, VertexId, VertexuseId}; +use solidcore::topology::{ + Brep, EdgeId, EdgeuseId, FaceId, FaceuseId, LoopuseId, VertexId, VertexuseId, +}; use solidcore::{EntityRef, SolidError}; use solidcore_ffi::units::boolean_topology_intersector_p1 as ffi1; use solidcore_ffi::units::boolean_topology_intersector_p3::{self as ffi, Op}; @@ -65,6 +69,7 @@ use super::boolean_topology_intersector_p1::{ Two, H, K_BREP, K_EDGE, K_EDGE_CURVE, K_FACE, K_FACE_SURFACE, K_NULL, K_TRIM_CURVE, K_VERTEX, NULL_DEREF, TI_HANDLE, }; +use super::boolean_merge_p1::Canon; use super::topology_vertexuse::import; use crate::case::{CaseContext, CaseSource}; use crate::runner::default_cases; @@ -491,10 +496,58 @@ fn status(r: Result, o: &mut Vec) -> Result, So } } -fn put_list(o: &mut Vec, hs: &[H]) { +fn put_list(o: &mut Vec, walk: &Walk, hs: &[H]) { o.push(hs.len() as f64); for &h in hs { - put_h(o, Some(h)); + walk.put(o, Some(h)); + } +} + +/// The shim's `walk_all` after the merge: both B-reps re-walked +/// ([`Canon::reachable`], the shim's `walk`), so a vertex, edge or face handle (and an +/// edge curve's or face surface's) is its position in that walk, and a record the merge +/// deleted (unlinked, but kept in the Rust arena) is unknown, `-1 -3 -1`, as a deleted +/// object upstream. Without deletions the walk is the import order (the identity). +struct Walk([Option; 2]); + +impl Walk { + fn of(two: &Two) -> Self { + Self([0i8, 1].map(|b| two.brep(brep_h(b)).map(Canon::reachable))) + } + + /// The post-merge triple of `h` (see [`Walk`]). + fn triple(&self, h: Option) -> [f64; 3] { + let mut o = Vec::with_capacity(3); + self.put(&mut o, h); + [ + o.first().copied().unwrap_or(-1.0), + o.get(1).copied().unwrap_or(-3.0), + o.get(2).copied().unwrap_or(-1.0), + ] + } + + fn put(&self, o: &mut Vec, h: Option) { + let Some(h) = h else { + put_h(o, None); + return; + }; + let canon = usize::try_from(h.b) + .ok() + .and_then(|b| self.0.get(b)) + .and_then(Option::as_ref); + let at = match (canon, h.kind) { + (Some(c), K_VERTEX) => c.vertices.of(Some(VertexId::from_index(h.i))), + (Some(c), K_EDGE | K_EDGE_CURVE) => c.edges.of(Some(EdgeId::from_index(h.i))), + (Some(c), K_FACE | K_FACE_SURFACE) => c.faces.of(Some(FaceId::from_index(h.i))), + _ => { + put_h(o, Some(h)); + return; + } + }; + match u32::try_from(at) { + Ok(i) => put_h(o, Some(hd(h.b, h.kind, i))), + Err(_) => o.extend([-1.0, -3.0, -1.0]), + } } } @@ -575,7 +628,11 @@ fn new_class(k: &Kernel, c: &ClassR) -> Result, SolidErro Ok(cc) } -fn put_class_out(o: &mut Vec, cc: &CurveClassification) -> Result<(), SolidError> { +fn put_class_out( + o: &mut Vec, + walk: &Walk, + cc: &CurveClassification, +) -> Result<(), SolidError> { o.push(cc.size() as f64); for i in 0..cc.size() { let ivl = cc.at(i)?; @@ -583,29 +640,34 @@ fn put_class_out(o: &mut Vec, cc: &CurveClassification) -> Result<(), So o.push(ivl.interval.max()); for pc in [&ivl.start, &ivl.mid, &ivl.end] { o.push(f64::from(pc.point_class().code())); - put_h(o, pc.object()); + walk.put(o, pc.object()); } } Ok(()) } -/// The state after the merge (see the module docs). -fn put_state(o: &mut Vec, two: &Two, ti: &TopologyIntersector) -> Result<(), SolidError> { +/// The state after the merge (see the module docs), in the post-merge walk ([`Walk`]): +/// the records it reaches, in its order; mates sorted by their post-merge handles. +fn put_state( + o: &mut Vec, + two: &Two, + walk: &Walk, + ti: &TopologyIntersector, +) -> Result<(), SolidError> { for b in 0..2i8 { - let Some(br) = two.brep(brep_h(b)) else { + let canon = usize::try_from(b).ok().and_then(|i| walk.0.get(i)); + let (Some(br), Some(Some(c))) = (two.brep(brep_h(b)), canon) else { o.extend([0.0, 0.0, 0.0]); continue; }; - o.push(br.vertices().len() as f64); - o.push(br.edges().len() as f64); - o.push(br.faces().len() as f64); - for v in 0..br.vertices().len() { - let id = VertexId::from_index(u32::try_from(v).unwrap_or(u32::MAX)); - o.push(br.vertex(id)?.tolerance); + o.push(c.vertices.items.len() as f64); + o.push(c.edges.items.len() as f64); + o.push(c.faces.items.len() as f64); + for &v in &c.vertices.items { + o.push(br.vertex(v)?.tolerance); } - for e in 0..br.edges().len() { - let id = EdgeId::from_index(u32::try_from(e).unwrap_or(u32::MAX)); - o.push(br.edge(id)?.tolerance); + for &e in &c.edges.items { + o.push(br.edge(e)?.tolerance); } } for kind in [ @@ -613,12 +675,16 @@ fn put_state(o: &mut Vec, two: &Two, ti: &TopologyIntersector) -> Result ClassObjectKind::Edge, ClassObjectKind::Face, ] { - let mut objs = ti.mapped_brep_objects(kind)?; - objs.sort(); + let mut objs: Vec<_> = ti + .mapped_brep_objects(kind)? + .into_iter() + .map(|h| (walk.triple(Some(h)), walk.triple(ti.other_mate(Some(h))))) + .collect(); + objs.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal)); o.push(objs.len() as f64); - for h in objs { - put_h(o, Some(h)); - put_h(o, ti.other_mate(Some(h))); + for (h, mate) in objs { + o.extend(h); + o.extend(mate); } } Ok(()) @@ -702,9 +768,10 @@ fn run_rust_merge(i: &Inputs, p: Prepared) -> Result, SolidError> { }; let r = ti.merge_curve_on_surfaces(&mut k, &mut two, &input); if let Some(m) = status(r, &mut o)? { + let walk = Walk::of(&two); o.push(flag(m.deleted_topology)); - put_list(&mut o, &m.deleted_edges.unwrap_or_default()); - put_list(&mut o, &m.deleted_vertices.unwrap_or_default()); + put_list(&mut o, &walk, &m.deleted_edges.unwrap_or_default()); + put_list(&mut o, &walk, &m.deleted_vertices.unwrap_or_default()); } } MergeR::CurveClasses { @@ -722,11 +789,12 @@ fn run_rust_merge(i: &Inputs, p: Prepared) -> Result, SolidError> { let r = ti.merge_curve_classes(&mut k, &mut two, &mut cc1, &mut cc2, merge_singularities); if let Some(m) = status(r, &mut o)? { + let walk = Walk::of(&two); o.push(flag(m.topology_was_deleted)); - put_list(&mut o, &m.deleted_edges.unwrap_or_default()); - put_list(&mut o, &m.deleted_vertices.unwrap_or_default()); - put_class_out(&mut o, &cc1)?; - put_class_out(&mut o, &cc2)?; + put_list(&mut o, &walk, &m.deleted_edges.unwrap_or_default()); + put_list(&mut o, &walk, &m.deleted_vertices.unwrap_or_default()); + put_class_out(&mut o, &walk, &cc1)?; + put_class_out(&mut o, &walk, &cc2)?; } } MergeR::CoincidentSurfaces { s1, s2 } => { @@ -741,12 +809,13 @@ fn run_rust_merge(i: &Inputs, p: Prepared) -> Result, SolidError> { } let r = ti.merge_coincident_surfaces(&mut k, &mut two, s1, s2, Some(TI_HANDLE)); if let Some(m) = status(r, &mut o)? { - put_list(&mut o, &m.faces); - put_list(&mut o, &m.other_faces); + let walk = Walk::of(&two); + put_list(&mut o, &walk, &m.faces); + put_list(&mut o, &walk, &m.other_faces); } } } - put_state(&mut o, &two, &ti)?; + put_state(&mut o, &two, &Walk::of(&two), &ti)?; Ok(o) } diff --git a/crates/difftest/src/units/sweep_topology_sweep.rs b/crates/difftest/src/units/sweep_topology_sweep.rs index a2a436cf59..a97845eebe 100644 --- a/crates/difftest/src/units/sweep_topology_sweep.rs +++ b/crates/difftest/src/units/sweep_topology_sweep.rs @@ -847,14 +847,19 @@ mod tests { assert_ne!(model.k.context.current_mark(MarkType::Mark), old_mark); assert!(!model.k.context.is_mark_locked(MarkType::Mark)); model.transform(b, Axis2Placement::default())?; - // This fixture reaches HCR's separate, still-unavailable transform adapter. + // HCR's scene transform is wired (topology::brep::p5::transform_with_notifications): + // the scene is built, then this fixture has no service to retain it in. let hcr = model.hcr(b, Axis2Placement::default(), 0.001, 0.1, false, false, true); - assert!(matches!( - hcr, - Err(SolidError::Unported { - upstream: "SmBrep::Transform", unit: "topology.brep_p5" - }) - ), "{hcr:?}"); + assert!( + matches!( + hcr, + Err(SolidError::InvalidInput { + reason: "fixture has no retained HCR service", + .. + }) + ), + "{hcr:?}" + ); model.b[0].set_editing_enabled(false); model.k.context.set_doing_boolean(false); // query.brep (CalculateBoundingBox) is ported: the stitch now fails later, diff --git a/crates/solidcore/src/blend/hcr.rs b/crates/solidcore/src/blend/hcr.rs index 6bf79d6035..96df22d6fb 100644 --- a/crates/solidcore/src/blend/hcr.rs +++ b/crates/solidcore/src/blend/hcr.rs @@ -29,10 +29,9 @@ //! `topology::brep::p3::copy_brep`) notifies through the caller's notification //! environment, naming the copy's objects by its `key_of` and `brep`'s by `source_key` //! (upstream objects are known by address), so [`Hcr::new`] and [`Hcr::with_transform`] -//! take the kernel mutably with both. The scene's transformation (`Transform`) and -//! its bounding box -//! (`SmBrep::CalculateBoundingBox`) belong to unported units, so [`Hcr::with_transform`] -//! stops there; [`Hcr::from_scene`] is the rest of `ConstructorInit` for a caller that +//! take the kernel mutably with both. The scene's transformation (`SmBrep::Transform`, +//! `topology::brep::p5::transform_with_notifications`) notifies through the same +//! environment. [`Hcr::from_scene`] is the rest of `ConstructorInit` for a caller that //! already holds the transformed scene and its box. //! - The pointer maps `m_pSilVertices`, `m_pSilEdges` and `m_pBackFacingEdges` are id //! sets; [`Hcr::silhouette_edges`] returns the ids in ascending order (upstream @@ -224,16 +223,22 @@ fn copy_brep( crate::topology::brep::p3::copy_brep(k, env, brep, source_key, None, None, true, true) } -// ---- unported callees --------------------------------------------------------------------- - -/// `SmBrep::Transform` (unit `topology.brep_p5`). -fn transform_brep(brep: &mut Brep, transform: &Axis2Placement) -> Result<()> { - let _ = (brep, transform); - // UNPORTED(topology.brep_p5): SmBrep::Transform - Err(SolidError::unported( - "SmBrep::Transform", - "topology.brep_p5", - )) +/// `SmBrep::Transform(crRotateNMove)` with its defaults (no scale, `bScaleTol` `TRUE`), +/// notifying through the scene copy's environment `env`. +fn transform_brep( + k: &mut Kernel, + env: &mut edge::NotifyEnv<'_>, + brep: &mut Brep, + transform: &Axis2Placement, +) -> Result<()> { + crate::topology::brep::p5::transform_with_notifications( + k, + brep, + transform, + None, + true, + Some(env), + ) } /// `SmBrep::FindAndSetInfiniteRegion()` with upstream's default arguments (no shell to @@ -529,8 +534,9 @@ impl Hcr { /// /// # Errors /// - /// Those of the scene copy (`topology::brep::p3::copy_brep`); [`SolidError::Unported`] - /// from the transform and box (units `topology.brep_p5`, `query.brep`). + /// Those of the scene copy (`topology::brep::p3::copy_brep`); the transform's and + /// `FindAndSetInfiniteRegion`'s failures are ignored as upstream, apart from + /// [`SolidError::Unported`] from their callees. pub fn with_transform( k: &mut Kernel, env: &mut edge::NotifyEnv<'_>, @@ -544,7 +550,7 @@ impl Hcr { let mut scene = copy_brep(k, env, brep, source_key)?; scene.set_editing_enabled(true); // transform the copied Brep to place the eye looking up the Z axis - ignore_status(transform_brep(&mut scene, &vt))?; + ignore_status(transform_brep(k, env, &mut scene, &vt))?; ignore_status(find_and_set_infinite_region(k, &mut scene))?; // [B23] // its status is ignored upstream (the box then stays empty) let scene_box = ignored(qbrep::calculate_bounding_box( diff --git a/crates/solidcore/src/topology/brep/p5.rs b/crates/solidcore/src/topology/brep/p5.rs index 280e8954cb..3c7a045687 100644 --- a/crates/solidcore/src/topology/brep/p5.rs +++ b/crates/solidcore/src/topology/brep/p5.rs @@ -416,7 +416,7 @@ pub fn combine_with_notifications( notifications.as_deref_mut(), ))?; } - set_vertex_point(k, b, survivor, target.point, notifications)?; + set_vertex_point(k, b, survivor, target.point, notifications.as_deref_mut())?; crate::error::ignore_status(vertex::set_tolerance( b, survivor, @@ -424,7 +424,8 @@ pub fn combine_with_notifications( true, true, ))?; - update_vertex_edge_intervals(b, survivor) + let env = notifications.ok_or_else(|| SolidError::null_pointer(OP))?; + crate::topology::brep::p1::update_vertex_edge_intervals(k, b, env, survivor) } /// `Notify(SM_NO_MERGE_IN_BREP, pSurvivingVertex, pVertexToDelete, this)` @@ -553,11 +554,6 @@ fn set_vertex_point( ) } -fn update_vertex_edge_intervals(_b: &mut Brep, _vertex: VertexId) -> Result<()> { - // UNPORTED(topology.brep_p1): SmBrep::UpdateVertexEdgeIntervals - missing("SmBrep::UpdateVertexEdgeIntervals", "topology.brep_p1") -} - /// Makes a wire edge over the caller's curve interval. Evaluates start before end, /// then needs the owning B-rep's infinite region; the owned curve drops on error. pub fn create_wire_edge_from_curve( diff --git a/plan/units/blend.hcr.toml b/plan/units/blend.hcr.toml index 3fefefe31a..2e06fc3ef6 100644 --- a/plan/units/blend.hcr.toml +++ b/plan/units/blend.hcr.toml @@ -9,4 +9,4 @@ tests = ["SM_API_test:TestSmCreateSilhouetteCurves", "prog_test:my_test_tsurface status = "partial" claimed_by = "" claimed_at = "" -notes = "main CI e33985411 (2026-10-23): PARTIAL blend.hcr cases=434 passed=431 failed=0 deferred=3 blocked_by=topology.face_p2(3). EXACT SmSurfOfRevolution EXPORT (PR #304, shared graph exports carry revolution faces exactly; refreshed on main ca3bbd2b, 2026-10-21): difftest 434 cases, 393 pass, 0 failed, 41 deferred (topology.edge 30, intersect.surface_drop_lib_p3 8, surface.surface_p2 3), max_rel_err 0; 396 -> 393 pass: gen0043/gen0200/gen0245 (both sides errored before, nothing compared) now reach UNPORTED(topology.edge) SmEdge::GetVertex stubs in intersect/curve_class/p1.rs and p3.rs. Earlier: 14 fns; partial (PR #191): 434 cases (34 fixtures: hints 3/-1/big/fractional and fa >= fb / fa == fb, fixtures/blend.hcr), 394 pass, 0 fail, 40 deferred (topology.edge 27, topology.face_p3 7, surface.surface_p2 3, topology.vertex 2, intersect.surface_drop_lib_p3 1) after the maintainer merges of main with topology.brep_p1 and intersect.curve_class_p2 partial (SmCurveClassification::UpdatePointClass wired to curve_class_p2; its 35 deferrals now run: 28 pass, 7 move on to topology.edge / topology.vertex). With topology.brep_p1 alone: 366 pass, 68 deferred (GetInfiniteRegion, GetEdges, GetFaces, GetSurfaces, AddDeletedSurface wired to topology::brep::p1; the PR's m_bEditingEnabled co-port dropped for brep_p1's; the runner now sets each face surface's owner to its face as the upstream build does, without which GetSurfaces selected nothing and the 2 IsPointVisible cases failed; they now defer at surface.surface_p2 / topology.face_p3); before that merge 309 pass, 125 deferred (topology.brep_p1 67); --perturb SENSITIVE 309/309 (pre-merge); probe 3000 seeds clean (before the review round). The FFI guard accepts every int64 hint (C++: 0 none, 1 visible, other invisible) and either order of fa/fb (SmExtent1d swaps). The invisible-hint fixtures read the scene's infinite region (topology.brep_p1, wired at the merge). SmEdgeuse::EvaluateBinormal is wired to topology.edgeuse_p3 (on the scene itself, bUse3DOnly FALSE), and SmBrep::GetEdges without a filter to topology::brep::p1::edges. Of the 309 passes, 259 compare values (construction state, 1-interval classifications, single-piece ComputeEdgeVisibility, silhouette lists; status/flag records for the wire, lamina and listed early-outs) and 50 are both-errored (24 inexact_geometry, 25 rejected UB-169 guard cases, 1 rejected fixture). Classifications with 2 or more intervals: 21 generated (crossing wire-loop fixtures), all deferred: InsertPointClass splits through InsertAt / EditIntervalMin / EditIntervalMax (intersect.curve_class_p2, deferred before its merge; see the counts above); likewise the 13 multi-piece ComputeEdgeVisibility cases. The scene B-rep's SmBrep copy + Transform (topology.brep_p3) and its box (query.brep) are stubbed in Hcr::with_transform (FindAndSetInfiniteRegion is wired to topology.brep_p5 since PR #285); the runner imports the C++ scene and uses Hcr::from_scene. ComputeSilhouettes / ComputeGlobalVisibility / IsPointVisible start with topology.brep_p1 queries (only IsPointVisible is generated; it defers). SmFace::FindIfRectangularTrim is wired to topology.face_p1 (IsPointVisible's surface set; not reached while IsPointVisible defers on topology.brep_p1). CO-PORTED SmCurveClassification constructor (see intersect.curve_class_p4). Upstream defects UB-169, UB-170. hazards: ptr-hash (GetSilhouetteEdges sorted by id) Re-run after wiring stale stubs (PR #285): 434 cases, 396 pass, 0 fail, 38 deferred, max_rel_err 0. Main since: 14 fns; partial (PR #191): 434 cases (34 fixtures: hints 3/-1/big/fractional and fa >= fb / fa == fb, fixtures/blend.hcr), 394 pass, 0 fail, 40 deferred (topology.edge 27, topology.face_p3 7, surface.surface_p2 3, topology.vertex 2, intersect.surface_drop_lib_p3 1) after the maintainer merges of main with topology.brep_p1 and intersect.curve_class_p2 partial (SmCurveClassification::UpdatePointClass wired to curve_class_p2; its 35 deferrals now run: 28 pass, 7 move on to topology.edge / topology.vertex). With topology.brep_p1 alone: 366 pass, 68 deferred (GetInfiniteRegion, GetEdges, GetFaces, GetSurfaces, AddDeletedSurface wired to topology::brep::p1; the PR's m_bEditingEnabled co-port dropped for brep_p1's; the runner now sets each face surface's owner to its face as the upstream build does, without which GetSurfaces selected nothing and the 2 IsPointVisible cases failed; they now defer at surface.surface_p2 / topology.face_p3); before that merge 309 pass, 125 deferred (topology.brep_p1 67); --perturb SENSITIVE 309/309 (pre-merge); probe 3000 seeds clean (before the review round). The FFI guard accepts every int64 hint (C++: 0 none, 1 visible, other invisible) and either order of fa/fb (SmExtent1d swaps). The invisible-hint fixtures read the scene's infinite region (topology.brep_p1, wired at the merge). SmEdgeuse::EvaluateBinormal is wired to topology.edgeuse_p3 (on the scene itself, bUse3DOnly FALSE), and SmBrep::GetEdges without a filter to topology::brep::p1::edges. Of the 309 passes, 259 compare values (construction state, 1-interval classifications, single-piece ComputeEdgeVisibility, silhouette lists; status/flag records for the wire, lamina and listed early-outs) and 50 are both-errored (24 inexact_geometry, 25 rejected UB-169 guard cases, 1 rejected fixture). Classifications with 2 or more intervals: 21 generated (crossing wire-loop fixtures), all deferred: InsertPointClass splits through InsertAt / EditIntervalMin / EditIntervalMax (intersect.curve_class_p2, deferred before its merge; see the counts above); likewise the 13 multi-piece ComputeEdgeVisibility cases. The scene B-rep's SmBrep copy + Transform (topology.brep_p3) and its box (query.brep) are stubbed in Hcr::with_transform (FindAndSetInfiniteRegion is wired to topology.brep_p5 since PR #285); the runner imports the C++ scene and uses Hcr::from_scene. ComputeSilhouettes / ComputeGlobalVisibility / IsPointVisible start with topology.brep_p1 queries (only IsPointVisible is generated; it defers). SmFace::FindIfRectangularTrim is wired to topology.face_p1 (IsPointVisible's surface set; not reached while IsPointVisible defers on topology.brep_p1). CO-PORTED SmCurveClassification constructor (see intersect.curve_class_p4). Upstream defects UB-169, UB-170. hazards: ptr-hash (GetSilhouetteEdges sorted by id) Re-run after wiring stale stubs (PR #285): 434 cases, 396 pass, 0 fail, 38 deferred, max_rel_err 0. UPDATE (PR #204, topology.brep_p3): the UNPORTED(topology.brep_p3) copy constructor stub is wired to topology::brep::p3::copy_brep (no maps, bCopyAttributes and bAddAnalytics TRUE); Hcr::new and Hcr::with_transform take k: &mut Kernel, the caller's NotifyEnv and a source_key (the copy's objects under env.key_of, the source's under source_key, as api_brep_copy). The runner uses Hcr::from_scene only: report unchanged (434 cases, 394 pass, 0 fail, 40 deferred). API EXTENDED in sweep.topology_sweep (PR #222): Hcr::scene_brep_mut exposes the retained session scene for mutable topology-source borrows without cloning its identities. RE-RUN after intersect.ray_tracer wired RayTracer::add_surface_to_grid (the ray surfaces go through it): 434 cases, 396 pass, 0 fail, 38 deferred (topology.edge 37, topology.face_p2 1), max_rel_err 0; the CI report of main 03cfd79 was 386 pass, 48 deferred (surface.surface_p2 10): those 10 now run and match the C++. UPDATE 2026-10-23 (branch claude/edge-handles): blend::hcr::Geometry implements ClassGeometry::topology_entity / topology_handle for its vertex, edge and region handles, so the 37 SmEdge::GetVertex deferrals resolve (re-run after merging main with the ray-tracer change): PARTIAL blend.hcr cases=434 passed=431 failed=0 deferred=3 max_rel_err=0e0 with_values=403 no_values=28 vacuous=0 blocked_by=topology.face_p2(3)." +notes = "main CI e33985411 (2026-10-23): PARTIAL blend.hcr cases=434 passed=431 failed=0 deferred=3 blocked_by=topology.face_p2(3). EXACT SmSurfOfRevolution EXPORT (PR #304, shared graph exports carry revolution faces exactly; refreshed on main ca3bbd2b, 2026-10-21): difftest 434 cases, 393 pass, 0 failed, 41 deferred (topology.edge 30, intersect.surface_drop_lib_p3 8, surface.surface_p2 3), max_rel_err 0; 396 -> 393 pass: gen0043/gen0200/gen0245 (both sides errored before, nothing compared) now reach UNPORTED(topology.edge) SmEdge::GetVertex stubs in intersect/curve_class/p1.rs and p3.rs. Earlier: 14 fns; partial (PR #191): 434 cases (34 fixtures: hints 3/-1/big/fractional and fa >= fb / fa == fb, fixtures/blend.hcr), 394 pass, 0 fail, 40 deferred (topology.edge 27, topology.face_p3 7, surface.surface_p2 3, topology.vertex 2, intersect.surface_drop_lib_p3 1) after the maintainer merges of main with topology.brep_p1 and intersect.curve_class_p2 partial (SmCurveClassification::UpdatePointClass wired to curve_class_p2; its 35 deferrals now run: 28 pass, 7 move on to topology.edge / topology.vertex). With topology.brep_p1 alone: 366 pass, 68 deferred (GetInfiniteRegion, GetEdges, GetFaces, GetSurfaces, AddDeletedSurface wired to topology::brep::p1; the PR's m_bEditingEnabled co-port dropped for brep_p1's; the runner now sets each face surface's owner to its face as the upstream build does, without which GetSurfaces selected nothing and the 2 IsPointVisible cases failed; they now defer at surface.surface_p2 / topology.face_p3); before that merge 309 pass, 125 deferred (topology.brep_p1 67); --perturb SENSITIVE 309/309 (pre-merge); probe 3000 seeds clean (before the review round). The FFI guard accepts every int64 hint (C++: 0 none, 1 visible, other invisible) and either order of fa/fb (SmExtent1d swaps). The invisible-hint fixtures read the scene's infinite region (topology.brep_p1, wired at the merge). SmEdgeuse::EvaluateBinormal is wired to topology.edgeuse_p3 (on the scene itself, bUse3DOnly FALSE), and SmBrep::GetEdges without a filter to topology::brep::p1::edges. Of the 309 passes, 259 compare values (construction state, 1-interval classifications, single-piece ComputeEdgeVisibility, silhouette lists; status/flag records for the wire, lamina and listed early-outs) and 50 are both-errored (24 inexact_geometry, 25 rejected UB-169 guard cases, 1 rejected fixture). Classifications with 2 or more intervals: 21 generated (crossing wire-loop fixtures), all deferred: InsertPointClass splits through InsertAt / EditIntervalMin / EditIntervalMax (intersect.curve_class_p2, deferred before its merge; see the counts above); likewise the 13 multi-piece ComputeEdgeVisibility cases. The scene B-rep's SmBrep copy + Transform (topology.brep_p3) and its box (query.brep) are stubbed in Hcr::with_transform (FindAndSetInfiniteRegion is wired to topology.brep_p5 since PR #285); the runner imports the C++ scene and uses Hcr::from_scene. ComputeSilhouettes / ComputeGlobalVisibility / IsPointVisible start with topology.brep_p1 queries (only IsPointVisible is generated; it defers). SmFace::FindIfRectangularTrim is wired to topology.face_p1 (IsPointVisible's surface set; not reached while IsPointVisible defers on topology.brep_p1). CO-PORTED SmCurveClassification constructor (see intersect.curve_class_p4). Upstream defects UB-169, UB-170. hazards: ptr-hash (GetSilhouetteEdges sorted by id) Re-run after wiring stale stubs (PR #285): 434 cases, 396 pass, 0 fail, 38 deferred, max_rel_err 0. Main since: 14 fns; partial (PR #191): 434 cases (34 fixtures: hints 3/-1/big/fractional and fa >= fb / fa == fb, fixtures/blend.hcr), 394 pass, 0 fail, 40 deferred (topology.edge 27, topology.face_p3 7, surface.surface_p2 3, topology.vertex 2, intersect.surface_drop_lib_p3 1) after the maintainer merges of main with topology.brep_p1 and intersect.curve_class_p2 partial (SmCurveClassification::UpdatePointClass wired to curve_class_p2; its 35 deferrals now run: 28 pass, 7 move on to topology.edge / topology.vertex). With topology.brep_p1 alone: 366 pass, 68 deferred (GetInfiniteRegion, GetEdges, GetFaces, GetSurfaces, AddDeletedSurface wired to topology::brep::p1; the PR's m_bEditingEnabled co-port dropped for brep_p1's; the runner now sets each face surface's owner to its face as the upstream build does, without which GetSurfaces selected nothing and the 2 IsPointVisible cases failed; they now defer at surface.surface_p2 / topology.face_p3); before that merge 309 pass, 125 deferred (topology.brep_p1 67); --perturb SENSITIVE 309/309 (pre-merge); probe 3000 seeds clean (before the review round). The FFI guard accepts every int64 hint (C++: 0 none, 1 visible, other invisible) and either order of fa/fb (SmExtent1d swaps). The invisible-hint fixtures read the scene's infinite region (topology.brep_p1, wired at the merge). SmEdgeuse::EvaluateBinormal is wired to topology.edgeuse_p3 (on the scene itself, bUse3DOnly FALSE), and SmBrep::GetEdges without a filter to topology::brep::p1::edges. Of the 309 passes, 259 compare values (construction state, 1-interval classifications, single-piece ComputeEdgeVisibility, silhouette lists; status/flag records for the wire, lamina and listed early-outs) and 50 are both-errored (24 inexact_geometry, 25 rejected UB-169 guard cases, 1 rejected fixture). Classifications with 2 or more intervals: 21 generated (crossing wire-loop fixtures), all deferred: InsertPointClass splits through InsertAt / EditIntervalMin / EditIntervalMax (intersect.curve_class_p2, deferred before its merge; see the counts above); likewise the 13 multi-piece ComputeEdgeVisibility cases. The scene B-rep's SmBrep copy + Transform (topology.brep_p3) and its box (query.brep) are stubbed in Hcr::with_transform (FindAndSetInfiniteRegion is wired to topology.brep_p5 since PR #285); the runner imports the C++ scene and uses Hcr::from_scene. ComputeSilhouettes / ComputeGlobalVisibility / IsPointVisible start with topology.brep_p1 queries (only IsPointVisible is generated; it defers). SmFace::FindIfRectangularTrim is wired to topology.face_p1 (IsPointVisible's surface set; not reached while IsPointVisible defers on topology.brep_p1). CO-PORTED SmCurveClassification constructor (see intersect.curve_class_p4). Upstream defects UB-169, UB-170. hazards: ptr-hash (GetSilhouetteEdges sorted by id) Re-run after wiring stale stubs (PR #285): 434 cases, 396 pass, 0 fail, 38 deferred, max_rel_err 0. UPDATE (PR #204, topology.brep_p3): the UNPORTED(topology.brep_p3) copy constructor stub is wired to topology::brep::p3::copy_brep (no maps, bCopyAttributes and bAddAnalytics TRUE); Hcr::new and Hcr::with_transform take k: &mut Kernel, the caller's NotifyEnv and a source_key (the copy's objects under env.key_of, the source's under source_key, as api_brep_copy). The runner uses Hcr::from_scene only: report unchanged (434 cases, 394 pass, 0 fail, 40 deferred). API EXTENDED in sweep.topology_sweep (PR #222): Hcr::scene_brep_mut exposes the retained session scene for mutable topology-source borrows without cloning its identities. RE-RUN after intersect.ray_tracer wired RayTracer::add_surface_to_grid (the ray surfaces go through it): 434 cases, 396 pass, 0 fail, 38 deferred (topology.edge 37, topology.face_p2 1), max_rel_err 0; the CI report of main 03cfd79 was 386 pass, 48 deferred (surface.surface_p2 10): those 10 now run and match the C++. UPDATE 2026-10-23 (branch claude/edge-handles): blend::hcr::Geometry implements ClassGeometry::topology_entity / topology_handle for its vertex, edge and region handles, so the 37 SmEdge::GetVertex deferrals resolve (re-run after merging main with the ray-tracer change): PARTIAL blend.hcr cases=434 passed=431 failed=0 deferred=3 max_rel_err=0e0 with_values=403 no_values=28 vacuous=0 blocked_by=topology.face_p2(3). UPDATE (PR #679): Hcr::with_transform transforms the scene copy through topology::brep::p5::transform_with_notifications under the scene's notification environment. The runner builds its scenes with Hcr::from_scene, so its counts are unchanged (434 cases, 431 pass, 3 deferred)." diff --git a/plan/units/boolean.merge_p1.toml b/plan/units/boolean.merge_p1.toml index 863b665efc..c3a6648ce6 100644 --- a/plan/units/boolean.merge_p1.toml +++ b/plan/units/boolean.merge_p1.toml @@ -9,4 +9,4 @@ tests = ["prog_test:my_test_surface_closest_point", "prog_test:my_test_drop_poin status = "partial" claimed_by = "" claimed_at = "" -notes = "main CI e33985411 (2026-10-23): PARTIAL boolean.merge_p1 cases=248 passed=241 failed=0 deferred=7 blocked_by=boolean.topology_intersector_p3(4),topology.brep_p5(2),topology.face_p2(1). CO-PORTED in boolean.api_brep (PR #81): SmMergeOptions (fields, ctor, defaults, copy ctor, getters/setters) -> solidcore::boolean::merge::p1::MergeOptions (crates/solidcore/src/boolean/merge/p1.rs; extend it, keep boolean/merge.rs as the split root); 24 fns; largest: SmMerge::BooleanPostProcess, SmMerge::FixFoundFaceCoincidence, sm_FindBrepRegionOfOtherFace, SmMerge::PropagateOtherSrcRegionAttribs; cycle back-edges (call into later units; stub or co-port): blend.fillet_executive_p1(1); unit-SCC #22 (220 units); 317 upstream test entry points reach this unit; PR #163: merge control, face coincidence fast path, mapped vertex copy with topology/attribute notification, cleanup and region-attribute propagation; cross-B-rep radial classification keeps operand ids separate. Difftest 248 cases (152 fixtures + 96 generated), 175 passed, 73 deferred, 0 failed, max_rel_err=0, no absolute floors. Deferred: query.brep(14), topology.brep_p1(33), topology.brep_p9(3), topology.edgeuse_p2(20), topology.face_p2(3). DeleteEdge/DeleteVertex/DeleteTopologicalVertex remain tagged back-edges; scoped translation waits for query.brep bounding boxes; intersector construction is supplied by its owner. Wired to topology.shell (PR #154): SmRegion::IsVoid in sm_EdgeBordersSolid, SmShell::IsWireShell/IsFaceuseShell in BooleanPostProcess, region propagation takes a RegionLookup for the source regions' IsVoid. The wire/face shell combine block of BooleanPostProcess is wired end to end (GetCommonVertices = the B-rep vertices with an other mate in the shared mates, as TopologyIntersector::common_vertices after PR #146; then ignored-status shell::transfer_children and shell::region_remove_shell; boolean_post_process takes &mut Kernel for the mark lock) and is unreachable today because brep_shells / brep_vertices are UNPORTED(topology.brep_p1). No UNPORTED(boolean.merge_p1) callers on main. Review fixes: SM_USE_OLDTOL vertex tolerance growth restored; face tolerance growth calls a topology.face_p3 stub with old tolerance + dist_between. GetEdges/GetShells use topology.brep_p1 stubs (including count-only queries); no arena-order stand-ins. Reuses public topology_traverser::face_edges with per-loop uniqueness, stale-vertex sanity bound and unconditional merged-domain restore. Mapped/new vertex notification identities derive from ids; new MakeShellVertex path calls the intersector Relate (boolean::topology_intersector::p1::Relations::relate_vertices, wired in PR #146; the other operand is now mutable). merge_p5 now uses shared mutable bidirectional mates and the real MergeVertex adapter. 16 directed unequal-tolerance cases pass. Independent-operand radial queries and mated face-region cases are covered; full step-off numerics remain deferred, two cross-operand wire-sector cases compare real topology. Relate wired, not certified (PR #146): Relations::relate_vertices follows MakeShellVertex. PR #183 back-edge: MergeVertex calls topology::brep::p9::make_shell_vertex with its existing arena ids and source point; VertexCopy destination identities cover vertices and vertexuses, preserving caller-owned notification state. [topology.edgeuse_p3 merged (PR #117): the step-offs of RadialSectorClassify may create UV trim curves, so radial_sector_classify_between / find_radial_sector_between take `&mut Brep` and the other operand as `Option<&mut Brep>` (`None` = the same B-rep, replacing the pointer-equality test), and find_brep_region_of_other_face takes both operands `&mut`. 248 cases, 177 pass, 0 failed, 71 deferred (topology.brep_p1 33, intersect.surface_drop_lib_p3 18, query.brep 14, topology.brep_p9 3, topology.face_p2 3); the 20 topology.edgeuse_p2 step-off deferrals now run, 2 pass and 18 defer further on SmSurface::DropCurve] UPDATE (PR #145, topology.brep_p1): GetEdges/GetShells call topology::brep::p1 (brep_shells and boolean_post_process take &mut Kernel: upstream GetShells takes SmNewMarkAndLock on the context); GetShells returns the empty list on a B-rep without regions and still stops at SmRegion::GetShells otherwise, so the 33 topology.brep_p1 cases continue to the next stub (topology.shell 26, boolean.topology_intersector_p3 4, topology.face_p2 3): 248 cases, 153 pass, 0 failed, 95 deferred (topology.shell 48, topology.edgeuse_p2 20, query.brep 14, topology.face_p2 6, boolean.topology_intersector_p3 4, topology.brep_p9 3). After topology.shell (#154) merged and topology.brep_p1's GetShells is wired to it (SmRegion::GetShells under the mark lock): 248 cases, 201 pass, 0 failed, 47 deferred (topology.edgeuse_p2 20, query.brep 14, topology.face_p2 6, boolean.topology_intersector_p3 4, topology.brep_p9 3). Rerun with PR #183 (topology.brep_p9 wired, 7da151aa): 248 cases, 203 pass, 0 failed, 45 deferred (intersect.surface_drop_lib_p3 18, query.brep 14, topology.face_p2 6, boolean.topology_intersector_p3 4, topology.brep_p2 3); the 3 former topology.brep_p9 deferrals reach MakeShellVertexTopology (topology.brep_p2), and 2 earlier topology.edgeuse_p2 step-offs now pass. Re-run in PR #149 (review 5578349117 round, merged with main through fb58ea76; topology.brep_p2's UNPORTED stubs wired): 248 cases, 206 pass, 0 fail, 42 deferred (intersect.surface_drop_lib_p3 18, query.brep 14, topology.face_p2 6, boolean.topology_intersector_p3 4); the 3 MakeShellVertexTopology deferrals pass. graph() now numbers every record in the C++ shim's walk order (Canon::of: regions, then GetEdges, then the per-kind stages), and op 3 reports the copied vertex by that number and reads its flags and attribute through its own key (plan/findings.md, topology.brep_p2). UPDATE (PR #204, topology.brep_p3; overseer decision): the shared difftest graph() / Canon::of (this unit's runner, used by others) number, compare and count only the records the walk reaches from the B-rep's roots, as the C++ export walk emits; records upstream deletes stay in the Rust arenas unlinked (topology.brep_p2's deviation) and were compared before. The unreached count is a report diagnostic (Output::diagnostics, unreached_records, never compared). Unit test graph_compares_only_reachable_records (an unlinked record leaves graph() unchanged, a still-linked extra vertex use changes it). See plan/findings.md Owner zone-tol cache (surface/owner-zone-tol): 248 cases, 212 pass, 0 fail, 36 deferred." +notes = "main CI e33985411 (2026-10-23): PARTIAL boolean.merge_p1 cases=248 passed=241 failed=0 deferred=7 blocked_by=boolean.topology_intersector_p3(4),topology.brep_p5(2),topology.face_p2(1). CO-PORTED in boolean.api_brep (PR #81): SmMergeOptions (fields, ctor, defaults, copy ctor, getters/setters) -> solidcore::boolean::merge::p1::MergeOptions (crates/solidcore/src/boolean/merge/p1.rs; extend it, keep boolean/merge.rs as the split root); 24 fns; largest: SmMerge::BooleanPostProcess, SmMerge::FixFoundFaceCoincidence, sm_FindBrepRegionOfOtherFace, SmMerge::PropagateOtherSrcRegionAttribs; cycle back-edges (call into later units; stub or co-port): blend.fillet_executive_p1(1); unit-SCC #22 (220 units); 317 upstream test entry points reach this unit; PR #163: merge control, face coincidence fast path, mapped vertex copy with topology/attribute notification, cleanup and region-attribute propagation; cross-B-rep radial classification keeps operand ids separate. Difftest 248 cases (152 fixtures + 96 generated), 175 passed, 73 deferred, 0 failed, max_rel_err=0, no absolute floors. Deferred: query.brep(14), topology.brep_p1(33), topology.brep_p9(3), topology.edgeuse_p2(20), topology.face_p2(3). DeleteEdge/DeleteVertex/DeleteTopologicalVertex remain tagged back-edges; scoped translation waits for query.brep bounding boxes; intersector construction is supplied by its owner. Wired to topology.shell (PR #154): SmRegion::IsVoid in sm_EdgeBordersSolid, SmShell::IsWireShell/IsFaceuseShell in BooleanPostProcess, region propagation takes a RegionLookup for the source regions' IsVoid. The wire/face shell combine block of BooleanPostProcess is wired end to end (GetCommonVertices = the B-rep vertices with an other mate in the shared mates, as TopologyIntersector::common_vertices after PR #146; then ignored-status shell::transfer_children and shell::region_remove_shell; boolean_post_process takes &mut Kernel for the mark lock) and is unreachable today because brep_shells / brep_vertices are UNPORTED(topology.brep_p1). No UNPORTED(boolean.merge_p1) callers on main. Review fixes: SM_USE_OLDTOL vertex tolerance growth restored; face tolerance growth calls a topology.face_p3 stub with old tolerance + dist_between. GetEdges/GetShells use topology.brep_p1 stubs (including count-only queries); no arena-order stand-ins. Reuses public topology_traverser::face_edges with per-loop uniqueness, stale-vertex sanity bound and unconditional merged-domain restore. Mapped/new vertex notification identities derive from ids; new MakeShellVertex path calls the intersector Relate (boolean::topology_intersector::p1::Relations::relate_vertices, wired in PR #146; the other operand is now mutable). merge_p5 now uses shared mutable bidirectional mates and the real MergeVertex adapter. 16 directed unequal-tolerance cases pass. Independent-operand radial queries and mated face-region cases are covered; full step-off numerics remain deferred, two cross-operand wire-sector cases compare real topology. Relate wired, not certified (PR #146): Relations::relate_vertices follows MakeShellVertex. PR #183 back-edge: MergeVertex calls topology::brep::p9::make_shell_vertex with its existing arena ids and source point; VertexCopy destination identities cover vertices and vertexuses, preserving caller-owned notification state. [topology.edgeuse_p3 merged (PR #117): the step-offs of RadialSectorClassify may create UV trim curves, so radial_sector_classify_between / find_radial_sector_between take `&mut Brep` and the other operand as `Option<&mut Brep>` (`None` = the same B-rep, replacing the pointer-equality test), and find_brep_region_of_other_face takes both operands `&mut`. 248 cases, 177 pass, 0 failed, 71 deferred (topology.brep_p1 33, intersect.surface_drop_lib_p3 18, query.brep 14, topology.brep_p9 3, topology.face_p2 3); the 20 topology.edgeuse_p2 step-off deferrals now run, 2 pass and 18 defer further on SmSurface::DropCurve] UPDATE (PR #145, topology.brep_p1): GetEdges/GetShells call topology::brep::p1 (brep_shells and boolean_post_process take &mut Kernel: upstream GetShells takes SmNewMarkAndLock on the context); GetShells returns the empty list on a B-rep without regions and still stops at SmRegion::GetShells otherwise, so the 33 topology.brep_p1 cases continue to the next stub (topology.shell 26, boolean.topology_intersector_p3 4, topology.face_p2 3): 248 cases, 153 pass, 0 failed, 95 deferred (topology.shell 48, topology.edgeuse_p2 20, query.brep 14, topology.face_p2 6, boolean.topology_intersector_p3 4, topology.brep_p9 3). After topology.shell (#154) merged and topology.brep_p1's GetShells is wired to it (SmRegion::GetShells under the mark lock): 248 cases, 201 pass, 0 failed, 47 deferred (topology.edgeuse_p2 20, query.brep 14, topology.face_p2 6, boolean.topology_intersector_p3 4, topology.brep_p9 3). Rerun with PR #183 (topology.brep_p9 wired, 7da151aa): 248 cases, 203 pass, 0 failed, 45 deferred (intersect.surface_drop_lib_p3 18, query.brep 14, topology.face_p2 6, boolean.topology_intersector_p3 4, topology.brep_p2 3); the 3 former topology.brep_p9 deferrals reach MakeShellVertexTopology (topology.brep_p2), and 2 earlier topology.edgeuse_p2 step-offs now pass. Re-run in PR #149 (review 5578349117 round, merged with main through fb58ea76; topology.brep_p2's UNPORTED stubs wired): 248 cases, 206 pass, 0 fail, 42 deferred (intersect.surface_drop_lib_p3 18, query.brep 14, topology.face_p2 6, boolean.topology_intersector_p3 4); the 3 MakeShellVertexTopology deferrals pass. graph() now numbers every record in the C++ shim's walk order (Canon::of: regions, then GetEdges, then the per-kind stages), and op 3 reports the copied vertex by that number and reads its flags and attribute through its own key (plan/findings.md, topology.brep_p2). UPDATE (PR #204, topology.brep_p3; overseer decision): the shared difftest graph() / Canon::of (this unit's runner, used by others) number, compare and count only the records the walk reaches from the B-rep's roots, as the C++ export walk emits; records upstream deletes stay in the Rust arenas unlinked (topology.brep_p2's deviation) and were compared before. The unreached count is a report diagnostic (Output::diagnostics, unreached_records, never compared). Unit test graph_compares_only_reachable_records (an unlinked record leaves graph() unchanged, a still-linked extra vertex use changes it). See plan/findings.md Owner zone-tol cache (surface/owner-zone-tol): 248 cases, 212 pass, 0 fail, 36 deferred. UPDATE (PR #679): the runner's SmTemporaryBrepTransform probe applies and restores the shift through topology::brep::p5::transform. difftest 248 cases, 243 pass, 0 failed, 5 deferred (MergeCoincidentSurfaces 4, topology.face_p2 1); the 2 SmBrep::Transform deferrals pass." diff --git a/plan/units/boolean.topology_intersector_p2.toml b/plan/units/boolean.topology_intersector_p2.toml index 0cc11af3e4..e2c5cc324b 100644 --- a/plan/units/boolean.topology_intersector_p2.toml +++ b/plan/units/boolean.topology_intersector_p2.toml @@ -9,4 +9,4 @@ tests = ["prog_test:my_test_analytic_tsurf_creation", "prog_test:my_test_get_loo status = "partial" claimed_by = "" claimed_at = "" -notes = "partial (PR #203, on main's #146): difftest 905 cases (9 upstream-derived fixtures + 32 edge cases built in code + 800 generated two-B-rep scripts on part 1's script runner + 16 CloseIntersectionLoop edge cases + 48 seeded CloseIntersectionLoop scripts), 728 pass bit-identical (max_rel_err 0), 0 fail, 177 deferred (topology.face_p2 75: SmFace::GetTolerance (2 of them iir_negative_tol/iir_tiny_tol, formerly topology.brep_p2 SmBrep::GetMinEdgeBBoxSize before #149; 3 of them formerly topology.face_p4 GetUpwardLoopuses before #239) and query.brep 47: SmBrep::CalculateBoundingBox (41 in IntersectTopology/IntersectInsertRelate after SmBrep::GetFaces (wired to topology.brep_p1), 6 in CloseIntersectionLoop's BrepCurveSolve); topology.face_p4 31: SmFace::GetEdges/GetVertices/GetBrep (part 1's IntersectorBrep stubs, unwired since #239 landed); topology.face_p3 12: FaceIntersect; topology.brep_p3 8 / topology.brep_p1 4: SqueezeEdge / (since #149 wired SmBrep::Notify) m_pVertexListHead inside CombineCoincidentVertices (wired to topology.brep_p5); the 3 former m_bEditingEnabled deferrals pass since #285), 2 cases_both_errored (scripts cut at op 0), so 726 real passes; FUZZ-PASS 2000 (crashes/panics/timeouts 0). What passes cover (per op occurrence, non-deferred cases): RemoveRelationship 29 success / 35 SM_ERR; CheckForSqueezeVertices 75 success without a conflicting mate (empty lists) / 26 SM_ERR (existing relationship better), a conflict that combines defers inside CombineCoincidentVertices; DeleteFaces 68 success, all with an empty face list; FixGaps 119 success without a gap pair / 14 SM_ERR_NULL_POINTER (gap pair with an unmated vertex), a pair that combines defers inside CombineCoincidentVertices; SetRelationships 39 success on managers without definitions; FixupClassMates 56 success / 17 SM_ERR (sizes differ); FixupSplitVertexPair 57 success, 6 of them removing the interval; EdgeEdgeIntersect 136 success (9 more since main's topology.brep_p9), none with a non-empty first split list; EdgeFaceCoincidence 128 success, none imprinting; CloseIntersectionLoop 28 success without new topology / 19 success relating an edge pair (the single-common-edge drop) / 11 SM_ERR_NULL_POINTER (null or unmated vertex, no maps), the coincidence path defers (BrepCurveSolve, then MergeCurveClasses): no pass reaches BrepCurveSolve, IIREdgeBrep or makes new topology; IntersectTopology 1 success (the smTopoTest pair marked as not intersecting), IntersectInsertRelate none (SmFace::GetTolerance / SmBrep::CalculateBoundingBox). RegisterExistingSSI is now run by the port in part 1's script (op 16; a non-B-spline 3D curve, UB-184, is never registered). Also UB-175 [PSIRT] (~SmProtoTopologyManager frees proto edges before the vertices that still list them; the shim runs a verbatim IntersectInsertRelate copy that unlinks them first and answers -2, the port returns InvalidInput) and HANG-25 (IntersectInsertRelate at tolerance 0, refused by the FFI); op 47 also has a measured time-budget guard |approx| <= 0.1 (defined input, not UB; plan/findings.md). UNPORTED stubs added: topology.brep_p3 (DeleteFace), topology.brep_p4 (DeleteEdge/DeleteVertex), query.face_p1 (SmFace::Point3DClassify); part 1's stubs reused: topology.face_p4 (GetEdges/GetVertices/GetBrep), topology.face_p3 (FaceIntersect); CombineCoincidentVertices wired to topology.brep_p5 through IntersectorBrepP2::brep_combine_coincident_vertices (so check_for_squeeze_vertices and fix_gaps take &mut Kernel; follow-up to #203); MakeEdgeInFace goes through part 1's brep_p5 backend; GlueEdgesGeneral (topology.brep_p7, merged in #155) goes through IntersectorBrep::brep_glue_edges_general, so edge_face_coincidence takes &mut Kernel. SmBrep::GetFaces wired to topology.brep_p1 through IntersectorBrepP2::brep_faces (follow-up to #203). Back-edge wired: UNPORTED(boolean.topology_intersector_p2) RemoveRelationship in topology::proto_topology::p3 (GlueCoincidentBrepVertices) through the Mates trait (boolean::topology_intersector::p2::MapMates). CO-PORTED in boolean.topology_intersector_p2 (PR #281): SmTopologyIntersector::CloseIntersectionLoop (SmTopologyIntersector.cpp:6108-6354, called from SmMerge.cpp:2958) and its only callee SmTopologyIntersector::IIREdgeBrep (:2836-2971), both listed by no unit in harness/callgraph/units.json -> boolean::topology_intersector::p2::TopologyIntersector::{close_intersection_loop, iir_edge_brep}, wired into boolean.merge_p4's KernelManifoldIntersector in the same PR; IIREdgeBrep's MergeCurveClasses stays UNPORTED(boolean.topology_intersector_p3) (ported as TopologyIntersector::merge_curve_classes in draft #211). The two constructors were CO-PORTED in boolean.topology_intersector_p1 (PR #146) -> boolean::topology_intersector::p1::TopologyIntersector::{with_context, new}. Downstream: boolean.topology_intersector_p3 (#211) builds on this branch. 12 fns + 2 co-ported; largest: IntersectTopology, EdgeFaceCoincidence, FixupSplitVertexPair, IntersectInsertRelate; hazards: ptr-hash (none in this part's outputs), tbb (SM_USE_TBB blocks not built) Re-run in PR #149 (review 5578349117 round, merged with main through fb58ea76; topology.brep_p2's UNPORTED stubs wired): 841 cases, 666 pass, 0 fail, 175 deferred; its 2 SmBrep::Notify deferrals relabel to topology.brep_p1. UPDATE (PR #204, topology.brep_p3): the UNPORTED(topology.brep_p3) brep_delete_face stub is removed; DeleteFaces calls IntersectorBrep::brep_delete_face (boolean.topology_intersector_p1's hook, SmBrep::DeleteFace with the defaults FALSE, TRUE, NULL, NULL that this call passes), so delete_faces takes k: &mut Kernel. Still behind SmFace::GetEdges (topology.face_p4): report unchanged (841 cases, 666 pass, 0 fail, 175 deferred). Re-run in PR #204 (07c89fab, main through e5f3e441): 841 cases, 667 pass, 0 fail, 174 deferred; case by case the 8 SmBrep::SqueezeEdge (topology.brep_p3) deferrals relabel to topology.brep_p1 SmBrep::UpdateVertexEdgeIntervals (the squeeze is wired), no outcome change Owner zone-tol cache (surface/owner-zone-tol): 910 cases, 736 pass, 0 fail, 174 deferred. FaceIntersect wired (2026-10-23): face_face_intersect_curves runs through IntersectorBrep::face_intersect; 910 cases, 775 passes (was 773), 0 failed, 135 deferred (topology.face_p3 85 -> 0; now topology.face_p2 68, topology.edge 40, topology.brep_p1 13, query.face_p2 6, topology.vertex 6, query.face_p1 1, topology.edgeuse_p3 1)." +notes = "partial (PR #203, on main's #146): difftest 905 cases (9 upstream-derived fixtures + 32 edge cases built in code + 800 generated two-B-rep scripts on part 1's script runner + 16 CloseIntersectionLoop edge cases + 48 seeded CloseIntersectionLoop scripts), 728 pass bit-identical (max_rel_err 0), 0 fail, 177 deferred (topology.face_p2 75: SmFace::GetTolerance (2 of them iir_negative_tol/iir_tiny_tol, formerly topology.brep_p2 SmBrep::GetMinEdgeBBoxSize before #149; 3 of them formerly topology.face_p4 GetUpwardLoopuses before #239) and query.brep 47: SmBrep::CalculateBoundingBox (41 in IntersectTopology/IntersectInsertRelate after SmBrep::GetFaces (wired to topology.brep_p1), 6 in CloseIntersectionLoop's BrepCurveSolve); topology.face_p4 31: SmFace::GetEdges/GetVertices/GetBrep (part 1's IntersectorBrep stubs, unwired since #239 landed); topology.face_p3 12: FaceIntersect; topology.brep_p3 8 / topology.brep_p1 4: SqueezeEdge / (since #149 wired SmBrep::Notify) m_pVertexListHead inside CombineCoincidentVertices (wired to topology.brep_p5); the 3 former m_bEditingEnabled deferrals pass since #285), 2 cases_both_errored (scripts cut at op 0), so 726 real passes; FUZZ-PASS 2000 (crashes/panics/timeouts 0). What passes cover (per op occurrence, non-deferred cases): RemoveRelationship 29 success / 35 SM_ERR; CheckForSqueezeVertices 75 success without a conflicting mate (empty lists) / 26 SM_ERR (existing relationship better), a conflict that combines defers inside CombineCoincidentVertices; DeleteFaces 68 success, all with an empty face list; FixGaps 119 success without a gap pair / 14 SM_ERR_NULL_POINTER (gap pair with an unmated vertex), a pair that combines defers inside CombineCoincidentVertices; SetRelationships 39 success on managers without definitions; FixupClassMates 56 success / 17 SM_ERR (sizes differ); FixupSplitVertexPair 57 success, 6 of them removing the interval; EdgeEdgeIntersect 136 success (9 more since main's topology.brep_p9), none with a non-empty first split list; EdgeFaceCoincidence 128 success, none imprinting; CloseIntersectionLoop 28 success without new topology / 19 success relating an edge pair (the single-common-edge drop) / 11 SM_ERR_NULL_POINTER (null or unmated vertex, no maps), the coincidence path defers (BrepCurveSolve, then MergeCurveClasses): no pass reaches BrepCurveSolve, IIREdgeBrep or makes new topology; IntersectTopology 1 success (the smTopoTest pair marked as not intersecting), IntersectInsertRelate none (SmFace::GetTolerance / SmBrep::CalculateBoundingBox). RegisterExistingSSI is now run by the port in part 1's script (op 16; a non-B-spline 3D curve, UB-184, is never registered). Also UB-175 [PSIRT] (~SmProtoTopologyManager frees proto edges before the vertices that still list them; the shim runs a verbatim IntersectInsertRelate copy that unlinks them first and answers -2, the port returns InvalidInput) and HANG-25 (IntersectInsertRelate at tolerance 0, refused by the FFI); op 47 also has a measured time-budget guard |approx| <= 0.1 (defined input, not UB; plan/findings.md). UNPORTED stubs added: topology.brep_p3 (DeleteFace), topology.brep_p4 (DeleteEdge/DeleteVertex), query.face_p1 (SmFace::Point3DClassify); part 1's stubs reused: topology.face_p4 (GetEdges/GetVertices/GetBrep), topology.face_p3 (FaceIntersect); CombineCoincidentVertices wired to topology.brep_p5 through IntersectorBrepP2::brep_combine_coincident_vertices (so check_for_squeeze_vertices and fix_gaps take &mut Kernel; follow-up to #203); MakeEdgeInFace goes through part 1's brep_p5 backend; GlueEdgesGeneral (topology.brep_p7, merged in #155) goes through IntersectorBrep::brep_glue_edges_general, so edge_face_coincidence takes &mut Kernel. SmBrep::GetFaces wired to topology.brep_p1 through IntersectorBrepP2::brep_faces (follow-up to #203). Back-edge wired: UNPORTED(boolean.topology_intersector_p2) RemoveRelationship in topology::proto_topology::p3 (GlueCoincidentBrepVertices) through the Mates trait (boolean::topology_intersector::p2::MapMates). CO-PORTED in boolean.topology_intersector_p2 (PR #281): SmTopologyIntersector::CloseIntersectionLoop (SmTopologyIntersector.cpp:6108-6354, called from SmMerge.cpp:2958) and its only callee SmTopologyIntersector::IIREdgeBrep (:2836-2971), both listed by no unit in harness/callgraph/units.json -> boolean::topology_intersector::p2::TopologyIntersector::{close_intersection_loop, iir_edge_brep}, wired into boolean.merge_p4's KernelManifoldIntersector in the same PR; IIREdgeBrep's MergeCurveClasses stays UNPORTED(boolean.topology_intersector_p3) (ported as TopologyIntersector::merge_curve_classes in draft #211). The two constructors were CO-PORTED in boolean.topology_intersector_p1 (PR #146) -> boolean::topology_intersector::p1::TopologyIntersector::{with_context, new}. Downstream: boolean.topology_intersector_p3 (#211) builds on this branch. 12 fns + 2 co-ported; largest: IntersectTopology, EdgeFaceCoincidence, FixupSplitVertexPair, IntersectInsertRelate; hazards: ptr-hash (none in this part's outputs), tbb (SM_USE_TBB blocks not built) Re-run in PR #149 (review 5578349117 round, merged with main through fb58ea76; topology.brep_p2's UNPORTED stubs wired): 841 cases, 666 pass, 0 fail, 175 deferred; its 2 SmBrep::Notify deferrals relabel to topology.brep_p1. UPDATE (PR #204, topology.brep_p3): the UNPORTED(topology.brep_p3) brep_delete_face stub is removed; DeleteFaces calls IntersectorBrep::brep_delete_face (boolean.topology_intersector_p1's hook, SmBrep::DeleteFace with the defaults FALSE, TRUE, NULL, NULL that this call passes), so delete_faces takes k: &mut Kernel. Still behind SmFace::GetEdges (topology.face_p4): report unchanged (841 cases, 666 pass, 0 fail, 175 deferred). Re-run in PR #204 (07c89fab, main through e5f3e441): 841 cases, 667 pass, 0 fail, 174 deferred; case by case the 8 SmBrep::SqueezeEdge (topology.brep_p3) deferrals relabel to topology.brep_p1 SmBrep::UpdateVertexEdgeIntervals (the squeeze is wired), no outcome change Owner zone-tol cache (surface/owner-zone-tol): 910 cases, 736 pass, 0 fail, 174 deferred. FaceIntersect wired (2026-10-23): face_face_intersect_curves runs through IntersectorBrep::face_intersect; 910 cases, 775 passes (was 773), 0 failed, 135 deferred (topology.face_p3 85 -> 0; now topology.face_p2 68, topology.edge 40, topology.brep_p1 13, query.face_p2 6, topology.vertex 6, query.face_p1 1, topology.edgeuse_p3 1). UPDATE (PR #679, UpdateVertexEdgeIntervals wired in CombineCoincidentVertices): 910 cases, 788 pass, 0 failed, 122 deferred; the 13 UpdateVertexEdgeIntervals deferrals pass." diff --git a/plan/units/boolean.topology_intersector_p3.toml b/plan/units/boolean.topology_intersector_p3.toml index a9c31412b6..8a2d1936a5 100644 --- a/plan/units/boolean.topology_intersector_p3.toml +++ b/plan/units/boolean.topology_intersector_p3.toml @@ -9,4 +9,4 @@ tests = ["prog_test:my_test_bbu", "SM_API_test:TestSmSurfaceSurfaceFillet", "SM_ status = "partial" claimed_by = "" claimed_at = "" -notes = "PARTIAL (PR #211): MergeCurveClasses, MergeCurveOnSurfaces and MergeCoincidentSurfaces ported in full. query.face_p1 and topology.face_p4 (partial) are wired: SmFace::Point3DClassify, CalculateBoundingBox (bTight FALSE), GetEdges and GetUpwardLoopuses are IntersectorBrepP3 hooks the adapter implements with query::face::p1 / topology::face::p4; dCurveTolerance reaches the point classifications as SmZoneTol3d (zone_tol3d). One stub, for a call into a unit still todo: topology.brep_p4 DeleteEdge (#441). On main (#146, #203, #85, #137, and the curve_class topology wiring #347 / #365 merged); CheckForSqueezeVertices combines coincident vertices (#274), so IntersectorBrepP3 extends IntersectorBrepP2 and the merges take &mut Kernel. ClassGeometry::topology_handle_in lets the two-B-rep adapter name handles in main's curve_class helpers. difftest (linux/arm64 container): 271 cases (256 generated + 15 inline), 166 passed with values, 0 failed, 105 deferred (intersect.curve_class_p6 57, topology.vertex 27, intersect.surface_drop_curve 15, topology.brep_p3 6); --perturb SENSITIVE 166/166; --fuzz 2000 FUZZ-PASS (0 differs). Review r1 (tangency-monitor): the shim and runner refuse an SM_PC_UNKNOWN start or end of a face- or region-mid interval (MergeInterval leaves it without object; SmCurveClass.cpp:17659/:17672 dereference it); MergeCurveOnSurfaces takes UV trim curves (K_TRIM_CURVE, 3 inline cases, deferring at intersect.point_class's SmVertex::GetTolerance stub in MergeIntoObject); MergeCoincidentSurfaces' 3D edge-curve copy gets the edge curve's owner as upstream. The runner reuses the p1 runner's two-B-rep adapter and reorders imported vertex-use lists to upstream's list order (see plan/findings.md). Back-edges: blend.fillet_geom_p4's MergeCurveOnSurfaces stub retagged blend.fillet_executive_p1 (it runs on the executive's intersector); boolean.merge_p1's MergeCoincidentSurfaces stays UNPORTED(boolean.topology_intersector_p3) pending an IntersectorBrepP3 implementation for merge p4's ArenaPair (follow-up). 3 fns; largest: SmTopologyIntersector::MergeCurveClasses, SmTopologyIntersector::MergeCoincidentSurfaces, SmTopologyIntersector::MergeCurveOnSurfaces; unit-SCC #22 (220 units); 110 upstream test entry points reach this unit" +notes = "PARTIAL (PR #211): MergeCurveClasses, MergeCurveOnSurfaces and MergeCoincidentSurfaces ported in full. query.face_p1 and topology.face_p4 (partial) are wired: SmFace::Point3DClassify, CalculateBoundingBox (bTight FALSE), GetEdges and GetUpwardLoopuses are IntersectorBrepP3 hooks the adapter implements with query::face::p1 / topology::face::p4; dCurveTolerance reaches the point classifications as SmZoneTol3d (zone_tol3d). One stub, for a call into a unit still todo: topology.brep_p4 DeleteEdge (#441). On main (#146, #203, #85, #137, and the curve_class topology wiring #347 / #365 merged); CheckForSqueezeVertices combines coincident vertices (#274), so IntersectorBrepP3 extends IntersectorBrepP2 and the merges take &mut Kernel. ClassGeometry::topology_handle_in lets the two-B-rep adapter name handles in main's curve_class helpers. difftest (linux/arm64 container): 271 cases (256 generated + 15 inline), 166 passed with values, 0 failed, 105 deferred (intersect.curve_class_p6 57, topology.vertex 27, intersect.surface_drop_curve 15, topology.brep_p3 6); --perturb SENSITIVE 166/166; --fuzz 2000 FUZZ-PASS (0 differs). Review r1 (tangency-monitor): the shim and runner refuse an SM_PC_UNKNOWN start or end of a face- or region-mid interval (MergeInterval leaves it without object; SmCurveClass.cpp:17659/:17672 dereference it); MergeCurveOnSurfaces takes UV trim curves (K_TRIM_CURVE, 3 inline cases, deferring at intersect.point_class's SmVertex::GetTolerance stub in MergeIntoObject); MergeCoincidentSurfaces' 3D edge-curve copy gets the edge curve's owner as upstream. The runner reuses the p1 runner's two-B-rep adapter and reorders imported vertex-use lists to upstream's list order (see plan/findings.md). Back-edges: blend.fillet_geom_p4's MergeCurveOnSurfaces stub retagged blend.fillet_executive_p1 (it runs on the executive's intersector); boolean.merge_p1's MergeCoincidentSurfaces stays UNPORTED(boolean.topology_intersector_p3) pending an IntersectorBrepP3 implementation for merge p4's ArenaPair (follow-up). 3 fns; largest: SmTopologyIntersector::MergeCurveClasses, SmTopologyIntersector::MergeCoincidentSurfaces, SmTopologyIntersector::MergeCurveOnSurfaces; unit-SCC #22 (220 units); 110 upstream test entry points reach this unit UPDATE (PR #679, UpdateVertexEdgeIntervals wired in CombineCoincidentVertices): the runner numbers every post-merge handle and the state by a re-walk of both B-reps (boolean.merge_p1 Canon::reachable, the shim's walk_all), so records a squeeze deleted (unlinked, kept in the Rust arena) are unknown on both sides, as upstream. 271 cases, 172 pass, 0 failed, 99 deferred; the 6 UpdateVertexEdgeIntervals deferrals pass." diff --git a/plan/units/topology.brep_p10.toml b/plan/units/topology.brep_p10.toml index a93c223d62..6a4dd89d76 100644 --- a/plan/units/topology.brep_p10.toml +++ b/plan/units/topology.brep_p10.toml @@ -9,4 +9,4 @@ tests = ["prog_test:my_test_single_periodic_vertexuse", "SM_API_test:TestSmAdvan status = "partial" claimed_by = "" claimed_at = "" -notes = "main CI e33985411 (2026-10-23): PARTIAL topology.brep_p10 cases=1404 passed=1154 failed=0 deferred=233 rejected=17 blocked_by=intersect.curve_class_p4(150),query.face_p2(1),topology.brep_p1(3),topology.face_p2(7),topology.face_p4(72). #76 kernel-aware Surface dispatch (PR #313, 2026-10-21): 1404 cases, 922 pass, 0 failed, 482 deferred (surface.sphere 352, topology.face_p2 101, surface.surf_of_extrusion_p2 10, topology.face_p4 9, geom.tol 5, query.brep 5), max_rel_err 0; before 851 pass / 553 deferred. partial: cases=1404 passed=839 failed=0 deferred=565 max_rel_err=0 (surface.cone_p2 424: SmCone::IsNurbSurfaceCone in CopyAndAddAnalytics; topology.face_p2 63: SmFace::GetTolerance inside HealBrep; surface.cone_p1 41 / surface.surf_of_extrusion_p2 10: the Rust read of the file stops at the Surface::read_from_db dispatch gap (the cone_p1 label is a stale stub of surface/surface/p2.rs, not this unit's); topology.brep_p2 19: IsManifoldSolid, and SmBrep::Notify reached through the face use TestClosureSplitRegion of topology.brep_p6; topology.brep_p5 8: RemoveFaces / FindAndSetInfiniteRegion); --perturb SENSITIVE 839/839. Review fixes (PR #130): a non-SmBSplineCurve UV trim copy is not stored on the edge use (SM_CAST_PTR); crafted fixtures cover wire-edge/vertex shells, vertex loops, the analytic edge-curve end check and composite/B-spline UV curves (review_cases.json, 128 cases); adv_cases.json (192 review cases: manifold flags 3/-1, IGES empty shells, shared surfaces and curves with and without borrowed geometry, zone tolerance clamp limits, healer versions, pole/vertex edge uses mid-loop, relinked and overlapping loops, closure after relinking, a dropped UV curve, remove-all with editing disabled). Second review (after main merged topology.brep_p1 #145): dropped the duplicate co-ported m_bEditingEnabled (main's Brep field), new_with_infinite_region sets region_list_head (PreInsert); wired the topology.brep_p1 back-edges (sm_AddAttributes with TopologyEnv::all_attributes = rAllAttributes, HealBrep, GetRegions, GetFaces, GetWireEdges, CreateParallelProjectionCurves, sm_FindSideOfCurve) and topology.brep_p7's two UNPORTED(topology.brep_p10) sites (ReadFromFile, GlueEdgesGeneral). ProjectAndTrim and RemoveAllTopology (editing enabled) still defer at later units. 4 fns; cycle back-edges stubbed: topology.brep_p2 (IsManifoldSolid, MergeCurvesOnSurface, GetShellVertices, sm_GetPointFromSol), topology.brep_p4 (DeleteEdge, DeleteVertex), topology.brep_p5 (RemoveFaces, FindAndSetInfiniteRegion), topology.face_p4 (GetVertices, GetEdges), query.brep (CalculateBoundingBox). Harness-level deferral labels topology.brep_p10 (inexact .smb imports) remain in the topology.vertex, topology.edgeuse_p1/p2 and topology.loopuse runners (see findings) Batch integration (batch/clean-2026-10-19): RemoveFaces and FindAndSetInfiniteRegion() wired to topology::brep::p5 (remove_faces with the B-rep editing flag; find_and_set_infinite_region with the upstream defaults, which defers inside part 5). UPDATE after topology.brep_p2 (PR #149 follow-up): IsManifoldSolid (pBrepData->m_bManifold = IsManifoldSolid()), sm_GetPointFromSol and GetShellVertices are wired to topology::brep::p2; MergeCurvesOnSurface calls topology::brep::p2::merge_curves_on_surface (SmBrep.cpp:25074, header defaults) through an uninhabited MergeCurvesObjects world over the B-rep cache whose constructor is the UNPORTED(query.brep) stub: query.brep (todo) is the real blocker, as for topology.shell's shell_ray_sector (overseer ruling); test_closure_split_region takes the caller's NotifyEnv. 1404 cases, 851 pass, 0 fail, 553 deferred: 12 IsManifoldSolid deferrals now pass (201-16358 values each), 2 defer on topology.face_p2 SmFace::GetTolerance. Unit test healing_runs_and_manifold_is_computed (renamed) checks the UNSURE flag is computed (0 for a sheet). Re-run in PR #149 (review 5578349117 round, merged with main through fb58ea76; topology.brep_p2's UNPORTED stubs wired): 1404 cases, 851 pass, 0 fail, 553 deferred; 12 former topology.brep_p2 deferrals (SmBrep::IsManifoldSolid, Notify) pass (201-897 values), 2 defer on topology.face_p2 SmFace::GetTolerance, 5 Notify deferrals relabel to query.brep." +notes = "main CI e33985411 (2026-10-23): PARTIAL topology.brep_p10 cases=1404 passed=1154 failed=0 deferred=233 rejected=17 blocked_by=intersect.curve_class_p4(150),query.face_p2(1),topology.brep_p1(3),topology.face_p2(7),topology.face_p4(72). #76 kernel-aware Surface dispatch (PR #313, 2026-10-21): 1404 cases, 922 pass, 0 failed, 482 deferred (surface.sphere 352, topology.face_p2 101, surface.surf_of_extrusion_p2 10, topology.face_p4 9, geom.tol 5, query.brep 5), max_rel_err 0; before 851 pass / 553 deferred. partial: cases=1404 passed=839 failed=0 deferred=565 max_rel_err=0 (surface.cone_p2 424: SmCone::IsNurbSurfaceCone in CopyAndAddAnalytics; topology.face_p2 63: SmFace::GetTolerance inside HealBrep; surface.cone_p1 41 / surface.surf_of_extrusion_p2 10: the Rust read of the file stops at the Surface::read_from_db dispatch gap (the cone_p1 label is a stale stub of surface/surface/p2.rs, not this unit's); topology.brep_p2 19: IsManifoldSolid, and SmBrep::Notify reached through the face use TestClosureSplitRegion of topology.brep_p6; topology.brep_p5 8: RemoveFaces / FindAndSetInfiniteRegion); --perturb SENSITIVE 839/839. Review fixes (PR #130): a non-SmBSplineCurve UV trim copy is not stored on the edge use (SM_CAST_PTR); crafted fixtures cover wire-edge/vertex shells, vertex loops, the analytic edge-curve end check and composite/B-spline UV curves (review_cases.json, 128 cases); adv_cases.json (192 review cases: manifold flags 3/-1, IGES empty shells, shared surfaces and curves with and without borrowed geometry, zone tolerance clamp limits, healer versions, pole/vertex edge uses mid-loop, relinked and overlapping loops, closure after relinking, a dropped UV curve, remove-all with editing disabled). Second review (after main merged topology.brep_p1 #145): dropped the duplicate co-ported m_bEditingEnabled (main's Brep field), new_with_infinite_region sets region_list_head (PreInsert); wired the topology.brep_p1 back-edges (sm_AddAttributes with TopologyEnv::all_attributes = rAllAttributes, HealBrep, GetRegions, GetFaces, GetWireEdges, CreateParallelProjectionCurves, sm_FindSideOfCurve) and topology.brep_p7's two UNPORTED(topology.brep_p10) sites (ReadFromFile, GlueEdgesGeneral). ProjectAndTrim and RemoveAllTopology (editing enabled) still defer at later units. 4 fns; cycle back-edges stubbed: topology.brep_p2 (IsManifoldSolid, MergeCurvesOnSurface, GetShellVertices, sm_GetPointFromSol), topology.brep_p4 (DeleteEdge, DeleteVertex), topology.brep_p5 (RemoveFaces, FindAndSetInfiniteRegion), topology.face_p4 (GetVertices, GetEdges), query.brep (CalculateBoundingBox). Harness-level deferral labels topology.brep_p10 (inexact .smb imports) remain in the topology.vertex, topology.edgeuse_p1/p2 and topology.loopuse runners (see findings) Batch integration (batch/clean-2026-10-19): RemoveFaces and FindAndSetInfiniteRegion() wired to topology::brep::p5 (remove_faces with the B-rep editing flag; find_and_set_infinite_region with the upstream defaults, which defers inside part 5). UPDATE after topology.brep_p2 (PR #149 follow-up): IsManifoldSolid (pBrepData->m_bManifold = IsManifoldSolid()), sm_GetPointFromSol and GetShellVertices are wired to topology::brep::p2; MergeCurvesOnSurface calls topology::brep::p2::merge_curves_on_surface (SmBrep.cpp:25074, header defaults) through an uninhabited MergeCurvesObjects world over the B-rep cache whose constructor is the UNPORTED(query.brep) stub: query.brep (todo) is the real blocker, as for topology.shell's shell_ray_sector (overseer ruling); test_closure_split_region takes the caller's NotifyEnv. 1404 cases, 851 pass, 0 fail, 553 deferred: 12 IsManifoldSolid deferrals now pass (201-16358 values each), 2 defer on topology.face_p2 SmFace::GetTolerance. Unit test healing_runs_and_manifold_is_computed (renamed) checks the UNSURE flag is computed (0 for a sheet). Re-run in PR #149 (review 5578349117 round, merged with main through fb58ea76; topology.brep_p2's UNPORTED stubs wired): 1404 cases, 851 pass, 0 fail, 553 deferred; 12 former topology.brep_p2 deferrals (SmBrep::IsManifoldSolid, Notify) pass (201-897 values), 2 defer on topology.face_p2 SmFace::GetTolerance, 5 Notify deferrals relabel to query.brep. UPDATE (PR #679, UpdateVertexEdgeIntervals wired in CombineCoincidentVertices): 1404 cases, 1154 pass, 0 failed, 233 deferred (unchanged); the 3 UpdateVertexEdgeIntervals deferrals (read_Fillet_open_shell, read_Solids_fillet_ts, gen0115) now defer at intersect.curve_class_p4 GetLoopCrossings." diff --git a/plan/units/topology.brep_p5.toml b/plan/units/topology.brep_p5.toml index 1efcc50d03..f64eb838de 100644 --- a/plan/units/topology.brep_p5.toml +++ b/plan/units/topology.brep_p5.toml @@ -9,4 +9,4 @@ tests = ["prog_test:my_test_tsurf_cyl_creation_2d", "prog_test:my_test_tsurf_cyl status = "partial" claimed_by = "" claimed_at = "" -notes = "PR #637 (2026-10-23): face ChangeGeometry notification wired. difftest 797 cases, 787 pass, 0 failed, 10 deferred (topology.brep_p1 8, topology.brep_p4 2); the 2 former topology.face_p3 deferrals pass. PARTIAL (PR #194, functional source ecccb67b; official main 9a7c1ec0; Claude review fixes): all twelve owned editing control flows and pending-topology cleanup present; individual missing callees/types defer. Landed shell/region callees wired; stable shell, vertex and vertex-use record erasure remains the part 4 SmBrep destructor/storage integration gap. Native difftest: 797 cases (285 fixtures + 512 generated), 737 passed, 0 failed, 60 deferred (topology.brep_p1 10, topology.brep_p2 46, topology.brep_p4 2, topology.brep_p9 2), max_rel_err=0, no absolute floors. Meaningful successful topology-mutation paths certified: 0/12 operations (issue #210). Coverage is 409 point/tolerance-target-stage cases, 301 whole-operation refusals/empty/identity/alias no-ops, and 27 supporting notification scripts. Temporary-edge minimum-length threshold, successful topology mutation, nonidentity transforms, shared native geometry across distinct owning records and pending-reader integration remain uncertified. Sensitivity 737/737 detected; 4096 generated: 4375 cases, 4195 passed, 180 deferred, 0 failed. Formatting, both strict Clippy configurations, 1369 kernel tests, WASM, 3 native FFI tests and all twelve affected differential units pass; full green/partial sweep PASS: 205 units, 113 green, 92 partial, 0 failed. No open dependency PR bundled or other unit promoted. 14 fns; largest: SmBrep::RemoveTopologicalEdgesAndVertices, SmBrep::CombineCoincidentVertices, SmBrep::DeleteTopologicalVertex, SmBrep::GlueVertices; cycle back-edges (call into later units; stub or co-port): topology.brep_p3(8), topology.brep_p2(5), heal.stitch_p1(3), topology.brep_p9(3), heal.stitch_p2(2); unit-SCC #22 (220 units); hazards: ptr-iter; 200 upstream test entry points reach this unit Second review round (head e9df9346, merged official main with topology.brep_p1/p7 and heal.stitch_p1): isolated aliased CombineCoincidentVertices takes the native path (6 new fixtures, deferred at Notify; only the target stage refuses it, UB-191); Notify status ignored in combine as upstream; SqueezeFace unlock never hides its result; FFI guard refusals are rejected, not failed. Wired heal.stitch_p1 (GlueVertices, RemoveFaces, StitchAndOrient) and topology.brep_p7 (GlueVertices) into this unit. Calls into the now-partial topology.brep_p1 are still UNPORTED stubs pending a follow-up PR. Batch integration (batch/clean-2026-10-19): set_face_tolerance wired to topology::face::p3::set_tolerance (topology.face_p3). Re-run in PR #149 (review 5578349117 round, merged with main through fb58ea76; topology.brep_p2's UNPORTED stubs wired): 797 cases, 738 pass, 0 fail, 59 deferred (topology.brep_p1 38, topology.brep_p4 12, topology.brep_p3 7, topology.brep_p9 2); the 46 SmBrep::Notify deferrals: 1 pass, the rest relabel to brep_p1 GetVertices / UpdateVertexEdgeIntervals, brep_p4 DeleteVertex and brep_p3 TurnToNURBS. Re-run after wiring stale stubs (PR #285): 797 cases, 744 pass, 0 fail, 53 deferred, max_rel_err 0. UPDATE (PR #204, topology.brep_p3): the UNPORTED(topology.brep_p3) stubs SqueezeEdge, MakeWireEdgeVertex, DeleteFace, OrientTrimmedSurfaces and TurnToNURBS, and UNPORTED(topology.brep_p9) MakeShellVertex in create_wire_edge_from_curve, are wired to topology::brep::p3 / p9 through with_p3_notifications (the caller's environment, else a topology.brep_p2 ArenaNotifyState over the kernel context's callbacks lent with brep::p3::with_lent_context); make_manifold and stitch_and_orient take k: &mut Kernel. Case by case: no outcome change; the 7 TurnToNURBS deferrals relabel to topology.brep_p1 GetVertices (797 cases, 738 pass, 0 fail, 59 deferred: topology.brep_p1 45, topology.brep_p4 12, topology.brep_p9 2). UPDATE (PR #204, Codex review of 255d52b7): a plain entry point (no environment; the _with_notifications forms given None) runs its whole edit in one environment under the kernel's identities (plain_edit = topology::brep::p3::with_kernel_callbacks: the B-rep's serial in the kernel context's domain, so two B-reps' objects never share a key in one context; the callbacks lent to the edit; nested in another lent edit it is refused before anything changes). create_wire_edge_from_curve, make_manifold, remove_faces, stitch_and_orient and squeeze_face are plain_edit wrappers over *_in bodies; with_p3_notifications no longer falls back to ArenaNotifyState. UPDATE (PR #204, Codex re-check of 6b04141d): plain_edit's environment carries a default record for every existing object from the start (brep::p3::kernel_named_edit), so the notifications sent before any p3 edit (e.g. notify_edge_merge) are delivered. UPDATE (PR #204, merge of main #339): op 13 (CreateWireEdgeFromCurve) runs now that GetInfiniteRegion is wired; its 2 fixture cases (isolated_op13_editing_*) compared the vertex-shell vertex use MakeEdgeVertexTopology deletes upstream and the arena keeps unlinked; with graph() comparing reachable records only (boolean.merge_p1) they pass. API EXTENDED in sweep.topology_sweep (PR #222): make_edge_in_face_with_uv_curve preserves polymorphic UV curve kinds, including native SmBSplineCurve-derived lines; the existing B-spline-only Rust entry point delegates without changing behavior. UPDATE (PR #526): FindAndSetInfiniteRegion wired to topology.brep_p9 FindInfiniteRegion (through the caller's query::brep::BrepQuery) and topology.brep_p1 ResetInfiniteRegion; it takes the kernel and SetRegionVoidType. Re-run: 797 cases, 746 pass, 0 fail, 51 deferred (the 2 FindInfiniteRegion deferrals pass). UPDATE (wire/brep-p5-find-vertex-sector): MakeEdgeInFace wired to topology.face_p4 FindVertexSector and topology.brep_p9 MakeEdgeInFaceWithVU; make_edge_in_face(_with_uv_curve) take the kernel and an optional notification environment (None: plain edit). 797 cases, 746 pass, 51 deferred, 0 failed (unchanged). SweepStore transforms use owner-aware brep_p1 collections and brep_p3 NURBS conversion. Fixture import binds ownership metadata to its kernel without changing geometry values; unavailable face-p3 UV-trim construction remains deferred. UPDATE (PR #441): StitchAndOrient runs its two StitchFaces passes outside the edit lease (the stitch's glue/squeeze helpers notify through their own kernel-context edits, which the nested-edit guard refused, and the swallowed error left swept B-reps unstitched); the orientation pass keeps the caller's environment. The shell-vertex glue path still stops at remove_owned_vertex (plan/findings.md). UPDATE (oursland/solidcore#1): remove_owned_vertex is Brep::remove_from_vertex_list; the 36 heal.stitch_p2 differences it exposed were topology.brep_p7 GlueEdgesGeneral leaving the glued edge on the edge list (fixed); difftest 797 cases, 785 pass, 0 failed, 12 deferred." +notes = "PR #637 (2026-10-23): face ChangeGeometry notification wired. difftest 797 cases, 787 pass, 0 failed, 10 deferred (topology.brep_p1 8, topology.brep_p4 2); the 2 former topology.face_p3 deferrals pass. PARTIAL (PR #194, functional source ecccb67b; official main 9a7c1ec0; Claude review fixes): all twelve owned editing control flows and pending-topology cleanup present; individual missing callees/types defer. Landed shell/region callees wired; stable shell, vertex and vertex-use record erasure remains the part 4 SmBrep destructor/storage integration gap. Native difftest: 797 cases (285 fixtures + 512 generated), 737 passed, 0 failed, 60 deferred (topology.brep_p1 10, topology.brep_p2 46, topology.brep_p4 2, topology.brep_p9 2), max_rel_err=0, no absolute floors. Meaningful successful topology-mutation paths certified: 0/12 operations (issue #210). Coverage is 409 point/tolerance-target-stage cases, 301 whole-operation refusals/empty/identity/alias no-ops, and 27 supporting notification scripts. Temporary-edge minimum-length threshold, successful topology mutation, nonidentity transforms, shared native geometry across distinct owning records and pending-reader integration remain uncertified. Sensitivity 737/737 detected; 4096 generated: 4375 cases, 4195 passed, 180 deferred, 0 failed. Formatting, both strict Clippy configurations, 1369 kernel tests, WASM, 3 native FFI tests and all twelve affected differential units pass; full green/partial sweep PASS: 205 units, 113 green, 92 partial, 0 failed. No open dependency PR bundled or other unit promoted. 14 fns; largest: SmBrep::RemoveTopologicalEdgesAndVertices, SmBrep::CombineCoincidentVertices, SmBrep::DeleteTopologicalVertex, SmBrep::GlueVertices; cycle back-edges (call into later units; stub or co-port): topology.brep_p3(8), topology.brep_p2(5), heal.stitch_p1(3), topology.brep_p9(3), heal.stitch_p2(2); unit-SCC #22 (220 units); hazards: ptr-iter; 200 upstream test entry points reach this unit Second review round (head e9df9346, merged official main with topology.brep_p1/p7 and heal.stitch_p1): isolated aliased CombineCoincidentVertices takes the native path (6 new fixtures, deferred at Notify; only the target stage refuses it, UB-191); Notify status ignored in combine as upstream; SqueezeFace unlock never hides its result; FFI guard refusals are rejected, not failed. Wired heal.stitch_p1 (GlueVertices, RemoveFaces, StitchAndOrient) and topology.brep_p7 (GlueVertices) into this unit. Calls into the now-partial topology.brep_p1 are still UNPORTED stubs pending a follow-up PR. Batch integration (batch/clean-2026-10-19): set_face_tolerance wired to topology::face::p3::set_tolerance (topology.face_p3). Re-run in PR #149 (review 5578349117 round, merged with main through fb58ea76; topology.brep_p2's UNPORTED stubs wired): 797 cases, 738 pass, 0 fail, 59 deferred (topology.brep_p1 38, topology.brep_p4 12, topology.brep_p3 7, topology.brep_p9 2); the 46 SmBrep::Notify deferrals: 1 pass, the rest relabel to brep_p1 GetVertices / UpdateVertexEdgeIntervals, brep_p4 DeleteVertex and brep_p3 TurnToNURBS. Re-run after wiring stale stubs (PR #285): 797 cases, 744 pass, 0 fail, 53 deferred, max_rel_err 0. UPDATE (PR #204, topology.brep_p3): the UNPORTED(topology.brep_p3) stubs SqueezeEdge, MakeWireEdgeVertex, DeleteFace, OrientTrimmedSurfaces and TurnToNURBS, and UNPORTED(topology.brep_p9) MakeShellVertex in create_wire_edge_from_curve, are wired to topology::brep::p3 / p9 through with_p3_notifications (the caller's environment, else a topology.brep_p2 ArenaNotifyState over the kernel context's callbacks lent with brep::p3::with_lent_context); make_manifold and stitch_and_orient take k: &mut Kernel. Case by case: no outcome change; the 7 TurnToNURBS deferrals relabel to topology.brep_p1 GetVertices (797 cases, 738 pass, 0 fail, 59 deferred: topology.brep_p1 45, topology.brep_p4 12, topology.brep_p9 2). UPDATE (PR #204, Codex review of 255d52b7): a plain entry point (no environment; the _with_notifications forms given None) runs its whole edit in one environment under the kernel's identities (plain_edit = topology::brep::p3::with_kernel_callbacks: the B-rep's serial in the kernel context's domain, so two B-reps' objects never share a key in one context; the callbacks lent to the edit; nested in another lent edit it is refused before anything changes). create_wire_edge_from_curve, make_manifold, remove_faces, stitch_and_orient and squeeze_face are plain_edit wrappers over *_in bodies; with_p3_notifications no longer falls back to ArenaNotifyState. UPDATE (PR #204, Codex re-check of 6b04141d): plain_edit's environment carries a default record for every existing object from the start (brep::p3::kernel_named_edit), so the notifications sent before any p3 edit (e.g. notify_edge_merge) are delivered. UPDATE (PR #204, merge of main #339): op 13 (CreateWireEdgeFromCurve) runs now that GetInfiniteRegion is wired; its 2 fixture cases (isolated_op13_editing_*) compared the vertex-shell vertex use MakeEdgeVertexTopology deletes upstream and the arena keeps unlinked; with graph() comparing reachable records only (boolean.merge_p1) they pass. API EXTENDED in sweep.topology_sweep (PR #222): make_edge_in_face_with_uv_curve preserves polymorphic UV curve kinds, including native SmBSplineCurve-derived lines; the existing B-spline-only Rust entry point delegates without changing behavior. UPDATE (PR #526): FindAndSetInfiniteRegion wired to topology.brep_p9 FindInfiniteRegion (through the caller's query::brep::BrepQuery) and topology.brep_p1 ResetInfiniteRegion; it takes the kernel and SetRegionVoidType. Re-run: 797 cases, 746 pass, 0 fail, 51 deferred (the 2 FindInfiniteRegion deferrals pass). UPDATE (wire/brep-p5-find-vertex-sector): MakeEdgeInFace wired to topology.face_p4 FindVertexSector and topology.brep_p9 MakeEdgeInFaceWithVU; make_edge_in_face(_with_uv_curve) take the kernel and an optional notification environment (None: plain edit). 797 cases, 746 pass, 51 deferred, 0 failed (unchanged). SweepStore transforms use owner-aware brep_p1 collections and brep_p3 NURBS conversion. Fixture import binds ownership metadata to its kernel without changing geometry values; unavailable face-p3 UV-trim construction remains deferred. UPDATE (PR #441): StitchAndOrient runs its two StitchFaces passes outside the edit lease (the stitch's glue/squeeze helpers notify through their own kernel-context edits, which the nested-edit guard refused, and the swallowed error left swept B-reps unstitched); the orientation pass keeps the caller's environment. The shell-vertex glue path still stops at remove_owned_vertex (plan/findings.md). UPDATE (oursland/solidcore#1): remove_owned_vertex is Brep::remove_from_vertex_list; the 36 heal.stitch_p2 differences it exposed were topology.brep_p7 GlueEdgesGeneral leaving the glued edge on the edge list (fixed); difftest 797 cases, 785 pass, 0 failed, 12 deferred. UPDATE (wire: SmBrep::Transform into blend.hcr and the boolean.merge_p1 runner, PR #679): CombineCoincidentVertices calls the ported topology.brep_p1 UpdateVertexEdgeIntervals under the caller's notification environment (the stub is gone). difftest 797 cases, 795 pass, 0 failed, 2 deferred (topology.brep_p4 2); the 8 UpdateVertexEdgeIntervals deferrals pass."