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
|
@@ -3175,20 +3175,20 @@ function generateClampedKnots(n, degree) {
|
|
|
3175
3175
|
return knots;
|
|
3176
3176
|
}
|
|
3177
3177
|
const KNOT_EPSILON = 1e-10;
|
|
3178
|
-
function sameKnot(a2, b) {
|
|
3178
|
+
function sameKnot$1(a2, b) {
|
|
3179
3179
|
return Math.abs(a2 - b) <= KNOT_EPSILON;
|
|
3180
3180
|
}
|
|
3181
|
-
function knotMultiplicity(knots, u2) {
|
|
3182
|
-
return knots.reduce((count, knot) => count + (sameKnot(knot, u2) ? 1 : 0), 0);
|
|
3181
|
+
function knotMultiplicity$1(knots, u2) {
|
|
3182
|
+
return knots.reduce((count, knot) => count + (sameKnot$1(knot, u2) ? 1 : 0), 0);
|
|
3183
3183
|
}
|
|
3184
3184
|
function firstKnotIndex(knots, u2) {
|
|
3185
|
-
const index2 = knots.findIndex((knot) => sameKnot(knot, u2));
|
|
3185
|
+
const index2 = knots.findIndex((knot) => sameKnot$1(knot, u2));
|
|
3186
3186
|
if (index2 < 0) throw new Error(`NURBS subdomain extraction could not find boundary knot ${u2}.`);
|
|
3187
3187
|
return index2;
|
|
3188
3188
|
}
|
|
3189
3189
|
function lastKnotIndex(knots, u2) {
|
|
3190
3190
|
for (let index2 = knots.length - 1; index2 >= 0; index2 -= 1) {
|
|
3191
|
-
if (sameKnot(knots[index2], u2)) return index2;
|
|
3191
|
+
if (sameKnot$1(knots[index2], u2)) return index2;
|
|
3192
3192
|
}
|
|
3193
3193
|
throw new Error(`NURBS subdomain extraction could not find boundary knot ${u2}.`);
|
|
3194
3194
|
}
|
|
@@ -3201,7 +3201,7 @@ function insertKnotOnceHomogeneous(points, knots, degree, u2) {
|
|
|
3201
3201
|
const lastPoint = count - 1;
|
|
3202
3202
|
const lastKnot = knots.length - 1;
|
|
3203
3203
|
const span = findSpan(count, degree, u2, knots);
|
|
3204
|
-
const multiplicity = knotMultiplicity(knots, u2);
|
|
3204
|
+
const multiplicity = knotMultiplicity$1(knots, u2);
|
|
3205
3205
|
if (multiplicity >= degree) {
|
|
3206
3206
|
throw new Error(`NURBS subdomain extraction cannot insert knot ${u2}: multiplicity ${multiplicity} already reaches degree ${degree}.`);
|
|
3207
3207
|
}
|
|
@@ -3225,7 +3225,7 @@ function insertKnotOnceHomogeneous(points, knots, degree, u2) {
|
|
|
3225
3225
|
function insertBoundaryToMultiplicity(points, knots, degree, u2, targetMultiplicity) {
|
|
3226
3226
|
let refinedPoints = points;
|
|
3227
3227
|
let refinedKnots = knots;
|
|
3228
|
-
while (knotMultiplicity(refinedKnots, u2) < targetMultiplicity) {
|
|
3228
|
+
while (knotMultiplicity$1(refinedKnots, u2) < targetMultiplicity) {
|
|
3229
3229
|
const refined = insertKnotOnceHomogeneous(refinedPoints, refinedKnots, degree, u2);
|
|
3230
3230
|
refinedPoints = refined.points;
|
|
3231
3231
|
refinedKnots = refined.knots;
|
|
@@ -3245,8 +3245,8 @@ function extractNurbsCurveSubdomain(controlPoints, weights, knots, degree, uStar
|
|
|
3245
3245
|
}
|
|
3246
3246
|
const activeStart = knots[degree];
|
|
3247
3247
|
const activeEnd = knots[controlPoints.length];
|
|
3248
|
-
const start = sameKnot(uStart, activeStart) ? activeStart : sameKnot(uStart, activeEnd) ? activeEnd : uStart;
|
|
3249
|
-
const end = sameKnot(uEnd, activeEnd) ? activeEnd : sameKnot(uEnd, activeStart) ? activeStart : uEnd;
|
|
3248
|
+
const start = sameKnot$1(uStart, activeStart) ? activeStart : sameKnot$1(uStart, activeEnd) ? activeEnd : uStart;
|
|
3249
|
+
const end = sameKnot$1(uEnd, activeEnd) ? activeEnd : sameKnot$1(uEnd, activeStart) ? activeStart : uEnd;
|
|
3250
3250
|
if (start < activeStart - KNOT_EPSILON || end > activeEnd + KNOT_EPSILON) {
|
|
3251
3251
|
throw new Error(`NURBS subdomain [${uStart}, ${uEnd}] must stay inside active knot domain [${activeStart}, ${activeEnd}].`);
|
|
3252
3252
|
}
|
|
@@ -9653,7 +9653,7 @@ async function initTruckGeometryWasm() {
|
|
|
9653
9653
|
if (_initPromise$1) return _initPromise$1;
|
|
9654
9654
|
_initPromise$1 = (async () => {
|
|
9655
9655
|
try {
|
|
9656
|
-
const geometryModule = await import("./forgecad_geometry-
|
|
9656
|
+
const geometryModule = await import("./forgecad_geometry-CZ_IfuvA.js");
|
|
9657
9657
|
const isNode = isNodeRuntime();
|
|
9658
9658
|
if (isNode) {
|
|
9659
9659
|
const { readFileSync, existsSync } = await Promise.resolve().then(function() {
|
|
@@ -10451,23 +10451,62 @@ class NurbsSurface {
|
|
|
10451
10451
|
return [wx / wSum, wy / wSum, wz / wSum];
|
|
10452
10452
|
}
|
|
10453
10453
|
/**
|
|
10454
|
-
* Evaluate the surface normal at (u, v)
|
|
10454
|
+
* Evaluate the surface unit normal at (u, v) from analytic first derivatives.
|
|
10455
|
+
*
|
|
10456
|
+
* Uses Algorithm A2.3 basis-function derivatives with the rational quotient
|
|
10457
|
+
* rule, so the normal is exact (no finite-difference epsilon, no error near
|
|
10458
|
+
* the boundary). Constant chain-rule factors from the parameter remap scale Su
|
|
10459
|
+
* and Sv positively and cancel under normalization, so they are omitted.
|
|
10455
10460
|
*/
|
|
10456
10461
|
normalAt(u2, v) {
|
|
10457
|
-
const
|
|
10458
|
-
const
|
|
10459
|
-
const
|
|
10460
|
-
const
|
|
10461
|
-
const pv = this.pointAt(u2, v1), pmv = this.pointAt(u2, v0);
|
|
10462
|
-
const du = [pu[0] - pmu[0], pu[1] - pmu[1], pu[2] - pmu[2]];
|
|
10463
|
-
const dv = [pv[0] - pmv[0], pv[1] - pmv[1], pv[2] - pmv[2]];
|
|
10464
|
-
const nx = du[1] * dv[2] - du[2] * dv[1];
|
|
10465
|
-
const ny = du[2] * dv[0] - du[0] * dv[2];
|
|
10466
|
-
const nz = du[0] * dv[1] - du[1] * dv[0];
|
|
10462
|
+
const { Su, Sv } = this.derivativesAt(u2, v);
|
|
10463
|
+
const nx = Su[1] * Sv[2] - Su[2] * Sv[1];
|
|
10464
|
+
const ny = Su[2] * Sv[0] - Su[0] * Sv[2];
|
|
10465
|
+
const nz = Su[0] * Sv[1] - Su[1] * Sv[0];
|
|
10467
10466
|
const len2 = Math.sqrt(nx * nx + ny * ny + nz * nz);
|
|
10468
10467
|
if (len2 < 1e-12) return [0, 0, 1];
|
|
10469
10468
|
return [nx / len2, ny / len2, nz / len2];
|
|
10470
10469
|
}
|
|
10470
|
+
/** Analytic first partial derivatives S_u, S_v (rational quotient rule). */
|
|
10471
|
+
derivativesAt(u2, v) {
|
|
10472
|
+
const uu = this.remapU(Math.max(0, Math.min(1, u2)));
|
|
10473
|
+
const vv = this.remapV(Math.max(0, Math.min(1, v)));
|
|
10474
|
+
const spanU = findSpan(this.nU, this.degreeU, uu, this.knotsU);
|
|
10475
|
+
const spanV = findSpan(this.nV, this.degreeV, vv, this.knotsV);
|
|
10476
|
+
const dU = basisFunsDeriv(spanU, uu, this.degreeU, this.knotsU, 1);
|
|
10477
|
+
const dV = basisFunsDeriv(spanV, vv, this.degreeV, this.knotsV, 1);
|
|
10478
|
+
const A = [0, 0, 0];
|
|
10479
|
+
const Au = [0, 0, 0];
|
|
10480
|
+
const Av = [0, 0, 0];
|
|
10481
|
+
let w2 = 0;
|
|
10482
|
+
let wu = 0;
|
|
10483
|
+
let wv = 0;
|
|
10484
|
+
for (let i = 0; i <= this.degreeU; i++) {
|
|
10485
|
+
const rowIdx = spanU - this.degreeU + i;
|
|
10486
|
+
for (let j = 0; j <= this.degreeV; j++) {
|
|
10487
|
+
const colIdx = spanV - this.degreeV + j;
|
|
10488
|
+
const weight = this.weightsGrid[rowIdx][colIdx];
|
|
10489
|
+
const pt = this.controlGrid[rowIdx][colIdx];
|
|
10490
|
+
const n00 = dU[0][i] * dV[0][j] * weight;
|
|
10491
|
+
const n10 = dU[1][i] * dV[0][j] * weight;
|
|
10492
|
+
const n01 = dU[0][i] * dV[1][j] * weight;
|
|
10493
|
+
w2 += n00;
|
|
10494
|
+
wu += n10;
|
|
10495
|
+
wv += n01;
|
|
10496
|
+
for (let c2 = 0; c2 < 3; c2++) {
|
|
10497
|
+
A[c2] += n00 * pt[c2];
|
|
10498
|
+
Au[c2] += n10 * pt[c2];
|
|
10499
|
+
Av[c2] += n01 * pt[c2];
|
|
10500
|
+
}
|
|
10501
|
+
}
|
|
10502
|
+
}
|
|
10503
|
+
if (w2 === 0) return { S: [0, 0, 0], Su: [0, 0, 0], Sv: [0, 0, 0] };
|
|
10504
|
+
const invW = 1 / w2;
|
|
10505
|
+
const S = [A[0] * invW, A[1] * invW, A[2] * invW];
|
|
10506
|
+
const Su = [(Au[0] - wu * S[0]) * invW, (Au[1] - wu * S[1]) * invW, (Au[2] - wu * S[2]) * invW];
|
|
10507
|
+
const Sv = [(Av[0] - wv * S[0]) * invW, (Av[1] - wv * S[1]) * invW, (Av[2] - wv * S[2]) * invW];
|
|
10508
|
+
return { S, Su, Sv };
|
|
10509
|
+
}
|
|
10471
10510
|
/**
|
|
10472
10511
|
* Tessellate the surface into a triangle mesh.
|
|
10473
10512
|
* Returns positions, normals, and triangle indices.
|
|
@@ -11833,7 +11872,7 @@ function maxQuadDeviation(rings, heights, colA, colB) {
|
|
|
11833
11872
|
const b = [rings[i][colB][0], rings[i][colB][1], heights[i]];
|
|
11834
11873
|
const c2 = [rings[i + 1][colB][0], rings[i + 1][colB][1], heights[i + 1]];
|
|
11835
11874
|
const d2 = [rings[i + 1][colA][0], rings[i + 1][colA][1], heights[i + 1]];
|
|
11836
|
-
const n = cross3$7(sub3$
|
|
11875
|
+
const n = cross3$7(sub3$5(b, a2), sub3$5(d2, a2));
|
|
11837
11876
|
const len2 = Math.hypot(n[0], n[1], n[2]);
|
|
11838
11877
|
if (len2 < 1e-12) continue;
|
|
11839
11878
|
const deviation = Math.abs((n[0] * (c2[0] - a2[0]) + n[1] * (c2[1] - a2[1]) + n[2] * (c2[2] - a2[2])) / len2);
|
|
@@ -12109,15 +12148,15 @@ function buildSpanRows(rings, heights) {
|
|
|
12109
12148
|
function stationTangent(stations, t, i, j) {
|
|
12110
12149
|
const R = stations.length;
|
|
12111
12150
|
if (i === 0) {
|
|
12112
|
-
return scale3$1(sub3$
|
|
12151
|
+
return scale3$1(sub3$5(stations[1][j], stations[0][j]), 1 / (t[1] - t[0]));
|
|
12113
12152
|
}
|
|
12114
12153
|
if (i === R - 1) {
|
|
12115
|
-
return scale3$1(sub3$
|
|
12154
|
+
return scale3$1(sub3$5(stations[R - 1][j], stations[R - 2][j]), 1 / (t[R - 1] - t[R - 2]));
|
|
12116
12155
|
}
|
|
12117
12156
|
const hPrev = t[i] - t[i - 1];
|
|
12118
12157
|
const hNext = t[i + 1] - t[i];
|
|
12119
|
-
const dPrev = scale3$1(sub3$
|
|
12120
|
-
const dNext = scale3$1(sub3$
|
|
12158
|
+
const dPrev = scale3$1(sub3$5(stations[i][j], stations[i - 1][j]), 1 / hPrev);
|
|
12159
|
+
const dNext = scale3$1(sub3$5(stations[i + 1][j], stations[i][j]), 1 / hNext);
|
|
12121
12160
|
return scale3$1(add3$1(scale3$1(dPrev, hNext), scale3$1(dNext, hPrev)), 1 / (hPrev + hNext));
|
|
12122
12161
|
}
|
|
12123
12162
|
function hermite(p0, m0, p1, m1, h, u2) {
|
|
@@ -12173,12 +12212,12 @@ function stitchSingleLoopLoft(loops, heights, wasm, options) {
|
|
|
12173
12212
|
const curr = points[j];
|
|
12174
12213
|
const next = points[(j + 1) % N];
|
|
12175
12214
|
if (cornerSet.has(j)) {
|
|
12176
|
-
const nFwd = surfaceNormal(sub3$
|
|
12177
|
-
const nBwd = surfaceNormal(sub3$
|
|
12215
|
+
const nFwd = surfaceNormal(sub3$5(next, curr), tangents[j]);
|
|
12216
|
+
const nBwd = surfaceNormal(sub3$5(curr, prev), tangents[j]);
|
|
12178
12217
|
fwd[j] = pushVert(curr, nFwd);
|
|
12179
12218
|
bwd[j] = pushVert(curr, nBwd);
|
|
12180
12219
|
} else {
|
|
12181
|
-
const idx = pushVert(curr, surfaceNormal(sub3$
|
|
12220
|
+
const idx = pushVert(curr, surfaceNormal(sub3$5(next, prev), tangents[j]));
|
|
12182
12221
|
fwd[j] = idx;
|
|
12183
12222
|
bwd[j] = idx;
|
|
12184
12223
|
}
|
|
@@ -12239,7 +12278,7 @@ function surfaceNormal(chord, span) {
|
|
|
12239
12278
|
}
|
|
12240
12279
|
return [n[0] / len2, n[1] / len2, n[2] / len2];
|
|
12241
12280
|
}
|
|
12242
|
-
function sub3$
|
|
12281
|
+
function sub3$5(a2, b) {
|
|
12243
12282
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
12244
12283
|
}
|
|
12245
12284
|
function add3$1(a2, b) {
|
|
@@ -12265,7 +12304,7 @@ let _wasm = null;
|
|
|
12265
12304
|
async function initManifoldWasm() {
|
|
12266
12305
|
if (_wasm) return _wasm;
|
|
12267
12306
|
performance.mark("manifold:start");
|
|
12268
|
-
const Module = (await import("./manifold-
|
|
12307
|
+
const Module = (await import("./manifold-Crd_F2qx.js")).default;
|
|
12269
12308
|
performance.mark("manifold:imported");
|
|
12270
12309
|
const wasm = await Module();
|
|
12271
12310
|
wasm.setup();
|
|
@@ -12890,6 +12929,20 @@ function fromSlicesSingleSliceHalfExtentForManifold(plan) {
|
|
|
12890
12929
|
}
|
|
12891
12930
|
return Math.max(1, radius + maxOffsetMagnitude + plan.boundsPadding + plan.edgeLength * 3);
|
|
12892
12931
|
}
|
|
12932
|
+
const BOOLEAN_OPERAND_NORMAL_SHARP_ANGLE_DEG = 60;
|
|
12933
|
+
function promoteBooleanOperandNormals(shapes) {
|
|
12934
|
+
let maxExtra = 0;
|
|
12935
|
+
for (const shape of shapes) maxExtra = Math.max(maxExtra, shape.numProp());
|
|
12936
|
+
if (maxExtra < 3) return { operands: shapes, created: [] };
|
|
12937
|
+
const created = [];
|
|
12938
|
+
const operands = shapes.map((shape) => {
|
|
12939
|
+
if (shape.numProp() >= 3) return shape;
|
|
12940
|
+
const promoted = shape.calculateNormals(0, BOOLEAN_OPERAND_NORMAL_SHARP_ANGLE_DEG);
|
|
12941
|
+
created.push(promoted);
|
|
12942
|
+
return promoted;
|
|
12943
|
+
});
|
|
12944
|
+
return { operands, created };
|
|
12945
|
+
}
|
|
12893
12946
|
function lowerShapeBooleanCompilePlan(plan, wasm) {
|
|
12894
12947
|
const shapes = plan.shapes.map((shape) => lowerShapeCompilePlanToManifold(shape, wasm));
|
|
12895
12948
|
if (shapes.length === 0) {
|
|
@@ -12898,16 +12951,18 @@ function lowerShapeBooleanCompilePlan(plan, wasm) {
|
|
|
12898
12951
|
if (shapes.length === 1) {
|
|
12899
12952
|
return shapes[0];
|
|
12900
12953
|
}
|
|
12954
|
+
const { operands, created } = promoteBooleanOperandNormals(shapes);
|
|
12901
12955
|
try {
|
|
12902
12956
|
switch (plan.op) {
|
|
12903
12957
|
case "union":
|
|
12904
|
-
return wasm.Manifold.union(
|
|
12958
|
+
return wasm.Manifold.union(operands);
|
|
12905
12959
|
case "difference":
|
|
12906
|
-
return wasm.Manifold.difference(
|
|
12960
|
+
return wasm.Manifold.difference(operands);
|
|
12907
12961
|
case "intersection":
|
|
12908
|
-
return wasm.Manifold.intersection(
|
|
12962
|
+
return wasm.Manifold.intersection(operands);
|
|
12909
12963
|
}
|
|
12910
12964
|
} finally {
|
|
12965
|
+
disposeWasmObjects(created);
|
|
12911
12966
|
disposeWasmObjects(shapes);
|
|
12912
12967
|
}
|
|
12913
12968
|
}
|
|
@@ -14257,12 +14312,16 @@ function lowerNurbsSurfaceToManifold(plan, wasm) {
|
|
|
14257
14312
|
const { positions, normals, indices } = surface.tessellate(res, res);
|
|
14258
14313
|
const thickness = plan.thickness;
|
|
14259
14314
|
const numVerts = positions.length;
|
|
14260
|
-
const
|
|
14261
|
-
for (
|
|
14315
|
+
const vertProps = [];
|
|
14316
|
+
for (let i = 0; i < numVerts; i++) {
|
|
14317
|
+
const [x2, y2, z2] = positions[i];
|
|
14318
|
+
const [nx, ny, nz] = normals[i];
|
|
14319
|
+
vertProps.push(x2, y2, z2, nx, ny, nz);
|
|
14320
|
+
}
|
|
14262
14321
|
for (let i = 0; i < numVerts; i++) {
|
|
14263
14322
|
const [x2, y2, z2] = positions[i];
|
|
14264
14323
|
const [nx, ny, nz] = normals[i];
|
|
14265
|
-
|
|
14324
|
+
vertProps.push(x2 - nx * thickness, y2 - ny * thickness, z2 - nz * thickness, -nx, -ny, -nz);
|
|
14266
14325
|
}
|
|
14267
14326
|
const allIndices = [];
|
|
14268
14327
|
for (const idx of indices) allIndices.push(idx);
|
|
@@ -14287,11 +14346,12 @@ function lowerNurbsSurfaceToManifold(plan, wasm) {
|
|
|
14287
14346
|
allIndices.push(c2, c2 + numVerts, d2, d2, c2 + numVerts, d2 + numVerts);
|
|
14288
14347
|
}
|
|
14289
14348
|
const mesh = new wasm.Mesh({
|
|
14290
|
-
numProp:
|
|
14291
|
-
vertProperties: new Float32Array(
|
|
14349
|
+
numProp: 6,
|
|
14350
|
+
vertProperties: new Float32Array(vertProps),
|
|
14292
14351
|
triVerts: new Uint32Array(allIndices)
|
|
14293
14352
|
});
|
|
14294
14353
|
try {
|
|
14354
|
+
mesh.merge();
|
|
14295
14355
|
return new wasm.Manifold(mesh);
|
|
14296
14356
|
} finally {
|
|
14297
14357
|
disposeWasmObject(mesh);
|
|
@@ -30984,14 +31044,14 @@ function cross$6(a2, b) {
|
|
|
30984
31044
|
function isFiniteNumber$1(value) {
|
|
30985
31045
|
return typeof value === "number" && Number.isFinite(value);
|
|
30986
31046
|
}
|
|
30987
|
-
function isVec3$
|
|
31047
|
+
function isVec3$3(value) {
|
|
30988
31048
|
return Array.isArray(value) && value.length === 3 && value.every(isFiniteNumber$1);
|
|
30989
31049
|
}
|
|
30990
31050
|
function isVec2(value) {
|
|
30991
31051
|
return Array.isArray(value) && value.length === 2 && value.every(isFiniteNumber$1);
|
|
30992
31052
|
}
|
|
30993
31053
|
function isRuledRails(value) {
|
|
30994
|
-
return Array.isArray(value) && value.length === 2 && value.every((rail2) => Array.isArray(rail2) && rail2.length === 2 && rail2.every(isVec3$
|
|
31054
|
+
return Array.isArray(value) && value.length === 2 && value.every((rail2) => Array.isArray(rail2) && rail2.length === 2 && rail2.every(isVec3$3));
|
|
30995
31055
|
}
|
|
30996
31056
|
function axisSpan(face, axis) {
|
|
30997
31057
|
if (face.vertices.length === 0) return 0;
|
|
@@ -31008,14 +31068,14 @@ function isRationalWeights(weights) {
|
|
|
31008
31068
|
return Boolean(weights == null ? void 0 : weights.some((row) => row.some((weight) => Math.abs(weight - 1) > 1e-9)));
|
|
31009
31069
|
}
|
|
31010
31070
|
function explicitGeometrySurface(geometry, face) {
|
|
31011
|
-
if ("Plane" in geometry && isVec3$
|
|
31071
|
+
if ("Plane" in geometry && isVec3$3(geometry.Plane.normal)) {
|
|
31012
31072
|
return { kind: "plane", normal: normalizeVec3$3(geometry.Plane.normal) };
|
|
31013
31073
|
}
|
|
31014
31074
|
if ("AnalyticCylinder" in geometry) {
|
|
31015
31075
|
const origin = geometry.AnalyticCylinder.axis_origin;
|
|
31016
31076
|
const axis = geometry.AnalyticCylinder.axis_direction;
|
|
31017
31077
|
const radius = geometry.AnalyticCylinder.radius;
|
|
31018
|
-
if (isVec3$
|
|
31078
|
+
if (isVec3$3(origin) && isVec3$3(axis) && isFiniteNumber$1(radius) && radius > 0) {
|
|
31019
31079
|
const normalizedAxis = normalizeVec3$3(axis);
|
|
31020
31080
|
return { kind: "cylinder", origin, axis: normalizedAxis, radius, height: axisSpan(face, normalizedAxis) };
|
|
31021
31081
|
}
|
|
@@ -31025,7 +31085,7 @@ function explicitGeometrySurface(geometry, face) {
|
|
|
31025
31085
|
const axis = geometry.AnalyticCone.axis_direction;
|
|
31026
31086
|
const radiusBottom = geometry.AnalyticCone.radius_start;
|
|
31027
31087
|
const radiusTop = geometry.AnalyticCone.radius_end;
|
|
31028
|
-
if (isVec3$
|
|
31088
|
+
if (isVec3$3(origin) && isVec3$3(axis) && isFiniteNumber$1(radiusBottom) && isFiniteNumber$1(radiusTop)) {
|
|
31029
31089
|
const normalizedAxis = normalizeVec3$3(axis);
|
|
31030
31090
|
return {
|
|
31031
31091
|
kind: "cone",
|
|
@@ -31040,7 +31100,7 @@ function explicitGeometrySurface(geometry, face) {
|
|
|
31040
31100
|
if ("AnalyticSphere" in geometry) {
|
|
31041
31101
|
const center = geometry.AnalyticSphere.center;
|
|
31042
31102
|
const radius = geometry.AnalyticSphere.radius;
|
|
31043
|
-
if (isVec3$
|
|
31103
|
+
if (isVec3$3(center) && isFiniteNumber$1(radius) && radius > 0) {
|
|
31044
31104
|
return { kind: "sphere", center, radius };
|
|
31045
31105
|
}
|
|
31046
31106
|
}
|
|
@@ -31049,7 +31109,7 @@ function explicitGeometrySurface(geometry, face) {
|
|
|
31049
31109
|
const axis = geometry.AnalyticTorus.axis;
|
|
31050
31110
|
const majorRadius = geometry.AnalyticTorus.major_radius;
|
|
31051
31111
|
const minorRadius = geometry.AnalyticTorus.minor_radius;
|
|
31052
|
-
if (isVec3$
|
|
31112
|
+
if (isVec3$3(center) && isVec3$3(axis) && isFiniteNumber$1(majorRadius) && isFiniteNumber$1(minorRadius) && majorRadius > 0 && minorRadius > 0) {
|
|
31053
31113
|
return { kind: "torus", center, axis: normalizeVec3$3(axis), majorRadius, minorRadius };
|
|
31054
31114
|
}
|
|
31055
31115
|
}
|
|
@@ -31147,7 +31207,7 @@ function explicitEdgeCurve(geometry, faceName) {
|
|
|
31147
31207
|
const center = (_a3 = geometry.CircularArc) == null ? void 0 : _a3.center;
|
|
31148
31208
|
const axis = (_b3 = geometry.CircularArc) == null ? void 0 : _b3.axis;
|
|
31149
31209
|
const radius = (_c2 = geometry.CircularArc) == null ? void 0 : _c2.radius;
|
|
31150
|
-
if (center !== void 0 && axis !== void 0 && radius !== void 0 && isVec3$
|
|
31210
|
+
if (center !== void 0 && axis !== void 0 && radius !== void 0 && isVec3$3(center) && isVec3$3(axis) && isFiniteNumber$1(radius) && radius > 0) {
|
|
31151
31211
|
return {
|
|
31152
31212
|
kind: "circle",
|
|
31153
31213
|
center,
|
|
@@ -31158,7 +31218,7 @@ function explicitEdgeCurve(geometry, faceName) {
|
|
|
31158
31218
|
}
|
|
31159
31219
|
const start = (_d2 = geometry.Line) == null ? void 0 : _d2.start;
|
|
31160
31220
|
const end = (_e2 = geometry.Line) == null ? void 0 : _e2.end;
|
|
31161
|
-
if (isVec3$
|
|
31221
|
+
if (isVec3$3(start) && isVec3$3(end)) return makeLineEdgeCurve(start, end, faceName);
|
|
31162
31222
|
const polyline = geometry.PolylineUv;
|
|
31163
31223
|
if (polyline && Array.isArray(polyline.points)) {
|
|
31164
31224
|
const points = polyline.points.filter(isVec2);
|
|
@@ -31503,7 +31563,7 @@ function topologyPayloadToTopology(payload) {
|
|
|
31503
31563
|
if (explicitEdge.visual === false) continue;
|
|
31504
31564
|
const start = explicitVertices[explicitEdge.vertices[0]];
|
|
31505
31565
|
const end = explicitVertices[explicitEdge.vertices[1]];
|
|
31506
|
-
if (!isVec3$
|
|
31566
|
+
if (!isVec3$3(start) || !isVec3$3(end)) continue;
|
|
31507
31567
|
const key = edgeKey(start, end);
|
|
31508
31568
|
if (seenEdges.has(key)) continue;
|
|
31509
31569
|
seenEdges.set(key, edges.size);
|
|
@@ -31667,6 +31727,7 @@ const _TruckShapeBackend = class _TruckShapeBackend {
|
|
|
31667
31727
|
const payload = JSON.parse(getTruckGeometryWasm().geometry_mesh(this.getLiveHandle("getMesh()")));
|
|
31668
31728
|
const numTri = payload.triangles.length / 3;
|
|
31669
31729
|
const numVert = payload.positions.length / 3;
|
|
31730
|
+
const cornerNormals = payload.normals && payload.normals.length === numTri * 9 ? new Float32Array(payload.normals) : void 0;
|
|
31670
31731
|
this.resource.mesh = {
|
|
31671
31732
|
numProp: 3,
|
|
31672
31733
|
numTri,
|
|
@@ -31680,7 +31741,8 @@ const _TruckShapeBackend = class _TruckShapeBackend {
|
|
|
31680
31741
|
runTransform: new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0]),
|
|
31681
31742
|
faceID: new Int32Array(payload.face_ids),
|
|
31682
31743
|
faceIdNames: payload.face_id_names ?? [],
|
|
31683
|
-
halfedgeTangent: new Float32Array(0)
|
|
31744
|
+
halfedgeTangent: new Float32Array(0),
|
|
31745
|
+
cornerNormals
|
|
31684
31746
|
};
|
|
31685
31747
|
return this.resource.mesh;
|
|
31686
31748
|
}
|
|
@@ -32898,6 +32960,16 @@ function boundsInteriorOverlap(a2, b) {
|
|
|
32898
32960
|
const tolerance = 1e-8;
|
|
32899
32961
|
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;
|
|
32900
32962
|
}
|
|
32963
|
+
function boundsFaceOrInteriorOverlap(a2, b) {
|
|
32964
|
+
const tolerance = 1e-8;
|
|
32965
|
+
const overlaps = [
|
|
32966
|
+
Math.min(a2.max[0], b.max[0]) - Math.max(a2.min[0], b.min[0]),
|
|
32967
|
+
Math.min(a2.max[1], b.max[1]) - Math.max(a2.min[1], b.min[1]),
|
|
32968
|
+
Math.min(a2.max[2], b.max[2]) - Math.max(a2.min[2], b.min[2])
|
|
32969
|
+
];
|
|
32970
|
+
if (overlaps.some((overlap) => overlap < -tolerance)) return false;
|
|
32971
|
+
return overlaps.filter((overlap) => overlap <= tolerance).length <= 1;
|
|
32972
|
+
}
|
|
32901
32973
|
function boundsFromPoints(points) {
|
|
32902
32974
|
const first = points[0];
|
|
32903
32975
|
if (!first) return null;
|
|
@@ -32980,16 +33052,16 @@ function shapePlanBounds(plan) {
|
|
|
32980
33052
|
return null;
|
|
32981
33053
|
}
|
|
32982
33054
|
}
|
|
32983
|
-
function
|
|
33055
|
+
function hasPairwiseFaceOrInteriorBoundsOverlap(shapes) {
|
|
32984
33056
|
const bounds = shapes.map((shape) => shape.boundingBox());
|
|
32985
33057
|
for (let i = 0; i < bounds.length; i++) {
|
|
32986
33058
|
for (let j = i + 1; j < bounds.length; j++) {
|
|
32987
|
-
if (
|
|
33059
|
+
if (boundsFaceOrInteriorOverlap(bounds[i], bounds[j])) return true;
|
|
32988
33060
|
}
|
|
32989
33061
|
}
|
|
32990
33062
|
return false;
|
|
32991
33063
|
}
|
|
32992
|
-
function
|
|
33064
|
+
function faceOrInteriorOverlapComponents(shapes) {
|
|
32993
33065
|
const bounds = shapes.map((shape) => shape.boundingBox());
|
|
32994
33066
|
const visited = /* @__PURE__ */ new Set();
|
|
32995
33067
|
const components = [];
|
|
@@ -33003,7 +33075,7 @@ function interiorOverlapComponents(shapes) {
|
|
|
33003
33075
|
component.push(current);
|
|
33004
33076
|
for (let next = 0; next < shapes.length; next++) {
|
|
33005
33077
|
if (visited.has(next)) continue;
|
|
33006
|
-
if (
|
|
33078
|
+
if (boundsFaceOrInteriorOverlap(bounds[current], bounds[next])) {
|
|
33007
33079
|
visited.add(next);
|
|
33008
33080
|
queue.push(next);
|
|
33009
33081
|
}
|
|
@@ -33048,10 +33120,10 @@ function lowerGenericBooleanPlan(plan) {
|
|
|
33048
33120
|
let returned = null;
|
|
33049
33121
|
try {
|
|
33050
33122
|
if (plan.op === "union") {
|
|
33051
|
-
if (!
|
|
33123
|
+
if (!hasPairwiseFaceOrInteriorBoundsOverlap(shapes)) {
|
|
33052
33124
|
return null;
|
|
33053
33125
|
}
|
|
33054
|
-
const components =
|
|
33126
|
+
const components = faceOrInteriorOverlapComponents(shapes);
|
|
33055
33127
|
if (components.length > 1) {
|
|
33056
33128
|
returned = lowerClusteredUnion(shapes, components);
|
|
33057
33129
|
return returned;
|
|
@@ -33581,9 +33653,13 @@ function shapeHasClosedNativeTopology(shape) {
|
|
|
33581
33653
|
function normalizeTruckShapeForBooleanInput(shape) {
|
|
33582
33654
|
if (shapeHasClosedNativeTopology(shape)) return shape;
|
|
33583
33655
|
if (!meshHasRawBoundaryEdges(shape.getMesh())) return shape;
|
|
33584
|
-
|
|
33585
|
-
|
|
33586
|
-
|
|
33656
|
+
try {
|
|
33657
|
+
const normalized = normalizeFacetedTruckShape(shape);
|
|
33658
|
+
disposeShapeBackend(shape);
|
|
33659
|
+
return normalized;
|
|
33660
|
+
} catch {
|
|
33661
|
+
return shape;
|
|
33662
|
+
}
|
|
33587
33663
|
}
|
|
33588
33664
|
function lowerSdfPlan(plan) {
|
|
33589
33665
|
if (getUnsupportedSdfProgramReason(plan.tree) === void 0) {
|
|
@@ -35467,7 +35543,7 @@ function circleProfilePlan(radius, segments) {
|
|
|
35467
35543
|
}
|
|
35468
35544
|
function annulusProfilePlan(outerRadius, innerRadius, segments) {
|
|
35469
35545
|
const outer = Math.abs(outerRadius);
|
|
35470
|
-
const inner = Math.
|
|
35546
|
+
const inner = Math.max(0, innerRadius);
|
|
35471
35547
|
if (outer <= EXACT_PROFILE_EPS) return emptyProfilePlan();
|
|
35472
35548
|
if (inner <= EXACT_PROFILE_EPS) return circleProfilePlan(outer, segments);
|
|
35473
35549
|
return {
|
|
@@ -40405,11 +40481,11 @@ function requireFiniteVec3$3(v, label) {
|
|
|
40405
40481
|
}
|
|
40406
40482
|
return [x2, y2, z2];
|
|
40407
40483
|
}
|
|
40408
|
-
function len3$
|
|
40484
|
+
function len3$3(v) {
|
|
40409
40485
|
return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
|
40410
40486
|
}
|
|
40411
40487
|
function normalize3$2(v) {
|
|
40412
|
-
const l = len3$
|
|
40488
|
+
const l = len3$3(v);
|
|
40413
40489
|
if (l < 1e-10) throw new Error("Cannot normalize zero-length vector");
|
|
40414
40490
|
return [v[0] / l, v[1] / l, v[2] / l];
|
|
40415
40491
|
}
|
|
@@ -40419,7 +40495,7 @@ function dot3$5(a2, b) {
|
|
|
40419
40495
|
function cross3$3(a2, b) {
|
|
40420
40496
|
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]];
|
|
40421
40497
|
}
|
|
40422
|
-
function sub3$
|
|
40498
|
+
function sub3$4(a2, b) {
|
|
40423
40499
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
40424
40500
|
}
|
|
40425
40501
|
function negate3$1(v) {
|
|
@@ -40442,7 +40518,7 @@ function normalizePortInput(input) {
|
|
|
40442
40518
|
const end = requireFiniteVec3$3(input.end, "port end");
|
|
40443
40519
|
origin = [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2, (start[2] + end[2]) / 2];
|
|
40444
40520
|
const dir = [end[0] - start[0], end[1] - start[1], end[2] - start[2]];
|
|
40445
|
-
const dirLen = len3$
|
|
40521
|
+
const dirLen = len3$3(dir);
|
|
40446
40522
|
if (dirLen < 1e-10) {
|
|
40447
40523
|
throw new Error("Port start and end must not be the same point");
|
|
40448
40524
|
}
|
|
@@ -40453,7 +40529,7 @@ function normalizePortInput(input) {
|
|
|
40453
40529
|
} else if (hasOriginAxis) {
|
|
40454
40530
|
origin = requireFiniteVec3$3(input.origin, "port origin");
|
|
40455
40531
|
const rawAxis = requireFiniteVec3$3(input.axis, "port axis");
|
|
40456
|
-
if (len3$
|
|
40532
|
+
if (len3$3(rawAxis) < 1e-10) {
|
|
40457
40533
|
throw new Error("Port axis must be non-zero");
|
|
40458
40534
|
}
|
|
40459
40535
|
axis = normalize3$2(rawAxis);
|
|
@@ -40469,12 +40545,12 @@ function normalizePortInput(input) {
|
|
|
40469
40545
|
let up;
|
|
40470
40546
|
if (input.up != null) {
|
|
40471
40547
|
const rawUp = requireFiniteVec3$3(input.up, "port up");
|
|
40472
|
-
if (len3$
|
|
40548
|
+
if (len3$3(rawUp) < 1e-10) {
|
|
40473
40549
|
throw new Error("Port up vector must be non-zero");
|
|
40474
40550
|
}
|
|
40475
40551
|
const proj = dot3$5(rawUp, axis);
|
|
40476
40552
|
const ortho = [rawUp[0] - proj * axis[0], rawUp[1] - proj * axis[1], rawUp[2] - proj * axis[2]];
|
|
40477
|
-
if (len3$
|
|
40553
|
+
if (len3$3(ortho) < 1e-10) {
|
|
40478
40554
|
throw new Error("Port up vector must not be parallel to axis");
|
|
40479
40555
|
}
|
|
40480
40556
|
up = normalize3$2(ortho);
|
|
@@ -40538,8 +40614,8 @@ function transformPort(port, matrix) {
|
|
|
40538
40614
|
const newOrigin = tx.point(port.origin);
|
|
40539
40615
|
const rawAxis = tx.vector(port.axis);
|
|
40540
40616
|
const rawUp = tx.vector(port.up);
|
|
40541
|
-
const axisLen = len3$
|
|
40542
|
-
const upLen = len3$
|
|
40617
|
+
const axisLen = len3$3(rawAxis);
|
|
40618
|
+
const upLen = len3$3(rawUp);
|
|
40543
40619
|
const out = {
|
|
40544
40620
|
origin: newOrigin,
|
|
40545
40621
|
axis: axisLen > 1e-10 ? normalize3$2(rawAxis) : port.axis,
|
|
@@ -40591,7 +40667,7 @@ function computeConnectFrame(childBase, childPort, parentPort, _flip, childAlign
|
|
|
40591
40667
|
r10 * cI[0] + r11 * cI[1] + r12 * cI[2],
|
|
40592
40668
|
r20 * cI[0] + r21 * cI[1] + r22 * cI[2]
|
|
40593
40669
|
];
|
|
40594
|
-
const t = sub3$
|
|
40670
|
+
const t = sub3$4(pOrigin, rcI);
|
|
40595
40671
|
const frame = Transform.from([r00, r10, r20, 0, r01, r11, r21, 0, r02, r12, r22, 0, t[0], t[1], t[2], 1]);
|
|
40596
40672
|
const axis = cAxis;
|
|
40597
40673
|
return { frame, axis };
|
|
@@ -40676,18 +40752,18 @@ function validateConnectorMatch(selfName, selfPort, targetName, targetPort, forc
|
|
|
40676
40752
|
}
|
|
40677
40753
|
}
|
|
40678
40754
|
}
|
|
40679
|
-
function len3$
|
|
40755
|
+
function len3$2(v) {
|
|
40680
40756
|
return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
|
40681
40757
|
}
|
|
40682
40758
|
function normalize3$1(v) {
|
|
40683
|
-
const l = len3$
|
|
40759
|
+
const l = len3$2(v);
|
|
40684
40760
|
if (l < 1e-10) throw new Error("Cannot normalize zero-length vector");
|
|
40685
40761
|
return [v[0] / l, v[1] / l, v[2] / l];
|
|
40686
40762
|
}
|
|
40687
40763
|
function cross3$2(a2, b) {
|
|
40688
40764
|
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]];
|
|
40689
40765
|
}
|
|
40690
|
-
function sub3$
|
|
40766
|
+
function sub3$3(a2, b) {
|
|
40691
40767
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
40692
40768
|
}
|
|
40693
40769
|
function negate3(v) {
|
|
@@ -40710,7 +40786,7 @@ function alignmentMatrix(childOrigin, childAxis, childUp, parentOrigin, parentAx
|
|
|
40710
40786
|
r10 * childOrigin[0] + r11 * childOrigin[1] + r12 * childOrigin[2],
|
|
40711
40787
|
r20 * childOrigin[0] + r21 * childOrigin[1] + r22 * childOrigin[2]
|
|
40712
40788
|
];
|
|
40713
|
-
const t = sub3$
|
|
40789
|
+
const t = sub3$3(parentOrigin, rc);
|
|
40714
40790
|
return Transform.from([r00, r10, r20, 0, r01, r11, r21, 0, r02, r12, r22, 0, t[0], t[1], t[2], 1]);
|
|
40715
40791
|
}
|
|
40716
40792
|
function computeSinglePairAlignment(childPort, targetPort) {
|
|
@@ -40749,8 +40825,8 @@ function computeMultiPairAlignment(pairs, childPorts, targetPorts, tolerance = 0
|
|
|
40749
40825
|
[0, 0, 0]
|
|
40750
40826
|
];
|
|
40751
40827
|
for (const p2 of pairs) {
|
|
40752
|
-
const s = sub3$
|
|
40753
|
-
const t2 = sub3$
|
|
40828
|
+
const s = sub3$3(p2.childOrigin, srcCentroid);
|
|
40829
|
+
const t2 = sub3$3(p2.targetOrigin, tgtCentroid);
|
|
40754
40830
|
for (let i = 0; i < 3; i++) {
|
|
40755
40831
|
for (let j = 0; j < 3; j++) {
|
|
40756
40832
|
h[i][j] += s[i] * t2[j];
|
|
@@ -40763,7 +40839,7 @@ function computeMultiPairAlignment(pairs, childPorts, targetPorts, tolerance = 0
|
|
|
40763
40839
|
R[1][0] * srcCentroid[0] + R[1][1] * srcCentroid[1] + R[1][2] * srcCentroid[2],
|
|
40764
40840
|
R[2][0] * srcCentroid[0] + R[2][1] * srcCentroid[1] + R[2][2] * srcCentroid[2]
|
|
40765
40841
|
];
|
|
40766
|
-
const t = sub3$
|
|
40842
|
+
const t = sub3$3(tgtCentroid, rSrc);
|
|
40767
40843
|
const transform = Transform.from([
|
|
40768
40844
|
R[0][0],
|
|
40769
40845
|
R[1][0],
|
|
@@ -40785,8 +40861,8 @@ function computeMultiPairAlignment(pairs, childPorts, targetPorts, tolerance = 0
|
|
|
40785
40861
|
const residuals = [];
|
|
40786
40862
|
for (const p2 of pairs) {
|
|
40787
40863
|
const transformed = transform.point(p2.childOrigin);
|
|
40788
|
-
const diff = sub3$
|
|
40789
|
-
residuals.push(len3$
|
|
40864
|
+
const diff = sub3$3(transformed, p2.targetOrigin);
|
|
40865
|
+
residuals.push(len3$2(diff));
|
|
40790
40866
|
}
|
|
40791
40867
|
const maxResidual = Math.max(...residuals);
|
|
40792
40868
|
if (maxResidual > tolerance) {
|
|
@@ -40986,8 +41062,8 @@ function getConnectorDistance(ports, nameA, nameB) {
|
|
|
40986
41062
|
const b = ports[nameB];
|
|
40987
41063
|
if (!a2) throw new Error(`connectorDistance: unknown connector "${nameA}"`);
|
|
40988
41064
|
if (!b) throw new Error(`connectorDistance: unknown connector "${nameB}"`);
|
|
40989
|
-
const d2 = sub3$
|
|
40990
|
-
return len3$
|
|
41065
|
+
const d2 = sub3$3(a2.origin, b.origin);
|
|
41066
|
+
return len3$2(d2);
|
|
40991
41067
|
}
|
|
40992
41068
|
function getConnectorMeasurements(ports, name) {
|
|
40993
41069
|
const p2 = ports[name];
|
|
@@ -46096,8 +46172,7 @@ function sculptLook(preset = "gallery") {
|
|
|
46096
46172
|
{ type: "directional", position: [90, -110, 150], color: "#ffffff", intensity: 1.4 },
|
|
46097
46173
|
{ type: "directional", position: [-120, 70, 80], color: "#dcecff", intensity: 0.65 },
|
|
46098
46174
|
{ type: "hemisphere", skyColor: "#d9edff", groundColor: "#f0e6d4", intensity: 0.45 }
|
|
46099
|
-
]
|
|
46100
|
-
postProcessing: { toneMappingExposure: 1.12, vignette: { darkness: 0.16, offset: 0.6 } }
|
|
46175
|
+
]
|
|
46101
46176
|
};
|
|
46102
46177
|
case "candy-shop":
|
|
46103
46178
|
return {
|
|
@@ -46108,8 +46183,7 @@ function sculptLook(preset = "gallery") {
|
|
|
46108
46183
|
{ type: "point", position: [70, -60, 90], color: "#ff8ac8", intensity: 2.2, distance: 280, decay: 1.2 },
|
|
46109
46184
|
{ type: "point", position: [-85, 80, 70], color: "#70e1ff", intensity: 1.8, distance: 260, decay: 1.4 },
|
|
46110
46185
|
{ type: "directional", position: [30, -80, 140], color: "#fff6dd", intensity: 0.9 }
|
|
46111
|
-
]
|
|
46112
|
-
postProcessing: { toneMappingExposure: 1.25, bloom: { intensity: 0.35, threshold: 0.78, radius: 0.45 } }
|
|
46186
|
+
]
|
|
46113
46187
|
};
|
|
46114
46188
|
case "midnight":
|
|
46115
46189
|
return {
|
|
@@ -46121,8 +46195,7 @@ function sculptLook(preset = "gallery") {
|
|
|
46121
46195
|
{ type: "point", position: [-90, 70, 50], color: "#ff7ac8", intensity: 1.4, distance: 300, decay: 1.3 },
|
|
46122
46196
|
{ type: "directional", position: [30, -30, 160], color: "#d7e9ff", intensity: 0.65 }
|
|
46123
46197
|
],
|
|
46124
|
-
fog: { color: "#060814", near: 180, far: 520 }
|
|
46125
|
-
postProcessing: { toneMappingExposure: 1.35, bloom: { intensity: 0.65, threshold: 0.65, radius: 0.6 } }
|
|
46198
|
+
fog: { color: "#060814", near: 180, far: 520 }
|
|
46126
46199
|
};
|
|
46127
46200
|
case "workbench":
|
|
46128
46201
|
return {
|
|
@@ -46133,8 +46206,7 @@ function sculptLook(preset = "gallery") {
|
|
|
46133
46206
|
{ type: "directional", position: [80, -100, 130], color: "#fff5df", intensity: 1.25 },
|
|
46134
46207
|
{ type: "hemisphere", skyColor: "#eef6ff", groundColor: "#d8d0c0", intensity: 0.35 }
|
|
46135
46208
|
],
|
|
46136
|
-
ground: { visible: true, color: "#d8d4ca", offset: 1, receiveShadow: true }
|
|
46137
|
-
postProcessing: { toneMappingExposure: 1.05 }
|
|
46209
|
+
ground: { visible: true, color: "#d8d4ca", offset: 1, receiveShadow: true }
|
|
46138
46210
|
};
|
|
46139
46211
|
default:
|
|
46140
46212
|
return {
|
|
@@ -46145,8 +46217,7 @@ function sculptLook(preset = "gallery") {
|
|
|
46145
46217
|
{ type: "directional", position: [110, -130, 150], color: "#ffffff", intensity: 1.5 },
|
|
46146
46218
|
{ type: "directional", position: [-90, 80, 90], color: "#b8d8ff", intensity: 0.55 },
|
|
46147
46219
|
{ type: "hemisphere", skyColor: "#ddefff", groundColor: "#e8e2d8", intensity: 0.45 }
|
|
46148
|
-
]
|
|
46149
|
-
postProcessing: { toneMappingExposure: 1.18, vignette: { darkness: 0.18, offset: 0.55 } }
|
|
46220
|
+
]
|
|
46150
46221
|
};
|
|
46151
46222
|
}
|
|
46152
46223
|
}
|
|
@@ -47319,7 +47390,7 @@ class Shape {
|
|
|
47319
47390
|
*
|
|
47320
47391
|
* Use `.color()` to set the base diffuse color; `.material()` controls how that color behaves
|
|
47321
47392
|
* under light (metalness, roughness, clearcoat) and can add emissive glow independent of
|
|
47322
|
-
* lighting.
|
|
47393
|
+
* lighting.
|
|
47323
47394
|
*
|
|
47324
47395
|
* **Example**
|
|
47325
47396
|
*
|
|
@@ -49693,14 +49764,14 @@ function dot3$3(a2, b) {
|
|
|
49693
49764
|
function cross3$1(a2, b) {
|
|
49694
49765
|
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]];
|
|
49695
49766
|
}
|
|
49696
|
-
function sub3$
|
|
49767
|
+
function sub3$2(a2, b) {
|
|
49697
49768
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
49698
49769
|
}
|
|
49699
|
-
function len3(v) {
|
|
49770
|
+
function len3$1(v) {
|
|
49700
49771
|
return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
|
49701
49772
|
}
|
|
49702
49773
|
function normalize3(v) {
|
|
49703
|
-
const l = len3(v);
|
|
49774
|
+
const l = len3$1(v);
|
|
49704
49775
|
if (l < EPS$8) return [0, 0, 0];
|
|
49705
49776
|
return [v[0] / l, v[1] / l, v[2] / l];
|
|
49706
49777
|
}
|
|
@@ -49712,7 +49783,7 @@ const alignDef = {
|
|
|
49712
49783
|
const faceB = ctx.worldFace(constraint.refB.bodyId, constraint.refB.featureName);
|
|
49713
49784
|
const n1 = faceA.normal;
|
|
49714
49785
|
const n2 = faceB.normal;
|
|
49715
|
-
const delta = sub3$
|
|
49786
|
+
const delta = sub3$2(faceB.center, faceA.center);
|
|
49716
49787
|
const parallel = dot3$3(n1, n2) - 1;
|
|
49717
49788
|
const normalDist = dot3$3(delta, n1);
|
|
49718
49789
|
const cx = n1[1] * n2[2] - n1[2] * n2[1];
|
|
@@ -49755,7 +49826,7 @@ const concentricDef = {
|
|
|
49755
49826
|
const axisA = ctx.worldAxis(constraint.refA.bodyId, constraint.refA.featureName);
|
|
49756
49827
|
const axisB = ctx.worldAxis(constraint.refB.bodyId, constraint.refB.featureName);
|
|
49757
49828
|
const dirCross = cross3$1(axisA.direction, axisB.direction);
|
|
49758
|
-
const delta = sub3$
|
|
49829
|
+
const delta = sub3$2(axisB.origin, axisA.origin);
|
|
49759
49830
|
const offsetCross = cross3$1(delta, axisA.direction);
|
|
49760
49831
|
const pickTwo = (v) => {
|
|
49761
49832
|
const ax = Math.abs(v[0]);
|
|
@@ -49779,9 +49850,9 @@ const faceDistanceDef = {
|
|
|
49779
49850
|
const distance2 = constraint.value ?? 0;
|
|
49780
49851
|
const n1 = faceA.normal;
|
|
49781
49852
|
const n2 = faceB.normal;
|
|
49782
|
-
const delta = sub3$
|
|
49853
|
+
const delta = sub3$2(faceB.center, faceA.center);
|
|
49783
49854
|
const antiParallel = dot3$3(n1, n2) + 1;
|
|
49784
|
-
const crossMag = len3(cross3$1(n1, n2));
|
|
49855
|
+
const crossMag = len3$1(cross3$1(n1, n2));
|
|
49785
49856
|
const signedDist = dot3$3(delta, n1) - distance2;
|
|
49786
49857
|
return [antiParallel, crossMag, signedDist];
|
|
49787
49858
|
}
|
|
@@ -49801,7 +49872,7 @@ const flushDef = {
|
|
|
49801
49872
|
const faceB = ctx.worldFace(constraint.refB.bodyId, constraint.refB.featureName);
|
|
49802
49873
|
const n1 = faceA.normal;
|
|
49803
49874
|
const n2 = faceB.normal;
|
|
49804
|
-
const delta = sub3$
|
|
49875
|
+
const delta = sub3$2(faceB.center, faceA.center);
|
|
49805
49876
|
const antiParallel = dot3$3(n1, n2) + 1;
|
|
49806
49877
|
const normalDist = dot3$3(delta, n1);
|
|
49807
49878
|
const cx = n1[1] * n2[2] - n1[2] * n2[1];
|
|
@@ -49841,7 +49912,7 @@ const pointOnAxisDef = {
|
|
|
49841
49912
|
residual(constraint, ctx) {
|
|
49842
49913
|
const point2 = ctx.worldPoint(constraint.refA.bodyId, constraint.refA.featureName);
|
|
49843
49914
|
const axis = ctx.worldAxis(constraint.refB.bodyId, constraint.refB.featureName);
|
|
49844
|
-
const delta = sub3$
|
|
49915
|
+
const delta = sub3$2(point2, axis.origin);
|
|
49845
49916
|
const c2 = cross3$1(delta, axis.direction);
|
|
49846
49917
|
const ax = Math.abs(c2[0]);
|
|
49847
49918
|
const ay = Math.abs(c2[1]);
|
|
@@ -49857,7 +49928,7 @@ const pointOnFaceDef = {
|
|
|
49857
49928
|
residual(constraint, ctx) {
|
|
49858
49929
|
const point2 = ctx.worldPoint(constraint.refA.bodyId, constraint.refA.featureName);
|
|
49859
49930
|
const face = ctx.worldFace(constraint.refB.bodyId, constraint.refB.featureName);
|
|
49860
|
-
const delta = sub3$
|
|
49931
|
+
const delta = sub3$2(point2, face.center);
|
|
49861
49932
|
return [dot3$3(delta, face.normal)];
|
|
49862
49933
|
}
|
|
49863
49934
|
};
|
|
@@ -50406,7 +50477,7 @@ function runWithForgeValidationPolicy(policy, fn) {
|
|
|
50406
50477
|
}
|
|
50407
50478
|
let _collected$8 = null;
|
|
50408
50479
|
const isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
50409
|
-
const isVec3$
|
|
50480
|
+
const isVec3$2 = (value) => Array.isArray(value) && value.length === 3 && isFiniteNumber(value[0]) && isFiniteNumber(value[1]) && isFiniteNumber(value[2]);
|
|
50410
50481
|
const normalizeAxis = (axis) => {
|
|
50411
50482
|
const len2 = Math.hypot(axis[0], axis[1], axis[2]);
|
|
50412
50483
|
if (len2 <= 1e-8) throw new Error("jointsView joint axis must be non-zero");
|
|
@@ -50441,10 +50512,10 @@ const normalizeJoint = (joint2) => {
|
|
|
50441
50512
|
throw new Error(`jointsView joint "${name}" type must be "revolute" or "prismatic"`);
|
|
50442
50513
|
}
|
|
50443
50514
|
const axisRaw = joint2.axis ?? [0, 0, 1];
|
|
50444
|
-
if (!isVec3$
|
|
50515
|
+
if (!isVec3$2(axisRaw)) throw new Error(`jointsView joint "${name}" axis must be [x, y, z]`);
|
|
50445
50516
|
const axis = normalizeAxis([axisRaw[0], axisRaw[1], axisRaw[2]]);
|
|
50446
50517
|
const pivotRaw = joint2.pivot ?? [0, 0, 0];
|
|
50447
|
-
if (!isVec3$
|
|
50518
|
+
if (!isVec3$2(pivotRaw)) throw new Error(`jointsView joint "${name}" pivot must be [x, y, z]`);
|
|
50448
50519
|
const pivot = [pivotRaw[0], pivotRaw[1], pivotRaw[2]];
|
|
50449
50520
|
if (joint2.min !== void 0 && !isFiniteNumber(joint2.min)) {
|
|
50450
50521
|
throw new Error(`jointsView joint "${name}" min must be a finite number`);
|
|
@@ -51363,7 +51434,7 @@ function deriveExplodeHintsFromMates(constraints, result, bodies, ctx) {
|
|
|
51363
51434
|
const posA = result.transforms.get(c2.refA.bodyId);
|
|
51364
51435
|
const posB = result.transforms.get(c2.refB.bodyId);
|
|
51365
51436
|
if (posA && posB) {
|
|
51366
|
-
const raw = sub3$
|
|
51437
|
+
const raw = sub3$2(bodyA.grounded ? posB.position : posA.position, bodyA.grounded ? posA.position : posB.position);
|
|
51367
51438
|
dir = normalize3(raw);
|
|
51368
51439
|
}
|
|
51369
51440
|
break;
|
|
@@ -51889,8 +51960,8 @@ class Assembly {
|
|
|
51889
51960
|
*
|
|
51890
51961
|
* Use this after adding physical parts and joints. Robot-body profiles require
|
|
51891
51962
|
* `rootPart`; asset profiles can describe one-part or multi-part physical assets.
|
|
51892
|
-
* URDF/SDF exporters and `forgecad check simready` read this
|
|
51893
|
-
*
|
|
51963
|
+
* URDF/SDF/MJCF/USD exporters and `forgecad check simready` read this
|
|
51964
|
+
* contract directly from the returned assembly.
|
|
51894
51965
|
*
|
|
51895
51966
|
* @category Assembly
|
|
51896
51967
|
*/
|
|
@@ -60937,7 +61008,7 @@ function beltDrive(options) {
|
|
|
60937
61008
|
}
|
|
60938
61009
|
const GEAR_META_KEY = Symbol.for("forgecad.library.gearMeta");
|
|
60939
61010
|
const EPSILON$1 = 1e-9;
|
|
60940
|
-
function clamp01$
|
|
61011
|
+
function clamp01$2(value) {
|
|
60941
61012
|
return Math.max(-1, Math.min(1, value));
|
|
60942
61013
|
}
|
|
60943
61014
|
function isFinitePositive(value) {
|
|
@@ -60957,7 +61028,7 @@ function addArcPoints(target, radius, startAngle, endAngle, steps, includeStart
|
|
|
60957
61028
|
}
|
|
60958
61029
|
}
|
|
60959
61030
|
function flankAngleAtRadius(radius, baseRadius, halfThicknessAtPitch, pressureAngleRad) {
|
|
60960
|
-
const alphaAtRadius = Math.acos(clamp01$
|
|
61031
|
+
const alphaAtRadius = Math.acos(clamp01$2(baseRadius / Math.max(radius, baseRadius)));
|
|
60961
61032
|
return halfThicknessAtPitch + involuteFn(pressureAngleRad) - involuteFn(alphaAtRadius);
|
|
60962
61033
|
}
|
|
60963
61034
|
function addRootFilletPoints(target, rootRadius, filletRadius, flankAngle, sign2, fromFlank, steps) {
|
|
@@ -62077,7 +62148,7 @@ function gearPair(options) {
|
|
|
62077
62148
|
message: `Center distance ${centerDistance.toFixed(4)} exceeds addendum reach ${addendumReach.toFixed(4)} (no mesh contact)`
|
|
62078
62149
|
});
|
|
62079
62150
|
}
|
|
62080
|
-
const cosWorking = clamp01$
|
|
62151
|
+
const cosWorking = clamp01$2(baseSum / Math.max(centerDistance, EPSILON$1));
|
|
62081
62152
|
const alphaWorking = Math.acos(cosWorking);
|
|
62082
62153
|
const basePitch = Math.PI * module * Math.cos(alpha);
|
|
62083
62154
|
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);
|
|
@@ -62987,7 +63058,8 @@ class FrozenShape extends Shape {
|
|
|
62987
63058
|
const EDGE_THRESHOLD_DOT = Math.cos(Math.PI / 180);
|
|
62988
63059
|
const SMOOTH_THRESHOLD_DOT = Math.cos(30 * Math.PI / 180);
|
|
62989
63060
|
function computeGeometryArrays(mesh, options = {}) {
|
|
62990
|
-
const { numProp, numTri: triCount, triVerts, vertProperties, vertNormals } = mesh;
|
|
63061
|
+
const { numProp, numTri: triCount, triVerts, vertProperties, vertNormals, cornerNormals } = mesh;
|
|
63062
|
+
const useCornerNormals = !!cornerNormals && cornerNormals.length === triCount * 9;
|
|
62991
63063
|
const positions = new Float32Array(triCount * 9);
|
|
62992
63064
|
const normals = new Float32Array(triCount * 9);
|
|
62993
63065
|
const faceNx = new Float32Array(triCount);
|
|
@@ -63019,7 +63091,9 @@ function computeGeometryArrays(mesh, options = {}) {
|
|
|
63019
63091
|
positions[o + 6] = cx;
|
|
63020
63092
|
positions[o + 7] = cy;
|
|
63021
63093
|
positions[o + 8] = cz;
|
|
63022
|
-
if (
|
|
63094
|
+
if (useCornerNormals) {
|
|
63095
|
+
for (let k2 = 0; k2 < 9; k2++) normals[o + k2] = cornerNormals[o + k2];
|
|
63096
|
+
} else if (vertNormals) {
|
|
63023
63097
|
normals[o] = vertNormals[i0 * 3];
|
|
63024
63098
|
normals[o + 1] = vertNormals[i0 * 3 + 1];
|
|
63025
63099
|
normals[o + 2] = vertNormals[i0 * 3 + 2];
|
|
@@ -63062,7 +63136,7 @@ function computeGeometryArrays(mesh, options = {}) {
|
|
|
63062
63136
|
faceNy[t] = fny;
|
|
63063
63137
|
faceNz[t] = fnz;
|
|
63064
63138
|
}
|
|
63065
|
-
if (!vertNormals && numProp < 6 && triCount > 0) {
|
|
63139
|
+
if (!useCornerNormals && !vertNormals && numProp < 6 && triCount > 0) {
|
|
63066
63140
|
computeAutoSmoothNormals(
|
|
63067
63141
|
triVerts,
|
|
63068
63142
|
vertProperties,
|
|
@@ -63249,7 +63323,8 @@ function shapeToGeometryFallback(shape) {
|
|
|
63249
63323
|
vertProperties: mesh.vertProperties,
|
|
63250
63324
|
mergeFromVert: mesh.mergeFromVert,
|
|
63251
63325
|
mergeToVert: mesh.mergeToVert,
|
|
63252
|
-
vertNormals
|
|
63326
|
+
vertNormals,
|
|
63327
|
+
cornerNormals: mesh.cornerNormals && mesh.cornerNormals.length === mesh.numTri * 9 ? mesh.cornerNormals : void 0
|
|
63253
63328
|
});
|
|
63254
63329
|
const solid = new BufferGeometry();
|
|
63255
63330
|
solid.setAttribute("position", new BufferAttribute(positions, 3));
|
|
@@ -63279,26 +63354,26 @@ function getPendingShapeHighlights() {
|
|
|
63279
63354
|
function resetPendingShapeHighlights() {
|
|
63280
63355
|
pendingShapeHighlights = [];
|
|
63281
63356
|
}
|
|
63282
|
-
function isVec3(v) {
|
|
63357
|
+
function isVec3$1(v) {
|
|
63283
63358
|
return Array.isArray(v) && v.length === 3 && typeof v[0] === "number" && typeof v[1] === "number" && typeof v[2] === "number";
|
|
63284
63359
|
}
|
|
63285
63360
|
function isEdgePair(v) {
|
|
63286
|
-
return Array.isArray(v) && v.length === 2 && isVec3(v[0]) && isVec3(v[1]);
|
|
63361
|
+
return Array.isArray(v) && v.length === 2 && isVec3$1(v[0]) && isVec3$1(v[1]);
|
|
63287
63362
|
}
|
|
63288
63363
|
function isPlaneSpec(v) {
|
|
63289
63364
|
if (typeof v !== "object" || v === null) return false;
|
|
63290
63365
|
const obj = v;
|
|
63291
|
-
return isVec3(obj.normal) && (typeof obj.offset === "number" || isVec3(obj.point));
|
|
63366
|
+
return isVec3$1(obj.normal) && (typeof obj.offset === "number" || isVec3$1(obj.point));
|
|
63292
63367
|
}
|
|
63293
63368
|
function isFaceRef$1(v) {
|
|
63294
63369
|
if (typeof v !== "object" || v === null) return false;
|
|
63295
63370
|
const obj = v;
|
|
63296
|
-
return isVec3(obj.normal) && isVec3(obj.center) && typeof obj.name === "string";
|
|
63371
|
+
return isVec3$1(obj.normal) && isVec3$1(obj.center) && typeof obj.name === "string";
|
|
63297
63372
|
}
|
|
63298
63373
|
function isEdgeRef(v) {
|
|
63299
63374
|
if (typeof v !== "object" || v === null) return false;
|
|
63300
63375
|
const obj = v;
|
|
63301
|
-
return isVec3(obj.start) && isVec3(obj.end) && typeof obj.name === "string";
|
|
63376
|
+
return isVec3$1(obj.start) && isVec3$1(obj.end) && typeof obj.name === "string";
|
|
63302
63377
|
}
|
|
63303
63378
|
function requireFiniteVec3(v, name) {
|
|
63304
63379
|
for (let i = 0; i < 3; i++) {
|
|
@@ -63337,7 +63412,7 @@ function highlight(target, opts) {
|
|
|
63337
63412
|
});
|
|
63338
63413
|
return;
|
|
63339
63414
|
}
|
|
63340
|
-
if (isVec3(target)) {
|
|
63415
|
+
if (isVec3$1(target)) {
|
|
63341
63416
|
requireFiniteVec3(target, "point");
|
|
63342
63417
|
collectedDebugHighlights3D.push({
|
|
63343
63418
|
kind: "point",
|
|
@@ -64381,7 +64456,7 @@ const PRESETS = {
|
|
|
64381
64456
|
}
|
|
64382
64457
|
};
|
|
64383
64458
|
new Set(Object.keys(PRESETS));
|
|
64384
|
-
function clamp01(value) {
|
|
64459
|
+
function clamp01$1(value) {
|
|
64385
64460
|
return Math.max(0, Math.min(1, value));
|
|
64386
64461
|
}
|
|
64387
64462
|
function applyMaterial(shape, preset) {
|
|
@@ -64403,7 +64478,7 @@ const materials = {
|
|
|
64403
64478
|
clearPolycarbonate(options = {}) {
|
|
64404
64479
|
return {
|
|
64405
64480
|
color: options.tint ?? "#bdefff",
|
|
64406
|
-
material: { opacity: clamp01(options.opacity ?? 0.34), roughness: 0.08, metalness: 0, clearcoat: 1, clearcoatRoughness: 0.03 }
|
|
64481
|
+
material: { opacity: clamp01$1(options.opacity ?? 0.34), roughness: 0.08, metalness: 0, clearcoat: 1, clearcoatRoughness: 0.03 }
|
|
64407
64482
|
};
|
|
64408
64483
|
},
|
|
64409
64484
|
/** Brushed steel-like material for trim, soleplates, and hardware. */
|
|
@@ -64880,31 +64955,6 @@ function validateFog(fog, label) {
|
|
|
64880
64955
|
if (fog.density !== void 0) out.density = requireFinite$5(fog.density, `${label}.density`);
|
|
64881
64956
|
return out;
|
|
64882
64957
|
}
|
|
64883
|
-
function validatePostProcessing(pp, label) {
|
|
64884
|
-
const out = {};
|
|
64885
|
-
if (pp.bloom !== void 0) {
|
|
64886
|
-
if (!pp.bloom || typeof pp.bloom !== "object") throw new Error(`${label}.bloom must be an object`);
|
|
64887
|
-
out.bloom = {};
|
|
64888
|
-
if (pp.bloom.intensity !== void 0) out.bloom.intensity = requireFinite$5(pp.bloom.intensity, `${label}.bloom.intensity`);
|
|
64889
|
-
if (pp.bloom.threshold !== void 0) out.bloom.threshold = requireFinite$5(pp.bloom.threshold, `${label}.bloom.threshold`);
|
|
64890
|
-
if (pp.bloom.radius !== void 0) out.bloom.radius = requireFinite$5(pp.bloom.radius, `${label}.bloom.radius`);
|
|
64891
|
-
}
|
|
64892
|
-
if (pp.vignette !== void 0) {
|
|
64893
|
-
if (!pp.vignette || typeof pp.vignette !== "object") throw new Error(`${label}.vignette must be an object`);
|
|
64894
|
-
out.vignette = {};
|
|
64895
|
-
if (pp.vignette.darkness !== void 0) out.vignette.darkness = requireFinite$5(pp.vignette.darkness, `${label}.vignette.darkness`);
|
|
64896
|
-
if (pp.vignette.offset !== void 0) out.vignette.offset = requireFinite$5(pp.vignette.offset, `${label}.vignette.offset`);
|
|
64897
|
-
}
|
|
64898
|
-
if (pp.grain !== void 0) {
|
|
64899
|
-
if (!pp.grain || typeof pp.grain !== "object") throw new Error(`${label}.grain must be an object`);
|
|
64900
|
-
out.grain = {};
|
|
64901
|
-
if (pp.grain.intensity !== void 0) out.grain.intensity = requireFinite$5(pp.grain.intensity, `${label}.grain.intensity`);
|
|
64902
|
-
}
|
|
64903
|
-
if (pp.toneMappingExposure !== void 0) {
|
|
64904
|
-
out.toneMappingExposure = requireFinite$5(pp.toneMappingExposure, `${label}.toneMappingExposure`);
|
|
64905
|
-
}
|
|
64906
|
-
return out;
|
|
64907
|
-
}
|
|
64908
64958
|
function validateGround(ground, label) {
|
|
64909
64959
|
const out = {};
|
|
64910
64960
|
if (ground.visible !== void 0) {
|
|
@@ -64986,7 +65036,6 @@ function scene(options) {
|
|
|
64986
65036
|
lights: null,
|
|
64987
65037
|
environment: null,
|
|
64988
65038
|
fog: null,
|
|
64989
|
-
postProcessing: null,
|
|
64990
65039
|
ground: null,
|
|
64991
65040
|
capture: null
|
|
64992
65041
|
};
|
|
@@ -65026,12 +65075,9 @@ function scene(options) {
|
|
|
65026
65075
|
}
|
|
65027
65076
|
current.fog = validateFog(options.fog, "scene.fog");
|
|
65028
65077
|
}
|
|
65029
|
-
|
|
65030
|
-
|
|
65031
|
-
|
|
65032
|
-
}
|
|
65033
|
-
const validated = validatePostProcessing(options.postProcessing, "scene.postProcessing");
|
|
65034
|
-
current.postProcessing = current.postProcessing ? { ...current.postProcessing, ...validated } : validated;
|
|
65078
|
+
const disabledPostProcessing = options.postProcessing;
|
|
65079
|
+
if (disabledPostProcessing !== void 0) {
|
|
65080
|
+
console.warn("scene.postProcessing is disabled for now while the browser post-processing path is being rebuilt.");
|
|
65035
65081
|
}
|
|
65036
65082
|
if (options.ground !== void 0) {
|
|
65037
65083
|
if (!options.ground || typeof options.ground !== "object") {
|
|
@@ -65191,8 +65237,7 @@ function scenePreset(name) {
|
|
|
65191
65237
|
{ type: "hemisphere", skyColor: "#ffffff", groundColor: "#d5d9de", intensity: 0.75 },
|
|
65192
65238
|
{ type: "directional", position: [90, -120, 180], color: "#ffffff", intensity: 1.8, castShadow: true },
|
|
65193
65239
|
{ type: "directional", position: [-140, 100, 80], color: "#dfe9ff", intensity: 0.55 }
|
|
65194
|
-
]
|
|
65195
|
-
postProcessing: { toneMappingExposure: 1.08 }
|
|
65240
|
+
]
|
|
65196
65241
|
});
|
|
65197
65242
|
return;
|
|
65198
65243
|
}
|
|
@@ -65434,8 +65479,8 @@ function shapeBoundsCenter(shape) {
|
|
|
65434
65479
|
];
|
|
65435
65480
|
}
|
|
65436
65481
|
class ProductSurfaceRef {
|
|
65437
|
-
constructor(
|
|
65438
|
-
this.skin =
|
|
65482
|
+
constructor(skin2, query, name) {
|
|
65483
|
+
this.skin = skin2;
|
|
65439
65484
|
this.query = query;
|
|
65440
65485
|
this.name = name;
|
|
65441
65486
|
}
|
|
@@ -65952,8 +65997,8 @@ class ProductHandleBuilder {
|
|
|
65952
65997
|
}
|
|
65953
65998
|
}
|
|
65954
65999
|
class ProductSurfaceBuilder {
|
|
65955
|
-
constructor(
|
|
65956
|
-
this.skin =
|
|
66000
|
+
constructor(skin2, side) {
|
|
66001
|
+
this.skin = skin2;
|
|
65957
66002
|
this.side = side;
|
|
65958
66003
|
}
|
|
65959
66004
|
/** Create a ref on this skin side. */
|
|
@@ -66021,9 +66066,9 @@ class ProductRibbonBuilder {
|
|
|
66021
66066
|
* ProductSkin.frame(), so the ribbon bends along the selected side as station width/depth changes.
|
|
66022
66067
|
* All query path points must stay on one side; split side transitions into separate ribbons.
|
|
66023
66068
|
*/
|
|
66024
|
-
on(
|
|
66069
|
+
on(skin2, points, options = {}) {
|
|
66025
66070
|
if (points.length < 2) throw new Error("Product.ribbon().on(skin, points) requires at least two path points");
|
|
66026
|
-
this.skinValue =
|
|
66071
|
+
this.skinValue = skin2;
|
|
66027
66072
|
this.queryPath = resolvePathQueries(points);
|
|
66028
66073
|
this.refPath = [];
|
|
66029
66074
|
return this.applyOptions(options);
|
|
@@ -66146,7 +66191,7 @@ class ProductRibbonBuilder {
|
|
|
66146
66191
|
const localT = scaled - segment;
|
|
66147
66192
|
return interpolateQuery(this.queryPath[segment], this.queryPath[segment + 1], localT);
|
|
66148
66193
|
}
|
|
66149
|
-
buildSkinGrid(
|
|
66194
|
+
buildSkinGrid(skin2, path2) {
|
|
66150
66195
|
if (path2.length < 2) throw new Error("Product.ribbon().on(skin, points) must be called before .build()");
|
|
66151
66196
|
const side = normalizedSide(path2[0].side);
|
|
66152
66197
|
if (side === "front" || side === "rear") {
|
|
@@ -66165,7 +66210,7 @@ class ProductRibbonBuilder {
|
|
|
66165
66210
|
for (let i = 0; i < this.samplesValue; i += 1) {
|
|
66166
66211
|
const along = this.samplesValue === 1 ? 0 : i / (this.samplesValue - 1);
|
|
66167
66212
|
const center = this.samplePathQuery(along);
|
|
66168
|
-
const station =
|
|
66213
|
+
const station = skin2.stationAt(center.v ?? 0.5);
|
|
66169
66214
|
const span = sideSpan(side, station.width, station.depth);
|
|
66170
66215
|
for (let j = 0; j < this.widthSamplesValue; j += 1) {
|
|
66171
66216
|
const across = this.widthSamplesValue === 1 ? 0 : j / (this.widthSamplesValue - 1) - 0.5;
|
|
@@ -66182,13 +66227,13 @@ class ProductRibbonBuilder {
|
|
|
66182
66227
|
u: u2,
|
|
66183
66228
|
offset: (center.offset ?? 0) + this.offsetValue + this.thicknessValue
|
|
66184
66229
|
};
|
|
66185
|
-
rows[j].push(
|
|
66230
|
+
rows[j].push(skin2.frame(query).point);
|
|
66186
66231
|
}
|
|
66187
66232
|
}
|
|
66188
66233
|
return {
|
|
66189
66234
|
grid: rows,
|
|
66190
66235
|
diagnostics: this.makeDiagnostics({
|
|
66191
|
-
skin:
|
|
66236
|
+
skin: skin2.name,
|
|
66192
66237
|
side,
|
|
66193
66238
|
pathPointCount: path2.length,
|
|
66194
66239
|
clampedUCount,
|
|
@@ -66345,16 +66390,16 @@ const Product = {
|
|
|
66345
66390
|
return scaleProfileTo(sketch, width, depth);
|
|
66346
66391
|
},
|
|
66347
66392
|
/** Create an ad-hoc ProductSurfaceRef from a skin and side/u/v query. */
|
|
66348
|
-
ref(
|
|
66349
|
-
return new ProductSurfaceRef(
|
|
66393
|
+
ref(skin2, query) {
|
|
66394
|
+
return new ProductSurfaceRef(skin2, query);
|
|
66350
66395
|
},
|
|
66351
66396
|
/**
|
|
66352
66397
|
* Create a fluent surface helper for refs and conformal features on one side of a skin.
|
|
66353
66398
|
*
|
|
66354
66399
|
* Equivalent to skin.surface(side), useful when writing in Product.* namespace style.
|
|
66355
66400
|
*/
|
|
66356
|
-
surface(
|
|
66357
|
-
return
|
|
66401
|
+
surface(skin2, side) {
|
|
66402
|
+
return skin2.surface(side);
|
|
66358
66403
|
},
|
|
66359
66404
|
/** Start a panel feature builder. */
|
|
66360
66405
|
panel(name) {
|
|
@@ -66823,9 +66868,9 @@ function coordinateOnSide(coordinate, side, label) {
|
|
|
66823
66868
|
return { ...coordinate, kind: "productSkin", side };
|
|
66824
66869
|
}
|
|
66825
66870
|
class ProductSkinCarrier {
|
|
66826
|
-
constructor(
|
|
66871
|
+
constructor(skin2, name = skin2.name, sideValue2, offsetValue = 0) {
|
|
66827
66872
|
__publicField(this, "kind", "productSkin");
|
|
66828
|
-
this.skin =
|
|
66873
|
+
this.skin = skin2;
|
|
66829
66874
|
this.name = name;
|
|
66830
66875
|
this.sideValue = sideValue2;
|
|
66831
66876
|
this.offsetValue = offsetValue;
|
|
@@ -69759,8 +69804,8 @@ const Carrier = {
|
|
|
69759
69804
|
return new PlaneCarrier(name);
|
|
69760
69805
|
},
|
|
69761
69806
|
/** Adapt an existing ProductSkin into the general surface-member carrier protocol. */
|
|
69762
|
-
productSkin(
|
|
69763
|
-
return new ProductSkinCarrier(
|
|
69807
|
+
productSkin(skin2) {
|
|
69808
|
+
return new ProductSkinCarrier(skin2);
|
|
69764
69809
|
},
|
|
69765
69810
|
/** Reserved stub for future parameterized mesh carriers; arbitrary mesh parameterization is not implemented yet. */
|
|
69766
69811
|
mesh(name) {
|
|
@@ -70017,7 +70062,7 @@ function norm(v) {
|
|
|
70017
70062
|
if (len2 < 1e-12) return [0, 0, 1];
|
|
70018
70063
|
return [v[0] / len2, v[1] / len2, v[2] / len2];
|
|
70019
70064
|
}
|
|
70020
|
-
function sub3(a2, b) {
|
|
70065
|
+
function sub3$1(a2, b) {
|
|
70021
70066
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
70022
70067
|
}
|
|
70023
70068
|
function mul3(a2, s) {
|
|
@@ -70253,7 +70298,7 @@ function collectShapeTriangles(shape) {
|
|
|
70253
70298
|
mesh.vertProperties[i2 * numProp + 1],
|
|
70254
70299
|
mesh.vertProperties[i2 * numProp + 2]
|
|
70255
70300
|
];
|
|
70256
|
-
const n = norm(cross3(sub3(b, a2), sub3(c2, a2)));
|
|
70301
|
+
const n = norm(cross3(sub3$1(b, a2), sub3$1(c2, a2)));
|
|
70257
70302
|
tris.push({ a: a2, b, c: c2, normal: n });
|
|
70258
70303
|
}
|
|
70259
70304
|
return tris;
|
|
@@ -70323,7 +70368,7 @@ function summarizeMetricSeries(values) {
|
|
|
70323
70368
|
return [signatureNumber(sum2), signatureNumber(sumSquares), signatureNumber(min2), signatureNumber(max2)].join(":");
|
|
70324
70369
|
}
|
|
70325
70370
|
function triangleArea(triangle) {
|
|
70326
|
-
const c2 = cross3(sub3(triangle.b, triangle.a), sub3(triangle.c, triangle.a));
|
|
70371
|
+
const c2 = cross3(sub3$1(triangle.b, triangle.a), sub3$1(triangle.c, triangle.a));
|
|
70327
70372
|
return Math.hypot(c2[0], c2[1], c2[2]) * 0.5;
|
|
70328
70373
|
}
|
|
70329
70374
|
function makeComponentPageSignature(object) {
|
|
@@ -70411,7 +70456,7 @@ function setReportLengthUnit(unit) {
|
|
|
70411
70456
|
_reportLengthUnit = unit;
|
|
70412
70457
|
}
|
|
70413
70458
|
function projectPoint(point2, center, frame) {
|
|
70414
|
-
const rel = sub3(point2, center);
|
|
70459
|
+
const rel = sub3$1(point2, center);
|
|
70415
70460
|
return {
|
|
70416
70461
|
x: dot3$1(rel, frame.right),
|
|
70417
70462
|
y: dot3$1(rel, frame.up),
|
|
@@ -70432,7 +70477,7 @@ function makeViewFrame(view) {
|
|
|
70432
70477
|
return { id: view, label: c2.label, right, up, forward };
|
|
70433
70478
|
}
|
|
70434
70479
|
function isDimensionVisibleInView(dim2, frame, toleranceDeg) {
|
|
70435
|
-
const dir = sub3(dim2.to, dim2.from);
|
|
70480
|
+
const dir = sub3$1(dim2.to, dim2.from);
|
|
70436
70481
|
const len2 = Math.hypot(dir[0], dir[1], dir[2]);
|
|
70437
70482
|
if (len2 < 1e-9) return false;
|
|
70438
70483
|
const d2 = [dir[0] / len2, dir[1] / len2, dir[2] / len2];
|
|
@@ -70468,7 +70513,7 @@ function pickDimensionOffsetBasis(dirModel, frame) {
|
|
|
70468
70513
|
candidates.push({ dir3, proj, projDir, projLen });
|
|
70469
70514
|
};
|
|
70470
70515
|
worldAxes.forEach((axis) => {
|
|
70471
|
-
const axisPerp = sub3(axis, mul3(dirModel, dot3$1(axis, dirModel)));
|
|
70516
|
+
const axisPerp = sub3$1(axis, mul3(dirModel, dot3$1(axis, dirModel)));
|
|
70472
70517
|
pushCandidate(axisPerp);
|
|
70473
70518
|
});
|
|
70474
70519
|
if (candidates.length === 0) {
|
|
@@ -71008,7 +71053,7 @@ function drawDimension(dim2, frame, mapPoint, mapScale, color, cell, fromProject
|
|
|
71008
71053
|
const dy = toProjected[1] - fromProjected[1];
|
|
71009
71054
|
const len2 = Math.hypot(dx, dy);
|
|
71010
71055
|
if (len2 < 1e-8) return { graphicsCmd: "", labelPlan: null, lineSegments: [] };
|
|
71011
|
-
const modelDirRaw = sub3(dim2.to, dim2.from);
|
|
71056
|
+
const modelDirRaw = sub3$1(dim2.to, dim2.from);
|
|
71012
71057
|
const modelLen = Math.hypot(modelDirRaw[0], modelDirRaw[1], modelDirRaw[2]);
|
|
71013
71058
|
if (modelLen < 1e-9) return { graphicsCmd: "", labelPlan: null, lineSegments: [] };
|
|
71014
71059
|
const modelDir = [modelDirRaw[0] / modelLen, modelDirRaw[1] / modelLen, modelDirRaw[2] / modelLen];
|
|
@@ -71642,206 +71687,6 @@ function generateReportPdf(result, options = {}) {
|
|
|
71642
71687
|
bomItemCount: collectBomRows(bomEntries).length
|
|
71643
71688
|
};
|
|
71644
71689
|
}
|
|
71645
|
-
let _collectedRobotExport = null;
|
|
71646
|
-
function cloneLinkOptions(input) {
|
|
71647
|
-
if (!input) return {};
|
|
71648
|
-
return Object.fromEntries(Object.entries(input).map(([name, opts]) => [name, { ...opts }]));
|
|
71649
|
-
}
|
|
71650
|
-
function cloneJointOptions(input) {
|
|
71651
|
-
if (!input) return {};
|
|
71652
|
-
return Object.fromEntries(Object.entries(input).map(([name, opts]) => [name, { ...opts }]));
|
|
71653
|
-
}
|
|
71654
|
-
function cloneDiffDrive(input) {
|
|
71655
|
-
if (!input) return void 0;
|
|
71656
|
-
return {
|
|
71657
|
-
...input,
|
|
71658
|
-
leftJoints: [...input.leftJoints],
|
|
71659
|
-
rightJoints: [...input.rightJoints]
|
|
71660
|
-
};
|
|
71661
|
-
}
|
|
71662
|
-
function cloneJointStatePublisher(input) {
|
|
71663
|
-
if (!input) return void 0;
|
|
71664
|
-
return {
|
|
71665
|
-
...input,
|
|
71666
|
-
joints: input.joints ? [...input.joints] : void 0
|
|
71667
|
-
};
|
|
71668
|
-
}
|
|
71669
|
-
function cloneWorld(input) {
|
|
71670
|
-
if (!input) return null;
|
|
71671
|
-
return {
|
|
71672
|
-
...input,
|
|
71673
|
-
spawnPose: input.spawnPose ? [...input.spawnPose] : void 0,
|
|
71674
|
-
keyboardTeleop: input.keyboardTeleop ? { ...input.keyboardTeleop } : void 0
|
|
71675
|
-
};
|
|
71676
|
-
}
|
|
71677
|
-
function assertFinite(value, label) {
|
|
71678
|
-
if (value !== void 0 && !Number.isFinite(value)) {
|
|
71679
|
-
throw new Error(`${label} must be finite`);
|
|
71680
|
-
}
|
|
71681
|
-
}
|
|
71682
|
-
function metadataNumber(value) {
|
|
71683
|
-
return typeof value === "number" ? value : void 0;
|
|
71684
|
-
}
|
|
71685
|
-
function metadataCollision(value) {
|
|
71686
|
-
return value === "none" || value === "visual" || value === "box" || value === "convex" ? value : void 0;
|
|
71687
|
-
}
|
|
71688
|
-
function colliderCollision(collider2) {
|
|
71689
|
-
if (!collider2) return void 0;
|
|
71690
|
-
if (collider2.mode === "box") return "box";
|
|
71691
|
-
if (collider2.mode === "visual") return "visual";
|
|
71692
|
-
return collider2.mode;
|
|
71693
|
-
}
|
|
71694
|
-
function velocityDriveEffort(drive) {
|
|
71695
|
-
return (drive == null ? void 0 : drive.kind) === "velocity" ? drive.maxTorqueNm : void 0;
|
|
71696
|
-
}
|
|
71697
|
-
function velocityDriveVelocityDegS(drive) {
|
|
71698
|
-
return (drive == null ? void 0 : drive.kind) === "velocity" ? drive.maxSpeedRpm * 6 : void 0;
|
|
71699
|
-
}
|
|
71700
|
-
function driveDamping(drive) {
|
|
71701
|
-
return drive == null ? void 0 : drive.damping;
|
|
71702
|
-
}
|
|
71703
|
-
function driveFriction(drive) {
|
|
71704
|
-
return drive == null ? void 0 : drive.friction;
|
|
71705
|
-
}
|
|
71706
|
-
function jointByName(assembly2) {
|
|
71707
|
-
return new Map(assembly2.joints.map((joint2) => [joint2.name, joint2]));
|
|
71708
|
-
}
|
|
71709
|
-
function diffDriveFromControllers(controllers) {
|
|
71710
|
-
if (!controllers) return void 0;
|
|
71711
|
-
const diffDrives = controllers.filter((controller) => controller.kind === "diffDrive");
|
|
71712
|
-
if (diffDrives.length > 1) {
|
|
71713
|
-
throw new Error("assembly.withSimulation(...) currently supports one Sim.controller.diffDrive(...) controller");
|
|
71714
|
-
}
|
|
71715
|
-
const diffDrive2 = diffDrives[0];
|
|
71716
|
-
return diffDrive2 ? {
|
|
71717
|
-
leftJoints: [...diffDrive2.leftJoints],
|
|
71718
|
-
rightJoints: [...diffDrive2.rightJoints],
|
|
71719
|
-
wheelSeparationMm: diffDrive2.wheelSeparationMm,
|
|
71720
|
-
wheelRadiusMm: diffDrive2.wheelRadiusMm,
|
|
71721
|
-
topic: diffDrive2.topic,
|
|
71722
|
-
odomTopic: diffDrive2.odomTopic,
|
|
71723
|
-
tfTopic: diffDrive2.tfTopic,
|
|
71724
|
-
frameId: diffDrive2.frameId,
|
|
71725
|
-
odomFrameId: diffDrive2.odomFrameId,
|
|
71726
|
-
maxLinearVelocity: diffDrive2.maxLinearVelocity,
|
|
71727
|
-
maxAngularVelocity: diffDrive2.maxAngularVelocity,
|
|
71728
|
-
linearAcceleration: diffDrive2.linearAcceleration,
|
|
71729
|
-
angularAcceleration: diffDrive2.angularAcceleration
|
|
71730
|
-
} : void 0;
|
|
71731
|
-
}
|
|
71732
|
-
function resetRobotExport() {
|
|
71733
|
-
_collectedRobotExport = null;
|
|
71734
|
-
}
|
|
71735
|
-
function getCollectedRobotExport() {
|
|
71736
|
-
return _collectedRobotExport;
|
|
71737
|
-
}
|
|
71738
|
-
function collectSimulationModel(assemblyInput, options = {}) {
|
|
71739
|
-
var _a3, _b3, _c2, _d2, _e2, _f3, _g, _h, _i;
|
|
71740
|
-
const assembly2 = typeof assemblyInput.describe === "function" ? assemblyInput.describe() : assemblyInput;
|
|
71741
|
-
const partNames = new Set(assembly2.parts.map((part) => part.name));
|
|
71742
|
-
const joints = jointByName(assembly2);
|
|
71743
|
-
const links = cloneLinkOptions(options.links);
|
|
71744
|
-
for (const part of assembly2.parts) {
|
|
71745
|
-
const fromSim = part.sim;
|
|
71746
|
-
const fromMaterialDensity = (_a3 = fromSim == null ? void 0 : fromSim.material) == null ? void 0 : _a3.densityKgM3;
|
|
71747
|
-
const current = links[part.name] ?? {};
|
|
71748
|
-
const next = {
|
|
71749
|
-
massKg: current.massKg ?? (fromSim == null ? void 0 : fromSim.massKg) ?? metadataNumber((_b3 = part.metadata) == null ? void 0 : _b3.massKg),
|
|
71750
|
-
densityKgM3: current.densityKgM3 ?? (fromSim == null ? void 0 : fromSim.densityKgM3) ?? fromMaterialDensity ?? metadataNumber((_c2 = part.metadata) == null ? void 0 : _c2.densityKgM3),
|
|
71751
|
-
collision: current.collision ?? colliderCollision(fromSim == null ? void 0 : fromSim.collider) ?? metadataCollision((_d2 = part.metadata) == null ? void 0 : _d2.collision)
|
|
71752
|
-
};
|
|
71753
|
-
if (next.massKg !== void 0 || next.densityKgM3 !== void 0 || next.collision !== void 0) {
|
|
71754
|
-
links[part.name] = next;
|
|
71755
|
-
}
|
|
71756
|
-
}
|
|
71757
|
-
for (const [partName, link] of Object.entries(links)) {
|
|
71758
|
-
if (!partNames.has(partName)) throw new Error(`simulation model references unknown link "${partName}"`);
|
|
71759
|
-
assertFinite(link.massKg, `simulation model link "${partName}" massKg`);
|
|
71760
|
-
assertFinite(link.densityKgM3, `simulation model link "${partName}" densityKgM3`);
|
|
71761
|
-
}
|
|
71762
|
-
const jointOpts = cloneJointOptions(options.joints);
|
|
71763
|
-
for (const joint2 of assembly2.joints) {
|
|
71764
|
-
const drive = (_e2 = joint2.sim) == null ? void 0 : _e2.drive;
|
|
71765
|
-
const current = jointOpts[joint2.name] ?? {};
|
|
71766
|
-
const next = {
|
|
71767
|
-
effort: current.effort ?? velocityDriveEffort(drive) ?? joint2.effort,
|
|
71768
|
-
velocity: current.velocity ?? velocityDriveVelocityDegS(drive) ?? joint2.velocity,
|
|
71769
|
-
damping: current.damping ?? driveDamping(drive) ?? joint2.damping,
|
|
71770
|
-
friction: current.friction ?? driveFriction(drive) ?? joint2.friction
|
|
71771
|
-
};
|
|
71772
|
-
if (next.effort !== void 0 || next.velocity !== void 0 || next.damping !== void 0 || next.friction !== void 0) {
|
|
71773
|
-
jointOpts[joint2.name] = next;
|
|
71774
|
-
}
|
|
71775
|
-
}
|
|
71776
|
-
for (const [jointName, joint2] of Object.entries(jointOpts)) {
|
|
71777
|
-
if (!joints.has(jointName)) throw new Error(`simulation model references unknown joint "${jointName}"`);
|
|
71778
|
-
assertFinite(joint2.effort, `simulation model joint "${jointName}" effort`);
|
|
71779
|
-
assertFinite(joint2.velocity, `simulation model joint "${jointName}" velocity`);
|
|
71780
|
-
assertFinite(joint2.damping, `simulation model joint "${jointName}" damping`);
|
|
71781
|
-
assertFinite(joint2.friction, `simulation model joint "${jointName}" friction`);
|
|
71782
|
-
}
|
|
71783
|
-
const simulation = cloneSimAssemblySimulation(assembly2.sim) ?? null;
|
|
71784
|
-
const simulationDiffDrive = diffDriveFromControllers(simulation == null ? void 0 : simulation.controllers);
|
|
71785
|
-
const diffDrive2 = cloneDiffDrive((_f3 = options.plugins) == null ? void 0 : _f3.diffDrive) ?? simulationDiffDrive;
|
|
71786
|
-
if (diffDrive2) {
|
|
71787
|
-
if (diffDrive2.leftJoints.length === 0 || diffDrive2.rightJoints.length === 0) {
|
|
71788
|
-
throw new Error("simulation model diffDrive requires at least one left joint and one right joint");
|
|
71789
|
-
}
|
|
71790
|
-
assertFinite(diffDrive2.wheelSeparationMm, "simulation model diffDrive wheelSeparationMm");
|
|
71791
|
-
assertFinite(diffDrive2.wheelRadiusMm, "simulation model diffDrive wheelRadiusMm");
|
|
71792
|
-
if (diffDrive2.wheelSeparationMm <= 0 || diffDrive2.wheelRadiusMm <= 0) {
|
|
71793
|
-
throw new Error("simulation model diffDrive wheel separation and radius must be > 0");
|
|
71794
|
-
}
|
|
71795
|
-
[...diffDrive2.leftJoints, ...diffDrive2.rightJoints].forEach((jointName) => {
|
|
71796
|
-
const joint2 = joints.get(jointName);
|
|
71797
|
-
if (!joint2) throw new Error(`simulation model diffDrive references unknown joint "${jointName}"`);
|
|
71798
|
-
if (joint2.type !== "revolute") {
|
|
71799
|
-
throw new Error(`simulation model diffDrive joint "${jointName}" must be revolute`);
|
|
71800
|
-
}
|
|
71801
|
-
});
|
|
71802
|
-
}
|
|
71803
|
-
const jointStatePublisher = cloneJointStatePublisher((_g = options.plugins) == null ? void 0 : _g.jointStatePublisher);
|
|
71804
|
-
if (jointStatePublisher == null ? void 0 : jointStatePublisher.joints) {
|
|
71805
|
-
jointStatePublisher.joints.forEach((jointName) => {
|
|
71806
|
-
if (!joints.has(jointName)) {
|
|
71807
|
-
throw new Error(`simulation model jointStatePublisher references unknown joint "${jointName}"`);
|
|
71808
|
-
}
|
|
71809
|
-
});
|
|
71810
|
-
}
|
|
71811
|
-
const world = cloneWorld(options.world);
|
|
71812
|
-
if (world == null ? void 0 : world.spawnPose) {
|
|
71813
|
-
world.spawnPose.forEach((value, index2) => assertFinite(value, `simulation model world spawnPose[${index2}]`));
|
|
71814
|
-
}
|
|
71815
|
-
assertFinite((_h = world == null ? void 0 : world.keyboardTeleop) == null ? void 0 : _h.linearStep, "simulation model world keyboardTeleop.linearStep");
|
|
71816
|
-
assertFinite((_i = world == null ? void 0 : world.keyboardTeleop) == null ? void 0 : _i.angularStep, "simulation model world keyboardTeleop.angularStep");
|
|
71817
|
-
return {
|
|
71818
|
-
modelName: (options.modelName ?? assembly2.name ?? "ForgeCAD Simulation").trim() || "ForgeCAD Simulation",
|
|
71819
|
-
assembly: assembly2,
|
|
71820
|
-
simulation,
|
|
71821
|
-
source: options.source ?? "assembly",
|
|
71822
|
-
state: { ...options.state ?? {} },
|
|
71823
|
-
static: options.static ?? false,
|
|
71824
|
-
selfCollide: options.selfCollide ?? false,
|
|
71825
|
-
allowAutoDisable: options.allowAutoDisable ?? true,
|
|
71826
|
-
links,
|
|
71827
|
-
joints: jointOpts,
|
|
71828
|
-
plugins: {
|
|
71829
|
-
diffDrive: diffDrive2,
|
|
71830
|
-
jointStatePublisher
|
|
71831
|
-
},
|
|
71832
|
-
world
|
|
71833
|
-
};
|
|
71834
|
-
}
|
|
71835
|
-
function robotExport(options) {
|
|
71836
|
-
if (!options || typeof options !== "object") {
|
|
71837
|
-
throw new Error("robotExport(...) expects an options object");
|
|
71838
|
-
}
|
|
71839
|
-
if (!options.assembly || typeof options.assembly.describe !== "function") {
|
|
71840
|
-
throw new Error("robotExport(...) requires an assembly");
|
|
71841
|
-
}
|
|
71842
|
-
_collectedRobotExport = collectSimulationModel(options.assembly, { ...options, source: "robotExport" });
|
|
71843
|
-
return _collectedRobotExport;
|
|
71844
|
-
}
|
|
71845
71690
|
class Point2D {
|
|
71846
71691
|
constructor(x2, y2) {
|
|
71847
71692
|
this.x = x2;
|
|
@@ -94115,6 +93960,847 @@ function sketchOnFace(sketch, parentOrFace, faceOrOpts, maybeOpts = {}) {
|
|
|
94115
93960
|
Sketch.prototype.onFace = function(parentOrFace, faceOrOpts, maybeOpts = {}) {
|
|
94116
93961
|
return sketchOnFace(this, parentOrFace, faceOrOpts, maybeOpts);
|
|
94117
93962
|
};
|
|
93963
|
+
const KNOT_EPS = 1e-10;
|
|
93964
|
+
const KNOT_MERGE_EPS = 1e-7;
|
|
93965
|
+
function sameKnot(a2, b) {
|
|
93966
|
+
return Math.abs(a2 - b) <= KNOT_EPS;
|
|
93967
|
+
}
|
|
93968
|
+
function knotMultiplicity(knots, u2) {
|
|
93969
|
+
let count = 0;
|
|
93970
|
+
for (const k2 of knots) if (sameKnot(k2, u2)) count++;
|
|
93971
|
+
return count;
|
|
93972
|
+
}
|
|
93973
|
+
function computeParamsCentripetal(points, alpha = 0.5) {
|
|
93974
|
+
const n = points.length;
|
|
93975
|
+
const params = new Array(n).fill(0);
|
|
93976
|
+
const seg = new Array(n).fill(0);
|
|
93977
|
+
let total = 0;
|
|
93978
|
+
for (let k2 = 1; k2 < n; k2++) {
|
|
93979
|
+
const dx = points[k2][0] - points[k2 - 1][0];
|
|
93980
|
+
const dy = points[k2][1] - points[k2 - 1][1];
|
|
93981
|
+
const dz = points[k2][2] - points[k2 - 1][2];
|
|
93982
|
+
seg[k2] = Math.hypot(dx, dy, dz) ** alpha;
|
|
93983
|
+
total += seg[k2];
|
|
93984
|
+
}
|
|
93985
|
+
if (total === 0) {
|
|
93986
|
+
for (let k2 = 0; k2 < n; k2++) params[k2] = n > 1 ? k2 / (n - 1) : 0;
|
|
93987
|
+
return params;
|
|
93988
|
+
}
|
|
93989
|
+
let acc = 0;
|
|
93990
|
+
for (let k2 = 1; k2 < n - 1; k2++) {
|
|
93991
|
+
acc += seg[k2];
|
|
93992
|
+
params[k2] = acc / total;
|
|
93993
|
+
}
|
|
93994
|
+
params[n - 1] = 1;
|
|
93995
|
+
return params;
|
|
93996
|
+
}
|
|
93997
|
+
function knotsFromParamsAveraging(params, degree) {
|
|
93998
|
+
const n = params.length - 1;
|
|
93999
|
+
const m2 = n + degree + 1;
|
|
94000
|
+
const knots = new Array(m2 + 1).fill(0);
|
|
94001
|
+
for (let i = m2 - degree; i <= m2; i++) knots[i] = 1;
|
|
94002
|
+
for (let j = 1; j <= n - degree; j++) {
|
|
94003
|
+
let s = 0;
|
|
94004
|
+
for (let i = j; i <= j + degree - 1; i++) s += params[i];
|
|
94005
|
+
knots[j + degree] = s / degree;
|
|
94006
|
+
}
|
|
94007
|
+
return knots;
|
|
94008
|
+
}
|
|
94009
|
+
function solveLinear(A, B2) {
|
|
94010
|
+
const n = A.length;
|
|
94011
|
+
const k2 = B2[0].length;
|
|
94012
|
+
const M = A.map((row, i) => [...row, ...B2[i]]);
|
|
94013
|
+
for (let col = 0; col < n; col++) {
|
|
94014
|
+
let piv = col;
|
|
94015
|
+
for (let r = col + 1; r < n; r++) if (Math.abs(M[r][col]) > Math.abs(M[piv][col])) piv = r;
|
|
94016
|
+
if (Math.abs(M[piv][col]) < 1e-14) throw new Error(`Singular interpolation matrix at column ${col}.`);
|
|
94017
|
+
[M[col], M[piv]] = [M[piv], M[col]];
|
|
94018
|
+
const inv = 1 / M[col][col];
|
|
94019
|
+
for (let j = col; j < n + k2; j++) M[col][j] *= inv;
|
|
94020
|
+
for (let r = 0; r < n; r++) {
|
|
94021
|
+
if (r === col) continue;
|
|
94022
|
+
const f3 = M[r][col];
|
|
94023
|
+
if (f3 === 0) continue;
|
|
94024
|
+
for (let j = col; j < n + k2; j++) M[r][j] -= f3 * M[col][j];
|
|
94025
|
+
}
|
|
94026
|
+
}
|
|
94027
|
+
return Array.from({ length: n }, (_2, i) => M[i].slice(n));
|
|
94028
|
+
}
|
|
94029
|
+
function globalCurveInterp(points, degree, paramsIn) {
|
|
94030
|
+
const last = points.length - 1;
|
|
94031
|
+
if (last < degree) throw new Error(`Curve interpolation needs at least ${degree + 1} points, got ${points.length}.`);
|
|
94032
|
+
const params = paramsIn ? paramsIn.slice() : computeParamsCentripetal(points, 0.5);
|
|
94033
|
+
const knots = knotsFromParamsAveraging(params, degree);
|
|
94034
|
+
const count = last + 1;
|
|
94035
|
+
const N = Array.from({ length: count }, () => new Array(count).fill(0));
|
|
94036
|
+
for (let i = 0; i <= last; i++) {
|
|
94037
|
+
const span = findSpan(count, degree, params[i], knots);
|
|
94038
|
+
const basis = basisFuns(span, params[i], degree, knots);
|
|
94039
|
+
for (let j = 0; j <= degree; j++) N[i][span - degree + j] = basis[j];
|
|
94040
|
+
}
|
|
94041
|
+
const Q = points.map((p2) => [p2[0], p2[1], p2[2]]);
|
|
94042
|
+
const cps = solveLinear(N, Q);
|
|
94043
|
+
return { cps, knots, degree };
|
|
94044
|
+
}
|
|
94045
|
+
function insertKnotCurve(cps, knots, degree, u2) {
|
|
94046
|
+
const n = cps.length;
|
|
94047
|
+
const span = findSpan(n, degree, u2, knots);
|
|
94048
|
+
const mult = knotMultiplicity(knots, u2);
|
|
94049
|
+
if (mult >= degree) return { cps: cps.slice(), knots: knots.slice() };
|
|
94050
|
+
const newCps = new Array(n + 1);
|
|
94051
|
+
const newKnots = new Array(knots.length + 1);
|
|
94052
|
+
for (let i = 0; i <= span; i++) newKnots[i] = knots[i];
|
|
94053
|
+
newKnots[span + 1] = u2;
|
|
94054
|
+
for (let i = span + 1; i < knots.length; i++) newKnots[i + 1] = knots[i];
|
|
94055
|
+
for (let i = 0; i <= span - degree; i++) newCps[i] = [...cps[i]];
|
|
94056
|
+
for (let i = span - mult; i < n; i++) newCps[i + 1] = [...cps[i]];
|
|
94057
|
+
for (let i = span - degree + 1; i <= span - mult; i++) {
|
|
94058
|
+
const denom = knots[i + degree] - knots[i];
|
|
94059
|
+
const alpha = denom === 0 ? 0 : (u2 - knots[i]) / denom;
|
|
94060
|
+
const a2 = cps[i - 1];
|
|
94061
|
+
const b = cps[i];
|
|
94062
|
+
newCps[i] = [(1 - alpha) * a2[0] + alpha * b[0], (1 - alpha) * a2[1] + alpha * b[1], (1 - alpha) * a2[2] + alpha * b[2]];
|
|
94063
|
+
}
|
|
94064
|
+
return { cps: newCps, knots: newKnots };
|
|
94065
|
+
}
|
|
94066
|
+
function interiorKnotBuckets(kv, mergeEps = KNOT_MERGE_EPS) {
|
|
94067
|
+
const buckets = [];
|
|
94068
|
+
for (const k2 of kv) {
|
|
94069
|
+
if (k2 <= KNOT_EPS || k2 >= 1 - KNOT_EPS) continue;
|
|
94070
|
+
const b = buckets.find((x2) => Math.abs(x2.value - k2) <= mergeEps);
|
|
94071
|
+
if (b) b.mult++;
|
|
94072
|
+
else buckets.push({ value: k2, mult: 1 });
|
|
94073
|
+
}
|
|
94074
|
+
return buckets;
|
|
94075
|
+
}
|
|
94076
|
+
function mergeKnotVectors(knotVectors, degree, mergeEps = KNOT_MERGE_EPS) {
|
|
94077
|
+
const merged = [];
|
|
94078
|
+
for (const kv of knotVectors) {
|
|
94079
|
+
for (const { value, mult } of interiorKnotBuckets(kv, mergeEps)) {
|
|
94080
|
+
const b = merged.find((x2) => Math.abs(x2.value - value) <= mergeEps);
|
|
94081
|
+
if (b) b.mult = Math.max(b.mult, mult);
|
|
94082
|
+
else merged.push({ value, mult });
|
|
94083
|
+
}
|
|
94084
|
+
}
|
|
94085
|
+
merged.sort((a2, b) => a2.value - b.value);
|
|
94086
|
+
const out = [];
|
|
94087
|
+
for (let i = 0; i <= degree; i++) out.push(0);
|
|
94088
|
+
for (const { value, mult } of merged) for (let i = 0; i < mult; i++) out.push(value);
|
|
94089
|
+
for (let i = 0; i <= degree; i++) out.push(1);
|
|
94090
|
+
return out;
|
|
94091
|
+
}
|
|
94092
|
+
function refineKnotsToTarget(cps, knots, degree, targetKnots, mergeEps = KNOT_MERGE_EPS) {
|
|
94093
|
+
let curCps = cps.map((p2) => [...p2]);
|
|
94094
|
+
let curKnots = knots.slice();
|
|
94095
|
+
for (const { value, mult } of interiorKnotBuckets(targetKnots, mergeEps)) {
|
|
94096
|
+
const curMult = () => curKnots.reduce((c2, k2) => c2 + (Math.abs(k2 - value) <= mergeEps ? 1 : 0), 0);
|
|
94097
|
+
let guard = 0;
|
|
94098
|
+
while (curMult() < mult) {
|
|
94099
|
+
const r = insertKnotCurve(curCps, curKnots, degree, value);
|
|
94100
|
+
if (r.cps.length === curCps.length) break;
|
|
94101
|
+
curCps = r.cps;
|
|
94102
|
+
curKnots = r.knots;
|
|
94103
|
+
if (++guard > degree + 2) break;
|
|
94104
|
+
}
|
|
94105
|
+
}
|
|
94106
|
+
return { cps: curCps, knots: curKnots };
|
|
94107
|
+
}
|
|
94108
|
+
function binomial(a2, b) {
|
|
94109
|
+
if (b < 0 || b > a2) return 0;
|
|
94110
|
+
let r = 1;
|
|
94111
|
+
for (let i = 0; i < b; i++) r = r * (a2 - i) / (i + 1);
|
|
94112
|
+
return r;
|
|
94113
|
+
}
|
|
94114
|
+
function bezierElevate(bez, p2, t) {
|
|
94115
|
+
const m2 = p2 + t;
|
|
94116
|
+
const out = [];
|
|
94117
|
+
for (let i = 0; i <= m2; i++) {
|
|
94118
|
+
const acc = [0, 0, 0];
|
|
94119
|
+
const lo = Math.max(0, i - t);
|
|
94120
|
+
const hi = Math.min(p2, i);
|
|
94121
|
+
for (let j = lo; j <= hi; j++) {
|
|
94122
|
+
const w2 = binomial(p2, j) * binomial(t, i - j) / binomial(m2, i);
|
|
94123
|
+
acc[0] += w2 * bez[j][0];
|
|
94124
|
+
acc[1] += w2 * bez[j][1];
|
|
94125
|
+
acc[2] += w2 * bez[j][2];
|
|
94126
|
+
}
|
|
94127
|
+
out.push(acc);
|
|
94128
|
+
}
|
|
94129
|
+
return out;
|
|
94130
|
+
}
|
|
94131
|
+
function elevateCurveDegree(cps, knots, degree, targetDegree) {
|
|
94132
|
+
if (targetDegree === degree) return { cps: cps.map((p2) => [...p2]), knots: knots.slice(), degree };
|
|
94133
|
+
if (targetDegree < degree) throw new Error("Degree reduction is not supported.");
|
|
94134
|
+
const t = targetDegree - degree;
|
|
94135
|
+
let curCps = cps.map((p2) => [...p2]);
|
|
94136
|
+
let curKnots = knots.slice();
|
|
94137
|
+
const breaks = interiorKnotBuckets(curKnots).map((b) => b.value);
|
|
94138
|
+
for (const v of breaks) {
|
|
94139
|
+
let guard = 0;
|
|
94140
|
+
while (knotMultiplicity(curKnots, v) < degree) {
|
|
94141
|
+
const r = insertKnotCurve(curCps, curKnots, degree, v);
|
|
94142
|
+
curCps = r.cps;
|
|
94143
|
+
curKnots = r.knots;
|
|
94144
|
+
if (++guard > degree + 2) break;
|
|
94145
|
+
}
|
|
94146
|
+
}
|
|
94147
|
+
const segVals = [0, ...breaks.slice().sort((a2, b) => a2 - b), 1];
|
|
94148
|
+
const nSeg = segVals.length - 1;
|
|
94149
|
+
const newCps = [];
|
|
94150
|
+
for (let s = 0; s < nSeg; s++) {
|
|
94151
|
+
const bez = curCps.slice(s * degree, s * degree + degree + 1);
|
|
94152
|
+
const elevated = bezierElevate(bez, degree, t);
|
|
94153
|
+
if (s === 0) newCps.push(...elevated);
|
|
94154
|
+
else newCps.push(...elevated.slice(1));
|
|
94155
|
+
}
|
|
94156
|
+
const newKnots = [];
|
|
94157
|
+
for (let i = 0; i <= targetDegree; i++) newKnots.push(0);
|
|
94158
|
+
for (let s = 1; s < nSeg; s++) for (let i = 0; i < targetDegree; i++) newKnots.push(segVals[s]);
|
|
94159
|
+
for (let i = 0; i <= targetDegree; i++) newKnots.push(1);
|
|
94160
|
+
if (newKnots.length !== newCps.length + targetDegree + 1) {
|
|
94161
|
+
throw new Error(
|
|
94162
|
+
`Degree elevation produced inconsistent knots (${newKnots.length}) for ${newCps.length} control points at degree ${targetDegree}.`
|
|
94163
|
+
);
|
|
94164
|
+
}
|
|
94165
|
+
return { cps: newCps, knots: newKnots, degree: targetDegree };
|
|
94166
|
+
}
|
|
94167
|
+
function unifyCurves(curves) {
|
|
94168
|
+
const targetDeg = Math.max(...curves.map((c2) => c2.degree));
|
|
94169
|
+
const elevated = curves.map((c2) => elevateCurveDegree(c2.cps, c2.knots, c2.degree, targetDeg));
|
|
94170
|
+
const common = mergeKnotVectors(
|
|
94171
|
+
elevated.map((c2) => c2.knots),
|
|
94172
|
+
targetDeg
|
|
94173
|
+
);
|
|
94174
|
+
const refined = elevated.map((c2) => refineKnotsToTarget(c2.cps, c2.knots, targetDeg, common));
|
|
94175
|
+
const ncp = common.length - targetDeg - 1;
|
|
94176
|
+
for (const r of refined) {
|
|
94177
|
+
if (r.cps.length !== ncp) throw new Error(`Curve unification produced ${r.cps.length} control points, expected ${ncp}.`);
|
|
94178
|
+
}
|
|
94179
|
+
return { degree: targetDeg, knots: common, curves: refined.map((r) => r.cps) };
|
|
94180
|
+
}
|
|
94181
|
+
function transpose(g2) {
|
|
94182
|
+
const out = [];
|
|
94183
|
+
for (let j = 0; j < g2[0].length; j++) {
|
|
94184
|
+
const row = [];
|
|
94185
|
+
for (let i = 0; i < g2.length; i++) row.push(g2[i][j]);
|
|
94186
|
+
out.push(row);
|
|
94187
|
+
}
|
|
94188
|
+
return out;
|
|
94189
|
+
}
|
|
94190
|
+
function skin(curveCps, spanParams, spanDegree) {
|
|
94191
|
+
const ncp = curveCps[0].length;
|
|
94192
|
+
const cols = [];
|
|
94193
|
+
for (let j = 0; j < ncp; j++) {
|
|
94194
|
+
const pts = curveCps.map((row) => row[j]);
|
|
94195
|
+
cols.push(globalCurveInterp(pts, spanDegree, spanParams).cps);
|
|
94196
|
+
}
|
|
94197
|
+
const spanCp = cols[0].length;
|
|
94198
|
+
const grid = [];
|
|
94199
|
+
for (let s = 0; s < spanCp; s++) {
|
|
94200
|
+
const row = [];
|
|
94201
|
+
for (let j = 0; j < ncp; j++) row.push(cols[j][s]);
|
|
94202
|
+
grid.push(row);
|
|
94203
|
+
}
|
|
94204
|
+
const spanKnots = knotsFromParamsAveraging(spanParams, spanDegree);
|
|
94205
|
+
return { grid, spanKnots };
|
|
94206
|
+
}
|
|
94207
|
+
function tensorInterp(gridPts, uParams, vParams, degreeU, degreeV) {
|
|
94208
|
+
const rowInterp = gridPts.map((rowPts) => globalCurveInterp(rowPts, degreeV, vParams));
|
|
94209
|
+
const knotsV = rowInterp[0].knots;
|
|
94210
|
+
const ncpV = rowInterp[0].cps.length;
|
|
94211
|
+
const cols = [];
|
|
94212
|
+
for (let j = 0; j < ncpV; j++) {
|
|
94213
|
+
const pts = rowInterp.map((ri) => ri.cps[j]);
|
|
94214
|
+
cols.push(globalCurveInterp(pts, degreeU, uParams));
|
|
94215
|
+
}
|
|
94216
|
+
const knotsU = cols[0].knots;
|
|
94217
|
+
const cpU = cols[0].cps.length;
|
|
94218
|
+
const grid = [];
|
|
94219
|
+
for (let i = 0; i < cpU; i++) {
|
|
94220
|
+
const row = [];
|
|
94221
|
+
for (let j = 0; j < ncpV; j++) row.push(cols[j].cps[i]);
|
|
94222
|
+
grid.push(row);
|
|
94223
|
+
}
|
|
94224
|
+
return { grid, knotsU, knotsV };
|
|
94225
|
+
}
|
|
94226
|
+
function refineSurface(surf, targetKnotsU, targetKnotsV) {
|
|
94227
|
+
const { grid, knotsU, knotsV, degreeU, degreeV } = surf;
|
|
94228
|
+
const rows = grid.map((row) => refineKnotsToTarget(row, knotsV, degreeV, targetKnotsV).cps);
|
|
94229
|
+
const ncpV = rows[0].length;
|
|
94230
|
+
const cols = [];
|
|
94231
|
+
for (let j = 0; j < ncpV; j++) {
|
|
94232
|
+
const colPts = rows.map((r) => r[j]);
|
|
94233
|
+
cols.push(refineKnotsToTarget(colPts, knotsU, degreeU, targetKnotsU).cps);
|
|
94234
|
+
}
|
|
94235
|
+
const cpU = cols[0].length;
|
|
94236
|
+
const out = [];
|
|
94237
|
+
for (let i = 0; i < cpU; i++) {
|
|
94238
|
+
const row = [];
|
|
94239
|
+
for (let j = 0; j < ncpV; j++) row.push(cols[j][i]);
|
|
94240
|
+
out.push(row);
|
|
94241
|
+
}
|
|
94242
|
+
return { grid: out, knotsU: targetKnotsU, knotsV: targetKnotsV, degreeU, degreeV };
|
|
94243
|
+
}
|
|
94244
|
+
function averageParams(polylines, alpha = 0.5) {
|
|
94245
|
+
const count = polylines[0].length;
|
|
94246
|
+
const acc = new Array(count).fill(0);
|
|
94247
|
+
for (const pl of polylines) {
|
|
94248
|
+
const p2 = computeParamsCentripetal(pl, alpha);
|
|
94249
|
+
for (let i = 0; i < count; i++) acc[i] += p2[i];
|
|
94250
|
+
}
|
|
94251
|
+
return acc.map((s) => s / polylines.length);
|
|
94252
|
+
}
|
|
94253
|
+
function gordonGridParams(gridPts) {
|
|
94254
|
+
const rows = gridPts;
|
|
94255
|
+
const cols = gridPts[0].map((_2, l) => gridPts.map((r) => r[l]));
|
|
94256
|
+
return { vParams: averageParams(rows, 0.5), uParams: averageParams(cols, 0.5) };
|
|
94257
|
+
}
|
|
94258
|
+
function buildGordonFromGrid(gridPts, degreeU = 3, degreeV = 3) {
|
|
94259
|
+
const m2 = gridPts.length - 1;
|
|
94260
|
+
const n = gridPts[0].length - 1;
|
|
94261
|
+
if (m2 < 1 || n < 1) throw new Error("A Gordon surface needs at least a 2x2 curve network.");
|
|
94262
|
+
if (m2 < degreeU) degreeU = m2;
|
|
94263
|
+
if (n < degreeV) degreeV = n;
|
|
94264
|
+
const rows = gridPts;
|
|
94265
|
+
const cols = gridPts[0].map((_2, l) => gridPts.map((r) => r[l]));
|
|
94266
|
+
const { uParams, vParams } = gordonGridParams(gridPts);
|
|
94267
|
+
const uCurves = rows.map((rowPts) => globalCurveInterp(rowPts, degreeV, vParams));
|
|
94268
|
+
const vCurves = cols.map((colPts) => globalCurveInterp(colPts, degreeU, uParams));
|
|
94269
|
+
const uFam = unifyCurves(uCurves);
|
|
94270
|
+
const vFam = unifyCurves(vCurves);
|
|
94271
|
+
const Lu = skin(uFam.curves, uParams, degreeU);
|
|
94272
|
+
const LvRaw = skin(vFam.curves, vParams, degreeV);
|
|
94273
|
+
const Lv = { grid: transpose(LvRaw.grid), knotsU: vFam.knots, knotsV: LvRaw.spanKnots };
|
|
94274
|
+
const T = tensorInterp(gridPts, uParams, vParams, degreeU, degreeV);
|
|
94275
|
+
const knotsU = mergeKnotVectors([Lu.spanKnots, Lv.knotsU, T.knotsU], degreeU);
|
|
94276
|
+
const knotsV = mergeKnotVectors([uFam.knots, Lv.knotsV, T.knotsV], degreeV);
|
|
94277
|
+
const LuS = refineSurface({ grid: Lu.grid, knotsU: Lu.spanKnots, knotsV: uFam.knots, degreeU, degreeV }, knotsU, knotsV);
|
|
94278
|
+
const LvS = refineSurface({ grid: Lv.grid, knotsU: Lv.knotsU, knotsV: Lv.knotsV, degreeU, degreeV }, knotsU, knotsV);
|
|
94279
|
+
const TS = refineSurface({ grid: T.grid, knotsU: T.knotsU, knotsV: T.knotsV, degreeU, degreeV }, knotsU, knotsV);
|
|
94280
|
+
const nuc = LuS.grid.length;
|
|
94281
|
+
const nvc = LuS.grid[0].length;
|
|
94282
|
+
if (LvS.grid.length !== nuc || TS.grid.length !== nuc || LvS.grid[0].length !== nvc || TS.grid[0].length !== nvc) {
|
|
94283
|
+
throw new Error(
|
|
94284
|
+
`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}.`
|
|
94285
|
+
);
|
|
94286
|
+
}
|
|
94287
|
+
const grid = [];
|
|
94288
|
+
for (let i = 0; i < nuc; i++) {
|
|
94289
|
+
const row = [];
|
|
94290
|
+
for (let j = 0; j < nvc; j++) {
|
|
94291
|
+
const a2 = LuS.grid[i][j];
|
|
94292
|
+
const b = LvS.grid[i][j];
|
|
94293
|
+
const c2 = TS.grid[i][j];
|
|
94294
|
+
row.push([a2[0] + b[0] - c2[0], a2[1] + b[1] - c2[1], a2[2] + b[2] - c2[2]]);
|
|
94295
|
+
}
|
|
94296
|
+
grid.push(row);
|
|
94297
|
+
}
|
|
94298
|
+
return { grid, knotsU, knotsV, degreeU, degreeV };
|
|
94299
|
+
}
|
|
94300
|
+
function evalSurface(surf, u2, v) {
|
|
94301
|
+
const { grid, knotsU, knotsV, degreeU, degreeV } = surf;
|
|
94302
|
+
const nu = grid.length;
|
|
94303
|
+
const nv = grid[0].length;
|
|
94304
|
+
const su = findSpan(nu, degreeU, u2, knotsU);
|
|
94305
|
+
const sv = findSpan(nv, degreeV, v, knotsV);
|
|
94306
|
+
const Nu = basisFuns(su, u2, degreeU, knotsU);
|
|
94307
|
+
const Nv = basisFuns(sv, v, degreeV, knotsV);
|
|
94308
|
+
const out = [0, 0, 0];
|
|
94309
|
+
for (let i = 0; i <= degreeU; i++) {
|
|
94310
|
+
const ui = su - degreeU + i;
|
|
94311
|
+
for (let j = 0; j <= degreeV; j++) {
|
|
94312
|
+
const vj = sv - degreeV + j;
|
|
94313
|
+
const w2 = Nu[i] * Nv[j];
|
|
94314
|
+
const p2 = grid[ui][vj];
|
|
94315
|
+
out[0] += w2 * p2[0];
|
|
94316
|
+
out[1] += w2 * p2[1];
|
|
94317
|
+
out[2] += w2 * p2[2];
|
|
94318
|
+
}
|
|
94319
|
+
}
|
|
94320
|
+
return out;
|
|
94321
|
+
}
|
|
94322
|
+
function evalSurfaceJet(surf, u2, v) {
|
|
94323
|
+
const { grid, knotsU, knotsV, degreeU, degreeV } = surf;
|
|
94324
|
+
const nu = grid.length;
|
|
94325
|
+
const nv = grid[0].length;
|
|
94326
|
+
const su = findSpan(nu, degreeU, u2, knotsU);
|
|
94327
|
+
const sv = findSpan(nv, degreeV, v, knotsV);
|
|
94328
|
+
const dU = basisFunsDeriv(su, u2, degreeU, knotsU, 2);
|
|
94329
|
+
const dV = basisFunsDeriv(sv, v, degreeV, knotsV, 2);
|
|
94330
|
+
const S = [0, 0, 0];
|
|
94331
|
+
const Su = [0, 0, 0];
|
|
94332
|
+
const Sv = [0, 0, 0];
|
|
94333
|
+
const Suu = [0, 0, 0];
|
|
94334
|
+
const Suv = [0, 0, 0];
|
|
94335
|
+
const Svv = [0, 0, 0];
|
|
94336
|
+
for (let i = 0; i <= degreeU; i++) {
|
|
94337
|
+
const ui = su - degreeU + i;
|
|
94338
|
+
for (let j = 0; j <= degreeV; j++) {
|
|
94339
|
+
const vj = sv - degreeV + j;
|
|
94340
|
+
const p2 = grid[ui][vj];
|
|
94341
|
+
const w00 = dU[0][i] * dV[0][j];
|
|
94342
|
+
const w10 = dU[1][i] * dV[0][j];
|
|
94343
|
+
const w01 = dU[0][i] * dV[1][j];
|
|
94344
|
+
const w20 = dU[2][i] * dV[0][j];
|
|
94345
|
+
const w11 = dU[1][i] * dV[1][j];
|
|
94346
|
+
const w02 = dU[0][i] * dV[2][j];
|
|
94347
|
+
for (let c2 = 0; c2 < 3; c2++) {
|
|
94348
|
+
S[c2] += w00 * p2[c2];
|
|
94349
|
+
Su[c2] += w10 * p2[c2];
|
|
94350
|
+
Sv[c2] += w01 * p2[c2];
|
|
94351
|
+
Suu[c2] += w20 * p2[c2];
|
|
94352
|
+
Suv[c2] += w11 * p2[c2];
|
|
94353
|
+
Svv[c2] += w02 * p2[c2];
|
|
94354
|
+
}
|
|
94355
|
+
}
|
|
94356
|
+
}
|
|
94357
|
+
let nx = Su[1] * Sv[2] - Su[2] * Sv[1];
|
|
94358
|
+
let ny = Su[2] * Sv[0] - Su[0] * Sv[2];
|
|
94359
|
+
let nz = Su[0] * Sv[1] - Su[1] * Sv[0];
|
|
94360
|
+
const len2 = Math.hypot(nx, ny, nz) || 1;
|
|
94361
|
+
nx /= len2;
|
|
94362
|
+
ny /= len2;
|
|
94363
|
+
nz /= len2;
|
|
94364
|
+
return { S, Su, Sv, Suu, Suv, Svv, normal: [nx, ny, nz] };
|
|
94365
|
+
}
|
|
94366
|
+
function surfaceCurvature(jet) {
|
|
94367
|
+
const { Su, Sv, Suu, Suv, Svv, normal } = jet;
|
|
94368
|
+
const E = Su[0] * Su[0] + Su[1] * Su[1] + Su[2] * Su[2];
|
|
94369
|
+
const F = Su[0] * Sv[0] + Su[1] * Sv[1] + Su[2] * Sv[2];
|
|
94370
|
+
const G = Sv[0] * Sv[0] + Sv[1] * Sv[1] + Sv[2] * Sv[2];
|
|
94371
|
+
const e = Suu[0] * normal[0] + Suu[1] * normal[1] + Suu[2] * normal[2];
|
|
94372
|
+
const f3 = Suv[0] * normal[0] + Suv[1] * normal[1] + Suv[2] * normal[2];
|
|
94373
|
+
const g2 = Svv[0] * normal[0] + Svv[1] * normal[1] + Svv[2] * normal[2];
|
|
94374
|
+
const denom = E * G - F * F;
|
|
94375
|
+
if (Math.abs(denom) < 1e-20) return { k1: 0, k2: 0, K: 0, H: 0 };
|
|
94376
|
+
const K = (e * g2 - f3 * f3) / denom;
|
|
94377
|
+
const H = (e * G - 2 * f3 * F + g2 * E) / (2 * denom);
|
|
94378
|
+
const disc = Math.max(0, H * H - K);
|
|
94379
|
+
const root = Math.sqrt(disc);
|
|
94380
|
+
return { k1: H + root, k2: H - root, K, H };
|
|
94381
|
+
}
|
|
94382
|
+
function isVec3(v) {
|
|
94383
|
+
return Array.isArray(v) && v.length === 3 && typeof v[0] === "number" && typeof v[1] === "number" && typeof v[2] === "number";
|
|
94384
|
+
}
|
|
94385
|
+
function requireFiniteVec(p2, label) {
|
|
94386
|
+
for (let i = 0; i < 3; i++)
|
|
94387
|
+
if (!Number.isFinite(p2[i])) throw new Error(`Surface.Net: ${label} component ${i} must be finite, got ${p2[i]}`);
|
|
94388
|
+
return [p2[0], p2[1], p2[2]];
|
|
94389
|
+
}
|
|
94390
|
+
function toSampler(input, label) {
|
|
94391
|
+
if (input instanceof NurbsCurve3D) {
|
|
94392
|
+
return (t) => input.pointAt(t);
|
|
94393
|
+
}
|
|
94394
|
+
if (Array.isArray(input) && input.length > 0 && isVec3(input[0])) {
|
|
94395
|
+
const pts = input.map((p2, i) => requireFiniteVec(p2, `${label}[${i}]`));
|
|
94396
|
+
if (pts.length < 2) throw new Error(`Surface.Net: ${label} needs at least 2 points.`);
|
|
94397
|
+
if (pts.length === 2) {
|
|
94398
|
+
const [a2, b] = pts;
|
|
94399
|
+
return (t) => [a2[0] + (b[0] - a2[0]) * t, a2[1] + (b[1] - a2[1]) * t, a2[2] + (b[2] - a2[2]) * t];
|
|
94400
|
+
}
|
|
94401
|
+
const curve = globalCurveInterp(pts, Math.min(3, pts.length - 1));
|
|
94402
|
+
const nc = new NurbsCurve3D(curve.cps, { degree: curve.degree, knots: curve.knots });
|
|
94403
|
+
return (t) => nc.pointAt(t);
|
|
94404
|
+
}
|
|
94405
|
+
throw new Error(`Surface.Net: ${label} must be a Curve.Fit/Curve.Nurbs value or an array of [x,y,z] points.`);
|
|
94406
|
+
}
|
|
94407
|
+
function sampleCurve(sampler, count) {
|
|
94408
|
+
const out = [];
|
|
94409
|
+
for (let i = 0; i < count; i++) out.push(sampler(i / (count - 1)));
|
|
94410
|
+
return out;
|
|
94411
|
+
}
|
|
94412
|
+
const DEFAULT_THICKEN_RESOLUTION = 48;
|
|
94413
|
+
class Sheet {
|
|
94414
|
+
constructor(surface) {
|
|
94415
|
+
this.surface = surface;
|
|
94416
|
+
}
|
|
94417
|
+
/** Edge naming follows parameter direction (documented): front=v0, rear=v1, left=u0, right=u1. */
|
|
94418
|
+
get frontEdge() {
|
|
94419
|
+
return { sheet: this, fixed: "v", value: 0 };
|
|
94420
|
+
}
|
|
94421
|
+
get rearEdge() {
|
|
94422
|
+
return { sheet: this, fixed: "v", value: 1 };
|
|
94423
|
+
}
|
|
94424
|
+
get leftEdge() {
|
|
94425
|
+
return { sheet: this, fixed: "u", value: 0 };
|
|
94426
|
+
}
|
|
94427
|
+
get rightEdge() {
|
|
94428
|
+
return { sheet: this, fixed: "u", value: 1 };
|
|
94429
|
+
}
|
|
94430
|
+
pointAt(u2, v) {
|
|
94431
|
+
return evalSurface(this.surface, clamp01(u2), clamp01(v));
|
|
94432
|
+
}
|
|
94433
|
+
normalAt(u2, v) {
|
|
94434
|
+
return evalSurfaceJet(this.surface, clamp01(u2), clamp01(v)).normal;
|
|
94435
|
+
}
|
|
94436
|
+
curvatureAt(u2, v) {
|
|
94437
|
+
return surfaceCurvature(evalSurfaceJet(this.surface, clamp01(u2), clamp01(v)));
|
|
94438
|
+
}
|
|
94439
|
+
/** Largest principal curvature magnitude over a sampling grid (for offset safety). */
|
|
94440
|
+
maxAbsPrincipalCurvature(samples = 9) {
|
|
94441
|
+
let maxK = 0;
|
|
94442
|
+
for (let i = 0; i <= samples; i++) {
|
|
94443
|
+
for (let j = 0; j <= samples; j++) {
|
|
94444
|
+
const c2 = this.curvatureAt(i / samples, j / samples);
|
|
94445
|
+
maxK = Math.max(maxK, Math.abs(c2.k1), Math.abs(c2.k2));
|
|
94446
|
+
}
|
|
94447
|
+
}
|
|
94448
|
+
return maxK;
|
|
94449
|
+
}
|
|
94450
|
+
/**
|
|
94451
|
+
* Offset the sheet along its analytic normals into a watertight solid shell of
|
|
94452
|
+
* the given wall thickness. Throws if the wall would self-intersect on a
|
|
94453
|
+
* concave region (no silent degenerate solid).
|
|
94454
|
+
*/
|
|
94455
|
+
thicken(wall, options = {}) {
|
|
94456
|
+
if (!Number.isFinite(wall) || wall <= 0) throw new Error(`Sheet.thicken: wall must be a positive finite number, got ${wall}`);
|
|
94457
|
+
const maxK = this.maxAbsPrincipalCurvature();
|
|
94458
|
+
if (wall * maxK >= 1) {
|
|
94459
|
+
throw new Error(
|
|
94460
|
+
`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.`
|
|
94461
|
+
);
|
|
94462
|
+
}
|
|
94463
|
+
const resolution = options.resolution ?? DEFAULT_THICKEN_RESOLUTION;
|
|
94464
|
+
return nurbsSurface(this.surface.grid, {
|
|
94465
|
+
degreeU: this.surface.degreeU,
|
|
94466
|
+
degreeV: this.surface.degreeV,
|
|
94467
|
+
knotsU: this.surface.knotsU,
|
|
94468
|
+
knotsV: this.surface.knotsV,
|
|
94469
|
+
thickness: wall,
|
|
94470
|
+
resolution
|
|
94471
|
+
});
|
|
94472
|
+
}
|
|
94473
|
+
/** Per-edge continuity match against a neighbor (returns a NEW Sheet). */
|
|
94474
|
+
matchEdge(edge) {
|
|
94475
|
+
if (edge.sheet !== this) throw new Error("Sheet.matchEdge: the edge must belong to this sheet.");
|
|
94476
|
+
return new MatchEdgeBuilder(this, edge);
|
|
94477
|
+
}
|
|
94478
|
+
}
|
|
94479
|
+
function clamp01(t) {
|
|
94480
|
+
return t < 0 ? 0 : t > 1 ? 1 : t;
|
|
94481
|
+
}
|
|
94482
|
+
class CurveNetBuilder {
|
|
94483
|
+
constructor() {
|
|
94484
|
+
__publicField(this, "lengthwiseCurves", []);
|
|
94485
|
+
__publicField(this, "crosswiseCurves", []);
|
|
94486
|
+
__publicField(this, "railCurves", []);
|
|
94487
|
+
__publicField(this, "cageGrid", null);
|
|
94488
|
+
__publicField(this, "degU");
|
|
94489
|
+
__publicField(this, "degV");
|
|
94490
|
+
__publicField(this, "built", null);
|
|
94491
|
+
}
|
|
94492
|
+
lengthwise(...curves) {
|
|
94493
|
+
this.lengthwiseCurves = curves;
|
|
94494
|
+
this.built = null;
|
|
94495
|
+
return this;
|
|
94496
|
+
}
|
|
94497
|
+
crosswise(...curves) {
|
|
94498
|
+
this.crosswiseCurves = curves;
|
|
94499
|
+
this.built = null;
|
|
94500
|
+
return this;
|
|
94501
|
+
}
|
|
94502
|
+
alongRails(railA, railB) {
|
|
94503
|
+
this.railCurves = [railA, railB];
|
|
94504
|
+
this.built = null;
|
|
94505
|
+
return this;
|
|
94506
|
+
}
|
|
94507
|
+
sections(...curves) {
|
|
94508
|
+
this.crosswiseCurves = curves;
|
|
94509
|
+
this.built = null;
|
|
94510
|
+
return this;
|
|
94511
|
+
}
|
|
94512
|
+
cage(grid) {
|
|
94513
|
+
if (!Array.isArray(grid) || grid.length < 2 || !Array.isArray(grid[0]) || grid[0].length < 2) {
|
|
94514
|
+
throw new Error("Surface.Net().cage: grid must be at least a 2x2 array of [x,y,z] points.");
|
|
94515
|
+
}
|
|
94516
|
+
const cols = grid[0].length;
|
|
94517
|
+
this.cageGrid = grid.map((row, k2) => {
|
|
94518
|
+
if (row.length !== cols) throw new Error(`Surface.Net().cage: row ${k2} has ${row.length} points, expected ${cols}.`);
|
|
94519
|
+
return row.map((p2, l) => requireFiniteVec(p2, `cage[${k2}][${l}]`));
|
|
94520
|
+
});
|
|
94521
|
+
this.built = null;
|
|
94522
|
+
return this;
|
|
94523
|
+
}
|
|
94524
|
+
degree(u2, v) {
|
|
94525
|
+
if (!Number.isInteger(u2) || u2 < 1) throw new Error(`Surface.Net().degree: degree must be a positive integer, got ${u2}`);
|
|
94526
|
+
this.degU = u2;
|
|
94527
|
+
this.degV = v === void 0 ? u2 : v;
|
|
94528
|
+
if (v !== void 0 && (!Number.isInteger(v) || v < 1))
|
|
94529
|
+
throw new Error(`Surface.Net().degree: degree must be a positive integer, got ${v}`);
|
|
94530
|
+
this.built = null;
|
|
94531
|
+
return this;
|
|
94532
|
+
}
|
|
94533
|
+
/** Build (once) and return the Sheet. */
|
|
94534
|
+
toSheet() {
|
|
94535
|
+
if (this.built) return this.built;
|
|
94536
|
+
const grid = this.buildGrid();
|
|
94537
|
+
const m2 = grid.length - 1;
|
|
94538
|
+
const n = grid[0].length - 1;
|
|
94539
|
+
const degreeU = Math.max(1, Math.min(this.degU ?? 3, m2));
|
|
94540
|
+
const degreeV = Math.max(1, Math.min(this.degV ?? 3, n));
|
|
94541
|
+
const surface = buildGordonFromGrid(grid, degreeU, degreeV);
|
|
94542
|
+
this.built = new Sheet(surface);
|
|
94543
|
+
return this.built;
|
|
94544
|
+
}
|
|
94545
|
+
buildGrid() {
|
|
94546
|
+
if (this.cageGrid) return this.cageGrid;
|
|
94547
|
+
const lengthwise = this.railCurves.length > 0 ? this.railCurves : this.lengthwiseCurves;
|
|
94548
|
+
const crosswise = this.crosswiseCurves;
|
|
94549
|
+
const SAMPLES = 17;
|
|
94550
|
+
if (lengthwise.length >= 2) {
|
|
94551
|
+
const samplers = lengthwise.map((c2, l) => toSampler(c2, `lengthwise[${l}]`));
|
|
94552
|
+
const grid = [];
|
|
94553
|
+
for (let i = 0; i < SAMPLES; i++) {
|
|
94554
|
+
const u2 = i / (SAMPLES - 1);
|
|
94555
|
+
grid.push(samplers.map((s) => s(u2)));
|
|
94556
|
+
}
|
|
94557
|
+
return grid;
|
|
94558
|
+
}
|
|
94559
|
+
if (crosswise.length >= 2) {
|
|
94560
|
+
const samplers = crosswise.map((c2, k2) => toSampler(c2, `crosswise[${k2}]`));
|
|
94561
|
+
const grid = [];
|
|
94562
|
+
for (const s of samplers) grid.push(sampleCurve(s, SAMPLES));
|
|
94563
|
+
return grid;
|
|
94564
|
+
}
|
|
94565
|
+
throw new Error(
|
|
94566
|
+
"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."
|
|
94567
|
+
);
|
|
94568
|
+
}
|
|
94569
|
+
// ── Sheet delegation (build on first access) ──────────────────────────────
|
|
94570
|
+
get frontEdge() {
|
|
94571
|
+
return this.toSheet().frontEdge;
|
|
94572
|
+
}
|
|
94573
|
+
get rearEdge() {
|
|
94574
|
+
return this.toSheet().rearEdge;
|
|
94575
|
+
}
|
|
94576
|
+
get leftEdge() {
|
|
94577
|
+
return this.toSheet().leftEdge;
|
|
94578
|
+
}
|
|
94579
|
+
get rightEdge() {
|
|
94580
|
+
return this.toSheet().rightEdge;
|
|
94581
|
+
}
|
|
94582
|
+
get surface() {
|
|
94583
|
+
return this.toSheet().surface;
|
|
94584
|
+
}
|
|
94585
|
+
pointAt(u2, v) {
|
|
94586
|
+
return this.toSheet().pointAt(u2, v);
|
|
94587
|
+
}
|
|
94588
|
+
normalAt(u2, v) {
|
|
94589
|
+
return this.toSheet().normalAt(u2, v);
|
|
94590
|
+
}
|
|
94591
|
+
curvatureAt(u2, v) {
|
|
94592
|
+
return this.toSheet().curvatureAt(u2, v);
|
|
94593
|
+
}
|
|
94594
|
+
thicken(wall, options) {
|
|
94595
|
+
return this.toSheet().thicken(wall, options);
|
|
94596
|
+
}
|
|
94597
|
+
matchEdge(edge) {
|
|
94598
|
+
return this.toSheet().matchEdge(edge);
|
|
94599
|
+
}
|
|
94600
|
+
}
|
|
94601
|
+
function createCurveNet() {
|
|
94602
|
+
return new CurveNetBuilder();
|
|
94603
|
+
}
|
|
94604
|
+
class MatchEdgeBuilder {
|
|
94605
|
+
constructor(sheet, edge) {
|
|
94606
|
+
this.sheet = sheet;
|
|
94607
|
+
this.edge = edge;
|
|
94608
|
+
}
|
|
94609
|
+
toG0(neighbor) {
|
|
94610
|
+
return applyEdgeMatch(this.sheet, this.edge, neighbor, 0);
|
|
94611
|
+
}
|
|
94612
|
+
toG1(neighbor) {
|
|
94613
|
+
return applyEdgeMatch(this.sheet, this.edge, neighbor, 1);
|
|
94614
|
+
}
|
|
94615
|
+
toG2(neighbor) {
|
|
94616
|
+
return applyEdgeMatch(this.sheet, this.edge, neighbor, 2);
|
|
94617
|
+
}
|
|
94618
|
+
}
|
|
94619
|
+
function boundaryRows(surf, fixed, value, depth) {
|
|
94620
|
+
const rows = [];
|
|
94621
|
+
if (fixed === "u") {
|
|
94622
|
+
const nU = surf.grid.length;
|
|
94623
|
+
for (let d2 = 0; d2 <= depth; d2++) {
|
|
94624
|
+
const i = value === 0 ? d2 : nU - 1 - d2;
|
|
94625
|
+
rows.push(surf.grid[i].map((p2) => [p2[0], p2[1], p2[2]]));
|
|
94626
|
+
}
|
|
94627
|
+
} else {
|
|
94628
|
+
const nV = surf.grid[0].length;
|
|
94629
|
+
for (let d2 = 0; d2 <= depth; d2++) {
|
|
94630
|
+
const j = value === 0 ? d2 : nV - 1 - d2;
|
|
94631
|
+
rows.push(surf.grid.map((p2) => [p2[j][0], p2[j][1], p2[j][2]]));
|
|
94632
|
+
}
|
|
94633
|
+
}
|
|
94634
|
+
return rows;
|
|
94635
|
+
}
|
|
94636
|
+
function setBoundaryRow(surf, fixed, value, depth, row) {
|
|
94637
|
+
if (fixed === "u") {
|
|
94638
|
+
const nU = surf.grid.length;
|
|
94639
|
+
const i = value === 0 ? depth : nU - 1 - depth;
|
|
94640
|
+
for (let l = 0; l < surf.grid[i].length; l++) surf.grid[i][l] = [row[l][0], row[l][1], row[l][2]];
|
|
94641
|
+
} else {
|
|
94642
|
+
const nV = surf.grid[0].length;
|
|
94643
|
+
const j = value === 0 ? depth : nV - 1 - depth;
|
|
94644
|
+
for (let k2 = 0; k2 < surf.grid.length; k2++) surf.grid[k2][j] = [row[k2][0], row[k2][1], row[k2][2]];
|
|
94645
|
+
}
|
|
94646
|
+
}
|
|
94647
|
+
function cloneSurface(surf) {
|
|
94648
|
+
return {
|
|
94649
|
+
grid: surf.grid.map((row) => row.map((p2) => [p2[0], p2[1], p2[2]])),
|
|
94650
|
+
knotsU: [...surf.knotsU],
|
|
94651
|
+
knotsV: [...surf.knotsV],
|
|
94652
|
+
degreeU: surf.degreeU,
|
|
94653
|
+
degreeV: surf.degreeV
|
|
94654
|
+
};
|
|
94655
|
+
}
|
|
94656
|
+
function applyEdgeMatch(sheet, edge, neighbor, order) {
|
|
94657
|
+
const result = cloneSurface(sheet.surface);
|
|
94658
|
+
const my = boundaryRows(result, edge.fixed, edge.value, order);
|
|
94659
|
+
const their = boundaryRows(neighbor.sheet.surface, neighbor.fixed, neighbor.value, order);
|
|
94660
|
+
if (my[0].length !== their[0].length) {
|
|
94661
|
+
throw new Error(
|
|
94662
|
+
`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.`
|
|
94663
|
+
);
|
|
94664
|
+
}
|
|
94665
|
+
const len2 = my[0].length;
|
|
94666
|
+
const b0 = their[0].map((p2) => [p2[0], p2[1], p2[2]]);
|
|
94667
|
+
setBoundaryRow(result, edge.fixed, edge.value, 0, b0);
|
|
94668
|
+
if (order === 0) return new Sheet(result);
|
|
94669
|
+
const b1 = [];
|
|
94670
|
+
for (let i = 0; i < len2; i++) {
|
|
94671
|
+
const p0 = their[0][i];
|
|
94672
|
+
const p1 = their[1][i];
|
|
94673
|
+
b1.push([2 * p0[0] - p1[0], 2 * p0[1] - p1[1], 2 * p0[2] - p1[2]]);
|
|
94674
|
+
}
|
|
94675
|
+
setBoundaryRow(result, edge.fixed, edge.value, 1, b1);
|
|
94676
|
+
if (order === 1) return new Sheet(result);
|
|
94677
|
+
const b22 = [];
|
|
94678
|
+
for (let i = 0; i < len2; i++) {
|
|
94679
|
+
const p0 = their[0][i];
|
|
94680
|
+
const p1 = their[1][i];
|
|
94681
|
+
const p2 = their[2][i];
|
|
94682
|
+
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]]);
|
|
94683
|
+
}
|
|
94684
|
+
setBoundaryRow(result, edge.fixed, edge.value, 2, b22);
|
|
94685
|
+
return new Sheet(result);
|
|
94686
|
+
}
|
|
94687
|
+
function edgeJet(edge, t) {
|
|
94688
|
+
const surf = edge.sheet.surface;
|
|
94689
|
+
if (edge.fixed === "u") {
|
|
94690
|
+
const j2 = evalSurfaceJet(surf, edge.value, clamp01(t));
|
|
94691
|
+
return { point: j2.S, cross: j2.Su, cross2: j2.Suu };
|
|
94692
|
+
}
|
|
94693
|
+
const j = evalSurfaceJet(surf, clamp01(t), edge.value);
|
|
94694
|
+
return { point: j.S, cross: j.Sv, cross2: j.Svv };
|
|
94695
|
+
}
|
|
94696
|
+
function sub3(a2, b) {
|
|
94697
|
+
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
94698
|
+
}
|
|
94699
|
+
function len3(a2) {
|
|
94700
|
+
return Math.hypot(a2[0], a2[1], a2[2]);
|
|
94701
|
+
}
|
|
94702
|
+
function unit3(a2) {
|
|
94703
|
+
const l = len3(a2) || 1;
|
|
94704
|
+
return [a2[0] / l, a2[1] / l, a2[2] / l];
|
|
94705
|
+
}
|
|
94706
|
+
function edgeMatchReport(edgeA, edgeB, samples = 24) {
|
|
94707
|
+
let maxPositionGap = 0;
|
|
94708
|
+
let maxTangentAngleDeg = 0;
|
|
94709
|
+
let maxCurvatureRelErr = 0;
|
|
94710
|
+
for (let i = 0; i <= samples; i++) {
|
|
94711
|
+
const t = i / samples;
|
|
94712
|
+
const a2 = edgeJet(edgeA, t);
|
|
94713
|
+
const b = edgeJet(edgeB, t);
|
|
94714
|
+
maxPositionGap = Math.max(maxPositionGap, len3(sub3(a2.point, b.point)));
|
|
94715
|
+
const ua = unit3(a2.cross);
|
|
94716
|
+
const ub = unit3(b.cross);
|
|
94717
|
+
const dot2 = Math.max(-1, Math.min(1, ua[0] * ub[0] + ua[1] * ub[1] + ua[2] * ub[2]));
|
|
94718
|
+
const angle = Math.acos(Math.abs(dot2)) * 180 / Math.PI;
|
|
94719
|
+
maxTangentAngleDeg = Math.max(maxTangentAngleDeg, angle);
|
|
94720
|
+
const la = len3(a2.cross) ** 2 || 1;
|
|
94721
|
+
const lb = len3(b.cross) ** 2 || 1;
|
|
94722
|
+
const ka = (a2.cross2[0] * ua[0] + a2.cross2[1] * ua[1] + a2.cross2[2] * ua[2]) / la;
|
|
94723
|
+
const kb = (b.cross2[0] * ub[0] + b.cross2[1] * ub[1] + b.cross2[2] * ub[2]) / lb;
|
|
94724
|
+
maxCurvatureRelErr = Math.max(maxCurvatureRelErr, Math.abs(ka - kb) / (Math.abs(ka) + Math.abs(kb) + 1e-9));
|
|
94725
|
+
}
|
|
94726
|
+
return { maxPositionGap, maxTangentAngleDeg, maxCurvatureRelErr };
|
|
94727
|
+
}
|
|
94728
|
+
function bridgeBetween(edgeA, edgeB) {
|
|
94729
|
+
return new BridgeBuilder(edgeA, edgeB);
|
|
94730
|
+
}
|
|
94731
|
+
class BridgeBuilder {
|
|
94732
|
+
constructor(edgeA, edgeB) {
|
|
94733
|
+
__publicField(this, "bulgeA", 0.5);
|
|
94734
|
+
__publicField(this, "bulgeB", 0.5);
|
|
94735
|
+
this.edgeA = edgeA;
|
|
94736
|
+
this.edgeB = edgeB;
|
|
94737
|
+
}
|
|
94738
|
+
/** Tune the influence of each side (Rhino-style bulge). */
|
|
94739
|
+
bulge(a2, b) {
|
|
94740
|
+
if (!Number.isFinite(a2) || !Number.isFinite(b)) throw new Error("Surfaces.bridge.bulge: both factors must be finite.");
|
|
94741
|
+
this.bulgeA = a2;
|
|
94742
|
+
this.bulgeB = b;
|
|
94743
|
+
return this;
|
|
94744
|
+
}
|
|
94745
|
+
g0() {
|
|
94746
|
+
return this.build(0);
|
|
94747
|
+
}
|
|
94748
|
+
g1() {
|
|
94749
|
+
return this.build(1);
|
|
94750
|
+
}
|
|
94751
|
+
g2() {
|
|
94752
|
+
return this.build(2);
|
|
94753
|
+
}
|
|
94754
|
+
build(order) {
|
|
94755
|
+
const SAMPLES = 21;
|
|
94756
|
+
const CROSS = 11;
|
|
94757
|
+
const cage = [];
|
|
94758
|
+
for (let i = 0; i < SAMPLES; i++) {
|
|
94759
|
+
const t = i / (SAMPLES - 1);
|
|
94760
|
+
const a2 = edgeJet(this.edgeA, t);
|
|
94761
|
+
const b = edgeJet(this.edgeB, t);
|
|
94762
|
+
const chord = len3(sub3(b.point, a2.point));
|
|
94763
|
+
const toward = unit3(sub3(b.point, a2.point));
|
|
94764
|
+
const tA = orientToward(unit3(a2.cross), toward);
|
|
94765
|
+
const tB = orientToward(unit3(b.cross), [-toward[0], -toward[1], -toward[2]]);
|
|
94766
|
+
const poles = bridgePoles(a2.point, b.point, tA, tB, chord, this.bulgeA, this.bulgeB, order);
|
|
94767
|
+
const row = [];
|
|
94768
|
+
for (let c2 = 0; c2 < CROSS; c2++) row.push(deCasteljau(poles, c2 / (CROSS - 1)));
|
|
94769
|
+
cage.push(row);
|
|
94770
|
+
}
|
|
94771
|
+
return new CurveNetBuilder().cage(cage).degree(Math.min(3, SAMPLES - 1), Math.min(2 * order + 1 || 1, CROSS - 1)).toSheet();
|
|
94772
|
+
}
|
|
94773
|
+
}
|
|
94774
|
+
function orientToward(v, toward) {
|
|
94775
|
+
const dot2 = v[0] * toward[0] + v[1] * toward[1] + v[2] * toward[2];
|
|
94776
|
+
return dot2 < 0 ? [-v[0], -v[1], -v[2]] : v;
|
|
94777
|
+
}
|
|
94778
|
+
function bridgePoles(a2, b, tA, tB, chord, bulgeA, bulgeB, order) {
|
|
94779
|
+
if (order === 0) return [a2, b];
|
|
94780
|
+
const dA = chord * bulgeA / (order === 1 ? 3 : 5);
|
|
94781
|
+
const dB = chord * bulgeB / (order === 1 ? 3 : 5);
|
|
94782
|
+
const a1 = [a2[0] + tA[0] * dA, a2[1] + tA[1] * dA, a2[2] + tA[2] * dA];
|
|
94783
|
+
const b1 = [b[0] + tB[0] * dB, b[1] + tB[1] * dB, b[2] + tB[2] * dB];
|
|
94784
|
+
if (order === 1) return [a2, a1, b1, b];
|
|
94785
|
+
const a22 = [a1[0] + tA[0] * dA, a1[1] + tA[1] * dA, a1[2] + tA[2] * dA];
|
|
94786
|
+
const b22 = [b1[0] + tB[0] * dB, b1[1] + tB[1] * dB, b1[2] + tB[2] * dB];
|
|
94787
|
+
return [a2, a1, a22, b22, b1, b];
|
|
94788
|
+
}
|
|
94789
|
+
function deCasteljau(poles, s) {
|
|
94790
|
+
let pts = poles.map((p2) => [p2[0], p2[1], p2[2]]);
|
|
94791
|
+
while (pts.length > 1) {
|
|
94792
|
+
const next = [];
|
|
94793
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
94794
|
+
next.push([
|
|
94795
|
+
pts[i][0] + (pts[i + 1][0] - pts[i][0]) * s,
|
|
94796
|
+
pts[i][1] + (pts[i + 1][1] - pts[i][1]) * s,
|
|
94797
|
+
pts[i][2] + (pts[i + 1][2] - pts[i][2]) * s
|
|
94798
|
+
]);
|
|
94799
|
+
}
|
|
94800
|
+
pts = next;
|
|
94801
|
+
}
|
|
94802
|
+
return pts[0];
|
|
94803
|
+
}
|
|
94118
94804
|
const CORNER_Y_ALPHA_ISSUE_URL = "https://github.com/KoStard/forgecad-private/issues/162";
|
|
94119
94805
|
function isVec3Array(value) {
|
|
94120
94806
|
return Array.isArray(value) && (value.length === 0 || Array.isArray(value[0]));
|
|
@@ -95503,7 +96189,16 @@ const Surface = {
|
|
|
95503
96189
|
"Surface.MatchEdge",
|
|
95504
96190
|
"Surface.Match()",
|
|
95505
96191
|
(shape, options) => Surface.Match(shape, options)
|
|
95506
|
-
)
|
|
96192
|
+
),
|
|
96193
|
+
/**
|
|
96194
|
+
* Begin a curve-network (Gordon) surface — the class-A keystone. Chain
|
|
96195
|
+
* `.lengthwise(...)/.crosswise(...)` (or `.alongRails(a,b).sections(...)`, or
|
|
96196
|
+
* `.cage(grid)`), then `.thicken(wall)` to get a solid Shape. Returns a fluent
|
|
96197
|
+
* `Sheet` builder with analytic point/normal/curvature queries and named edges.
|
|
96198
|
+
*/
|
|
96199
|
+
Net() {
|
|
96200
|
+
return createCurveNet();
|
|
96201
|
+
}
|
|
95507
96202
|
};
|
|
95508
96203
|
const Blend = {
|
|
95509
96204
|
Edge(options) {
|
|
@@ -95523,6 +96218,14 @@ const Blend = {
|
|
|
95523
96218
|
Surface(options) {
|
|
95524
96219
|
return Surface.Fill(options);
|
|
95525
96220
|
},
|
|
96221
|
+
/**
|
|
96222
|
+
* Build a transition strip between two `Surface.Net` sheet edges. Chain
|
|
96223
|
+
* `.bulge(a, b)` then `.g0()/.g1()/.g2()` for the continuity order. Returns a
|
|
96224
|
+
* `Sheet`; verify the seam with `Analysis.EdgeMatch`.
|
|
96225
|
+
*/
|
|
96226
|
+
Bridge(edgeA, edgeB) {
|
|
96227
|
+
return bridgeBetween(edgeA, edgeB);
|
|
96228
|
+
},
|
|
95526
96229
|
/**
|
|
95527
96230
|
* @alpha
|
|
95528
96231
|
* Current implementation uses continuity-controlled edge fillets on solid edges.
|
|
@@ -95557,6 +96260,14 @@ const Analysis = {
|
|
|
95557
96260
|
SurfaceContinuity(shape, options = {}) {
|
|
95558
96261
|
return evaluateEdgeContinuityReport(shape, options, "Analysis.SurfaceContinuity()");
|
|
95559
96262
|
},
|
|
96263
|
+
/**
|
|
96264
|
+
* Measure G0/G1/G2 agreement between two `Surface.Net` sheet edges: worst
|
|
96265
|
+
* position gap, cross-boundary tangent angle (0 = G1), and normal-curvature
|
|
96266
|
+
* mismatch (0 = G2). The reflection/fairness check for matched panel seams.
|
|
96267
|
+
*/
|
|
96268
|
+
EdgeMatch(edgeA, edgeB, options = {}) {
|
|
96269
|
+
return edgeMatchReport(edgeA, edgeB, options.samples);
|
|
96270
|
+
},
|
|
95560
96271
|
CurvatureComb(input, options = {}) {
|
|
95561
96272
|
if (input instanceof NurbsCurve3D) {
|
|
95562
96273
|
const count = Math.max(8, options.samples ?? 32);
|
|
@@ -99001,22 +99712,10 @@ function roundNum(n, digits = 4) {
|
|
|
99001
99712
|
return Number.isFinite(n) ? n.toFixed(digits).replace(/\.?0+$/, "") : String(n);
|
|
99002
99713
|
}
|
|
99003
99714
|
const COLLISION_OVERLAP_VOLUME_TOLERANCE = 1e-6;
|
|
99004
|
-
function meshDerivedManifoldBackend(shape) {
|
|
99005
|
-
const mesh = getShapeRuntimeBackend(shape).getMesh();
|
|
99006
|
-
return reconstructBackendFromMesh({
|
|
99007
|
-
numProp: mesh.numProp,
|
|
99008
|
-
triVerts: mesh.triVerts,
|
|
99009
|
-
vertProperties: mesh.vertProperties,
|
|
99010
|
-
mergeFromVert: mesh.mergeFromVert ?? new Uint32Array(),
|
|
99011
|
-
mergeToVert: mesh.mergeToVert ?? new Uint32Array()
|
|
99012
|
-
});
|
|
99013
|
-
}
|
|
99014
99715
|
function backendForMinGap(shape) {
|
|
99015
99716
|
const backend = getShapeRuntimeBackend(shape);
|
|
99016
99717
|
if (isManifoldCapableBackend(backend)) return { kind: "manifold", backend, method: "exact", dispose: false };
|
|
99017
|
-
|
|
99018
|
-
return { kind: "mesh", backend, method: "mesh-derived", dispose: false };
|
|
99019
|
-
return { kind: "manifold", backend: meshDerivedManifoldBackend(shape), method: "mesh-derived", dispose: true };
|
|
99718
|
+
return { kind: "mesh", backend, method: "mesh-derived", dispose: false };
|
|
99020
99719
|
}
|
|
99021
99720
|
function meshHasPointInsideSdf(source, target) {
|
|
99022
99721
|
const mesh = source.getMesh();
|
|
@@ -100189,7 +100888,6 @@ function resetExecutionSession(logs) {
|
|
|
100189
100888
|
resetHighlights();
|
|
100190
100889
|
resetBom();
|
|
100191
100890
|
resetSheetStock();
|
|
100192
|
-
resetRobotExport();
|
|
100193
100891
|
resetCutPlanes();
|
|
100194
100892
|
resetRenderLabels();
|
|
100195
100893
|
resetCameraTrajectory();
|
|
@@ -100272,7 +100970,6 @@ function collectSuccessfulExecutionSnapshot(args) {
|
|
|
100272
100970
|
jointsView: getCollectedJointsView(),
|
|
100273
100971
|
viewConfig: getCollectedViewConfig(),
|
|
100274
100972
|
sceneConfig: getCollectedScene(),
|
|
100275
|
-
robotExport: getCollectedRobotExport(),
|
|
100276
100973
|
quality: args.quality,
|
|
100277
100974
|
logs: args.logs.slice(),
|
|
100278
100975
|
verifications: getCollectedVerifications(),
|
|
@@ -100297,7 +100994,6 @@ function collectFailedExecutionSnapshot(args) {
|
|
|
100297
100994
|
jointsView: getCollectedJointsView(),
|
|
100298
100995
|
viewConfig: getCollectedViewConfig(),
|
|
100299
100996
|
sceneConfig: getCollectedScene(),
|
|
100300
|
-
robotExport: getCollectedRobotExport(),
|
|
100301
100997
|
quality: args.quality,
|
|
100302
100998
|
logs: args.logs.slice(),
|
|
100303
100999
|
verifications: getCollectedVerifications(),
|
|
@@ -312858,7 +313554,6 @@ function executeFile(code, fileName, allFiles, visited, scope = {}, options, exe
|
|
|
312858
313554
|
sketchToDxf,
|
|
312859
313555
|
bom,
|
|
312860
313556
|
sheetStock,
|
|
312861
|
-
robotExport,
|
|
312862
313557
|
Sim,
|
|
312863
313558
|
group,
|
|
312864
313559
|
ShapeGroup,
|