Files
solvecraft/harness/cpp/shim/geom_tol.cpp
T
Brett Lawson a42ce1e9e6 wire: geom.tol SmTol overloads over topology::Brep
Port the SmBrep overloads of SmTol (Get/SetModelSizeEstimate,
Get/SetLargeSmallSizeRatio, GetZoneTol3d/GetApproxTol3d/GetXSectTol3d,
CalcFace/Edge/VertexZoneTol3d and GetScaledZero) as geom::tol::brep_* and
scaled_zero_of_brep, and use them in place of the local copies in
heal_data p2, vertex corner, brep_cache and the fillet finish. Add
geom.tol brep difftest cases (shim sc_geom_tol_brep) and unit tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MNXLXj2sJf943y2dDF36Vy
2026-10-30 13:52:04 +01:00

661 lines
23 KiB
C++

// SPDX-License-Identifier: Apache-2.0
// difftest shim for unit `geom.tol`.
// Wraps upstream SmTol (source/SMLib/inc/SmTol.h, source/SMLib/src/SmTol.cpp) and
// SmRelation (source/SMLib/inc/SmRelation.h).
#include "sc_shim.h"
#include "support/topology_fixture.h"
#include "StdAfx.h"
#include <SmBSplineCurve.h>
#include <SmBSplineVolume.h>
#include <SmBrep.h>
#include <SmCacheMgr.h>
#include <SmFace.h>
#include <SmSurface.h>
#include <SmCircle.h>
#include <SmCurve.h>
#include <SmLine.h>
#include <SmMemory.h>
#include <SmNurbsCrv.h>
#include <SmNurbsVol.h>
#include <SmVolume.h>
#include <nurbs.h>
#include <SmContext.h>
#include <SmExtent1d.h>
#include <SmExtent2d.h>
#include <SmExtent3d.h>
#include <SmRelation.h>
#include <SmTol.h>
#include <SmVector2d.h>
#include <SmVector3d.h>
#include <algorithm>
#include <cmath>
#include <memory>
#include <stdexcept>
#include <vector>
// Operation codes (keep in sync with crates/solidcore-ffi/src/units/geom_tol.rs).
enum ScTolOp : int32_t
{
OP_SIZE_ESTIMATE = 0, // SizeEstimateToZoneTol3d(a0)
OP_IN_TOL = 1, // InTol(a0, SmScaledZero(a1))
OP_CALC_FACE_ZONE = 2, // CalcFaceZoneTol3d()
OP_CALC_EDGE_ZONE = 3, // CalcEdgeZoneTol3d(a0, a1, flag ? &SmZoneTol3d(a2) : NULL)
OP_CALC_VERTEX_ZONE = 4, // CalcVertexZoneTol3d(a0, a1, flag ? &SmZoneTol3d(a2) : NULL)
OP_EFF_ZEROS = 5, // GetEffZero, GetEffZeroSq, GetEffZeroParam, GetEffZeroAngRad
OP_XSECT_FROM_ZONES = 6, // GetXSectTol3d(SmZoneTol3d(a0), SmZoneTol3d(a1))
OP_ANG_TOL_RAD = 7, // GetAngTolRad()
OP_MODEL_SIZE = 8, // GetModelSizeEstimate()
OP_ARE_PARALLEL = 9, // AreParallel(v(a0..a2), v(a3..a5), flag ? &a6 : NULL)
OP_ARE_PERPENDICULAR = 10, // ArePerpendicular(..)
OP_IS_ZERO_SPEED = 11, // IsZeroSpeed(a0, a1, flag ? &SmScaledZero(a2) : NULL)
OP_IS_DEGENERATE_V3 = 12, // IsDegenerate(v(a0..a2), flag ? &SmXSectTol3d(a3) : NULL)
OP_GAP_GAIN = 13, // GetGapGain()
OP_ZONE_SYS = 14, // GetZoneTol3d(small)
OP_ZONE_CTX = 15, // GetZoneTol3d((const SmContext*)NULL, small)
OP_APPROX_SYS = 16, // GetApproxTol3d(small)
OP_APPROX_CTX = 17, // GetApproxTol3d((const SmContext*)NULL, small)
OP_XSECT_SYS = 18, // GetXSectTol3d(small)
OP_XSECT_CTX = 19, // GetXSectTol3d((const SmContext*)NULL, small)
OP_SCALED_ZERO_SYS = 20, // GetScaledZero(), GetScaledZeroSq(), GetScaledZeroSqrt()
OP_SCALED_ZERO_N = 21, // GetScaledZero{,Sq,Sqrt}(o1[, o2[, o3[, o4]]]) (n = small)
OP_TOL_CTORS = 22, // all tolerance constructors and operator=(double) of a0
OP_TOL_MUL = 23, // SmTol3d t(a0); t *= a1
OP_MAP_TO_1D = 24, // MapTo1d(SmTol3d(a0), v(a1..a3), SmExtent1d(a4, a5))
};
// Object kinds for OP_SCALED_ZERO_N (6 doubles each in `objs`).
enum ScTolKind : int32_t
{
KIND_DOUBLE = 0, // d[0]
KIND_VEC2 = 1, // SmVector2d(d[0], d[1])
KIND_VEC3 = 2, // SmVector3d(d[0], d[1], d[2])
KIND_EXTENT3 = 3, // SmExtent3d(d[0..6])
KIND_EXTENT1 = 4, // SmExtent1d(d[0], d[1])
KIND_EXTENT2 = 5, // SmExtent2d(d[0], d[1], d[2], d[3])
};
namespace
{
// Calls f with the upstream object of kind `kind` built from d[0..6].
template <class F> void sc_with_obj(int32_t kind, const double* d, F f)
{
switch (kind)
{
case KIND_VEC2: f(SmVector2d(d[0], d[1])); break;
case KIND_VEC3: f(SmVector3d(d[0], d[1], d[2])); break;
case KIND_EXTENT3: f(SmExtent3d(d[0], d[1], d[2], d[3], d[4], d[5])); break;
case KIND_EXTENT1: f(SmExtent1d(d[0], d[1])); break;
case KIND_EXTENT2: f(SmExtent2d(d[0], d[1], d[2], d[3])); break;
default: f(d[0]); break;
}
}
// Sets the system tolerance statics, restoring the defaults on scope exit.
struct ScSystemTol
{
ScSystemTol(double dModelSize, double dRatio)
{
SmTol::SetModelSizeEstimate(dModelSize);
SmTol::SetLargeSmallSizeRatio(dRatio);
}
~ScSystemTol()
{
SmTol::SetModelSizeEstimate(SM_USE_DEFAULT);
SmTol::SetLargeSmallSizeRatio(SM_USE_DEFAULT);
}
};
} // namespace
// Runs one SmTol operation with the system tolerances set from (model_size, ratio).
// a[8] : scalar operands
// flag : optional-argument switch (non-zero = pass the pointer)
// small : SmSmallTopoType (0..4), or the object count for OP_SCALED_ZERO_N (1..4)
// kinds[4], objs[24] : objects of OP_SCALED_ZERO_N
// out[16]: result values; *out_n receives how many were written
SC_EXPORT int32_t sc_geom_tol_op(int32_t op, double model_size, double ratio, const double* a,
int32_t flag, int32_t small, const int32_t* kinds,
const double* objs, double* out, int32_t* out_n)
{
SC_TRY
ScSystemTol sSys(model_size, ratio);
SmSmallTopoType eSmall = (SmSmallTopoType)small;
int32_t n = 0;
auto put = [&](double v) { out[n++] = v; };
switch (op)
{
case OP_SIZE_ESTIMATE: put(SmTol::SizeEstimateToZoneTol3d(a[0])); break;
case OP_IN_TOL:
{
SmScaledZero sTol(a[1]);
put(SmTol::InTol(a[0], sTol) ? 1.0 : 0.0);
break;
}
case OP_CALC_FACE_ZONE: put(SmTol::CalcFaceZoneTol3d()); break;
case OP_CALC_EDGE_ZONE:
{
SmZoneTol3d sMax(a[2]);
put(SmTol::CalcEdgeZoneTol3d(a[0], a[1], flag ? &sMax : (SmZoneTol3d*)NULL));
break;
}
case OP_CALC_VERTEX_ZONE:
{
SmZoneTol3d sMax(a[2]);
put(SmTol::CalcVertexZoneTol3d(a[0], a[1], flag ? &sMax : (SmZoneTol3d*)NULL));
break;
}
case OP_EFF_ZEROS:
put(SmTol::GetEffZero());
put(SmTol::GetEffZeroSq());
put(SmTol::GetEffZeroParam());
put(SmTol::GetEffZeroAngRad());
break;
case OP_XSECT_FROM_ZONES:
{
SmZoneTol3d s1(a[0]), s2(a[1]);
put(SmTol::GetXSectTol3d(s1, s2));
break;
}
case OP_ANG_TOL_RAD: put(SmTol::GetAngTolRad()); break;
case OP_MODEL_SIZE: put(SmTol::GetModelSizeEstimate()); break;
case OP_ARE_PARALLEL:
case OP_ARE_PERPENDICULAR:
{
SmVector3d v1(a[0], a[1], a[2]), v2(a[3], a[4], a[5]);
double dAng = a[6];
double* pAng = flag ? &dAng : NULL;
SmBoolean b = op == OP_ARE_PARALLEL ? SmTol::AreParallel(&v1, &v2, pAng)
: SmTol::ArePerpendicular(&v1, &v2, pAng);
put(b ? 1.0 : 0.0);
break;
}
case OP_IS_ZERO_SPEED:
{
SmScaledZero sZero(a[2]);
put(SmTol::IsZeroSpeed(a[0], a[1], flag ? &sZero : NULL) ? 1.0 : 0.0);
break;
}
case OP_IS_DEGENERATE_V3:
{
SmVector3d v(a[0], a[1], a[2]);
SmXSectTol3d sTol(a[3]);
put(SmTol::IsDegenerate(v, flag ? &sTol : NULL) ? 1.0 : 0.0);
break;
}
case OP_GAP_GAIN: put(SmTol::GetGapGain()); break;
case OP_ZONE_SYS: put(SmTol::GetZoneTol3d(eSmall)); break;
case OP_ZONE_CTX: put(SmTol::GetZoneTol3d((const SmContext*)NULL, eSmall)); break;
case OP_APPROX_SYS: put(SmTol::GetApproxTol3d(eSmall)); break;
case OP_APPROX_CTX: put(SmTol::GetApproxTol3d((const SmContext*)NULL, eSmall)); break;
case OP_XSECT_SYS: put(SmTol::GetXSectTol3d(eSmall)); break;
case OP_XSECT_CTX: put(SmTol::GetXSectTol3d((const SmContext*)NULL, eSmall)); break;
case OP_SCALED_ZERO_SYS:
put(SmTol::GetScaledZero());
put(SmTol::GetScaledZeroSq());
put(SmTol::GetScaledZeroSqrt());
break;
case OP_SCALED_ZERO_N:
{
const double* d0 = objs;
const double* d1 = objs + 6;
const double* d2 = objs + 12;
const double* d3 = objs + 18;
if (small == 1)
{
sc_with_obj(kinds[0], d0, [&](const auto& o0) {
put(SmTol::GetScaledZero(o0));
put(SmTol::GetScaledZeroSq(o0));
put(SmTol::GetScaledZeroSqrt(o0));
});
}
else if (small == 2)
{
sc_with_obj(kinds[0], d0, [&](const auto& o0) {
sc_with_obj(kinds[1], d1, [&](const auto& o1) {
put(SmTol::GetScaledZero(o0, o1));
put(SmTol::GetScaledZeroSq(o0, o1));
put(SmTol::GetScaledZeroSqrt(o0, o1));
});
});
}
else if (small == 3)
{
sc_with_obj(kinds[0], d0, [&](const auto& o0) {
sc_with_obj(kinds[1], d1, [&](const auto& o1) {
sc_with_obj(kinds[2], d2, [&](const auto& o2) {
put(SmTol::GetScaledZero(o0, o1, o2));
put(SmTol::GetScaledZeroSq(o0, o1, o2));
put(SmTol::GetScaledZeroSqrt(o0, o1, o2));
});
});
});
}
else if (small == 4)
{
sc_with_obj(kinds[0], d0, [&](const auto& o0) {
sc_with_obj(kinds[1], d1, [&](const auto& o1) {
sc_with_obj(kinds[2], d2, [&](const auto& o2) {
sc_with_obj(kinds[3], d3, [&](const auto& o3) {
put(SmTol::GetScaledZero(o0, o1, o2, o3));
put(SmTol::GetScaledZeroSq(o0, o1, o2, o3));
put(SmTol::GetScaledZeroSqrt(o0, o1, o2, o3));
});
});
});
});
}
else
return SC_ERR_ARG;
break;
}
case OP_MAP_TO_1D:
{
SmTol3d sTol(a[0]);
SmVector3d sDeriv(a[1], a[2], a[3]);
const SmExtent1d sIvl(a[4], a[5]);
put(SmTol::MapTo1d(sTol, sDeriv, sIvl));
break;
}
case OP_TOL_CTORS:
{
SmTol3d t3(a[0]);
SmZoneTol3d tz(a[0]);
SmXSectTol3d tx(a[0]);
SmApproxTol3d ta(a[0]);
SmScaledZero ts(a[0]);
SmTol2d t2(a[0]);
SmTol1d t1(a[0]);
put(t3);
put(tz);
put(tx);
put(ta);
put(ts);
put(t2);
put(t1);
// operator=(double) of SM_TOL_CALLS; SmTol3d::operator=(const SmTol3d)
SmZoneTol3d az(1.0);
SmXSectTol3d ax(1.0);
SmApproxTol3d aa(1.0);
SmScaledZero as(1.0);
SmTol2d a2(1.0);
SmTol1d a1(1.0);
SmTol3d a3(1.0);
az = a[0];
ax = a[0];
aa = a[0];
as = a[0];
a2 = a[0];
a1 = a[0];
a3 = tz;
put(az);
put(ax);
put(aa);
put(as);
put(a2);
put(a1);
put(a3);
break;
}
case OP_TOL_MUL:
{
SmTol3d t(a[0]);
t *= a[1];
put(t);
break;
}
default: return SC_ERR_ARG;
}
*out_n = n;
return SC_OK;
SC_CATCH
}
// Runs a script of SmRelation<int, int> operations on objects first[0..n1) and
// second[0..n2) (identified by index) with cardinality `card`.
// script: [0, f, s] = RelatePair, [1, f, k, s1..sk] = RelateToMany, [2] = RemoveAll,
// [3, f, s] = RemovePair
// Output (int64): per script op its status; then for every first f:
// GetSecond(f) index (-1 = NULL), count, sorted GetSeconds(f);
// then per second s count + sorted GetFirsts(s), then count + sorted GetAllFirsts,
// count + sorted GetAllSeconds.
// (upstream returns these in hash order, which is unspecified; they are sorted here.)
SC_EXPORT int32_t sc_geom_tol_relation(int32_t card, int64_t n1, int64_t n2, const int64_t* script,
int64_t n_script, int64_t* out, int64_t cap,
int64_t* out_len)
{
if (card < 0 || card > 3 || n1 < 0 || n2 < 0 || n_script < 0)
return SC_ERR_ARG;
SC_TRY
static SmContext* s_pContext = new SmContext();
std::vector<int> vFirsts((size_t)n1 + 1), vSeconds((size_t)n2 + 1);
auto f_idx = [&](int* p) { return p ? (int64_t)(p - vFirsts.data()) : (int64_t)-1; };
auto s_idx = [&](int* p) { return p ? (int64_t)(p - vSeconds.data()) : (int64_t)-1; };
SmRelation<int, int> sRel(*s_pContext, (SmRelationCardinalityType)card);
std::vector<int64_t> res;
int64_t i = 0;
while (i < n_script)
{
int64_t kind = script[i];
if (kind == 0 || kind == 3)
{
if (i + 3 > n_script) return SC_ERR_ARG;
int64_t f = script[i + 1], s = script[i + 2];
if (f < 0 || f >= n1 || s < 0 || s >= n2) return SC_ERR_ARG;
res.push_back(kind == 0 ? sRel.RelatePair(&vFirsts[f], &vSeconds[s])
: sRel.RemovePair(&vFirsts[f], &vSeconds[s]));
i += 3;
}
else if (kind == 1)
{
if (i + 3 > n_script) return SC_ERR_ARG;
int64_t f = script[i + 1], k = script[i + 2];
if (f < 0 || f >= n1 || k < 0 || i + 3 + k > n_script) return SC_ERR_ARG;
SmTArray<int*> sSeconds;
for (int64_t j = 0; j < k; ++j)
{
int64_t s = script[i + 3 + j];
if (s < 0 || s >= n2) return SC_ERR_ARG;
sSeconds.Add(&vSeconds[s]);
}
res.push_back(sRel.RelateToMany(&vFirsts[f], sSeconds));
i += 3 + k;
}
else if (kind == 2)
{
sRel.RemoveAll();
res.push_back(0);
i += 1;
}
else
return SC_ERR_ARG;
}
auto push_sorted = [&](std::vector<int64_t> v) {
std::sort(v.begin(), v.end());
res.push_back((int64_t)v.size());
res.insert(res.end(), v.begin(), v.end());
};
for (int64_t f = 0; f < n1; ++f)
{
res.push_back(s_idx(sRel.GetSecond(&vFirsts[f])));
SmTArray<int*> sSeconds;
sRel.GetSeconds(&vFirsts[f], sSeconds);
std::vector<int64_t> v;
for (ULONG j = 0; j < sSeconds.GetSize(); ++j) v.push_back(s_idx(sSeconds[j]));
push_sorted(v);
}
for (int64_t s = 0; s < n2; ++s)
{
SmTArray<int*> sFirsts;
sRel.GetFirsts(&vSeconds[s], sFirsts);
std::vector<int64_t> v;
for (ULONG j = 0; j < sFirsts.GetSize(); ++j) v.push_back(f_idx(sFirsts[j]));
push_sorted(v);
}
{
SmTArray<int*> sAll;
sRel.GetAllFirsts(sAll);
std::vector<int64_t> v;
for (ULONG j = 0; j < sAll.GetSize(); ++j) v.push_back(f_idx(sAll[j]));
push_sorted(v);
}
{
SmTArray<int*> sAll;
sRel.GetAllSeconds(sAll);
std::vector<int64_t> v;
for (ULONG j = 0; j < sAll.GetSize(); ++j) v.push_back(s_idx(sAll[j]));
push_sorted(v);
}
return sc_copy_out(res.data(), (int64_t)res.size(), out, cap, out_len);
SC_CATCH
}
// ---- tolerances of a curve ---------------------------------------------------------
//
// sc_geom_tol_curve(in, n_in, out, cap, out_len): one real SmLine / SmCircle /
// SmBSplineCurve and, on it, GetScaledZero(const SmCurve&), MapTo1d(SmTol3d, dParam,
// const SmCurve&) and IsDegenerate(const SmCurve*, SmExtent1d, [&xsect]). Input:
// curve block: `0 a[3] b[3]` SmLine | `1 c[3] x[3] y[3] d0 d1 r` SmCircle |
// `2 p ncp cpts(x y z w) knots` SmBSplineCurve (dim 3)
// then: param tol ivl0 ivl1 has_xsect xsect_tol
// Output: scaled_zero map_to_1d is_degenerate (1 or 0).
namespace
{
struct ScBad : std::runtime_error
{
ScBad() : std::runtime_error("bad program") {}
};
struct ScRd
{
const double* in;
int64_t n;
int64_t at;
double num()
{
if (at >= n)
throw ScBad();
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 ScBad();
return (int64_t)d;
}
SmVector3d vec()
{
double x = num();
double y = num();
double z = num();
return SmVector3d(x, y, z);
}
};
SmCurve* sc_read_curve(ScRd& rd, const SmContext& ctx)
{
int64_t kind = rd.idx(0, 2);
if (kind == 0)
{
SmPoint3d a = rd.vec();
SmPoint3d b = rd.vec();
SmLine* l = new (ctx) SmLine(a, b, 3, &ctx);
l->GetOrCreateGwNurbPointer();
return l;
}
if (kind == 1)
{
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);
}
int64_t p = rd.idx(1, 8);
int64_t ncp = rd.idx(2, 64);
if (p >= ncp)
throw ScBad();
int64_t n = ncp - 1;
int64_t m = n + p + 1;
std::vector<double> 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];
return new (ctx) SmBSplineCurve(cur, 3, FALSE);
}
} // namespace
// Fixed B-spline volume (degree 1, 2x2x2 control points, unit weights, knots 0/0/1/1)
// followed by SmTol::GetScaledZero(*volume). 24 doubles = 8 poles x 3 coords.
SC_EXPORT int32_t sc_geom_tol_volume(const double* in, int64_t n_in, double* out, int64_t cap, int64_t* out_len)
{
if (in == nullptr || n_in != 24 || out_len == nullptr || (out == nullptr && cap > 0))
return SC_ERR_ARG;
for (int64_t i = 0; i < 24; ++i)
if (!std::isfinite(in[i]))
return SC_ERR_ARG;
SC_TRY
SmContext ctx;
gw_VOLUME* rec = sm_AllocateNurbVolume(1, 1, 1, 1, 1, 1, 3, 3, 3);
if (rec == NULL)
return SC_ERR_ARG;
SmMemDelete cleanup(rec);
for (int i = 0; i <= 3; ++i)
{
rec->knu->U[i] = (i < 2) ? 0.0 : 1.0;
rec->knv->U[i] = (i < 2) ? 0.0 : 1.0;
rec->knw->U[i] = (i < 2) ? 0.0 : 1.0;
}
for (int i = 0; i <= 1; ++i)
for (int j = 0; j <= 1; ++j)
for (int k = 0; k <= 1; ++k)
{
NL_CPOINT& c = rec->mesh->Pw[i][j][k];
const int base = (i * 4 + j * 2 + k) * 3;
c.x = in[base];
c.y = in[base + 1];
c.z = in[base + 2];
c.w = 1.0;
}
// This constructor copies the record, as in surface_b_spline_volume.cpp.
// Both allocations are guarded through native exceptions, before ctx dies.
std::unique_ptr<SmVolume> v(new (ctx) SmBSplineVolume(&ctx, rec));
smos_Free(rec);
cleanup.Clear();
const double scaled_zero = (double)SmTol::GetScaledZero(*v);
return sc_copy_out(&scaled_zero, 1, out, cap, out_len);
SC_CATCH
}
SC_EXPORT int32_t sc_geom_tol_curve(const double* in, int64_t n_in, double* out, int64_t cap, int64_t* out_len)
{
if (in == nullptr || n_in < 1 || out_len == nullptr || (out == nullptr && cap > 0))
return SC_ERR_ARG;
SC_TRY
SmContext ctx;
ScRd rd{in, n_in, 0};
std::vector<double> o;
SmCurve* c = NULL;
try
{
c = sc_read_curve(rd, ctx);
double param = rd.num();
double tol = rd.num();
double i0 = rd.num();
double i1 = rd.num();
int64_t has_x = rd.idx(0, 1);
double xs = rd.num();
if (rd.at != n_in)
{
delete c;
return SC_ERR_ARG;
}
SmScaledZero sz = SmTol::GetScaledZero(*c);
SmTol3d t3(tol);
SmTol1d t1 = SmTol::MapTo1d(t3, param, *c);
SmXSectTol3d xt(xs);
SmBoolean deg = SmTol::IsDegenerate(c, SmExtent1d(i0, i1), has_x ? &xt : NULL);
o.push_back((double)sz);
o.push_back((double)t1);
o.push_back(deg ? 1.0 : 0.0);
}
catch (const ScBad&)
{
delete c;
return SC_ERR_ARG;
}
delete c;
return sc_copy_out(o.data(), (int64_t)o.size(), out, cap, out_len);
SC_CATCH
}
namespace
{
// Points a B-rep at `ctx` for the scope (the fixture B-rep lives in the shared fixture
// context, which SmTol::SetModelSizeEstimate(SmBrep&, ..) would change for later cases).
struct ScBrepContext
{
SmBrep& brep;
const SmContext* orig;
ScBrepContext(SmBrep& b, const SmContext* ctx) : brep(b), orig(b.GetContext()) { brep.SetContext(ctx); }
~ScBrepContext() { brep.SetContext(orig); }
};
} // namespace
// The SmBrep overloads of SmTol on the B-rep of `path` / `block`, with the system
// tolerances set from (model_size, ratio) and the B-rep moved into a fresh SmContext.
// args: set_est est set_ratio ratio small gap1 gap2 has_max max (9 doubles)
// out : SetModelSizeEstimate (0 when not set), GetModelSizeEstimate,
// GetLargeSmallSizeRatio, GetZoneTol3d, GetApproxTol3d, GetXSectTol3d,
// CalcFaceZoneTol3d, CalcEdgeZoneTol3d, CalcVertexZoneTol3d, GetScaledZero (10)
SC_EXPORT int32_t sc_geom_tol_brep(const char* path, const double* block, int64_t n_block,
double model_size, double ratio, const double* args, int64_t n_args,
double* out, int64_t cap, int64_t* out_len)
{
if (path == nullptr || block == nullptr || n_block < 1 || args == nullptr || n_args != 9 ||
out_len == nullptr || (out == nullptr && cap > 0))
return SC_ERR_ARG;
for (int64_t i = 0; i < n_args; ++i)
if (!std::isfinite(args[i]))
return SC_ERR_ARG;
int64_t small = (int64_t)args[4];
if (small < 0 || small > 4)
return SC_ERR_ARG;
SC_TRY
std::vector<double> o;
int32_t rc = sc_topology_fixture::apply(path, block, n_block, [&](const sc_topology_fixture::View& g) -> int32_t {
// the trimmed surface caches a file read leaves on face surfaces (the port has
// no object caches)
for (SmFace* f : g.faces)
{
SmSurface* srf = f->GetSurface();
if (srf == nullptr)
return SC_ERR_ARG; // SmFace::CalculateBoundingBox dereferences it unchecked
if (SmCacheMgr::GetObjectCache(SM_OC_TRIMSRF, srf) != nullptr)
SmCacheMgr::DeleteObjectsCache(SM_OC_TRIMSRF, srf);
}
ScSystemTol sSys(model_size, ratio);
SmContext ctx;
ScBrepContext sCtx(g.brep, &ctx);
SmBrep& b = g.brep;
const SmBrep* cb = &b;
SmSmallTopoType eSmall = (SmSmallTopoType)small;
double z = 0.0;
if (args[0] != 0.0)
z = (double)SmTol::SetModelSizeEstimate(b, args[1]);
if (args[2] != 0.0)
SmTol::SetLargeSmallSizeRatio(b, args[3]);
SmZoneTol3d sMax(args[8]);
SmZoneTol3d* pMax = args[7] != 0.0 ? &sMax : NULL;
o.push_back(z);
o.push_back(SmTol::GetModelSizeEstimate(*cb));
o.push_back(SmTol::GetLargeSmallSizeRatio(*cb));
o.push_back((double)SmTol::GetZoneTol3d(cb, eSmall));
o.push_back((double)SmTol::GetApproxTol3d(cb, eSmall));
o.push_back((double)SmTol::GetXSectTol3d(cb, eSmall));
o.push_back((double)SmTol::CalcFaceZoneTol3d(&b));
o.push_back((double)SmTol::CalcEdgeZoneTol3d(args[5], args[6], &b, pMax));
o.push_back((double)SmTol::CalcVertexZoneTol3d(args[5], args[6], &b, pMax));
o.push_back((double)SmTol::GetScaledZero(*cb));
return 0;
});
if (rc != 0)
return rc;
return sc_copy_out(o.data(), (int64_t)o.size(), out, cap, out_len);
SC_CATCH
}