// SPDX-License-Identifier: Apache-2.0 // difftest shim for unit `intersect.surface_intersector_p3`: the SmSurfaceIntersector members // FindBoundaryStartPoints, ComputeNextPoint, TestPoint (with the file function sm_UpdateTsectPoint) // and SplitSpan (source/SMLib/src/SmSurfaceIntersector.cpp), run on a real SmSurfaceIntersector // over SmPlane / SmBSplineSurface / SmCurveBoundedSurface objects (a derived class only exposes the // protected state). // // One entry point, sc_intersect_surface_intersector_p3_run(in, n_in, out, cap, out_len). `in` is // a program (layout in crates/solidcore-ffi/src/units/intersect_surface_intersector_p3.rs); // every read is bounds checked here (a bad program is SC_ERR_ARG). Ops whose upstream behaviour // is undefined report REJECTED (see the op cases). #include "sc_shim.h" #include "StdAfx.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { enum Op : int32_t { OP_STATE = 1, OP_POINT = 2, OP_ADD_CURVE = 3, OP_ADD_START = 4, OP_ADD_THROUGH = 5, OP_CPV = 6, OP_MEMBERS = 7, OP_ENDS = 8, OP_EXT = 9, OP_TEST = 10, OP_NEXT = 11, OP_SPLIT = 12, OP_FBSP = 13, OP_LAST = 13, }; // Op status of an op the shim does not run. const double REJECTED = -2.0; struct Bad : std::runtime_error { Bad() : std::runtime_error("bad program") {} }; struct Rd { const double* in; int64_t n; int64_t at; double num() { if (at >= n) throw Bad(); return in[at++]; } int64_t idx(int64_t lo, int64_t hi) { double d = num(); if (!(d >= (double)lo && d <= (double)hi) || d != (double)(int64_t)d) throw Bad(); return (int64_t)d; } bool flag() { return idx(0, 1) != 0; } SmVector3d vec() { double x = num(); double y = num(); double z = num(); return SmVector3d(x, y, z); } SmPoint2d uv() { double x = num(); double y = num(); return SmPoint2d(x, y); } SmExtent2d dom4() { double a = num(); double b = num(); double c = num(); double d = num(); return SmExtent2d(a, b, c, d); } }; double st(SmStatus s) { return s == SM_SUCCESS ? 0.0 : (double)s; } double flg(SmBoolean b) { return b ? 1.0 : 0.0; } void put_vec(std::vector& o, const SmVector3d& v) { o.push_back(v.x); o.push_back(v.y); o.push_back(v.z); } void put_uv(std::vector& o, const SmVector2d& v) { o.push_back(v.x); o.push_back(v.y); } // `0 o x y scale dom` (SmPlane) or `1 p q nu nv net(x y z w) u v` (SmBSplineSurface). SmSurface* read_basic_surface(Rd& rd, const SmContext& ctx) { int64_t kind = rd.idx(0, 1); if (kind == 0) { SmPoint3d o = rd.vec(); SmVector3d x = rd.vec(); SmVector3d y = rd.vec(); SmVector2d s = rd.uv(); SmExtent2d d = rd.dom4(); return new (ctx) SmPlane(o, x, y, s, d, &ctx); } gw_DEGREE p = (gw_DEGREE)rd.idx(1, 8); gw_DEGREE q = (gw_DEGREE)rd.idx(1, 8); gw_INDEX nu = (gw_INDEX)rd.idx(2, 17); gw_INDEX nv = (gw_INDEX)rd.idx(2, 17); if (p >= nu || q >= nv) throw Bad(); gw_INDEX n = nu - 1; gw_INDEX m = nv - 1; gw_INDEX r = n + p + 1; gw_INDEX s = m + q + 1; std::vector v; for (int64_t i = 0; i < 4 * (int64_t)nu * nv + r + 1 + s + 1; i++) v.push_back(rd.num()); gw_SURFACE* sur = sm_AllocateNurbSurface(n, m, p, q, r, s); size_t k = 0; for (gw_INDEX i = 0; i <= n; i++) for (gw_INDEX j = 0; j <= m; j++) { sur->net->Pw[i][j].x = v[k++]; sur->net->Pw[i][j].y = v[k++]; sur->net->Pw[i][j].z = v[k++]; sur->net->Pw[i][j].w = v[k++]; } for (gw_INDEX i = 0; i <= r; i++) sur->knu->U[i] = v[k++]; for (gw_INDEX i = 0; i <= s; i++) sur->knv->U[i] = v[k++]; return new (ctx) SmBSplineSurface(sur, FALSE, &ctx); } // A basic surface (`0 ..` / `1 ..`) or `3 basic`: an SmCurveBoundedSurface (implicit outer, no // boundary curves) over it, which owns (deletes) its basis. (`2` marks B = A.) SmSurface* read_surface(Rd& rd, const SmContext& ctx) { if (rd.at < rd.n && rd.in[rd.at] == 3.0) { rd.at++; SmSurface* basis = read_basic_surface(rd, ctx); SmTArray c3d(ctx), cuv(ctx); return new (ctx) SmCurveBoundedSurface(*basis, &c3d, &cuv, TRUE); } return read_basic_surface(rd, ctx); } // `0 a b` (SmLine::CreateLineSegment, dim 3) or `1 dim p ncp cpts(x y z w) knots`. SmCurve* read_curve(Rd& rd, const SmContext& ctx) { int64_t kind = rd.idx(0, 1); if (kind == 0) { SmPoint3d a = rd.vec(); SmPoint3d b = rd.vec(); SmLine* l = NULL; if (SmLine::CreateLineSegment(ctx, 3, a, b, l) != SM_SUCCESS || l == NULL) throw Bad(); return l; } ULONG dim = (ULONG)rd.idx(2, 3); int64_t p = rd.idx(1, 8); int64_t ncp = rd.idx(2, 33); if (p >= ncp) throw Bad(); int64_t n = ncp - 1; int64_t m = n + p + 1; std::vector v; for (int64_t i = 0; i < 4 * ncp + m + 1; i++) v.push_back(rd.num()); gw_CURVE* cur = sm_AllocateNurbCurve((gw_INDEX)n, (gw_DEGREE)p, (gw_INDEX)m); for (int64_t i = 0; i <= n; i++) { cur->pol->Pw[i].x = v[4 * i]; cur->pol->Pw[i].y = v[4 * i + 1]; cur->pol->Pw[i].z = v[4 * i + 2]; cur->pol->Pw[i].w = v[4 * i + 3]; } for (int64_t i = 0; i <= m; i++) cur->knt->U[i] = v[4 * ncp + i]; SmBSplineCurve* c = new (ctx) SmBSplineCurve(dim, (ULONG)cur->pol->n, (ULONG)cur->knt->m, (short)cur->p, cur->pol->Pw, cur->knt->U, &ctx); smos_Free(cur); return c; } // A point: `type ptype[4] uv0 duv0 uv1 duv1 srf0[3][3] srf1[3][3] crv_pos crv_deriv param // angle deviation user[4]` (80 values). void read_point(Rd& rd, SmTsectPnt& t) { t.m_ePointType = (SmIntersectionPointType)rd.idx(0, 7); for (int s = 0; s < 2; s++) for (int d = 0; d < 2; d++) t.m_eUVParamType[s][d] = (SmExtentPointType)rd.idx(0, 5); for (int s = 0; s < 2; s++) for (int d = 0; d < 2; d++) t.m_vUVCurvePV[s][d] = rd.uv(); for (int s = 0; s < 2; s++) for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) t.m_vSurfacePV[s][i][j] = rd.vec(); t.m_v3DCurvePV[0] = rd.vec(); t.m_v3DCurvePV[1] = rd.vec(); t.m_dCurveParameter = rd.num(); t.m_dTangentPlaneAngleRad = rd.num(); t.m_dDeviation = rd.num(); for (int i = 0; i < SM_MAX_USER_DOUBLES; i++) t.m_adUserDoubles[i] = rd.num(); } void put_point(std::vector& o, const SmTsectPnt& t) { o.push_back((double)t.m_ePointType); for (int s = 0; s < 2; s++) for (int d = 0; d < 2; d++) o.push_back((double)t.m_eUVParamType[s][d]); for (int s = 0; s < 2; s++) for (int d = 0; d < 2; d++) put_uv(o, t.m_vUVCurvePV[s][d]); for (int s = 0; s < 2; s++) for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) put_vec(o, t.m_vSurfacePV[s][i][j]); put_vec(o, t.m_v3DCurvePV[0]); put_vec(o, t.m_v3DCurvePV[1]); o.push_back(t.m_dCurveParameter); o.push_back(t.m_dTangentPlaneAngleRad); o.push_back(t.m_dDeviation); for (int i = 0; i < SM_MAX_USER_DOUBLES; i++) o.push_back(t.m_adUserDoubles[i]); } // Reads the protected STEP flags through pointers to members named in a derived class. struct PeekC : SmBSplineCurve { static SmKnotType SmBSplineCurve::*kt() { return &PeekC::m_eKnotType; } static SmBSplineCurveForm SmBSplineCurve::*form() { return &PeekC::m_eBSplineCurveForm; } }; // `type dim knot_type form has [n p m cpts(x y z w) knots] ivl[2]` of a B-spline curve. void put_bspline(std::vector& o, const SmCurve* c) { o.push_back((double)c->GetType()); o.push_back((double)c->GetDim()); if (!c->IsKindOf(SmBSplineCurve_TYPE)) throw Bad(); const SmBSplineCurve* b = (const SmBSplineCurve*)c; o.push_back((double)(b->*PeekC::kt())); o.push_back((double)(b->*PeekC::form())); const gw_CURVE* g = b->GetGwNurbPointer(); o.push_back(g ? 1.0 : 0.0); if (g) { o.push_back((double)g->pol->n); o.push_back((double)g->p); o.push_back((double)g->knt->m); for (gw_INDEX i = 0; i <= g->pol->n; i++) { o.push_back(g->pol->Pw[i].x); o.push_back(g->pol->Pw[i].y); o.push_back(g->pol->Pw[i].z); o.push_back(g->pol->Pw[i].w); } for (gw_INDEX i = 0; i <= g->knt->m; i++) o.push_back(g->knt->U[i]); } SmExtent1d ivl = c->GetNaturalInterval(); o.push_back(ivl.GetMin()); o.push_back(ivl.GetMax()); } // `count curves..` from index `from` of an array. void put_curves(std::vector& o, const SmTArray& a, ULONG from) { o.push_back((double)(a.GetSize() - from)); for (ULONG i = from; i < a.GetSize(); i++) put_bspline(o, a[i]); } struct Prog { const SmContext& ctx; std::vector pts; std::vector curves; ~Prog() { for (SmTsectPnt* p : pts) delete p; for (SmCurve* c : curves) delete c; } SmTsectPnt* pt(Rd& rd) { int64_t i = rd.idx(0, (int64_t)pts.size() - 1); return pts[(size_t)i]; } // `has idx`: a point or NULL SmTsectPnt* opt_pt(Rd& rd) { bool has = rd.flag(); SmTsectPnt* p = pt(rd); return has ? p : NULL; } static bool linked(const SmTsectPnt* p) { return p->m_pNext != NULL || p->m_pPrev != NULL; } double index_of(const SmTsectPnt* p) const { for (size_t i = 0; i < pts.size(); i++) if (pts[i] == p) return (double)i; return -1.0; } // a program point's index, or `-1` and the point's values void put_ref(std::vector& o, const SmTsectPnt* p) const { double i = index_of(p); o.push_back(i); if (i < 0.0) put_point(o, *p); } }; // The intersector with its protected state reachable. struct Peek : SmSurfaceIntersector { Peek(const SmSurface& a, const SmExtent2d& da, const SmSurface& b, const SmExtent2d& db, SmBoolean f) : SmSurfaceIntersector(a, da, b, db, f) { } void set_context(const SmContext& c) { m_cpContext = &c; } void set_members(double trace_dir, double approx_tol, double start_tol, double step_tol, double ang_tol) { m_dCurveTraceDirection = trace_dir; m_dThisApproxTol3d = approx_tol; m_dStartParallelTolRadians = start_tol; m_dSteppingParallelTolRadians = step_tol; m_dThisAngTolRad = ang_tol; } void set_ends(SmTsectPnt* s, SmTsectPnt* e) { m_pStartPoint = s; m_pEndPoint = e; } void set_ext(bool clip, double last, double dist) { m_bDoingExtensionClipping = clip ? TRUE : FALSE; m_dLastThroughParmeter = last; m_dExtensionDistance = dist; } SmTsectPnt* last_curve_point() const { return m_vCurvePoints.GetLastNode(); } bool no_curve_points() const { return m_vCurvePoints.GetFirstNode() == NULL; } bool needs_prev() const { return m_vThroughPoints.GetFirstNode() != NULL || m_pStartPoint != NULL || m_pEndPoint != NULL; } void put_list(std::vector& o, const Prog& pr, const SmTList& l) const { SmTsectPnt* aData[70]; SmTArray a(70, aData); l.GetAllNodes(a); o.push_back((double)a.GetSize()); for (ULONG i = 0; i < a.GetSize(); i++) pr.put_ref(o, a[i]); } // `approx_tol trace_dir start_tol step_tol ang_tol last_through ext_dist ext_clip closed // clipped interior start_point end_point curve_list start_list through_list npoints points` void put_state(std::vector& o, const Prog& pr) const { o.push_back(m_dThisApproxTol3d); o.push_back(m_dCurveTraceDirection); o.push_back(m_dStartParallelTolRadians); o.push_back(m_dSteppingParallelTolRadians); o.push_back(m_dThisAngTolRad); o.push_back(m_dLastThroughParmeter); o.push_back(m_dExtensionDistance); o.push_back(flg(m_bDoingExtensionClipping)); o.push_back(flg(m_bCurveIsClosed)); o.push_back(flg(m_bClippedByThroughPoint)); o.push_back(flg(m_bCheckForInteriorCurves)); o.push_back(m_pStartPoint ? pr.index_of(m_pStartPoint) : -3.0); o.push_back(m_pEndPoint ? pr.index_of(m_pEndPoint) : -3.0); put_list(o, pr, m_vCurvePoints); put_list(o, pr, m_vStartPoints); put_list(o, pr, m_vThroughPoints); o.push_back((double)pr.pts.size()); for (const SmTsectPnt* p : pr.pts) put_point(o, *p); } }; // The accumulated FlushCurve outputs: `out3d` starts with the program's curves. struct Outs { SmCurve* a3[8]; SmCurve* a0[8]; SmCurve* a1[8]; SmTsectCurveType at[8]; double ad[8]; SmTArray c3; SmTArray c0; SmTArray c1; SmTArray types; SmTArray devs; ULONG first; Outs() : c3(8, a3), c0(8, a0), c1(8, a1), types(8, at), devs(8, ad), first(0) {} ~Outs() { for (ULONG i = first; i < c3.GetSize(); i++) delete c3[i]; for (ULONG i = 0; i < c0.GetSize(); i++) delete c0[i]; for (ULONG i = 0; i < c1.GetSize(); i++) delete c1[i]; } void put(std::vector& o) const { put_curves(o, c3, first); put_curves(o, c0, 0); put_curves(o, c1, 0); o.push_back((double)types.GetSize()); for (ULONG i = 0; i < types.GetSize(); i++) o.push_back((double)types[i]); o.push_back((double)devs.GetSize()); for (ULONG i = 0; i < devs.GetSize(); i++) o.push_back(devs[i]); } }; void run_op(int32_t op, Rd& rd, Prog& pr, Peek& si, Outs& outs, std::vector& o) { switch (op) { case OP_STATE: o.push_back(0.0); si.put_state(o, pr); return; case OP_POINT: { SmTsectPnt* p = pr.pt(rd); o.push_back(0.0); put_point(o, *p); return; } case OP_ADD_CURVE: case OP_ADD_START: case OP_ADD_THROUGH: { SmTsectPnt* p = pr.pt(rd); // linking a point that is on a list corrupts the intrusive lists if (Prog::linked(p)) { o.push_back(REJECTED); return; } SmStatus s = op == OP_ADD_CURVE ? si.AddPointToCurve(*p) : op == OP_ADD_START ? si.AddStartPoint(*p) : si.AddThroughPoint(*p); o.push_back(st(s)); return; } case OP_CPV: { SmPoint2d uvs[2]; uvs[0] = rd.uv(); uvs[1] = rd.uv(); SmTsectPnt* p = pr.pt(rd); SmTsectPnt* prev = pr.opt_pt(rd); bool has_step = rd.flag(); double step = rd.num(); if (prev == p) throw Bad(); o.push_back(st(si.ComputePointValues(uvs, *p, prev, has_step ? &step : NULL))); put_point(o, *p); return; } case OP_MEMBERS: { double td = rd.num(); double at = rd.num(); double sp = rd.num(); double stp = rd.num(); double ang = rd.num(); si.set_members(td, at, sp, stp, ang); o.push_back(0.0); return; } case OP_ENDS: { SmTsectPnt* s = pr.opt_pt(rd); SmTsectPnt* e = pr.opt_pt(rd); si.set_ends(s, e); o.push_back(0.0); return; } case OP_EXT: { bool clip = rd.flag(); double last = rd.num(); double dist = rd.num(); si.set_ext(clip, last, dist); o.push_back(0.0); return; } case OP_TEST: { SmTsectPnt* p = pr.pt(rd); bool done0 = rd.flag(); // the next point as the previous point (evaluated onto itself through two // aliases), or a null previous point dereferenced if (p == si.last_curve_point() || (si.no_curve_points() && si.needs_prev())) { o.push_back(REJECTED); return; } SmBoolean done = done0 ? TRUE : FALSE; SmStatus s = si.TestPoint(*p, done); o.push_back(st(s)); if (s == SM_SUCCESS) o.push_back(flg(done)); put_point(o, *p); return; } case OP_NEXT: { SmTsectPnt* prev = pr.pt(rd); double step = rd.num(); SmTsectPnt* next = pr.pt(rd); double dev = rd.num(); double ang = rd.num(); if (prev == next) { o.push_back(REJECTED); return; } SmBoolean good = 7, clipped = 7, hit = 7; SmStatus s = si.ComputeNextPoint(*prev, step, *next, good, dev, ang, clipped, hit); o.push_back(st(s)); if (s == SM_SUCCESS) { o.push_back(flg(good)); o.push_back(dev); o.push_back(ang); o.push_back(flg(clipped)); o.push_back(flg(hit)); } put_point(o, *next); return; } case OP_SPLIT: { SmTsectPnt* curr = pr.pt(rd); SmTsectPnt* next = pr.pt(rd); double delta = rd.num(); SmVector3d normals[2]; normals[0] = rd.vec(); normals[1] = rd.vec(); bool avg = rd.flag(); bool orient = rd.flag(); if (pr.curves.empty()) throw Bad(); SmCurve* crv = pr.curves[(size_t)rd.idx(0, (int64_t)pr.curves.size() - 1)]; double a = rd.num(); double b = rd.num(); SmExtent1d ivl(a, b); double so[2]; so[0] = rd.num(); so[1] = rd.num(); SmFilletLaw law; SmTsectPnt* np = NULL; SmStatus s = si.SplitSpan(curr, next, delta, normals, avg ? TRUE : FALSE, law, orient ? TRUE : FALSE, *crv, ivl, so, np); o.push_back(st(s)); if (s == SM_SUCCESS) put_point(o, *np); return; } case OP_FBSP: { SmPoint3d bmin = rd.vec(); SmPoint3d bmax = rd.vec(); bool use0 = rd.flag(); bool use1 = rd.flag(); SmExtent3d box(bmin.x, bmin.y, bmin.z, bmax.x, bmax.y, bmax.z); si.SetUseSurfaceEdges(use0 ? TRUE : FALSE, use1 ? TRUE : FALSE); SmStatus s = si.FindBoundaryStartPoints(box, outs.c3, outs.c0, outs.c1, outs.types, outs.devs); o.push_back(st(s)); outs.put(o); return; } default: throw Bad(); } } } // namespace // Program: `surfA surfB` (surfB `2` = the same object as A), `dom1[4] dom2[4] fillet`, // `ncurves curves`, `npoints points`, `nops ops`. Output: per op `op status [values]`, then the // final state. SC_EXPORT int32_t sc_intersect_surface_intersector_p3_run(const double* in, int64_t n_in, double* out, int64_t cap, int64_t* out_len) { if (in == NULL || n_in < 2) return SC_ERR_ARG; try { SmContext ctx; std::vector o; Rd rd{in, n_in, 0}; SmSurface* a = read_surface(rd, ctx); SmSurface* b = NULL; bool same = false; if (rd.at < rd.n && rd.in[rd.at] == 2.0) { rd.at++; same = true; b = a; } else b = read_surface(rd, ctx); SmExtent2d d1 = rd.dom4(); SmExtent2d d2 = rd.dom4(); bool fillet = rd.flag(); Prog pr{ctx, {}, {}}; int64_t nc = rd.idx(0, 8); for (int64_t i = 0; i < nc; i++) pr.curves.push_back(read_curve(rd, ctx)); int64_t np = rd.idx(0, 64); for (int64_t i = 0; i < np; i++) { SmTsectPnt* t = new SmTsectPnt(); pr.pts.push_back(t); read_point(rd, *t); } int64_t nops = rd.idx(0, 64); { Peek si(*a, d1, *b, d2, fillet ? TRUE : FALSE); si.set_context(ctx); Outs outs; for (SmCurve* c : pr.curves) outs.c3.Add(c); outs.first = outs.c3.GetSize(); for (int64_t i = 0; i < nops; i++) { int32_t op = (int32_t)rd.idx(1, OP_LAST); o.push_back((double)op); run_op(op, rd, pr, si, outs, o); } si.put_state(o, pr); // unlink every point before the lists go away for (SmTsectPnt* t : pr.pts) t->ReSet(); si.set_ends(NULL, NULL); } if (!same) delete b; delete a; return sc_copy_out(o.data(), (int64_t)o.size(), out, cap, out_len); } catch (const Bad&) { return SC_ERR_ARG; } catch (...) { return SC_ERR_EXCEPTION; } }