Files
solvecraft/harness/cpp/shim/boolean_topology_intersector_p2.cpp
T
Brett Lawson e701079774 boolean: go on past imprints whose MakeEdgeInFace finds no vertex sector (UB-242)
SmBrep::MakeEdgeInFace makes both FindVertexSector checks before
MakeEdgeInFaceWithVU, its first edit, so an edge-definition imprint that
stops there leaves the B-rep untouched (and an edge already made in Brep1 is
whole). Upstream IntersectInsertRelate goes on past such failures, and that
continuation is defined, but the port stopped as for a UB-242 half-edited
imprint. ImprintOutcome::failure_edited no longer counts them
(topology::brep::p5::is_no_vertex_sector), and the
boolean.topology_intersector_p2 shim decides the same case with the same const
FindVertexSector calls.

Five blend.feature_executive cases now match the C++ (defillet face 8 and
fillet1019 extended-surface XORs, whose folded extensions put the
intersection UV curves on the other preimage of the vertices). Regression
test from single_face_defillet_2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MNXLXj2sJf943y2dDF36Vy
2026-10-31 06:20:51 +01:00

697 lines
27 KiB
C++

// SPDX-License-Identifier: Apache-2.0
// difftest shim for unit `boolean.topology_intersector_p2`: the SmTopologyIntersector members
// owned by the unit (source/SMLib/src/SmTopologyIntersector.cpp,
// source/SMLib/inc/SmTopologyIntersector.h) on real SmBrep objects.
//
// Part 2 runs on the same intersector object and two-B-rep script as part 1, so this file
// includes part 1's shim (boolean_topology_intersector_p1.cpp) with SC_TI_EXTRA_OPS: every
// op of part 1 works unchanged and the script ops 41 to 52 below are part 2's. The entry
// point is sc_boolean_topology_intersector_p2_eval(op, path, in, n_in, out, cap, out_len);
// layouts are documented in crates/solidcore-ffi/src/units/boolean_topology_intersector_p2.rs,
// which validates every input (on top of part 1's validation).
#define SC_TI_EXTRA_OPS 1
#define SC_TI_EVAL_NAME sc_boolean_topology_intersector_p2_eval
#include "boolean_topology_intersector_p1.cpp"
namespace
{
enum TiScriptOp2 : int64_t
{
S_REMOVE_REL = 41,
S_SQUEEZE = 42,
S_DELETE_FACES = 43,
S_FIX_GAPS = 44,
S_INTERSECT_TOPO = 45,
S_SET_RELS = 46,
S_IIR = 47,
S_FIXUP_CLASS = 48,
S_FIXUP_SPLIT = 49,
S_EDGE_EDGE = 50,
S_EDGE_FACE_COIN = 51,
S_CLOSE_LOOP = 52,
};
bool is_vertex(const SmObject* p) { return p != nullptr && p->IsKindOf(SmVertex_TYPE); }
// A mate that upstream casts to SmVertex*: fine when null or a vertex.
bool vertex_mate_ok(const SmObject* p) { return p == nullptr || is_vertex(p); }
// A classification: `curve t0 t1 tol n (a b pc3*)*`, each pc `class h t has_t dev`.
// Returns false (after reading everything) when a vertex classification names no vertex.
bool read_cc(Rd& rd, const Two& t, const SmContext& ctx, SmCurveClassification*& cc)
{
SmCurve* c = (SmCurve*)rd_h(rd, t);
double t0 = rd.num(), t1 = rd.num(), tol = rd.num();
int64_t n = rd.idx();
cc = new (ctx) SmCurveClassification(c, SmExtent1d(t0, t1), nullptr, tol);
cc->SetSize((ULONG)n);
bool ok = c != nullptr;
for (int64_t i = 0; i < n; i++)
{
double a = rd.num(), b = rd.num();
(*cc)[(ULONG)i] = SmCurveInterval(SmExtent1d(a, b), &ctx, cc);
SmPointClassification* pcs[3] = {&(*cc)[(ULONG)i].m_vStart, &(*cc)[(ULONG)i].m_vMid, &(*cc)[(ULONG)i].m_vEnd};
for (SmPointClassification* pc : pcs)
{
int64_t cls = rd.idx();
SmObject* obj = rd_h(rd, t);
double tp = rd.num();
bool has_t = rd.flag();
double dev = rd.num();
pc->SetClassObject((SmPointClassificationType)cls, obj);
if (has_t)
pc->SetTParam(tp);
pc->SetDeviation(dev);
if (cls == SM_PC_VERTEX && !is_vertex(obj))
ok = false;
}
}
return ok;
}
void put_cc(std::vector<double>& o, const Two& t, const SmCurveClassification& cc)
{
o.push_back((double)cc.GetSize());
for (ULONG i = 0; i < cc.GetSize(); i++)
{
o.push_back(cc[i].GetInterval().GetMin());
o.push_back(cc[i].GetInterval().GetMax());
put_pc_full(o, t, cc[i].m_vStart);
put_pc_full(o, t, cc[i].m_vMid);
put_pc_full(o, t, cc[i].m_vEnd);
}
}
template <class T> void rd_list(Rd& rd, const Two& t, SmTArray<T*>& a)
{
int64_t n = rd.idx();
for (int64_t i = 0; i < n; i++)
a.Add((T*)rd_h(rd, t));
}
// The edges of a vertex all have a curve and vertices (FixGaps, EdgeFaceCoincidence
// dereference them).
bool vertex_edges_ok(SmVertex* v)
{
SmTArray<SmEdge*> es;
v->GetEdges(es);
for (ULONG i = 0; i < es.GetSize(); i++)
if (!edge_ok(es[i]))
return false;
return true;
}
bool vertex_faces_ok(SmVertex* v)
{
SmTArray<SmFace*> fs;
v->GetFaces(fs);
for (ULONG i = 0; i < fs.GetSize(); i++)
if (!face_ok(fs[i]))
return false;
return true;
}
// CloseIntersectionLoop dereferences the edges of both vertices (curves, vertices) and the
// primary edge use of the B-rep vertex's edges (NormalizedEvaluate).
bool close_loop_edges_ok(SmTopologyIntersector* ti, SmVertex* bv)
{
if (!vertex_edges_ok(bv))
return false;
SmTArray<SmEdge*> es;
bv->GetEdges(es);
for (ULONG i = 0; i < es.GetSize(); i++)
if (es[i]->GetPrimaryEdgeuse() == nullptr)
return false;
SmObject* om = ti->GetOtherMate(bv);
return !is_vertex(om) || vertex_edges_ok((SmVertex*)om);
}
// FixGaps' scan up to its first squeeze candidate (read-only): whether CombineCoincidentVertices
// would run with a null B-rep or a mate that is not a vertex (the steps before it return early).
bool fix_gaps_first_candidate_ok(SmTopologyIntersector* ti, const SmTArray<SmVertex*>& verts)
{
ULONG n = verts.GetSize();
for (ULONG ii = 0; ii < n; ii++)
for (ULONG jj = ii + 1; jj < n; jj++)
{
SmVertex *v1 = verts[ii], *v2 = verts[jj];
double tol = 1.01 * (v1->GetTolerance() + v2->GetTolerance());
if (!(v1->GetPoint().DistanceBetween(v2->GetPoint()) < tol))
continue;
SmTArray<SmEdge*> e1, e2;
v1->GetEdges(e1);
v2->GetEdges(e2);
bool common = false;
for (ULONG k = 0; k < e1.GetSize(); k++)
{
ULONG idx;
if (e2.FindElement(e1[k], idx) && e1[k]->GetCurve()->ApproximateLength(e1[k]->GetInterval(), 10) > tol)
common = true;
}
if (common)
continue;
SmObject* o1 = ti->GetOtherMate(v1);
SmObject* o2 = ti->GetOtherMate(v2);
if (o1 == nullptr || o2 == nullptr)
return true; // NER returns SM_ERR_NULL_POINTER first
return is_vertex(o1) && is_vertex(o2) && ti->GetPrimaryBrep() != nullptr && ti->GetOtherBrep() != nullptr;
}
return true;
}
// CheckForSqueezeVertices: whether a combination would run with a null B-rep (each block
// combines in its B-rep unless the previous relationship is better, which returns SM_ERR),
// or the second block would dereference a B-rep mate of the other vertex that is not a
// vertex (the first block's SM_ERR returns before; a first block that combines is assumed
// to reach the second). Since topology.brep_p5 the port runs CombineCoincidentVertices
// and reports InvalidInput (-2) exactly where the second block dereferences, so this guard
// can only over-refuse when RemoveRelationship or CombineCoincidentVertices fails partway
// through the first block: the port then returns that status where the shim answers -2,
// a mismatch the difftest would report (none in the generated or fixture cases).
// The other mate of the B-rep vertex is checked by the caller: the first block
// dereferences it at once.
// Each combination must also be given vertices of the B-rep it runs in: a vertex
// related to one of its own B-rep (Relate does not check) would be combined through
// the wrong B-rep, which upstream does without complaint and the port refuses
// ("not a vertex of this B-rep").
bool squeeze_breps_ok(SmTopologyIntersector* ti, SmVertex* bv, SmVertex* ov)
{
bool need_other = false, need_brep = false;
SmVertex* other_v = (SmVertex*)ti->GetOtherMate(bv);
SmObject* brep_mate = ti->GetBrepMate(ov);
SmVertex* brep_v = (SmVertex*)brep_mate;
if (other_v && other_v != ov)
{
double d_old = bv->GetPoint().DistanceBetween(other_v->GetPoint());
double d_new = bv->GetPoint().DistanceBetween(ov->GetPoint());
if (d_new > d_old + SM_EFF_ZERO_SQRT)
return true;
need_other = true;
}
if (brep_v && brep_v != bv)
{
if (!is_vertex(brep_mate))
return false;
double d_old = ov->GetPoint().DistanceBetween(brep_v->GetPoint());
double d_new = ov->GetPoint().DistanceBetween(bv->GetPoint());
if (!(d_new > d_old + SM_EFF_ZERO_SQRT))
need_brep = true;
}
SmBrep* other = ti->GetOtherBrep();
SmBrep* primary = ti->GetPrimaryBrep();
return (!need_other || (other != nullptr && ov->GetBrep() == other && other_v->GetBrep() == other)) &&
(!need_brep || (primary != nullptr && bv->GetBrep() == primary && brep_v->GetBrep() == primary));
}
// SetRelationships without the maps (constructor 1): Relate dereferences the null maps only
// for a definition with both final topologies (the others are skipped with a warning).
bool set_rels_maps_ok(SmProtoTopologyManager* mgr, int64_t ctor_kind)
{
if (ctor_kind == 0)
return true;
SmTArray<SmVertexDefinition*> vds;
mgr->GetAllVertexDefinitions(vds);
for (ULONG i = 0; i < vds.GetSize(); i++)
if (vds[i]->GetFinalVertex(1) != nullptr && vds[i]->GetFinalVertex(2) != nullptr)
return false;
SmTArray<SmEdgeDefinition*> eds;
mgr->GetAllEdgeDefinitions(eds);
for (ULONG i = 0; i < eds.GetSize(); i++)
if (eds[i] != nullptr && eds[i]->GetFinalEdge(1) != nullptr && eds[i]->GetFinalEdge(2) != nullptr)
return false;
return true;
}
// ImprintIntersections (SmTopologyIntersector.cpp:2302-2413, upstream commit c9c5979;
// byte-identical in v1.0.1 3fd5b95 at the same lines) VERBATIM, apart from the SM_DEBUG_CODE
// blocks and the members reached through accessors,
// plus one addition: `edited` answers whether a failing imprint may have edited a B-rep,
// as the port's ImprintOutcome::failure_edited does. A vertex definition's imprint and the
// glue edit first; an edge definition's imprint edits nothing when it stops before its
// first MakeEdgeInFace (a NULL final vertex or source face, SmProtoTopology.cpp:1375-1445)
// or when a MakeEdgeInFace stops at its const FindVertexSector checks, which the addition
// tests with the same const calls beforehand (ed_fails_unedited).
bool ed_stops_before_edit(SmEdgeDefinition* pED)
{
SmProtoVertex *pStartPV = pED->GetStartProtoVertex();
SmProtoVertex *pEndPV = pED->GetEndProtoVertex();
if ( pStartPV->GetFinalVertex( 1, pED, TRUE ) == NULL || pEndPV->GetFinalVertex( 1, pED, FALSE ) == NULL
|| pStartPV->GetFinalVertex( 2, pED, TRUE ) == NULL || pEndPV->GetFinalVertex( 2, pED, FALSE ) == NULL )
{ return true; }
SmPointClassificationType eType1 = pED->GetSrcTopo_TYPE( 1 );
SmPointClassificationType eType2 = pED->GetSrcTopo_TYPE( 2 );
if ( eType1 == SM_PC_FACE )
{ return SM_CAST_PTR( SmFace, pED->GetSrcTopo( 1 ) ) == NULL; }
if ( eType2 == SM_PC_FACE )
{ return SM_CAST_PTR( SmFace, pED->GetSrcTopo( 2 ) ) == NULL; }
return true;
}
// The addition's member access: SmEdgeDefinition's intersection UV curves and interval.
struct EdUv1 { typedef SmCurve* SmEdgeDefinition::*type; friend type ti_get(EdUv1); };
struct EdUv2 { typedef SmCurve* SmEdgeDefinition::*type; friend type ti_get(EdUv2); };
struct EdIvl { typedef SmExtent1d SmEdgeDefinition::*type; friend type ti_get(EdIvl); };
template struct TiRob<EdUv1, &SmEdgeDefinition::m_pXSectUVCurve1>;
template struct TiRob<EdUv2, &SmEdgeDefinition::m_pXSectUVCurve2>;
template struct TiRob<EdIvl, &SmEdgeDefinition::m_pXSectInterval>;
// Whether SmBrep::MakeEdgeInFace (SmBrep.cpp:13770) stops at one of its two const
// FindVertexSector checks (status or NER(pStartVU) / NER(pEndVU), :13845-13873), before
// MakeEdgeInFaceWithVU edits the B-rep: the same calls with the same arguments.
bool mefi_stops_at_sector(SmBrep* pBrep, SmFace* pFace, SmVertex* pStartVertex, SmVertex* pEndVertex,
const SmCurve* p3DCurve, SmCurve* pUV, const SmExtent1d& crInterval)
{
if ( pFace == NULL || p3DCurve == NULL ) { return false; }
SmBSplineCurve *pUVCurve = SM_CAST_PTR( SmBSplineCurve, pUV ) ;
SmVertexuse *pStartVU = NULL, *pEndVU = NULL;
SmBoolean bPossibleProblem = FALSE;
SmClassifyProblem eProblem = SM_CP_NONE;
double dTStart = crInterval.GetMin(), dTEnd = crInterval.GetMax();
if ( pFace->FindVertexSector( pStartVertex, *p3DCurve, pUVCurve, dTStart, dTEnd, SM_OT_SAME,
pBrep->GetTolerance(), FALSE, pStartVU, bPossibleProblem, eProblem ) != SM_SUCCESS
|| pStartVU == NULL )
{ return true; }
if ( pFace->FindVertexSector( pEndVertex, *p3DCurve, pUVCurve, dTEnd, dTStart, SM_OT_OPPOSITE,
pBrep->GetTolerance(), FALSE, pEndVU, bPossibleProblem, eProblem ) != SM_SUCCESS
|| pEndVU == NULL )
{ return true; }
return false;
}
// Whether an edge definition's imprint that fails stops without leaving a B-rep half edited:
// before its first MakeEdgeInFace (ed_stops_before_edit), or at a MakeEdgeInFace's
// FindVertexSector checks (SmProtoTopology.cpp:1462 for Brep1, :1538 for Brep2). Brep2's
// checks read only Brep2, which Brep1's MakeEdgeInFace does not touch, and an edge made in
// Brep1 is whole, so both are decided beforehand.
bool ed_fails_unedited(SmEdgeDefinition* pED)
{
if ( ed_stops_before_edit( pED ) ) { return true; }
SmProtoVertex *pStartPV = pED->GetStartProtoVertex();
SmProtoVertex *pEndPV = pED->GetEndProtoVertex();
SmProtoTopologyManager *pMgr = pED->GetProtoTopologyManager();
const SmExtent1d& rIvl = pED->*ti_get(EdIvl());
if ( pED->GetSrcTopo_TYPE( 1 ) == SM_PC_FACE
&& mefi_stops_at_sector( pMgr->GetWhichBrep( 1 ), SM_CAST_PTR( SmFace, pED->GetSrcTopo( 1 ) ),
pStartPV->GetFinalVertex( 1, pED, TRUE ), pEndPV->GetFinalVertex( 1, pED, FALSE ),
pED->GetXSectCurve3d(), pED->*ti_get(EdUv1()), rIvl ) )
{ return true; }
if ( pED->GetSrcTopo_TYPE( 2 ) == SM_PC_FACE
&& mefi_stops_at_sector( pMgr->GetWhichBrep( 2 ), SM_CAST_PTR( SmFace, pED->GetSrcTopo( 2 ) ),
pStartPV->GetFinalVertex( 2, pED, TRUE ), pEndPV->GetFinalVertex( 2, pED, FALSE ),
pED->GetXSectCurve3d(), pED->*ti_get(EdUv2()), rIvl ) )
{ return true; }
return false;
}
SmStatus ii_verbatim(SmTopologyIntersector* ti, bool& edited)
{
SmProtoTopologyManager*& m_pProtoTopoMgr = ti->*ti_get(TiMgr());
SmBrep* m_pBrep = ti->GetPrimaryBrep();
SmBrep* m_pOther = ti->GetOtherBrep();
edited = false;
SmStatus eStat, eRetStat = SM_SUCCESS;
SmTemporaryChangeValue <SmBoolean> sTempChange1( m_pBrep ->m_bEditingEnabled, TRUE );
SmTemporaryChangeValue <SmBoolean> sTempChange2( m_pOther->m_bEditingEnabled, TRUE );
const SmContext *cpContext = m_pBrep->GetContext();
SmTemporaryChangeValue <SmBoolean> sTempChangeDB( ((SmContext*)cpContext)->GetDoingBooleanRef(), TRUE );
SmTArray< SmProtoVertex* > rPVs = m_pProtoTopoMgr->GetProtoVertices();
ULONG ii, jj, lNumObjs = rPVs.GetSize();
for(ii=0;ii<lNumObjs;ii++)
{
SmProtoVertex *pPV = rPVs[ii];
SmTArray< SmVertexDefinition* > & rVDs = pPV->GetVertexDefinitions();
for(jj=0;jj<rVDs.GetSize();jj++)
{
SmVertexDefinition *pVD = rVDs[jj];
eStat = pVD->Imprint();
if ( eStat != SM_SUCCESS ) { edited = true; } // (addition)
if ( eStat != SM_SUCCESS && eRetStat == SM_SUCCESS ) // Record the first problem.
{ eRetStat = eStat; }
}
}
SmTArray< SmProtoEdge* > rPEs = m_pProtoTopoMgr->GetProtoEdges();
lNumObjs = rPEs.GetSize();
for(ii=0;ii<lNumObjs;ii++)
{
SmProtoEdge *pPE = rPEs[ii];
SmTArray< SmEdgeDefinition* > & rEDs = pPE->GetEdgeDefinitions();
for(jj=0;jj<rEDs.GetSize();jj++)
{
SmEdgeDefinition *pED = rEDs[jj];
bool bStopsBeforeEdit = ed_fails_unedited( pED ); // (addition)
eStat = pED->Imprint();
if(eStat != SM_SUCCESS)
{
if ( !bStopsBeforeEdit ) { edited = true; } // (addition)
if(eRetStat == SM_SUCCESS) // Record the first problem.
{ eRetStat = eStat; }
pPE->RemoveEdgeDefinition( pED );
pED->Destruct();
delete pED; pED = NULL;
jj--;
}
}
}
eStat = m_pProtoTopoMgr->GlueCoincidentBrepVertices();
if ( eStat != SM_SUCCESS ) { edited = true; } // (addition)
if(eStat != SM_SUCCESS && eRetStat == SM_SUCCESS) // Record the first problem.
{ eRetStat = eStat; }
return eRetStat;
}
// IntersectInsertRelate (SmTopologyIntersector.cpp:1092-1270, upstream commit c9c5979;
// byte-identical in v1.0.1 3fd5b95 at the same lines) VERBATIM, apart from the
// SM_VALIDATE_TOPOLOGY, SM_DEBUG_CODE and SM_USE_TBB blocks (not defined in the reference
// build) and the members reached through accessors, plus one addition: it stops after a
// failed ImprintIntersections (UB-242) whose failure may have edited a B-rep (ii_verbatim
// above), where upstream goes on with half-imprinted B-reps, and sets `ub` (the port
// answers InvalidInput there). v1.0.1's ~SmProtoTopologyManager cuts the proto vertex /
// proto edge links before deleting them, so the teardown is defined (UB-175 is a v1.0.0
// defect) and the copy deletes the manager as upstream does. The real
// IntersectInsertRelate is no longer exercised by this difftest, so re-diff this copy
// against upstream whenever the pinned commit moves.
SmStatus iir_verbatim(SmTopologyIntersector* ti, bool& ub)
{
SmProtoTopologyManager*& m_pProtoTopoMgr = ti->*ti_get(TiMgr());
SmBrep* m_pBrep = ti->GetPrimaryBrep();
SmBrep* m_pOther = ti->GetOtherBrep();
// Capture Distance
double dCaptureDistance = 0.01;
// Make sure it's not smaller than our tolerance value.
double dTopoIntTol = ti->GetThisApproxTol3d();
if ( dCaptureDistance < dTopoIntTol * 2000 )
{ dCaptureDistance = dTopoIntTol * 2000; }
m_pProtoTopoMgr = new( m_pBrep ) SmProtoTopologyManager( m_pBrep, m_pOther, ti, dCaptureDistance );
SmStatus eStat = ti->IntersectTopology();
SER_MSG( eStat, _T("\nSmProtoExecutive: Failure returned from IntersectTopology().\n") );
eStat = ti->ResolveIntersections();
SER_MSG( eStat, _T("\nSmProtoExecutive: Failure returned from ResolveProtoTopology().\n") );
// Imprint Intersections, now we'll modify the Breps.
bool bEdited = false;
eStat = ii_verbatim( ti, bEdited );
// If that didn't work, inform the public
SE_MSG( eStat, _T("\nSmProtoExecutive: Error returned from ImprintIntersections().\n") );
// (addition, UB-242: upstream goes on to SetRelationships with the half-imprinted
// B-reps; stop here as the port does, deleting the manager as below, and answer -2)
if (eStat != SM_SUCCESS && bEdited)
{
if(m_pProtoTopoMgr) { delete m_pProtoTopoMgr ; m_pProtoTopoMgr = NULL; }
ub = true;
return eStat;
}
// Set relationship maps.
eStat = ti->SetRelationships();
// all done - clean up and exit
if(m_pProtoTopoMgr) { delete m_pProtoTopoMgr ; m_pProtoTopoMgr = NULL; }
return eStat;
}
} // namespace
namespace
{
bool run_extra_op(int64_t op, Rd& rd, Two& t, SmTopologyIntersector* ti, SmProtoTopologyManager*& mgr,
int64_t ctor_kind, std::vector<double>& o)
{
const SmContext& ctx = the_context();
switch (op)
{
case S_REMOVE_REL:
{
SmTopology* a = (SmTopology*)rd_h(rd, t);
SmTopology* b = (SmTopology*)rd_h(rd, t);
// (IsMatedPair compares pointers only; without maps nothing is mated)
o.push_back(st(ti->RemoveRelationship(a, b)));
return true;
}
case S_SQUEEZE:
{
SmVertex* bv = (SmVertex*)rd_h(rd, t);
SmVertex* ov = (SmVertex*)rd_h(rd, t);
if (!is_vertex(bv) || !is_vertex(ov) || !vertex_mate_ok(ti->GetOtherMate(bv)) ||
!squeeze_breps_ok(ti, bv, ov))
{
o.push_back(ST_NULL_DEREF);
return true;
}
SmTArray<SmVertex*> dv, sv;
SmTArray<SmEdge*> de;
SmStatus s = ti->CheckForSqueezeVertices(bv, ov, dv, sv, de);
o.push_back(st(s));
if (s != SM_SUCCESS)
return true;
o.push_back((double)dv.GetSize());
o.push_back((double)sv.GetSize());
o.push_back((double)de.GetSize());
return true;
}
case S_DELETE_FACES:
{
SmTArray<SmFace*> faces;
rd_list(rd, t, faces);
bool has_keep = rd.flag();
SmTArray<SmEdge*> keep;
rd_list(rd, t, keep);
bool ok = true;
for (ULONG i = 0; i < faces.GetSize(); i++)
if (faces[i] == nullptr)
ok = false;
if (!ok)
{
o.push_back(ST_NULL_DEREF);
return true;
}
o.push_back(st(ti->DeleteFaces(faces, has_keep ? &keep : nullptr)));
return true;
}
case S_FIX_GAPS:
{
SmTArray<SmVertex*> verts;
rd_list(rd, t, verts);
bool ok = true;
for (ULONG i = 0; i < verts.GetSize(); i++)
if (!is_vertex(verts[i]) || !vertex_edges_ok(verts[i]))
ok = false;
if (ok && !fix_gaps_first_candidate_ok(ti, verts))
ok = false;
if (!ok)
{
o.push_back(ST_NULL_DEREF);
return true;
}
SmBoolean modified = FALSE;
SmTArray<SmVertex*> dv;
SmTArray<SmEdge*> de;
SmStatus s = ti->FixGaps(verts, modified, dv, de);
o.push_back(st(s));
if (s != SM_SUCCESS)
return true;
o.push_back(flg(modified));
o.push_back((double)dv.GetSize());
o.push_back((double)de.GetSize());
return true;
}
case S_INTERSECT_TOPO:
case S_SET_RELS:
{
// (IntersectTopology reads both B-reps' faces; SetRelationships' Relate needs the maps)
if (mgr == nullptr || (op == S_INTERSECT_TOPO && (ti->GetPrimaryBrep() == nullptr || ti->GetOtherBrep() == nullptr)) ||
(op == S_SET_RELS && !set_rels_maps_ok(mgr, ctor_kind)))
{
o.push_back(ST_NULL_DEREF);
return true;
}
SmStatus s = (op == S_INTERSECT_TOPO) ? ti->IntersectTopology() : ti->SetRelationships();
o.push_back(st(s));
o.push_back((double)mgr->GetProtoVertices().GetSize());
o.push_back((double)mgr->GetProtoEdges().GetSize());
o.push_back((double)mgr->GetProtoFaces().GetSize());
return true;
}
case S_IIR:
{
if (ctor_kind != 0 || ti->GetPrimaryBrep() == nullptr || ti->GetOtherBrep() == nullptr)
{
o.push_back(ST_NULL_DEREF);
return true;
}
// (the manager IntersectInsertRelate makes replaces the installed one, which stays
// owned by the script; it is the script's last op)
bool ub = false;
SmStatus s = iir_verbatim(ti, ub);
o.push_back(ub ? ST_NULL_DEREF : st(s));
return true;
}
case S_FIXUP_CLASS:
case S_FIXUP_SPLIT:
{
int64_t index = (op == S_FIXUP_SPLIT) ? rd.idx() : 0;
SmCurveClassification *cb = nullptr, *co = nullptr;
bool ok_b = read_cc(rd, t, ctx, cb);
bool ok_o = read_cc(rd, t, ctx, co);
struct Del
{
SmCurveClassification*& a;
SmCurveClassification*& b;
~Del()
{
delete a;
delete b;
}
} del{cb, co};
if (!ok_b || !ok_o || (op == S_FIXUP_SPLIT && ((ULONG)index >= cb->GetSize() || (ULONG)index >= co->GetSize())))
{
o.push_back(ST_NULL_DEREF);
return true;
}
SmBoolean changed = FALSE;
SmStatus s = (op == S_FIXUP_CLASS) ? ti->FixupClassMates(*cb, *co)
: ti->FixupSplitVertexPair((ULONG)index, *cb, *co, changed);
o.push_back(st(s));
if (s != SM_SUCCESS)
return true;
if (op == S_FIXUP_SPLIT)
o.push_back(flg(changed));
put_cc(o, t, *cb);
put_cc(o, t, *co);
return true;
}
case S_EDGE_EDGE:
{
SmTArray<SmEdge*> e1, e2;
rd_list(rd, t, e1);
rd_list(rd, t, e2);
SmBoolean manifold = rd.bflag();
SmBoolean ignore = rd.bflag();
bool ok = true;
for (ULONG i = 0; i < e1.GetSize(); i++)
if (e1[i] != nullptr && !edge_ok(e1[i]))
ok = false;
for (ULONG i = 0; i < e2.GetSize(); i++)
if (e2[i] != nullptr && !edge_ok(e2[i]))
ok = false;
if (!ok)
{
o.push_back(ST_NULL_DEREF);
return true;
}
SmTArray<SmEdge*> s1, n1, s2, n2;
SmTArray<SmVertex*> nv;
double mx = 0.0, mn = 0.0;
SmStatus s = ti->EdgeEdgeIntersect(e1, e2, s1, n1, s2, n2, nv, mx, mn, manifold, ignore);
o.push_back(st(s));
if (s != SM_SUCCESS)
return true;
put_list(o, t, as_objs(s1));
o.push_back((double)n1.GetSize());
put_list(o, t, as_objs(s2));
o.push_back((double)n2.GetSize());
o.push_back((double)nv.GetSize());
o.push_back(mx);
o.push_back(mn);
return true;
}
case S_EDGE_FACE_COIN:
{
SmTArray<SmVertex*> verts;
SmTArray<SmEdge*> excl;
rd_list(rd, t, verts);
rd_list(rd, t, excl);
SmBoolean ignore = rd.bflag();
SmBoolean nesting = rd.bflag();
bool has_null = false;
for (ULONG i = 0; i < verts.GetSize(); i++)
if (verts[i] == nullptr)
has_null = true;
bool ok = true;
for (ULONG i = 0; i < verts.GetSize(); i++)
{
if (verts[i] == nullptr)
continue;
if (!is_vertex(verts[i]) || !vertex_edges_ok(verts[i]) || !vertex_faces_ok(verts[i]))
{
ok = false;
continue;
}
// a null other vertex matches a null entry of the list and is then dereferenced
SmTArray<SmEdge*> es;
verts[i]->GetEdges(es);
for (ULONG j = 0; j < es.GetSize(); j++)
if (has_null && es[j]->GetOtherVertex(verts[i]) == nullptr)
ok = false;
}
if (!ok)
{
o.push_back(ST_NULL_DEREF);
return true;
}
SmTArray<SmEdge*> ie;
SmTArray<SmFace*> pf, nf, sf;
double mx = 0.0, mn = 0.0;
SmStatus s = ti->EdgeFaceCoincidence(verts, excl, ie, pf, nf, sf, mx, mn, ignore, nesting);
o.push_back(st(s));
if (s != SM_SUCCESS)
return true;
put_list(o, t, as_objs(ie));
put_list(o, t, as_objs(pf));
o.push_back((double)nf.GetSize());
put_list(o, t, as_objs(sf));
o.push_back(mx);
o.push_back(mn);
return true;
}
case S_CLOSE_LOOP:
{
SmVertex* bv = (SmVertex*)rd_h(rd, t);
if (bv != nullptr && (!is_vertex(bv) || !close_loop_edges_ok(ti, bv)))
{
o.push_back(ST_NULL_DEREF);
return true;
}
SmBoolean made = FALSE;
SmTArray<SmVertex*> dv;
SmTArray<SmEdge*> de;
SmStatus s = ti->CloseIntersectionLoop(bv, made, dv, de);
o.push_back(st(s));
if (s != SM_SUCCESS)
return true;
o.push_back(flg(made));
// the counts of the vertices and edges its squeezes deleted
o.push_back((double)dv.GetSize());
o.push_back((double)de.GetSize());
// the relationships it made: the other mate of each edge of the vertex (op 6
// cannot follow an op that may change topology)
SmTArray<SmEdge*> es;
bv->GetEdges(es);
o.push_back((double)es.GetSize());
for (ULONG i = 0; i < es.GetSize(); i++)
t.put(o, ti->GetOtherMate(es[i]));
return true;
}
default: return false;
}
}
} // namespace