mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-08 13:01:52 +00:00
ec9e30a9bd
Conflicts resolved in favour of main where main superseded this branch's code, combined where both added; main's new kernel argument threaded through the offset point solves, the fillet solver's LocalPointSolve and copy_and_add_analytics callers; OffsetSurface arms added to main's new SurfaceKind matches (evaluation copy, drop curve, CrvOnSurf dispatch gap, loop UV gap test). With surface_point_solve_p3 landed, the 213 cases that deferred on LocalPointSolve_1 now run, and 5 disagreed: Rust found a pole solution C++ rejects. Cause: upstream's owning offset does m_pSurface->SetOwner(this), so LocalPointSolve_1 on the base takes GetZoneTol3d(GetOwner()) as its loose tolerance instead of 0.01. Ported that ownership: an owning offset gives its base the BASE_OWNER surface owner wherever upstream calls SetOwner(this) (constructor, Copy, ReadFromDB), and hands down the face its own owner chain reaches, so Surface::face of a base follows the chain as GetFace does. This replaces the face_owned flag the box code passed down (review item 2 keeps its Unported(topology.face_p2) result). The approximation-tolerance and surface-cache zone checks that asked "any owner" now ask GetFace(), as SmTol::GetZoneTol3d(const SmObject*) does. The six cases (three generated, base owned and shared) are fixtures. surface.offset_surface: 3072 cases, 3072 passed, 0 failed, 0 deferred. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
867 lines
27 KiB
C++
867 lines
27 KiB
C++
// SPDX-License-Identifier: Apache-2.0
|
|
// difftest shim for unit `surface.offset_surface`.
|
|
// Wraps SmOffsetSurface (source/SMLib/src/SmOffsetSurface.cpp, source/SMLib/inc/SmOffsetSurface.h):
|
|
// the constructor, Copy, operator==, CalculateBoundingBox, GetKnots, EvaluateNormal,
|
|
// EvaluatePoint, Evaluate, EvaluateSimple, GetNaturalUVDomain, Reverse, SplitAt,
|
|
// STEPInversion, SwapUV, Transform, TrimWithDomain, CreateOffsetSurface,
|
|
// CreateExtendedSurface (both), ReadFromDB, IsKindOf and the setters, run as a script on
|
|
// one offset so that the extension upstream's const evaluators store is seen by the
|
|
// following ops. WriteToDB is a generator helper.
|
|
//
|
|
// One entry point, sc_surface_offset_surface_eval(op, in, n_in, out, cap, out_len). Layouts
|
|
// are documented in crates/solidcore-ffi/src/units/surface_offset_surface.rs, which
|
|
// validates every input so upstream only touches valid memory. The surface-of-revolution
|
|
// block and its builder are those of surface_surf_of_revolution_p1.cpp.
|
|
#include "sc_shim.h"
|
|
#include "sc_offset_guard.h"
|
|
|
|
#include "StdAfx.h"
|
|
#include <SmAxis2Placement.h>
|
|
#include <SmBSplineCurve.h>
|
|
#include <SmBSplineSurface.h>
|
|
#include <SmCircle.h>
|
|
#include <SmContext.h>
|
|
#include <SmCurve.h>
|
|
#include <SmCurveBoundedSurface.h>
|
|
#include <SmDatabaseIO.h>
|
|
#include <SmExtent1d.h>
|
|
#include <SmExtent2d.h>
|
|
#include <SmExtent3d.h>
|
|
#include <SmLine.h>
|
|
#include <SmNurbsCrv.h>
|
|
#include <SmNurbsSrf.h>
|
|
#include <SmOffsetSurface.h>
|
|
#include <SmPlane.h>
|
|
#include <SmPolarBox.h>
|
|
#include <SmPseudoBox.h>
|
|
#include <SmSTEPSurface.h>
|
|
#include <SmSurfOfRevolution.h>
|
|
#include <SmSurface.h>
|
|
#include <SmTArray.h>
|
|
#include <SmVector2d.h>
|
|
#include <SmVector3d.h>
|
|
#include <nurbs.h>
|
|
|
|
#include <memory>
|
|
#include <cmath>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
// (see sc_offset_guard.h; set by the offset worker)
|
|
extern "C" int sc_offset_extension_guards_enabled = 0;
|
|
|
|
namespace
|
|
{
|
|
// Entry ops (keep in sync with crates/solidcore-ffi/src/units/surface_offset_surface.rs).
|
|
enum : int32_t
|
|
{
|
|
OP_SCRIPT = 0,
|
|
OP_WRITE_DB = 1,
|
|
OP_READ_DB = 2,
|
|
};
|
|
|
|
// Script steps.
|
|
enum : int64_t
|
|
{
|
|
S_STATE = 0,
|
|
S_EVAL = 1,
|
|
S_EVAL_SIMPLE = 2,
|
|
S_EVAL_PT = 3,
|
|
S_NORMAL = 4,
|
|
S_KNOTS = 5,
|
|
S_MAX_ANAL = 6,
|
|
S_BBOX = 7,
|
|
S_STEP_INV = 8,
|
|
S_REVERSE = 9,
|
|
S_SWAP = 10,
|
|
S_TRANSFORM = 11,
|
|
S_TRIM = 12,
|
|
S_SPLIT = 13,
|
|
S_CREATE_OFFSET = 14,
|
|
S_EXTEND = 15,
|
|
S_EXTEND_DIR = 16,
|
|
S_COPY = 17,
|
|
S_EQ = 18,
|
|
S_SET_DIST = 19,
|
|
S_SET_ALLOW = 20,
|
|
S_IS_PLANAR = 21,
|
|
S_IS_KIND_OF = 22,
|
|
S_BASE_OFFSET = 23,
|
|
};
|
|
|
|
// Generator kinds and construction modes of a surface-of-revolution block.
|
|
enum : int32_t
|
|
{
|
|
GK_BSPLINE = 0,
|
|
GK_LINE = 1,
|
|
GK_CIRCLE = 2,
|
|
};
|
|
enum : int32_t
|
|
{
|
|
BM_CANONICAL = 0,
|
|
BM_CTOR = 1,
|
|
};
|
|
|
|
// Reads the protected members of SmOffsetSurface through pointers to members named in a
|
|
// derived class.
|
|
struct PeekOff : SmOffsetSurface
|
|
{
|
|
static SmBoolean SmOffsetSurface::*allow() { return &PeekOff::m_bAllowExtension; }
|
|
static SmBoolean SmOffsetSurface::*owns() { return &PeekOff::m_bOwnsSurface; }
|
|
static SmBoolean SmOffsetSurface::*per_u() { return &PeekOff::m_bIsPeriodicOnU; }
|
|
static SmBoolean SmOffsetSurface::*per_v() { return &PeekOff::m_bIsPeriodicOnV; }
|
|
};
|
|
|
|
// Reaches the protected SmSurfOfRevolution constructor.
|
|
struct PeekRev : SmSurfOfRevolution
|
|
{
|
|
PeekRev(SmBSplineCurve* c, const SmPoint3d& o, const SmVector3d& x, const SmVector3d& y,
|
|
const SmExtent2d& d, SmBoolean swap)
|
|
: SmSurfOfRevolution(c, o, x, y, d, swap)
|
|
{
|
|
}
|
|
};
|
|
|
|
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; }
|
|
SmBoolean bflag() { return flag() ? TRUE : FALSE; }
|
|
SmVector3d vec()
|
|
{
|
|
double x = num();
|
|
double y = num();
|
|
double z = num();
|
|
return SmVector3d(x, y, z);
|
|
}
|
|
SmPoint2d uv()
|
|
{
|
|
double u = num();
|
|
double v = num();
|
|
return SmPoint2d(u, v);
|
|
}
|
|
SmExtent2d dom()
|
|
{
|
|
double u0 = num();
|
|
double v0 = num();
|
|
double u1 = num();
|
|
double v1 = num();
|
|
return SmExtent2d(SmPoint2d(u0, v0), SmPoint2d(u1, v1));
|
|
}
|
|
};
|
|
|
|
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<double>& o, const SmVector3d& v)
|
|
{
|
|
o.push_back(v.x);
|
|
o.push_back(v.y);
|
|
o.push_back(v.z);
|
|
}
|
|
|
|
void put_dom(std::vector<double>& o, const SmExtent2d& d)
|
|
{
|
|
o.push_back(d.GetMin().x);
|
|
o.push_back(d.GetMin().y);
|
|
o.push_back(d.GetMax().x);
|
|
o.push_back(d.GetMax().y);
|
|
}
|
|
|
|
void put_ivl(std::vector<double>& o, const SmExtent1d& e)
|
|
{
|
|
o.push_back(e.GetMin());
|
|
o.push_back(e.GetMax());
|
|
}
|
|
|
|
// Bases borrowed by an offset remain owned by its caller. Destroy the offset roots
|
|
// before this list, and the list before the context.
|
|
using BorrowedBases = std::vector<std::unique_ptr<SmSurface>>;
|
|
|
|
// The nonzero offset evaluator asks its base for a square derivative matrix; only
|
|
// zero-distance delegation reaches SmPlane's unsigned hv-k loop with caller orders.
|
|
bool delegates_to_plane(SmOffsetSurface* s, const SmPoint2d& uv, ULONG hu, ULONG hv, bool simple)
|
|
{
|
|
double distance = s->GetOffsetDistance();
|
|
SmSurface* base = s->GetBaseSurface();
|
|
if (auto* nested = SM_CAST_PTR(SmOffsetSurface, base))
|
|
{
|
|
distance += nested->GetOffsetDistance();
|
|
base = nested->GetBaseSurface();
|
|
}
|
|
if (std::max(hu, hv) > 3 || distance != 0.0) return false;
|
|
if (!base->GetNaturalUVDomain().ContainsPoint2d(uv) && s->GetExtendedBaseSurface())
|
|
base = s->GetExtendedBaseSurface();
|
|
// SmPlane::EvaluateSimple inherits the same evaluator. Other base kinds have
|
|
// their own checked derivative layouts; only this direct delegation is UB-163.
|
|
(void)simple;
|
|
return SM_CAST_PTR(SmPlane, base) != NULL;
|
|
}
|
|
|
|
// UB-162: with bOnlyUpperHalf, EvaluatePlainOrSimple's non-zero path writes the mixed
|
|
// derivatives at their full-layout indices, past a caller array sized (hu+1)(hv+1).
|
|
// Mirror its checks in order: offset order above 3 and an offset nested twice fail
|
|
// before anything is written, an exact 0.0 compound distance delegates to the base.
|
|
// (A base evaluation or extension failing first would also write nothing; that cannot
|
|
// be known in advance, so those inputs are refused too.)
|
|
ULONG max_write_index(ULONG hu, ULONG hv, bool upper)
|
|
{
|
|
ULONG hod = upper ? std::max(hu, hv) : hu + hv;
|
|
ULONG m = 0;
|
|
auto at = [&](ULONG i) { m = std::max(m, i); };
|
|
if (hu >= 1) at(hv + 1);
|
|
if (hv >= 1) at(1);
|
|
if (hu >= 2) at(2 * (hv + 1));
|
|
if ((upper && hod >= 2) || (!upper && hu >= 1 && hv >= 1)) at(hv + 2);
|
|
if (hv >= 2) at(2);
|
|
if (hu >= 3) at(3 * (hv + 1));
|
|
if ((upper && hod >= 3) || (!upper && hu >= 2 && hv >= 1)) at(2 * (hv + 1) + 1);
|
|
if ((upper && hod >= 3) || (!upper && hu >= 1 && hv >= 2)) at((hv + 1) + 2);
|
|
if (hv >= 3) at(3);
|
|
return m;
|
|
}
|
|
|
|
bool writes_past_array(SmOffsetSurface* s, ULONG hu, ULONG hv, bool upper)
|
|
{
|
|
ULONG hod = upper ? std::max(hu, hv) : hu + hv;
|
|
if (hod > 3) return false;
|
|
double distance = s->GetOffsetDistance();
|
|
if (auto* nested = SM_CAST_PTR(SmOffsetSurface, s->GetBaseSurface()))
|
|
{
|
|
if (SM_CAST_PTR(SmOffsetSurface, nested->GetBaseSurface())) return false;
|
|
distance += nested->GetOffsetDistance();
|
|
}
|
|
if (distance == 0.0) return false;
|
|
return max_write_index(hu, hv, upper) >= (hu + 1) * (hv + 1);
|
|
}
|
|
|
|
// ---- surface-of-revolution block (as surface_surf_of_revolution_p1.cpp) ----------------
|
|
|
|
SmBSplineCurve* read_rev_bspline(Rd& rd, const SmContext& ctx)
|
|
{
|
|
gw_INDEX n = (gw_INDEX)rd.idx();
|
|
gw_DEGREE p = (gw_DEGREE)rd.idx();
|
|
gw_INDEX m = (gw_INDEX)rd.idx();
|
|
gw_CURVE* cur = sm_AllocateNurbCurve(n, p, m);
|
|
for (gw_INDEX i = 0; i <= n; i++)
|
|
{
|
|
cur->pol->Pw[i].x = rd.num();
|
|
cur->pol->Pw[i].y = rd.num();
|
|
cur->pol->Pw[i].z = rd.num();
|
|
cur->pol->Pw[i].w = rd.num();
|
|
}
|
|
for (gw_INDEX i = 0; i <= m; i++)
|
|
cur->knt->U[i] = rd.num();
|
|
return new (ctx) SmBSplineCurve(cur, 3, FALSE);
|
|
}
|
|
|
|
SmBSplineCurve* read_gen(Rd& rd, const SmContext& ctx)
|
|
{
|
|
int64_t kind = rd.idx();
|
|
switch (kind)
|
|
{
|
|
case GK_BSPLINE: return read_rev_bspline(rd, ctx);
|
|
case GK_LINE:
|
|
{
|
|
SmPoint3d a = rd.vec();
|
|
SmPoint3d b = rd.vec();
|
|
SmLine* l = new (ctx) SmLine(a, b, 3, &ctx);
|
|
l->GetOrCreateGwNurbPointer();
|
|
return l;
|
|
}
|
|
case GK_CIRCLE:
|
|
{
|
|
SmPoint3d c = rd.vec();
|
|
SmVector3d x = rd.vec();
|
|
SmVector3d y = rd.vec();
|
|
double d0 = rd.num();
|
|
double d1 = rd.num();
|
|
double r = rd.num();
|
|
return new (ctx) SmCircle(c, x, y, SmExtent1d(d0, d1), r, 3, &ctx);
|
|
}
|
|
default: return NULL;
|
|
}
|
|
}
|
|
|
|
// Writes nothing; returns NULL and sets `stt` when the construction fails.
|
|
SmSurfOfRevolution* read_revolution(Rd& rd, const SmContext& ctx, SmStatus& stt)
|
|
{
|
|
int64_t mode = rd.idx();
|
|
std::unique_ptr<SmBSplineCurve> gen(read_gen(rd, ctx));
|
|
bool oob = rd.flag();
|
|
bool swap = rd.flag();
|
|
SmSurfOfRevolution* s = NULL;
|
|
stt = SM_SUCCESS;
|
|
if (mode == BM_CANONICAL)
|
|
{
|
|
SmPoint3d p = rd.vec();
|
|
SmVector3d a = rd.vec();
|
|
stt = SmSurfOfRevolution::CreateCanonical(ctx, gen.get(), p, a, s, swap ? TRUE : FALSE);
|
|
if (s) gen.release(); // ownership transfers even if MakeNurb then fails
|
|
}
|
|
else
|
|
{
|
|
SmPoint3d org = rd.vec();
|
|
SmVector3d x = rd.vec();
|
|
SmVector3d y = rd.vec();
|
|
double a = rd.num();
|
|
double b = rd.num();
|
|
double c = rd.num();
|
|
double d = rd.num();
|
|
s = new (ctx) PeekRev(gen.get(), org, x, y, SmExtent2d(a, b, c, d), swap ? TRUE : FALSE);
|
|
gen.release();
|
|
stt = s->MakeNurb();
|
|
}
|
|
if (stt != SM_SUCCESS)
|
|
{
|
|
delete s;
|
|
return NULL;
|
|
}
|
|
s->SetOutOfBoundsEnabled(oob ? TRUE : FALSE);
|
|
return s;
|
|
}
|
|
|
|
// ---- base and offset blocks -------------------------------------------------------------
|
|
|
|
SmOffsetSurface* read_offset(Rd& rd, const SmContext& ctx, SmStatus& stt, BorrowedBases& borrowed);
|
|
|
|
// A base block; NULL (with `stt`) when a surface of revolution fails to build.
|
|
SmSurface* read_surface(Rd& rd, const SmContext& ctx, SmStatus& stt, BorrowedBases& borrowed)
|
|
{
|
|
int64_t kind = rd.idx();
|
|
switch (kind)
|
|
{
|
|
case 0:
|
|
{
|
|
bool oob = rd.flag();
|
|
SmBSplineSurfaceForm form = (SmBSplineSurfaceForm)rd.idx();
|
|
gw_INDEX n = (gw_INDEX)rd.idx();
|
|
gw_INDEX m = (gw_INDEX)rd.idx();
|
|
gw_DEGREE p = (gw_DEGREE)rd.idx();
|
|
gw_DEGREE q = (gw_DEGREE)rd.idx();
|
|
gw_INDEX r = (gw_INDEX)rd.idx();
|
|
gw_INDEX s = (gw_INDEX)rd.idx();
|
|
gw_SURFACE* sur = sm_AllocateNurbSurface(n, m, p, q, r, s);
|
|
for (gw_INDEX i = 0; i <= n; i++)
|
|
for (gw_INDEX j = 0; j <= m; j++)
|
|
{
|
|
sur->net->Pw[i][j].x = rd.num();
|
|
sur->net->Pw[i][j].y = rd.num();
|
|
sur->net->Pw[i][j].z = rd.num();
|
|
sur->net->Pw[i][j].w = rd.num();
|
|
}
|
|
for (gw_INDEX i = 0; i <= r; i++)
|
|
sur->knu->U[i] = rd.num();
|
|
for (gw_INDEX i = 0; i <= s; i++)
|
|
sur->knv->U[i] = rd.num();
|
|
SmBSplineSurface* b = new (ctx) SmBSplineSurface(sur, FALSE, NULL);
|
|
b->GetOutOfBoundsEnabled() = oob ? TRUE : FALSE;
|
|
b->SetBSplineSurfaceForm(form);
|
|
return b;
|
|
}
|
|
case 1:
|
|
{
|
|
SmPoint3d o = rd.vec();
|
|
SmVector3d n = rd.vec();
|
|
return new (ctx) SmPlane(o, n, &ctx);
|
|
}
|
|
case 2:
|
|
{
|
|
std::unique_ptr<SmSurface> basis(read_surface(rd, ctx, stt, borrowed));
|
|
bool implicit = rd.flag();
|
|
if (basis == NULL)
|
|
return NULL;
|
|
SmTArray<SmCurve*> c3d, cuv;
|
|
auto* result = new (ctx) SmCurveBoundedSurface(*basis, &c3d, &cuv, implicit ? TRUE : FALSE);
|
|
basis.release();
|
|
return result;
|
|
}
|
|
case 3:
|
|
{
|
|
std::unique_ptr<SmSurface> base(read_surface(rd, ctx, stt, borrowed));
|
|
if (base == NULL)
|
|
return NULL;
|
|
auto* result = new (ctx) SmSTEPSurface(*base, TRUE);
|
|
base.release();
|
|
return result;
|
|
}
|
|
case 4: return read_revolution(rd, ctx, stt);
|
|
default: return read_offset(rd, ctx, stt, borrowed);
|
|
}
|
|
}
|
|
|
|
// An offset block `d owns sing allow base`.
|
|
SmOffsetSurface* read_offset(Rd& rd, const SmContext& ctx, SmStatus& stt, BorrowedBases& borrowed)
|
|
{
|
|
double d = rd.num();
|
|
SmBoolean owns = rd.bflag();
|
|
ULONG sing = (ULONG)rd.idx();
|
|
SmBoolean allow = rd.bflag();
|
|
std::unique_ptr<SmSurface> base(read_surface(rd, ctx, stt, borrowed));
|
|
if (base == NULL)
|
|
return NULL;
|
|
std::unique_ptr<SmOffsetSurface> off(new (ctx) SmOffsetSurface(d, *base, owns, sing));
|
|
if (owns) base.release();
|
|
else borrowed.push_back(std::move(base));
|
|
off->SetAllowExtension(allow);
|
|
return off.release();
|
|
}
|
|
|
|
// ---- outputs ------------------------------------------------------------------------------
|
|
|
|
// A 3x3 EvaluatePoint grid over the natural domain: each status and point.
|
|
void put_grid(std::vector<double>& o, const SmSurface* s)
|
|
{
|
|
SmExtent2d d = s->GetNaturalUVDomain();
|
|
for (int i = 0; i < 3; i++)
|
|
for (int j = 0; j < 3; j++)
|
|
{
|
|
SmPoint2d uv = d.Evaluate(i / 2.0, j / 2.0);
|
|
SmPoint3d p;
|
|
SmStatus stt = s->EvaluatePoint(uv, p);
|
|
o.push_back(st(stt));
|
|
if (stt == SM_SUCCESS)
|
|
put_vec(o, p);
|
|
}
|
|
}
|
|
|
|
void put_state(std::vector<double>& o, SmOffsetSurface* s)
|
|
{
|
|
put_dom(o, s->GetNaturalUVDomain());
|
|
o.push_back(s->GetOffsetDistance());
|
|
o.push_back(flg(s->*PeekOff::allow()));
|
|
o.push_back(flg(s->*PeekOff::owns()));
|
|
o.push_back((double)(s->*PeekOff::per_u()));
|
|
o.push_back((double)(s->*PeekOff::per_v()));
|
|
o.push_back((double)s->GetSingularities());
|
|
SmSurface* e = s->GetExtendedBaseSurface();
|
|
o.push_back(e ? 1.0 : 0.0);
|
|
if (e)
|
|
{
|
|
put_dom(o, e->GetNaturalUVDomain());
|
|
o.push_back((double)e->GetType());
|
|
}
|
|
SmSurface* b = s->GetBaseSurface();
|
|
o.push_back((double)b->GetType());
|
|
put_dom(o, b->GetNaturalUVDomain());
|
|
o.push_back((double)s->GetType());
|
|
put_grid(o, s);
|
|
}
|
|
|
|
// A surface an op returns: its type, then the offset's state, or (another kind) its
|
|
// natural domain and point grid.
|
|
void put_result(std::vector<double>& o, SmSurface* r)
|
|
{
|
|
o.push_back((double)r->GetType());
|
|
SmOffsetSurface* off = SM_CAST_PTR(SmOffsetSurface, r);
|
|
if (off)
|
|
{
|
|
put_state(o, off);
|
|
}
|
|
else
|
|
{
|
|
put_dom(o, r->GetNaturalUVDomain());
|
|
put_grid(o, r);
|
|
}
|
|
}
|
|
|
|
// The entries upstream writes: all, or with bOnlyUpperHalf those with i + j up to the
|
|
// larger order (the others are untouched caller memory).
|
|
void put_matrix(std::vector<double>& o, const std::vector<SmVector3d>& a, ULONG hu, ULONG hv, bool upper)
|
|
{
|
|
ULONG hod = hu > hv ? hu : hv;
|
|
for (ULONG i = 0; i <= hu; i++)
|
|
for (ULONG j = 0; j <= hv; j++)
|
|
if (!upper || i + j <= hod)
|
|
put_vec(o, a[i * (hv + 1) + j]);
|
|
}
|
|
|
|
// ---- the script -----------------------------------------------------------------------------
|
|
|
|
void run_step(int64_t step, Rd& rd, const SmContext& ctx, SmOffsetSurface* s, std::vector<double>& o)
|
|
{
|
|
switch (step)
|
|
{
|
|
case S_STATE:
|
|
o.push_back(0.0);
|
|
put_state(o, s);
|
|
break;
|
|
case S_EVAL:
|
|
case S_EVAL_SIMPLE:
|
|
{
|
|
SmPoint2d uv = rd.uv();
|
|
ULONG hu = (ULONG)rd.idx();
|
|
ULONG hv = (ULONG)rd.idx();
|
|
SmBoolean ul = rd.bflag();
|
|
SmBoolean vl = rd.bflag();
|
|
SmBoolean upper = rd.bflag();
|
|
if (upper && hu > hv && delegates_to_plane(s, uv, hu, hv, step == S_EVAL_SIMPLE)) throw ScOffsetUnsafeInput{};
|
|
if (writes_past_array(s, hu, hv, upper)) throw ScOffsetUnsafeInput{};
|
|
std::vector<SmVector3d> a((hu + 1) * (hv + 1)); // SM_UNDEF_DOUBLE entries
|
|
SmStatus stt;
|
|
if (step == S_EVAL)
|
|
{
|
|
SmBoolean nzt = rd.bflag();
|
|
stt = s->Evaluate(uv, hu, hv, ul, vl, upper, a.data(), nzt);
|
|
}
|
|
else
|
|
{
|
|
stt = s->EvaluateSimple(uv, hu, hv, ul, vl, upper, a.data());
|
|
}
|
|
o.push_back(st(stt));
|
|
if (stt == SM_SUCCESS)
|
|
put_matrix(o, a, hu, hv, upper != FALSE);
|
|
break;
|
|
}
|
|
case S_EVAL_PT:
|
|
{
|
|
SmPoint2d uv = rd.uv();
|
|
SmPoint3d p;
|
|
SmStatus stt = s->EvaluatePoint(uv, p);
|
|
o.push_back(st(stt));
|
|
if (stt == SM_SUCCESS)
|
|
put_vec(o, p);
|
|
break;
|
|
}
|
|
case S_NORMAL:
|
|
{
|
|
SmPoint2d uv = rd.uv();
|
|
SmBoolean ul = rd.bflag();
|
|
SmBoolean vl = rd.bflag();
|
|
SmVector3d n;
|
|
SmStatus stt = s->EvaluateNormal(uv, ul, vl, n);
|
|
o.push_back(st(stt));
|
|
if (stt == SM_SUCCESS)
|
|
put_vec(o, n);
|
|
break;
|
|
}
|
|
case S_KNOTS:
|
|
{
|
|
SmSurfParamType param = (SmSurfParamType)rd.idx();
|
|
bool has_ivl = rd.flag();
|
|
double a = rd.num();
|
|
double b = rd.num();
|
|
SmExtent1d ivl(a, b);
|
|
SmTArray<double> knots;
|
|
SmTArray<ULONG> mults;
|
|
SmStatus stt = s->GetKnots(param, knots, &mults, has_ivl ? &ivl : NULL);
|
|
o.push_back(st(stt));
|
|
if (stt == SM_SUCCESS)
|
|
{
|
|
o.push_back((double)knots.GetSize());
|
|
for (ULONG i = 0; i < knots.GetSize(); i++)
|
|
o.push_back(knots[i]);
|
|
o.push_back((double)mults.GetSize());
|
|
for (ULONG i = 0; i < mults.GetSize(); i++)
|
|
o.push_back((double)mults[i]);
|
|
}
|
|
break;
|
|
}
|
|
case S_MAX_ANAL:
|
|
o.push_back(0.0);
|
|
put_dom(o, s->GetMaxAnalyticDomain());
|
|
break;
|
|
case S_BBOX:
|
|
{
|
|
SmExtent2d d = rd.dom();
|
|
bool w0 = rd.flag();
|
|
bool w1 = rd.flag();
|
|
bool w2 = rd.flag();
|
|
SmBoolean expand = rd.bflag();
|
|
// Follow SmOffsetSurface::CalculateBoundingBox down the chain on the actual
|
|
// surfaces after the preceding ops: each level tests the domain against its own
|
|
// m_pSurface and passes the call on, or runs the SmSurface body on itself, whose
|
|
// offset surface cache can recurse (UB-164) or subdivide without progress
|
|
// (HANG-17). Refuse a box that reaches that body at any level.
|
|
for (SmOffsetSurface* level = s; level != NULL;
|
|
level = SM_CAST_PTR(SmOffsetSurface, level->GetBaseSurface()))
|
|
{
|
|
if (!d.IsContainedBy(level->GetBaseSurface()->GetNaturalUVDomain(), SM_EFF_ZERO))
|
|
throw ScOffsetUnsafeInput{};
|
|
}
|
|
SmExtent3d nb;
|
|
SmPseudoBox pb;
|
|
SmPolarBox qb;
|
|
SmStatus stt = s->CalculateBoundingBox(d, w0 ? &nb : NULL, w1 ? &pb : NULL, w2 ? &qb : NULL, NULL,
|
|
NULL, expand);
|
|
o.push_back(st(stt));
|
|
if (stt == SM_SUCCESS)
|
|
{
|
|
if (w0)
|
|
{
|
|
put_vec(o, nb.GetMin());
|
|
put_vec(o, nb.GetMax());
|
|
}
|
|
if (w1)
|
|
{
|
|
for (ULONG i = 0; i < 3; i++)
|
|
put_vec(o, pb.GetBasis(i));
|
|
for (ULONG i = 0; i < 3; i++)
|
|
put_ivl(o, pb.GetInterval(i));
|
|
}
|
|
if (w2)
|
|
{
|
|
o.push_back((double)qb.GetPolarBoxState());
|
|
for (ULONG i = 0; i < 3; i++)
|
|
put_vec(o, *qb.GetBasis(i));
|
|
put_dom(o, qb.GetDomain());
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case S_STEP_INV:
|
|
{
|
|
SmExtent2d d = rd.dom();
|
|
SmPoint3d p = rd.vec();
|
|
double tol = rd.num();
|
|
bool has_guess = rd.flag();
|
|
SmPoint2d g = rd.uv();
|
|
SmPoint2d uv(0.0, 0.0);
|
|
SmLocationType loc = SM_LT_INTERIOR;
|
|
SmStatus stt = s->STEPInversion(d, p, tol, uv, loc, has_guess ? &g : NULL);
|
|
o.push_back(st(stt));
|
|
if (stt == SM_SUCCESS)
|
|
{
|
|
o.push_back(uv.x);
|
|
o.push_back(uv.y);
|
|
o.push_back((double)loc);
|
|
}
|
|
break;
|
|
}
|
|
case S_REVERSE:
|
|
o.push_back(st(s->Reverse((SmSurfParamType)rd.idx())));
|
|
break;
|
|
case S_SWAP:
|
|
o.push_back(st(s->SwapUV()));
|
|
break;
|
|
case S_TRANSFORM:
|
|
{
|
|
SmPoint3d org = rd.vec();
|
|
SmVector3d x = rd.vec();
|
|
SmVector3d y = rd.vec();
|
|
bool has_scale = rd.flag();
|
|
SmVector3d scale = rd.vec();
|
|
SmAxis2Placement ax(org, x, y);
|
|
o.push_back(st(s->Transform(ax, has_scale ? &scale : NULL)));
|
|
break;
|
|
}
|
|
case S_TRIM:
|
|
{
|
|
SmExtent2d d = rd.dom();
|
|
SmStatus stt = s->TrimWithDomain(d);
|
|
o.push_back(st(stt));
|
|
if (stt == SM_SUCCESS)
|
|
put_dom(o, d);
|
|
break;
|
|
}
|
|
case S_SPLIT:
|
|
{
|
|
double t = rd.num();
|
|
SmSurfParamType param = (SmSurfParamType)rd.idx();
|
|
SmSurface* l = NULL;
|
|
SmSurface* r = NULL;
|
|
SmStatus stt = s->SplitAt(ctx, t, param, l, r);
|
|
std::unique_ptr<SmSurface> left(l), right(r);
|
|
o.push_back(st(stt));
|
|
if (stt == SM_SUCCESS)
|
|
{
|
|
put_result(o, l);
|
|
put_result(o, r);
|
|
|
|
}
|
|
break;
|
|
}
|
|
case S_CREATE_OFFSET:
|
|
case S_EXTEND:
|
|
case S_EXTEND_DIR:
|
|
case S_COPY:
|
|
{
|
|
SmSurface* r = NULL;
|
|
SmStatus stt;
|
|
if (step == S_CREATE_OFFSET)
|
|
{
|
|
double d = rd.num();
|
|
stt = s->CreateOffsetSurface(ctx, d, 0.0, r);
|
|
}
|
|
else if (step == S_EXTEND)
|
|
{
|
|
// CreateExtendedSurface reaches UB-107 and the HANG-6/HANG-18 searches,
|
|
// guarded only by the worker's link wrappers (not on every platform).
|
|
if (!sc_offset_extensions_guarded()) throw ScOffsetUnsafeInput{};
|
|
double dist = rd.num();
|
|
SmContinuityType cont = (SmContinuityType)rd.idx();
|
|
stt = s->CreateExtendedSurface(ctx, dist, cont, r);
|
|
}
|
|
else if (step == S_EXTEND_DIR)
|
|
{
|
|
if (!sc_offset_extensions_guarded()) throw ScOffsetUnsafeInput{};
|
|
SmSurfParamType param = (SmSurfParamType)rd.idx();
|
|
double dist = rd.num();
|
|
SmContinuityType cont = (SmContinuityType)rd.idx();
|
|
stt = s->CreateExtendedSurface(ctx, param, dist, cont, r);
|
|
}
|
|
else
|
|
{
|
|
stt = s->Copy(ctx, r);
|
|
}
|
|
std::unique_ptr<SmSurface> result(r);
|
|
o.push_back(st(stt));
|
|
if (stt == SM_SUCCESS && r)
|
|
{
|
|
put_result(o, r);
|
|
}
|
|
break;
|
|
}
|
|
case S_EQ:
|
|
{
|
|
SmStatus bst = SM_SUCCESS;
|
|
BorrowedBases borrowed;
|
|
std::unique_ptr<SmOffsetSurface> other(read_offset(rd, ctx, bst, borrowed));
|
|
o.push_back(st(bst));
|
|
if (other)
|
|
{
|
|
o.push_back(flg(*s == *other));
|
|
o.push_back(flg(*s == *s));
|
|
|
|
}
|
|
break;
|
|
}
|
|
case S_SET_DIST:
|
|
s->SetOffsetDistance(rd.num());
|
|
o.push_back(0.0);
|
|
break;
|
|
case S_SET_ALLOW:
|
|
s->SetAllowExtension(rd.bflag());
|
|
o.push_back(0.0);
|
|
break;
|
|
case S_IS_PLANAR:
|
|
{
|
|
double tol = rd.num();
|
|
SmPoint3d p(SM_UNDEF_DOUBLE, SM_UNDEF_DOUBLE, SM_UNDEF_DOUBLE);
|
|
SmVector3d n(SM_UNDEF_DOUBLE, SM_UNDEF_DOUBLE, SM_UNDEF_DOUBLE);
|
|
double dev = SM_UNDEF_DOUBLE;
|
|
SmBoolean r = s->IsPlanar(tol, &p, &n, &dev);
|
|
o.push_back(0.0);
|
|
o.push_back(flg(r));
|
|
put_vec(o, p);
|
|
put_vec(o, n);
|
|
o.push_back(dev);
|
|
break;
|
|
}
|
|
case S_IS_KIND_OF:
|
|
o.push_back(0.0);
|
|
o.push_back(flg(s->IsKindOf((SM_TYPE)rd.idx())));
|
|
break;
|
|
case S_BASE_OFFSET:
|
|
{
|
|
// the virtual through SmSurface*: SmPlane, SmSurfOfRevolution and SmOffsetSurface
|
|
// override it; SmBSplineSurface's forwards to the SmSurface body
|
|
double d = rd.num();
|
|
double tol = rd.num();
|
|
SmSurface* r = NULL;
|
|
SmStatus stt = s->GetBaseSurface()->CreateOffsetSurface(ctx, d, tol, r);
|
|
std::unique_ptr<SmSurface> result(r);
|
|
o.push_back(st(stt));
|
|
if (stt == SM_SUCCESS && r) put_result(o, r);
|
|
break;
|
|
}
|
|
default: o.push_back((double)SC_ERR_ARG); break;
|
|
}
|
|
}
|
|
|
|
void run_script(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
|
{
|
|
SmStatus bst = SM_SUCCESS;
|
|
BorrowedBases borrowed;
|
|
std::unique_ptr<SmOffsetSurface> s(read_offset(rd, ctx, bst, borrowed));
|
|
o.push_back(st(bst));
|
|
int64_t n_steps = rd.idx();
|
|
if (s == NULL)
|
|
{
|
|
// skip the steps (the record is the construction status only)
|
|
rd.at = -1;
|
|
return;
|
|
}
|
|
for (int64_t i = 0; i < n_steps; i++)
|
|
run_step(rd.idx(), rd, ctx, s.get(), o);
|
|
}
|
|
|
|
void write_db(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
|
{
|
|
SmStatus bst = SM_SUCCESS;
|
|
BorrowedBases borrowed;
|
|
std::unique_ptr<SmOffsetSurface> s(read_offset(rd, ctx, bst, borrowed));
|
|
bool binary = rd.flag();
|
|
ULONG version = (ULONG)rd.idx();
|
|
o.push_back(st(bst));
|
|
if (s == NULL)
|
|
return;
|
|
std::ostringstream oss;
|
|
oss.precision(17);
|
|
SmDatabaseIOStream db(binary ? SM_BINARY : SM_ASCII, &oss);
|
|
SmStatus stt = s->WriteToDB(db, version);
|
|
o.push_back(st(stt));
|
|
std::string bytes = oss.str();
|
|
for (char ch : bytes)
|
|
o.push_back((double)(unsigned char)ch);
|
|
}
|
|
|
|
void read_db(Rd& rd, const SmContext& ctx, std::vector<double>& o)
|
|
{
|
|
bool binary = rd.flag();
|
|
ULONG version = (ULONG)rd.idx();
|
|
int64_t nb = rd.idx();
|
|
std::string bytes;
|
|
for (int64_t i = 0; i < nb; i++)
|
|
bytes.push_back((char)(unsigned char)rd.idx());
|
|
std::istringstream iss(bytes);
|
|
SmDatabaseIOStream db(binary ? SM_BINARY : SM_ASCII, &iss);
|
|
SmSurface* s = NULL;
|
|
SmStatus stt = SmOffsetSurface::ReadFromDB(SmOffsetSurface_TYPE, db, ctx, s, version);
|
|
o.push_back(st(stt));
|
|
if (stt == SM_SUCCESS && s)
|
|
put_state(o, (SmOffsetSurface*)s);
|
|
delete s;
|
|
}
|
|
} // namespace
|
|
|
|
SC_EXPORT int32_t sc_surface_offset_surface_eval(int32_t op, 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)
|
|
return SC_ERR_ARG;
|
|
SC_TRY
|
|
SmContext ctx;
|
|
Rd rd{in, 0};
|
|
std::vector<double> o;
|
|
switch (op)
|
|
{
|
|
case OP_SCRIPT: run_script(rd, ctx, o); break;
|
|
case OP_WRITE_DB: write_db(rd, ctx, o); break;
|
|
case OP_READ_DB: read_db(rd, ctx, o); break;
|
|
default: *out_len = 0; return SC_ERR_ARG;
|
|
}
|
|
// (a failed construction skips the rest of the input)
|
|
if (rd.at != n_in && rd.at != -1)
|
|
{
|
|
*out_len = 0;
|
|
return SC_ERR_ARG;
|
|
}
|
|
return sc_copy_out(o.data(), (int64_t)o.size(), out, cap, out_len);
|
|
}
|
|
catch (const ScOffsetUnsafeInput&)
|
|
{
|
|
*out_len = 0;
|
|
return SC_ERR_ARG;
|
|
}
|
|
catch (...)
|
|
{
|
|
*out_len = 0;
|
|
return SC_ERR_EXCEPTION;
|
|
}
|
|
}
|