Files
solvecraft/harness/cpp/shim/offset_offset_geometry_creation.cpp
T
Christian Svensson b69ff1c8cc offset.offset_geometry_creation: first complete difftest (398 pass, 0 fail, 463 deferred)
Hang guards: HANG-19 (negative offset distance makes CreateExtendedSurface's
doubling loop unbounded) as an exact dist < 0 term beside the measured closed-face
proxy for HANG-17. Local pairs name store surfaces only (a face's own surface
was deleted by the analytic copy and double-freed: three segfaults), resolved
at each local op. Output rule on both sides: out-parameters after a success
status only, existing-topology states after every status. A library
InvalidInput maps to upstream's 1007. UV trim curves of every B-spline kind
(SM_CAST_PTR), the ruled-plane fallback for any non-deferral failure, the
original arc kept across the plane tests of VertexOffset, and WR-103 in
nurbs.b_spline_surface_p2 (rOrigin aliases sRefFrame). MAX_INDEX 4096.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBgcxze64BBVRNjGewaEuN
2026-10-17 08:12:19 +02:00

976 lines
35 KiB
C++

// SPDX-License-Identifier: Apache-2.0
// difftest shim for unit `offset.offset_geometry_creation`: SmOffsetGeometryCreation and
// SmLocalOperationGeometryCreation (source/SMLib/src/SmOffsetGeometryCreation.cpp) on two
// real SmBreps (the "original" and the "offset" B-rep of an SmOffsetExecutive).
//
// One entry point, sc_offset_offset_geometry_creation_eval(op, path, in, n_in, out, cap,
// out_len). Layouts are documented in
// crates/solidcore-ffi/src/units/offset_offset_geometry_creation.rs, which validates every
// input. The B-rep construction, traversal and export are those of
// harness/cpp/shim/sc_topology_fixture.h (two B-rep blocks, as the
// `boolean.topology_intersector_p1` shim).
//
// A RUN call makes an SmOffsetGeometryCreation (and, with a face/surface list, an
// SmLocalOperationGeometryCreation), an SmOffsetExecutive over the first B-rep whose
// flags, shell faces and original->offset edge-use relation (`m_vEUToEU`, filled through
// pointer-to-member access with the pairs given) come from the input, locks a mark, and
// runs a script: fixture edits (store curves/surfaces, replace an edge's curve, set an
// edge use's UV trim curve, mark an edge use) and the unit's functions. Each op emits its
// code, a status (0 success, the SmStatus code, or -2 when the shim refused the op
// because upstream would dereference null) and its outputs; the objects the unit makes
// are emitted as geometry (`put_curve` of the fixture header, surfaces as their type, UV
// domain and a 3x3 point sample), the B-rep edits as edge / edge-use state blocks.
#include "sc_topology_fixture.h"
#include <SmOffsetExecutive.h>
#include <SmOffsetGeometryCreation.h>
#include <SmRelation.h>
#include <SmSSIData.h>
#include <cstdlib>
#include <string>
#include <array>
namespace
{
// ---- private member access (pointer-to-member through explicit instantiation) -------------
template <typename Tag, typename Tag::type M>
struct ExRob
{
friend typename Tag::type ex_get(Tag) { return M; }
};
struct ExMap
{
typedef SmRelation<SmEdgeuse, SmEdgeuse> SmOffsetExecutive::*type;
friend type ex_get(ExMap);
};
template struct ExRob<ExMap, &SmOffsetExecutive::m_vEUToEU>;
enum OgOp : int32_t
{
OG_EXPORT = 0,
OG_RUN = 1,
};
// Script op codes.
enum OgScriptOp : int64_t
{
S_ADD_CURVE = 1,
S_ADD_SURFACE = 2,
S_SET_EDGE_CURVE = 3,
S_SET_EU_UV = 4,
S_MARK = 5,
S_GETTERS = 6,
S_FACEUSE_OFFSET = 7,
S_VERTEX_OFFSET = 8,
S_UPDATE_FACE_DOMAIN = 9,
S_REPLACE_EDGE_CURVES = 10,
S_TRIM_EXTENDED_EDGE = 11,
S_EXTEND_TRIM_EDGES = 12,
S_MATE_EDGES = 13,
S_HAVE_TANGENT_GAPS = 14,
S_EDGEUSE_OFFSET = 15,
S_LOCAL_FACEUSE_OFFSET = 16,
S_EDGE_STATE = 17,
S_SET_EDGE_TOL = 18,
};
// Handle kinds (`kind` of a `brep kind index` triple).
enum HKind : int64_t
{
H_VERTEX = 0,
H_EDGE = 1,
H_FACE = 2,
H_EDGE_CURVE = 3,
H_FACE_SURFACE = 4,
H_CURVE = 5, // the script's curve store
H_BREP = 6,
H_SURFACE = 7, // the script's surface store
H_NULL = 8,
H_EDGEUSE = 9,
H_FACEUSE = 10,
H_EU_UV = 11, // the UV trim curve stored on an edge use
};
struct Two
{
SmBrep* brep[2] = {nullptr, nullptr};
Graph g[2];
std::vector<SmBSplineCurve*> curves;
std::vector<SmSurface*> surfaces;
SmObject* resolve(int64_t b, int64_t kind, int64_t i) const
{
if (kind == H_NULL || i < 0)
return nullptr;
if (kind == H_BREP)
return (b == 0 || b == 1) ? (SmObject*)brep[b] : nullptr;
if (kind == H_CURVE)
return (i < (int64_t)curves.size()) ? (SmObject*)curves[(size_t)i] : nullptr;
if (kind == H_SURFACE)
return (i < (int64_t)surfaces.size()) ? (SmObject*)surfaces[(size_t)i] : nullptr;
if (b != 0 && b != 1)
return nullptr;
const Graph& gr = g[b];
auto at = [i](auto& v) -> SmTopology* { return (i >= 0 && i < (int64_t)v.size()) ? (SmTopology*)v[(size_t)i] : nullptr; };
switch (kind)
{
case H_VERTEX: return at(gr.vertices);
case H_EDGE: return at(gr.edges);
case H_FACE: return at(gr.faces);
case H_EDGEUSE: return at(gr.edgeuses);
case H_FACEUSE: return at(gr.faceuses);
case H_EDGE_CURVE:
{
SmEdge* e = (SmEdge*)at(gr.edges);
return e ? (SmObject*)e->GetCurve() : nullptr;
}
case H_FACE_SURFACE:
{
SmFace* f = (SmFace*)at(gr.faces);
return f ? (SmObject*)f->GetSurface() : nullptr;
}
case H_EU_UV:
{
SmEdgeuse* eu = (SmEdgeuse*)at(gr.edgeuses);
return eu ? (SmObject*)eu->GetUVTrimCurvePointer() : nullptr;
}
default: return nullptr;
}
}
};
const double ST_OK = 0.0;
// Status of an op the shim refuses because upstream would not return (HANG-17): the
// extended surface of SmOffsetExecutive::GetCreateExtendedSurface (ST_surxsdr's
// exponential split for surfaces whose iso-curves are short relative to 5 * offset).
const double ST_HANG = -5.0;
// Whether the hang guard is active (SC_OG_NO_HANG_GUARD=1 disables it: a measurement
// switch for the over-rejection figures in plan/findings.md, never set by the runner).
bool hang_guard_on()
{
static int v = -1;
if (v < 0)
v = getenv("SC_OG_NO_HANG_GUARD") ? 0 : 1;
return v == 1;
}
// The HANG-17 trigger: a face surface closed in U or V (its iso-curves are short relative
// to the five-offset extension distance).
bool closed_face_surface(const SmFace* f)
{
if (f == nullptr || f->GetSurface() == nullptr)
return false;
const SmSurface* s = f->GetSurface();
SmExtent2d d = s->GetNaturalUVDomain();
return s->IsClosed(d, SM_SP_U) || s->IsClosed(d, SM_SP_V);
}
bool closed_offset_face(const SmEdgeuse* eu) { return eu != nullptr && closed_face_surface(eu->GetFace()); }
// The HANG-19 trigger: a negative offset distance (dDist = 5 * offset < 0) makes
// SmBSplineSurface::CreateExtendedSurface's doubling loop (`while (dExtDist < dDist / 2.0)`)
// unbounded on any surface, planes included; `dist < 0.0` is tested beside the closed-face
// test at every site that would extend a surface.
// Whether TrimExtendedEdge would extend a closed neighbouring face's surface (HANG-17) or
// any neighbour's with a negative offset distance (HANG-19): the neighbour classification
// of its two ends, replicated.
bool would_extend_closed_neighbour(SmOffsetExecutive& exec, double dist, const SmEdgeuse* base, const SmEdgeuse* other_base)
{
double tol_deg = exec.GetTangencyTolDegrees();
SmEdge* base_edge = base->GetEdge();
for (int i = 0; i < 2; i++)
{
SmVertex* base_vertex = (i == 0) ? base->GetVertexuse()->GetVertex() : base->GetMate()->GetVertexuse()->GetVertex();
const SmEdgeuse* nbrs[2];
const SmEdgeuse* owners[2];
nbrs[0] = (i == 0) ? base->GetCWEdgeuse() : base->GetCCWEdgeuse();
owners[0] = base;
nbrs[1] = other_base ? ((i == 0) ? other_base->GetCCWEdgeuse() : other_base->GetCWEdgeuse()) : nullptr;
owners[1] = other_base;
for (int k = 0; k < 2; k++)
{
const SmEdgeuse* n = nbrs[k];
if (n == nullptr || n == owners[k])
continue;
SmEdge* n_edge = n->GetEdge();
if (n_edge->IsLamina() || n->IsTangentSector(tol_deg))
continue; // the tangent lists: no extension
SmVertex* v = (k == 0) ? base_vertex : base_edge->GetOtherVertex(base_vertex);
SmEdge* e = (k == 0) ? base_edge : other_base->GetEdge();
if (v == nullptr)
continue;
if (!v->AreEdgesTangent(e, n_edge, 1.0))
{
SmEdgeuse* adj = n->GetRadial();
SmEdgeuse* off_adj = adj ? exec.GetOffsetEdgeuse(adj) : nullptr;
if (off_adj && (dist < 0.0 || closed_offset_face(off_adj)))
return true;
}
}
}
return false;
}
bool eu_ok(SmEdgeuse* eu)
{
if (eu == nullptr || eu->GetEdge() == nullptr || eu->GetEdge()->GetCurve() == nullptr)
return false;
if (eu->GetVertexuse() == nullptr || eu->GetVertexuse()->GetVertex() == nullptr)
return false;
if (eu->GetMate() == nullptr || !eu->IsLoopEdgeuse())
return false;
if (eu->GetCWEdgeuse() == nullptr || eu->GetCCWEdgeuse() == nullptr)
return false;
if (eu->GetFaceuse() == nullptr || eu->GetFace() == nullptr || eu->GetFace()->GetSurface() == nullptr)
return false;
return true;
}
// Whether every edge use of the B-rep is a loop edge use with edge (with curve), vertex,
// mate, CW/CCW neighbours and a face with surface, and every edge has a vertex: what the
// topology-walking functions dereference without checking.
bool brep_ok(SmBrep* brep, Graph& g)
{
if (brep == nullptr)
return false;
for (SmEdgeuse* eu : g.edgeuses)
if (!eu_ok(eu))
return false;
for (SmEdge* e : g.edges)
if (e->GetVertex() == nullptr || e->GetCurve() == nullptr)
return false;
return true;
}
SmObject* rd_h(Rd& rd, const Two& t)
{
int64_t b = rd.idx();
int64_t kind = rd.idx();
int64_t i = rd.idx();
return t.resolve(b, kind, i);
}
void put_srf(std::vector<double>& o, const SmSurface* s)
{
if (s == nullptr)
{
o.push_back(-1.0);
return;
}
o.push_back((double)s->GetType());
SmExtent2d d = s->GetNaturalUVDomain();
o.push_back(d.GetMin().x);
o.push_back(d.GetMax().x);
o.push_back(d.GetMin().y);
o.push_back(d.GetMax().y);
for (int i = 0; i <= 2; i++)
for (int j = 0; j <= 2; j++)
{
SmPoint2d uv(d.GetMin().x + (d.GetMax().x - d.GetMin().x) * ((double)i / 2.0),
d.GetMin().y + (d.GetMax().y - d.GetMin().y) * ((double)j / 2.0));
SmPoint3d p;
SmStatus st_e = s->EvaluatePoint(uv, p);
if (st_e == SM_SUCCESS)
{
o.push_back(0.0);
put_vec(o, p);
}
else
o.push_back(st(st_e));
}
}
void put_orients(std::vector<double>& o, const SmTArray<SmOrientType>& v)
{
o.push_back((double)v.GetSize());
for (ULONG k = 0; k < v.GetSize(); k++)
o.push_back((double)v[k]);
}
void put_curves(std::vector<double>& o, const SmTArray<SmCurve*>& v)
{
o.push_back((double)v.GetSize());
for (ULONG k = 0; k < v.GetSize(); k++)
put_curve(o, v[k]);
}
// `curve t0 t1 tolerance vertex_point other_vertex_point n_edgeuses uv_trim_curve*`.
void put_edge_state(std::vector<double>& o, SmEdge* e)
{
put_curve(o, e->GetCurve());
SmExtent1d ivl = e->GetInterval();
o.push_back(ivl.GetMin());
o.push_back(ivl.GetMax());
o.push_back((double)e->GetTolerance());
SmVertex* v = e->GetVertex();
if (v)
{
o.push_back(1.0);
put_vec(o, v->GetPoint());
SmVertex* w = e->GetOtherVertex(v);
if (w)
{
o.push_back(1.0);
put_vec(o, w->GetPoint());
}
else
o.push_back(0.0);
}
else
o.push_back(0.0);
SM_PTR_ARRAY(eus, SmEdgeuse, 16);
e->GetEdgeuses(eus);
o.push_back((double)eus.GetSize());
for (ULONG k = 0; k < eus.GetSize(); k++)
put_curve(o, eus[k]->GetUVTrimCurve());
}
template <class T> void delete_curves(SmTArray<T*>& v)
{
for (ULONG k = 0; k < v.GetSize(); k++)
delete v[k];
v.ReSet();
}
// Whether `ReplaceEdgeCurves(eu, c3, NULL, orig)` would reverse the curves (and dereference
// the null UV curve): the shape test of the function, replicated.
bool would_reverse(SmEdgeuse* eu, SmCurve* c3, SmEdgeuse* orig)
{
SmEdge* edge = eu->GetEdge();
SmCurve* euc = edge->GetCurve();
SmExtent1d ivl = edge->GetInterval();
double tol = edge->GetTolerance();
SmPoint3d es, ee, cs, ce;
euc->EvaluatePoint(ivl.GetMin(), es);
euc->EvaluatePoint(ivl.GetMax(), ee);
SmExtent1d civl = c3->GetNaturalInterval();
c3->EvaluatePoint(civl.GetMin(), cs);
c3->EvaluatePoint(civl.GetMax(), ce);
if (es.DistanceBetween(cs) < tol && ee.DistanceBetween(ce) < tol)
return false;
if (es.DistanceBetween(ce) < tol && ee.DistanceBetween(cs) < tol)
return false;
SmVector3d pv[2], pv2[2];
if (orig == nullptr)
euc->Evaluate(ivl.Evaluate(0.5), 1, TRUE, pv);
else
{
SmEdge* oe = orig->GetEdge();
oe->GetCurve()->Evaluate(oe->GetInterval().Evaluate(0.5), 1, TRUE, pv);
}
c3->Evaluate(civl.Evaluate(0.5), 1, TRUE, pv2);
return pv[1].Dot(pv2[1]) < 0.0;
}
bool build_two(Rd& rd, const char* path, Two& t, std::vector<double>& o)
{
std::string p(path);
std::string pa = p, pb;
size_t nl = p.find('\n');
if (nl != std::string::npos)
{
pa = p.substr(0, nl);
pb = p.substr(nl + 1);
}
SmStatus st_a;
t.brep[0] = build(rd, pa.c_str(), st_a);
if (t.brep[0] == nullptr)
{
o.push_back(-1.0);
o.push_back(st(st_a));
return false;
}
bool has_b = rd.flag();
if (has_b)
{
SmStatus st_b;
t.brep[1] = build(rd, pb.c_str(), st_b);
if (t.brep[1] == nullptr)
{
o.push_back(-3.0);
o.push_back(st(st_b));
return false;
}
}
walk(t.brep[0], t.g[0]);
if (t.brep[1])
walk(t.brep[1], t.g[1]);
return true;
}
void run_script(Rd& rd, Two& t, std::vector<double>& o)
{
const SmContext& ctx = the_context();
double dist = rd.num();
double approx = rd.num();
bool make_analytics = rd.flag();
SmOffsetGeometryCreation geom(dist, approx);
geom.SetMakeAnalytics(make_analytics ? TRUE : FALSE);
// the local-operation creation (its lists are the face/surface pairs given)
// The listed surfaces are store surfaces (the FFI refuses a face's own: the local
// creation owns them), named by handle; the store is filled by the script's ops, so
// the handles are resolved again before each local op (upstream copies the lists at
// construction; the local creation is rebuilt then). Its m_bMakeAnalytics stays
// FALSE, as the constructor leaves it (SetMakeAnalytics is never called on it), so
// FaceuseOffset returns the listed surface as is.
int64_t n_local = rd.idx();
SmTArray<SmFace*> local_faces(ctx);
SmTArray<SmSurface*> local_surfaces(ctx);
std::vector<std::array<int64_t, 3>> local_srf;
for (int64_t k = 0; k < n_local; k++)
{
SmFace* f = (SmFace*)rd_h(rd, t);
std::array<int64_t, 3> h;
h[0] = rd.idx();
h[1] = rd.idx();
h[2] = rd.idx();
local_faces.Add(f);
local_surfaces.Add((SmSurface*)t.resolve(h[0], h[1], h[2]));
local_srf.push_back(h);
}
SmLocalOperationGeometryCreation local(dist, approx, local_faces, local_surfaces);
// the executive
SmOffsetExecutive exec(ctx, SM_OO_SOLID_OFFSET, geom, t.brep[0]);
exec.SetInset(rd.bflag());
exec.SetExtendConvexEdges(rd.bflag());
int64_t n_shell = rd.idx();
if (n_shell > 0)
{
SmTArray<SmFace*> shell(ctx);
for (int64_t k = 0; k < n_shell; k++)
shell.Add((SmFace*)rd_h(rd, t));
exec.SetShellFaces(shell);
}
int64_t n_pairs = rd.idx();
for (int64_t k = 0; k < n_pairs; k++)
{
SmEdgeuse* a = (SmEdgeuse*)rd_h(rd, t);
SmEdgeuse* b = (SmEdgeuse*)rd_h(rd, t);
(exec.*ex_get(ExMap())).RelatePair(a, b);
}
// the mark the trim uses
SmNewMarkAndLock mark_lock(&ctx);
SmMarkType mark = mark_lock.GetMarkType();
bool ok[2] = {brep_ok(t.brep[0], t.g[0]), brep_ok(t.brep[1], t.g[1])};
bool all_ok = ok[0] && (t.brep[1] == nullptr || ok[1]);
int64_t n_ops = rd.idx();
for (int64_t k = 0; k < n_ops; k++)
{
int64_t op = rd.idx();
o.push_back((double)op);
switch (op)
{
case S_ADD_CURVE:
{
SmBSplineCurve* c = read_curve(rd, ctx);
t.curves.push_back(c);
o.push_back(ST_OK);
o.push_back((double)(t.curves.size() - 1));
break;
}
case S_ADD_SURFACE:
{
SmSurface* s = read_surface(rd, ctx);
t.surfaces.push_back(s);
o.push_back(ST_OK);
o.push_back((double)(t.surfaces.size() - 1));
break;
}
case S_SET_EDGE_CURVE:
{
SmEdge* e = (SmEdge*)rd_h(rd, t);
SmBSplineCurve* c = (SmBSplineCurve*)rd_h(rd, t);
if (e == nullptr || c == nullptr)
{
o.push_back(ST_NULL_DEREF);
break;
}
SmBSplineCurve* copy = new (ctx) SmBSplineCurve(*c);
e->SetCurve(copy, TRUE);
copy->SetOwner(e);
e->SetInterval(copy->GetNaturalInterval());
o.push_back(ST_OK);
put_edge_state(o, e);
break;
}
case S_SET_EU_UV:
{
SmEdgeuse* eu = (SmEdgeuse*)rd_h(rd, t);
SmBSplineCurve* c = (SmBSplineCurve*)rd_h(rd, t);
double max_dist = rd.num();
if (eu == nullptr)
{
o.push_back(ST_NULL_DEREF);
break;
}
SmBSplineCurve* copy = c ? new (ctx) SmBSplineCurve(*c) : nullptr;
eu->SetUVTrimCurve(copy, max_dist, TRUE);
o.push_back(ST_OK);
put_curve(o, eu->GetUVTrimCurve());
break;
}
case S_MARK:
{
SmEdgeuse* eu = (SmEdgeuse*)rd_h(rd, t);
if (eu == nullptr)
{
o.push_back(ST_NULL_DEREF);
break;
}
eu->Mark(mark);
o.push_back(ST_OK);
o.push_back(flg(eu->IsMarked(mark)));
break;
}
case S_GETTERS:
{
o.push_back(ST_OK);
o.push_back(geom.GetThisApproxTol3d());
o.push_back(geom.GetTangencyTolRadians());
o.push_back(geom.GetTangencyTolDegrees());
o.push_back(geom.GetOffsetDistance());
o.push_back(local.GetThisApproxTol3d());
o.push_back(local.GetTangencyTolDegrees());
o.push_back(local.GetOffsetDistance());
o.push_back(exec.GetTangencyTolDegrees());
o.push_back(flg(exec.GetInset()));
o.push_back(flg(exec.GetExtendConvexEdges()));
SmTArray<SmFace*> sh(ctx);
exec.GetShellFaces(sh);
o.push_back((double)sh.GetSize());
break;
}
case S_FACEUSE_OFFSET:
case S_LOCAL_FACEUSE_OFFSET:
{
SmFaceuse* fu = (SmFaceuse*)rd_h(rd, t);
bool skip = rd.flag();
if (fu == nullptr || fu->GetFace() == nullptr || fu->GetFace()->GetSurface() == nullptr)
{
o.push_back(ST_NULL_DEREF);
break;
}
ULONG lidx = 0;
bool lfound = false;
if (op == S_LOCAL_FACEUSE_OFFSET)
{
// the listed surfaces, as the store stands now
for (size_t k = 0; k < local_srf.size(); k++)
local_surfaces[(ULONG)k] = (SmSurface*)t.resolve(local_srf[k][0], local_srf[k][1], local_srf[k][2]);
// a listed face without surface (not added yet, or deleted) is dereferenced
// upstream
lfound = local_faces.FindElement(fu->GetFace(), lidx) ? true : false;
if (lfound && local_surfaces[lidx] == nullptr)
{
o.push_back(ST_NULL_DEREF);
break;
}
}
SmLocalOperationGeometryCreation local_now(dist, approx, local_faces, local_surfaces);
SmSurface* ns = nullptr;
SmSSIData ssi;
SmStatus s = (op == S_LOCAL_FACEUSE_OFFSET)
? local_now.FaceuseOffset(ctx, fu, skip ? TRUE : FALSE, ns, ssi)
: geom.FaceuseOffset(ctx, fu, skip ? TRUE : FALSE, ns, ssi);
o.push_back(st(s));
if (s == SM_SUCCESS) // the out-parameters: only after success
{
o.push_back(ns ? 1.0 : 0.0);
if (ns)
put_srf(o, ns);
o.push_back((double)ssi.m_v3DCurves.GetSize());
for (ULONG j = 0; j < ssi.m_v3DCurves.GetSize(); j++)
{
put_curve(o, ssi.m_v3DCurves[j]);
put_curve(o, j < ssi.m_vUVCurves1.GetSize() ? ssi.m_vUVCurves1[j] : nullptr);
put_curve(o, j < ssi.m_vUVCurves2.GetSize() ? ssi.m_vUVCurves2[j] : nullptr);
o.push_back(j < ssi.m_vCurveTypes.GetSize() ? (double)ssi.m_vCurveTypes[j] : -1.0);
o.push_back(j < ssi.m_vDeviations.GetSize() ? ssi.m_vDeviations[j] : -1.0);
}
}
if (op == S_LOCAL_FACEUSE_OFFSET && ns != nullptr)
{
// the listed store surface, returned as is (owned by the store already)
bool listed = false;
for (size_t q = 0; q < t.surfaces.size(); q++)
if (t.surfaces[q] == ns)
listed = true;
if (!listed)
t.surfaces.push_back(ns);
}
else
delete ns;
delete_curves(ssi.m_v3DCurves);
delete_curves(ssi.m_vUVCurves1);
delete_curves(ssi.m_vUVCurves2);
break;
}
case S_VERTEX_OFFSET:
{
SmVertex* v = (SmVertex*)rd_h(rd, t);
int64_t n = rd.idx();
SmTArray<SmEdge*> edges(ctx);
bool bad = false;
for (int64_t j = 0; j < n; j++)
{
SmEdge* e = (SmEdge*)rd_h(rd, t);
if (e == nullptr || e->GetCurve() == nullptr)
bad = true;
edges.Add(e);
}
if (!bad && n == 1)
{
SM_PTR_ARRAY(faces, SmFace, 8);
edges[0]->GetFaces(faces);
if (faces.GetSize() == 0 || faces[0]->GetSurface() == nullptr ||
!edges[0]->GetCurve()->IsKindOf(SmBSplineCurve_TYPE))
bad = true;
}
if (bad || !all_ok)
{
o.push_back(ST_NULL_DEREF);
break;
}
SmTArray<SmCurve*> curves(ctx);
SmTArray<SmOrientType> orients(ctx);
SmSurface* ns = nullptr;
SmStatus s = geom.VertexOffset(ctx, v, edges, curves, orients, ns);
o.push_back(st(s));
if (s == SM_SUCCESS) // the out-parameters: only after success
{
put_curves(o, curves);
put_orients(o, orients);
o.push_back(ns ? 1.0 : 0.0);
if (ns)
put_srf(o, ns);
}
delete_curves(curves);
delete ns;
break;
}
case S_UPDATE_FACE_DOMAIN:
{
SmFace* f = (SmFace*)rd_h(rd, t);
SmSurface* ext = (SmSurface*)rd_h(rd, t);
if (f == nullptr || f->GetSurface() == nullptr || ext == nullptr)
{
o.push_back(ST_NULL_DEREF);
break;
}
SmStatus s = geom.UpdateFaceSurfaceDomain(f, ext);
o.push_back(st(s));
SmExtent2d d = f->GetUVDomain();
o.push_back(d.GetMin().x);
o.push_back(d.GetMax().x);
o.push_back(d.GetMin().y);
o.push_back(d.GetMax().y);
put_srf(o, f->GetSurface());
break;
}
case S_REPLACE_EDGE_CURVES:
{
SmEdgeuse* eu = (SmEdgeuse*)rd_h(rd, t);
SmBSplineCurve* c3 = (SmBSplineCurve*)rd_h(rd, t);
SmBSplineCurve* uv = (SmBSplineCurve*)rd_h(rd, t);
SmEdgeuse* orig = (SmEdgeuse*)rd_h(rd, t);
bool bad = eu == nullptr || eu->GetEdge() == nullptr;
if (!bad && c3 != nullptr)
{
SmEdge* e = eu->GetEdge();
bad = e->GetCurve() == nullptr || eu->GetVertexuse() == nullptr ||
eu->GetVertexuse()->GetVertex() == nullptr || eu->GetMate() == nullptr ||
eu->GetMate()->GetVertexuse() == nullptr || eu->GetMate()->GetVertexuse()->GetVertex() == nullptr;
if (!bad && orig != nullptr)
bad = orig->GetEdge() == nullptr || orig->GetEdge()->GetCurve() == nullptr;
if (!bad && uv == nullptr && would_reverse(eu, c3, orig))
bad = true;
}
if (bad)
{
o.push_back(ST_NULL_DEREF);
break;
}
SmCurve* c3c = c3 ? new (ctx) SmBSplineCurve(*c3) : nullptr;
SmBSplineCurve* uvc = uv ? new (ctx) SmBSplineCurve(*uv) : nullptr;
SmBoolean del3 = FALSE, deluv = FALSE;
SmStatus s = geom.ReplaceEdgeCurves(eu, c3c, uvc, orig, del3, deluv);
o.push_back(st(s));
if (s == SM_SUCCESS) // the out-parameters: only after success
{
o.push_back(flg(del3));
o.push_back(flg(deluv));
}
put_edge_state(o, eu->GetEdge()); // the edge's state: after every status
if (del3)
delete c3c;
if (deluv)
delete uvc;
break;
}
case S_TRIM_EXTENDED_EDGE:
{
SmEdgeuse* base = (SmEdgeuse*)rd_h(rd, t);
SmEdgeuse* other_base = (SmEdgeuse*)rd_h(rd, t);
SmEdgeuse* off = (SmEdgeuse*)rd_h(rd, t);
SmEdgeuse* other_off = (SmEdgeuse*)rd_h(rd, t);
SmCurve* c3 = (SmCurve*)rd_h(rd, t);
SmBSplineCurve* uv_base = (SmBSplineCurve*)rd_h(rd, t);
SmBSplineCurve* uv_other = (SmBSplineCurve*)rd_h(rd, t);
if (!all_ok || base == nullptr || c3 == nullptr)
{
o.push_back(ST_NULL_DEREF);
break;
}
if (hang_guard_on() && would_extend_closed_neighbour(exec, dist, base, other_base))
{
o.push_back(ST_HANG);
break;
}
SmBoolean good = FALSE;
SmExtent1d ivl;
SmStatus s = geom.TrimExtendedEdge(ctx, &exec, base, other_base, off, other_off, c3, uv_base, uv_other, good,
ivl, mark);
o.push_back(st(s));
if (s == SM_SUCCESS) // the out-parameters: only after success
{
o.push_back(flg(good));
if (good)
{
o.push_back(ivl.GetMin());
o.push_back(ivl.GetMax());
}
}
break;
}
case S_EXTEND_TRIM_EDGES:
{
SmEdgeuse* eu = (SmEdgeuse*)rd_h(rd, t);
SmEdgeuse* other = (SmEdgeuse*)rd_h(rd, t);
SmEdgeuse* off = (SmEdgeuse*)rd_h(rd, t);
SmEdgeuse* other_off = (SmEdgeuse*)rd_h(rd, t);
bool bad = !all_ok || eu == nullptr || off == nullptr || eu->GetUVTrimCurve() == nullptr;
if (!bad && !eu->GetEdge()->IsLamina() && eu->GetRadial() != nullptr &&
eu->GetRadial()->GetUVTrimCurve() == nullptr)
bad = true;
if (!bad && !eu->GetEdge()->IsLamina() && eu->GetRadial() == nullptr)
bad = true;
if (bad)
{
o.push_back(ST_NULL_DEREF);
break;
}
if (hang_guard_on() && (dist < 0.0 || closed_offset_face(off) || closed_offset_face(other_off)))
{
o.push_back(ST_HANG); // HANG-17: the offset faces' surfaces are extended
break;
}
SmStatus s = geom.ExtendTrimEdges(ctx, &exec, eu, other, off, other_off, mark);
o.push_back(st(s));
put_edge_state(o, off->GetEdge());
o.push_back(other_off ? 1.0 : 0.0);
if (other_off)
put_edge_state(o, other_off->GetEdge());
break;
}
case S_MATE_EDGES:
{
SmEdgeuse* eu = (SmEdgeuse*)rd_h(rd, t);
SmEdgeuse* other = (SmEdgeuse*)rd_h(rd, t);
SmEdgeuse* off = (SmEdgeuse*)rd_h(rd, t);
SmEdgeuse* other_off = (SmEdgeuse*)rd_h(rd, t);
if (!all_ok)
{
o.push_back(ST_NULL_DEREF);
break;
}
if (hang_guard_on() && eu && other && off && other_off && (dist < 0.0 || closed_offset_face(off) || closed_offset_face(other_off)))
{
o.push_back(ST_HANG); // HANG-17: both faces' surfaces are extended
break;
}
SmStatus s = geom.MateEdges(ctx, &exec, eu, other, off, other_off, mark);
o.push_back(st(s));
o.push_back(off ? 1.0 : 0.0);
if (off)
put_edge_state(o, off->GetEdge());
o.push_back(other_off ? 1.0 : 0.0);
if (other_off)
put_edge_state(o, other_off->GetEdge());
break;
}
case S_HAVE_TANGENT_GAPS:
{
int64_t n = rd.idx();
SmEdgeuse* off = (SmEdgeuse*)rd_h(rd, t);
SmEdgeuse* other_off = (SmEdgeuse*)rd_h(rd, t);
if (!all_ok || off == nullptr || other_off == nullptr)
{
o.push_back(ST_NULL_DEREF);
break;
}
o.push_back(ST_OK);
o.push_back(flg(geom.HaveTangentGaps((ULONG)n, off, other_off)));
break;
}
case S_EDGEUSE_OFFSET:
{
SmEdgeuse* orig = (SmEdgeuse*)rd_h(rd, t);
SmEdgeuse* other_orig = (SmEdgeuse*)rd_h(rd, t);
SmEdgeuse* off = (SmEdgeuse*)rd_h(rd, t);
SmEdgeuse* other_off = (SmEdgeuse*)rd_h(rd, t);
if (!all_ok || orig == nullptr)
{
o.push_back(ST_NULL_DEREF);
break;
}
if (exec.GetExtendConvexEdges())
{
// HANG-17: a tangent or lamina edge goes to ExtendTrimEdges (always extends),
// the others to MateEdges (extends unless an edge use is null)
bool lamina = orig->GetEdge()->IsLamina();
bool tangent = orig->IsTangentSector(exec.GetTangencyTolDegrees());
if (tangent || lamina)
{
// ExtendTrimEdges: null offset edge use (upstream dereferences it), its UV
// trim curves, then the extension of the offset faces' surfaces
bool bad = off == nullptr || orig->GetUVTrimCurve() == nullptr;
if (!bad && !lamina && (orig->GetRadial() == nullptr || orig->GetRadial()->GetUVTrimCurve() == nullptr))
bad = true;
if (bad)
{
o.push_back(ST_NULL_DEREF);
break;
}
if (hang_guard_on() && (dist < 0.0 || closed_offset_face(off) || closed_offset_face(other_off)))
{
o.push_back(ST_HANG);
break;
}
}
else if (hang_guard_on() && other_orig && off && other_off && (dist < 0.0 || closed_offset_face(off) || closed_offset_face(other_off)))
{
o.push_back(ST_HANG); // MateEdges extends both faces' surfaces
break;
}
}
SmSurface* ns = nullptr;
SmTArray<SmCurve*> curves(ctx);
SmTArray<SmOrientType> orients(ctx);
SmSSIData ssi;
SmStatus s = geom.EdgeuseOffset(ctx, &exec, orig, other_orig, off, other_off, ns, curves, orients, ssi, mark);
o.push_back(st(s));
if (s == SM_SUCCESS) // the out-parameters: only after success
{
o.push_back(ns ? 1.0 : 0.0);
if (ns)
put_srf(o, ns);
put_curves(o, curves);
put_orients(o, orients);
o.push_back((double)ssi.m_v3DCurves.GetSize());
}
o.push_back(off ? 1.0 : 0.0); // the edges' states: after every status
if (off)
put_edge_state(o, off->GetEdge());
o.push_back(other_off ? 1.0 : 0.0);
if (other_off)
put_edge_state(o, other_off->GetEdge());
delete_curves(curves);
delete_curves(ssi.m_v3DCurves);
delete_curves(ssi.m_vUVCurves1);
delete_curves(ssi.m_vUVCurves2);
delete ns;
break;
}
case S_EDGE_STATE:
{
SmEdge* e = (SmEdge*)rd_h(rd, t);
if (e == nullptr)
{
o.push_back(ST_NULL_DEREF);
break;
}
o.push_back(ST_OK);
put_edge_state(o, e);
break;
}
case S_SET_EDGE_TOL:
{
SmEdge* e = (SmEdge*)rd_h(rd, t);
double tol = rd.num();
if (e == nullptr)
{
o.push_back(ST_NULL_DEREF);
break;
}
e->SetTolerance(tol, FALSE, FALSE);
o.push_back(ST_OK);
o.push_back((double)e->GetTolerance());
break;
}
default: o.push_back(-9.0); return;
}
}
}
void run_og(int32_t op, Rd& rd, const char* path, std::vector<double>& o)
{
Two t;
struct Clean
{
Two& t;
~Clean()
{
for (SmBSplineCurve* c : t.curves)
delete c;
for (SmSurface* s : t.surfaces)
delete s;
delete t.brep[0];
delete t.brep[1];
}
} clean{t};
if (!build_two(rd, path, t, o))
return;
o.push_back(0.0);
if (op == OG_EXPORT)
{
for (int b = 0; b < 2; b++)
{
if (t.brep[b] == nullptr)
{
o.push_back(0.0);
continue;
}
std::vector<double> ex;
ex.push_back(0.0);
export_graph(t.brep[b], t.g[b], ex);
o.push_back((double)ex.size());
o.insert(o.end(), ex.begin(), ex.end());
}
return;
}
run_script(rd, t, o);
}
} // namespace
SC_EXPORT int32_t sc_offset_offset_geometry_creation_eval(int32_t op, const char* path, const double* in, int64_t n_in,
double* out, int64_t cap, int64_t* out_len)
{
if (in == NULL || n_in < 1 || out_len == NULL || path == NULL)
return SC_ERR_ARG;
SC_TRY
Rd rd{in, 0};
std::vector<double> o;
run_og(op, rd, path, o);
return sc_copy_out(o.data(), (int64_t)o.size(), out, cap, out_len);
SC_CATCH
}