mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-08 13:01:52 +00:00
Merge main into codex/fillet-rollover-concrete-solver-ops
Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
@@ -424,6 +424,22 @@ pub(super) enum OpIn {
|
||||
CloseLoop {
|
||||
v: Hin,
|
||||
},
|
||||
/// A marker, dropped by the resolver: later `FaceFace` ops (with no input
|
||||
/// vertices) may follow a `VertexFace` or `EdgeFace` op, taking only face handles
|
||||
/// (faces keep their handles through those edits; edges and vertices do not).
|
||||
/// Existing cases never carry it, so their resolution is unchanged.
|
||||
ChainFaces,
|
||||
/// `pBrep->m_bEditingEnabled = enable` on the intersector's B-rep `which` (0 or 1),
|
||||
/// which `IntersectTopology` sets around its stages and the edits need (shim op 34).
|
||||
SetEditing {
|
||||
which: u8,
|
||||
enable: bool,
|
||||
},
|
||||
/// `pPrimaryBrep->MergeBrep(*pOtherBrep, NULL, NULL, FALSE)`, as `SmMerge` does before
|
||||
/// it intersects, so both operands' faces share an owner. Only as the first op (shim
|
||||
/// op 35); the other B-rep stays as it was, and the primary's handles then number the
|
||||
/// primary's entities followed by the copies.
|
||||
MergeBreps,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -1056,6 +1072,7 @@ impl ClassGeometry<H> for Two {
|
||||
K_FACE => EntityRef::Face(fid(o)),
|
||||
K_LOOP => EntityRef::Loop(LoopId::from_index(o.i)),
|
||||
K_EDGEUSE => EntityRef::Edgeuse(EdgeuseId::from_index(o.i)),
|
||||
K_VERTEXUSE => EntityRef::Vertexuse(VertexuseId::from_index(o.i)),
|
||||
_ => return None,
|
||||
};
|
||||
Some((b, e))
|
||||
@@ -2558,6 +2575,8 @@ fn resolve(
|
||||
let mut curves = 0usize;
|
||||
let mut has_mgr = false;
|
||||
let mut mutated = false;
|
||||
let mut chain = false;
|
||||
let mut soft = false;
|
||||
// the intersector's B-reps (the FFI's `have_breps`)
|
||||
let mut have_breps = [true, c.breps[1].is_some()];
|
||||
let mut closed = false;
|
||||
@@ -2594,13 +2613,20 @@ fn resolve(
|
||||
| OpIn::ShellVerts { .. }
|
||||
| OpIn::WireLamina { .. }
|
||||
| OpIn::MgrNew { .. }
|
||||
| OpIn::SetEditing { .. }
|
||||
| OpIn::MergeBreps
|
||||
| OpIn::CommonEdges
|
||||
| OpIn::CommonVertices
|
||||
| OpIn::IntersectTopo
|
||||
| OpIn::SetRels
|
||||
| OpIn::Iir
|
||||
);
|
||||
if mutated && takes_handles {
|
||||
if matches!(op, OpIn::ChainFaces) {
|
||||
chain = true;
|
||||
continue;
|
||||
}
|
||||
let face_only = matches!(op, OpIn::FaceFace { verts, .. } if verts.is_empty());
|
||||
if (mutated || (soft && !face_only)) && takes_handles {
|
||||
return Err(fail("after_mutation"));
|
||||
}
|
||||
// part 2: what the FFI refuses
|
||||
@@ -2869,6 +2895,12 @@ fn resolve(
|
||||
}
|
||||
op.clone()
|
||||
}
|
||||
OpIn::SetEditing { which, .. } => {
|
||||
if *which > 1 || (*which == 1 && c.breps[1].is_none()) {
|
||||
return Err(fail("brep"));
|
||||
}
|
||||
op.clone()
|
||||
}
|
||||
OpIn::MgrNew { .. } => {
|
||||
if has_mgr || c.breps[1].is_none() {
|
||||
return Err(fail("manager"));
|
||||
@@ -2940,6 +2972,14 @@ fn resolve(
|
||||
OpIn::CloseLoop { v } => OpIn::CloseLoop {
|
||||
v: hk(*v, &[K_VERTEX], true)?,
|
||||
},
|
||||
// handled above (never reaches here)
|
||||
OpIn::ChainFaces => continue,
|
||||
OpIn::MergeBreps => {
|
||||
if idx != 0 || c.breps[1].is_none() {
|
||||
return Err(fail("merge"));
|
||||
}
|
||||
op.clone()
|
||||
}
|
||||
};
|
||||
if matches!(
|
||||
r,
|
||||
@@ -2958,7 +2998,11 @@ fn resolve(
|
||||
| OpIn::EdgeFaceCoin { .. }
|
||||
| OpIn::CloseLoop { .. }
|
||||
) {
|
||||
mutated = true;
|
||||
if chain && !mutated && matches!(r, OpIn::VertexFace { .. } | OpIn::EdgeFace { .. }) {
|
||||
soft = true;
|
||||
} else {
|
||||
mutated = true;
|
||||
}
|
||||
}
|
||||
out.push(ROp { op: r });
|
||||
}
|
||||
@@ -3247,6 +3291,9 @@ fn encode(i: &Inputs, ops: &[ROp]) -> Result<Vec<f64>, HarnessError> {
|
||||
capture,
|
||||
override_tol,
|
||||
} => v.extend([33.0, *capture, *override_tol]),
|
||||
OpIn::SetEditing { which, enable } => {
|
||||
v.extend([34.0, f64::from(*which), flag(*enable)]);
|
||||
}
|
||||
OpIn::RemoveRel { a, b } => {
|
||||
v.push(41.0);
|
||||
push_h(&mut v, *a);
|
||||
@@ -3307,6 +3354,9 @@ fn encode(i: &Inputs, ops: &[ROp]) -> Result<Vec<f64>, HarnessError> {
|
||||
v.push(52.0);
|
||||
push_h(&mut v, *bv);
|
||||
}
|
||||
// dropped by the resolver
|
||||
OpIn::ChainFaces => {}
|
||||
OpIn::MergeBreps => v.push(35.0),
|
||||
}
|
||||
}
|
||||
Ok(v)
|
||||
@@ -3342,6 +3392,20 @@ fn put_cut(o: &mut Vec<f64>, cut: Option<&(usize, String)>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// `target->MergeBrep(other, NULL, NULL, FALSE)`: `topology::brep::p3::merge_brep`.
|
||||
fn merge_primary(target: &mut Brep, other: &mut Brep) -> Result<(), HarnessError> {
|
||||
use solidcore::topology::brep::p3 as bp3;
|
||||
let mut k = Kernel::default();
|
||||
let serial = bp3::notify_serial(&mut k.context, other)
|
||||
.map_err(|e| harness(format!("merge: {e}")))?;
|
||||
let source_key = move |e| bp3::topology_ref(serial, e);
|
||||
let other = &*other;
|
||||
bp3::with_kernel_callbacks(&mut k, target, |k, b, env| {
|
||||
bp3::merge_brep(k, env, b, other, &source_key, None, None, false)
|
||||
})
|
||||
.map_err(|e| harness(format!("merge: {e}")))
|
||||
}
|
||||
|
||||
/// Exports and imports the B-reps and resolves the script; `Err(Ok(output))` is the
|
||||
/// output both sides report when the B-reps cannot be built or an op cannot be
|
||||
/// resolved.
|
||||
@@ -3355,6 +3419,13 @@ fn prepare(k: &Kernel, i: &Inputs) -> Result<Prepared, Box<Result<Output, Harnes
|
||||
Some(b) => Some(import(b).map_err(|e| Box::new(Err(e)))?.0),
|
||||
None => None,
|
||||
};
|
||||
let mut brep_a = brep_a;
|
||||
let mut brep_b = brep_b;
|
||||
if matches!(i.ops.first(), Some(OpIn::MergeBreps)) {
|
||||
if let Some(other) = brep_b.as_mut() {
|
||||
merge_primary(&mut brep_a, other).map_err(|e| Box::new(Err(e)))?;
|
||||
}
|
||||
}
|
||||
let count3 = |b: &Brep| [b.vertices().len(), b.edges().len(), b.faces().len()];
|
||||
let counts = Counts {
|
||||
breps: [Some(count3(&brep_a)), brep_b.as_ref().map(count3)],
|
||||
@@ -3926,6 +3997,15 @@ fn run_rust_script(i: &Inputs, p: Prepared) -> Result<(Vec<f64>, Vec<usize>), Fa
|
||||
o.push(r.max_gap);
|
||||
o.push(r.min_unstitched);
|
||||
}
|
||||
OpIn::MergeBreps => {
|
||||
// (merged in `prepare`, which the resolution needs the counts of)
|
||||
o.push(35.0);
|
||||
o.push(0.0);
|
||||
}
|
||||
OpIn::SetEditing { which, enable } => {
|
||||
o.push(34.0);
|
||||
two.set_editing_enabled(brep_h(i8::try_from(*which).unwrap_or(0)), *enable)?;
|
||||
}
|
||||
OpIn::MgrNew {
|
||||
capture,
|
||||
override_tol,
|
||||
@@ -5536,6 +5616,130 @@ fn edge_cases() -> Vec<(String, Value)> {
|
||||
},
|
||||
);
|
||||
}
|
||||
// Crossing faces: the intersection edge is inserted inside each face
|
||||
// (`SmBrep::MakeEdgeInFace`), appended after the cases above.
|
||||
let plane = |o: [f64; 3], n: [f64; 3], dom: [f64; 4]| BrepIn::Face {
|
||||
surface: SrfIn::Plane { o, n },
|
||||
dom,
|
||||
};
|
||||
let cross = |name: &str,
|
||||
a: BrepIn,
|
||||
b: BrepIn,
|
||||
approx: f64,
|
||||
tail: Vec<OpIn>,
|
||||
add: &mut dyn FnMut(&str, Inputs)| {
|
||||
let mut ops = vec![OpIn::FaceFace {
|
||||
verts: vec![],
|
||||
faces1: vec![f(0, 0)],
|
||||
faces2: vec![f(1, 0)],
|
||||
ignore: false,
|
||||
nesting: false,
|
||||
}];
|
||||
ops.extend(tail);
|
||||
add(
|
||||
name,
|
||||
Inputs {
|
||||
brep_a: a,
|
||||
brep_b: Some(b),
|
||||
ctor: 0,
|
||||
approx,
|
||||
ang: 0.1,
|
||||
ops,
|
||||
},
|
||||
);
|
||||
};
|
||||
let xy = [-1.0, -1.0, 1.0, 1.0];
|
||||
for (name, na, nb, da, db) in [
|
||||
(
|
||||
"cross_planes_perpendicular",
|
||||
[0.0, 0.0, 1.0],
|
||||
[1.0, 0.0, 0.0],
|
||||
xy,
|
||||
xy,
|
||||
),
|
||||
(
|
||||
"cross_planes_tilted",
|
||||
[0.0, 0.0, 1.0],
|
||||
[1.0, 0.0, 1.0],
|
||||
xy,
|
||||
xy,
|
||||
),
|
||||
(
|
||||
"cross_planes_partial_overlap",
|
||||
[0.0, 0.0, 1.0],
|
||||
[0.0, 1.0, 0.0],
|
||||
[-1.0, -1.0, 1.0, 1.0],
|
||||
[-3.0, -1.0, 0.5, 1.0],
|
||||
),
|
||||
(
|
||||
"cross_planes_one_wide",
|
||||
[0.0, 0.0, 1.0],
|
||||
[1.0, 1.0, 0.0],
|
||||
[-3.0, -3.0, 3.0, 3.0],
|
||||
[-0.5, -1.0, 0.5, 1.0],
|
||||
),
|
||||
] {
|
||||
for (suffix, tail) in [
|
||||
("", vec![]),
|
||||
("_resolve", vec![OpIn::Resolve, OpIn::Imprint]),
|
||||
] {
|
||||
cross(
|
||||
&format!("{name}{suffix}"),
|
||||
plane([0.0, 0.0, 0.0], na, da),
|
||||
plane([0.0, 0.0, 0.0], nb, db),
|
||||
1e-5,
|
||||
tail,
|
||||
&mut add,
|
||||
);
|
||||
}
|
||||
}
|
||||
// Faces of one B-rep, as `SmMerge` intersects them: the other operand is merged into
|
||||
// the primary B-rep first, so `GlueEdgesGeneral` sees edges of one owner. The edge
|
||||
// intersections come first (they give the interval ends their vertices).
|
||||
for (fa, na) in [("plane_square.smb", 4i64), ("disk.smb", 2)] {
|
||||
for (fb, nb) in [("sphere.smb", 1i64), ("cone.smb", 2), ("torus.smb", 2)] {
|
||||
add(
|
||||
&format!("merged_{fa}_{fb}"),
|
||||
Inputs {
|
||||
brep_a: file(fa),
|
||||
brep_b: Some(file(fb)),
|
||||
ctor: 0,
|
||||
approx: 1e-5,
|
||||
ang: 0.1,
|
||||
ops: vec![
|
||||
OpIn::MergeBreps,
|
||||
OpIn::SetEditing {
|
||||
which: 0,
|
||||
enable: true,
|
||||
},
|
||||
OpIn::ChainFaces,
|
||||
OpIn::EdgeFace {
|
||||
edges: (0..na + nb).map(|k| e(0, k)).collect(),
|
||||
faces: vec![f(0, 0), f(0, 1)],
|
||||
manifold: false,
|
||||
ignore: false,
|
||||
},
|
||||
OpIn::FaceFace {
|
||||
verts: vec![],
|
||||
faces1: vec![f(0, 0)],
|
||||
faces2: vec![f(0, 1)],
|
||||
ignore: false,
|
||||
nesting: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
// A plane through a B-spline surface and a file face.
|
||||
cross(
|
||||
"cross_plane_file_sphere",
|
||||
plane([0.0, 0.0, 0.0], [0.0, 0.0, 1.0], [-3.0, -3.0, 3.0, 3.0]),
|
||||
file("sphere.smb"),
|
||||
1e-5,
|
||||
vec![],
|
||||
&mut add,
|
||||
);
|
||||
out
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,8 @@
|
||||
//! `.smb` files, well-formed clamped B-splines, handles inside the B-reps' exports (it
|
||||
//! builds the B-reps once more to count), the registered curves that exist, edges with a
|
||||
//! curve and vertices and faces with a surface where upstream dereferences them, no
|
||||
//! handle-taking op after an op that may delete topology (23 to 28, 30), ops 7 and 8
|
||||
//! handle-taking op after an op that may delete topology (23 to 28, 30; only a `FaceFace`
|
||||
//! without input vertices may follow a `VertexFace` or an `EdgeFace`), ops 7 and 8
|
||||
//! only while the intersector's primary B-rep is set and 27 to 30 only while both are
|
||||
//! (either constructor sets both from the blocks, then `SetBreps` may change them; they
|
||||
//! dereference them unchecked); ops 29 to 33 need the
|
||||
@@ -157,6 +158,8 @@ pub enum ScriptOp {
|
||||
ShellVerts = 31,
|
||||
WireLamina = 32,
|
||||
MgrNew = 33,
|
||||
SetEditing = 34,
|
||||
MergeBreps = 35,
|
||||
}
|
||||
|
||||
impl ScriptOp {
|
||||
@@ -173,10 +176,10 @@ impl ScriptOp {
|
||||
BrepMate, CanImprint, CheckInterior, CheckSsi, ClearSsi, CommonEdges, CommonVertices,
|
||||
EdgeFace, FaceFace, FaceFaceCoin, FixupEnd, FixupMid, Getters, Imprint, ImprintFace,
|
||||
IsMated, Lowest, MappedBrep, MgrNew, OtherMate, RegisterSsi, Relate, Resolve, SetBreps,
|
||||
SetEdgeClass, SetSubset, SetTols, ShellVerts, SplitTwo, ThisMate, VertexFace,
|
||||
MergeBreps, SetEdgeClass, SetEditing, SetSubset, SetTols, ShellVerts, SplitTwo, ThisMate, VertexFace,
|
||||
WireLamina,
|
||||
};
|
||||
const ALL: [ScriptOp; 32] = [
|
||||
const ALL: [ScriptOp; 34] = [
|
||||
Relate,
|
||||
OtherMate,
|
||||
ThisMate,
|
||||
@@ -209,6 +212,8 @@ impl ScriptOp {
|
||||
ShellVerts,
|
||||
WireLamina,
|
||||
MgrNew,
|
||||
SetEditing,
|
||||
MergeBreps,
|
||||
];
|
||||
ALL.iter().copied().find(|o| o.code() == code)
|
||||
}
|
||||
@@ -716,6 +721,9 @@ fn script(
|
||||
rd.finite()?; // ang
|
||||
let n_ops = rd.index(MAX_OPS)?;
|
||||
let mut mutated = false;
|
||||
// after a `VertexFace` / `EdgeFace` only: face handles stay valid (neither deletes
|
||||
// a face), so a `FaceFace` without input vertices may still follow
|
||||
let mut soft = false;
|
||||
let mut has_mgr = false;
|
||||
// the intersector's B-reps: both from the blocks with either constructor (1 sets
|
||||
// them with SetPrimaryBrep / SetOtherBrep), then whatever SetBreps sets
|
||||
@@ -729,13 +737,13 @@ fn script(
|
||||
HandleKind::Curve,
|
||||
Brep,
|
||||
];
|
||||
for _ in 0..n_ops {
|
||||
let code = rd.int(1, extra.map_or(33, |x| x.max_code().max(33)))?;
|
||||
for op_index in 0..n_ops {
|
||||
let code = rd.int(1, extra.map_or(35, |x| x.max_code().max(35)))?;
|
||||
let Some(op) = ScriptOp::from_code(code) else {
|
||||
let extra = extra.ok_or_else(|| bad("bad script op"))?;
|
||||
let mut st = ScriptState {
|
||||
ctor,
|
||||
mutated,
|
||||
mutated: mutated || soft,
|
||||
has_mgr,
|
||||
have_breps,
|
||||
closed: false,
|
||||
@@ -746,6 +754,7 @@ fn script(
|
||||
return Err(bad("bad script op"));
|
||||
}
|
||||
mutated = st.mutated;
|
||||
soft = false;
|
||||
has_mgr = st.has_mgr;
|
||||
have_breps = st.have_breps;
|
||||
if st.closed {
|
||||
@@ -765,10 +774,12 @@ fn script(
|
||||
| ScriptOp::ShellVerts
|
||||
| ScriptOp::WireLamina
|
||||
| ScriptOp::MgrNew
|
||||
| ScriptOp::SetEditing
|
||||
| ScriptOp::MergeBreps
|
||||
| ScriptOp::CommonEdges
|
||||
| ScriptOp::CommonVertices
|
||||
);
|
||||
if mutated && takes_handles {
|
||||
if (mutated || (soft && !matches!(op, ScriptOp::FaceFace))) && takes_handles {
|
||||
return Err(bad("handle-taking op after a mutating op"));
|
||||
}
|
||||
// the ops that dereference the intersector's B-reps without checking
|
||||
@@ -876,7 +887,13 @@ fn script(
|
||||
rd.flag()?;
|
||||
}
|
||||
ScriptOp::FaceFace => {
|
||||
c.list(rd, &[Vertex], false)?;
|
||||
if soft {
|
||||
if rd.index(MAX_LIST)? != 0 {
|
||||
return Err(bad("vertex handles after a mutating op"));
|
||||
}
|
||||
} else {
|
||||
c.list(rd, &[Vertex], false)?;
|
||||
}
|
||||
c.list(rd, &[Face], false)?;
|
||||
c.list(rd, &[Face], false)?;
|
||||
rd.flag()?;
|
||||
@@ -908,6 +925,23 @@ fn script(
|
||||
rd.flag()?;
|
||||
rd.flag()?;
|
||||
}
|
||||
ScriptOp::MergeBreps => {
|
||||
// the first op only; the primary B-rep then holds both operands
|
||||
let (Some(a), Some(b)) = (c.breps[0], c.breps[1]) else {
|
||||
return Err(bad("no second B-rep"));
|
||||
};
|
||||
if op_index != 0 {
|
||||
return Err(bad("merge is not the first op"));
|
||||
}
|
||||
c.breps[0] = Some([a[0] + b[0], a[1] + b[1], a[2] + b[2]]);
|
||||
}
|
||||
ScriptOp::SetEditing => {
|
||||
let which = rd.index(2)?;
|
||||
if which == 1 && c.breps[1].is_none() {
|
||||
return Err(bad("no second B-rep"));
|
||||
}
|
||||
rd.flag()?;
|
||||
}
|
||||
ScriptOp::MgrNew => {
|
||||
if has_mgr || c.breps[1].is_none() {
|
||||
return Err(bad("manager already made or no second B-rep"));
|
||||
@@ -918,7 +952,11 @@ fn script(
|
||||
}
|
||||
}
|
||||
if op.mutates() {
|
||||
mutated = true;
|
||||
if !mutated && matches!(op, ScriptOp::VertexFace | ScriptOp::EdgeFace) {
|
||||
soft = true;
|
||||
} else {
|
||||
mutated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -917,6 +917,8 @@ enum TiScriptOp : int64_t
|
||||
S_SHELL_VERTS = 31,
|
||||
S_WIRE_LAMINA = 32,
|
||||
S_MGR_NEW = 33,
|
||||
S_SET_EDITING = 34,
|
||||
S_MERGE_BREPS = 35,
|
||||
};
|
||||
|
||||
// Handle kinds (`kind` of a `brep kind index` triple).
|
||||
@@ -1661,6 +1663,35 @@ void run_script(Rd& rd, Two& t, std::vector<double>& o)
|
||||
o.push_back(mn);
|
||||
break;
|
||||
}
|
||||
case S_MERGE_BREPS:
|
||||
{
|
||||
// the first op only: copies the other B-rep into the primary one, as SmMerge
|
||||
// does before it intersects, so both operands' faces share an owner
|
||||
if (t.brep[1] == nullptr)
|
||||
{
|
||||
o.push_back(ST_NULL_DEREF);
|
||||
break;
|
||||
}
|
||||
SmStatus s = t.brep[0]->MergeBrep(*t.brep[1], NULL, NULL, FALSE);
|
||||
o.push_back(st(s));
|
||||
t.g[0] = Graph();
|
||||
walk(t.brep[0], t.g[0]);
|
||||
t.index_geometry();
|
||||
break;
|
||||
}
|
||||
case S_SET_EDITING:
|
||||
{
|
||||
// pBrep->m_bEditingEnabled = flag (IntersectTopology sets it around the stages)
|
||||
int64_t which = rd.idx();
|
||||
SmBoolean enable = rd.bflag();
|
||||
if (which < 0 || which > 1 || t.brep[which] == nullptr)
|
||||
{
|
||||
o.push_back(ST_NULL_DEREF);
|
||||
break;
|
||||
}
|
||||
t.brep[which]->m_bEditingEnabled = enable;
|
||||
break;
|
||||
}
|
||||
case S_MGR_NEW:
|
||||
{
|
||||
double capture = rd.num();
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
Updates: plan/findings/boolean.topology_intersector_p1--cross-brep-glue-edges.md
|
||||
|
||||
Whether C++ is defined for the cross-B-rep `GlueEdgesGeneral` was not established (no ASan run), so no FFI guard was added for it. The committed fixtures take the route `SmMerge` takes: a `MergeBreps` runner op (shim op 35, `SmBrep::MergeBrep`) puts both operands into the primary B-rep, then `SetEditing`, `EdgeFace`, `FaceFace` on faces of that one B-rep. `merged_*` cases (plane_square or disk x sphere, cone, torus) reach `MakeEdgeInFace` in both faces and agree with C++ at 1e-9.
|
||||
@@ -0,0 +1,9 @@
|
||||
# FaceFaceIntersect with faces of two B-reps: GlueEdgesGeneral across B-reps
|
||||
|
||||
Unit: boolean.topology_intersector_p1 (PR #1165 runner work).
|
||||
|
||||
Inputs: plane_square.smb x sphere.smb (and torus.smb), `SetEditing` on both B-reps, one `EdgeFace` over all edges and both faces, then `FaceFace` with faces f(0,i), f(1,j) (8 cases, temporary names `cand_*`, not committed).
|
||||
|
||||
Both sides reach `MakeEdgeInFace` for the interval (Vertex/Vertex ends) in both faces. Upstream then calls `m_pBrep->GlueEdgesGeneral(pNewEdge1, ..., pNewEdge2)` with edges of two different B-reps. C++ continues (state length 36); the Rust runner returns "not an edge of this B-rep" because `brep_glue_edges_general` takes one arena (state length 25).
|
||||
|
||||
Suspected cause: upstream only calls this with both faces in one (merged) B-rep; the two-B-rep call is outside the contract and `topology::brep::p7::glue_edges_general` cannot model it. A fixture needs both faces in one B-rep, or the FFI should reject the cross-B-rep case as undefined.
|
||||
Reference in New Issue
Block a user