// SPDX-License-Identifier: Apache-2.0 // difftest shim for unit `surface.torus`. // Wraps the SmTorus members owned by the unit (source/SMLib/src/SmTorus.cpp): CreateCanonical // (the constructor and MakeNurb), operator==, Copy, CreateOffsetSurface, // ConvertUVFromSTEPToNURBS, ConvertUVFromNURBSToSTEP, EvaluateSTEP, EvaluateSTEPPoint, // CreateIsoParametricCurve, RebuildSTEPFromNURBParameters, Transform, GetMaxAnalyticDomain, // GetInsideOut and IsKindOf; and the inherited SmSurfOfRevolution::TrimWithDomain, // IntersectWithPlane and GlobalSurfaceIntersect (with a plane). // // One entry point, sc_surface_torus_eval(op, in, n_in, out, cap, out_len). Layouts are // documented in crates/solidcore-ffi/src/units/surface_torus.rs, which validates every input // so upstream only touches valid memory. `out` starts with the status of CreateCanonical. #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 "sc_sm.h" using sc::flg; using sc::put_vec; using sc::st; namespace { // Ops (keep in sync with crates/solidcore-ffi/src/units/surface_torus.rs). enum : int32_t { OP_BUILD = 0, OP_EVAL_STEP = 1, OP_TO_STEP = 2, OP_TO_NURBS = 3, OP_ISO = 4, OP_OFFSET = 5, OP_REBUILD = 6, OP_TRANSFORM = 7, OP_EQ = 8, OP_COPY = 9, OP_TRIM_DOMAIN = 10, OP_ISECT_PLANE = 11, OP_GLOBAL = 12, }; // Reads the protected members through pointers to members named in a derived class. struct Peek : SmTorus { static SmBoolean SmSurfOfRevolution::*planar() { return &Peek::m_bPlanarGenerator; } static SmPolarConversion SmSurfOfRevolution::*polar() { return &Peek::m_vPolarConverter; } }; struct Rd { const double* in; int64_t at; double num() { return in[at++]; } int64_t idx() { return (int64_t)in[at++]; } bool flag() { return in[at++] != 0.0; } SmVector3d vec() { double x = num(); double y = num(); double z = num(); return SmVector3d(x, y, z); } SmExtent2d domain() { double a = num(); double b = num(); double c = num(); double d = num(); return SmExtent2d(a, b, c, d); } }; // A torus block `O[3] X[3] Y[3] major minor swap inside_out has_dom D[4] has_circle // [c[3] x[3] y[3] d0 d1 r]`: SmTorus::CreateCanonical(SmAxis2Placement(O, X, Y), ..). // Writes the status; NULL on failure. SmTorus* read_torus(Rd& rd, const SmContext& ctx, std::vector& o) { SmPoint3d org = rd.vec(); SmVector3d x = rd.vec(); SmVector3d y = rd.vec(); double major = rd.num(); double minor = rd.num(); bool swap = rd.flag(); bool inside = rd.flag(); bool has_dom = rd.flag(); SmExtent2d dom = rd.domain(); bool has_circle = rd.flag(); SmCircle* circle = NULL; if (has_circle) { SmPoint3d c = rd.vec(); SmVector3d cx = rd.vec(); SmVector3d cy = rd.vec(); double d0 = rd.num(); double d1 = rd.num(); double r = rd.num(); circle = new (ctx) SmCircle(c, cx, cy, SmExtent1d(d0, d1), r, 3, &ctx); } else { for (int i = 0; i < 12; i++) rd.num(); } SmTorus* t = NULL; SmStatus stt = SmTorus::CreateCanonical(ctx, SmAxis2Placement(org, x, y), major, minor, t, swap ? TRUE : FALSE, inside ? TRUE : FALSE, circle, has_dom ? &dom : NULL); o.push_back(st(stt)); if (stt != SM_SUCCESS || t == NULL) { // the torus owns the circle once constructed (and deletes it on failure); before // that (a degenerate radius) nobody does if (t == NULL && circle != NULL && !(minor > SM_EFF_ZERO && major > SM_EFF_ZERO)) delete circle; delete t; return NULL; } return t; } // `has [type step0 step1 has_nurb [p n cpts(4 n) nk knots]] [ellipse O X Y rx ry inside]`. void put_curve(std::vector& o, const SmCurve* c) { o.push_back(flg(c != NULL)); if (!c) return; o.push_back((double)c->GetType()); SmExtent1d ivl = c->GetSTEPInterval(); o.push_back(ivl.GetMin()); o.push_back(ivl.GetMax()); const SmBSplineCurve* b = c->IsKindOf(SmBSplineCurve_TYPE) ? (const SmBSplineCurve*)c : NULL; const gw_CURVE* cur = b ? b->GetGwNurbPointer() : NULL; o.push_back(flg(cur != NULL)); if (cur) { o.push_back((double)cur->p); o.push_back((double)(cur->pol->n + 1)); for (gw_INDEX i = 0; i <= cur->pol->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); } o.push_back((double)(cur->knt->m + 1)); for (gw_INDEX i = 0; i <= cur->knt->m; i++) o.push_back(cur->knt->U[i]); } const SmEllipse* e = c->IsKindOf(SmEllipse_TYPE) ? (const SmEllipse*)c : NULL; o.push_back(flg(e != NULL)); if (e) { const SmAxis2Placement& p = e->GetPosition(); put_vec(o, p.GetOriginRef()); put_vec(o, p.GetXAxisRef()); put_vec(o, p.GetYAxisRef()); o.push_back(e->GetXRadius()); o.push_back(e->GetYRadius()); o.push_back(flg(e->GetInsideOut())); } } // The torus state. void put_state(std::vector& o, const SmTorus* s) { o.push_back(flg(s->IsKindOf(SmTorus_TYPE))); o.push_back(flg(s->IsKindOf(SmSurfOfRevolution_TYPE))); o.push_back(flg(s->IsKindOf(SmBSplineSurface_TYPE))); o.push_back(flg(s->IsKindOf(SmSurface_TYPE))); o.push_back((double)s->GetType()); o.push_back(flg(s->GetSwapUV())); o.push_back(flg(s->*Peek::planar())); o.push_back(flg(s->GetInsideOut())); o.push_back(s->GetMajorRadius()); o.push_back(s->GetMinorRadius()); const SmAxis2Placement& pos = s->GetPosition(); put_vec(o, pos.GetOriginRef()); put_vec(o, pos.GetXAxisRef()); put_vec(o, pos.GetYAxisRef()); SmExtent2d d = s->GetSTEPUVDomain(); o.push_back(d.GetMin().x); o.push_back(d.GetMin().y); o.push_back(d.GetMax().x); o.push_back(d.GetMax().y); SmExtent2d mx = s->GetMaxAnalyticDomain(); o.push_back(mx.GetMin().x); o.push_back(mx.GetMin().y); o.push_back(mx.GetMax().x); o.push_back(mx.GetMax().y); const SmPolarConversion& pc = s->*Peek::polar(); o.push_back(flg(s->HavePolarConversion())); ULONG cnt = pc.GetAngleCount(); o.push_back((double)cnt); if (cnt > 0) { o.push_back(pc.GetFirstAngle()); o.push_back(pc.GetLastAngle()); } put_curve(o, pc.GetCurve()); put_curve(o, s->GetGenCurve()); const gw_SURFACE* sur = s->GetGwNurbPointer(); o.push_back(flg(sur != NULL)); if (!sur) return; o.push_back((double)sur->p); o.push_back((double)sur->q); o.push_back((double)(sur->net->n + 1)); o.push_back((double)(sur->net->m + 1)); for (gw_INDEX i = 0; i <= sur->net->n; i++) for (gw_INDEX j = 0; j <= sur->net->m; j++) { o.push_back(sur->net->Pw[i][j].x); o.push_back(sur->net->Pw[i][j].y); o.push_back(sur->net->Pw[i][j].z); o.push_back(sur->net->Pw[i][j].w); } o.push_back((double)(sur->knu->m + 1)); for (gw_INDEX i = 0; i <= sur->knu->m; i++) o.push_back(sur->knu->U[i]); o.push_back((double)(sur->knv->m + 1)); for (gw_INDEX i = 0; i <= sur->knv->m; i++) o.push_back(sur->knv->U[i]); } // A domain block `natural D[4]`: the surface's natural domain or D. SmExtent2d read_dom(Rd& rd, const SmSurface* s) { bool natural = rd.flag(); SmExtent2d d = rd.domain(); return natural ? s->GetNaturalUVDomain() : d; } // A plane block `kind`: 0 `O[3] X[3] Y[3] su sv D[4]` (SmPlane(O, X, Y, (su, sv), D)), // 1 `O[3] N[3]` (SmPlane(O, N)). SmPlane* read_plane(Rd& rd, const SmContext& ctx) { int64_t kind = rd.idx(); if (kind == 0) { SmPoint3d o = rd.vec(); SmVector3d x = rd.vec(); SmVector3d y = rd.vec(); double su = rd.num(); double sv = rd.num(); SmExtent2d d = rd.domain(); return new (ctx) SmPlane(o, x, y, SmVector2d(su, sv), d, &ctx); } SmPoint3d o = rd.vec(); SmVector3d n = rd.vec(); return new (ctx) SmPlane(o, n, &ctx); } // The intersection record: `more ncurves curves ntypes types ndevs devs nuv1 nuv2 nnull1 // nnull2`. void put_intersection(std::vector& o, SmBoolean more, SmTArray& c3d, SmTArray& uv1, SmTArray& uv2, SmTArray& types, SmTArray& devs) { o.push_back(flg(more)); o.push_back((double)c3d.GetSize()); for (ULONG i = 0; i < c3d.GetSize(); i++) put_curve(o, c3d[i]); 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]); ULONG null1 = 0, null2 = 0; for (ULONG i = 0; i < uv1.GetSize(); i++) null1 += uv1[i] == NULL ? 1 : 0; for (ULONG i = 0; i < uv2.GetSize(); i++) null2 += uv2[i] == NULL ? 1 : 0; o.push_back((double)uv1.GetSize()); o.push_back((double)uv2.GetSize()); o.push_back((double)null1); o.push_back((double)null2); } void torus_op(int32_t op, Rd& rd, const SmContext& ctx, SmTorus* s, std::vector& o) { switch (op) { case OP_BUILD: put_state(o, s); break; case OP_EVAL_STEP: { double u = rd.num(); double v = rd.num(); ULONG k = (ULONG)rd.idx(); bool point = rd.flag(); if (point) { SmPoint3d p(0.0, 0.0, 0.0); SmStatus stt = s->EvaluateSTEPPoint(SmPoint2d(u, v), p); o.push_back(st(stt)); if (stt == SM_SUCCESS) put_vec(o, p); break; } std::vector d((k + 1) * (k + 1), SmVector3d(0.0, 0.0, 0.0)); SmStatus stt = s->EvaluateSTEP(SmPoint2d(u, v), k, k, TRUE, TRUE, TRUE, d.data()); o.push_back(st(stt)); if (stt == SM_SUCCESS) for (size_t i = 0; i < d.size(); i++) put_vec(o, d[i]); break; } case OP_TO_STEP: case OP_TO_NURBS: { double u = rd.num(); double v = rd.num(); SmPoint2d r(0.0, 0.0); SmStatus stt = op == OP_TO_STEP ? s->ConvertUVFromNURBSToSTEP(SmPoint2d(u, v), r) : s->ConvertUVFromSTEPToNURBS(SmPoint2d(u, v), r); o.push_back(st(stt)); if (stt == SM_SUCCESS) { o.push_back(r.x); o.push_back(r.y); } break; } case OP_ISO: { SmSurfParamType param = rd.idx() == 0 ? SM_SP_U : SM_SP_V; double iso = rd.num(); bool has_dom = rd.flag(); SmExtent2d dom = rd.domain(); // the override ignores the status of the base extraction and then reads its // curve: reject the arguments it fails on SmBSplineCurve* probe = NULL; SmStatus pst = s->SmBSplineSurface::CreateIsoParametricCurve( ctx, param, iso, 0.0, probe, has_dom ? &dom : NULL, NULL, NULL); bool ok = pst == SM_SUCCESS && probe != NULL; delete probe; if (!ok) { o.push_back((double)SC_ERR_ARG); break; } SmBSplineCurve* c = NULL; double gap = -1.0; SmStatus stt = s->CreateIsoParametricCurve(ctx, param, iso, 0.0, c, has_dom ? &dom : NULL, &gap, NULL); o.push_back(st(stt)); if (stt == SM_SUCCESS) { o.push_back(gap); put_curve(o, c); } delete c; break; } case OP_OFFSET: { double dist = rd.num(); double tol = rd.num(); bool rebuild = rd.flag(); SmSurface* off = NULL; SmStatus stt = s->CreateOffsetSurface(ctx, dist, tol, off); o.push_back(st(stt)); if (stt == SM_SUCCESS) { o.push_back(flg(off != NULL)); if (off) { SmTorus* t = (SmTorus*)off; if (rebuild) o.push_back(st(t->RebuildSTEPFromNURBParameters())); put_state(o, t); } } delete off; break; } case OP_REBUILD: { SmStatus stt = s->RebuildSTEPFromNURBParameters(); o.push_back(st(stt)); put_state(o, s); break; } case OP_TRANSFORM: { SmPoint3d org = rd.vec(); SmVector3d x = rd.vec(); SmVector3d y = rd.vec(); bool has_scale = rd.flag(); SmVector3d sc = rd.vec(); SmAxis2Placement a2p(org, x, y); SmStatus stt = s->Transform(a2p, has_scale ? &sc : NULL); o.push_back(st(stt)); put_state(o, s); break; } case OP_EQ: { std::vector o2; SmTorus* other = read_torus(rd, ctx, o2); o.push_back(o2.empty() ? -1.0 : o2[0]); if (other) { o.push_back(flg(*s == *other)); o.push_back(flg(*other == *s)); delete other; } break; } case OP_COPY: { SmSurface* c = NULL; SmStatus stt = s->Copy(ctx, c); o.push_back(st(stt)); if (stt == SM_SUCCESS && c) { put_state(o, (SmTorus*)c); o.push_back(flg(*s == *c)); } delete c; break; } case OP_ISECT_PLANE: case OP_GLOBAL: { SmPlane* plane = read_plane(rd, ctx); SmExtent2d dom = read_dom(rd, s); SmExtent2d odom = read_dom(rd, plane); bool has_tol = rd.flag(); SmApproxTol3d tol = rd.num(); SmBoolean edges[2] = {TRUE, TRUE}; SmBoolean more = FALSE; SmTArray c3d, uv1, uv2; SmTArray types; SmTArray devs; SmStatus stt = op == OP_ISECT_PLANE ? s->IntersectWithPlane(ctx, dom, *plane, odom, edges, has_tol ? &tol : NULL, NULL, more, &c3d, &uv1, &uv2, &types, &devs) : s->GlobalSurfaceIntersect(ctx, dom, *plane, odom, edges, has_tol ? &tol : NULL, NULL, &c3d, &uv1, &uv2, &types, &devs); o.push_back(st(stt)); if (stt == SM_SUCCESS) put_intersection(o, more, c3d, uv1, uv2, types, devs); for (ULONG i = 0; i < c3d.GetSize(); i++) delete c3d[i]; delete plane; break; } case OP_TRIM_DOMAIN: { SmExtent2d dom = rd.domain(); SmStatus stt = s->TrimWithDomain(dom); o.push_back(st(stt)); o.push_back(dom.GetMin().x); o.push_back(dom.GetMin().y); o.push_back(dom.GetMax().x); o.push_back(dom.GetMax().y); put_state(o, s); break; } default: o.push_back((double)SC_ERR_ARG); break; } } } // namespace SC_EXPORT int32_t sc_surface_torus_eval(int32_t op, const double* in, int64_t n_in, double* out, int64_t cap, int64_t* out_len) { (void)n_in; SC_TRY SmContext ctx; std::vector o; Rd rd{in, 0}; SmTorus* s = read_torus(rd, ctx, o); if (s) { torus_op(op, rd, ctx, s, o); delete s; } return sc_copy_out(o.data(), (int64_t)o.size(), out, cap, out_len); SC_CATCH }