forgecad 0.10.2 → 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -6
- package/dist/assets/{AdminPage-CHY6ZN-p.js → AdminPage-CK7ObBz3.js} +1 -1
- package/dist/assets/{BenchmarkPage-BcRT5iGN.js → BenchmarkPage-Ds7Z2doN.js} +1 -1
- package/dist/assets/{BlogPage-BssBbnb-.js → BlogPage-DlPbpt6A.js} +1 -1
- package/dist/assets/{DocsPage-DsvdiRNK.js → DocsPage-vZb3b3Y0.js} +9 -14
- package/dist/assets/{EditorApp-BpjZgzk0.css → EditorApp-C5f24ZN9.css} +8 -0
- package/dist/assets/{EditorApp-Bfd3jbtC.js → EditorApp-HLoKfe15.js} +141 -12
- package/dist/assets/{EmbedViewer-D5t8WamV.js → EmbedViewer--KnqBKrJ.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-DbN7o-Be.js → LandingPageProofDriven-C_LssmnA.js} +1 -1
- package/dist/assets/{LegalPage-DNGrrY0p.js → LegalPage-DGsyo4n1.js} +1 -1
- package/dist/assets/{PricingPage-Nczr3pRz.js → PricingPage-BOE27B-R.js} +1 -1
- package/dist/assets/{SettingsPage-DZlyu4d4.js → SettingsPage-f47cnk39.js} +1 -1
- package/dist/assets/{app-C9ct2hRD.js → app-D6ccu2Xx.js} +6854 -7373
- package/dist/assets/{backendInit-ymjonyQp.js → backendInit-DbTkQN9J.js} +2557 -809
- package/dist/assets/cli/{render-B_0lQwKU.js → render-BsngirjC.js} +114 -9
- package/dist/assets/{constructionHistoryWorker-CZ42Dksy.js → constructionHistoryWorker-PCwXrTDB.js} +175 -36
- package/dist/assets/{evalWorker-C2pm8LHP.js → evalWorker-CS63PfZu.js} +1125 -447
- package/dist/assets/{forgecad_geometry-BlMtqluF.js → forgecad_geometry-CZ_IfuvA.js} +1 -9
- package/dist/assets/{forgecad_geometry_bg-BllP_WiL.wasm → forgecad_geometry_bg-C3rQHfwg.wasm} +0 -0
- package/dist/assets/{inspectWorker-D5T5VbfK.js → inspectWorker-Y4cOzNyA.js} +4345 -373
- package/dist/assets/{jointPose-4r8ed8_5.js → jointPose-AMvCywzS.js} +1 -1
- package/dist/assets/{manifold-C4r6B-XY.js → manifold-CBry38ly.js} +2 -2
- package/dist/assets/{manifold-5PP1eGLN.js → manifold-Crd_F2qx.js} +1 -1
- package/dist/assets/{manifold-DjBkyIc8.js → manifold-k2kRcc85.js} +1 -1
- package/dist/assets/{reportWorker-CwenM7wB.js → reportWorker-CWvn0CEv.js} +1095 -400
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/AI/usage.md +2 -4
- package/dist/docs-raw/CLI.md +9 -7
- package/dist/docs-raw/README.md +1 -1
- package/dist/docs-raw/component-model.md +1 -1
- package/dist/docs-raw/generated/assembly.md +1 -1
- package/dist/docs-raw/generated/concepts.md +5 -3
- package/dist/docs-raw/generated/core.md +70 -1
- package/dist/docs-raw/generated/curves.md +8 -1
- package/dist/docs-raw/generated/output.md +0 -64
- package/dist/docs-raw/generated/runtime-names.md +6 -6
- package/dist/docs-raw/generated/viewport.md +3 -12
- package/dist/docs-raw/guides/inspection-bundles.md +1 -1
- package/dist/docs-raw/simulation-workflow.md +58 -0
- package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -1
- package/dist/docs-raw/skills/forgecad-image-replicator.md +2 -2
- package/dist/docs-raw/skills/forgecad-mujoco-verify.md +78 -0
- package/dist/docs-raw/skills/forgecad-spec-by-walking-through-it.md +145 -0
- package/dist/docs-raw/skills/forgecad-visual-spec.md +1 -1
- package/dist/docs-raw/skills/forgecad.md +24 -24
- package/dist/docs-raw/skills/index.md +2 -3
- package/dist/index.html +1 -1
- package/dist/sitemap.xml +15 -15
- package/dist-cli/{check-compiler-SP7FAL7R.js → check-compiler-HPF2T2FS.js} +1 -1
- package/dist-cli/{check-query-propagation-BRLSHP22.js → check-query-propagation-HYSLTXAB.js} +1 -1
- package/dist-cli/{chunk-RQQ42YCP.js → chunk-WLUKAW3H.js} +1025 -158
- package/dist-cli/forgecad.js +2621 -232
- package/dist-cli/{forgecad_geometry-7TVSNVUB.js → forgecad_geometry-2IMYCUWW.js} +0 -8
- package/dist-cli/forgecad_geometry_bg.wasm +0 -0
- package/dist-skill/CONTEXT.md +85 -73
- package/dist-skill/SKILL.md +1 -1
- package/dist-skill/docs/CLI.md +9 -7
- package/dist-skill/docs/generated/assembly.md +1 -1
- package/dist-skill/docs/generated/core.md +70 -1
- package/dist-skill/docs/generated/curves.md +8 -1
- package/dist-skill/docs/generated/output.md +0 -64
- package/dist-skill/docs/generated/runtime-names.md +6 -6
- package/dist-skill/docs/generated/viewport.md +3 -12
- package/dist-skill/docs/guides/inspection-bundles.md +1 -1
- package/dist-skill/library/README.md +2 -3
- package/dist-skill/library/forgecad-blockout-model/SKILL.md +1 -1
- package/dist-skill/library/forgecad-image-replicator/SKILL.md +2 -2
- package/dist-skill/library/forgecad-mujoco-verify/SKILL.md +66 -0
- package/dist-skill/library/forgecad-mujoco-verify/scripts/mujoco_verify.py +385 -0
- package/dist-skill/library/forgecad-spec-by-walking-through-it/SKILL.md +132 -0
- package/dist-skill/library/forgecad-visual-spec/SKILL.md +1 -1
- package/dist-skill/website/skills/forgecad-blockout-model.md +1 -1
- package/dist-skill/website/skills/forgecad-image-replicator.md +2 -2
- package/dist-skill/website/skills/forgecad-mujoco-verify.md +78 -0
- package/dist-skill/website/skills/forgecad-spec-by-walking-through-it.md +145 -0
- package/dist-skill/website/skills/forgecad-visual-spec.md +1 -1
- package/dist-skill/website/skills/forgecad.md +24 -24
- package/dist-skill/website/skills/index.md +2 -3
- package/examples/analysis/clearance-fit.forge.js +31 -0
- package/examples/analysis/lever-arm-actuator.forge.js +43 -0
- package/examples/analysis/tipping-tripod.forge.js +35 -0
- package/examples/products/sportscar.forge.js +77 -0
- package/package.json +1 -3
- package/dist/docs-raw/skills/forgecad-high-level-spec.md +0 -101
- package/dist/docs-raw/skills/forgecad-lld.md +0 -41
- package/dist/docs-raw/skills/forgecad-prepare-prompt.md +0 -63
- package/dist-skill/library/forgecad-high-level-spec/SKILL.md +0 -94
- package/dist-skill/library/forgecad-lld/SKILL.md +0 -34
- package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +0 -50
- package/dist-skill/website/skills/forgecad-high-level-spec.md +0 -101
- package/dist-skill/website/skills/forgecad-lld.md +0 -41
- package/dist-skill/website/skills/forgecad-prepare-prompt.md +0 -63
- /package/dist-skill/library/{forgecad-prepare-prompt → forgecad-spec-by-walking-through-it}/references/default-profiles.md +0 -0
- /package/dist-skill/library/{forgecad-prepare-prompt → forgecad-spec-by-walking-through-it}/references/master-prompt.md +0 -0
|
@@ -3190,20 +3190,20 @@ function flatKnotsToKnotsMults(flatKnots) {
|
|
|
3190
3190
|
return { knots, mults };
|
|
3191
3191
|
}
|
|
3192
3192
|
const KNOT_EPSILON = 1e-10;
|
|
3193
|
-
function sameKnot(a2, b) {
|
|
3193
|
+
function sameKnot$1(a2, b) {
|
|
3194
3194
|
return Math.abs(a2 - b) <= KNOT_EPSILON;
|
|
3195
3195
|
}
|
|
3196
|
-
function knotMultiplicity(knots, u2) {
|
|
3197
|
-
return knots.reduce((count, knot) => count + (sameKnot(knot, u2) ? 1 : 0), 0);
|
|
3196
|
+
function knotMultiplicity$1(knots, u2) {
|
|
3197
|
+
return knots.reduce((count, knot) => count + (sameKnot$1(knot, u2) ? 1 : 0), 0);
|
|
3198
3198
|
}
|
|
3199
3199
|
function firstKnotIndex(knots, u2) {
|
|
3200
|
-
const index2 = knots.findIndex((knot) => sameKnot(knot, u2));
|
|
3200
|
+
const index2 = knots.findIndex((knot) => sameKnot$1(knot, u2));
|
|
3201
3201
|
if (index2 < 0) throw new Error(`NURBS subdomain extraction could not find boundary knot ${u2}.`);
|
|
3202
3202
|
return index2;
|
|
3203
3203
|
}
|
|
3204
3204
|
function lastKnotIndex(knots, u2) {
|
|
3205
3205
|
for (let index2 = knots.length - 1; index2 >= 0; index2 -= 1) {
|
|
3206
|
-
if (sameKnot(knots[index2], u2)) return index2;
|
|
3206
|
+
if (sameKnot$1(knots[index2], u2)) return index2;
|
|
3207
3207
|
}
|
|
3208
3208
|
throw new Error(`NURBS subdomain extraction could not find boundary knot ${u2}.`);
|
|
3209
3209
|
}
|
|
@@ -3216,7 +3216,7 @@ function insertKnotOnceHomogeneous(points, knots, degree, u2) {
|
|
|
3216
3216
|
const lastPoint = count - 1;
|
|
3217
3217
|
const lastKnot = knots.length - 1;
|
|
3218
3218
|
const span = findSpan(count, degree, u2, knots);
|
|
3219
|
-
const multiplicity = knotMultiplicity(knots, u2);
|
|
3219
|
+
const multiplicity = knotMultiplicity$1(knots, u2);
|
|
3220
3220
|
if (multiplicity >= degree) {
|
|
3221
3221
|
throw new Error(`NURBS subdomain extraction cannot insert knot ${u2}: multiplicity ${multiplicity} already reaches degree ${degree}.`);
|
|
3222
3222
|
}
|
|
@@ -3240,7 +3240,7 @@ function insertKnotOnceHomogeneous(points, knots, degree, u2) {
|
|
|
3240
3240
|
function insertBoundaryToMultiplicity(points, knots, degree, u2, targetMultiplicity) {
|
|
3241
3241
|
let refinedPoints = points;
|
|
3242
3242
|
let refinedKnots = knots;
|
|
3243
|
-
while (knotMultiplicity(refinedKnots, u2) < targetMultiplicity) {
|
|
3243
|
+
while (knotMultiplicity$1(refinedKnots, u2) < targetMultiplicity) {
|
|
3244
3244
|
const refined = insertKnotOnceHomogeneous(refinedPoints, refinedKnots, degree, u2);
|
|
3245
3245
|
refinedPoints = refined.points;
|
|
3246
3246
|
refinedKnots = refined.knots;
|
|
@@ -3260,8 +3260,8 @@ function extractNurbsCurveSubdomain(controlPoints, weights, knots, degree, uStar
|
|
|
3260
3260
|
}
|
|
3261
3261
|
const activeStart = knots[degree];
|
|
3262
3262
|
const activeEnd = knots[controlPoints.length];
|
|
3263
|
-
const start = sameKnot(uStart, activeStart) ? activeStart : sameKnot(uStart, activeEnd) ? activeEnd : uStart;
|
|
3264
|
-
const end = sameKnot(uEnd, activeEnd) ? activeEnd : sameKnot(uEnd, activeStart) ? activeStart : uEnd;
|
|
3263
|
+
const start = sameKnot$1(uStart, activeStart) ? activeStart : sameKnot$1(uStart, activeEnd) ? activeEnd : uStart;
|
|
3264
|
+
const end = sameKnot$1(uEnd, activeEnd) ? activeEnd : sameKnot$1(uEnd, activeStart) ? activeStart : uEnd;
|
|
3265
3265
|
if (start < activeStart - KNOT_EPSILON || end > activeEnd + KNOT_EPSILON) {
|
|
3266
3266
|
throw new Error(`NURBS subdomain [${uStart}, ${uEnd}] must stay inside active knot domain [${activeStart}, ${activeEnd}].`);
|
|
3267
3267
|
}
|
|
@@ -10172,7 +10172,7 @@ async function initTruckGeometryWasm() {
|
|
|
10172
10172
|
if (_initPromise$2) return _initPromise$2;
|
|
10173
10173
|
_initPromise$2 = (async () => {
|
|
10174
10174
|
try {
|
|
10175
|
-
const geometryModule = await import("./forgecad_geometry-
|
|
10175
|
+
const geometryModule = await import("./forgecad_geometry-CZ_IfuvA.js");
|
|
10176
10176
|
const isNode = isNodeRuntime();
|
|
10177
10177
|
if (isNode) {
|
|
10178
10178
|
const { readFileSync, existsSync } = await Promise.resolve().then(function() {
|
|
@@ -11100,23 +11100,62 @@ class NurbsSurface {
|
|
|
11100
11100
|
return [wx / wSum, wy / wSum, wz / wSum];
|
|
11101
11101
|
}
|
|
11102
11102
|
/**
|
|
11103
|
-
* Evaluate the surface normal at (u, v)
|
|
11103
|
+
* Evaluate the surface unit normal at (u, v) from analytic first derivatives.
|
|
11104
|
+
*
|
|
11105
|
+
* Uses Algorithm A2.3 basis-function derivatives with the rational quotient
|
|
11106
|
+
* rule, so the normal is exact (no finite-difference epsilon, no error near
|
|
11107
|
+
* the boundary). Constant chain-rule factors from the parameter remap scale Su
|
|
11108
|
+
* and Sv positively and cancel under normalization, so they are omitted.
|
|
11104
11109
|
*/
|
|
11105
11110
|
normalAt(u2, v) {
|
|
11106
|
-
const
|
|
11107
|
-
const
|
|
11108
|
-
const
|
|
11109
|
-
const
|
|
11110
|
-
const pv = this.pointAt(u2, v1), pmv = this.pointAt(u2, v0);
|
|
11111
|
-
const du = [pu[0] - pmu[0], pu[1] - pmu[1], pu[2] - pmu[2]];
|
|
11112
|
-
const dv = [pv[0] - pmv[0], pv[1] - pmv[1], pv[2] - pmv[2]];
|
|
11113
|
-
const nx = du[1] * dv[2] - du[2] * dv[1];
|
|
11114
|
-
const ny = du[2] * dv[0] - du[0] * dv[2];
|
|
11115
|
-
const nz = du[0] * dv[1] - du[1] * dv[0];
|
|
11111
|
+
const { Su, Sv } = this.derivativesAt(u2, v);
|
|
11112
|
+
const nx = Su[1] * Sv[2] - Su[2] * Sv[1];
|
|
11113
|
+
const ny = Su[2] * Sv[0] - Su[0] * Sv[2];
|
|
11114
|
+
const nz = Su[0] * Sv[1] - Su[1] * Sv[0];
|
|
11116
11115
|
const len2 = Math.sqrt(nx * nx + ny * ny + nz * nz);
|
|
11117
11116
|
if (len2 < 1e-12) return [0, 0, 1];
|
|
11118
11117
|
return [nx / len2, ny / len2, nz / len2];
|
|
11119
11118
|
}
|
|
11119
|
+
/** Analytic first partial derivatives S_u, S_v (rational quotient rule). */
|
|
11120
|
+
derivativesAt(u2, v) {
|
|
11121
|
+
const uu = this.remapU(Math.max(0, Math.min(1, u2)));
|
|
11122
|
+
const vv = this.remapV(Math.max(0, Math.min(1, v)));
|
|
11123
|
+
const spanU = findSpan(this.nU, this.degreeU, uu, this.knotsU);
|
|
11124
|
+
const spanV = findSpan(this.nV, this.degreeV, vv, this.knotsV);
|
|
11125
|
+
const dU = basisFunsDeriv(spanU, uu, this.degreeU, this.knotsU, 1);
|
|
11126
|
+
const dV = basisFunsDeriv(spanV, vv, this.degreeV, this.knotsV, 1);
|
|
11127
|
+
const A = [0, 0, 0];
|
|
11128
|
+
const Au = [0, 0, 0];
|
|
11129
|
+
const Av = [0, 0, 0];
|
|
11130
|
+
let w2 = 0;
|
|
11131
|
+
let wu = 0;
|
|
11132
|
+
let wv = 0;
|
|
11133
|
+
for (let i = 0; i <= this.degreeU; i++) {
|
|
11134
|
+
const rowIdx = spanU - this.degreeU + i;
|
|
11135
|
+
for (let j = 0; j <= this.degreeV; j++) {
|
|
11136
|
+
const colIdx = spanV - this.degreeV + j;
|
|
11137
|
+
const weight = this.weightsGrid[rowIdx][colIdx];
|
|
11138
|
+
const pt = this.controlGrid[rowIdx][colIdx];
|
|
11139
|
+
const n00 = dU[0][i] * dV[0][j] * weight;
|
|
11140
|
+
const n10 = dU[1][i] * dV[0][j] * weight;
|
|
11141
|
+
const n01 = dU[0][i] * dV[1][j] * weight;
|
|
11142
|
+
w2 += n00;
|
|
11143
|
+
wu += n10;
|
|
11144
|
+
wv += n01;
|
|
11145
|
+
for (let c2 = 0; c2 < 3; c2++) {
|
|
11146
|
+
A[c2] += n00 * pt[c2];
|
|
11147
|
+
Au[c2] += n10 * pt[c2];
|
|
11148
|
+
Av[c2] += n01 * pt[c2];
|
|
11149
|
+
}
|
|
11150
|
+
}
|
|
11151
|
+
}
|
|
11152
|
+
if (w2 === 0) return { S: [0, 0, 0], Su: [0, 0, 0], Sv: [0, 0, 0] };
|
|
11153
|
+
const invW = 1 / w2;
|
|
11154
|
+
const S = [A[0] * invW, A[1] * invW, A[2] * invW];
|
|
11155
|
+
const Su = [(Au[0] - wu * S[0]) * invW, (Au[1] - wu * S[1]) * invW, (Au[2] - wu * S[2]) * invW];
|
|
11156
|
+
const Sv = [(Av[0] - wv * S[0]) * invW, (Av[1] - wv * S[1]) * invW, (Av[2] - wv * S[2]) * invW];
|
|
11157
|
+
return { S, Su, Sv };
|
|
11158
|
+
}
|
|
11120
11159
|
/**
|
|
11121
11160
|
* Tessellate the surface into a triangle mesh.
|
|
11122
11161
|
* Returns positions, normals, and triangle indices.
|
|
@@ -12487,7 +12526,7 @@ function maxQuadDeviation(rings, heights, colA, colB) {
|
|
|
12487
12526
|
const b = [rings[i][colB][0], rings[i][colB][1], heights[i]];
|
|
12488
12527
|
const c2 = [rings[i + 1][colB][0], rings[i + 1][colB][1], heights[i + 1]];
|
|
12489
12528
|
const d2 = [rings[i + 1][colA][0], rings[i + 1][colA][1], heights[i + 1]];
|
|
12490
|
-
const n = cross3$7(sub3$
|
|
12529
|
+
const n = cross3$7(sub3$7(b, a2), sub3$7(d2, a2));
|
|
12491
12530
|
const len2 = Math.hypot(n[0], n[1], n[2]);
|
|
12492
12531
|
if (len2 < 1e-12) continue;
|
|
12493
12532
|
const deviation = Math.abs((n[0] * (c2[0] - a2[0]) + n[1] * (c2[1] - a2[1]) + n[2] * (c2[2] - a2[2])) / len2);
|
|
@@ -12763,15 +12802,15 @@ function buildSpanRows(rings, heights) {
|
|
|
12763
12802
|
function stationTangent(stations, t, i, j) {
|
|
12764
12803
|
const R = stations.length;
|
|
12765
12804
|
if (i === 0) {
|
|
12766
|
-
return scale3$2(sub3$
|
|
12805
|
+
return scale3$2(sub3$7(stations[1][j], stations[0][j]), 1 / (t[1] - t[0]));
|
|
12767
12806
|
}
|
|
12768
12807
|
if (i === R - 1) {
|
|
12769
|
-
return scale3$2(sub3$
|
|
12808
|
+
return scale3$2(sub3$7(stations[R - 1][j], stations[R - 2][j]), 1 / (t[R - 1] - t[R - 2]));
|
|
12770
12809
|
}
|
|
12771
12810
|
const hPrev = t[i] - t[i - 1];
|
|
12772
12811
|
const hNext = t[i + 1] - t[i];
|
|
12773
|
-
const dPrev = scale3$2(sub3$
|
|
12774
|
-
const dNext = scale3$2(sub3$
|
|
12812
|
+
const dPrev = scale3$2(sub3$7(stations[i][j], stations[i - 1][j]), 1 / hPrev);
|
|
12813
|
+
const dNext = scale3$2(sub3$7(stations[i + 1][j], stations[i][j]), 1 / hNext);
|
|
12775
12814
|
return scale3$2(add3$2(scale3$2(dPrev, hNext), scale3$2(dNext, hPrev)), 1 / (hPrev + hNext));
|
|
12776
12815
|
}
|
|
12777
12816
|
function hermite(p0, m0, p1, m1, h, u2) {
|
|
@@ -12827,12 +12866,12 @@ function stitchSingleLoopLoft(loops, heights, wasm, options) {
|
|
|
12827
12866
|
const curr = points[j];
|
|
12828
12867
|
const next = points[(j + 1) % N];
|
|
12829
12868
|
if (cornerSet.has(j)) {
|
|
12830
|
-
const nFwd = surfaceNormal(sub3$
|
|
12831
|
-
const nBwd = surfaceNormal(sub3$
|
|
12869
|
+
const nFwd = surfaceNormal(sub3$7(next, curr), tangents[j]);
|
|
12870
|
+
const nBwd = surfaceNormal(sub3$7(curr, prev), tangents[j]);
|
|
12832
12871
|
fwd[j] = pushVert(curr, nFwd);
|
|
12833
12872
|
bwd[j] = pushVert(curr, nBwd);
|
|
12834
12873
|
} else {
|
|
12835
|
-
const idx = pushVert(curr, surfaceNormal(sub3$
|
|
12874
|
+
const idx = pushVert(curr, surfaceNormal(sub3$7(next, prev), tangents[j]));
|
|
12836
12875
|
fwd[j] = idx;
|
|
12837
12876
|
bwd[j] = idx;
|
|
12838
12877
|
}
|
|
@@ -12893,7 +12932,7 @@ function surfaceNormal(chord, span) {
|
|
|
12893
12932
|
}
|
|
12894
12933
|
return [n[0] / len2, n[1] / len2, n[2] / len2];
|
|
12895
12934
|
}
|
|
12896
|
-
function sub3$
|
|
12935
|
+
function sub3$7(a2, b) {
|
|
12897
12936
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
12898
12937
|
}
|
|
12899
12938
|
function add3$2(a2, b) {
|
|
@@ -12919,7 +12958,7 @@ let _wasm = null;
|
|
|
12919
12958
|
async function initManifoldWasm() {
|
|
12920
12959
|
if (_wasm) return _wasm;
|
|
12921
12960
|
performance.mark("manifold:start");
|
|
12922
|
-
const Module = (await import("./manifold-
|
|
12961
|
+
const Module = (await import("./manifold-k2kRcc85.js")).default;
|
|
12923
12962
|
performance.mark("manifold:imported");
|
|
12924
12963
|
const wasm = await Module();
|
|
12925
12964
|
wasm.setup();
|
|
@@ -13053,23 +13092,6 @@ let ManifoldShapeBackend = _ManifoldShapeBackend;
|
|
|
13053
13092
|
function wrapManifoldShapeBackend(manifold) {
|
|
13054
13093
|
return new ManifoldShapeBackend(manifold);
|
|
13055
13094
|
}
|
|
13056
|
-
function reconstructBackendFromMesh(mesh) {
|
|
13057
|
-
const wasm = getManifoldWasm();
|
|
13058
|
-
const wasmMesh = new wasm.Mesh({
|
|
13059
|
-
numProp: mesh.numProp,
|
|
13060
|
-
triVerts: mesh.triVerts,
|
|
13061
|
-
vertProperties: mesh.vertProperties,
|
|
13062
|
-
mergeFromVert: mesh.mergeFromVert.length > 0 ? mesh.mergeFromVert : void 0,
|
|
13063
|
-
mergeToVert: mesh.mergeToVert.length > 0 ? mesh.mergeToVert : void 0
|
|
13064
|
-
});
|
|
13065
|
-
let manifold;
|
|
13066
|
-
try {
|
|
13067
|
-
manifold = new wasm.Manifold(wasmMesh);
|
|
13068
|
-
} catch {
|
|
13069
|
-
manifold = wasm.Manifold.cube([0, 0, 0]);
|
|
13070
|
-
}
|
|
13071
|
-
return new ManifoldShapeBackend(manifold);
|
|
13072
|
-
}
|
|
13073
13095
|
function requireManifoldShapeBackend(backend, apiName = "requireManifoldShapeBackend()") {
|
|
13074
13096
|
if (isManifoldCapableBackend(backend)) {
|
|
13075
13097
|
return backend.requireManifold(apiName);
|
|
@@ -13549,6 +13571,20 @@ function fromSlicesSingleSliceHalfExtentForManifold(plan) {
|
|
|
13549
13571
|
}
|
|
13550
13572
|
return Math.max(1, radius + maxOffsetMagnitude + plan.boundsPadding + plan.edgeLength * 3);
|
|
13551
13573
|
}
|
|
13574
|
+
const BOOLEAN_OPERAND_NORMAL_SHARP_ANGLE_DEG = 60;
|
|
13575
|
+
function promoteBooleanOperandNormals(shapes) {
|
|
13576
|
+
let maxExtra = 0;
|
|
13577
|
+
for (const shape of shapes) maxExtra = Math.max(maxExtra, shape.numProp());
|
|
13578
|
+
if (maxExtra < 3) return { operands: shapes, created: [] };
|
|
13579
|
+
const created = [];
|
|
13580
|
+
const operands = shapes.map((shape) => {
|
|
13581
|
+
if (shape.numProp() >= 3) return shape;
|
|
13582
|
+
const promoted = shape.calculateNormals(0, BOOLEAN_OPERAND_NORMAL_SHARP_ANGLE_DEG);
|
|
13583
|
+
created.push(promoted);
|
|
13584
|
+
return promoted;
|
|
13585
|
+
});
|
|
13586
|
+
return { operands, created };
|
|
13587
|
+
}
|
|
13552
13588
|
function lowerShapeBooleanCompilePlan(plan, wasm) {
|
|
13553
13589
|
const shapes = plan.shapes.map((shape) => lowerShapeCompilePlanToManifold(shape, wasm));
|
|
13554
13590
|
if (shapes.length === 0) {
|
|
@@ -13557,16 +13593,18 @@ function lowerShapeBooleanCompilePlan(plan, wasm) {
|
|
|
13557
13593
|
if (shapes.length === 1) {
|
|
13558
13594
|
return shapes[0];
|
|
13559
13595
|
}
|
|
13596
|
+
const { operands, created } = promoteBooleanOperandNormals(shapes);
|
|
13560
13597
|
try {
|
|
13561
13598
|
switch (plan.op) {
|
|
13562
13599
|
case "union":
|
|
13563
|
-
return wasm.Manifold.union(
|
|
13600
|
+
return wasm.Manifold.union(operands);
|
|
13564
13601
|
case "difference":
|
|
13565
|
-
return wasm.Manifold.difference(
|
|
13602
|
+
return wasm.Manifold.difference(operands);
|
|
13566
13603
|
case "intersection":
|
|
13567
|
-
return wasm.Manifold.intersection(
|
|
13604
|
+
return wasm.Manifold.intersection(operands);
|
|
13568
13605
|
}
|
|
13569
13606
|
} finally {
|
|
13607
|
+
disposeWasmObjects(created);
|
|
13570
13608
|
disposeWasmObjects(shapes);
|
|
13571
13609
|
}
|
|
13572
13610
|
}
|
|
@@ -14916,12 +14954,16 @@ function lowerNurbsSurfaceToManifold(plan, wasm) {
|
|
|
14916
14954
|
const { positions, normals, indices } = surface.tessellate(res, res);
|
|
14917
14955
|
const thickness = plan.thickness;
|
|
14918
14956
|
const numVerts = positions.length;
|
|
14919
|
-
const
|
|
14920
|
-
for (const [x2, y2, z2] of positions) allPositions.push(x2, y2, z2);
|
|
14957
|
+
const vertProps = [];
|
|
14921
14958
|
for (let i = 0; i < numVerts; i++) {
|
|
14922
14959
|
const [x2, y2, z2] = positions[i];
|
|
14923
14960
|
const [nx, ny, nz] = normals[i];
|
|
14924
|
-
|
|
14961
|
+
vertProps.push(x2, y2, z2, nx, ny, nz);
|
|
14962
|
+
}
|
|
14963
|
+
for (let i = 0; i < numVerts; i++) {
|
|
14964
|
+
const [x2, y2, z2] = positions[i];
|
|
14965
|
+
const [nx, ny, nz] = normals[i];
|
|
14966
|
+
vertProps.push(x2 - nx * thickness, y2 - ny * thickness, z2 - nz * thickness, -nx, -ny, -nz);
|
|
14925
14967
|
}
|
|
14926
14968
|
const allIndices = [];
|
|
14927
14969
|
for (const idx of indices) allIndices.push(idx);
|
|
@@ -14946,11 +14988,12 @@ function lowerNurbsSurfaceToManifold(plan, wasm) {
|
|
|
14946
14988
|
allIndices.push(c2, c2 + numVerts, d2, d2, c2 + numVerts, d2 + numVerts);
|
|
14947
14989
|
}
|
|
14948
14990
|
const mesh = new wasm.Mesh({
|
|
14949
|
-
numProp:
|
|
14950
|
-
vertProperties: new Float32Array(
|
|
14991
|
+
numProp: 6,
|
|
14992
|
+
vertProperties: new Float32Array(vertProps),
|
|
14951
14993
|
triVerts: new Uint32Array(allIndices)
|
|
14952
14994
|
});
|
|
14953
14995
|
try {
|
|
14996
|
+
mesh.merge();
|
|
14954
14997
|
return new wasm.Manifold(mesh);
|
|
14955
14998
|
} finally {
|
|
14956
14999
|
disposeWasmObject(mesh);
|
|
@@ -20196,7 +20239,7 @@ function uvInsideTrim(uv, trim) {
|
|
|
20196
20239
|
function add3$1(a2, b) {
|
|
20197
20240
|
return [a2[0] + b[0], a2[1] + b[1], a2[2] + b[2]];
|
|
20198
20241
|
}
|
|
20199
|
-
function sub3$
|
|
20242
|
+
function sub3$6(a2, b) {
|
|
20200
20243
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
20201
20244
|
}
|
|
20202
20245
|
function scale3$1(v, scale2) {
|
|
@@ -20242,7 +20285,7 @@ function resamplePolyline$1(points, count) {
|
|
|
20242
20285
|
if (points.length === 1 || count <= 1) return [points[0]];
|
|
20243
20286
|
const cumulative = [0];
|
|
20244
20287
|
for (let index2 = 1; index2 < points.length; index2 += 1) {
|
|
20245
|
-
cumulative.push(cumulative[index2 - 1] + Math.hypot(...sub3$
|
|
20288
|
+
cumulative.push(cumulative[index2 - 1] + Math.hypot(...sub3$6(points[index2], points[index2 - 1])));
|
|
20246
20289
|
}
|
|
20247
20290
|
const total = cumulative[cumulative.length - 1];
|
|
20248
20291
|
if (total <= 1e-12) return Array.from({ length: count }, () => points[0]);
|
|
@@ -20403,7 +20446,7 @@ function computeGridNormals(positions, indices) {
|
|
|
20403
20446
|
const a2 = positions[indices[index2]];
|
|
20404
20447
|
const b = positions[indices[index2 + 1]];
|
|
20405
20448
|
const c2 = positions[indices[index2 + 2]];
|
|
20406
|
-
const normal2 = cross3$5(sub3$
|
|
20449
|
+
const normal2 = cross3$5(sub3$6(b, a2), sub3$6(c2, a2));
|
|
20407
20450
|
for (const vertexIndex of [indices[index2], indices[index2 + 1], indices[index2 + 2]]) {
|
|
20408
20451
|
normals[vertexIndex][0] += normal2[0];
|
|
20409
20452
|
normals[vertexIndex][1] += normal2[1];
|
|
@@ -20496,16 +20539,16 @@ function planarProjectionFrame(points, context) {
|
|
|
20496
20539
|
const tolerance = pointTolerance(points) * 100;
|
|
20497
20540
|
const uSource = points.find((point2) => distance3$1(point2, origin) > tolerance);
|
|
20498
20541
|
if (!uSource) throw new Error(`${context} boundary loop collapsed to one point.`);
|
|
20499
|
-
const u2 = normalize3$5(sub3$
|
|
20542
|
+
const u2 = normalize3$5(sub3$6(uSource, origin), `${context} u axis`);
|
|
20500
20543
|
const v = cross3$5(normal2, u2);
|
|
20501
|
-
const maxPlaneError = points.reduce((max2, point2) => Math.max(max2, Math.abs(dot3$6(sub3$
|
|
20544
|
+
const maxPlaneError = points.reduce((max2, point2) => Math.max(max2, Math.abs(dot3$6(sub3$6(point2, origin), normal2))), 0);
|
|
20502
20545
|
if (maxPlaneError > tolerance) throw new Error(`${context} boundary loop must be planar for sampled SDF triangulation.`);
|
|
20503
20546
|
return { origin, u: u2, v, tolerance };
|
|
20504
20547
|
}
|
|
20505
20548
|
function meshFromPlanarBoundaryLoop(loop, context) {
|
|
20506
20549
|
const frame = planarProjectionFrame(loop, context);
|
|
20507
20550
|
const projected = loop.map((point2) => {
|
|
20508
|
-
const relative = sub3$
|
|
20551
|
+
const relative = sub3$6(point2, frame.origin);
|
|
20509
20552
|
return new Vector2(dot3$6(relative, frame.u), dot3$6(relative, frame.v));
|
|
20510
20553
|
});
|
|
20511
20554
|
const triangles = ShapeUtils.triangulateShape(projected, []);
|
|
@@ -20514,7 +20557,7 @@ function meshFromPlanarBoundaryLoop(loop, context) {
|
|
|
20514
20557
|
const indices = [];
|
|
20515
20558
|
for (const triangle of triangles) {
|
|
20516
20559
|
const [a2, b, c2] = triangle;
|
|
20517
|
-
const areaNormal = cross3$5(sub3$
|
|
20560
|
+
const areaNormal = cross3$5(sub3$6(positions[b], positions[a2]), sub3$6(positions[c2], positions[a2]));
|
|
20518
20561
|
if (Math.hypot(...areaNormal) > frame.tolerance * frame.tolerance) indices.push(a2, b, c2);
|
|
20519
20562
|
}
|
|
20520
20563
|
if (indices.length === 0) throw new Error(`${context} boundary loop collapsed to zero area.`);
|
|
@@ -29031,7 +29074,7 @@ function wrapSdfMeshShapeBackend(field2, mesh) {
|
|
|
29031
29074
|
return new SdfShapeBackend(field2, mesh);
|
|
29032
29075
|
}
|
|
29033
29076
|
const EPS$c = 1e-8;
|
|
29034
|
-
function sub3$
|
|
29077
|
+
function sub3$5(a2, b) {
|
|
29035
29078
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
29036
29079
|
}
|
|
29037
29080
|
function unit(v, context) {
|
|
@@ -29054,13 +29097,13 @@ function loopNormal(points) {
|
|
|
29054
29097
|
}
|
|
29055
29098
|
function firstEdgeDirection(points, filePath, context) {
|
|
29056
29099
|
for (let index2 = 0; index2 < points.length; index2 += 1) {
|
|
29057
|
-
const edge = sub3$
|
|
29100
|
+
const edge = sub3$5(points[(index2 + 1) % points.length], points[index2]);
|
|
29058
29101
|
if (Math.hypot(edge[0], edge[1], edge[2]) > EPS$c) return unit(edge, context);
|
|
29059
29102
|
}
|
|
29060
29103
|
failStepImport(filePath, `${context} face loop has no non-zero edges.`);
|
|
29061
29104
|
}
|
|
29062
29105
|
function project(point2, origin, u2, v) {
|
|
29063
|
-
const local = sub3$
|
|
29106
|
+
const local = sub3$5(point2, origin);
|
|
29064
29107
|
return new Vector2(dot3$6(local, u2), dot3$6(local, v));
|
|
29065
29108
|
}
|
|
29066
29109
|
function triangulateStepFace(face, filePath) {
|
|
@@ -29084,7 +29127,7 @@ function triangulateStepFace(face, filePath) {
|
|
|
29084
29127
|
const a2 = allPoints[ia];
|
|
29085
29128
|
const b = allPoints[ib];
|
|
29086
29129
|
const c2 = allPoints[ic];
|
|
29087
|
-
const triNormal = cross3$5(sub3$
|
|
29130
|
+
const triNormal = cross3$5(sub3$5(b, a2), sub3$5(c2, a2));
|
|
29088
29131
|
if (Math.hypot(triNormal[0], triNormal[1], triNormal[2]) <= EPS$c) {
|
|
29089
29132
|
failStepImport(filePath, `${face.context} triangulates to a degenerate triangle.`);
|
|
29090
29133
|
}
|
|
@@ -30126,7 +30169,7 @@ function angleDistance(a2, b) {
|
|
|
30126
30169
|
return Math.abs(normalizeAngleDelta(a2 - b));
|
|
30127
30170
|
}
|
|
30128
30171
|
function torusMajorAngle(frame, point2, context) {
|
|
30129
|
-
const local = sub3$
|
|
30172
|
+
const local = sub3$4(point2, frame.origin);
|
|
30130
30173
|
const x2 = dot3$6(local, frame.xAxis);
|
|
30131
30174
|
const y2 = dot3$6(local, frame.yAxis);
|
|
30132
30175
|
if (Math.hypot(x2, y2) <= 1e-12) throw new Error(`${context} torus major angle is degenerate.`);
|
|
@@ -30142,11 +30185,11 @@ function torusRadialDirection(frame, u2) {
|
|
|
30142
30185
|
function torusTubeAngle(frame, majorRadius, u2, point2) {
|
|
30143
30186
|
const radial = torusRadialDirection(frame, u2);
|
|
30144
30187
|
const tubeCenter = addScaled$2(frame.origin, radial, majorRadius);
|
|
30145
|
-
const local = sub3$
|
|
30188
|
+
const local = sub3$4(point2, tubeCenter);
|
|
30146
30189
|
return Math.atan2(dot3$6(local, frame.axis), dot3$6(local, radial));
|
|
30147
30190
|
}
|
|
30148
30191
|
function rotateAroundAxis(point2, axis, angle) {
|
|
30149
|
-
const local = sub3$
|
|
30192
|
+
const local = sub3$4(point2, axis.origin);
|
|
30150
30193
|
const cos2 = Math.cos(angle);
|
|
30151
30194
|
const sin2 = Math.sin(angle);
|
|
30152
30195
|
const cross4 = cross3$5(axis.axis, local);
|
|
@@ -30158,12 +30201,12 @@ function rotateAroundAxis(point2, axis, angle) {
|
|
|
30158
30201
|
];
|
|
30159
30202
|
}
|
|
30160
30203
|
function distanceFromAxis(point2, axis) {
|
|
30161
|
-
const local = sub3$
|
|
30204
|
+
const local = sub3$4(point2, axis.origin);
|
|
30162
30205
|
const axial = dot3$6(local, axis.axis);
|
|
30163
30206
|
return Math.hypot(local[0] - axis.axis[0] * axial, local[1] - axis.axis[1] * axial, local[2] - axis.axis[2] * axial);
|
|
30164
30207
|
}
|
|
30165
30208
|
function radialFromAxis(frame, point2, context) {
|
|
30166
|
-
const local = sub3$
|
|
30209
|
+
const local = sub3$4(point2, frame.origin);
|
|
30167
30210
|
const axial = dot3$6(local, frame.axis);
|
|
30168
30211
|
const radialVector = [local[0] - frame.axis[0] * axial, local[1] - frame.axis[1] * axial, local[2] - frame.axis[2] * axial];
|
|
30169
30212
|
return { axial, radial: Math.hypot(radialVector[0], radialVector[1], radialVector[2]), direction: normalize3$5(radialVector, context) };
|
|
@@ -30232,9 +30275,9 @@ function triangulateBoundedLinearSurfaceOfRevolution(records, curveId, axis, loo
|
|
|
30232
30275
|
return triangulateBoundedLineCurveSurfaceOfRevolution(line2, axis, loop, sameSense, filePath, context);
|
|
30233
30276
|
}
|
|
30234
30277
|
function triangulateBoundedLineCurveSurfaceOfRevolution(line2, axis, loop, sameSense, filePath, context) {
|
|
30235
|
-
const startAxial = dot3$6(sub3$
|
|
30278
|
+
const startAxial = dot3$6(sub3$4(line2.point, axis.origin), axis.axis);
|
|
30236
30279
|
const end = linePoint(line2, 1);
|
|
30237
|
-
const endAxial = dot3$6(sub3$
|
|
30280
|
+
const endAxial = dot3$6(sub3$4(end, axis.origin), axis.axis);
|
|
30238
30281
|
const axialSpan = endAxial - startAxial;
|
|
30239
30282
|
if (Math.abs(axialSpan) <= 1e-8)
|
|
30240
30283
|
failStepImport(filePath, `${context} bounded SURFACE_OF_REVOLUTION LINE profile must change along the revolution axis.`);
|
|
@@ -30263,7 +30306,7 @@ function trimmedLineCurveForSurfaceOfRevolution(records, curve, filePath, contex
|
|
|
30263
30306
|
const sameSense = stepBoolean(curve.args[4], `TRIMMED_CURVE #${curve.id} sense agreement`);
|
|
30264
30307
|
const point2 = sameSense ? start : end;
|
|
30265
30308
|
const target = sameSense ? end : start;
|
|
30266
|
-
const delta = sub3$
|
|
30309
|
+
const delta = sub3$4(target, point2);
|
|
30267
30310
|
const magnitude = Math.hypot(delta[0], delta[1], delta[2]);
|
|
30268
30311
|
if (magnitude <= 1e-10) failStepImport(filePath, `${context} TRIMMED_CURVE LINE profile has zero length.`);
|
|
30269
30312
|
return {
|
|
@@ -30274,10 +30317,10 @@ function trimmedLineCurveForSurfaceOfRevolution(records, curve, filePath, contex
|
|
|
30274
30317
|
}
|
|
30275
30318
|
function revolvedCircleTorus(records, curveId, axis, filePath, context) {
|
|
30276
30319
|
const circle2 = resolveCircle(records, curveId, filePath);
|
|
30277
|
-
const centerOffset = sub3$
|
|
30320
|
+
const centerOffset = sub3$4(circle2.frame.origin, axis.origin);
|
|
30278
30321
|
const axial = dot3$6(centerOffset, axis.axis);
|
|
30279
30322
|
const axisPoint = addScaled$2(axis.origin, axis.axis, axial);
|
|
30280
|
-
const radial = sub3$
|
|
30323
|
+
const radial = sub3$4(circle2.frame.origin, axisPoint);
|
|
30281
30324
|
const majorRadius = Math.hypot(radial[0], radial[1], radial[2]);
|
|
30282
30325
|
if (majorRadius <= 1e-8) failStepImport(filePath, `${context} SURFACE_OF_REVOLUTION circle profile is centered on the axis.`);
|
|
30283
30326
|
if (majorRadius <= circle2.radius)
|
|
@@ -30289,7 +30332,7 @@ function revolvedCircleTorus(records, curveId, axis, filePath, context) {
|
|
|
30289
30332
|
return { frame, majorRadius, minorRadius: circle2.radius };
|
|
30290
30333
|
}
|
|
30291
30334
|
function revolutionAngle(frame, point2, filePath, context) {
|
|
30292
|
-
const local = sub3$
|
|
30335
|
+
const local = sub3$4(point2, frame.origin);
|
|
30293
30336
|
const x2 = dot3$6(local, frame.xAxis);
|
|
30294
30337
|
const y2 = dot3$6(local, frame.yAxis);
|
|
30295
30338
|
if (Math.hypot(x2, y2) <= 1e-12) failStepImport(filePath, `${context} major angle is degenerate.`);
|
|
@@ -30331,10 +30374,10 @@ function orientRevolvedProfileTriangles(triangles, frame, axis, profileCenter, p
|
|
|
30331
30374
|
];
|
|
30332
30375
|
const angle = revolutionAngle(frame, sample, filePath, "SURFACE_OF_REVOLUTION");
|
|
30333
30376
|
const center = rotateAroundAxis(profileCenter, axis, normalizeAngleDelta(angle - profileAngle));
|
|
30334
|
-
const outward = sub3$
|
|
30377
|
+
const outward = sub3$4(sample, center);
|
|
30335
30378
|
if (Math.hypot(outward[0], outward[1], outward[2]) <= 1e-12)
|
|
30336
30379
|
failStepImport(filePath, "SURFACE_OF_REVOLUTION sample hit degenerate profile centerline.");
|
|
30337
|
-
const normal2 = cross3$5(sub3$
|
|
30380
|
+
const normal2 = cross3$5(sub3$4(triangle[1], triangle[0]), sub3$4(triangle[2], triangle[0]));
|
|
30338
30381
|
const outwardFacing = dot3$6(normal2, outward) >= 0;
|
|
30339
30382
|
return sameSense === outwardFacing ? triangle : reverseTriangle(triangle);
|
|
30340
30383
|
});
|
|
@@ -30418,7 +30461,7 @@ function triangulateBoundedToroidalTubeSegment(frame, majorRadius, minorRadius,
|
|
|
30418
30461
|
const loop = compactLoops[loopIndex];
|
|
30419
30462
|
const majorAngle = majorAngles[loopIndex];
|
|
30420
30463
|
for (const point2 of loop) {
|
|
30421
|
-
const local = sub3$
|
|
30464
|
+
const local = sub3$4(point2, frame.origin);
|
|
30422
30465
|
const x2 = dot3$6(local, frame.xAxis);
|
|
30423
30466
|
const y2 = dot3$6(local, frame.yAxis);
|
|
30424
30467
|
const radialLength = Math.hypot(x2, y2);
|
|
@@ -30953,7 +30996,7 @@ function triangulateClosedVBSplineSurfaceBand(surface, loops, sameSense, filePat
|
|
|
30953
30996
|
}
|
|
30954
30997
|
return orientBridgeTriangles(triangles, centroid(a2.points), centroid(b.points), sameSense);
|
|
30955
30998
|
}
|
|
30956
|
-
function sub3$
|
|
30999
|
+
function sub3$4(a2, b) {
|
|
30957
31000
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
30958
31001
|
}
|
|
30959
31002
|
function newellNormal(points) {
|
|
@@ -30992,9 +31035,9 @@ function triangulateBoundedSphericalCap(frame, radius, loop, sameSense, filePath
|
|
|
30992
31035
|
}
|
|
30993
31036
|
const boundaryCenter = centroid(boundary);
|
|
30994
31037
|
const capDirection = normalize3$5(newellNormal(boundary), `${context} bounded SPHERICAL_SURFACE boundary normal`);
|
|
30995
|
-
const maxPlaneError = boundary.reduce((max2, point2) => Math.max(max2, Math.abs(dot3$6(sub3$
|
|
31038
|
+
const maxPlaneError = boundary.reduce((max2, point2) => Math.max(max2, Math.abs(dot3$6(sub3$4(point2, boundaryCenter), capDirection))), 0);
|
|
30996
31039
|
if (maxPlaneError > tolerance) failStepImport(filePath, `${context} bounded SPHERICAL_SURFACE boundary loop is not planar.`);
|
|
30997
|
-
const boundaryDirections = boundary.map((point2) => normalize3$5(sub3$
|
|
31040
|
+
const boundaryDirections = boundary.map((point2) => normalize3$5(sub3$4(point2, frame.origin), `${context} bounded SPHERICAL_SURFACE point`));
|
|
30998
31041
|
const maxAngle = boundaryDirections.reduce((max2, direction2) => Math.max(max2, Math.acos(clamp$8(dot3$6(direction2, capDirection), -1, 1))), 0);
|
|
30999
31042
|
if (Math.PI - maxAngle <= 1e-6) failStepImport(filePath, `${context} bounded SPHERICAL_SURFACE cap is degenerate.`);
|
|
31000
31043
|
const radialSegments = Math.max(4, Math.ceil(maxAngle / (Math.PI / 24)));
|
|
@@ -31033,17 +31076,17 @@ function sphericalLoopInfo(frame, radius, loop, filePath, context) {
|
|
|
31033
31076
|
}
|
|
31034
31077
|
const center = centroid(points);
|
|
31035
31078
|
const normal2 = normalize3$5(newellNormal(points), `${context} bounded SPHERICAL_SURFACE boundary normal`);
|
|
31036
|
-
const maxPlaneError = points.reduce((max2, point2) => Math.max(max2, Math.abs(dot3$6(sub3$
|
|
31079
|
+
const maxPlaneError = points.reduce((max2, point2) => Math.max(max2, Math.abs(dot3$6(sub3$4(point2, center), normal2))), 0);
|
|
31037
31080
|
if (maxPlaneError > tolerance) failStepImport(filePath, `${context} bounded SPHERICAL_SURFACE boundary loop is not planar.`);
|
|
31038
31081
|
return { points, center, normal: normal2 };
|
|
31039
31082
|
}
|
|
31040
31083
|
function triangulateBoundedSphericalBand(frame, radius, loops, sameSense, filePath, context) {
|
|
31041
31084
|
if (loops.length !== 2) failStepImport(filePath, `${context} bounded SPHERICAL_SURFACE band supports exactly two loops right now.`);
|
|
31042
31085
|
const [aInfo, bInfo] = loops.map((loop) => sphericalLoopInfo(frame, radius, loop, filePath, context));
|
|
31043
|
-
const axis = normalize3$5(sub3$
|
|
31086
|
+
const axis = normalize3$5(sub3$4(bInfo.center, aInfo.center), `${context} bounded SPHERICAL_SURFACE band axis`);
|
|
31044
31087
|
const tolerance = Math.max(1e-5, radius * 1e-4);
|
|
31045
31088
|
const axisPointError = (point2) => {
|
|
31046
|
-
const local = sub3$
|
|
31089
|
+
const local = sub3$4(point2, frame.origin);
|
|
31047
31090
|
const projected = addScaled$2(frame.origin, axis, dot3$6(local, axis));
|
|
31048
31091
|
return Math.hypot(point2[0] - projected[0], point2[1] - projected[1], point2[2] - projected[2]);
|
|
31049
31092
|
};
|
|
@@ -31052,12 +31095,12 @@ function triangulateBoundedSphericalBand(frame, radius, loops, sameSense, filePa
|
|
|
31052
31095
|
const count = Math.max(aInfo.points.length, bInfo.points.length, 8);
|
|
31053
31096
|
const aLoop = resampleClosedLoop(aInfo.points, count);
|
|
31054
31097
|
let bLoop = resampleClosedLoop(bInfo.points, count);
|
|
31055
|
-
const rawX = sub3$
|
|
31098
|
+
const rawX = sub3$4(aLoop[0], aInfo.center);
|
|
31056
31099
|
const xProjection = dot3$6(rawX, axis);
|
|
31057
31100
|
const xAxis = normalize3$5([rawX[0] - axis[0] * xProjection, rawX[1] - axis[1] * xProjection, rawX[2] - axis[2] * xProjection], context);
|
|
31058
31101
|
const yAxis = cross3$5(axis, xAxis);
|
|
31059
31102
|
const loopAngle = (point2, center) => {
|
|
31060
|
-
const local = sub3$
|
|
31103
|
+
const local = sub3$4(point2, center);
|
|
31061
31104
|
return Math.atan2(dot3$6(local, yAxis), dot3$6(local, xAxis));
|
|
31062
31105
|
};
|
|
31063
31106
|
const rotateLoop = (loop, center, targetAngle) => {
|
|
@@ -31078,8 +31121,8 @@ function triangulateBoundedSphericalBand(frame, radius, loops, sameSense, filePa
|
|
|
31078
31121
|
const reversed = rotateLoop([...bLoop].reverse(), bInfo.center, aAngles[0]);
|
|
31079
31122
|
bLoop = sequenceMismatch(forward) <= sequenceMismatch(reversed) ? forward : reversed;
|
|
31080
31123
|
const angularSpan = aLoop.reduce((max2, point2, index2) => {
|
|
31081
|
-
const aDirection = normalize3$5(sub3$
|
|
31082
|
-
const bDirection = normalize3$5(sub3$
|
|
31124
|
+
const aDirection = normalize3$5(sub3$4(point2, frame.origin), `${context} bounded SPHERICAL_SURFACE band point`);
|
|
31125
|
+
const bDirection = normalize3$5(sub3$4(bLoop[index2], frame.origin), `${context} bounded SPHERICAL_SURFACE band point`);
|
|
31083
31126
|
return Math.max(max2, Math.acos(clamp$8(dot3$6(aDirection, bDirection), -1, 1)));
|
|
31084
31127
|
}, 0);
|
|
31085
31128
|
if (angularSpan <= 1e-6) failStepImport(filePath, `${context} bounded SPHERICAL_SURFACE band has zero angular span.`);
|
|
@@ -31089,8 +31132,8 @@ function triangulateBoundedSphericalBand(frame, radius, loops, sameSense, filePa
|
|
|
31089
31132
|
const t = ringIndex / radialSegments;
|
|
31090
31133
|
rings.push(
|
|
31091
31134
|
aLoop.map((point2, index2) => {
|
|
31092
|
-
const aDirection = normalize3$5(sub3$
|
|
31093
|
-
const bDirection = normalize3$5(sub3$
|
|
31135
|
+
const aDirection = normalize3$5(sub3$4(point2, frame.origin), `${context} bounded SPHERICAL_SURFACE band point`);
|
|
31136
|
+
const bDirection = normalize3$5(sub3$4(bLoop[index2], frame.origin), `${context} bounded SPHERICAL_SURFACE band point`);
|
|
31094
31137
|
return addScaled$2(frame.origin, slerpUnit(aDirection, bDirection, t), radius);
|
|
31095
31138
|
})
|
|
31096
31139
|
);
|
|
@@ -33139,14 +33182,14 @@ function cross$6(a2, b) {
|
|
|
33139
33182
|
function isFiniteNumber$1(value) {
|
|
33140
33183
|
return typeof value === "number" && Number.isFinite(value);
|
|
33141
33184
|
}
|
|
33142
|
-
function isVec3$
|
|
33185
|
+
function isVec3$3(value) {
|
|
33143
33186
|
return Array.isArray(value) && value.length === 3 && value.every(isFiniteNumber$1);
|
|
33144
33187
|
}
|
|
33145
33188
|
function isVec2(value) {
|
|
33146
33189
|
return Array.isArray(value) && value.length === 2 && value.every(isFiniteNumber$1);
|
|
33147
33190
|
}
|
|
33148
33191
|
function isRuledRails(value) {
|
|
33149
|
-
return Array.isArray(value) && value.length === 2 && value.every((rail2) => Array.isArray(rail2) && rail2.length === 2 && rail2.every(isVec3$
|
|
33192
|
+
return Array.isArray(value) && value.length === 2 && value.every((rail2) => Array.isArray(rail2) && rail2.length === 2 && rail2.every(isVec3$3));
|
|
33150
33193
|
}
|
|
33151
33194
|
function axisSpan(face, axis) {
|
|
33152
33195
|
if (face.vertices.length === 0) return 0;
|
|
@@ -33163,14 +33206,14 @@ function isRationalWeights(weights) {
|
|
|
33163
33206
|
return Boolean(weights == null ? void 0 : weights.some((row) => row.some((weight) => Math.abs(weight - 1) > 1e-9)));
|
|
33164
33207
|
}
|
|
33165
33208
|
function explicitGeometrySurface(geometry, face) {
|
|
33166
|
-
if ("Plane" in geometry && isVec3$
|
|
33209
|
+
if ("Plane" in geometry && isVec3$3(geometry.Plane.normal)) {
|
|
33167
33210
|
return { kind: "plane", normal: normalizeVec3$3(geometry.Plane.normal) };
|
|
33168
33211
|
}
|
|
33169
33212
|
if ("AnalyticCylinder" in geometry) {
|
|
33170
33213
|
const origin = geometry.AnalyticCylinder.axis_origin;
|
|
33171
33214
|
const axis = geometry.AnalyticCylinder.axis_direction;
|
|
33172
33215
|
const radius = geometry.AnalyticCylinder.radius;
|
|
33173
|
-
if (isVec3$
|
|
33216
|
+
if (isVec3$3(origin) && isVec3$3(axis) && isFiniteNumber$1(radius) && radius > 0) {
|
|
33174
33217
|
const normalizedAxis = normalizeVec3$3(axis);
|
|
33175
33218
|
return { kind: "cylinder", origin, axis: normalizedAxis, radius, height: axisSpan(face, normalizedAxis) };
|
|
33176
33219
|
}
|
|
@@ -33180,7 +33223,7 @@ function explicitGeometrySurface(geometry, face) {
|
|
|
33180
33223
|
const axis = geometry.AnalyticCone.axis_direction;
|
|
33181
33224
|
const radiusBottom = geometry.AnalyticCone.radius_start;
|
|
33182
33225
|
const radiusTop = geometry.AnalyticCone.radius_end;
|
|
33183
|
-
if (isVec3$
|
|
33226
|
+
if (isVec3$3(origin) && isVec3$3(axis) && isFiniteNumber$1(radiusBottom) && isFiniteNumber$1(radiusTop)) {
|
|
33184
33227
|
const normalizedAxis = normalizeVec3$3(axis);
|
|
33185
33228
|
return {
|
|
33186
33229
|
kind: "cone",
|
|
@@ -33195,7 +33238,7 @@ function explicitGeometrySurface(geometry, face) {
|
|
|
33195
33238
|
if ("AnalyticSphere" in geometry) {
|
|
33196
33239
|
const center = geometry.AnalyticSphere.center;
|
|
33197
33240
|
const radius = geometry.AnalyticSphere.radius;
|
|
33198
|
-
if (isVec3$
|
|
33241
|
+
if (isVec3$3(center) && isFiniteNumber$1(radius) && radius > 0) {
|
|
33199
33242
|
return { kind: "sphere", center, radius };
|
|
33200
33243
|
}
|
|
33201
33244
|
}
|
|
@@ -33204,7 +33247,7 @@ function explicitGeometrySurface(geometry, face) {
|
|
|
33204
33247
|
const axis = geometry.AnalyticTorus.axis;
|
|
33205
33248
|
const majorRadius = geometry.AnalyticTorus.major_radius;
|
|
33206
33249
|
const minorRadius = geometry.AnalyticTorus.minor_radius;
|
|
33207
|
-
if (isVec3$
|
|
33250
|
+
if (isVec3$3(center) && isVec3$3(axis) && isFiniteNumber$1(majorRadius) && isFiniteNumber$1(minorRadius) && majorRadius > 0 && minorRadius > 0) {
|
|
33208
33251
|
return { kind: "torus", center, axis: normalizeVec3$3(axis), majorRadius, minorRadius };
|
|
33209
33252
|
}
|
|
33210
33253
|
}
|
|
@@ -33302,7 +33345,7 @@ function explicitEdgeCurve(geometry, faceName) {
|
|
|
33302
33345
|
const center = (_a3 = geometry.CircularArc) == null ? void 0 : _a3.center;
|
|
33303
33346
|
const axis = (_b3 = geometry.CircularArc) == null ? void 0 : _b3.axis;
|
|
33304
33347
|
const radius = (_c2 = geometry.CircularArc) == null ? void 0 : _c2.radius;
|
|
33305
|
-
if (center !== void 0 && axis !== void 0 && radius !== void 0 && isVec3$
|
|
33348
|
+
if (center !== void 0 && axis !== void 0 && radius !== void 0 && isVec3$3(center) && isVec3$3(axis) && isFiniteNumber$1(radius) && radius > 0) {
|
|
33306
33349
|
return {
|
|
33307
33350
|
kind: "circle",
|
|
33308
33351
|
center,
|
|
@@ -33313,7 +33356,7 @@ function explicitEdgeCurve(geometry, faceName) {
|
|
|
33313
33356
|
}
|
|
33314
33357
|
const start = (_d2 = geometry.Line) == null ? void 0 : _d2.start;
|
|
33315
33358
|
const end = (_e2 = geometry.Line) == null ? void 0 : _e2.end;
|
|
33316
|
-
if (isVec3$
|
|
33359
|
+
if (isVec3$3(start) && isVec3$3(end)) return makeLineEdgeCurve(start, end, faceName);
|
|
33317
33360
|
const polyline = geometry.PolylineUv;
|
|
33318
33361
|
if (polyline && Array.isArray(polyline.points)) {
|
|
33319
33362
|
const points = polyline.points.filter(isVec2);
|
|
@@ -33658,7 +33701,7 @@ function topologyPayloadToTopology(payload) {
|
|
|
33658
33701
|
if (explicitEdge.visual === false) continue;
|
|
33659
33702
|
const start = explicitVertices[explicitEdge.vertices[0]];
|
|
33660
33703
|
const end = explicitVertices[explicitEdge.vertices[1]];
|
|
33661
|
-
if (!isVec3$
|
|
33704
|
+
if (!isVec3$3(start) || !isVec3$3(end)) continue;
|
|
33662
33705
|
const key2 = edgeKey(start, end);
|
|
33663
33706
|
if (seenEdges.has(key2)) continue;
|
|
33664
33707
|
seenEdges.set(key2, edges.size);
|
|
@@ -33822,6 +33865,7 @@ const _TruckShapeBackend = class _TruckShapeBackend {
|
|
|
33822
33865
|
const payload = JSON.parse(getTruckGeometryWasm().geometry_mesh(this.getLiveHandle("getMesh()")));
|
|
33823
33866
|
const numTri = payload.triangles.length / 3;
|
|
33824
33867
|
const numVert = payload.positions.length / 3;
|
|
33868
|
+
const cornerNormals = payload.normals && payload.normals.length === numTri * 9 ? new Float32Array(payload.normals) : void 0;
|
|
33825
33869
|
this.resource.mesh = {
|
|
33826
33870
|
numProp: 3,
|
|
33827
33871
|
numTri,
|
|
@@ -33835,7 +33879,8 @@ const _TruckShapeBackend = class _TruckShapeBackend {
|
|
|
33835
33879
|
runTransform: new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0]),
|
|
33836
33880
|
faceID: new Int32Array(payload.face_ids),
|
|
33837
33881
|
faceIdNames: payload.face_id_names ?? [],
|
|
33838
|
-
halfedgeTangent: new Float32Array(0)
|
|
33882
|
+
halfedgeTangent: new Float32Array(0),
|
|
33883
|
+
cornerNormals
|
|
33839
33884
|
};
|
|
33840
33885
|
return this.resource.mesh;
|
|
33841
33886
|
}
|
|
@@ -35056,6 +35101,16 @@ function boundsInteriorOverlap(a2, b) {
|
|
|
35056
35101
|
const tolerance = 1e-8;
|
|
35057
35102
|
return Math.min(a2.max[0], b.max[0]) - Math.max(a2.min[0], b.min[0]) > tolerance && Math.min(a2.max[1], b.max[1]) - Math.max(a2.min[1], b.min[1]) > tolerance && Math.min(a2.max[2], b.max[2]) - Math.max(a2.min[2], b.min[2]) > tolerance;
|
|
35058
35103
|
}
|
|
35104
|
+
function boundsFaceOrInteriorOverlap(a2, b) {
|
|
35105
|
+
const tolerance = 1e-8;
|
|
35106
|
+
const overlaps = [
|
|
35107
|
+
Math.min(a2.max[0], b.max[0]) - Math.max(a2.min[0], b.min[0]),
|
|
35108
|
+
Math.min(a2.max[1], b.max[1]) - Math.max(a2.min[1], b.min[1]),
|
|
35109
|
+
Math.min(a2.max[2], b.max[2]) - Math.max(a2.min[2], b.min[2])
|
|
35110
|
+
];
|
|
35111
|
+
if (overlaps.some((overlap) => overlap < -tolerance)) return false;
|
|
35112
|
+
return overlaps.filter((overlap) => overlap <= tolerance).length <= 1;
|
|
35113
|
+
}
|
|
35059
35114
|
function boundsFromPoints(points) {
|
|
35060
35115
|
const first = points[0];
|
|
35061
35116
|
if (!first) return null;
|
|
@@ -35138,16 +35193,16 @@ function shapePlanBounds(plan) {
|
|
|
35138
35193
|
return null;
|
|
35139
35194
|
}
|
|
35140
35195
|
}
|
|
35141
|
-
function
|
|
35196
|
+
function hasPairwiseFaceOrInteriorBoundsOverlap(shapes) {
|
|
35142
35197
|
const bounds = shapes.map((shape) => shape.boundingBox());
|
|
35143
35198
|
for (let i = 0; i < bounds.length; i++) {
|
|
35144
35199
|
for (let j = i + 1; j < bounds.length; j++) {
|
|
35145
|
-
if (
|
|
35200
|
+
if (boundsFaceOrInteriorOverlap(bounds[i], bounds[j])) return true;
|
|
35146
35201
|
}
|
|
35147
35202
|
}
|
|
35148
35203
|
return false;
|
|
35149
35204
|
}
|
|
35150
|
-
function
|
|
35205
|
+
function faceOrInteriorOverlapComponents(shapes) {
|
|
35151
35206
|
const bounds = shapes.map((shape) => shape.boundingBox());
|
|
35152
35207
|
const visited = /* @__PURE__ */ new Set();
|
|
35153
35208
|
const components = [];
|
|
@@ -35161,7 +35216,7 @@ function interiorOverlapComponents(shapes) {
|
|
|
35161
35216
|
component.push(current);
|
|
35162
35217
|
for (let next = 0; next < shapes.length; next++) {
|
|
35163
35218
|
if (visited.has(next)) continue;
|
|
35164
|
-
if (
|
|
35219
|
+
if (boundsFaceOrInteriorOverlap(bounds[current], bounds[next])) {
|
|
35165
35220
|
visited.add(next);
|
|
35166
35221
|
queue.push(next);
|
|
35167
35222
|
}
|
|
@@ -35206,10 +35261,10 @@ function lowerGenericBooleanPlan(plan) {
|
|
|
35206
35261
|
let returned = null;
|
|
35207
35262
|
try {
|
|
35208
35263
|
if (plan.op === "union") {
|
|
35209
|
-
if (!
|
|
35264
|
+
if (!hasPairwiseFaceOrInteriorBoundsOverlap(shapes)) {
|
|
35210
35265
|
return null;
|
|
35211
35266
|
}
|
|
35212
|
-
const components =
|
|
35267
|
+
const components = faceOrInteriorOverlapComponents(shapes);
|
|
35213
35268
|
if (components.length > 1) {
|
|
35214
35269
|
returned = lowerClusteredUnion(shapes, components);
|
|
35215
35270
|
return returned;
|
|
@@ -35739,9 +35794,13 @@ function shapeHasClosedNativeTopology(shape) {
|
|
|
35739
35794
|
function normalizeTruckShapeForBooleanInput(shape) {
|
|
35740
35795
|
if (shapeHasClosedNativeTopology(shape)) return shape;
|
|
35741
35796
|
if (!meshHasRawBoundaryEdges(shape.getMesh())) return shape;
|
|
35742
|
-
|
|
35743
|
-
|
|
35744
|
-
|
|
35797
|
+
try {
|
|
35798
|
+
const normalized = normalizeFacetedTruckShape(shape);
|
|
35799
|
+
disposeShapeBackend(shape);
|
|
35800
|
+
return normalized;
|
|
35801
|
+
} catch {
|
|
35802
|
+
return shape;
|
|
35803
|
+
}
|
|
35745
35804
|
}
|
|
35746
35805
|
function lowerSdfPlan(plan) {
|
|
35747
35806
|
if (getUnsupportedSdfProgramReason(plan.tree) === void 0) {
|
|
@@ -37625,7 +37684,7 @@ function circleProfilePlan(radius, segments) {
|
|
|
37625
37684
|
}
|
|
37626
37685
|
function annulusProfilePlan(outerRadius, innerRadius, segments) {
|
|
37627
37686
|
const outer = Math.abs(outerRadius);
|
|
37628
|
-
const inner = Math.
|
|
37687
|
+
const inner = Math.max(0, innerRadius);
|
|
37629
37688
|
if (outer <= EXACT_PROFILE_EPS) return emptyProfilePlan();
|
|
37630
37689
|
if (inner <= EXACT_PROFILE_EPS) return circleProfilePlan(outer, segments);
|
|
37631
37690
|
return {
|
|
@@ -42563,11 +42622,11 @@ function requireFiniteVec3$3(v, label) {
|
|
|
42563
42622
|
}
|
|
42564
42623
|
return [x2, y2, z2];
|
|
42565
42624
|
}
|
|
42566
|
-
function len3$
|
|
42625
|
+
function len3$3(v) {
|
|
42567
42626
|
return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
|
42568
42627
|
}
|
|
42569
42628
|
function normalize3$4(v) {
|
|
42570
|
-
const l = len3$
|
|
42629
|
+
const l = len3$3(v);
|
|
42571
42630
|
if (l < 1e-10) throw new Error("Cannot normalize zero-length vector");
|
|
42572
42631
|
return [v[0] / l, v[1] / l, v[2] / l];
|
|
42573
42632
|
}
|
|
@@ -42577,7 +42636,7 @@ function dot3$4(a2, b) {
|
|
|
42577
42636
|
function cross3$2(a2, b) {
|
|
42578
42637
|
return [a2[1] * b[2] - a2[2] * b[1], a2[2] * b[0] - a2[0] * b[2], a2[0] * b[1] - a2[1] * b[0]];
|
|
42579
42638
|
}
|
|
42580
|
-
function sub3$
|
|
42639
|
+
function sub3$3(a2, b) {
|
|
42581
42640
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
42582
42641
|
}
|
|
42583
42642
|
function negate3$1(v) {
|
|
@@ -42600,7 +42659,7 @@ function normalizePortInput(input) {
|
|
|
42600
42659
|
const end = requireFiniteVec3$3(input.end, "port end");
|
|
42601
42660
|
origin = [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2, (start[2] + end[2]) / 2];
|
|
42602
42661
|
const dir = [end[0] - start[0], end[1] - start[1], end[2] - start[2]];
|
|
42603
|
-
const dirLen = len3$
|
|
42662
|
+
const dirLen = len3$3(dir);
|
|
42604
42663
|
if (dirLen < 1e-10) {
|
|
42605
42664
|
throw new Error("Port start and end must not be the same point");
|
|
42606
42665
|
}
|
|
@@ -42611,7 +42670,7 @@ function normalizePortInput(input) {
|
|
|
42611
42670
|
} else if (hasOriginAxis) {
|
|
42612
42671
|
origin = requireFiniteVec3$3(input.origin, "port origin");
|
|
42613
42672
|
const rawAxis = requireFiniteVec3$3(input.axis, "port axis");
|
|
42614
|
-
if (len3$
|
|
42673
|
+
if (len3$3(rawAxis) < 1e-10) {
|
|
42615
42674
|
throw new Error("Port axis must be non-zero");
|
|
42616
42675
|
}
|
|
42617
42676
|
axis = normalize3$4(rawAxis);
|
|
@@ -42627,12 +42686,12 @@ function normalizePortInput(input) {
|
|
|
42627
42686
|
let up;
|
|
42628
42687
|
if (input.up != null) {
|
|
42629
42688
|
const rawUp = requireFiniteVec3$3(input.up, "port up");
|
|
42630
|
-
if (len3$
|
|
42689
|
+
if (len3$3(rawUp) < 1e-10) {
|
|
42631
42690
|
throw new Error("Port up vector must be non-zero");
|
|
42632
42691
|
}
|
|
42633
42692
|
const proj = dot3$4(rawUp, axis);
|
|
42634
42693
|
const ortho = [rawUp[0] - proj * axis[0], rawUp[1] - proj * axis[1], rawUp[2] - proj * axis[2]];
|
|
42635
|
-
if (len3$
|
|
42694
|
+
if (len3$3(ortho) < 1e-10) {
|
|
42636
42695
|
throw new Error("Port up vector must not be parallel to axis");
|
|
42637
42696
|
}
|
|
42638
42697
|
up = normalize3$4(ortho);
|
|
@@ -42696,8 +42755,8 @@ function transformPort(port, matrix) {
|
|
|
42696
42755
|
const newOrigin = tx.point(port.origin);
|
|
42697
42756
|
const rawAxis = tx.vector(port.axis);
|
|
42698
42757
|
const rawUp = tx.vector(port.up);
|
|
42699
|
-
const axisLen = len3$
|
|
42700
|
-
const upLen = len3$
|
|
42758
|
+
const axisLen = len3$3(rawAxis);
|
|
42759
|
+
const upLen = len3$3(rawUp);
|
|
42701
42760
|
const out = {
|
|
42702
42761
|
origin: newOrigin,
|
|
42703
42762
|
axis: axisLen > 1e-10 ? normalize3$4(rawAxis) : port.axis,
|
|
@@ -42749,7 +42808,7 @@ function computeConnectFrame(childBase, childPort, parentPort, _flip, childAlign
|
|
|
42749
42808
|
r10 * cI[0] + r11 * cI[1] + r12 * cI[2],
|
|
42750
42809
|
r20 * cI[0] + r21 * cI[1] + r22 * cI[2]
|
|
42751
42810
|
];
|
|
42752
|
-
const t = sub3$
|
|
42811
|
+
const t = sub3$3(pOrigin, rcI);
|
|
42753
42812
|
const frame = Transform.from([r00, r10, r20, 0, r01, r11, r21, 0, r02, r12, r22, 0, t[0], t[1], t[2], 1]);
|
|
42754
42813
|
const axis = cAxis;
|
|
42755
42814
|
return { frame, axis };
|
|
@@ -42834,18 +42893,18 @@ function validateConnectorMatch(selfName, selfPort, targetName, targetPort, forc
|
|
|
42834
42893
|
}
|
|
42835
42894
|
}
|
|
42836
42895
|
}
|
|
42837
|
-
function len3$
|
|
42896
|
+
function len3$2(v) {
|
|
42838
42897
|
return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
|
42839
42898
|
}
|
|
42840
42899
|
function normalize3$3(v) {
|
|
42841
|
-
const l = len3$
|
|
42900
|
+
const l = len3$2(v);
|
|
42842
42901
|
if (l < 1e-10) throw new Error("Cannot normalize zero-length vector");
|
|
42843
42902
|
return [v[0] / l, v[1] / l, v[2] / l];
|
|
42844
42903
|
}
|
|
42845
42904
|
function cross3$1(a2, b) {
|
|
42846
42905
|
return [a2[1] * b[2] - a2[2] * b[1], a2[2] * b[0] - a2[0] * b[2], a2[0] * b[1] - a2[1] * b[0]];
|
|
42847
42906
|
}
|
|
42848
|
-
function sub3$
|
|
42907
|
+
function sub3$2(a2, b) {
|
|
42849
42908
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
42850
42909
|
}
|
|
42851
42910
|
function negate3(v) {
|
|
@@ -42868,7 +42927,7 @@ function alignmentMatrix(childOrigin, childAxis, childUp, parentOrigin, parentAx
|
|
|
42868
42927
|
r10 * childOrigin[0] + r11 * childOrigin[1] + r12 * childOrigin[2],
|
|
42869
42928
|
r20 * childOrigin[0] + r21 * childOrigin[1] + r22 * childOrigin[2]
|
|
42870
42929
|
];
|
|
42871
|
-
const t = sub3$
|
|
42930
|
+
const t = sub3$2(parentOrigin, rc);
|
|
42872
42931
|
return Transform.from([r00, r10, r20, 0, r01, r11, r21, 0, r02, r12, r22, 0, t[0], t[1], t[2], 1]);
|
|
42873
42932
|
}
|
|
42874
42933
|
function computeSinglePairAlignment(childPort, targetPort) {
|
|
@@ -42907,8 +42966,8 @@ function computeMultiPairAlignment(pairs, childPorts, targetPorts, tolerance = 0
|
|
|
42907
42966
|
[0, 0, 0]
|
|
42908
42967
|
];
|
|
42909
42968
|
for (const p2 of pairs) {
|
|
42910
|
-
const s = sub3$
|
|
42911
|
-
const t2 = sub3$
|
|
42969
|
+
const s = sub3$2(p2.childOrigin, srcCentroid);
|
|
42970
|
+
const t2 = sub3$2(p2.targetOrigin, tgtCentroid);
|
|
42912
42971
|
for (let i = 0; i < 3; i++) {
|
|
42913
42972
|
for (let j = 0; j < 3; j++) {
|
|
42914
42973
|
h[i][j] += s[i] * t2[j];
|
|
@@ -42921,7 +42980,7 @@ function computeMultiPairAlignment(pairs, childPorts, targetPorts, tolerance = 0
|
|
|
42921
42980
|
R[1][0] * srcCentroid[0] + R[1][1] * srcCentroid[1] + R[1][2] * srcCentroid[2],
|
|
42922
42981
|
R[2][0] * srcCentroid[0] + R[2][1] * srcCentroid[1] + R[2][2] * srcCentroid[2]
|
|
42923
42982
|
];
|
|
42924
|
-
const t = sub3$
|
|
42983
|
+
const t = sub3$2(tgtCentroid, rSrc);
|
|
42925
42984
|
const transform = Transform.from([
|
|
42926
42985
|
R[0][0],
|
|
42927
42986
|
R[1][0],
|
|
@@ -42943,8 +43002,8 @@ function computeMultiPairAlignment(pairs, childPorts, targetPorts, tolerance = 0
|
|
|
42943
43002
|
const residuals = [];
|
|
42944
43003
|
for (const p2 of pairs) {
|
|
42945
43004
|
const transformed = transform.point(p2.childOrigin);
|
|
42946
|
-
const diff = sub3$
|
|
42947
|
-
residuals.push(len3$
|
|
43005
|
+
const diff = sub3$2(transformed, p2.targetOrigin);
|
|
43006
|
+
residuals.push(len3$2(diff));
|
|
42948
43007
|
}
|
|
42949
43008
|
const maxResidual = Math.max(...residuals);
|
|
42950
43009
|
if (maxResidual > tolerance) {
|
|
@@ -43144,8 +43203,8 @@ function getConnectorDistance(ports, nameA, nameB) {
|
|
|
43144
43203
|
const b = ports[nameB];
|
|
43145
43204
|
if (!a2) throw new Error(`connectorDistance: unknown connector "${nameA}"`);
|
|
43146
43205
|
if (!b) throw new Error(`connectorDistance: unknown connector "${nameB}"`);
|
|
43147
|
-
const d2 = sub3$
|
|
43148
|
-
return len3$
|
|
43206
|
+
const d2 = sub3$2(a2.origin, b.origin);
|
|
43207
|
+
return len3$2(d2);
|
|
43149
43208
|
}
|
|
43150
43209
|
function getConnectorMeasurements(ports, name) {
|
|
43151
43210
|
const p2 = ports[name];
|
|
@@ -48750,8 +48809,7 @@ function sculptLook(preset = "gallery") {
|
|
|
48750
48809
|
{ type: "directional", position: [90, -110, 150], color: "#ffffff", intensity: 1.4 },
|
|
48751
48810
|
{ type: "directional", position: [-120, 70, 80], color: "#dcecff", intensity: 0.65 },
|
|
48752
48811
|
{ type: "hemisphere", skyColor: "#d9edff", groundColor: "#f0e6d4", intensity: 0.45 }
|
|
48753
|
-
]
|
|
48754
|
-
postProcessing: { toneMappingExposure: 1.12, vignette: { darkness: 0.16, offset: 0.6 } }
|
|
48812
|
+
]
|
|
48755
48813
|
};
|
|
48756
48814
|
case "candy-shop":
|
|
48757
48815
|
return {
|
|
@@ -48762,8 +48820,7 @@ function sculptLook(preset = "gallery") {
|
|
|
48762
48820
|
{ type: "point", position: [70, -60, 90], color: "#ff8ac8", intensity: 2.2, distance: 280, decay: 1.2 },
|
|
48763
48821
|
{ type: "point", position: [-85, 80, 70], color: "#70e1ff", intensity: 1.8, distance: 260, decay: 1.4 },
|
|
48764
48822
|
{ type: "directional", position: [30, -80, 140], color: "#fff6dd", intensity: 0.9 }
|
|
48765
|
-
]
|
|
48766
|
-
postProcessing: { toneMappingExposure: 1.25, bloom: { intensity: 0.35, threshold: 0.78, radius: 0.45 } }
|
|
48823
|
+
]
|
|
48767
48824
|
};
|
|
48768
48825
|
case "midnight":
|
|
48769
48826
|
return {
|
|
@@ -48775,8 +48832,7 @@ function sculptLook(preset = "gallery") {
|
|
|
48775
48832
|
{ type: "point", position: [-90, 70, 50], color: "#ff7ac8", intensity: 1.4, distance: 300, decay: 1.3 },
|
|
48776
48833
|
{ type: "directional", position: [30, -30, 160], color: "#d7e9ff", intensity: 0.65 }
|
|
48777
48834
|
],
|
|
48778
|
-
fog: { color: "#060814", near: 180, far: 520 }
|
|
48779
|
-
postProcessing: { toneMappingExposure: 1.35, bloom: { intensity: 0.65, threshold: 0.65, radius: 0.6 } }
|
|
48835
|
+
fog: { color: "#060814", near: 180, far: 520 }
|
|
48780
48836
|
};
|
|
48781
48837
|
case "workbench":
|
|
48782
48838
|
return {
|
|
@@ -48787,8 +48843,7 @@ function sculptLook(preset = "gallery") {
|
|
|
48787
48843
|
{ type: "directional", position: [80, -100, 130], color: "#fff5df", intensity: 1.25 },
|
|
48788
48844
|
{ type: "hemisphere", skyColor: "#eef6ff", groundColor: "#d8d0c0", intensity: 0.35 }
|
|
48789
48845
|
],
|
|
48790
|
-
ground: { visible: true, color: "#d8d4ca", offset: 1, receiveShadow: true }
|
|
48791
|
-
postProcessing: { toneMappingExposure: 1.05 }
|
|
48846
|
+
ground: { visible: true, color: "#d8d4ca", offset: 1, receiveShadow: true }
|
|
48792
48847
|
};
|
|
48793
48848
|
default:
|
|
48794
48849
|
return {
|
|
@@ -48799,8 +48854,7 @@ function sculptLook(preset = "gallery") {
|
|
|
48799
48854
|
{ type: "directional", position: [110, -130, 150], color: "#ffffff", intensity: 1.5 },
|
|
48800
48855
|
{ type: "directional", position: [-90, 80, 90], color: "#b8d8ff", intensity: 0.55 },
|
|
48801
48856
|
{ type: "hemisphere", skyColor: "#ddefff", groundColor: "#e8e2d8", intensity: 0.45 }
|
|
48802
|
-
]
|
|
48803
|
-
postProcessing: { toneMappingExposure: 1.18, vignette: { darkness: 0.18, offset: 0.55 } }
|
|
48857
|
+
]
|
|
48804
48858
|
};
|
|
48805
48859
|
}
|
|
48806
48860
|
}
|
|
@@ -50621,7 +50675,7 @@ class Shape {
|
|
|
50621
50675
|
*
|
|
50622
50676
|
* Use `.color()` to set the base diffuse color; `.material()` controls how that color behaves
|
|
50623
50677
|
* under light (metalness, roughness, clearcoat) and can add emissive glow independent of
|
|
50624
|
-
* lighting.
|
|
50678
|
+
* lighting.
|
|
50625
50679
|
*
|
|
50626
50680
|
* **Example**
|
|
50627
50681
|
*
|
|
@@ -53031,14 +53085,14 @@ function dot3$2(a2, b) {
|
|
|
53031
53085
|
function cross3(a2, b) {
|
|
53032
53086
|
return [a2[1] * b[2] - a2[2] * b[1], a2[2] * b[0] - a2[0] * b[2], a2[0] * b[1] - a2[1] * b[0]];
|
|
53033
53087
|
}
|
|
53034
|
-
function sub3(a2, b) {
|
|
53088
|
+
function sub3$1(a2, b) {
|
|
53035
53089
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
53036
53090
|
}
|
|
53037
|
-
function len3(v) {
|
|
53091
|
+
function len3$1(v) {
|
|
53038
53092
|
return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
|
53039
53093
|
}
|
|
53040
53094
|
function normalize3(v) {
|
|
53041
|
-
const l = len3(v);
|
|
53095
|
+
const l = len3$1(v);
|
|
53042
53096
|
if (l < EPS$8) return [0, 0, 0];
|
|
53043
53097
|
return [v[0] / l, v[1] / l, v[2] / l];
|
|
53044
53098
|
}
|
|
@@ -53050,7 +53104,7 @@ const alignDef = {
|
|
|
53050
53104
|
const faceB = ctx.worldFace(constraint.refB.bodyId, constraint.refB.featureName);
|
|
53051
53105
|
const n1 = faceA.normal;
|
|
53052
53106
|
const n2 = faceB.normal;
|
|
53053
|
-
const delta = sub3(faceB.center, faceA.center);
|
|
53107
|
+
const delta = sub3$1(faceB.center, faceA.center);
|
|
53054
53108
|
const parallel = dot3$2(n1, n2) - 1;
|
|
53055
53109
|
const normalDist = dot3$2(delta, n1);
|
|
53056
53110
|
const cx = n1[1] * n2[2] - n1[2] * n2[1];
|
|
@@ -53093,7 +53147,7 @@ const concentricDef = {
|
|
|
53093
53147
|
const axisA = ctx.worldAxis(constraint.refA.bodyId, constraint.refA.featureName);
|
|
53094
53148
|
const axisB = ctx.worldAxis(constraint.refB.bodyId, constraint.refB.featureName);
|
|
53095
53149
|
const dirCross = cross3(axisA.direction, axisB.direction);
|
|
53096
|
-
const delta = sub3(axisB.origin, axisA.origin);
|
|
53150
|
+
const delta = sub3$1(axisB.origin, axisA.origin);
|
|
53097
53151
|
const offsetCross = cross3(delta, axisA.direction);
|
|
53098
53152
|
const pickTwo = (v) => {
|
|
53099
53153
|
const ax = Math.abs(v[0]);
|
|
@@ -53117,9 +53171,9 @@ const faceDistanceDef = {
|
|
|
53117
53171
|
const distance2 = constraint.value ?? 0;
|
|
53118
53172
|
const n1 = faceA.normal;
|
|
53119
53173
|
const n2 = faceB.normal;
|
|
53120
|
-
const delta = sub3(faceB.center, faceA.center);
|
|
53174
|
+
const delta = sub3$1(faceB.center, faceA.center);
|
|
53121
53175
|
const antiParallel = dot3$2(n1, n2) + 1;
|
|
53122
|
-
const crossMag = len3(cross3(n1, n2));
|
|
53176
|
+
const crossMag = len3$1(cross3(n1, n2));
|
|
53123
53177
|
const signedDist = dot3$2(delta, n1) - distance2;
|
|
53124
53178
|
return [antiParallel, crossMag, signedDist];
|
|
53125
53179
|
}
|
|
@@ -53139,7 +53193,7 @@ const flushDef = {
|
|
|
53139
53193
|
const faceB = ctx.worldFace(constraint.refB.bodyId, constraint.refB.featureName);
|
|
53140
53194
|
const n1 = faceA.normal;
|
|
53141
53195
|
const n2 = faceB.normal;
|
|
53142
|
-
const delta = sub3(faceB.center, faceA.center);
|
|
53196
|
+
const delta = sub3$1(faceB.center, faceA.center);
|
|
53143
53197
|
const antiParallel = dot3$2(n1, n2) + 1;
|
|
53144
53198
|
const normalDist = dot3$2(delta, n1);
|
|
53145
53199
|
const cx = n1[1] * n2[2] - n1[2] * n2[1];
|
|
@@ -53179,7 +53233,7 @@ const pointOnAxisDef = {
|
|
|
53179
53233
|
residual(constraint, ctx) {
|
|
53180
53234
|
const point2 = ctx.worldPoint(constraint.refA.bodyId, constraint.refA.featureName);
|
|
53181
53235
|
const axis = ctx.worldAxis(constraint.refB.bodyId, constraint.refB.featureName);
|
|
53182
|
-
const delta = sub3(point2, axis.origin);
|
|
53236
|
+
const delta = sub3$1(point2, axis.origin);
|
|
53183
53237
|
const c2 = cross3(delta, axis.direction);
|
|
53184
53238
|
const ax = Math.abs(c2[0]);
|
|
53185
53239
|
const ay = Math.abs(c2[1]);
|
|
@@ -53195,7 +53249,7 @@ const pointOnFaceDef = {
|
|
|
53195
53249
|
residual(constraint, ctx) {
|
|
53196
53250
|
const point2 = ctx.worldPoint(constraint.refA.bodyId, constraint.refA.featureName);
|
|
53197
53251
|
const face = ctx.worldFace(constraint.refB.bodyId, constraint.refB.featureName);
|
|
53198
|
-
const delta = sub3(point2, face.center);
|
|
53252
|
+
const delta = sub3$1(point2, face.center);
|
|
53199
53253
|
return [dot3$2(delta, face.normal)];
|
|
53200
53254
|
}
|
|
53201
53255
|
};
|
|
@@ -53744,7 +53798,7 @@ function runWithForgeValidationPolicy(policy, fn) {
|
|
|
53744
53798
|
}
|
|
53745
53799
|
let _collected$8 = null;
|
|
53746
53800
|
const isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
53747
|
-
const isVec3$
|
|
53801
|
+
const isVec3$2 = (value) => Array.isArray(value) && value.length === 3 && isFiniteNumber(value[0]) && isFiniteNumber(value[1]) && isFiniteNumber(value[2]);
|
|
53748
53802
|
const normalizeAxis = (axis) => {
|
|
53749
53803
|
const len2 = Math.hypot(axis[0], axis[1], axis[2]);
|
|
53750
53804
|
if (len2 <= 1e-8) throw new Error("jointsView joint axis must be non-zero");
|
|
@@ -53779,10 +53833,10 @@ const normalizeJoint = (joint2) => {
|
|
|
53779
53833
|
throw new Error(`jointsView joint "${name}" type must be "revolute" or "prismatic"`);
|
|
53780
53834
|
}
|
|
53781
53835
|
const axisRaw = joint2.axis ?? [0, 0, 1];
|
|
53782
|
-
if (!isVec3$
|
|
53836
|
+
if (!isVec3$2(axisRaw)) throw new Error(`jointsView joint "${name}" axis must be [x, y, z]`);
|
|
53783
53837
|
const axis = normalizeAxis([axisRaw[0], axisRaw[1], axisRaw[2]]);
|
|
53784
53838
|
const pivotRaw = joint2.pivot ?? [0, 0, 0];
|
|
53785
|
-
if (!isVec3$
|
|
53839
|
+
if (!isVec3$2(pivotRaw)) throw new Error(`jointsView joint "${name}" pivot must be [x, y, z]`);
|
|
53786
53840
|
const pivot = [pivotRaw[0], pivotRaw[1], pivotRaw[2]];
|
|
53787
53841
|
if (joint2.min !== void 0 && !isFiniteNumber(joint2.min)) {
|
|
53788
53842
|
throw new Error(`jointsView joint "${name}" min must be a finite number`);
|
|
@@ -54701,7 +54755,7 @@ function deriveExplodeHintsFromMates(constraints, result, bodies, ctx) {
|
|
|
54701
54755
|
const posA = result.transforms.get(c2.refA.bodyId);
|
|
54702
54756
|
const posB = result.transforms.get(c2.refB.bodyId);
|
|
54703
54757
|
if (posA && posB) {
|
|
54704
|
-
const raw = sub3(bodyA.grounded ? posB.position : posA.position, bodyA.grounded ? posA.position : posB.position);
|
|
54758
|
+
const raw = sub3$1(bodyA.grounded ? posB.position : posA.position, bodyA.grounded ? posA.position : posB.position);
|
|
54705
54759
|
dir = normalize3(raw);
|
|
54706
54760
|
}
|
|
54707
54761
|
break;
|
|
@@ -55227,8 +55281,8 @@ class Assembly {
|
|
|
55227
55281
|
*
|
|
55228
55282
|
* Use this after adding physical parts and joints. Robot-body profiles require
|
|
55229
55283
|
* `rootPart`; asset profiles can describe one-part or multi-part physical assets.
|
|
55230
|
-
* URDF/SDF exporters and `forgecad check simready` read this
|
|
55231
|
-
*
|
|
55284
|
+
* URDF/SDF/MJCF/USD exporters and `forgecad check simready` read this
|
|
55285
|
+
* contract directly from the returned assembly.
|
|
55232
55286
|
*
|
|
55233
55287
|
* @category Assembly
|
|
55234
55288
|
*/
|
|
@@ -64532,7 +64586,7 @@ function beltDrive(options) {
|
|
|
64532
64586
|
}
|
|
64533
64587
|
const GEAR_META_KEY = Symbol.for("forgecad.library.gearMeta");
|
|
64534
64588
|
const EPSILON$1 = 1e-9;
|
|
64535
|
-
function clamp01$
|
|
64589
|
+
function clamp01$2(value) {
|
|
64536
64590
|
return Math.max(-1, Math.min(1, value));
|
|
64537
64591
|
}
|
|
64538
64592
|
function isFinitePositive(value) {
|
|
@@ -64552,7 +64606,7 @@ function addArcPoints(target, radius, startAngle, endAngle, steps, includeStart
|
|
|
64552
64606
|
}
|
|
64553
64607
|
}
|
|
64554
64608
|
function flankAngleAtRadius(radius, baseRadius, halfThicknessAtPitch, pressureAngleRad) {
|
|
64555
|
-
const alphaAtRadius = Math.acos(clamp01$
|
|
64609
|
+
const alphaAtRadius = Math.acos(clamp01$2(baseRadius / Math.max(radius, baseRadius)));
|
|
64556
64610
|
return halfThicknessAtPitch + involuteFn(pressureAngleRad) - involuteFn(alphaAtRadius);
|
|
64557
64611
|
}
|
|
64558
64612
|
function addRootFilletPoints(target, rootRadius, filletRadius, flankAngle, sign2, fromFlank, steps) {
|
|
@@ -65672,7 +65726,7 @@ function gearPair(options) {
|
|
|
65672
65726
|
message: `Center distance ${centerDistance.toFixed(4)} exceeds addendum reach ${addendumReach.toFixed(4)} (no mesh contact)`
|
|
65673
65727
|
});
|
|
65674
65728
|
}
|
|
65675
|
-
const cosWorking = clamp01$
|
|
65729
|
+
const cosWorking = clamp01$2(baseSum / Math.max(centerDistance, EPSILON$1));
|
|
65676
65730
|
const alphaWorking = Math.acos(cosWorking);
|
|
65677
65731
|
const basePitch = Math.PI * module * Math.cos(alpha);
|
|
65678
65732
|
const pathLength = Math.sqrt(Math.max(0, pinion.meta.outerRadius ** 2 - pinion.meta.baseRadius ** 2)) + Math.sqrt(Math.max(0, gear.meta.outerRadius ** 2 - gear.meta.baseRadius ** 2)) - centerDistance * Math.sin(alphaWorking);
|
|
@@ -66385,7 +66439,8 @@ const partLibrary = {
|
|
|
66385
66439
|
const EDGE_THRESHOLD_DOT = Math.cos(Math.PI / 180);
|
|
66386
66440
|
const SMOOTH_THRESHOLD_DOT = Math.cos(30 * Math.PI / 180);
|
|
66387
66441
|
function computeGeometryArrays(mesh, options = {}) {
|
|
66388
|
-
const { numProp, numTri: triCount, triVerts, vertProperties, vertNormals } = mesh;
|
|
66442
|
+
const { numProp, numTri: triCount, triVerts, vertProperties, vertNormals, cornerNormals } = mesh;
|
|
66443
|
+
const useCornerNormals = !!cornerNormals && cornerNormals.length === triCount * 9;
|
|
66389
66444
|
const positions = new Float32Array(triCount * 9);
|
|
66390
66445
|
const normals = new Float32Array(triCount * 9);
|
|
66391
66446
|
const faceNx = new Float32Array(triCount);
|
|
@@ -66417,7 +66472,9 @@ function computeGeometryArrays(mesh, options = {}) {
|
|
|
66417
66472
|
positions[o + 6] = cx;
|
|
66418
66473
|
positions[o + 7] = cy;
|
|
66419
66474
|
positions[o + 8] = cz;
|
|
66420
|
-
if (
|
|
66475
|
+
if (useCornerNormals) {
|
|
66476
|
+
for (let k2 = 0; k2 < 9; k2++) normals[o + k2] = cornerNormals[o + k2];
|
|
66477
|
+
} else if (vertNormals) {
|
|
66421
66478
|
normals[o] = vertNormals[i0 * 3];
|
|
66422
66479
|
normals[o + 1] = vertNormals[i0 * 3 + 1];
|
|
66423
66480
|
normals[o + 2] = vertNormals[i0 * 3 + 2];
|
|
@@ -66460,7 +66517,7 @@ function computeGeometryArrays(mesh, options = {}) {
|
|
|
66460
66517
|
faceNy[t] = fny;
|
|
66461
66518
|
faceNz[t] = fnz;
|
|
66462
66519
|
}
|
|
66463
|
-
if (!vertNormals && numProp < 6 && triCount > 0) {
|
|
66520
|
+
if (!useCornerNormals && !vertNormals && numProp < 6 && triCount > 0) {
|
|
66464
66521
|
computeAutoSmoothNormals(
|
|
66465
66522
|
triVerts,
|
|
66466
66523
|
vertProperties,
|
|
@@ -66625,26 +66682,26 @@ function getPendingShapeHighlights() {
|
|
|
66625
66682
|
function resetPendingShapeHighlights() {
|
|
66626
66683
|
pendingShapeHighlights = [];
|
|
66627
66684
|
}
|
|
66628
|
-
function isVec3(v) {
|
|
66685
|
+
function isVec3$1(v) {
|
|
66629
66686
|
return Array.isArray(v) && v.length === 3 && typeof v[0] === "number" && typeof v[1] === "number" && typeof v[2] === "number";
|
|
66630
66687
|
}
|
|
66631
66688
|
function isEdgePair(v) {
|
|
66632
|
-
return Array.isArray(v) && v.length === 2 && isVec3(v[0]) && isVec3(v[1]);
|
|
66689
|
+
return Array.isArray(v) && v.length === 2 && isVec3$1(v[0]) && isVec3$1(v[1]);
|
|
66633
66690
|
}
|
|
66634
66691
|
function isPlaneSpec(v) {
|
|
66635
66692
|
if (typeof v !== "object" || v === null) return false;
|
|
66636
66693
|
const obj = v;
|
|
66637
|
-
return isVec3(obj.normal) && (typeof obj.offset === "number" || isVec3(obj.point));
|
|
66694
|
+
return isVec3$1(obj.normal) && (typeof obj.offset === "number" || isVec3$1(obj.point));
|
|
66638
66695
|
}
|
|
66639
66696
|
function isFaceRef$1(v) {
|
|
66640
66697
|
if (typeof v !== "object" || v === null) return false;
|
|
66641
66698
|
const obj = v;
|
|
66642
|
-
return isVec3(obj.normal) && isVec3(obj.center) && typeof obj.name === "string";
|
|
66699
|
+
return isVec3$1(obj.normal) && isVec3$1(obj.center) && typeof obj.name === "string";
|
|
66643
66700
|
}
|
|
66644
66701
|
function isEdgeRef(v) {
|
|
66645
66702
|
if (typeof v !== "object" || v === null) return false;
|
|
66646
66703
|
const obj = v;
|
|
66647
|
-
return isVec3(obj.start) && isVec3(obj.end) && typeof obj.name === "string";
|
|
66704
|
+
return isVec3$1(obj.start) && isVec3$1(obj.end) && typeof obj.name === "string";
|
|
66648
66705
|
}
|
|
66649
66706
|
function requireFiniteVec3(v, name) {
|
|
66650
66707
|
for (let i = 0; i < 3; i++) {
|
|
@@ -66683,7 +66740,7 @@ function highlight(target, opts) {
|
|
|
66683
66740
|
});
|
|
66684
66741
|
return;
|
|
66685
66742
|
}
|
|
66686
|
-
if (isVec3(target)) {
|
|
66743
|
+
if (isVec3$1(target)) {
|
|
66687
66744
|
requireFiniteVec3(target, "point");
|
|
66688
66745
|
collectedDebugHighlights3D.push({
|
|
66689
66746
|
kind: "point",
|
|
@@ -67727,7 +67784,7 @@ const PRESETS = {
|
|
|
67727
67784
|
}
|
|
67728
67785
|
};
|
|
67729
67786
|
new Set(Object.keys(PRESETS));
|
|
67730
|
-
function clamp01(value) {
|
|
67787
|
+
function clamp01$1(value) {
|
|
67731
67788
|
return Math.max(0, Math.min(1, value));
|
|
67732
67789
|
}
|
|
67733
67790
|
function applyMaterial(shape, preset) {
|
|
@@ -67749,7 +67806,7 @@ const materials = {
|
|
|
67749
67806
|
clearPolycarbonate(options = {}) {
|
|
67750
67807
|
return {
|
|
67751
67808
|
color: options.tint ?? "#bdefff",
|
|
67752
|
-
material: { opacity: clamp01(options.opacity ?? 0.34), roughness: 0.08, metalness: 0, clearcoat: 1, clearcoatRoughness: 0.03 }
|
|
67809
|
+
material: { opacity: clamp01$1(options.opacity ?? 0.34), roughness: 0.08, metalness: 0, clearcoat: 1, clearcoatRoughness: 0.03 }
|
|
67753
67810
|
};
|
|
67754
67811
|
},
|
|
67755
67812
|
/** Brushed steel-like material for trim, soleplates, and hardware. */
|
|
@@ -68226,31 +68283,6 @@ function validateFog(fog, label) {
|
|
|
68226
68283
|
if (fog.density !== void 0) out.density = requireFinite$5(fog.density, `${label}.density`);
|
|
68227
68284
|
return out;
|
|
68228
68285
|
}
|
|
68229
|
-
function validatePostProcessing(pp, label) {
|
|
68230
|
-
const out = {};
|
|
68231
|
-
if (pp.bloom !== void 0) {
|
|
68232
|
-
if (!pp.bloom || typeof pp.bloom !== "object") throw new Error(`${label}.bloom must be an object`);
|
|
68233
|
-
out.bloom = {};
|
|
68234
|
-
if (pp.bloom.intensity !== void 0) out.bloom.intensity = requireFinite$5(pp.bloom.intensity, `${label}.bloom.intensity`);
|
|
68235
|
-
if (pp.bloom.threshold !== void 0) out.bloom.threshold = requireFinite$5(pp.bloom.threshold, `${label}.bloom.threshold`);
|
|
68236
|
-
if (pp.bloom.radius !== void 0) out.bloom.radius = requireFinite$5(pp.bloom.radius, `${label}.bloom.radius`);
|
|
68237
|
-
}
|
|
68238
|
-
if (pp.vignette !== void 0) {
|
|
68239
|
-
if (!pp.vignette || typeof pp.vignette !== "object") throw new Error(`${label}.vignette must be an object`);
|
|
68240
|
-
out.vignette = {};
|
|
68241
|
-
if (pp.vignette.darkness !== void 0) out.vignette.darkness = requireFinite$5(pp.vignette.darkness, `${label}.vignette.darkness`);
|
|
68242
|
-
if (pp.vignette.offset !== void 0) out.vignette.offset = requireFinite$5(pp.vignette.offset, `${label}.vignette.offset`);
|
|
68243
|
-
}
|
|
68244
|
-
if (pp.grain !== void 0) {
|
|
68245
|
-
if (!pp.grain || typeof pp.grain !== "object") throw new Error(`${label}.grain must be an object`);
|
|
68246
|
-
out.grain = {};
|
|
68247
|
-
if (pp.grain.intensity !== void 0) out.grain.intensity = requireFinite$5(pp.grain.intensity, `${label}.grain.intensity`);
|
|
68248
|
-
}
|
|
68249
|
-
if (pp.toneMappingExposure !== void 0) {
|
|
68250
|
-
out.toneMappingExposure = requireFinite$5(pp.toneMappingExposure, `${label}.toneMappingExposure`);
|
|
68251
|
-
}
|
|
68252
|
-
return out;
|
|
68253
|
-
}
|
|
68254
68286
|
function validateGround(ground, label) {
|
|
68255
68287
|
const out = {};
|
|
68256
68288
|
if (ground.visible !== void 0) {
|
|
@@ -68332,7 +68364,6 @@ function scene(options) {
|
|
|
68332
68364
|
lights: null,
|
|
68333
68365
|
environment: null,
|
|
68334
68366
|
fog: null,
|
|
68335
|
-
postProcessing: null,
|
|
68336
68367
|
ground: null,
|
|
68337
68368
|
capture: null
|
|
68338
68369
|
};
|
|
@@ -68372,12 +68403,9 @@ function scene(options) {
|
|
|
68372
68403
|
}
|
|
68373
68404
|
current.fog = validateFog(options.fog, "scene.fog");
|
|
68374
68405
|
}
|
|
68375
|
-
|
|
68376
|
-
|
|
68377
|
-
|
|
68378
|
-
}
|
|
68379
|
-
const validated = validatePostProcessing(options.postProcessing, "scene.postProcessing");
|
|
68380
|
-
current.postProcessing = current.postProcessing ? { ...current.postProcessing, ...validated } : validated;
|
|
68406
|
+
const disabledPostProcessing = options.postProcessing;
|
|
68407
|
+
if (disabledPostProcessing !== void 0) {
|
|
68408
|
+
console.warn("scene.postProcessing is disabled for now while the browser post-processing path is being rebuilt.");
|
|
68381
68409
|
}
|
|
68382
68410
|
if (options.ground !== void 0) {
|
|
68383
68411
|
if (!options.ground || typeof options.ground !== "object") {
|
|
@@ -68537,8 +68565,7 @@ function scenePreset(name) {
|
|
|
68537
68565
|
{ type: "hemisphere", skyColor: "#ffffff", groundColor: "#d5d9de", intensity: 0.75 },
|
|
68538
68566
|
{ type: "directional", position: [90, -120, 180], color: "#ffffff", intensity: 1.8, castShadow: true },
|
|
68539
68567
|
{ type: "directional", position: [-140, 100, 80], color: "#dfe9ff", intensity: 0.55 }
|
|
68540
|
-
]
|
|
68541
|
-
postProcessing: { toneMappingExposure: 1.08 }
|
|
68568
|
+
]
|
|
68542
68569
|
});
|
|
68543
68570
|
return;
|
|
68544
68571
|
}
|
|
@@ -68780,8 +68807,8 @@ function shapeBoundsCenter(shape) {
|
|
|
68780
68807
|
];
|
|
68781
68808
|
}
|
|
68782
68809
|
class ProductSurfaceRef {
|
|
68783
|
-
constructor(
|
|
68784
|
-
this.skin =
|
|
68810
|
+
constructor(skin2, query, name) {
|
|
68811
|
+
this.skin = skin2;
|
|
68785
68812
|
this.query = query;
|
|
68786
68813
|
this.name = name;
|
|
68787
68814
|
}
|
|
@@ -69298,8 +69325,8 @@ class ProductHandleBuilder {
|
|
|
69298
69325
|
}
|
|
69299
69326
|
}
|
|
69300
69327
|
class ProductSurfaceBuilder {
|
|
69301
|
-
constructor(
|
|
69302
|
-
this.skin =
|
|
69328
|
+
constructor(skin2, side) {
|
|
69329
|
+
this.skin = skin2;
|
|
69303
69330
|
this.side = side;
|
|
69304
69331
|
}
|
|
69305
69332
|
/** Create a ref on this skin side. */
|
|
@@ -69367,9 +69394,9 @@ class ProductRibbonBuilder {
|
|
|
69367
69394
|
* ProductSkin.frame(), so the ribbon bends along the selected side as station width/depth changes.
|
|
69368
69395
|
* All query path points must stay on one side; split side transitions into separate ribbons.
|
|
69369
69396
|
*/
|
|
69370
|
-
on(
|
|
69397
|
+
on(skin2, points, options = {}) {
|
|
69371
69398
|
if (points.length < 2) throw new Error("Product.ribbon().on(skin, points) requires at least two path points");
|
|
69372
|
-
this.skinValue =
|
|
69399
|
+
this.skinValue = skin2;
|
|
69373
69400
|
this.queryPath = resolvePathQueries(points);
|
|
69374
69401
|
this.refPath = [];
|
|
69375
69402
|
return this.applyOptions(options);
|
|
@@ -69492,7 +69519,7 @@ class ProductRibbonBuilder {
|
|
|
69492
69519
|
const localT = scaled - segment;
|
|
69493
69520
|
return interpolateQuery(this.queryPath[segment], this.queryPath[segment + 1], localT);
|
|
69494
69521
|
}
|
|
69495
|
-
buildSkinGrid(
|
|
69522
|
+
buildSkinGrid(skin2, path2) {
|
|
69496
69523
|
if (path2.length < 2) throw new Error("Product.ribbon().on(skin, points) must be called before .build()");
|
|
69497
69524
|
const side = normalizedSide(path2[0].side);
|
|
69498
69525
|
if (side === "front" || side === "rear") {
|
|
@@ -69511,7 +69538,7 @@ class ProductRibbonBuilder {
|
|
|
69511
69538
|
for (let i = 0; i < this.samplesValue; i += 1) {
|
|
69512
69539
|
const along = this.samplesValue === 1 ? 0 : i / (this.samplesValue - 1);
|
|
69513
69540
|
const center = this.samplePathQuery(along);
|
|
69514
|
-
const station =
|
|
69541
|
+
const station = skin2.stationAt(center.v ?? 0.5);
|
|
69515
69542
|
const span = sideSpan(side, station.width, station.depth);
|
|
69516
69543
|
for (let j = 0; j < this.widthSamplesValue; j += 1) {
|
|
69517
69544
|
const across = this.widthSamplesValue === 1 ? 0 : j / (this.widthSamplesValue - 1) - 0.5;
|
|
@@ -69528,13 +69555,13 @@ class ProductRibbonBuilder {
|
|
|
69528
69555
|
u: u2,
|
|
69529
69556
|
offset: (center.offset ?? 0) + this.offsetValue + this.thicknessValue
|
|
69530
69557
|
};
|
|
69531
|
-
rows[j].push(
|
|
69558
|
+
rows[j].push(skin2.frame(query).point);
|
|
69532
69559
|
}
|
|
69533
69560
|
}
|
|
69534
69561
|
return {
|
|
69535
69562
|
grid: rows,
|
|
69536
69563
|
diagnostics: this.makeDiagnostics({
|
|
69537
|
-
skin:
|
|
69564
|
+
skin: skin2.name,
|
|
69538
69565
|
side,
|
|
69539
69566
|
pathPointCount: path2.length,
|
|
69540
69567
|
clampedUCount,
|
|
@@ -69691,16 +69718,16 @@ const Product = {
|
|
|
69691
69718
|
return scaleProfileTo(sketch, width, depth);
|
|
69692
69719
|
},
|
|
69693
69720
|
/** Create an ad-hoc ProductSurfaceRef from a skin and side/u/v query. */
|
|
69694
|
-
ref(
|
|
69695
|
-
return new ProductSurfaceRef(
|
|
69721
|
+
ref(skin2, query) {
|
|
69722
|
+
return new ProductSurfaceRef(skin2, query);
|
|
69696
69723
|
},
|
|
69697
69724
|
/**
|
|
69698
69725
|
* Create a fluent surface helper for refs and conformal features on one side of a skin.
|
|
69699
69726
|
*
|
|
69700
69727
|
* Equivalent to skin.surface(side), useful when writing in Product.* namespace style.
|
|
69701
69728
|
*/
|
|
69702
|
-
surface(
|
|
69703
|
-
return
|
|
69729
|
+
surface(skin2, side) {
|
|
69730
|
+
return skin2.surface(side);
|
|
69704
69731
|
},
|
|
69705
69732
|
/** Start a panel feature builder. */
|
|
69706
69733
|
panel(name) {
|
|
@@ -70169,9 +70196,9 @@ function coordinateOnSide(coordinate, side, label) {
|
|
|
70169
70196
|
return { ...coordinate, kind: "productSkin", side };
|
|
70170
70197
|
}
|
|
70171
70198
|
class ProductSkinCarrier {
|
|
70172
|
-
constructor(
|
|
70199
|
+
constructor(skin2, name = skin2.name, sideValue2, offsetValue = 0) {
|
|
70173
70200
|
__publicField(this, "kind", "productSkin");
|
|
70174
|
-
this.skin =
|
|
70201
|
+
this.skin = skin2;
|
|
70175
70202
|
this.name = name;
|
|
70176
70203
|
this.sideValue = sideValue2;
|
|
70177
70204
|
this.offsetValue = offsetValue;
|
|
@@ -73105,8 +73132,8 @@ const Carrier = {
|
|
|
73105
73132
|
return new PlaneCarrier(name);
|
|
73106
73133
|
},
|
|
73107
73134
|
/** Adapt an existing ProductSkin into the general surface-member carrier protocol. */
|
|
73108
|
-
productSkin(
|
|
73109
|
-
return new ProductSkinCarrier(
|
|
73135
|
+
productSkin(skin2) {
|
|
73136
|
+
return new ProductSkinCarrier(skin2);
|
|
73110
73137
|
},
|
|
73111
73138
|
/** Reserved stub for future parameterized mesh carriers; arbitrary mesh parameterization is not implemented yet. */
|
|
73112
73139
|
mesh(name) {
|
|
@@ -73174,206 +73201,6 @@ const SurfaceMembers = {
|
|
|
73174
73201
|
}
|
|
73175
73202
|
};
|
|
73176
73203
|
new TextEncoder();
|
|
73177
|
-
let _collectedRobotExport = null;
|
|
73178
|
-
function cloneLinkOptions(input) {
|
|
73179
|
-
if (!input) return {};
|
|
73180
|
-
return Object.fromEntries(Object.entries(input).map(([name, opts]) => [name, { ...opts }]));
|
|
73181
|
-
}
|
|
73182
|
-
function cloneJointOptions(input) {
|
|
73183
|
-
if (!input) return {};
|
|
73184
|
-
return Object.fromEntries(Object.entries(input).map(([name, opts]) => [name, { ...opts }]));
|
|
73185
|
-
}
|
|
73186
|
-
function cloneDiffDrive(input) {
|
|
73187
|
-
if (!input) return void 0;
|
|
73188
|
-
return {
|
|
73189
|
-
...input,
|
|
73190
|
-
leftJoints: [...input.leftJoints],
|
|
73191
|
-
rightJoints: [...input.rightJoints]
|
|
73192
|
-
};
|
|
73193
|
-
}
|
|
73194
|
-
function cloneJointStatePublisher(input) {
|
|
73195
|
-
if (!input) return void 0;
|
|
73196
|
-
return {
|
|
73197
|
-
...input,
|
|
73198
|
-
joints: input.joints ? [...input.joints] : void 0
|
|
73199
|
-
};
|
|
73200
|
-
}
|
|
73201
|
-
function cloneWorld(input) {
|
|
73202
|
-
if (!input) return null;
|
|
73203
|
-
return {
|
|
73204
|
-
...input,
|
|
73205
|
-
spawnPose: input.spawnPose ? [...input.spawnPose] : void 0,
|
|
73206
|
-
keyboardTeleop: input.keyboardTeleop ? { ...input.keyboardTeleop } : void 0
|
|
73207
|
-
};
|
|
73208
|
-
}
|
|
73209
|
-
function assertFinite(value, label) {
|
|
73210
|
-
if (value !== void 0 && !Number.isFinite(value)) {
|
|
73211
|
-
throw new Error(`${label} must be finite`);
|
|
73212
|
-
}
|
|
73213
|
-
}
|
|
73214
|
-
function metadataNumber(value) {
|
|
73215
|
-
return typeof value === "number" ? value : void 0;
|
|
73216
|
-
}
|
|
73217
|
-
function metadataCollision(value) {
|
|
73218
|
-
return value === "none" || value === "visual" || value === "box" || value === "convex" ? value : void 0;
|
|
73219
|
-
}
|
|
73220
|
-
function colliderCollision(collider2) {
|
|
73221
|
-
if (!collider2) return void 0;
|
|
73222
|
-
if (collider2.mode === "box") return "box";
|
|
73223
|
-
if (collider2.mode === "visual") return "visual";
|
|
73224
|
-
return collider2.mode;
|
|
73225
|
-
}
|
|
73226
|
-
function velocityDriveEffort(drive) {
|
|
73227
|
-
return (drive == null ? void 0 : drive.kind) === "velocity" ? drive.maxTorqueNm : void 0;
|
|
73228
|
-
}
|
|
73229
|
-
function velocityDriveVelocityDegS(drive) {
|
|
73230
|
-
return (drive == null ? void 0 : drive.kind) === "velocity" ? drive.maxSpeedRpm * 6 : void 0;
|
|
73231
|
-
}
|
|
73232
|
-
function driveDamping(drive) {
|
|
73233
|
-
return drive == null ? void 0 : drive.damping;
|
|
73234
|
-
}
|
|
73235
|
-
function driveFriction(drive) {
|
|
73236
|
-
return drive == null ? void 0 : drive.friction;
|
|
73237
|
-
}
|
|
73238
|
-
function jointByName(assembly2) {
|
|
73239
|
-
return new Map(assembly2.joints.map((joint2) => [joint2.name, joint2]));
|
|
73240
|
-
}
|
|
73241
|
-
function diffDriveFromControllers(controllers) {
|
|
73242
|
-
if (!controllers) return void 0;
|
|
73243
|
-
const diffDrives = controllers.filter((controller) => controller.kind === "diffDrive");
|
|
73244
|
-
if (diffDrives.length > 1) {
|
|
73245
|
-
throw new Error("assembly.withSimulation(...) currently supports one Sim.controller.diffDrive(...) controller");
|
|
73246
|
-
}
|
|
73247
|
-
const diffDrive2 = diffDrives[0];
|
|
73248
|
-
return diffDrive2 ? {
|
|
73249
|
-
leftJoints: [...diffDrive2.leftJoints],
|
|
73250
|
-
rightJoints: [...diffDrive2.rightJoints],
|
|
73251
|
-
wheelSeparationMm: diffDrive2.wheelSeparationMm,
|
|
73252
|
-
wheelRadiusMm: diffDrive2.wheelRadiusMm,
|
|
73253
|
-
topic: diffDrive2.topic,
|
|
73254
|
-
odomTopic: diffDrive2.odomTopic,
|
|
73255
|
-
tfTopic: diffDrive2.tfTopic,
|
|
73256
|
-
frameId: diffDrive2.frameId,
|
|
73257
|
-
odomFrameId: diffDrive2.odomFrameId,
|
|
73258
|
-
maxLinearVelocity: diffDrive2.maxLinearVelocity,
|
|
73259
|
-
maxAngularVelocity: diffDrive2.maxAngularVelocity,
|
|
73260
|
-
linearAcceleration: diffDrive2.linearAcceleration,
|
|
73261
|
-
angularAcceleration: diffDrive2.angularAcceleration
|
|
73262
|
-
} : void 0;
|
|
73263
|
-
}
|
|
73264
|
-
function resetRobotExport() {
|
|
73265
|
-
_collectedRobotExport = null;
|
|
73266
|
-
}
|
|
73267
|
-
function getCollectedRobotExport() {
|
|
73268
|
-
return _collectedRobotExport;
|
|
73269
|
-
}
|
|
73270
|
-
function collectSimulationModel(assemblyInput, options = {}) {
|
|
73271
|
-
var _a3, _b3, _c2, _d2, _e2, _f2, _g2, _h2, _i2;
|
|
73272
|
-
const assembly2 = typeof assemblyInput.describe === "function" ? assemblyInput.describe() : assemblyInput;
|
|
73273
|
-
const partNames = new Set(assembly2.parts.map((part) => part.name));
|
|
73274
|
-
const joints = jointByName(assembly2);
|
|
73275
|
-
const links = cloneLinkOptions(options.links);
|
|
73276
|
-
for (const part of assembly2.parts) {
|
|
73277
|
-
const fromSim = part.sim;
|
|
73278
|
-
const fromMaterialDensity = (_a3 = fromSim == null ? void 0 : fromSim.material) == null ? void 0 : _a3.densityKgM3;
|
|
73279
|
-
const current = links[part.name] ?? {};
|
|
73280
|
-
const next = {
|
|
73281
|
-
massKg: current.massKg ?? (fromSim == null ? void 0 : fromSim.massKg) ?? metadataNumber((_b3 = part.metadata) == null ? void 0 : _b3.massKg),
|
|
73282
|
-
densityKgM3: current.densityKgM3 ?? (fromSim == null ? void 0 : fromSim.densityKgM3) ?? fromMaterialDensity ?? metadataNumber((_c2 = part.metadata) == null ? void 0 : _c2.densityKgM3),
|
|
73283
|
-
collision: current.collision ?? colliderCollision(fromSim == null ? void 0 : fromSim.collider) ?? metadataCollision((_d2 = part.metadata) == null ? void 0 : _d2.collision)
|
|
73284
|
-
};
|
|
73285
|
-
if (next.massKg !== void 0 || next.densityKgM3 !== void 0 || next.collision !== void 0) {
|
|
73286
|
-
links[part.name] = next;
|
|
73287
|
-
}
|
|
73288
|
-
}
|
|
73289
|
-
for (const [partName, link] of Object.entries(links)) {
|
|
73290
|
-
if (!partNames.has(partName)) throw new Error(`simulation model references unknown link "${partName}"`);
|
|
73291
|
-
assertFinite(link.massKg, `simulation model link "${partName}" massKg`);
|
|
73292
|
-
assertFinite(link.densityKgM3, `simulation model link "${partName}" densityKgM3`);
|
|
73293
|
-
}
|
|
73294
|
-
const jointOpts = cloneJointOptions(options.joints);
|
|
73295
|
-
for (const joint2 of assembly2.joints) {
|
|
73296
|
-
const drive = (_e2 = joint2.sim) == null ? void 0 : _e2.drive;
|
|
73297
|
-
const current = jointOpts[joint2.name] ?? {};
|
|
73298
|
-
const next = {
|
|
73299
|
-
effort: current.effort ?? velocityDriveEffort(drive) ?? joint2.effort,
|
|
73300
|
-
velocity: current.velocity ?? velocityDriveVelocityDegS(drive) ?? joint2.velocity,
|
|
73301
|
-
damping: current.damping ?? driveDamping(drive) ?? joint2.damping,
|
|
73302
|
-
friction: current.friction ?? driveFriction(drive) ?? joint2.friction
|
|
73303
|
-
};
|
|
73304
|
-
if (next.effort !== void 0 || next.velocity !== void 0 || next.damping !== void 0 || next.friction !== void 0) {
|
|
73305
|
-
jointOpts[joint2.name] = next;
|
|
73306
|
-
}
|
|
73307
|
-
}
|
|
73308
|
-
for (const [jointName, joint2] of Object.entries(jointOpts)) {
|
|
73309
|
-
if (!joints.has(jointName)) throw new Error(`simulation model references unknown joint "${jointName}"`);
|
|
73310
|
-
assertFinite(joint2.effort, `simulation model joint "${jointName}" effort`);
|
|
73311
|
-
assertFinite(joint2.velocity, `simulation model joint "${jointName}" velocity`);
|
|
73312
|
-
assertFinite(joint2.damping, `simulation model joint "${jointName}" damping`);
|
|
73313
|
-
assertFinite(joint2.friction, `simulation model joint "${jointName}" friction`);
|
|
73314
|
-
}
|
|
73315
|
-
const simulation = cloneSimAssemblySimulation(assembly2.sim) ?? null;
|
|
73316
|
-
const simulationDiffDrive = diffDriveFromControllers(simulation == null ? void 0 : simulation.controllers);
|
|
73317
|
-
const diffDrive2 = cloneDiffDrive((_f2 = options.plugins) == null ? void 0 : _f2.diffDrive) ?? simulationDiffDrive;
|
|
73318
|
-
if (diffDrive2) {
|
|
73319
|
-
if (diffDrive2.leftJoints.length === 0 || diffDrive2.rightJoints.length === 0) {
|
|
73320
|
-
throw new Error("simulation model diffDrive requires at least one left joint and one right joint");
|
|
73321
|
-
}
|
|
73322
|
-
assertFinite(diffDrive2.wheelSeparationMm, "simulation model diffDrive wheelSeparationMm");
|
|
73323
|
-
assertFinite(diffDrive2.wheelRadiusMm, "simulation model diffDrive wheelRadiusMm");
|
|
73324
|
-
if (diffDrive2.wheelSeparationMm <= 0 || diffDrive2.wheelRadiusMm <= 0) {
|
|
73325
|
-
throw new Error("simulation model diffDrive wheel separation and radius must be > 0");
|
|
73326
|
-
}
|
|
73327
|
-
[...diffDrive2.leftJoints, ...diffDrive2.rightJoints].forEach((jointName) => {
|
|
73328
|
-
const joint2 = joints.get(jointName);
|
|
73329
|
-
if (!joint2) throw new Error(`simulation model diffDrive references unknown joint "${jointName}"`);
|
|
73330
|
-
if (joint2.type !== "revolute") {
|
|
73331
|
-
throw new Error(`simulation model diffDrive joint "${jointName}" must be revolute`);
|
|
73332
|
-
}
|
|
73333
|
-
});
|
|
73334
|
-
}
|
|
73335
|
-
const jointStatePublisher = cloneJointStatePublisher((_g2 = options.plugins) == null ? void 0 : _g2.jointStatePublisher);
|
|
73336
|
-
if (jointStatePublisher == null ? void 0 : jointStatePublisher.joints) {
|
|
73337
|
-
jointStatePublisher.joints.forEach((jointName) => {
|
|
73338
|
-
if (!joints.has(jointName)) {
|
|
73339
|
-
throw new Error(`simulation model jointStatePublisher references unknown joint "${jointName}"`);
|
|
73340
|
-
}
|
|
73341
|
-
});
|
|
73342
|
-
}
|
|
73343
|
-
const world = cloneWorld(options.world);
|
|
73344
|
-
if (world == null ? void 0 : world.spawnPose) {
|
|
73345
|
-
world.spawnPose.forEach((value, index2) => assertFinite(value, `simulation model world spawnPose[${index2}]`));
|
|
73346
|
-
}
|
|
73347
|
-
assertFinite((_h2 = world == null ? void 0 : world.keyboardTeleop) == null ? void 0 : _h2.linearStep, "simulation model world keyboardTeleop.linearStep");
|
|
73348
|
-
assertFinite((_i2 = world == null ? void 0 : world.keyboardTeleop) == null ? void 0 : _i2.angularStep, "simulation model world keyboardTeleop.angularStep");
|
|
73349
|
-
return {
|
|
73350
|
-
modelName: (options.modelName ?? assembly2.name ?? "ForgeCAD Simulation").trim() || "ForgeCAD Simulation",
|
|
73351
|
-
assembly: assembly2,
|
|
73352
|
-
simulation,
|
|
73353
|
-
source: options.source ?? "assembly",
|
|
73354
|
-
state: { ...options.state ?? {} },
|
|
73355
|
-
static: options.static ?? false,
|
|
73356
|
-
selfCollide: options.selfCollide ?? false,
|
|
73357
|
-
allowAutoDisable: options.allowAutoDisable ?? true,
|
|
73358
|
-
links,
|
|
73359
|
-
joints: jointOpts,
|
|
73360
|
-
plugins: {
|
|
73361
|
-
diffDrive: diffDrive2,
|
|
73362
|
-
jointStatePublisher
|
|
73363
|
-
},
|
|
73364
|
-
world
|
|
73365
|
-
};
|
|
73366
|
-
}
|
|
73367
|
-
function robotExport(options) {
|
|
73368
|
-
if (!options || typeof options !== "object") {
|
|
73369
|
-
throw new Error("robotExport(...) expects an options object");
|
|
73370
|
-
}
|
|
73371
|
-
if (!options.assembly || typeof options.assembly.describe !== "function") {
|
|
73372
|
-
throw new Error("robotExport(...) requires an assembly");
|
|
73373
|
-
}
|
|
73374
|
-
_collectedRobotExport = collectSimulationModel(options.assembly, { ...options, source: "robotExport" });
|
|
73375
|
-
return _collectedRobotExport;
|
|
73376
|
-
}
|
|
73377
73204
|
class Point2D {
|
|
73378
73205
|
constructor(x2, y2) {
|
|
73379
73206
|
this.x = x2;
|
|
@@ -95647,6 +95474,847 @@ function sketchOnFace(sketch, parentOrFace, faceOrOpts, maybeOpts = {}) {
|
|
|
95647
95474
|
Sketch.prototype.onFace = function(parentOrFace, faceOrOpts, maybeOpts = {}) {
|
|
95648
95475
|
return sketchOnFace(this, parentOrFace, faceOrOpts, maybeOpts);
|
|
95649
95476
|
};
|
|
95477
|
+
const KNOT_EPS = 1e-10;
|
|
95478
|
+
const KNOT_MERGE_EPS = 1e-7;
|
|
95479
|
+
function sameKnot(a2, b) {
|
|
95480
|
+
return Math.abs(a2 - b) <= KNOT_EPS;
|
|
95481
|
+
}
|
|
95482
|
+
function knotMultiplicity(knots, u2) {
|
|
95483
|
+
let count = 0;
|
|
95484
|
+
for (const k2 of knots) if (sameKnot(k2, u2)) count++;
|
|
95485
|
+
return count;
|
|
95486
|
+
}
|
|
95487
|
+
function computeParamsCentripetal(points, alpha = 0.5) {
|
|
95488
|
+
const n = points.length;
|
|
95489
|
+
const params = new Array(n).fill(0);
|
|
95490
|
+
const seg = new Array(n).fill(0);
|
|
95491
|
+
let total = 0;
|
|
95492
|
+
for (let k2 = 1; k2 < n; k2++) {
|
|
95493
|
+
const dx = points[k2][0] - points[k2 - 1][0];
|
|
95494
|
+
const dy = points[k2][1] - points[k2 - 1][1];
|
|
95495
|
+
const dz = points[k2][2] - points[k2 - 1][2];
|
|
95496
|
+
seg[k2] = Math.hypot(dx, dy, dz) ** alpha;
|
|
95497
|
+
total += seg[k2];
|
|
95498
|
+
}
|
|
95499
|
+
if (total === 0) {
|
|
95500
|
+
for (let k2 = 0; k2 < n; k2++) params[k2] = n > 1 ? k2 / (n - 1) : 0;
|
|
95501
|
+
return params;
|
|
95502
|
+
}
|
|
95503
|
+
let acc = 0;
|
|
95504
|
+
for (let k2 = 1; k2 < n - 1; k2++) {
|
|
95505
|
+
acc += seg[k2];
|
|
95506
|
+
params[k2] = acc / total;
|
|
95507
|
+
}
|
|
95508
|
+
params[n - 1] = 1;
|
|
95509
|
+
return params;
|
|
95510
|
+
}
|
|
95511
|
+
function knotsFromParamsAveraging(params, degree) {
|
|
95512
|
+
const n = params.length - 1;
|
|
95513
|
+
const m2 = n + degree + 1;
|
|
95514
|
+
const knots = new Array(m2 + 1).fill(0);
|
|
95515
|
+
for (let i = m2 - degree; i <= m2; i++) knots[i] = 1;
|
|
95516
|
+
for (let j = 1; j <= n - degree; j++) {
|
|
95517
|
+
let s = 0;
|
|
95518
|
+
for (let i = j; i <= j + degree - 1; i++) s += params[i];
|
|
95519
|
+
knots[j + degree] = s / degree;
|
|
95520
|
+
}
|
|
95521
|
+
return knots;
|
|
95522
|
+
}
|
|
95523
|
+
function solveLinear(A, B2) {
|
|
95524
|
+
const n = A.length;
|
|
95525
|
+
const k2 = B2[0].length;
|
|
95526
|
+
const M = A.map((row, i) => [...row, ...B2[i]]);
|
|
95527
|
+
for (let col = 0; col < n; col++) {
|
|
95528
|
+
let piv = col;
|
|
95529
|
+
for (let r = col + 1; r < n; r++) if (Math.abs(M[r][col]) > Math.abs(M[piv][col])) piv = r;
|
|
95530
|
+
if (Math.abs(M[piv][col]) < 1e-14) throw new Error(`Singular interpolation matrix at column ${col}.`);
|
|
95531
|
+
[M[col], M[piv]] = [M[piv], M[col]];
|
|
95532
|
+
const inv = 1 / M[col][col];
|
|
95533
|
+
for (let j = col; j < n + k2; j++) M[col][j] *= inv;
|
|
95534
|
+
for (let r = 0; r < n; r++) {
|
|
95535
|
+
if (r === col) continue;
|
|
95536
|
+
const f3 = M[r][col];
|
|
95537
|
+
if (f3 === 0) continue;
|
|
95538
|
+
for (let j = col; j < n + k2; j++) M[r][j] -= f3 * M[col][j];
|
|
95539
|
+
}
|
|
95540
|
+
}
|
|
95541
|
+
return Array.from({ length: n }, (_2, i) => M[i].slice(n));
|
|
95542
|
+
}
|
|
95543
|
+
function globalCurveInterp(points, degree, paramsIn) {
|
|
95544
|
+
const last = points.length - 1;
|
|
95545
|
+
if (last < degree) throw new Error(`Curve interpolation needs at least ${degree + 1} points, got ${points.length}.`);
|
|
95546
|
+
const params = paramsIn ? paramsIn.slice() : computeParamsCentripetal(points, 0.5);
|
|
95547
|
+
const knots = knotsFromParamsAveraging(params, degree);
|
|
95548
|
+
const count = last + 1;
|
|
95549
|
+
const N = Array.from({ length: count }, () => new Array(count).fill(0));
|
|
95550
|
+
for (let i = 0; i <= last; i++) {
|
|
95551
|
+
const span = findSpan(count, degree, params[i], knots);
|
|
95552
|
+
const basis = basisFuns(span, params[i], degree, knots);
|
|
95553
|
+
for (let j = 0; j <= degree; j++) N[i][span - degree + j] = basis[j];
|
|
95554
|
+
}
|
|
95555
|
+
const Q = points.map((p2) => [p2[0], p2[1], p2[2]]);
|
|
95556
|
+
const cps = solveLinear(N, Q);
|
|
95557
|
+
return { cps, knots, degree };
|
|
95558
|
+
}
|
|
95559
|
+
function insertKnotCurve(cps, knots, degree, u2) {
|
|
95560
|
+
const n = cps.length;
|
|
95561
|
+
const span = findSpan(n, degree, u2, knots);
|
|
95562
|
+
const mult = knotMultiplicity(knots, u2);
|
|
95563
|
+
if (mult >= degree) return { cps: cps.slice(), knots: knots.slice() };
|
|
95564
|
+
const newCps = new Array(n + 1);
|
|
95565
|
+
const newKnots = new Array(knots.length + 1);
|
|
95566
|
+
for (let i = 0; i <= span; i++) newKnots[i] = knots[i];
|
|
95567
|
+
newKnots[span + 1] = u2;
|
|
95568
|
+
for (let i = span + 1; i < knots.length; i++) newKnots[i + 1] = knots[i];
|
|
95569
|
+
for (let i = 0; i <= span - degree; i++) newCps[i] = [...cps[i]];
|
|
95570
|
+
for (let i = span - mult; i < n; i++) newCps[i + 1] = [...cps[i]];
|
|
95571
|
+
for (let i = span - degree + 1; i <= span - mult; i++) {
|
|
95572
|
+
const denom = knots[i + degree] - knots[i];
|
|
95573
|
+
const alpha = denom === 0 ? 0 : (u2 - knots[i]) / denom;
|
|
95574
|
+
const a2 = cps[i - 1];
|
|
95575
|
+
const b = cps[i];
|
|
95576
|
+
newCps[i] = [(1 - alpha) * a2[0] + alpha * b[0], (1 - alpha) * a2[1] + alpha * b[1], (1 - alpha) * a2[2] + alpha * b[2]];
|
|
95577
|
+
}
|
|
95578
|
+
return { cps: newCps, knots: newKnots };
|
|
95579
|
+
}
|
|
95580
|
+
function interiorKnotBuckets(kv, mergeEps = KNOT_MERGE_EPS) {
|
|
95581
|
+
const buckets = [];
|
|
95582
|
+
for (const k2 of kv) {
|
|
95583
|
+
if (k2 <= KNOT_EPS || k2 >= 1 - KNOT_EPS) continue;
|
|
95584
|
+
const b = buckets.find((x2) => Math.abs(x2.value - k2) <= mergeEps);
|
|
95585
|
+
if (b) b.mult++;
|
|
95586
|
+
else buckets.push({ value: k2, mult: 1 });
|
|
95587
|
+
}
|
|
95588
|
+
return buckets;
|
|
95589
|
+
}
|
|
95590
|
+
function mergeKnotVectors(knotVectors, degree, mergeEps = KNOT_MERGE_EPS) {
|
|
95591
|
+
const merged = [];
|
|
95592
|
+
for (const kv of knotVectors) {
|
|
95593
|
+
for (const { value, mult } of interiorKnotBuckets(kv, mergeEps)) {
|
|
95594
|
+
const b = merged.find((x2) => Math.abs(x2.value - value) <= mergeEps);
|
|
95595
|
+
if (b) b.mult = Math.max(b.mult, mult);
|
|
95596
|
+
else merged.push({ value, mult });
|
|
95597
|
+
}
|
|
95598
|
+
}
|
|
95599
|
+
merged.sort((a2, b) => a2.value - b.value);
|
|
95600
|
+
const out = [];
|
|
95601
|
+
for (let i = 0; i <= degree; i++) out.push(0);
|
|
95602
|
+
for (const { value, mult } of merged) for (let i = 0; i < mult; i++) out.push(value);
|
|
95603
|
+
for (let i = 0; i <= degree; i++) out.push(1);
|
|
95604
|
+
return out;
|
|
95605
|
+
}
|
|
95606
|
+
function refineKnotsToTarget(cps, knots, degree, targetKnots, mergeEps = KNOT_MERGE_EPS) {
|
|
95607
|
+
let curCps = cps.map((p2) => [...p2]);
|
|
95608
|
+
let curKnots = knots.slice();
|
|
95609
|
+
for (const { value, mult } of interiorKnotBuckets(targetKnots, mergeEps)) {
|
|
95610
|
+
const curMult = () => curKnots.reduce((c2, k2) => c2 + (Math.abs(k2 - value) <= mergeEps ? 1 : 0), 0);
|
|
95611
|
+
let guard = 0;
|
|
95612
|
+
while (curMult() < mult) {
|
|
95613
|
+
const r = insertKnotCurve(curCps, curKnots, degree, value);
|
|
95614
|
+
if (r.cps.length === curCps.length) break;
|
|
95615
|
+
curCps = r.cps;
|
|
95616
|
+
curKnots = r.knots;
|
|
95617
|
+
if (++guard > degree + 2) break;
|
|
95618
|
+
}
|
|
95619
|
+
}
|
|
95620
|
+
return { cps: curCps, knots: curKnots };
|
|
95621
|
+
}
|
|
95622
|
+
function binomial(a2, b) {
|
|
95623
|
+
if (b < 0 || b > a2) return 0;
|
|
95624
|
+
let r = 1;
|
|
95625
|
+
for (let i = 0; i < b; i++) r = r * (a2 - i) / (i + 1);
|
|
95626
|
+
return r;
|
|
95627
|
+
}
|
|
95628
|
+
function bezierElevate(bez, p2, t) {
|
|
95629
|
+
const m2 = p2 + t;
|
|
95630
|
+
const out = [];
|
|
95631
|
+
for (let i = 0; i <= m2; i++) {
|
|
95632
|
+
const acc = [0, 0, 0];
|
|
95633
|
+
const lo = Math.max(0, i - t);
|
|
95634
|
+
const hi = Math.min(p2, i);
|
|
95635
|
+
for (let j = lo; j <= hi; j++) {
|
|
95636
|
+
const w2 = binomial(p2, j) * binomial(t, i - j) / binomial(m2, i);
|
|
95637
|
+
acc[0] += w2 * bez[j][0];
|
|
95638
|
+
acc[1] += w2 * bez[j][1];
|
|
95639
|
+
acc[2] += w2 * bez[j][2];
|
|
95640
|
+
}
|
|
95641
|
+
out.push(acc);
|
|
95642
|
+
}
|
|
95643
|
+
return out;
|
|
95644
|
+
}
|
|
95645
|
+
function elevateCurveDegree(cps, knots, degree, targetDegree) {
|
|
95646
|
+
if (targetDegree === degree) return { cps: cps.map((p2) => [...p2]), knots: knots.slice(), degree };
|
|
95647
|
+
if (targetDegree < degree) throw new Error("Degree reduction is not supported.");
|
|
95648
|
+
const t = targetDegree - degree;
|
|
95649
|
+
let curCps = cps.map((p2) => [...p2]);
|
|
95650
|
+
let curKnots = knots.slice();
|
|
95651
|
+
const breaks = interiorKnotBuckets(curKnots).map((b) => b.value);
|
|
95652
|
+
for (const v of breaks) {
|
|
95653
|
+
let guard = 0;
|
|
95654
|
+
while (knotMultiplicity(curKnots, v) < degree) {
|
|
95655
|
+
const r = insertKnotCurve(curCps, curKnots, degree, v);
|
|
95656
|
+
curCps = r.cps;
|
|
95657
|
+
curKnots = r.knots;
|
|
95658
|
+
if (++guard > degree + 2) break;
|
|
95659
|
+
}
|
|
95660
|
+
}
|
|
95661
|
+
const segVals = [0, ...breaks.slice().sort((a2, b) => a2 - b), 1];
|
|
95662
|
+
const nSeg = segVals.length - 1;
|
|
95663
|
+
const newCps = [];
|
|
95664
|
+
for (let s = 0; s < nSeg; s++) {
|
|
95665
|
+
const bez = curCps.slice(s * degree, s * degree + degree + 1);
|
|
95666
|
+
const elevated = bezierElevate(bez, degree, t);
|
|
95667
|
+
if (s === 0) newCps.push(...elevated);
|
|
95668
|
+
else newCps.push(...elevated.slice(1));
|
|
95669
|
+
}
|
|
95670
|
+
const newKnots = [];
|
|
95671
|
+
for (let i = 0; i <= targetDegree; i++) newKnots.push(0);
|
|
95672
|
+
for (let s = 1; s < nSeg; s++) for (let i = 0; i < targetDegree; i++) newKnots.push(segVals[s]);
|
|
95673
|
+
for (let i = 0; i <= targetDegree; i++) newKnots.push(1);
|
|
95674
|
+
if (newKnots.length !== newCps.length + targetDegree + 1) {
|
|
95675
|
+
throw new Error(
|
|
95676
|
+
`Degree elevation produced inconsistent knots (${newKnots.length}) for ${newCps.length} control points at degree ${targetDegree}.`
|
|
95677
|
+
);
|
|
95678
|
+
}
|
|
95679
|
+
return { cps: newCps, knots: newKnots, degree: targetDegree };
|
|
95680
|
+
}
|
|
95681
|
+
function unifyCurves(curves) {
|
|
95682
|
+
const targetDeg = Math.max(...curves.map((c2) => c2.degree));
|
|
95683
|
+
const elevated = curves.map((c2) => elevateCurveDegree(c2.cps, c2.knots, c2.degree, targetDeg));
|
|
95684
|
+
const common = mergeKnotVectors(
|
|
95685
|
+
elevated.map((c2) => c2.knots),
|
|
95686
|
+
targetDeg
|
|
95687
|
+
);
|
|
95688
|
+
const refined = elevated.map((c2) => refineKnotsToTarget(c2.cps, c2.knots, targetDeg, common));
|
|
95689
|
+
const ncp = common.length - targetDeg - 1;
|
|
95690
|
+
for (const r of refined) {
|
|
95691
|
+
if (r.cps.length !== ncp) throw new Error(`Curve unification produced ${r.cps.length} control points, expected ${ncp}.`);
|
|
95692
|
+
}
|
|
95693
|
+
return { degree: targetDeg, knots: common, curves: refined.map((r) => r.cps) };
|
|
95694
|
+
}
|
|
95695
|
+
function transpose(g2) {
|
|
95696
|
+
const out = [];
|
|
95697
|
+
for (let j = 0; j < g2[0].length; j++) {
|
|
95698
|
+
const row = [];
|
|
95699
|
+
for (let i = 0; i < g2.length; i++) row.push(g2[i][j]);
|
|
95700
|
+
out.push(row);
|
|
95701
|
+
}
|
|
95702
|
+
return out;
|
|
95703
|
+
}
|
|
95704
|
+
function skin(curveCps, spanParams, spanDegree) {
|
|
95705
|
+
const ncp = curveCps[0].length;
|
|
95706
|
+
const cols = [];
|
|
95707
|
+
for (let j = 0; j < ncp; j++) {
|
|
95708
|
+
const pts = curveCps.map((row) => row[j]);
|
|
95709
|
+
cols.push(globalCurveInterp(pts, spanDegree, spanParams).cps);
|
|
95710
|
+
}
|
|
95711
|
+
const spanCp = cols[0].length;
|
|
95712
|
+
const grid = [];
|
|
95713
|
+
for (let s = 0; s < spanCp; s++) {
|
|
95714
|
+
const row = [];
|
|
95715
|
+
for (let j = 0; j < ncp; j++) row.push(cols[j][s]);
|
|
95716
|
+
grid.push(row);
|
|
95717
|
+
}
|
|
95718
|
+
const spanKnots = knotsFromParamsAveraging(spanParams, spanDegree);
|
|
95719
|
+
return { grid, spanKnots };
|
|
95720
|
+
}
|
|
95721
|
+
function tensorInterp(gridPts, uParams, vParams, degreeU, degreeV) {
|
|
95722
|
+
const rowInterp = gridPts.map((rowPts) => globalCurveInterp(rowPts, degreeV, vParams));
|
|
95723
|
+
const knotsV = rowInterp[0].knots;
|
|
95724
|
+
const ncpV = rowInterp[0].cps.length;
|
|
95725
|
+
const cols = [];
|
|
95726
|
+
for (let j = 0; j < ncpV; j++) {
|
|
95727
|
+
const pts = rowInterp.map((ri) => ri.cps[j]);
|
|
95728
|
+
cols.push(globalCurveInterp(pts, degreeU, uParams));
|
|
95729
|
+
}
|
|
95730
|
+
const knotsU = cols[0].knots;
|
|
95731
|
+
const cpU = cols[0].cps.length;
|
|
95732
|
+
const grid = [];
|
|
95733
|
+
for (let i = 0; i < cpU; i++) {
|
|
95734
|
+
const row = [];
|
|
95735
|
+
for (let j = 0; j < ncpV; j++) row.push(cols[j].cps[i]);
|
|
95736
|
+
grid.push(row);
|
|
95737
|
+
}
|
|
95738
|
+
return { grid, knotsU, knotsV };
|
|
95739
|
+
}
|
|
95740
|
+
function refineSurface(surf, targetKnotsU, targetKnotsV) {
|
|
95741
|
+
const { grid, knotsU, knotsV, degreeU, degreeV } = surf;
|
|
95742
|
+
const rows = grid.map((row) => refineKnotsToTarget(row, knotsV, degreeV, targetKnotsV).cps);
|
|
95743
|
+
const ncpV = rows[0].length;
|
|
95744
|
+
const cols = [];
|
|
95745
|
+
for (let j = 0; j < ncpV; j++) {
|
|
95746
|
+
const colPts = rows.map((r) => r[j]);
|
|
95747
|
+
cols.push(refineKnotsToTarget(colPts, knotsU, degreeU, targetKnotsU).cps);
|
|
95748
|
+
}
|
|
95749
|
+
const cpU = cols[0].length;
|
|
95750
|
+
const out = [];
|
|
95751
|
+
for (let i = 0; i < cpU; i++) {
|
|
95752
|
+
const row = [];
|
|
95753
|
+
for (let j = 0; j < ncpV; j++) row.push(cols[j][i]);
|
|
95754
|
+
out.push(row);
|
|
95755
|
+
}
|
|
95756
|
+
return { grid: out, knotsU: targetKnotsU, knotsV: targetKnotsV, degreeU, degreeV };
|
|
95757
|
+
}
|
|
95758
|
+
function averageParams(polylines, alpha = 0.5) {
|
|
95759
|
+
const count = polylines[0].length;
|
|
95760
|
+
const acc = new Array(count).fill(0);
|
|
95761
|
+
for (const pl of polylines) {
|
|
95762
|
+
const p2 = computeParamsCentripetal(pl, alpha);
|
|
95763
|
+
for (let i = 0; i < count; i++) acc[i] += p2[i];
|
|
95764
|
+
}
|
|
95765
|
+
return acc.map((s) => s / polylines.length);
|
|
95766
|
+
}
|
|
95767
|
+
function gordonGridParams(gridPts) {
|
|
95768
|
+
const rows = gridPts;
|
|
95769
|
+
const cols = gridPts[0].map((_2, l) => gridPts.map((r) => r[l]));
|
|
95770
|
+
return { vParams: averageParams(rows, 0.5), uParams: averageParams(cols, 0.5) };
|
|
95771
|
+
}
|
|
95772
|
+
function buildGordonFromGrid(gridPts, degreeU = 3, degreeV = 3) {
|
|
95773
|
+
const m2 = gridPts.length - 1;
|
|
95774
|
+
const n = gridPts[0].length - 1;
|
|
95775
|
+
if (m2 < 1 || n < 1) throw new Error("A Gordon surface needs at least a 2x2 curve network.");
|
|
95776
|
+
if (m2 < degreeU) degreeU = m2;
|
|
95777
|
+
if (n < degreeV) degreeV = n;
|
|
95778
|
+
const rows = gridPts;
|
|
95779
|
+
const cols = gridPts[0].map((_2, l) => gridPts.map((r) => r[l]));
|
|
95780
|
+
const { uParams, vParams } = gordonGridParams(gridPts);
|
|
95781
|
+
const uCurves = rows.map((rowPts) => globalCurveInterp(rowPts, degreeV, vParams));
|
|
95782
|
+
const vCurves = cols.map((colPts) => globalCurveInterp(colPts, degreeU, uParams));
|
|
95783
|
+
const uFam = unifyCurves(uCurves);
|
|
95784
|
+
const vFam = unifyCurves(vCurves);
|
|
95785
|
+
const Lu = skin(uFam.curves, uParams, degreeU);
|
|
95786
|
+
const LvRaw = skin(vFam.curves, vParams, degreeV);
|
|
95787
|
+
const Lv = { grid: transpose(LvRaw.grid), knotsU: vFam.knots, knotsV: LvRaw.spanKnots };
|
|
95788
|
+
const T = tensorInterp(gridPts, uParams, vParams, degreeU, degreeV);
|
|
95789
|
+
const knotsU = mergeKnotVectors([Lu.spanKnots, Lv.knotsU, T.knotsU], degreeU);
|
|
95790
|
+
const knotsV = mergeKnotVectors([uFam.knots, Lv.knotsV, T.knotsV], degreeV);
|
|
95791
|
+
const LuS = refineSurface({ grid: Lu.grid, knotsU: Lu.spanKnots, knotsV: uFam.knots, degreeU, degreeV }, knotsU, knotsV);
|
|
95792
|
+
const LvS = refineSurface({ grid: Lv.grid, knotsU: Lv.knotsU, knotsV: Lv.knotsV, degreeU, degreeV }, knotsU, knotsV);
|
|
95793
|
+
const TS = refineSurface({ grid: T.grid, knotsU: T.knotsU, knotsV: T.knotsV, degreeU, degreeV }, knotsU, knotsV);
|
|
95794
|
+
const nuc = LuS.grid.length;
|
|
95795
|
+
const nvc = LuS.grid[0].length;
|
|
95796
|
+
if (LvS.grid.length !== nuc || TS.grid.length !== nuc || LvS.grid[0].length !== nvc || TS.grid[0].length !== nvc) {
|
|
95797
|
+
throw new Error(
|
|
95798
|
+
`Gordon blend grid mismatch: Lu ${nuc}x${nvc}, Lv ${LvS.grid.length}x${LvS.grid[0].length}, T ${TS.grid.length}x${TS.grid[0].length}.`
|
|
95799
|
+
);
|
|
95800
|
+
}
|
|
95801
|
+
const grid = [];
|
|
95802
|
+
for (let i = 0; i < nuc; i++) {
|
|
95803
|
+
const row = [];
|
|
95804
|
+
for (let j = 0; j < nvc; j++) {
|
|
95805
|
+
const a2 = LuS.grid[i][j];
|
|
95806
|
+
const b = LvS.grid[i][j];
|
|
95807
|
+
const c2 = TS.grid[i][j];
|
|
95808
|
+
row.push([a2[0] + b[0] - c2[0], a2[1] + b[1] - c2[1], a2[2] + b[2] - c2[2]]);
|
|
95809
|
+
}
|
|
95810
|
+
grid.push(row);
|
|
95811
|
+
}
|
|
95812
|
+
return { grid, knotsU, knotsV, degreeU, degreeV };
|
|
95813
|
+
}
|
|
95814
|
+
function evalSurface(surf, u2, v) {
|
|
95815
|
+
const { grid, knotsU, knotsV, degreeU, degreeV } = surf;
|
|
95816
|
+
const nu = grid.length;
|
|
95817
|
+
const nv = grid[0].length;
|
|
95818
|
+
const su = findSpan(nu, degreeU, u2, knotsU);
|
|
95819
|
+
const sv = findSpan(nv, degreeV, v, knotsV);
|
|
95820
|
+
const Nu = basisFuns(su, u2, degreeU, knotsU);
|
|
95821
|
+
const Nv = basisFuns(sv, v, degreeV, knotsV);
|
|
95822
|
+
const out = [0, 0, 0];
|
|
95823
|
+
for (let i = 0; i <= degreeU; i++) {
|
|
95824
|
+
const ui = su - degreeU + i;
|
|
95825
|
+
for (let j = 0; j <= degreeV; j++) {
|
|
95826
|
+
const vj = sv - degreeV + j;
|
|
95827
|
+
const w2 = Nu[i] * Nv[j];
|
|
95828
|
+
const p2 = grid[ui][vj];
|
|
95829
|
+
out[0] += w2 * p2[0];
|
|
95830
|
+
out[1] += w2 * p2[1];
|
|
95831
|
+
out[2] += w2 * p2[2];
|
|
95832
|
+
}
|
|
95833
|
+
}
|
|
95834
|
+
return out;
|
|
95835
|
+
}
|
|
95836
|
+
function evalSurfaceJet(surf, u2, v) {
|
|
95837
|
+
const { grid, knotsU, knotsV, degreeU, degreeV } = surf;
|
|
95838
|
+
const nu = grid.length;
|
|
95839
|
+
const nv = grid[0].length;
|
|
95840
|
+
const su = findSpan(nu, degreeU, u2, knotsU);
|
|
95841
|
+
const sv = findSpan(nv, degreeV, v, knotsV);
|
|
95842
|
+
const dU = basisFunsDeriv(su, u2, degreeU, knotsU, 2);
|
|
95843
|
+
const dV = basisFunsDeriv(sv, v, degreeV, knotsV, 2);
|
|
95844
|
+
const S = [0, 0, 0];
|
|
95845
|
+
const Su = [0, 0, 0];
|
|
95846
|
+
const Sv = [0, 0, 0];
|
|
95847
|
+
const Suu = [0, 0, 0];
|
|
95848
|
+
const Suv = [0, 0, 0];
|
|
95849
|
+
const Svv = [0, 0, 0];
|
|
95850
|
+
for (let i = 0; i <= degreeU; i++) {
|
|
95851
|
+
const ui = su - degreeU + i;
|
|
95852
|
+
for (let j = 0; j <= degreeV; j++) {
|
|
95853
|
+
const vj = sv - degreeV + j;
|
|
95854
|
+
const p2 = grid[ui][vj];
|
|
95855
|
+
const w00 = dU[0][i] * dV[0][j];
|
|
95856
|
+
const w10 = dU[1][i] * dV[0][j];
|
|
95857
|
+
const w01 = dU[0][i] * dV[1][j];
|
|
95858
|
+
const w20 = dU[2][i] * dV[0][j];
|
|
95859
|
+
const w11 = dU[1][i] * dV[1][j];
|
|
95860
|
+
const w02 = dU[0][i] * dV[2][j];
|
|
95861
|
+
for (let c2 = 0; c2 < 3; c2++) {
|
|
95862
|
+
S[c2] += w00 * p2[c2];
|
|
95863
|
+
Su[c2] += w10 * p2[c2];
|
|
95864
|
+
Sv[c2] += w01 * p2[c2];
|
|
95865
|
+
Suu[c2] += w20 * p2[c2];
|
|
95866
|
+
Suv[c2] += w11 * p2[c2];
|
|
95867
|
+
Svv[c2] += w02 * p2[c2];
|
|
95868
|
+
}
|
|
95869
|
+
}
|
|
95870
|
+
}
|
|
95871
|
+
let nx = Su[1] * Sv[2] - Su[2] * Sv[1];
|
|
95872
|
+
let ny = Su[2] * Sv[0] - Su[0] * Sv[2];
|
|
95873
|
+
let nz = Su[0] * Sv[1] - Su[1] * Sv[0];
|
|
95874
|
+
const len2 = Math.hypot(nx, ny, nz) || 1;
|
|
95875
|
+
nx /= len2;
|
|
95876
|
+
ny /= len2;
|
|
95877
|
+
nz /= len2;
|
|
95878
|
+
return { S, Su, Sv, Suu, Suv, Svv, normal: [nx, ny, nz] };
|
|
95879
|
+
}
|
|
95880
|
+
function surfaceCurvature(jet) {
|
|
95881
|
+
const { Su, Sv, Suu, Suv, Svv, normal: normal2 } = jet;
|
|
95882
|
+
const E = Su[0] * Su[0] + Su[1] * Su[1] + Su[2] * Su[2];
|
|
95883
|
+
const F = Su[0] * Sv[0] + Su[1] * Sv[1] + Su[2] * Sv[2];
|
|
95884
|
+
const G = Sv[0] * Sv[0] + Sv[1] * Sv[1] + Sv[2] * Sv[2];
|
|
95885
|
+
const e = Suu[0] * normal2[0] + Suu[1] * normal2[1] + Suu[2] * normal2[2];
|
|
95886
|
+
const f3 = Suv[0] * normal2[0] + Suv[1] * normal2[1] + Suv[2] * normal2[2];
|
|
95887
|
+
const g2 = Svv[0] * normal2[0] + Svv[1] * normal2[1] + Svv[2] * normal2[2];
|
|
95888
|
+
const denom = E * G - F * F;
|
|
95889
|
+
if (Math.abs(denom) < 1e-20) return { k1: 0, k2: 0, K: 0, H: 0 };
|
|
95890
|
+
const K = (e * g2 - f3 * f3) / denom;
|
|
95891
|
+
const H = (e * G - 2 * f3 * F + g2 * E) / (2 * denom);
|
|
95892
|
+
const disc = Math.max(0, H * H - K);
|
|
95893
|
+
const root = Math.sqrt(disc);
|
|
95894
|
+
return { k1: H + root, k2: H - root, K, H };
|
|
95895
|
+
}
|
|
95896
|
+
function isVec3(v) {
|
|
95897
|
+
return Array.isArray(v) && v.length === 3 && typeof v[0] === "number" && typeof v[1] === "number" && typeof v[2] === "number";
|
|
95898
|
+
}
|
|
95899
|
+
function requireFiniteVec(p2, label) {
|
|
95900
|
+
for (let i = 0; i < 3; i++)
|
|
95901
|
+
if (!Number.isFinite(p2[i])) throw new Error(`Surface.Net: ${label} component ${i} must be finite, got ${p2[i]}`);
|
|
95902
|
+
return [p2[0], p2[1], p2[2]];
|
|
95903
|
+
}
|
|
95904
|
+
function toSampler(input, label) {
|
|
95905
|
+
if (input instanceof NurbsCurve3D) {
|
|
95906
|
+
return (t) => input.pointAt(t);
|
|
95907
|
+
}
|
|
95908
|
+
if (Array.isArray(input) && input.length > 0 && isVec3(input[0])) {
|
|
95909
|
+
const pts = input.map((p2, i) => requireFiniteVec(p2, `${label}[${i}]`));
|
|
95910
|
+
if (pts.length < 2) throw new Error(`Surface.Net: ${label} needs at least 2 points.`);
|
|
95911
|
+
if (pts.length === 2) {
|
|
95912
|
+
const [a2, b] = pts;
|
|
95913
|
+
return (t) => [a2[0] + (b[0] - a2[0]) * t, a2[1] + (b[1] - a2[1]) * t, a2[2] + (b[2] - a2[2]) * t];
|
|
95914
|
+
}
|
|
95915
|
+
const curve = globalCurveInterp(pts, Math.min(3, pts.length - 1));
|
|
95916
|
+
const nc = new NurbsCurve3D(curve.cps, { degree: curve.degree, knots: curve.knots });
|
|
95917
|
+
return (t) => nc.pointAt(t);
|
|
95918
|
+
}
|
|
95919
|
+
throw new Error(`Surface.Net: ${label} must be a Curve.Fit/Curve.Nurbs value or an array of [x,y,z] points.`);
|
|
95920
|
+
}
|
|
95921
|
+
function sampleCurve(sampler, count) {
|
|
95922
|
+
const out = [];
|
|
95923
|
+
for (let i = 0; i < count; i++) out.push(sampler(i / (count - 1)));
|
|
95924
|
+
return out;
|
|
95925
|
+
}
|
|
95926
|
+
const DEFAULT_THICKEN_RESOLUTION = 48;
|
|
95927
|
+
class Sheet {
|
|
95928
|
+
constructor(surface) {
|
|
95929
|
+
this.surface = surface;
|
|
95930
|
+
}
|
|
95931
|
+
/** Edge naming follows parameter direction (documented): front=v0, rear=v1, left=u0, right=u1. */
|
|
95932
|
+
get frontEdge() {
|
|
95933
|
+
return { sheet: this, fixed: "v", value: 0 };
|
|
95934
|
+
}
|
|
95935
|
+
get rearEdge() {
|
|
95936
|
+
return { sheet: this, fixed: "v", value: 1 };
|
|
95937
|
+
}
|
|
95938
|
+
get leftEdge() {
|
|
95939
|
+
return { sheet: this, fixed: "u", value: 0 };
|
|
95940
|
+
}
|
|
95941
|
+
get rightEdge() {
|
|
95942
|
+
return { sheet: this, fixed: "u", value: 1 };
|
|
95943
|
+
}
|
|
95944
|
+
pointAt(u2, v) {
|
|
95945
|
+
return evalSurface(this.surface, clamp01(u2), clamp01(v));
|
|
95946
|
+
}
|
|
95947
|
+
normalAt(u2, v) {
|
|
95948
|
+
return evalSurfaceJet(this.surface, clamp01(u2), clamp01(v)).normal;
|
|
95949
|
+
}
|
|
95950
|
+
curvatureAt(u2, v) {
|
|
95951
|
+
return surfaceCurvature(evalSurfaceJet(this.surface, clamp01(u2), clamp01(v)));
|
|
95952
|
+
}
|
|
95953
|
+
/** Largest principal curvature magnitude over a sampling grid (for offset safety). */
|
|
95954
|
+
maxAbsPrincipalCurvature(samples = 9) {
|
|
95955
|
+
let maxK = 0;
|
|
95956
|
+
for (let i = 0; i <= samples; i++) {
|
|
95957
|
+
for (let j = 0; j <= samples; j++) {
|
|
95958
|
+
const c2 = this.curvatureAt(i / samples, j / samples);
|
|
95959
|
+
maxK = Math.max(maxK, Math.abs(c2.k1), Math.abs(c2.k2));
|
|
95960
|
+
}
|
|
95961
|
+
}
|
|
95962
|
+
return maxK;
|
|
95963
|
+
}
|
|
95964
|
+
/**
|
|
95965
|
+
* Offset the sheet along its analytic normals into a watertight solid shell of
|
|
95966
|
+
* the given wall thickness. Throws if the wall would self-intersect on a
|
|
95967
|
+
* concave region (no silent degenerate solid).
|
|
95968
|
+
*/
|
|
95969
|
+
thicken(wall, options = {}) {
|
|
95970
|
+
if (!Number.isFinite(wall) || wall <= 0) throw new Error(`Sheet.thicken: wall must be a positive finite number, got ${wall}`);
|
|
95971
|
+
const maxK = this.maxAbsPrincipalCurvature();
|
|
95972
|
+
if (wall * maxK >= 1) {
|
|
95973
|
+
throw new Error(
|
|
95974
|
+
`Sheet.thicken: wall ${wall} exceeds the minimum radius of curvature (1/${maxK.toFixed(4)} = ${(1 / maxK).toFixed(3)}). The offset surface would self-intersect. Reduce the wall thickness or relax the curvature.`
|
|
95975
|
+
);
|
|
95976
|
+
}
|
|
95977
|
+
const resolution = options.resolution ?? DEFAULT_THICKEN_RESOLUTION;
|
|
95978
|
+
return nurbsSurface(this.surface.grid, {
|
|
95979
|
+
degreeU: this.surface.degreeU,
|
|
95980
|
+
degreeV: this.surface.degreeV,
|
|
95981
|
+
knotsU: this.surface.knotsU,
|
|
95982
|
+
knotsV: this.surface.knotsV,
|
|
95983
|
+
thickness: wall,
|
|
95984
|
+
resolution
|
|
95985
|
+
});
|
|
95986
|
+
}
|
|
95987
|
+
/** Per-edge continuity match against a neighbor (returns a NEW Sheet). */
|
|
95988
|
+
matchEdge(edge) {
|
|
95989
|
+
if (edge.sheet !== this) throw new Error("Sheet.matchEdge: the edge must belong to this sheet.");
|
|
95990
|
+
return new MatchEdgeBuilder(this, edge);
|
|
95991
|
+
}
|
|
95992
|
+
}
|
|
95993
|
+
function clamp01(t) {
|
|
95994
|
+
return t < 0 ? 0 : t > 1 ? 1 : t;
|
|
95995
|
+
}
|
|
95996
|
+
class CurveNetBuilder {
|
|
95997
|
+
constructor() {
|
|
95998
|
+
__publicField(this, "lengthwiseCurves", []);
|
|
95999
|
+
__publicField(this, "crosswiseCurves", []);
|
|
96000
|
+
__publicField(this, "railCurves", []);
|
|
96001
|
+
__publicField(this, "cageGrid", null);
|
|
96002
|
+
__publicField(this, "degU");
|
|
96003
|
+
__publicField(this, "degV");
|
|
96004
|
+
__publicField(this, "built", null);
|
|
96005
|
+
}
|
|
96006
|
+
lengthwise(...curves) {
|
|
96007
|
+
this.lengthwiseCurves = curves;
|
|
96008
|
+
this.built = null;
|
|
96009
|
+
return this;
|
|
96010
|
+
}
|
|
96011
|
+
crosswise(...curves) {
|
|
96012
|
+
this.crosswiseCurves = curves;
|
|
96013
|
+
this.built = null;
|
|
96014
|
+
return this;
|
|
96015
|
+
}
|
|
96016
|
+
alongRails(railA, railB) {
|
|
96017
|
+
this.railCurves = [railA, railB];
|
|
96018
|
+
this.built = null;
|
|
96019
|
+
return this;
|
|
96020
|
+
}
|
|
96021
|
+
sections(...curves) {
|
|
96022
|
+
this.crosswiseCurves = curves;
|
|
96023
|
+
this.built = null;
|
|
96024
|
+
return this;
|
|
96025
|
+
}
|
|
96026
|
+
cage(grid) {
|
|
96027
|
+
if (!Array.isArray(grid) || grid.length < 2 || !Array.isArray(grid[0]) || grid[0].length < 2) {
|
|
96028
|
+
throw new Error("Surface.Net().cage: grid must be at least a 2x2 array of [x,y,z] points.");
|
|
96029
|
+
}
|
|
96030
|
+
const cols = grid[0].length;
|
|
96031
|
+
this.cageGrid = grid.map((row, k2) => {
|
|
96032
|
+
if (row.length !== cols) throw new Error(`Surface.Net().cage: row ${k2} has ${row.length} points, expected ${cols}.`);
|
|
96033
|
+
return row.map((p2, l) => requireFiniteVec(p2, `cage[${k2}][${l}]`));
|
|
96034
|
+
});
|
|
96035
|
+
this.built = null;
|
|
96036
|
+
return this;
|
|
96037
|
+
}
|
|
96038
|
+
degree(u2, v) {
|
|
96039
|
+
if (!Number.isInteger(u2) || u2 < 1) throw new Error(`Surface.Net().degree: degree must be a positive integer, got ${u2}`);
|
|
96040
|
+
this.degU = u2;
|
|
96041
|
+
this.degV = v === void 0 ? u2 : v;
|
|
96042
|
+
if (v !== void 0 && (!Number.isInteger(v) || v < 1))
|
|
96043
|
+
throw new Error(`Surface.Net().degree: degree must be a positive integer, got ${v}`);
|
|
96044
|
+
this.built = null;
|
|
96045
|
+
return this;
|
|
96046
|
+
}
|
|
96047
|
+
/** Build (once) and return the Sheet. */
|
|
96048
|
+
toSheet() {
|
|
96049
|
+
if (this.built) return this.built;
|
|
96050
|
+
const grid = this.buildGrid();
|
|
96051
|
+
const m2 = grid.length - 1;
|
|
96052
|
+
const n = grid[0].length - 1;
|
|
96053
|
+
const degreeU = Math.max(1, Math.min(this.degU ?? 3, m2));
|
|
96054
|
+
const degreeV = Math.max(1, Math.min(this.degV ?? 3, n));
|
|
96055
|
+
const surface = buildGordonFromGrid(grid, degreeU, degreeV);
|
|
96056
|
+
this.built = new Sheet(surface);
|
|
96057
|
+
return this.built;
|
|
96058
|
+
}
|
|
96059
|
+
buildGrid() {
|
|
96060
|
+
if (this.cageGrid) return this.cageGrid;
|
|
96061
|
+
const lengthwise = this.railCurves.length > 0 ? this.railCurves : this.lengthwiseCurves;
|
|
96062
|
+
const crosswise = this.crosswiseCurves;
|
|
96063
|
+
const SAMPLES = 17;
|
|
96064
|
+
if (lengthwise.length >= 2) {
|
|
96065
|
+
const samplers = lengthwise.map((c2, l) => toSampler(c2, `lengthwise[${l}]`));
|
|
96066
|
+
const grid = [];
|
|
96067
|
+
for (let i = 0; i < SAMPLES; i++) {
|
|
96068
|
+
const u2 = i / (SAMPLES - 1);
|
|
96069
|
+
grid.push(samplers.map((s) => s(u2)));
|
|
96070
|
+
}
|
|
96071
|
+
return grid;
|
|
96072
|
+
}
|
|
96073
|
+
if (crosswise.length >= 2) {
|
|
96074
|
+
const samplers = crosswise.map((c2, k2) => toSampler(c2, `crosswise[${k2}]`));
|
|
96075
|
+
const grid = [];
|
|
96076
|
+
for (const s of samplers) grid.push(sampleCurve(s, SAMPLES));
|
|
96077
|
+
return grid;
|
|
96078
|
+
}
|
|
96079
|
+
throw new Error(
|
|
96080
|
+
"Surface.Net: provide a .cage(grid), or a family of at least 2 curves via .lengthwise(...) / .crosswise(...) / .alongRails(a,b).sections(...). Intersecting two independent hand-drawn families is not available yet — supply the denser family or a cage."
|
|
96081
|
+
);
|
|
96082
|
+
}
|
|
96083
|
+
// ── Sheet delegation (build on first access) ──────────────────────────────
|
|
96084
|
+
get frontEdge() {
|
|
96085
|
+
return this.toSheet().frontEdge;
|
|
96086
|
+
}
|
|
96087
|
+
get rearEdge() {
|
|
96088
|
+
return this.toSheet().rearEdge;
|
|
96089
|
+
}
|
|
96090
|
+
get leftEdge() {
|
|
96091
|
+
return this.toSheet().leftEdge;
|
|
96092
|
+
}
|
|
96093
|
+
get rightEdge() {
|
|
96094
|
+
return this.toSheet().rightEdge;
|
|
96095
|
+
}
|
|
96096
|
+
get surface() {
|
|
96097
|
+
return this.toSheet().surface;
|
|
96098
|
+
}
|
|
96099
|
+
pointAt(u2, v) {
|
|
96100
|
+
return this.toSheet().pointAt(u2, v);
|
|
96101
|
+
}
|
|
96102
|
+
normalAt(u2, v) {
|
|
96103
|
+
return this.toSheet().normalAt(u2, v);
|
|
96104
|
+
}
|
|
96105
|
+
curvatureAt(u2, v) {
|
|
96106
|
+
return this.toSheet().curvatureAt(u2, v);
|
|
96107
|
+
}
|
|
96108
|
+
thicken(wall, options) {
|
|
96109
|
+
return this.toSheet().thicken(wall, options);
|
|
96110
|
+
}
|
|
96111
|
+
matchEdge(edge) {
|
|
96112
|
+
return this.toSheet().matchEdge(edge);
|
|
96113
|
+
}
|
|
96114
|
+
}
|
|
96115
|
+
function createCurveNet() {
|
|
96116
|
+
return new CurveNetBuilder();
|
|
96117
|
+
}
|
|
96118
|
+
class MatchEdgeBuilder {
|
|
96119
|
+
constructor(sheet, edge) {
|
|
96120
|
+
this.sheet = sheet;
|
|
96121
|
+
this.edge = edge;
|
|
96122
|
+
}
|
|
96123
|
+
toG0(neighbor) {
|
|
96124
|
+
return applyEdgeMatch(this.sheet, this.edge, neighbor, 0);
|
|
96125
|
+
}
|
|
96126
|
+
toG1(neighbor) {
|
|
96127
|
+
return applyEdgeMatch(this.sheet, this.edge, neighbor, 1);
|
|
96128
|
+
}
|
|
96129
|
+
toG2(neighbor) {
|
|
96130
|
+
return applyEdgeMatch(this.sheet, this.edge, neighbor, 2);
|
|
96131
|
+
}
|
|
96132
|
+
}
|
|
96133
|
+
function boundaryRows(surf, fixed, value, depth) {
|
|
96134
|
+
const rows = [];
|
|
96135
|
+
if (fixed === "u") {
|
|
96136
|
+
const nU = surf.grid.length;
|
|
96137
|
+
for (let d2 = 0; d2 <= depth; d2++) {
|
|
96138
|
+
const i = value === 0 ? d2 : nU - 1 - d2;
|
|
96139
|
+
rows.push(surf.grid[i].map((p2) => [p2[0], p2[1], p2[2]]));
|
|
96140
|
+
}
|
|
96141
|
+
} else {
|
|
96142
|
+
const nV = surf.grid[0].length;
|
|
96143
|
+
for (let d2 = 0; d2 <= depth; d2++) {
|
|
96144
|
+
const j = value === 0 ? d2 : nV - 1 - d2;
|
|
96145
|
+
rows.push(surf.grid.map((p2) => [p2[j][0], p2[j][1], p2[j][2]]));
|
|
96146
|
+
}
|
|
96147
|
+
}
|
|
96148
|
+
return rows;
|
|
96149
|
+
}
|
|
96150
|
+
function setBoundaryRow(surf, fixed, value, depth, row) {
|
|
96151
|
+
if (fixed === "u") {
|
|
96152
|
+
const nU = surf.grid.length;
|
|
96153
|
+
const i = value === 0 ? depth : nU - 1 - depth;
|
|
96154
|
+
for (let l = 0; l < surf.grid[i].length; l++) surf.grid[i][l] = [row[l][0], row[l][1], row[l][2]];
|
|
96155
|
+
} else {
|
|
96156
|
+
const nV = surf.grid[0].length;
|
|
96157
|
+
const j = value === 0 ? depth : nV - 1 - depth;
|
|
96158
|
+
for (let k2 = 0; k2 < surf.grid.length; k2++) surf.grid[k2][j] = [row[k2][0], row[k2][1], row[k2][2]];
|
|
96159
|
+
}
|
|
96160
|
+
}
|
|
96161
|
+
function cloneSurface(surf) {
|
|
96162
|
+
return {
|
|
96163
|
+
grid: surf.grid.map((row) => row.map((p2) => [p2[0], p2[1], p2[2]])),
|
|
96164
|
+
knotsU: [...surf.knotsU],
|
|
96165
|
+
knotsV: [...surf.knotsV],
|
|
96166
|
+
degreeU: surf.degreeU,
|
|
96167
|
+
degreeV: surf.degreeV
|
|
96168
|
+
};
|
|
96169
|
+
}
|
|
96170
|
+
function applyEdgeMatch(sheet, edge, neighbor, order) {
|
|
96171
|
+
const result = cloneSurface(sheet.surface);
|
|
96172
|
+
const my = boundaryRows(result, edge.fixed, edge.value, order);
|
|
96173
|
+
const their = boundaryRows(neighbor.sheet.surface, neighbor.fixed, neighbor.value, order);
|
|
96174
|
+
if (my[0].length !== their[0].length) {
|
|
96175
|
+
throw new Error(
|
|
96176
|
+
`Sheet.matchEdge: edge control-point counts differ (${my[0].length} vs ${their[0].length}). Both sheets must share section sampling along the matched edge for continuity.`
|
|
96177
|
+
);
|
|
96178
|
+
}
|
|
96179
|
+
const len2 = my[0].length;
|
|
96180
|
+
const b0 = their[0].map((p2) => [p2[0], p2[1], p2[2]]);
|
|
96181
|
+
setBoundaryRow(result, edge.fixed, edge.value, 0, b0);
|
|
96182
|
+
if (order === 0) return new Sheet(result);
|
|
96183
|
+
const b1 = [];
|
|
96184
|
+
for (let i = 0; i < len2; i++) {
|
|
96185
|
+
const p0 = their[0][i];
|
|
96186
|
+
const p1 = their[1][i];
|
|
96187
|
+
b1.push([2 * p0[0] - p1[0], 2 * p0[1] - p1[1], 2 * p0[2] - p1[2]]);
|
|
96188
|
+
}
|
|
96189
|
+
setBoundaryRow(result, edge.fixed, edge.value, 1, b1);
|
|
96190
|
+
if (order === 1) return new Sheet(result);
|
|
96191
|
+
const b22 = [];
|
|
96192
|
+
for (let i = 0; i < len2; i++) {
|
|
96193
|
+
const p0 = their[0][i];
|
|
96194
|
+
const p1 = their[1][i];
|
|
96195
|
+
const p2 = their[2][i];
|
|
96196
|
+
b22.push([3 * p0[0] - 3 * p1[0] + p2[0], 3 * p0[1] - 3 * p1[1] + p2[1], 3 * p0[2] - 3 * p1[2] + p2[2]]);
|
|
96197
|
+
}
|
|
96198
|
+
setBoundaryRow(result, edge.fixed, edge.value, 2, b22);
|
|
96199
|
+
return new Sheet(result);
|
|
96200
|
+
}
|
|
96201
|
+
function edgeJet(edge, t) {
|
|
96202
|
+
const surf = edge.sheet.surface;
|
|
96203
|
+
if (edge.fixed === "u") {
|
|
96204
|
+
const j2 = evalSurfaceJet(surf, edge.value, clamp01(t));
|
|
96205
|
+
return { point: j2.S, cross: j2.Su, cross2: j2.Suu };
|
|
96206
|
+
}
|
|
96207
|
+
const j = evalSurfaceJet(surf, clamp01(t), edge.value);
|
|
96208
|
+
return { point: j.S, cross: j.Sv, cross2: j.Svv };
|
|
96209
|
+
}
|
|
96210
|
+
function sub3(a2, b) {
|
|
96211
|
+
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
96212
|
+
}
|
|
96213
|
+
function len3(a2) {
|
|
96214
|
+
return Math.hypot(a2[0], a2[1], a2[2]);
|
|
96215
|
+
}
|
|
96216
|
+
function unit3(a2) {
|
|
96217
|
+
const l = len3(a2) || 1;
|
|
96218
|
+
return [a2[0] / l, a2[1] / l, a2[2] / l];
|
|
96219
|
+
}
|
|
96220
|
+
function edgeMatchReport(edgeA, edgeB, samples = 24) {
|
|
96221
|
+
let maxPositionGap = 0;
|
|
96222
|
+
let maxTangentAngleDeg = 0;
|
|
96223
|
+
let maxCurvatureRelErr = 0;
|
|
96224
|
+
for (let i = 0; i <= samples; i++) {
|
|
96225
|
+
const t = i / samples;
|
|
96226
|
+
const a2 = edgeJet(edgeA, t);
|
|
96227
|
+
const b = edgeJet(edgeB, t);
|
|
96228
|
+
maxPositionGap = Math.max(maxPositionGap, len3(sub3(a2.point, b.point)));
|
|
96229
|
+
const ua = unit3(a2.cross);
|
|
96230
|
+
const ub = unit3(b.cross);
|
|
96231
|
+
const dot2 = Math.max(-1, Math.min(1, ua[0] * ub[0] + ua[1] * ub[1] + ua[2] * ub[2]));
|
|
96232
|
+
const angle = Math.acos(Math.abs(dot2)) * 180 / Math.PI;
|
|
96233
|
+
maxTangentAngleDeg = Math.max(maxTangentAngleDeg, angle);
|
|
96234
|
+
const la = len3(a2.cross) ** 2 || 1;
|
|
96235
|
+
const lb = len3(b.cross) ** 2 || 1;
|
|
96236
|
+
const ka = (a2.cross2[0] * ua[0] + a2.cross2[1] * ua[1] + a2.cross2[2] * ua[2]) / la;
|
|
96237
|
+
const kb = (b.cross2[0] * ub[0] + b.cross2[1] * ub[1] + b.cross2[2] * ub[2]) / lb;
|
|
96238
|
+
maxCurvatureRelErr = Math.max(maxCurvatureRelErr, Math.abs(ka - kb) / (Math.abs(ka) + Math.abs(kb) + 1e-9));
|
|
96239
|
+
}
|
|
96240
|
+
return { maxPositionGap, maxTangentAngleDeg, maxCurvatureRelErr };
|
|
96241
|
+
}
|
|
96242
|
+
function bridgeBetween(edgeA, edgeB) {
|
|
96243
|
+
return new BridgeBuilder(edgeA, edgeB);
|
|
96244
|
+
}
|
|
96245
|
+
class BridgeBuilder {
|
|
96246
|
+
constructor(edgeA, edgeB) {
|
|
96247
|
+
__publicField(this, "bulgeA", 0.5);
|
|
96248
|
+
__publicField(this, "bulgeB", 0.5);
|
|
96249
|
+
this.edgeA = edgeA;
|
|
96250
|
+
this.edgeB = edgeB;
|
|
96251
|
+
}
|
|
96252
|
+
/** Tune the influence of each side (Rhino-style bulge). */
|
|
96253
|
+
bulge(a2, b) {
|
|
96254
|
+
if (!Number.isFinite(a2) || !Number.isFinite(b)) throw new Error("Surfaces.bridge.bulge: both factors must be finite.");
|
|
96255
|
+
this.bulgeA = a2;
|
|
96256
|
+
this.bulgeB = b;
|
|
96257
|
+
return this;
|
|
96258
|
+
}
|
|
96259
|
+
g0() {
|
|
96260
|
+
return this.build(0);
|
|
96261
|
+
}
|
|
96262
|
+
g1() {
|
|
96263
|
+
return this.build(1);
|
|
96264
|
+
}
|
|
96265
|
+
g2() {
|
|
96266
|
+
return this.build(2);
|
|
96267
|
+
}
|
|
96268
|
+
build(order) {
|
|
96269
|
+
const SAMPLES = 21;
|
|
96270
|
+
const CROSS = 11;
|
|
96271
|
+
const cage = [];
|
|
96272
|
+
for (let i = 0; i < SAMPLES; i++) {
|
|
96273
|
+
const t = i / (SAMPLES - 1);
|
|
96274
|
+
const a2 = edgeJet(this.edgeA, t);
|
|
96275
|
+
const b = edgeJet(this.edgeB, t);
|
|
96276
|
+
const chord = len3(sub3(b.point, a2.point));
|
|
96277
|
+
const toward = unit3(sub3(b.point, a2.point));
|
|
96278
|
+
const tA = orientToward(unit3(a2.cross), toward);
|
|
96279
|
+
const tB = orientToward(unit3(b.cross), [-toward[0], -toward[1], -toward[2]]);
|
|
96280
|
+
const poles = bridgePoles(a2.point, b.point, tA, tB, chord, this.bulgeA, this.bulgeB, order);
|
|
96281
|
+
const row = [];
|
|
96282
|
+
for (let c2 = 0; c2 < CROSS; c2++) row.push(deCasteljau(poles, c2 / (CROSS - 1)));
|
|
96283
|
+
cage.push(row);
|
|
96284
|
+
}
|
|
96285
|
+
return new CurveNetBuilder().cage(cage).degree(Math.min(3, SAMPLES - 1), Math.min(2 * order + 1 || 1, CROSS - 1)).toSheet();
|
|
96286
|
+
}
|
|
96287
|
+
}
|
|
96288
|
+
function orientToward(v, toward) {
|
|
96289
|
+
const dot2 = v[0] * toward[0] + v[1] * toward[1] + v[2] * toward[2];
|
|
96290
|
+
return dot2 < 0 ? [-v[0], -v[1], -v[2]] : v;
|
|
96291
|
+
}
|
|
96292
|
+
function bridgePoles(a2, b, tA, tB, chord, bulgeA, bulgeB, order) {
|
|
96293
|
+
if (order === 0) return [a2, b];
|
|
96294
|
+
const dA = chord * bulgeA / (order === 1 ? 3 : 5);
|
|
96295
|
+
const dB = chord * bulgeB / (order === 1 ? 3 : 5);
|
|
96296
|
+
const a1 = [a2[0] + tA[0] * dA, a2[1] + tA[1] * dA, a2[2] + tA[2] * dA];
|
|
96297
|
+
const b1 = [b[0] + tB[0] * dB, b[1] + tB[1] * dB, b[2] + tB[2] * dB];
|
|
96298
|
+
if (order === 1) return [a2, a1, b1, b];
|
|
96299
|
+
const a22 = [a1[0] + tA[0] * dA, a1[1] + tA[1] * dA, a1[2] + tA[2] * dA];
|
|
96300
|
+
const b22 = [b1[0] + tB[0] * dB, b1[1] + tB[1] * dB, b1[2] + tB[2] * dB];
|
|
96301
|
+
return [a2, a1, a22, b22, b1, b];
|
|
96302
|
+
}
|
|
96303
|
+
function deCasteljau(poles, s) {
|
|
96304
|
+
let pts = poles.map((p2) => [p2[0], p2[1], p2[2]]);
|
|
96305
|
+
while (pts.length > 1) {
|
|
96306
|
+
const next = [];
|
|
96307
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
96308
|
+
next.push([
|
|
96309
|
+
pts[i][0] + (pts[i + 1][0] - pts[i][0]) * s,
|
|
96310
|
+
pts[i][1] + (pts[i + 1][1] - pts[i][1]) * s,
|
|
96311
|
+
pts[i][2] + (pts[i + 1][2] - pts[i][2]) * s
|
|
96312
|
+
]);
|
|
96313
|
+
}
|
|
96314
|
+
pts = next;
|
|
96315
|
+
}
|
|
96316
|
+
return pts[0];
|
|
96317
|
+
}
|
|
95650
96318
|
const CORNER_Y_ALPHA_ISSUE_URL = "https://github.com/KoStard/forgecad-private/issues/162";
|
|
95651
96319
|
function isVec3Array(value) {
|
|
95652
96320
|
return Array.isArray(value) && (value.length === 0 || Array.isArray(value[0]));
|
|
@@ -97038,7 +97706,16 @@ const Surface = {
|
|
|
97038
97706
|
"Surface.MatchEdge",
|
|
97039
97707
|
"Surface.Match()",
|
|
97040
97708
|
(shape, options) => Surface.Match(shape, options)
|
|
97041
|
-
)
|
|
97709
|
+
),
|
|
97710
|
+
/**
|
|
97711
|
+
* Begin a curve-network (Gordon) surface — the class-A keystone. Chain
|
|
97712
|
+
* `.lengthwise(...)/.crosswise(...)` (or `.alongRails(a,b).sections(...)`, or
|
|
97713
|
+
* `.cage(grid)`), then `.thicken(wall)` to get a solid Shape. Returns a fluent
|
|
97714
|
+
* `Sheet` builder with analytic point/normal/curvature queries and named edges.
|
|
97715
|
+
*/
|
|
97716
|
+
Net() {
|
|
97717
|
+
return createCurveNet();
|
|
97718
|
+
}
|
|
97042
97719
|
};
|
|
97043
97720
|
const Blend = {
|
|
97044
97721
|
Edge(options) {
|
|
@@ -97058,6 +97735,14 @@ const Blend = {
|
|
|
97058
97735
|
Surface(options) {
|
|
97059
97736
|
return Surface.Fill(options);
|
|
97060
97737
|
},
|
|
97738
|
+
/**
|
|
97739
|
+
* Build a transition strip between two `Surface.Net` sheet edges. Chain
|
|
97740
|
+
* `.bulge(a, b)` then `.g0()/.g1()/.g2()` for the continuity order. Returns a
|
|
97741
|
+
* `Sheet`; verify the seam with `Analysis.EdgeMatch`.
|
|
97742
|
+
*/
|
|
97743
|
+
Bridge(edgeA, edgeB) {
|
|
97744
|
+
return bridgeBetween(edgeA, edgeB);
|
|
97745
|
+
},
|
|
97061
97746
|
/**
|
|
97062
97747
|
* @alpha
|
|
97063
97748
|
* Current implementation uses continuity-controlled edge fillets on solid edges.
|
|
@@ -97092,6 +97777,14 @@ const Analysis = {
|
|
|
97092
97777
|
SurfaceContinuity(shape, options = {}) {
|
|
97093
97778
|
return evaluateEdgeContinuityReport(shape, options, "Analysis.SurfaceContinuity()");
|
|
97094
97779
|
},
|
|
97780
|
+
/**
|
|
97781
|
+
* Measure G0/G1/G2 agreement between two `Surface.Net` sheet edges: worst
|
|
97782
|
+
* position gap, cross-boundary tangent angle (0 = G1), and normal-curvature
|
|
97783
|
+
* mismatch (0 = G2). The reflection/fairness check for matched panel seams.
|
|
97784
|
+
*/
|
|
97785
|
+
EdgeMatch(edgeA, edgeB, options = {}) {
|
|
97786
|
+
return edgeMatchReport(edgeA, edgeB, options.samples);
|
|
97787
|
+
},
|
|
97095
97788
|
CurvatureComb(input, options = {}) {
|
|
97096
97789
|
if (input instanceof NurbsCurve3D) {
|
|
97097
97790
|
const count = Math.max(8, options.samples ?? 32);
|
|
@@ -100544,22 +101237,10 @@ function roundNum(n, digits = 4) {
|
|
|
100544
101237
|
return Number.isFinite(n) ? n.toFixed(digits).replace(/\.?0+$/, "") : String(n);
|
|
100545
101238
|
}
|
|
100546
101239
|
const COLLISION_OVERLAP_VOLUME_TOLERANCE = 1e-6;
|
|
100547
|
-
function meshDerivedManifoldBackend(shape) {
|
|
100548
|
-
const mesh = getShapeRuntimeBackend(shape).getMesh();
|
|
100549
|
-
return reconstructBackendFromMesh({
|
|
100550
|
-
numProp: mesh.numProp,
|
|
100551
|
-
triVerts: mesh.triVerts,
|
|
100552
|
-
vertProperties: mesh.vertProperties,
|
|
100553
|
-
mergeFromVert: mesh.mergeFromVert ?? new Uint32Array(),
|
|
100554
|
-
mergeToVert: mesh.mergeToVert ?? new Uint32Array()
|
|
100555
|
-
});
|
|
100556
|
-
}
|
|
100557
101240
|
function backendForMinGap(shape) {
|
|
100558
101241
|
const backend = getShapeRuntimeBackend(shape);
|
|
100559
101242
|
if (isManifoldCapableBackend(backend)) return { kind: "manifold", backend, method: "exact", dispose: false };
|
|
100560
|
-
|
|
100561
|
-
return { kind: "mesh", backend, method: "mesh-derived", dispose: false };
|
|
100562
|
-
return { kind: "manifold", backend: meshDerivedManifoldBackend(shape), method: "mesh-derived", dispose: true };
|
|
101243
|
+
return { kind: "mesh", backend, method: "mesh-derived", dispose: false };
|
|
100563
101244
|
}
|
|
100564
101245
|
function meshHasPointInsideSdf(source, target) {
|
|
100565
101246
|
const mesh = source.getMesh();
|
|
@@ -101732,7 +102413,6 @@ function resetExecutionSession(logs) {
|
|
|
101732
102413
|
resetHighlights();
|
|
101733
102414
|
resetBom();
|
|
101734
102415
|
resetSheetStock();
|
|
101735
|
-
resetRobotExport();
|
|
101736
102416
|
resetCutPlanes();
|
|
101737
102417
|
resetRenderLabels();
|
|
101738
102418
|
resetCameraTrajectory();
|
|
@@ -101815,7 +102495,6 @@ function collectSuccessfulExecutionSnapshot(args) {
|
|
|
101815
102495
|
jointsView: getCollectedJointsView(),
|
|
101816
102496
|
viewConfig: getCollectedViewConfig(),
|
|
101817
102497
|
sceneConfig: getCollectedScene(),
|
|
101818
|
-
robotExport: getCollectedRobotExport(),
|
|
101819
102498
|
quality: args.quality,
|
|
101820
102499
|
logs: args.logs.slice(),
|
|
101821
102500
|
verifications: getCollectedVerifications(),
|
|
@@ -101840,7 +102519,6 @@ function collectFailedExecutionSnapshot(args) {
|
|
|
101840
102519
|
jointsView: getCollectedJointsView(),
|
|
101841
102520
|
viewConfig: getCollectedViewConfig(),
|
|
101842
102521
|
sceneConfig: getCollectedScene(),
|
|
101843
|
-
robotExport: getCollectedRobotExport(),
|
|
101844
102522
|
quality: args.quality,
|
|
101845
102523
|
logs: args.logs.slice(),
|
|
101846
102524
|
verifications: getCollectedVerifications(),
|
|
@@ -314439,7 +315117,6 @@ function executeFile(code, fileName, allFiles, visited, scope = {}, options, exe
|
|
|
314439
315117
|
sketchToDxf,
|
|
314440
315118
|
bom,
|
|
314441
315119
|
sheetStock,
|
|
314442
|
-
robotExport,
|
|
314443
315120
|
Sim,
|
|
314444
315121
|
group,
|
|
314445
315122
|
ShapeGroup,
|
|
@@ -315142,7 +315819,8 @@ function serializeShape(obj, timings) {
|
|
|
315142
315819
|
vertProperties: meshVertProperties,
|
|
315143
315820
|
mergeFromVert: meshMergeFromVert.length > 0 ? meshMergeFromVert : void 0,
|
|
315144
315821
|
mergeToVert: meshMergeToVert.length > 0 ? meshMergeToVert : void 0,
|
|
315145
|
-
vertNormals: vertNormals ?? void 0
|
|
315822
|
+
vertNormals: vertNormals ?? void 0,
|
|
315823
|
+
cornerNormals: rawMesh.cornerNormals && rawMesh.cornerNormals.length === numTriangles * 9 ? rawMesh.cornerNormals : void 0
|
|
315146
315824
|
},
|
|
315147
315825
|
{ skipEdges: occtBackend !== null }
|
|
315148
315826
|
);
|