// SPDX-License-Identifier: Apache-2.0 // difftest shim for unit `primitive.api_curves`: the SM_API curve constructors of // source/SM_API/src/SmApiCurves.cpp (SmApiCreateLineSegment, SmApiCreateLine, both // SmApiCreateCircle overloads, SmApiCreateEllipse, SmApiCreateArc, SmApiCreateHelix, // SmApiCreateRectangle, SmApiCreateRegularPolygon, SmApiCreateCurve, // SmApiCreateCanonicalCurve) and SmApiRemoveCurveKnots on a curve one of them made, // called exactly as a caller of the API would. // // One entry point, sc_primitive_api_curves_run(in, n_in, out, cap, out_len). `in` is // `op args...`; layouts are documented in crates/solidcore-ffi/src/units/primitive_api_curves.rs. // Every read is bounds checked here (a bad program, or input that would make the C++ read out // of bounds, is SC_ERR_ARG). `out` receives the API status (0 = SM_SUCCESS) and, on success, // the state of the created curve(s). Private members are read through explicit-instantiation // access. #include "sc_shim.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sc_sm.h" using sc::flg; using sc::put_ivl; using sc::put_vec; using sc::st; namespace { // Ops (keep in sync with crates/solidcore-ffi/src/units/primitive_api_curves.rs). enum : int32_t { OP_LINE_SEGMENT = 1, OP_LINE = 2, OP_CIRCLE = 3, OP_ANALYTIC_CIRCLE = 4, OP_ELLIPSE = 5, OP_ARC = 6, OP_HELIX = 7, OP_RECTANGLE = 8, OP_POLYGON = 9, OP_CURVE = 10, OP_CANONICAL = 11, OP_REMOVE_KNOTS = 12, }; // Set by OP_REMOVE_KNOTS: the created curve is passed to SmApiRemoveCurveKnots, whose // status precedes its state. bool g_remove_knots = false; // SmApiRemoveCurveKnots on a created curve, when asked: its status. void maybe_remove_knots(std::vector& o, SmCurve* c) { if (g_remove_knots) o.push_back(st(SmApiRemoveCurveKnots(c))); } // Access to private / protected members through explicit instantiation. template struct Rob { friend typename Tag::type get(Tag) { return M; } }; struct PcKnots { typedef SmTArray SmPolarConversion::*type; friend type get(PcKnots); }; template struct Rob; struct PcAngles { typedef SmTArray SmPolarConversion::*type; friend type get(PcAngles); }; template struct Rob; struct PcExact { typedef SmTArray SmPolarConversion::*type; friend type get(PcExact); }; template struct Rob; struct EllPolar { typedef SmPolarConversion SmEllipse::*type; friend type get(EllPolar); }; template struct Rob; struct EllInsideOut { typedef SmBoolean SmEllipse::*type; friend type get(EllInsideOut); }; template struct Rob; struct BsForm { typedef SmBSplineCurveForm SmBSplineCurve::*type; friend type get(BsForm); }; template struct Rob; struct BsKnotType { typedef SmKnotType SmBSplineCurve::*type; friend type get(BsKnotType); }; template struct Rob; 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); } }; // type dim knot_type form, the NURB (`has n p m Pw U`), natural interval. void put_bspline(std::vector& o, SmBSplineCurve* c) { o.push_back((double)c->GetType()); o.push_back((double)c->GetDim()); o.push_back((double)(c->*get(BsKnotType()))); o.push_back((double)(c->*get(BsForm()))); const gw_CURVE* cur = c->GetGwNurbPointer(); o.push_back(cur ? 1.0 : 0.0); if (cur) { gw_INDEX n = cur->pol->n; gw_INDEX m = cur->knt->m; o.push_back((double)n); o.push_back((double)cur->p); o.push_back((double)m); for (gw_INDEX i = 0; i <= n; i++) { o.push_back(cur->pol->Pw[i].x); o.push_back(cur->pol->Pw[i].y); o.push_back(cur->pol->Pw[i].z); o.push_back(cur->pol->Pw[i].w); } for (gw_INDEX i = 0; i <= m; i++) o.push_back(cur->knt->U[i]); put_ivl(o, c->GetNaturalInterval()); } } // point vector STEP-interval scale inside_out, then the B-spline part. void put_line(std::vector& o, SmLine* l) { put_vec(o, l->GetLinePoint()); put_vec(o, l->GetLineVector()); put_ivl(o, l->GetSTEPInterval()); o.push_back(l->GetLineScale()); o.push_back(flg(l->GetInsideOut())); put_bspline(o, l); } // placement STEP-interval radii inside_out, the polar converter, then the B-spline part. void put_ellipse(std::vector& o, SmEllipse* e) { const SmAxis2Placement& pos = e->GetPosition(); put_vec(o, pos.GetOriginRef()); put_vec(o, pos.GetXAxisRef()); put_vec(o, pos.GetYAxisRef()); put_ivl(o, e->GetSTEPInterval()); o.push_back(e->GetXRadius()); o.push_back(e->GetYRadius()); o.push_back(flg(e->*get(EllInsideOut()))); const SmPolarConversion& pc = e->*get(EllPolar()); o.push_back(flg(pc.IsPolarConversionPossible())); const SmTArray& k = pc.*get(PcKnots()); o.push_back((double)k.GetSize()); for (ULONG i = 0; i < k.GetSize(); i++) o.push_back(k[i]); const SmTArray& a = pc.*get(PcAngles()); o.push_back((double)a.GetSize()); for (ULONG i = 0; i < a.GetSize(); i++) o.push_back(a[i]); const SmTArray& x = pc.*get(PcExact()); o.push_back((double)x.GetSize()); for (ULONG i = 0; i < x.GetSize(); i++) o.push_back(flg(x[i])); put_bspline(o, e); } void put_segments(std::vector& o, SmStatus s, SmTArray& segs) { o.push_back(st(s)); if (s == SM_SUCCESS) { o.push_back((double)segs.GetSize()); for (ULONG i = 0; i < segs.GetSize(); i++) put_line(o, (SmLine*)segs[i]); } for (ULONG i = 0; i < segs.GetSize(); i++) delete segs[i]; } void put_result(std::vector& o, SmStatus s, SmBSplineCurve* c) { o.push_back(st(s)); if (s == SM_SUCCESS && c) { maybe_remove_knots(o, c); put_bspline(o, c); } delete c; } void run(Rd& rd, std::vector& o) { int64_t op = rd.idx(OP_LINE_SEGMENT, OP_REMOVE_KNOTS); g_remove_knots = false; if (op == OP_REMOVE_KNOTS) { // a curve of one call (not the segment arrays), then SmApiRemoveCurveKnots g_remove_knots = true; op = rd.idx(OP_LINE_SEGMENT, OP_CANONICAL); if (op == OP_RECTANGLE || op == OP_POLYGON) throw Bad(); } switch (op) { case OP_LINE_SEGMENT: case OP_LINE: { SmPoint3d a = rd.vec(); SmVector3d b = rd.vec(); SmLine* l = NULL; SmStatus s = op == OP_LINE ? SmApiCreateLine(a, b, l) : SmApiCreateLineSegment(a, b, l); o.push_back(st(s)); if (s == SM_SUCCESS && l) { maybe_remove_knots(o, l); put_line(o, l); } delete l; return; } case OP_CIRCLE: { SmPoint3d c = rd.vec(); double r = rd.num(); SmBSplineCurve* res = NULL; SmStatus s = SmApiCreateCircle(c, r, res); put_result(o, s, res); return; } case OP_ANALYTIC_CIRCLE: case OP_ELLIPSE: { SmPoint3d c = rd.vec(); double r1 = rd.num(); double r2 = op == OP_ELLIPSE ? rd.num() : r1; SmEllipse* e = NULL; SmStatus s; if (op == OP_ELLIPSE) s = SmApiCreateEllipse(c, r1, r2, e); else { SmCircle* ci = NULL; s = SmApiCreateCircle(c, r1, ci); e = ci; } o.push_back(st(s)); if (s == SM_SUCCESS && e) { maybe_remove_knots(o, e); o.push_back((double)e->GetType()); put_ellipse(o, e); } delete e; return; } case OP_ARC: { SmPoint3d c = rd.vec(); double r = rd.num(); double a0 = rd.num(); double a1 = rd.num(); SmBSplineCurve* res = NULL; SmStatus s = SmApiCreateArc(c, r, a0, a1, res); put_result(o, s, res); return; } case OP_HELIX: { SmPoint3d c = rd.vec(); double h = rd.num(); double r0 = rd.num(); double r1 = rd.num(); double turns = rd.num(); bool rh = rd.flag(); double tol = rd.num(); SmBSplineCurve* res = NULL; SmStatus s = SmApiCreateHelix(c, h, r0, r1, turns, rh ? TRUE : FALSE, tol, res); put_result(o, s, res); return; } case OP_RECTANGLE: { SmPoint3d c = rd.vec(); double len = rd.num(); double wid = rd.num(); SmTArray segs; SmStatus s = SmApiCreateRectangle(c, len, wid, segs); put_segments(o, s, segs); return; } case OP_POLYGON: { SmPoint3d c = rd.vec(); ULONG sides = (ULONG)rd.idx(0, 4096); double r = rd.num(); SmTArray segs; SmStatus s = SmApiCreateRegularPolygon(c, sides, r, segs); put_segments(o, s, segs); return; } case OP_CURVE: { int64_t np = rd.idx(0, 4096); SmTArray pts; for (int64_t i = 0; i < np; i++) pts.Add(rd.vec()); SmBSplineCurve* res = NULL; SmStatus s = SmApiCreateCurve(pts, res); put_result(o, s, res); return; } default: { // OP_CANONICAL: np pts nk knots mults(nk) has_w nw w(nw) degree form int64_t np = rd.idx(0, 4096); SmTArray pts; for (int64_t i = 0; i < np; i++) pts.Add(rd.vec()); int64_t nk = rd.idx(0, 4096); SmTArray knots; for (int64_t i = 0; i < nk; i++) knots.Add(rd.num()); SmTArray mults; int64_t total = 0; for (int64_t i = 0; i < nk; i++) { int64_t m = rd.idx(0, 64); total += m; mults.Add((ULONG)m); } bool has_w = rd.flag(); int64_t nw = rd.idx(0, 4096); SmTArray w; for (int64_t i = 0; i < nw; i++) w.Add(rd.num()); int64_t degree = rd.idx(0, 64); int64_t form = rd.idx(0, 6); // CreateCanonical reads knots [p] and [size-p-1] and N_Iges126Crv reads n+p+2 knots // and n+1 points / weights without checking: reject what would read out of bounds. if (degree >= 1 && degree <= 32 && (!has_w || nw == np)) { if (np < 1 || total < degree + 1 || total < (np - 1) + degree + 2) throw Bad(); } SmBSplineCurve* res = NULL; SmStatus s = SmApiCreateCanonicalCurve(pts, knots, mults, has_w ? &w : NULL, (ULONG)degree, (SmBSplineCurveForm)form, res); put_result(o, s, res); return; } } } } // namespace SC_EXPORT int32_t sc_primitive_api_curves_run(const double* in, int64_t n_in, double* out, int64_t cap, int64_t* out_len) { SC_TRY Rd rd{in, n_in, 0}; std::vector o; try { run(rd, o); } catch (const Bad&) { return SC_ERR_ARG; } if (rd.at != rd.n) return SC_ERR_ARG; return sc_copy_out(o.data(), (int64_t)o.size(), out, cap, out_len); SC_CATCH }