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.
Files changed (95) hide show
  1. package/README.md +7 -6
  2. package/dist/assets/{AdminPage-CHY6ZN-p.js → AdminPage-CK7ObBz3.js} +1 -1
  3. package/dist/assets/{BenchmarkPage-BcRT5iGN.js → BenchmarkPage-Ds7Z2doN.js} +1 -1
  4. package/dist/assets/{BlogPage-BssBbnb-.js → BlogPage-DlPbpt6A.js} +1 -1
  5. package/dist/assets/{DocsPage-DsvdiRNK.js → DocsPage-vZb3b3Y0.js} +9 -14
  6. package/dist/assets/{EditorApp-BpjZgzk0.css → EditorApp-C5f24ZN9.css} +8 -0
  7. package/dist/assets/{EditorApp-Bfd3jbtC.js → EditorApp-HLoKfe15.js} +141 -12
  8. package/dist/assets/{EmbedViewer-D5t8WamV.js → EmbedViewer--KnqBKrJ.js} +2 -2
  9. package/dist/assets/{LandingPageProofDriven-DbN7o-Be.js → LandingPageProofDriven-C_LssmnA.js} +1 -1
  10. package/dist/assets/{LegalPage-DNGrrY0p.js → LegalPage-DGsyo4n1.js} +1 -1
  11. package/dist/assets/{PricingPage-Nczr3pRz.js → PricingPage-BOE27B-R.js} +1 -1
  12. package/dist/assets/{SettingsPage-DZlyu4d4.js → SettingsPage-f47cnk39.js} +1 -1
  13. package/dist/assets/{app-C9ct2hRD.js → app-D6ccu2Xx.js} +6854 -7373
  14. package/dist/assets/{backendInit-ymjonyQp.js → backendInit-DbTkQN9J.js} +2557 -809
  15. package/dist/assets/cli/{render-B_0lQwKU.js → render-BsngirjC.js} +114 -9
  16. package/dist/assets/{constructionHistoryWorker-CZ42Dksy.js → constructionHistoryWorker-PCwXrTDB.js} +175 -36
  17. package/dist/assets/{evalWorker-C2pm8LHP.js → evalWorker-CS63PfZu.js} +1125 -447
  18. package/dist/assets/{forgecad_geometry-BlMtqluF.js → forgecad_geometry-CZ_IfuvA.js} +1 -9
  19. package/dist/assets/{forgecad_geometry_bg-BllP_WiL.wasm → forgecad_geometry_bg-C3rQHfwg.wasm} +0 -0
  20. package/dist/assets/{inspectWorker-D5T5VbfK.js → inspectWorker-Y4cOzNyA.js} +4345 -373
  21. package/dist/assets/{jointPose-4r8ed8_5.js → jointPose-AMvCywzS.js} +1 -1
  22. package/dist/assets/{manifold-C4r6B-XY.js → manifold-CBry38ly.js} +2 -2
  23. package/dist/assets/{manifold-5PP1eGLN.js → manifold-Crd_F2qx.js} +1 -1
  24. package/dist/assets/{manifold-DjBkyIc8.js → manifold-k2kRcc85.js} +1 -1
  25. package/dist/assets/{reportWorker-CwenM7wB.js → reportWorker-CWvn0CEv.js} +1095 -400
  26. package/dist/cli/render.html +1 -1
  27. package/dist/docs/index.html +2 -2
  28. package/dist/docs-raw/AI/usage.md +2 -4
  29. package/dist/docs-raw/CLI.md +9 -7
  30. package/dist/docs-raw/README.md +1 -1
  31. package/dist/docs-raw/component-model.md +1 -1
  32. package/dist/docs-raw/generated/assembly.md +1 -1
  33. package/dist/docs-raw/generated/concepts.md +5 -3
  34. package/dist/docs-raw/generated/core.md +70 -1
  35. package/dist/docs-raw/generated/curves.md +8 -1
  36. package/dist/docs-raw/generated/output.md +0 -64
  37. package/dist/docs-raw/generated/runtime-names.md +6 -6
  38. package/dist/docs-raw/generated/viewport.md +3 -12
  39. package/dist/docs-raw/guides/inspection-bundles.md +1 -1
  40. package/dist/docs-raw/simulation-workflow.md +58 -0
  41. package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -1
  42. package/dist/docs-raw/skills/forgecad-image-replicator.md +2 -2
  43. package/dist/docs-raw/skills/forgecad-mujoco-verify.md +78 -0
  44. package/dist/docs-raw/skills/forgecad-spec-by-walking-through-it.md +145 -0
  45. package/dist/docs-raw/skills/forgecad-visual-spec.md +1 -1
  46. package/dist/docs-raw/skills/forgecad.md +24 -24
  47. package/dist/docs-raw/skills/index.md +2 -3
  48. package/dist/index.html +1 -1
  49. package/dist/sitemap.xml +15 -15
  50. package/dist-cli/{check-compiler-SP7FAL7R.js → check-compiler-HPF2T2FS.js} +1 -1
  51. package/dist-cli/{check-query-propagation-BRLSHP22.js → check-query-propagation-HYSLTXAB.js} +1 -1
  52. package/dist-cli/{chunk-RQQ42YCP.js → chunk-WLUKAW3H.js} +1025 -158
  53. package/dist-cli/forgecad.js +2621 -232
  54. package/dist-cli/{forgecad_geometry-7TVSNVUB.js → forgecad_geometry-2IMYCUWW.js} +0 -8
  55. package/dist-cli/forgecad_geometry_bg.wasm +0 -0
  56. package/dist-skill/CONTEXT.md +85 -73
  57. package/dist-skill/SKILL.md +1 -1
  58. package/dist-skill/docs/CLI.md +9 -7
  59. package/dist-skill/docs/generated/assembly.md +1 -1
  60. package/dist-skill/docs/generated/core.md +70 -1
  61. package/dist-skill/docs/generated/curves.md +8 -1
  62. package/dist-skill/docs/generated/output.md +0 -64
  63. package/dist-skill/docs/generated/runtime-names.md +6 -6
  64. package/dist-skill/docs/generated/viewport.md +3 -12
  65. package/dist-skill/docs/guides/inspection-bundles.md +1 -1
  66. package/dist-skill/library/README.md +2 -3
  67. package/dist-skill/library/forgecad-blockout-model/SKILL.md +1 -1
  68. package/dist-skill/library/forgecad-image-replicator/SKILL.md +2 -2
  69. package/dist-skill/library/forgecad-mujoco-verify/SKILL.md +66 -0
  70. package/dist-skill/library/forgecad-mujoco-verify/scripts/mujoco_verify.py +385 -0
  71. package/dist-skill/library/forgecad-spec-by-walking-through-it/SKILL.md +132 -0
  72. package/dist-skill/library/forgecad-visual-spec/SKILL.md +1 -1
  73. package/dist-skill/website/skills/forgecad-blockout-model.md +1 -1
  74. package/dist-skill/website/skills/forgecad-image-replicator.md +2 -2
  75. package/dist-skill/website/skills/forgecad-mujoco-verify.md +78 -0
  76. package/dist-skill/website/skills/forgecad-spec-by-walking-through-it.md +145 -0
  77. package/dist-skill/website/skills/forgecad-visual-spec.md +1 -1
  78. package/dist-skill/website/skills/forgecad.md +24 -24
  79. package/dist-skill/website/skills/index.md +2 -3
  80. package/examples/analysis/clearance-fit.forge.js +31 -0
  81. package/examples/analysis/lever-arm-actuator.forge.js +43 -0
  82. package/examples/analysis/tipping-tripod.forge.js +35 -0
  83. package/examples/products/sportscar.forge.js +77 -0
  84. package/package.json +1 -3
  85. package/dist/docs-raw/skills/forgecad-high-level-spec.md +0 -101
  86. package/dist/docs-raw/skills/forgecad-lld.md +0 -41
  87. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +0 -63
  88. package/dist-skill/library/forgecad-high-level-spec/SKILL.md +0 -94
  89. package/dist-skill/library/forgecad-lld/SKILL.md +0 -34
  90. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +0 -50
  91. package/dist-skill/website/skills/forgecad-high-level-spec.md +0 -101
  92. package/dist-skill/website/skills/forgecad-lld.md +0 -41
  93. package/dist-skill/website/skills/forgecad-prepare-prompt.md +0 -63
  94. /package/dist-skill/library/{forgecad-prepare-prompt → forgecad-spec-by-walking-through-it}/references/default-profiles.md +0 -0
  95. /package/dist-skill/library/{forgecad-prepare-prompt → forgecad-spec-by-walking-through-it}/references/master-prompt.md +0 -0
@@ -10475,7 +10475,7 @@ async function initTruckGeometryWasm() {
10475
10475
  if (_initPromise) return _initPromise;
10476
10476
  _initPromise = (async () => {
10477
10477
  try {
10478
- const geometryModule = await import("./forgecad_geometry-7TVSNVUB.js");
10478
+ const geometryModule = await import("./forgecad_geometry-2IMYCUWW.js");
10479
10479
  const isNode = isNodeRuntime();
10480
10480
  if (isNode) {
10481
10481
  const { readFileSync: readFileSync3, existsSync: existsSync3 } = await import("fs");
@@ -11427,23 +11427,62 @@ var NurbsSurface = class {
11427
11427
  return [wx / wSum, wy / wSum, wz / wSum];
11428
11428
  }
11429
11429
  /**
11430
- * Evaluate the surface normal at (u, v) via cross product of partial derivatives.
11430
+ * Evaluate the surface unit normal at (u, v) from analytic first derivatives.
11431
+ *
11432
+ * Uses Algorithm A2.3 basis-function derivatives with the rational quotient
11433
+ * rule, so the normal is exact (no finite-difference epsilon, no error near
11434
+ * the boundary). Constant chain-rule factors from the parameter remap scale Su
11435
+ * and Sv positively and cancel under normalization, so they are omitted.
11431
11436
  */
11432
11437
  normalAt(u, v) {
11433
- const eps = 1e-5;
11434
- const u0 = Math.max(0, u - eps), u1 = Math.min(1, u + eps);
11435
- const v0 = Math.max(0, v - eps), v1 = Math.min(1, v + eps);
11436
- const pu = this.pointAt(u1, v), pmu = this.pointAt(u0, v);
11437
- const pv = this.pointAt(u, v1), pmv = this.pointAt(u, v0);
11438
- const du = [pu[0] - pmu[0], pu[1] - pmu[1], pu[2] - pmu[2]];
11439
- const dv = [pv[0] - pmv[0], pv[1] - pmv[1], pv[2] - pmv[2]];
11440
- const nx = du[1] * dv[2] - du[2] * dv[1];
11441
- const ny = du[2] * dv[0] - du[0] * dv[2];
11442
- const nz = du[0] * dv[1] - du[1] * dv[0];
11438
+ const { Su, Sv } = this.derivativesAt(u, v);
11439
+ const nx = Su[1] * Sv[2] - Su[2] * Sv[1];
11440
+ const ny = Su[2] * Sv[0] - Su[0] * Sv[2];
11441
+ const nz = Su[0] * Sv[1] - Su[1] * Sv[0];
11443
11442
  const len2 = Math.sqrt(nx * nx + ny * ny + nz * nz);
11444
11443
  if (len2 < 1e-12) return [0, 0, 1];
11445
11444
  return [nx / len2, ny / len2, nz / len2];
11446
11445
  }
11446
+ /** Analytic first partial derivatives S_u, S_v (rational quotient rule). */
11447
+ derivativesAt(u, v) {
11448
+ const uu = this.remapU(Math.max(0, Math.min(1, u)));
11449
+ const vv = this.remapV(Math.max(0, Math.min(1, v)));
11450
+ const spanU = findSpan(this.nU, this.degreeU, uu, this.knotsU);
11451
+ const spanV = findSpan(this.nV, this.degreeV, vv, this.knotsV);
11452
+ const dU = basisFunsDeriv(spanU, uu, this.degreeU, this.knotsU, 1);
11453
+ const dV = basisFunsDeriv(spanV, vv, this.degreeV, this.knotsV, 1);
11454
+ const A = [0, 0, 0];
11455
+ const Au = [0, 0, 0];
11456
+ const Av = [0, 0, 0];
11457
+ let w = 0;
11458
+ let wu = 0;
11459
+ let wv = 0;
11460
+ for (let i = 0; i <= this.degreeU; i++) {
11461
+ const rowIdx = spanU - this.degreeU + i;
11462
+ for (let j = 0; j <= this.degreeV; j++) {
11463
+ const colIdx = spanV - this.degreeV + j;
11464
+ const weight = this.weightsGrid[rowIdx][colIdx];
11465
+ const pt = this.controlGrid[rowIdx][colIdx];
11466
+ const n00 = dU[0][i] * dV[0][j] * weight;
11467
+ const n10 = dU[1][i] * dV[0][j] * weight;
11468
+ const n01 = dU[0][i] * dV[1][j] * weight;
11469
+ w += n00;
11470
+ wu += n10;
11471
+ wv += n01;
11472
+ for (let c = 0; c < 3; c++) {
11473
+ A[c] += n00 * pt[c];
11474
+ Au[c] += n10 * pt[c];
11475
+ Av[c] += n01 * pt[c];
11476
+ }
11477
+ }
11478
+ }
11479
+ if (w === 0) return { S: [0, 0, 0], Su: [0, 0, 0], Sv: [0, 0, 0] };
11480
+ const invW = 1 / w;
11481
+ const S = [A[0] * invW, A[1] * invW, A[2] * invW];
11482
+ const Su = [(Au[0] - wu * S[0]) * invW, (Au[1] - wu * S[1]) * invW, (Au[2] - wu * S[2]) * invW];
11483
+ const Sv = [(Av[0] - wv * S[0]) * invW, (Av[1] - wv * S[1]) * invW, (Av[2] - wv * S[2]) * invW];
11484
+ return { S, Su, Sv };
11485
+ }
11447
11486
  /**
11448
11487
  * Tessellate the surface into a triangle mesh.
11449
11488
  * Returns positions, normals, and triangle indices.
@@ -13889,6 +13928,20 @@ function fromSlicesSingleSliceHalfExtentForManifold(plan) {
13889
13928
  }
13890
13929
  return Math.max(1, radius + maxOffsetMagnitude + plan.boundsPadding + plan.edgeLength * 3);
13891
13930
  }
13931
+ var BOOLEAN_OPERAND_NORMAL_SHARP_ANGLE_DEG = 60;
13932
+ function promoteBooleanOperandNormals(shapes) {
13933
+ let maxExtra = 0;
13934
+ for (const shape of shapes) maxExtra = Math.max(maxExtra, shape.numProp());
13935
+ if (maxExtra < 3) return { operands: shapes, created: [] };
13936
+ const created = [];
13937
+ const operands = shapes.map((shape) => {
13938
+ if (shape.numProp() >= 3) return shape;
13939
+ const promoted = shape.calculateNormals(0, BOOLEAN_OPERAND_NORMAL_SHARP_ANGLE_DEG);
13940
+ created.push(promoted);
13941
+ return promoted;
13942
+ });
13943
+ return { operands, created };
13944
+ }
13892
13945
  function lowerShapeBooleanCompilePlan(plan, wasm) {
13893
13946
  const shapes = plan.shapes.map((shape) => lowerShapeCompilePlanToManifold(shape, wasm));
13894
13947
  if (shapes.length === 0) {
@@ -13897,16 +13950,18 @@ function lowerShapeBooleanCompilePlan(plan, wasm) {
13897
13950
  if (shapes.length === 1) {
13898
13951
  return shapes[0];
13899
13952
  }
13953
+ const { operands, created } = promoteBooleanOperandNormals(shapes);
13900
13954
  try {
13901
13955
  switch (plan.op) {
13902
13956
  case "union":
13903
- return wasm.Manifold.union(shapes);
13957
+ return wasm.Manifold.union(operands);
13904
13958
  case "difference":
13905
- return wasm.Manifold.difference(shapes);
13959
+ return wasm.Manifold.difference(operands);
13906
13960
  case "intersection":
13907
- return wasm.Manifold.intersection(shapes);
13961
+ return wasm.Manifold.intersection(operands);
13908
13962
  }
13909
13963
  } finally {
13964
+ disposeWasmObjects(created);
13910
13965
  disposeWasmObjects(shapes);
13911
13966
  }
13912
13967
  }
@@ -15258,12 +15313,16 @@ function lowerNurbsSurfaceToManifold(plan, wasm) {
15258
15313
  const { positions, normals, indices } = surface.tessellate(res, res);
15259
15314
  const thickness = plan.thickness;
15260
15315
  const numVerts = positions.length;
15261
- const allPositions = [];
15262
- for (const [x, y, z] of positions) allPositions.push(x, y, z);
15316
+ const vertProps = [];
15263
15317
  for (let i = 0; i < numVerts; i++) {
15264
15318
  const [x, y, z] = positions[i];
15265
15319
  const [nx, ny, nz] = normals[i];
15266
- allPositions.push(x - nx * thickness, y - ny * thickness, z - nz * thickness);
15320
+ vertProps.push(x, y, z, nx, ny, nz);
15321
+ }
15322
+ for (let i = 0; i < numVerts; i++) {
15323
+ const [x, y, z] = positions[i];
15324
+ const [nx, ny, nz] = normals[i];
15325
+ vertProps.push(x - nx * thickness, y - ny * thickness, z - nz * thickness, -nx, -ny, -nz);
15267
15326
  }
15268
15327
  const allIndices = [];
15269
15328
  for (const idx of indices) allIndices.push(idx);
@@ -15288,11 +15347,12 @@ function lowerNurbsSurfaceToManifold(plan, wasm) {
15288
15347
  allIndices.push(c, c + numVerts, d, d, c + numVerts, d + numVerts);
15289
15348
  }
15290
15349
  const mesh = new wasm.Mesh({
15291
- numProp: 3,
15292
- vertProperties: new Float32Array(allPositions),
15350
+ numProp: 6,
15351
+ vertProperties: new Float32Array(vertProps),
15293
15352
  triVerts: new Uint32Array(allIndices)
15294
15353
  });
15295
15354
  try {
15355
+ mesh.merge();
15296
15356
  return new wasm.Manifold(mesh);
15297
15357
  } finally {
15298
15358
  disposeWasmObject(mesh);
@@ -37311,6 +37371,7 @@ var TruckShapeBackend = class _TruckShapeBackend {
37311
37371
  const payload = JSON.parse(getTruckGeometryWasm().geometry_mesh(this.getLiveHandle("getMesh()")));
37312
37372
  const numTri = payload.triangles.length / 3;
37313
37373
  const numVert = payload.positions.length / 3;
37374
+ const cornerNormals = payload.normals && payload.normals.length === numTri * 9 ? new Float32Array(payload.normals) : void 0;
37314
37375
  this.resource.mesh = {
37315
37376
  numProp: 3,
37316
37377
  numTri,
@@ -37324,7 +37385,8 @@ var TruckShapeBackend = class _TruckShapeBackend {
37324
37385
  runTransform: new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0]),
37325
37386
  faceID: new Int32Array(payload.face_ids),
37326
37387
  faceIdNames: payload.face_id_names ?? [],
37327
- halfedgeTangent: new Float32Array(0)
37388
+ halfedgeTangent: new Float32Array(0),
37389
+ cornerNormals
37328
37390
  };
37329
37391
  return this.resource.mesh;
37330
37392
  }
@@ -38540,6 +38602,16 @@ function boundsInteriorOverlap2(a, b) {
38540
38602
  const tolerance = 1e-8;
38541
38603
  return Math.min(a.max[0], b.max[0]) - Math.max(a.min[0], b.min[0]) > tolerance && Math.min(a.max[1], b.max[1]) - Math.max(a.min[1], b.min[1]) > tolerance && Math.min(a.max[2], b.max[2]) - Math.max(a.min[2], b.min[2]) > tolerance;
38542
38604
  }
38605
+ function boundsFaceOrInteriorOverlap(a, b) {
38606
+ const tolerance = 1e-8;
38607
+ const overlaps = [
38608
+ Math.min(a.max[0], b.max[0]) - Math.max(a.min[0], b.min[0]),
38609
+ Math.min(a.max[1], b.max[1]) - Math.max(a.min[1], b.min[1]),
38610
+ Math.min(a.max[2], b.max[2]) - Math.max(a.min[2], b.min[2])
38611
+ ];
38612
+ if (overlaps.some((overlap) => overlap < -tolerance)) return false;
38613
+ return overlaps.filter((overlap) => overlap <= tolerance).length <= 1;
38614
+ }
38543
38615
  function boundsFromPoints3(points) {
38544
38616
  const first = points[0];
38545
38617
  if (!first) return null;
@@ -38622,16 +38694,16 @@ function shapePlanBounds(plan) {
38622
38694
  return null;
38623
38695
  }
38624
38696
  }
38625
- function hasPairwiseInteriorBoundsOverlap(shapes) {
38697
+ function hasPairwiseFaceOrInteriorBoundsOverlap(shapes) {
38626
38698
  const bounds = shapes.map((shape) => shape.boundingBox());
38627
38699
  for (let i = 0; i < bounds.length; i++) {
38628
38700
  for (let j = i + 1; j < bounds.length; j++) {
38629
- if (boundsInteriorOverlap2(bounds[i], bounds[j])) return true;
38701
+ if (boundsFaceOrInteriorOverlap(bounds[i], bounds[j])) return true;
38630
38702
  }
38631
38703
  }
38632
38704
  return false;
38633
38705
  }
38634
- function interiorOverlapComponents(shapes) {
38706
+ function faceOrInteriorOverlapComponents(shapes) {
38635
38707
  const bounds = shapes.map((shape) => shape.boundingBox());
38636
38708
  const visited = /* @__PURE__ */ new Set();
38637
38709
  const components = [];
@@ -38645,7 +38717,7 @@ function interiorOverlapComponents(shapes) {
38645
38717
  component.push(current);
38646
38718
  for (let next = 0; next < shapes.length; next++) {
38647
38719
  if (visited.has(next)) continue;
38648
- if (boundsInteriorOverlap2(bounds[current], bounds[next])) {
38720
+ if (boundsFaceOrInteriorOverlap(bounds[current], bounds[next])) {
38649
38721
  visited.add(next);
38650
38722
  queue.push(next);
38651
38723
  }
@@ -38690,10 +38762,10 @@ function lowerGenericBooleanPlan(plan) {
38690
38762
  let returned = null;
38691
38763
  try {
38692
38764
  if (plan.op === "union") {
38693
- if (!hasPairwiseInteriorBoundsOverlap(shapes)) {
38765
+ if (!hasPairwiseFaceOrInteriorBoundsOverlap(shapes)) {
38694
38766
  return null;
38695
38767
  }
38696
- const components = interiorOverlapComponents(shapes);
38768
+ const components = faceOrInteriorOverlapComponents(shapes);
38697
38769
  if (components.length > 1) {
38698
38770
  returned = lowerClusteredUnion(shapes, components);
38699
38771
  return returned;
@@ -39219,9 +39291,13 @@ function shapeHasClosedNativeTopology(shape) {
39219
39291
  function normalizeTruckShapeForBooleanInput(shape) {
39220
39292
  if (shapeHasClosedNativeTopology(shape)) return shape;
39221
39293
  if (!meshHasRawBoundaryEdges(shape.getMesh())) return shape;
39222
- const normalized = normalizeFacetedTruckShape(shape);
39223
- disposeShapeBackend(shape);
39224
- return normalized;
39294
+ try {
39295
+ const normalized = normalizeFacetedTruckShape(shape);
39296
+ disposeShapeBackend(shape);
39297
+ return normalized;
39298
+ } catch {
39299
+ return shape;
39300
+ }
39225
39301
  }
39226
39302
  function lowerSdfPlan(plan) {
39227
39303
  if (getUnsupportedSdfProgramReason(plan.tree) === void 0) {
@@ -41105,7 +41181,7 @@ function circleProfilePlan(radius, segments) {
41105
41181
  }
41106
41182
  function annulusProfilePlan(outerRadius, innerRadius, segments) {
41107
41183
  const outer = Math.abs(outerRadius);
41108
- const inner = Math.abs(innerRadius);
41184
+ const inner = Math.max(0, innerRadius);
41109
41185
  if (outer <= EXACT_PROFILE_EPS) return emptyProfilePlan();
41110
41186
  if (inner <= EXACT_PROFILE_EPS) return circleProfilePlan(outer, segments);
41111
41187
  return {
@@ -52512,8 +52588,7 @@ function sculptLook(preset = "gallery") {
52512
52588
  { type: "directional", position: [90, -110, 150], color: "#ffffff", intensity: 1.4 },
52513
52589
  { type: "directional", position: [-120, 70, 80], color: "#dcecff", intensity: 0.65 },
52514
52590
  { type: "hemisphere", skyColor: "#d9edff", groundColor: "#f0e6d4", intensity: 0.45 }
52515
- ],
52516
- postProcessing: { toneMappingExposure: 1.12, vignette: { darkness: 0.16, offset: 0.6 } }
52591
+ ]
52517
52592
  };
52518
52593
  case "candy-shop":
52519
52594
  return {
@@ -52524,8 +52599,7 @@ function sculptLook(preset = "gallery") {
52524
52599
  { type: "point", position: [70, -60, 90], color: "#ff8ac8", intensity: 2.2, distance: 280, decay: 1.2 },
52525
52600
  { type: "point", position: [-85, 80, 70], color: "#70e1ff", intensity: 1.8, distance: 260, decay: 1.4 },
52526
52601
  { type: "directional", position: [30, -80, 140], color: "#fff6dd", intensity: 0.9 }
52527
- ],
52528
- postProcessing: { toneMappingExposure: 1.25, bloom: { intensity: 0.35, threshold: 0.78, radius: 0.45 } }
52602
+ ]
52529
52603
  };
52530
52604
  case "midnight":
52531
52605
  return {
@@ -52537,8 +52611,7 @@ function sculptLook(preset = "gallery") {
52537
52611
  { type: "point", position: [-90, 70, 50], color: "#ff7ac8", intensity: 1.4, distance: 300, decay: 1.3 },
52538
52612
  { type: "directional", position: [30, -30, 160], color: "#d7e9ff", intensity: 0.65 }
52539
52613
  ],
52540
- fog: { color: "#060814", near: 180, far: 520 },
52541
- postProcessing: { toneMappingExposure: 1.35, bloom: { intensity: 0.65, threshold: 0.65, radius: 0.6 } }
52614
+ fog: { color: "#060814", near: 180, far: 520 }
52542
52615
  };
52543
52616
  case "workbench":
52544
52617
  return {
@@ -52549,8 +52622,7 @@ function sculptLook(preset = "gallery") {
52549
52622
  { type: "directional", position: [80, -100, 130], color: "#fff5df", intensity: 1.25 },
52550
52623
  { type: "hemisphere", skyColor: "#eef6ff", groundColor: "#d8d0c0", intensity: 0.35 }
52551
52624
  ],
52552
- ground: { visible: true, color: "#d8d4ca", offset: 1, receiveShadow: true },
52553
- postProcessing: { toneMappingExposure: 1.05 }
52625
+ ground: { visible: true, color: "#d8d4ca", offset: 1, receiveShadow: true }
52554
52626
  };
52555
52627
  default:
52556
52628
  return {
@@ -52561,8 +52633,7 @@ function sculptLook(preset = "gallery") {
52561
52633
  { type: "directional", position: [110, -130, 150], color: "#ffffff", intensity: 1.5 },
52562
52634
  { type: "directional", position: [-90, 80, 90], color: "#b8d8ff", intensity: 0.55 },
52563
52635
  { type: "hemisphere", skyColor: "#ddefff", groundColor: "#e8e2d8", intensity: 0.45 }
52564
- ],
52565
- postProcessing: { toneMappingExposure: 1.18, vignette: { darkness: 0.18, offset: 0.55 } }
52636
+ ]
52566
52637
  };
52567
52638
  }
52568
52639
  }
@@ -54395,7 +54466,7 @@ var Shape2 = class _Shape {
54395
54466
  *
54396
54467
  * Use `.color()` to set the base diffuse color; `.material()` controls how that color behaves
54397
54468
  * under light (metalness, roughness, clearcoat) and can add emissive glow independent of
54398
- * lighting. Emissive glow pairs naturally with the `postProcessing.bloom` effect in `scene()`.
54469
+ * lighting.
54399
54470
  *
54400
54471
  * **Example**
54401
54472
  *
@@ -59063,8 +59134,8 @@ var Assembly = class _Assembly {
59063
59134
  *
59064
59135
  * Use this after adding physical parts and joints. Robot-body profiles require
59065
59136
  * `rootPart`; asset profiles can describe one-part or multi-part physical assets.
59066
- * URDF/SDF exporters and `forgecad check simready` read this contract directly,
59067
- * so model files no longer need a separate `robotExport(...)` side effect.
59137
+ * URDF/SDF/MJCF/USD exporters and `forgecad check simready` read this
59138
+ * contract directly from the returned assembly.
59068
59139
  *
59069
59140
  * @category Assembly
59070
59141
  */
@@ -70553,7 +70624,8 @@ var FrozenShape = class extends Shape2 {
70553
70624
  var EDGE_THRESHOLD_DOT2 = Math.cos(Math.PI / 180);
70554
70625
  var SMOOTH_THRESHOLD_DOT = Math.cos(30 * Math.PI / 180);
70555
70626
  function computeGeometryArrays(mesh, options = {}) {
70556
- const { numProp, numTri: triCount, triVerts, vertProperties, vertNormals } = mesh;
70627
+ const { numProp, numTri: triCount, triVerts, vertProperties, vertNormals, cornerNormals } = mesh;
70628
+ const useCornerNormals = !!cornerNormals && cornerNormals.length === triCount * 9;
70557
70629
  const positions = new Float32Array(triCount * 9);
70558
70630
  const normals = new Float32Array(triCount * 9);
70559
70631
  const faceNx = new Float32Array(triCount);
@@ -70585,7 +70657,9 @@ function computeGeometryArrays(mesh, options = {}) {
70585
70657
  positions[o + 6] = cx;
70586
70658
  positions[o + 7] = cy;
70587
70659
  positions[o + 8] = cz;
70588
- if (vertNormals) {
70660
+ if (useCornerNormals) {
70661
+ for (let k = 0; k < 9; k++) normals[o + k] = cornerNormals[o + k];
70662
+ } else if (vertNormals) {
70589
70663
  normals[o] = vertNormals[i0 * 3];
70590
70664
  normals[o + 1] = vertNormals[i0 * 3 + 1];
70591
70665
  normals[o + 2] = vertNormals[i0 * 3 + 2];
@@ -70628,7 +70702,7 @@ function computeGeometryArrays(mesh, options = {}) {
70628
70702
  faceNy[t] = fny;
70629
70703
  faceNz[t] = fnz;
70630
70704
  }
70631
- if (!vertNormals && numProp < 6 && triCount > 0) {
70705
+ if (!useCornerNormals && !vertNormals && numProp < 6 && triCount > 0) {
70632
70706
  computeAutoSmoothNormals(
70633
70707
  triVerts,
70634
70708
  vertProperties,
@@ -70817,7 +70891,8 @@ function shapeToGeometryFallback(shape) {
70817
70891
  vertProperties: mesh.vertProperties,
70818
70892
  mergeFromVert: mesh.mergeFromVert,
70819
70893
  mergeToVert: mesh.mergeToVert,
70820
- vertNormals
70894
+ vertNormals,
70895
+ cornerNormals: mesh.cornerNormals && mesh.cornerNormals.length === mesh.numTri * 9 ? mesh.cornerNormals : void 0
70821
70896
  });
70822
70897
  const solid = new BufferGeometry();
70823
70898
  solid.setAttribute("position", new BufferAttribute(positions, 3));
@@ -72464,31 +72539,6 @@ function validateFog(fog, label) {
72464
72539
  if (fog.density !== void 0) out.density = requireFinite7(fog.density, `${label}.density`);
72465
72540
  return out;
72466
72541
  }
72467
- function validatePostProcessing(pp, label) {
72468
- const out = {};
72469
- if (pp.bloom !== void 0) {
72470
- if (!pp.bloom || typeof pp.bloom !== "object") throw new Error(`${label}.bloom must be an object`);
72471
- out.bloom = {};
72472
- if (pp.bloom.intensity !== void 0) out.bloom.intensity = requireFinite7(pp.bloom.intensity, `${label}.bloom.intensity`);
72473
- if (pp.bloom.threshold !== void 0) out.bloom.threshold = requireFinite7(pp.bloom.threshold, `${label}.bloom.threshold`);
72474
- if (pp.bloom.radius !== void 0) out.bloom.radius = requireFinite7(pp.bloom.radius, `${label}.bloom.radius`);
72475
- }
72476
- if (pp.vignette !== void 0) {
72477
- if (!pp.vignette || typeof pp.vignette !== "object") throw new Error(`${label}.vignette must be an object`);
72478
- out.vignette = {};
72479
- if (pp.vignette.darkness !== void 0) out.vignette.darkness = requireFinite7(pp.vignette.darkness, `${label}.vignette.darkness`);
72480
- if (pp.vignette.offset !== void 0) out.vignette.offset = requireFinite7(pp.vignette.offset, `${label}.vignette.offset`);
72481
- }
72482
- if (pp.grain !== void 0) {
72483
- if (!pp.grain || typeof pp.grain !== "object") throw new Error(`${label}.grain must be an object`);
72484
- out.grain = {};
72485
- if (pp.grain.intensity !== void 0) out.grain.intensity = requireFinite7(pp.grain.intensity, `${label}.grain.intensity`);
72486
- }
72487
- if (pp.toneMappingExposure !== void 0) {
72488
- out.toneMappingExposure = requireFinite7(pp.toneMappingExposure, `${label}.toneMappingExposure`);
72489
- }
72490
- return out;
72491
- }
72492
72542
  function validateGround(ground, label) {
72493
72543
  const out = {};
72494
72544
  if (ground.visible !== void 0) {
@@ -72570,7 +72620,6 @@ function scene(options) {
72570
72620
  lights: null,
72571
72621
  environment: null,
72572
72622
  fog: null,
72573
- postProcessing: null,
72574
72623
  ground: null,
72575
72624
  capture: null
72576
72625
  };
@@ -72610,12 +72659,9 @@ function scene(options) {
72610
72659
  }
72611
72660
  current.fog = validateFog(options.fog, "scene.fog");
72612
72661
  }
72613
- if (options.postProcessing !== void 0) {
72614
- if (!options.postProcessing || typeof options.postProcessing !== "object") {
72615
- throw new Error("scene.postProcessing must be an object");
72616
- }
72617
- const validated = validatePostProcessing(options.postProcessing, "scene.postProcessing");
72618
- current.postProcessing = current.postProcessing ? { ...current.postProcessing, ...validated } : validated;
72662
+ const disabledPostProcessing = options.postProcessing;
72663
+ if (disabledPostProcessing !== void 0) {
72664
+ console.warn("scene.postProcessing is disabled for now while the browser post-processing path is being rebuilt.");
72619
72665
  }
72620
72666
  if (options.ground !== void 0) {
72621
72667
  if (!options.ground || typeof options.ground !== "object") {
@@ -73518,8 +73564,7 @@ function scenePreset(name) {
73518
73564
  { type: "hemisphere", skyColor: "#ffffff", groundColor: "#d5d9de", intensity: 0.75 },
73519
73565
  { type: "directional", position: [90, -120, 180], color: "#ffffff", intensity: 1.8, castShadow: true },
73520
73566
  { type: "directional", position: [-140, 100, 80], color: "#dfe9ff", intensity: 0.55 }
73521
- ],
73522
- postProcessing: { toneMappingExposure: 1.08 }
73567
+ ]
73523
73568
  });
73524
73569
  return;
73525
73570
  }
@@ -73765,8 +73810,8 @@ function shapeBoundsCenter(shape) {
73765
73810
  ];
73766
73811
  }
73767
73812
  var ProductSurfaceRef = class _ProductSurfaceRef {
73768
- constructor(skin, query, name) {
73769
- this.skin = skin;
73813
+ constructor(skin2, query, name) {
73814
+ this.skin = skin2;
73770
73815
  this.query = query;
73771
73816
  this.name = name;
73772
73817
  }
@@ -74283,8 +74328,8 @@ var ProductHandleBuilder = class {
74283
74328
  }
74284
74329
  };
74285
74330
  var ProductSurfaceBuilder = class {
74286
- constructor(skin, side) {
74287
- this.skin = skin;
74331
+ constructor(skin2, side) {
74332
+ this.skin = skin2;
74288
74333
  this.side = side;
74289
74334
  }
74290
74335
  /** Create a ref on this skin side. */
@@ -74352,9 +74397,9 @@ var ProductRibbonBuilder = class {
74352
74397
  * ProductSkin.frame(), so the ribbon bends along the selected side as station width/depth changes.
74353
74398
  * All query path points must stay on one side; split side transitions into separate ribbons.
74354
74399
  */
74355
- on(skin, points, options = {}) {
74400
+ on(skin2, points, options = {}) {
74356
74401
  if (points.length < 2) throw new Error("Product.ribbon().on(skin, points) requires at least two path points");
74357
- this.skinValue = skin;
74402
+ this.skinValue = skin2;
74358
74403
  this.queryPath = resolvePathQueries(points);
74359
74404
  this.refPath = [];
74360
74405
  return this.applyOptions(options);
@@ -74477,7 +74522,7 @@ var ProductRibbonBuilder = class {
74477
74522
  const localT = scaled - segment;
74478
74523
  return interpolateQuery(this.queryPath[segment], this.queryPath[segment + 1], localT);
74479
74524
  }
74480
- buildSkinGrid(skin, path2) {
74525
+ buildSkinGrid(skin2, path2) {
74481
74526
  if (path2.length < 2) throw new Error("Product.ribbon().on(skin, points) must be called before .build()");
74482
74527
  const side = normalizedSide(path2[0].side);
74483
74528
  if (side === "front" || side === "rear") {
@@ -74496,7 +74541,7 @@ var ProductRibbonBuilder = class {
74496
74541
  for (let i = 0; i < this.samplesValue; i += 1) {
74497
74542
  const along = this.samplesValue === 1 ? 0 : i / (this.samplesValue - 1);
74498
74543
  const center = this.samplePathQuery(along);
74499
- const station = skin.stationAt(center.v ?? 0.5);
74544
+ const station = skin2.stationAt(center.v ?? 0.5);
74500
74545
  const span = sideSpan(side, station.width, station.depth);
74501
74546
  for (let j = 0; j < this.widthSamplesValue; j += 1) {
74502
74547
  const across = this.widthSamplesValue === 1 ? 0 : j / (this.widthSamplesValue - 1) - 0.5;
@@ -74513,13 +74558,13 @@ var ProductRibbonBuilder = class {
74513
74558
  u,
74514
74559
  offset: (center.offset ?? 0) + this.offsetValue + this.thicknessValue
74515
74560
  };
74516
- rows[j].push(skin.frame(query).point);
74561
+ rows[j].push(skin2.frame(query).point);
74517
74562
  }
74518
74563
  }
74519
74564
  return {
74520
74565
  grid: rows,
74521
74566
  diagnostics: this.makeDiagnostics({
74522
- skin: skin.name,
74567
+ skin: skin2.name,
74523
74568
  side,
74524
74569
  pathPointCount: path2.length,
74525
74570
  clampedUCount,
@@ -74676,16 +74721,16 @@ var Product = {
74676
74721
  return scaleProfileTo(sketch, width, depth);
74677
74722
  },
74678
74723
  /** Create an ad-hoc ProductSurfaceRef from a skin and side/u/v query. */
74679
- ref(skin, query) {
74680
- return new ProductSurfaceRef(skin, query);
74724
+ ref(skin2, query) {
74725
+ return new ProductSurfaceRef(skin2, query);
74681
74726
  },
74682
74727
  /**
74683
74728
  * Create a fluent surface helper for refs and conformal features on one side of a skin.
74684
74729
  *
74685
74730
  * Equivalent to skin.surface(side), useful when writing in Product.* namespace style.
74686
74731
  */
74687
- surface(skin, side) {
74688
- return skin.surface(side);
74732
+ surface(skin2, side) {
74733
+ return skin2.surface(side);
74689
74734
  },
74690
74735
  /** Start a panel feature builder. */
74691
74736
  panel(name) {
@@ -75166,8 +75211,8 @@ function coordinateOnSide(coordinate, side, label) {
75166
75211
  return { ...coordinate, kind: "productSkin", side };
75167
75212
  }
75168
75213
  var ProductSkinCarrier = class _ProductSkinCarrier {
75169
- constructor(skin, name = skin.name, sideValue2, offsetValue = 0) {
75170
- this.skin = skin;
75214
+ constructor(skin2, name = skin2.name, sideValue2, offsetValue = 0) {
75215
+ this.skin = skin2;
75171
75216
  this.name = name;
75172
75217
  this.sideValue = sideValue2;
75173
75218
  this.offsetValue = offsetValue;
@@ -78108,8 +78153,8 @@ var Carrier = {
78108
78153
  return new PlaneCarrier(name);
78109
78154
  },
78110
78155
  /** Adapt an existing ProductSkin into the general surface-member carrier protocol. */
78111
- productSkin(skin) {
78112
- return new ProductSkinCarrier(skin);
78156
+ productSkin(skin2) {
78157
+ return new ProductSkinCarrier(skin2);
78113
78158
  },
78114
78159
  /** Reserved stub for future parameterized mesh carriers; arbitrary mesh parameterization is not implemented yet. */
78115
78160
  mesh(name) {
@@ -78829,9 +78874,9 @@ function pickDimensionOffsetBasis(dirModel, frame) {
78829
78874
  ];
78830
78875
  const candidates = [];
78831
78876
  const pushCandidate = (candidate) => {
78832
- const len34 = Math.hypot(candidate[0], candidate[1], candidate[2]);
78833
- if (len34 < 1e-8) return;
78834
- const dir3 = [candidate[0] / len34, candidate[1] / len34, candidate[2] / len34];
78877
+ const len35 = Math.hypot(candidate[0], candidate[1], candidate[2]);
78878
+ if (len35 < 1e-8) return;
78879
+ const dir3 = [candidate[0] / len35, candidate[1] / len35, candidate[2] / len35];
78835
78880
  const proj = projectVectorToView(dir3, frame);
78836
78881
  const projLen = Math.hypot(proj[0], proj[1]);
78837
78882
  if (projLen < 1e-6) return;
@@ -80010,8 +80055,7 @@ function generateReportPdf(result, options = {}) {
80010
80055
  };
80011
80056
  }
80012
80057
 
80013
- // src/forge/export/robotExport.ts
80014
- var _collectedRobotExport = null;
80058
+ // src/forge/export/simulationModel.ts
80015
80059
  function cloneLinkOptions(input) {
80016
80060
  if (!input) return {};
80017
80061
  return Object.fromEntries(Object.entries(input).map(([name, opts]) => [name, { ...opts }]));
@@ -80098,12 +80142,6 @@ function diffDriveFromControllers(controllers) {
80098
80142
  angularAcceleration: diffDrive2.angularAcceleration
80099
80143
  } : void 0;
80100
80144
  }
80101
- function resetRobotExport() {
80102
- _collectedRobotExport = null;
80103
- }
80104
- function getCollectedRobotExport() {
80105
- return _collectedRobotExport;
80106
- }
80107
80145
  function collectSimulationModel(assemblyInput, options = {}) {
80108
80146
  const assembly2 = typeof assemblyInput.describe === "function" ? assemblyInput.describe() : assemblyInput;
80109
80147
  const partNames = new Set(assembly2.parts.map((part) => part.name));
@@ -80186,7 +80224,6 @@ function collectSimulationModel(assemblyInput, options = {}) {
80186
80224
  modelName: (options.modelName ?? assembly2.name ?? "ForgeCAD Simulation").trim() || "ForgeCAD Simulation",
80187
80225
  assembly: assembly2,
80188
80226
  simulation,
80189
- source: options.source ?? "assembly",
80190
80227
  state: { ...options.state ?? {} },
80191
80228
  static: options.static ?? false,
80192
80229
  selfCollide: options.selfCollide ?? false,
@@ -80200,24 +80237,11 @@ function collectSimulationModel(assemblyInput, options = {}) {
80200
80237
  world
80201
80238
  };
80202
80239
  }
80203
- function robotExport(options) {
80204
- if (!options || typeof options !== "object") {
80205
- throw new Error("robotExport(...) expects an options object");
80206
- }
80207
- if (!options.assembly || typeof options.assembly.describe !== "function") {
80208
- throw new Error("robotExport(...) requires an assembly");
80209
- }
80210
- _collectedRobotExport = collectSimulationModel(options.assembly, { ...options, source: "robotExport" });
80211
- return _collectedRobotExport;
80212
- }
80213
80240
 
80214
80241
  // src/forge/export/simreadyValidation.ts
80215
80242
  function error(findings, code, message, path2) {
80216
80243
  findings.push({ level: "error", code, message, path: path2 });
80217
80244
  }
80218
- function warn(findings, code, message, path2) {
80219
- findings.push({ level: "warning", code, message, path: path2 });
80220
- }
80221
80245
  function isFiniteNumber3(value) {
80222
80246
  return value === void 0 || Number.isFinite(value);
80223
80247
  }
@@ -80242,13 +80266,13 @@ function profileRequiresExplicitCollider(model) {
80242
80266
  }
80243
80267
  function validatePart(findings, model, part) {
80244
80268
  const link = model.links[part.name];
80245
- const hasLegacyPhysicalData = link?.massKg !== void 0 || link?.densityKgM3 !== void 0;
80269
+ const hasPhysicalData = link?.massKg !== void 0 || link?.densityKgM3 !== void 0;
80246
80270
  const path2 = `parts.${part.name}`;
80247
- if (!part.sim && !hasLegacyPhysicalData) {
80271
+ if (!part.sim && !hasPhysicalData) {
80248
80272
  error(
80249
80273
  findings,
80250
80274
  "SIM.BODY.MISSING",
80251
- `Part "${part.name}" needs Sim.body(...) metadata or legacy massKg/densityKgM3 export metadata.`,
80275
+ `Part "${part.name}" needs Sim.body(...) metadata with massKg or densityKgM3.`,
80252
80276
  path2
80253
80277
  );
80254
80278
  return;
@@ -80383,17 +80407,9 @@ function validateWorld(findings, model) {
80383
80407
  function validateSimulationModel(model) {
80384
80408
  const findings = [];
80385
80409
  const simulation = model.simulation;
80386
- if (!simulation && model.source !== "robotExport") {
80410
+ if (!simulation) {
80387
80411
  error(findings, "SIM.SIMULATION.MISSING", "Model must return assembly(...).withSimulation(...).", "sim");
80388
80412
  }
80389
- if (!simulation && model.source === "robotExport") {
80390
- warn(
80391
- findings,
80392
- "SIM.SIMULATION.LEGACY_ROBOT_EXPORT",
80393
- "Legacy robotExport(...) metadata is accepted for compatibility; prefer assembly.withSimulation(...).",
80394
- "sim"
80395
- );
80396
- }
80397
80413
  if (simulation && !simulation.profile) {
80398
80414
  error(findings, "SIM.PROFILE.MISSING", "Simulation contract needs a Sim.profile.*() value.", "sim.profile");
80399
80415
  }
@@ -80438,7 +80454,7 @@ function materialManifest(material2) {
80438
80454
  }
80439
80455
  function bodyPhysicalSource(sim, link) {
80440
80456
  if (sim) return "sim";
80441
- if (link?.massKg !== void 0 || link?.densityKgM3 !== void 0 || link?.collision !== void 0) return "legacy-export";
80457
+ if (link?.massKg !== void 0 || link?.densityKgM3 !== void 0 || link?.collision !== void 0) return "metadata";
80442
80458
  return "missing";
80443
80459
  }
80444
80460
  function bodyManifest(model) {
@@ -80508,7 +80524,7 @@ function buildSimReadyManifest(model) {
80508
80524
  version: 1,
80509
80525
  modelName: model.modelName,
80510
80526
  sourceModelName: model.assembly.name,
80511
- source: model.source,
80527
+ source: "assembly",
80512
80528
  profile: profile2?.name ?? null,
80513
80529
  profileVersion: "1.0.0",
80514
80530
  units: {
@@ -90816,6 +90832,849 @@ Sketch.prototype.onFace = function(parentOrFace, faceOrOpts, maybeOpts = {}) {
90816
90832
  return sketchOnFace(this, parentOrFace, faceOrOpts, maybeOpts);
90817
90833
  };
90818
90834
 
90835
+ // src/forge/sketch/nurbsSurfaceFit.ts
90836
+ var KNOT_EPS = 1e-10;
90837
+ var KNOT_MERGE_EPS = 1e-7;
90838
+ function sameKnot2(a, b) {
90839
+ return Math.abs(a - b) <= KNOT_EPS;
90840
+ }
90841
+ function knotMultiplicity2(knots, u) {
90842
+ let count = 0;
90843
+ for (const k of knots) if (sameKnot2(k, u)) count++;
90844
+ return count;
90845
+ }
90846
+ function computeParamsCentripetal(points, alpha = 0.5) {
90847
+ const n = points.length;
90848
+ const params = new Array(n).fill(0);
90849
+ const seg = new Array(n).fill(0);
90850
+ let total = 0;
90851
+ for (let k = 1; k < n; k++) {
90852
+ const dx = points[k][0] - points[k - 1][0];
90853
+ const dy = points[k][1] - points[k - 1][1];
90854
+ const dz = points[k][2] - points[k - 1][2];
90855
+ seg[k] = Math.hypot(dx, dy, dz) ** alpha;
90856
+ total += seg[k];
90857
+ }
90858
+ if (total === 0) {
90859
+ for (let k = 0; k < n; k++) params[k] = n > 1 ? k / (n - 1) : 0;
90860
+ return params;
90861
+ }
90862
+ let acc = 0;
90863
+ for (let k = 1; k < n - 1; k++) {
90864
+ acc += seg[k];
90865
+ params[k] = acc / total;
90866
+ }
90867
+ params[n - 1] = 1;
90868
+ return params;
90869
+ }
90870
+ function knotsFromParamsAveraging(params, degree) {
90871
+ const n = params.length - 1;
90872
+ const m = n + degree + 1;
90873
+ const knots = new Array(m + 1).fill(0);
90874
+ for (let i = m - degree; i <= m; i++) knots[i] = 1;
90875
+ for (let j = 1; j <= n - degree; j++) {
90876
+ let s = 0;
90877
+ for (let i = j; i <= j + degree - 1; i++) s += params[i];
90878
+ knots[j + degree] = s / degree;
90879
+ }
90880
+ return knots;
90881
+ }
90882
+ function solveLinear(A, B) {
90883
+ const n = A.length;
90884
+ const k = B[0].length;
90885
+ const M = A.map((row, i) => [...row, ...B[i]]);
90886
+ for (let col = 0; col < n; col++) {
90887
+ let piv = col;
90888
+ for (let r = col + 1; r < n; r++) if (Math.abs(M[r][col]) > Math.abs(M[piv][col])) piv = r;
90889
+ if (Math.abs(M[piv][col]) < 1e-14) throw new Error(`Singular interpolation matrix at column ${col}.`);
90890
+ [M[col], M[piv]] = [M[piv], M[col]];
90891
+ const inv = 1 / M[col][col];
90892
+ for (let j = col; j < n + k; j++) M[col][j] *= inv;
90893
+ for (let r = 0; r < n; r++) {
90894
+ if (r === col) continue;
90895
+ const f4 = M[r][col];
90896
+ if (f4 === 0) continue;
90897
+ for (let j = col; j < n + k; j++) M[r][j] -= f4 * M[col][j];
90898
+ }
90899
+ }
90900
+ return Array.from({ length: n }, (_, i) => M[i].slice(n));
90901
+ }
90902
+ function globalCurveInterp(points, degree, paramsIn) {
90903
+ const last = points.length - 1;
90904
+ if (last < degree) throw new Error(`Curve interpolation needs at least ${degree + 1} points, got ${points.length}.`);
90905
+ const params = paramsIn ? paramsIn.slice() : computeParamsCentripetal(points, 0.5);
90906
+ const knots = knotsFromParamsAveraging(params, degree);
90907
+ const count = last + 1;
90908
+ const N = Array.from({ length: count }, () => new Array(count).fill(0));
90909
+ for (let i = 0; i <= last; i++) {
90910
+ const span = findSpan(count, degree, params[i], knots);
90911
+ const basis = basisFuns(span, params[i], degree, knots);
90912
+ for (let j = 0; j <= degree; j++) N[i][span - degree + j] = basis[j];
90913
+ }
90914
+ const Q = points.map((p2) => [p2[0], p2[1], p2[2]]);
90915
+ const cps = solveLinear(N, Q);
90916
+ return { cps, knots, degree };
90917
+ }
90918
+ function insertKnotCurve(cps, knots, degree, u) {
90919
+ const n = cps.length;
90920
+ const span = findSpan(n, degree, u, knots);
90921
+ const mult = knotMultiplicity2(knots, u);
90922
+ if (mult >= degree) return { cps: cps.slice(), knots: knots.slice() };
90923
+ const newCps = new Array(n + 1);
90924
+ const newKnots = new Array(knots.length + 1);
90925
+ for (let i = 0; i <= span; i++) newKnots[i] = knots[i];
90926
+ newKnots[span + 1] = u;
90927
+ for (let i = span + 1; i < knots.length; i++) newKnots[i + 1] = knots[i];
90928
+ for (let i = 0; i <= span - degree; i++) newCps[i] = [...cps[i]];
90929
+ for (let i = span - mult; i < n; i++) newCps[i + 1] = [...cps[i]];
90930
+ for (let i = span - degree + 1; i <= span - mult; i++) {
90931
+ const denom = knots[i + degree] - knots[i];
90932
+ const alpha = denom === 0 ? 0 : (u - knots[i]) / denom;
90933
+ const a = cps[i - 1];
90934
+ const b = cps[i];
90935
+ newCps[i] = [(1 - alpha) * a[0] + alpha * b[0], (1 - alpha) * a[1] + alpha * b[1], (1 - alpha) * a[2] + alpha * b[2]];
90936
+ }
90937
+ return { cps: newCps, knots: newKnots };
90938
+ }
90939
+ function interiorKnotBuckets(kv, mergeEps = KNOT_MERGE_EPS) {
90940
+ const buckets = [];
90941
+ for (const k of kv) {
90942
+ if (k <= KNOT_EPS || k >= 1 - KNOT_EPS) continue;
90943
+ const b = buckets.find((x) => Math.abs(x.value - k) <= mergeEps);
90944
+ if (b) b.mult++;
90945
+ else buckets.push({ value: k, mult: 1 });
90946
+ }
90947
+ return buckets;
90948
+ }
90949
+ function mergeKnotVectors(knotVectors, degree, mergeEps = KNOT_MERGE_EPS) {
90950
+ const merged = [];
90951
+ for (const kv of knotVectors) {
90952
+ for (const { value, mult } of interiorKnotBuckets(kv, mergeEps)) {
90953
+ const b = merged.find((x) => Math.abs(x.value - value) <= mergeEps);
90954
+ if (b) b.mult = Math.max(b.mult, mult);
90955
+ else merged.push({ value, mult });
90956
+ }
90957
+ }
90958
+ merged.sort((a, b) => a.value - b.value);
90959
+ const out = [];
90960
+ for (let i = 0; i <= degree; i++) out.push(0);
90961
+ for (const { value, mult } of merged) for (let i = 0; i < mult; i++) out.push(value);
90962
+ for (let i = 0; i <= degree; i++) out.push(1);
90963
+ return out;
90964
+ }
90965
+ function refineKnotsToTarget(cps, knots, degree, targetKnots, mergeEps = KNOT_MERGE_EPS) {
90966
+ let curCps = cps.map((p2) => [...p2]);
90967
+ let curKnots = knots.slice();
90968
+ for (const { value, mult } of interiorKnotBuckets(targetKnots, mergeEps)) {
90969
+ const curMult = () => curKnots.reduce((c, k) => c + (Math.abs(k - value) <= mergeEps ? 1 : 0), 0);
90970
+ let guard = 0;
90971
+ while (curMult() < mult) {
90972
+ const r = insertKnotCurve(curCps, curKnots, degree, value);
90973
+ if (r.cps.length === curCps.length) break;
90974
+ curCps = r.cps;
90975
+ curKnots = r.knots;
90976
+ if (++guard > degree + 2) break;
90977
+ }
90978
+ }
90979
+ return { cps: curCps, knots: curKnots };
90980
+ }
90981
+ function binomial(a, b) {
90982
+ if (b < 0 || b > a) return 0;
90983
+ let r = 1;
90984
+ for (let i = 0; i < b; i++) r = r * (a - i) / (i + 1);
90985
+ return r;
90986
+ }
90987
+ function bezierElevate(bez, p2, t) {
90988
+ const m = p2 + t;
90989
+ const out = [];
90990
+ for (let i = 0; i <= m; i++) {
90991
+ const acc = [0, 0, 0];
90992
+ const lo = Math.max(0, i - t);
90993
+ const hi = Math.min(p2, i);
90994
+ for (let j = lo; j <= hi; j++) {
90995
+ const w = binomial(p2, j) * binomial(t, i - j) / binomial(m, i);
90996
+ acc[0] += w * bez[j][0];
90997
+ acc[1] += w * bez[j][1];
90998
+ acc[2] += w * bez[j][2];
90999
+ }
91000
+ out.push(acc);
91001
+ }
91002
+ return out;
91003
+ }
91004
+ function elevateCurveDegree(cps, knots, degree, targetDegree) {
91005
+ if (targetDegree === degree) return { cps: cps.map((p2) => [...p2]), knots: knots.slice(), degree };
91006
+ if (targetDegree < degree) throw new Error("Degree reduction is not supported.");
91007
+ const t = targetDegree - degree;
91008
+ let curCps = cps.map((p2) => [...p2]);
91009
+ let curKnots = knots.slice();
91010
+ const breaks = interiorKnotBuckets(curKnots).map((b) => b.value);
91011
+ for (const v of breaks) {
91012
+ let guard = 0;
91013
+ while (knotMultiplicity2(curKnots, v) < degree) {
91014
+ const r = insertKnotCurve(curCps, curKnots, degree, v);
91015
+ curCps = r.cps;
91016
+ curKnots = r.knots;
91017
+ if (++guard > degree + 2) break;
91018
+ }
91019
+ }
91020
+ const segVals = [0, ...breaks.slice().sort((a, b) => a - b), 1];
91021
+ const nSeg = segVals.length - 1;
91022
+ const newCps = [];
91023
+ for (let s = 0; s < nSeg; s++) {
91024
+ const bez = curCps.slice(s * degree, s * degree + degree + 1);
91025
+ const elevated = bezierElevate(bez, degree, t);
91026
+ if (s === 0) newCps.push(...elevated);
91027
+ else newCps.push(...elevated.slice(1));
91028
+ }
91029
+ const newKnots = [];
91030
+ for (let i = 0; i <= targetDegree; i++) newKnots.push(0);
91031
+ for (let s = 1; s < nSeg; s++) for (let i = 0; i < targetDegree; i++) newKnots.push(segVals[s]);
91032
+ for (let i = 0; i <= targetDegree; i++) newKnots.push(1);
91033
+ if (newKnots.length !== newCps.length + targetDegree + 1) {
91034
+ throw new Error(
91035
+ `Degree elevation produced inconsistent knots (${newKnots.length}) for ${newCps.length} control points at degree ${targetDegree}.`
91036
+ );
91037
+ }
91038
+ return { cps: newCps, knots: newKnots, degree: targetDegree };
91039
+ }
91040
+ function unifyCurves(curves) {
91041
+ const targetDeg = Math.max(...curves.map((c) => c.degree));
91042
+ const elevated = curves.map((c) => elevateCurveDegree(c.cps, c.knots, c.degree, targetDeg));
91043
+ const common2 = mergeKnotVectors(
91044
+ elevated.map((c) => c.knots),
91045
+ targetDeg
91046
+ );
91047
+ const refined = elevated.map((c) => refineKnotsToTarget(c.cps, c.knots, targetDeg, common2));
91048
+ const ncp = common2.length - targetDeg - 1;
91049
+ for (const r of refined) {
91050
+ if (r.cps.length !== ncp) throw new Error(`Curve unification produced ${r.cps.length} control points, expected ${ncp}.`);
91051
+ }
91052
+ return { degree: targetDeg, knots: common2, curves: refined.map((r) => r.cps) };
91053
+ }
91054
+ function transpose(g) {
91055
+ const out = [];
91056
+ for (let j = 0; j < g[0].length; j++) {
91057
+ const row = [];
91058
+ for (let i = 0; i < g.length; i++) row.push(g[i][j]);
91059
+ out.push(row);
91060
+ }
91061
+ return out;
91062
+ }
91063
+ function skin(curveCps, spanParams, spanDegree) {
91064
+ const ncp = curveCps[0].length;
91065
+ const cols = [];
91066
+ for (let j = 0; j < ncp; j++) {
91067
+ const pts = curveCps.map((row) => row[j]);
91068
+ cols.push(globalCurveInterp(pts, spanDegree, spanParams).cps);
91069
+ }
91070
+ const spanCp = cols[0].length;
91071
+ const grid = [];
91072
+ for (let s = 0; s < spanCp; s++) {
91073
+ const row = [];
91074
+ for (let j = 0; j < ncp; j++) row.push(cols[j][s]);
91075
+ grid.push(row);
91076
+ }
91077
+ const spanKnots = knotsFromParamsAveraging(spanParams, spanDegree);
91078
+ return { grid, spanKnots };
91079
+ }
91080
+ function tensorInterp(gridPts, uParams, vParams, degreeU, degreeV) {
91081
+ const rowInterp = gridPts.map((rowPts) => globalCurveInterp(rowPts, degreeV, vParams));
91082
+ const knotsV = rowInterp[0].knots;
91083
+ const ncpV = rowInterp[0].cps.length;
91084
+ const cols = [];
91085
+ for (let j = 0; j < ncpV; j++) {
91086
+ const pts = rowInterp.map((ri) => ri.cps[j]);
91087
+ cols.push(globalCurveInterp(pts, degreeU, uParams));
91088
+ }
91089
+ const knotsU = cols[0].knots;
91090
+ const cpU = cols[0].cps.length;
91091
+ const grid = [];
91092
+ for (let i = 0; i < cpU; i++) {
91093
+ const row = [];
91094
+ for (let j = 0; j < ncpV; j++) row.push(cols[j].cps[i]);
91095
+ grid.push(row);
91096
+ }
91097
+ return { grid, knotsU, knotsV };
91098
+ }
91099
+ function refineSurface(surf, targetKnotsU, targetKnotsV) {
91100
+ const { grid, knotsU, knotsV, degreeU, degreeV } = surf;
91101
+ const rows = grid.map((row) => refineKnotsToTarget(row, knotsV, degreeV, targetKnotsV).cps);
91102
+ const ncpV = rows[0].length;
91103
+ const cols = [];
91104
+ for (let j = 0; j < ncpV; j++) {
91105
+ const colPts = rows.map((r) => r[j]);
91106
+ cols.push(refineKnotsToTarget(colPts, knotsU, degreeU, targetKnotsU).cps);
91107
+ }
91108
+ const cpU = cols[0].length;
91109
+ const out = [];
91110
+ for (let i = 0; i < cpU; i++) {
91111
+ const row = [];
91112
+ for (let j = 0; j < ncpV; j++) row.push(cols[j][i]);
91113
+ out.push(row);
91114
+ }
91115
+ return { grid: out, knotsU: targetKnotsU, knotsV: targetKnotsV, degreeU, degreeV };
91116
+ }
91117
+ function averageParams(polylines, alpha = 0.5) {
91118
+ const count = polylines[0].length;
91119
+ const acc = new Array(count).fill(0);
91120
+ for (const pl of polylines) {
91121
+ const p2 = computeParamsCentripetal(pl, alpha);
91122
+ for (let i = 0; i < count; i++) acc[i] += p2[i];
91123
+ }
91124
+ return acc.map((s) => s / polylines.length);
91125
+ }
91126
+ function gordonGridParams(gridPts) {
91127
+ const rows = gridPts;
91128
+ const cols = gridPts[0].map((_, l) => gridPts.map((r) => r[l]));
91129
+ return { vParams: averageParams(rows, 0.5), uParams: averageParams(cols, 0.5) };
91130
+ }
91131
+ function buildGordonFromGrid(gridPts, degreeU = 3, degreeV = 3) {
91132
+ const m = gridPts.length - 1;
91133
+ const n = gridPts[0].length - 1;
91134
+ if (m < 1 || n < 1) throw new Error("A Gordon surface needs at least a 2x2 curve network.");
91135
+ if (m < degreeU) degreeU = m;
91136
+ if (n < degreeV) degreeV = n;
91137
+ const rows = gridPts;
91138
+ const cols = gridPts[0].map((_, l) => gridPts.map((r) => r[l]));
91139
+ const { uParams, vParams } = gordonGridParams(gridPts);
91140
+ const uCurves = rows.map((rowPts) => globalCurveInterp(rowPts, degreeV, vParams));
91141
+ const vCurves = cols.map((colPts) => globalCurveInterp(colPts, degreeU, uParams));
91142
+ const uFam = unifyCurves(uCurves);
91143
+ const vFam = unifyCurves(vCurves);
91144
+ const Lu = skin(uFam.curves, uParams, degreeU);
91145
+ const LvRaw = skin(vFam.curves, vParams, degreeV);
91146
+ const Lv = { grid: transpose(LvRaw.grid), knotsU: vFam.knots, knotsV: LvRaw.spanKnots };
91147
+ const T = tensorInterp(gridPts, uParams, vParams, degreeU, degreeV);
91148
+ const knotsU = mergeKnotVectors([Lu.spanKnots, Lv.knotsU, T.knotsU], degreeU);
91149
+ const knotsV = mergeKnotVectors([uFam.knots, Lv.knotsV, T.knotsV], degreeV);
91150
+ const LuS = refineSurface({ grid: Lu.grid, knotsU: Lu.spanKnots, knotsV: uFam.knots, degreeU, degreeV }, knotsU, knotsV);
91151
+ const LvS = refineSurface({ grid: Lv.grid, knotsU: Lv.knotsU, knotsV: Lv.knotsV, degreeU, degreeV }, knotsU, knotsV);
91152
+ const TS = refineSurface({ grid: T.grid, knotsU: T.knotsU, knotsV: T.knotsV, degreeU, degreeV }, knotsU, knotsV);
91153
+ const nuc = LuS.grid.length;
91154
+ const nvc = LuS.grid[0].length;
91155
+ if (LvS.grid.length !== nuc || TS.grid.length !== nuc || LvS.grid[0].length !== nvc || TS.grid[0].length !== nvc) {
91156
+ throw new Error(
91157
+ `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}.`
91158
+ );
91159
+ }
91160
+ const grid = [];
91161
+ for (let i = 0; i < nuc; i++) {
91162
+ const row = [];
91163
+ for (let j = 0; j < nvc; j++) {
91164
+ const a = LuS.grid[i][j];
91165
+ const b = LvS.grid[i][j];
91166
+ const c = TS.grid[i][j];
91167
+ row.push([a[0] + b[0] - c[0], a[1] + b[1] - c[1], a[2] + b[2] - c[2]]);
91168
+ }
91169
+ grid.push(row);
91170
+ }
91171
+ return { grid, knotsU, knotsV, degreeU, degreeV };
91172
+ }
91173
+ function evalSurface(surf, u, v) {
91174
+ const { grid, knotsU, knotsV, degreeU, degreeV } = surf;
91175
+ const nu = grid.length;
91176
+ const nv = grid[0].length;
91177
+ const su = findSpan(nu, degreeU, u, knotsU);
91178
+ const sv = findSpan(nv, degreeV, v, knotsV);
91179
+ const Nu = basisFuns(su, u, degreeU, knotsU);
91180
+ const Nv = basisFuns(sv, v, degreeV, knotsV);
91181
+ const out = [0, 0, 0];
91182
+ for (let i = 0; i <= degreeU; i++) {
91183
+ const ui = su - degreeU + i;
91184
+ for (let j = 0; j <= degreeV; j++) {
91185
+ const vj = sv - degreeV + j;
91186
+ const w = Nu[i] * Nv[j];
91187
+ const p2 = grid[ui][vj];
91188
+ out[0] += w * p2[0];
91189
+ out[1] += w * p2[1];
91190
+ out[2] += w * p2[2];
91191
+ }
91192
+ }
91193
+ return out;
91194
+ }
91195
+ function evalSurfaceJet(surf, u, v) {
91196
+ const { grid, knotsU, knotsV, degreeU, degreeV } = surf;
91197
+ const nu = grid.length;
91198
+ const nv = grid[0].length;
91199
+ const su = findSpan(nu, degreeU, u, knotsU);
91200
+ const sv = findSpan(nv, degreeV, v, knotsV);
91201
+ const dU = basisFunsDeriv(su, u, degreeU, knotsU, 2);
91202
+ const dV = basisFunsDeriv(sv, v, degreeV, knotsV, 2);
91203
+ const S = [0, 0, 0];
91204
+ const Su = [0, 0, 0];
91205
+ const Sv = [0, 0, 0];
91206
+ const Suu = [0, 0, 0];
91207
+ const Suv = [0, 0, 0];
91208
+ const Svv = [0, 0, 0];
91209
+ for (let i = 0; i <= degreeU; i++) {
91210
+ const ui = su - degreeU + i;
91211
+ for (let j = 0; j <= degreeV; j++) {
91212
+ const vj = sv - degreeV + j;
91213
+ const p2 = grid[ui][vj];
91214
+ const w00 = dU[0][i] * dV[0][j];
91215
+ const w10 = dU[1][i] * dV[0][j];
91216
+ const w01 = dU[0][i] * dV[1][j];
91217
+ const w20 = dU[2][i] * dV[0][j];
91218
+ const w11 = dU[1][i] * dV[1][j];
91219
+ const w02 = dU[0][i] * dV[2][j];
91220
+ for (let c = 0; c < 3; c++) {
91221
+ S[c] += w00 * p2[c];
91222
+ Su[c] += w10 * p2[c];
91223
+ Sv[c] += w01 * p2[c];
91224
+ Suu[c] += w20 * p2[c];
91225
+ Suv[c] += w11 * p2[c];
91226
+ Svv[c] += w02 * p2[c];
91227
+ }
91228
+ }
91229
+ }
91230
+ let nx = Su[1] * Sv[2] - Su[2] * Sv[1];
91231
+ let ny = Su[2] * Sv[0] - Su[0] * Sv[2];
91232
+ let nz = Su[0] * Sv[1] - Su[1] * Sv[0];
91233
+ const len2 = Math.hypot(nx, ny, nz) || 1;
91234
+ nx /= len2;
91235
+ ny /= len2;
91236
+ nz /= len2;
91237
+ return { S, Su, Sv, Suu, Suv, Svv, normal: [nx, ny, nz] };
91238
+ }
91239
+ function surfaceCurvature(jet) {
91240
+ const { Su, Sv, Suu, Suv, Svv, normal: normal2 } = jet;
91241
+ const E = Su[0] * Su[0] + Su[1] * Su[1] + Su[2] * Su[2];
91242
+ const F = Su[0] * Sv[0] + Su[1] * Sv[1] + Su[2] * Sv[2];
91243
+ const G = Sv[0] * Sv[0] + Sv[1] * Sv[1] + Sv[2] * Sv[2];
91244
+ const e = Suu[0] * normal2[0] + Suu[1] * normal2[1] + Suu[2] * normal2[2];
91245
+ const f4 = Suv[0] * normal2[0] + Suv[1] * normal2[1] + Suv[2] * normal2[2];
91246
+ const g = Svv[0] * normal2[0] + Svv[1] * normal2[1] + Svv[2] * normal2[2];
91247
+ const denom = E * G - F * F;
91248
+ if (Math.abs(denom) < 1e-20) return { k1: 0, k2: 0, K: 0, H: 0 };
91249
+ const K = (e * g - f4 * f4) / denom;
91250
+ const H = (e * G - 2 * f4 * F + g * E) / (2 * denom);
91251
+ const disc = Math.max(0, H * H - K);
91252
+ const root = Math.sqrt(disc);
91253
+ return { k1: H + root, k2: H - root, K, H };
91254
+ }
91255
+
91256
+ // src/forge/surfacing/curveNet.ts
91257
+ function isVec34(v) {
91258
+ return Array.isArray(v) && v.length === 3 && typeof v[0] === "number" && typeof v[1] === "number" && typeof v[2] === "number";
91259
+ }
91260
+ function requireFiniteVec(p2, label) {
91261
+ for (let i = 0; i < 3; i++)
91262
+ if (!Number.isFinite(p2[i])) throw new Error(`Surface.Net: ${label} component ${i} must be finite, got ${p2[i]}`);
91263
+ return [p2[0], p2[1], p2[2]];
91264
+ }
91265
+ function toSampler(input, label) {
91266
+ if (input instanceof NurbsCurve3D) {
91267
+ return (t) => input.pointAt(t);
91268
+ }
91269
+ if (Array.isArray(input) && input.length > 0 && isVec34(input[0])) {
91270
+ const pts = input.map((p2, i) => requireFiniteVec(p2, `${label}[${i}]`));
91271
+ if (pts.length < 2) throw new Error(`Surface.Net: ${label} needs at least 2 points.`);
91272
+ if (pts.length === 2) {
91273
+ const [a, b] = pts;
91274
+ return (t) => [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t];
91275
+ }
91276
+ const curve = globalCurveInterp(pts, Math.min(3, pts.length - 1));
91277
+ const nc = new NurbsCurve3D(curve.cps, { degree: curve.degree, knots: curve.knots });
91278
+ return (t) => nc.pointAt(t);
91279
+ }
91280
+ throw new Error(`Surface.Net: ${label} must be a Curve.Fit/Curve.Nurbs value or an array of [x,y,z] points.`);
91281
+ }
91282
+ function sampleCurve(sampler, count) {
91283
+ const out = [];
91284
+ for (let i = 0; i < count; i++) out.push(sampler(count > 1 ? i / (count - 1) : 0));
91285
+ return out;
91286
+ }
91287
+ var DEFAULT_THICKEN_RESOLUTION = 48;
91288
+ var Sheet = class {
91289
+ constructor(surface) {
91290
+ this.surface = surface;
91291
+ }
91292
+ /** Edge naming follows parameter direction (documented): front=v0, rear=v1, left=u0, right=u1. */
91293
+ get frontEdge() {
91294
+ return { sheet: this, fixed: "v", value: 0 };
91295
+ }
91296
+ get rearEdge() {
91297
+ return { sheet: this, fixed: "v", value: 1 };
91298
+ }
91299
+ get leftEdge() {
91300
+ return { sheet: this, fixed: "u", value: 0 };
91301
+ }
91302
+ get rightEdge() {
91303
+ return { sheet: this, fixed: "u", value: 1 };
91304
+ }
91305
+ pointAt(u, v) {
91306
+ return evalSurface(this.surface, clamp013(u), clamp013(v));
91307
+ }
91308
+ normalAt(u, v) {
91309
+ return evalSurfaceJet(this.surface, clamp013(u), clamp013(v)).normal;
91310
+ }
91311
+ curvatureAt(u, v) {
91312
+ return surfaceCurvature(evalSurfaceJet(this.surface, clamp013(u), clamp013(v)));
91313
+ }
91314
+ /** Largest principal curvature magnitude over a sampling grid (for offset safety). */
91315
+ maxAbsPrincipalCurvature(samples = 9) {
91316
+ let maxK = 0;
91317
+ for (let i = 0; i <= samples; i++) {
91318
+ for (let j = 0; j <= samples; j++) {
91319
+ const c = this.curvatureAt(i / samples, j / samples);
91320
+ maxK = Math.max(maxK, Math.abs(c.k1), Math.abs(c.k2));
91321
+ }
91322
+ }
91323
+ return maxK;
91324
+ }
91325
+ /**
91326
+ * Offset the sheet along its analytic normals into a watertight solid shell of
91327
+ * the given wall thickness. Throws if the wall would self-intersect on a
91328
+ * concave region (no silent degenerate solid).
91329
+ */
91330
+ thicken(wall, options = {}) {
91331
+ if (!Number.isFinite(wall) || wall <= 0) throw new Error(`Sheet.thicken: wall must be a positive finite number, got ${wall}`);
91332
+ const maxK = this.maxAbsPrincipalCurvature();
91333
+ if (wall * maxK >= 1) {
91334
+ throw new Error(
91335
+ `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.`
91336
+ );
91337
+ }
91338
+ const resolution = options.resolution ?? DEFAULT_THICKEN_RESOLUTION;
91339
+ return nurbsSurface(this.surface.grid, {
91340
+ degreeU: this.surface.degreeU,
91341
+ degreeV: this.surface.degreeV,
91342
+ knotsU: this.surface.knotsU,
91343
+ knotsV: this.surface.knotsV,
91344
+ thickness: wall,
91345
+ resolution
91346
+ });
91347
+ }
91348
+ /** Per-edge continuity match against a neighbor (returns a NEW Sheet). */
91349
+ matchEdge(edge) {
91350
+ if (edge.sheet !== this) throw new Error("Sheet.matchEdge: the edge must belong to this sheet.");
91351
+ return new MatchEdgeBuilder(this, edge);
91352
+ }
91353
+ };
91354
+ function clamp013(t) {
91355
+ return t < 0 ? 0 : t > 1 ? 1 : t;
91356
+ }
91357
+ var CurveNetBuilder = class {
91358
+ lengthwiseCurves = [];
91359
+ crosswiseCurves = [];
91360
+ railCurves = [];
91361
+ cageGrid = null;
91362
+ degU;
91363
+ degV;
91364
+ built = null;
91365
+ lengthwise(...curves) {
91366
+ this.lengthwiseCurves = curves;
91367
+ this.built = null;
91368
+ return this;
91369
+ }
91370
+ crosswise(...curves) {
91371
+ this.crosswiseCurves = curves;
91372
+ this.built = null;
91373
+ return this;
91374
+ }
91375
+ alongRails(railA, railB) {
91376
+ this.railCurves = [railA, railB];
91377
+ this.built = null;
91378
+ return this;
91379
+ }
91380
+ sections(...curves) {
91381
+ this.crosswiseCurves = curves;
91382
+ this.built = null;
91383
+ return this;
91384
+ }
91385
+ cage(grid) {
91386
+ if (!Array.isArray(grid) || grid.length < 2 || !Array.isArray(grid[0]) || grid[0].length < 2) {
91387
+ throw new Error("Surface.Net().cage: grid must be at least a 2x2 array of [x,y,z] points.");
91388
+ }
91389
+ const cols = grid[0].length;
91390
+ this.cageGrid = grid.map((row, k) => {
91391
+ if (row.length !== cols) throw new Error(`Surface.Net().cage: row ${k} has ${row.length} points, expected ${cols}.`);
91392
+ return row.map((p2, l) => requireFiniteVec(p2, `cage[${k}][${l}]`));
91393
+ });
91394
+ this.built = null;
91395
+ return this;
91396
+ }
91397
+ degree(u, v) {
91398
+ if (!Number.isInteger(u) || u < 1) throw new Error(`Surface.Net().degree: degree must be a positive integer, got ${u}`);
91399
+ this.degU = u;
91400
+ this.degV = v === void 0 ? u : v;
91401
+ if (v !== void 0 && (!Number.isInteger(v) || v < 1))
91402
+ throw new Error(`Surface.Net().degree: degree must be a positive integer, got ${v}`);
91403
+ this.built = null;
91404
+ return this;
91405
+ }
91406
+ /** Build (once) and return the Sheet. */
91407
+ toSheet() {
91408
+ if (this.built) return this.built;
91409
+ const grid = this.buildGrid();
91410
+ const m = grid.length - 1;
91411
+ const n = grid[0].length - 1;
91412
+ const degreeU = Math.max(1, Math.min(this.degU ?? 3, m));
91413
+ const degreeV = Math.max(1, Math.min(this.degV ?? 3, n));
91414
+ const surface = buildGordonFromGrid(grid, degreeU, degreeV);
91415
+ this.built = new Sheet(surface);
91416
+ return this.built;
91417
+ }
91418
+ buildGrid() {
91419
+ if (this.cageGrid) return this.cageGrid;
91420
+ const lengthwise = this.railCurves.length > 0 ? this.railCurves : this.lengthwiseCurves;
91421
+ const crosswise = this.crosswiseCurves;
91422
+ const SAMPLES = 17;
91423
+ if (lengthwise.length >= 2) {
91424
+ const samplers = lengthwise.map((c, l) => toSampler(c, `lengthwise[${l}]`));
91425
+ const grid = [];
91426
+ for (let i = 0; i < SAMPLES; i++) {
91427
+ const u = i / (SAMPLES - 1);
91428
+ grid.push(samplers.map((s) => s(u)));
91429
+ }
91430
+ return grid;
91431
+ }
91432
+ if (crosswise.length >= 2) {
91433
+ const samplers = crosswise.map((c, k) => toSampler(c, `crosswise[${k}]`));
91434
+ const grid = [];
91435
+ for (const s of samplers) grid.push(sampleCurve(s, SAMPLES));
91436
+ return grid;
91437
+ }
91438
+ throw new Error(
91439
+ "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 \u2014 supply the denser family or a cage."
91440
+ );
91441
+ }
91442
+ // ── Sheet delegation (build on first access) ──────────────────────────────
91443
+ get frontEdge() {
91444
+ return this.toSheet().frontEdge;
91445
+ }
91446
+ get rearEdge() {
91447
+ return this.toSheet().rearEdge;
91448
+ }
91449
+ get leftEdge() {
91450
+ return this.toSheet().leftEdge;
91451
+ }
91452
+ get rightEdge() {
91453
+ return this.toSheet().rightEdge;
91454
+ }
91455
+ get surface() {
91456
+ return this.toSheet().surface;
91457
+ }
91458
+ pointAt(u, v) {
91459
+ return this.toSheet().pointAt(u, v);
91460
+ }
91461
+ normalAt(u, v) {
91462
+ return this.toSheet().normalAt(u, v);
91463
+ }
91464
+ curvatureAt(u, v) {
91465
+ return this.toSheet().curvatureAt(u, v);
91466
+ }
91467
+ thicken(wall, options) {
91468
+ return this.toSheet().thicken(wall, options);
91469
+ }
91470
+ matchEdge(edge) {
91471
+ return this.toSheet().matchEdge(edge);
91472
+ }
91473
+ };
91474
+ function createCurveNet() {
91475
+ return new CurveNetBuilder();
91476
+ }
91477
+ var MatchEdgeBuilder = class {
91478
+ constructor(sheet, edge) {
91479
+ this.sheet = sheet;
91480
+ this.edge = edge;
91481
+ }
91482
+ toG0(neighbor) {
91483
+ return applyEdgeMatch(this.sheet, this.edge, neighbor, 0);
91484
+ }
91485
+ toG1(neighbor) {
91486
+ return applyEdgeMatch(this.sheet, this.edge, neighbor, 1);
91487
+ }
91488
+ toG2(neighbor) {
91489
+ return applyEdgeMatch(this.sheet, this.edge, neighbor, 2);
91490
+ }
91491
+ };
91492
+ function boundaryRows(surf, fixed, value, depth) {
91493
+ const rows = [];
91494
+ if (fixed === "u") {
91495
+ const nU = surf.grid.length;
91496
+ for (let d = 0; d <= depth; d++) {
91497
+ const i = value === 0 ? d : nU - 1 - d;
91498
+ rows.push(surf.grid[i].map((p2) => [p2[0], p2[1], p2[2]]));
91499
+ }
91500
+ } else {
91501
+ const nV = surf.grid[0].length;
91502
+ for (let d = 0; d <= depth; d++) {
91503
+ const j = value === 0 ? d : nV - 1 - d;
91504
+ rows.push(surf.grid.map((p2) => [p2[j][0], p2[j][1], p2[j][2]]));
91505
+ }
91506
+ }
91507
+ return rows;
91508
+ }
91509
+ function setBoundaryRow(surf, fixed, value, depth, row) {
91510
+ if (fixed === "u") {
91511
+ const nU = surf.grid.length;
91512
+ const i = value === 0 ? depth : nU - 1 - depth;
91513
+ for (let l = 0; l < surf.grid[i].length; l++) surf.grid[i][l] = [row[l][0], row[l][1], row[l][2]];
91514
+ } else {
91515
+ const nV = surf.grid[0].length;
91516
+ const j = value === 0 ? depth : nV - 1 - depth;
91517
+ for (let k = 0; k < surf.grid.length; k++) surf.grid[k][j] = [row[k][0], row[k][1], row[k][2]];
91518
+ }
91519
+ }
91520
+ function cloneSurface(surf) {
91521
+ return {
91522
+ grid: surf.grid.map((row) => row.map((p2) => [p2[0], p2[1], p2[2]])),
91523
+ knotsU: [...surf.knotsU],
91524
+ knotsV: [...surf.knotsV],
91525
+ degreeU: surf.degreeU,
91526
+ degreeV: surf.degreeV
91527
+ };
91528
+ }
91529
+ function applyEdgeMatch(sheet, edge, neighbor, order) {
91530
+ const result = cloneSurface(sheet.surface);
91531
+ const my = boundaryRows(result, edge.fixed, edge.value, order);
91532
+ const their = boundaryRows(neighbor.sheet.surface, neighbor.fixed, neighbor.value, order);
91533
+ if (my[0].length !== their[0].length) {
91534
+ throw new Error(
91535
+ `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.`
91536
+ );
91537
+ }
91538
+ const len2 = my[0].length;
91539
+ const b0 = their[0].map((p2) => [p2[0], p2[1], p2[2]]);
91540
+ setBoundaryRow(result, edge.fixed, edge.value, 0, b0);
91541
+ if (order === 0) return new Sheet(result);
91542
+ const b1 = [];
91543
+ for (let i = 0; i < len2; i++) {
91544
+ const p0 = their[0][i];
91545
+ const p1 = their[1][i];
91546
+ b1.push([2 * p0[0] - p1[0], 2 * p0[1] - p1[1], 2 * p0[2] - p1[2]]);
91547
+ }
91548
+ setBoundaryRow(result, edge.fixed, edge.value, 1, b1);
91549
+ if (order === 1) return new Sheet(result);
91550
+ const b22 = [];
91551
+ for (let i = 0; i < len2; i++) {
91552
+ const p0 = their[0][i];
91553
+ const p1 = their[1][i];
91554
+ const p2 = their[2][i];
91555
+ 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]]);
91556
+ }
91557
+ setBoundaryRow(result, edge.fixed, edge.value, 2, b22);
91558
+ return new Sheet(result);
91559
+ }
91560
+ function edgeJet(edge, t) {
91561
+ const surf = edge.sheet.surface;
91562
+ if (edge.fixed === "u") {
91563
+ const j2 = evalSurfaceJet(surf, edge.value, clamp013(t));
91564
+ return { point: j2.S, cross: j2.Su, cross2: j2.Suu };
91565
+ }
91566
+ const j = evalSurfaceJet(surf, clamp013(t), edge.value);
91567
+ return { point: j.S, cross: j.Sv, cross2: j.Svv };
91568
+ }
91569
+ function sub39(a, b) {
91570
+ return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
91571
+ }
91572
+ function len34(a) {
91573
+ return Math.hypot(a[0], a[1], a[2]);
91574
+ }
91575
+ function unit3(a) {
91576
+ const l = len34(a) || 1;
91577
+ return [a[0] / l, a[1] / l, a[2] / l];
91578
+ }
91579
+ function edgeMatchReport(edgeA, edgeB, samples = 24) {
91580
+ let maxPositionGap = 0;
91581
+ let maxTangentAngleDeg = 0;
91582
+ let maxCurvatureRelErr = 0;
91583
+ for (let i = 0; i <= samples; i++) {
91584
+ const t = i / samples;
91585
+ const a = edgeJet(edgeA, t);
91586
+ const b = edgeJet(edgeB, t);
91587
+ maxPositionGap = Math.max(maxPositionGap, len34(sub39(a.point, b.point)));
91588
+ const ua = unit3(a.cross);
91589
+ const ub = unit3(b.cross);
91590
+ const dot12 = Math.max(-1, Math.min(1, ua[0] * ub[0] + ua[1] * ub[1] + ua[2] * ub[2]));
91591
+ const angle = Math.acos(Math.abs(dot12)) * 180 / Math.PI;
91592
+ maxTangentAngleDeg = Math.max(maxTangentAngleDeg, angle);
91593
+ const la = len34(a.cross) ** 2 || 1;
91594
+ const lb = len34(b.cross) ** 2 || 1;
91595
+ const ka = (a.cross2[0] * ua[0] + a.cross2[1] * ua[1] + a.cross2[2] * ua[2]) / la;
91596
+ const kb = (b.cross2[0] * ub[0] + b.cross2[1] * ub[1] + b.cross2[2] * ub[2]) / lb;
91597
+ maxCurvatureRelErr = Math.max(maxCurvatureRelErr, Math.abs(ka - kb) / (Math.abs(ka) + Math.abs(kb) + 1e-9));
91598
+ }
91599
+ return { maxPositionGap, maxTangentAngleDeg, maxCurvatureRelErr };
91600
+ }
91601
+ function bridgeBetween(edgeA, edgeB) {
91602
+ return new BridgeBuilder(edgeA, edgeB);
91603
+ }
91604
+ var BridgeBuilder = class {
91605
+ constructor(edgeA, edgeB) {
91606
+ this.edgeA = edgeA;
91607
+ this.edgeB = edgeB;
91608
+ }
91609
+ bulgeA = 0.5;
91610
+ bulgeB = 0.5;
91611
+ /** Tune the influence of each side (Rhino-style bulge). */
91612
+ bulge(a, b) {
91613
+ if (!Number.isFinite(a) || !Number.isFinite(b)) throw new Error("Surfaces.bridge.bulge: both factors must be finite.");
91614
+ this.bulgeA = a;
91615
+ this.bulgeB = b;
91616
+ return this;
91617
+ }
91618
+ g0() {
91619
+ return this.build(0);
91620
+ }
91621
+ g1() {
91622
+ return this.build(1);
91623
+ }
91624
+ g2() {
91625
+ return this.build(2);
91626
+ }
91627
+ build(order) {
91628
+ const SAMPLES = 21;
91629
+ const CROSS = 11;
91630
+ const cage = [];
91631
+ for (let i = 0; i < SAMPLES; i++) {
91632
+ const t = i / (SAMPLES - 1);
91633
+ const a = edgeJet(this.edgeA, t);
91634
+ const b = edgeJet(this.edgeB, t);
91635
+ const chord = len34(sub39(b.point, a.point));
91636
+ const toward = unit3(sub39(b.point, a.point));
91637
+ const tA = orientToward(unit3(a.cross), toward);
91638
+ const tB = orientToward(unit3(b.cross), [-toward[0], -toward[1], -toward[2]]);
91639
+ const poles = bridgePoles(a.point, b.point, tA, tB, chord, this.bulgeA, this.bulgeB, order);
91640
+ const row = [];
91641
+ for (let c = 0; c < CROSS; c++) row.push(deCasteljau(poles, c / (CROSS - 1)));
91642
+ cage.push(row);
91643
+ }
91644
+ return new CurveNetBuilder().cage(cage).degree(Math.min(3, SAMPLES - 1), Math.min(2 * order + 1 || 1, CROSS - 1)).toSheet();
91645
+ }
91646
+ };
91647
+ function orientToward(v, toward) {
91648
+ const dot12 = v[0] * toward[0] + v[1] * toward[1] + v[2] * toward[2];
91649
+ return dot12 < 0 ? [-v[0], -v[1], -v[2]] : v;
91650
+ }
91651
+ function bridgePoles(a, b, tA, tB, chord, bulgeA, bulgeB, order) {
91652
+ if (order === 0) return [a, b];
91653
+ const dA = chord * bulgeA / (order === 1 ? 3 : 5);
91654
+ const dB = chord * bulgeB / (order === 1 ? 3 : 5);
91655
+ const a1 = [a[0] + tA[0] * dA, a[1] + tA[1] * dA, a[2] + tA[2] * dA];
91656
+ const b1 = [b[0] + tB[0] * dB, b[1] + tB[1] * dB, b[2] + tB[2] * dB];
91657
+ if (order === 1) return [a, a1, b1, b];
91658
+ const a2 = [a1[0] + tA[0] * dA, a1[1] + tA[1] * dA, a1[2] + tA[2] * dA];
91659
+ const b22 = [b1[0] + tB[0] * dB, b1[1] + tB[1] * dB, b1[2] + tB[2] * dB];
91660
+ return [a, a1, a2, b22, b1, b];
91661
+ }
91662
+ function deCasteljau(poles, s) {
91663
+ let pts = poles.map((p2) => [p2[0], p2[1], p2[2]]);
91664
+ while (pts.length > 1) {
91665
+ const next = [];
91666
+ for (let i = 0; i < pts.length - 1; i++) {
91667
+ next.push([
91668
+ pts[i][0] + (pts[i + 1][0] - pts[i][0]) * s,
91669
+ pts[i][1] + (pts[i + 1][1] - pts[i][1]) * s,
91670
+ pts[i][2] + (pts[i + 1][2] - pts[i][2]) * s
91671
+ ]);
91672
+ }
91673
+ pts = next;
91674
+ }
91675
+ return pts[0];
91676
+ }
91677
+
90819
91678
  // src/forge/surfacing.ts
90820
91679
  var CORNER_Y_ALPHA_ISSUE_URL = "https://github.com/KoStard/forgecad-private/issues/162";
90821
91680
  function isVec3Array(value) {
@@ -92204,7 +93063,16 @@ var Surface = {
92204
93063
  "Surface.MatchEdge",
92205
93064
  "Surface.Match()",
92206
93065
  (shape, options) => Surface.Match(shape, options)
92207
- )
93066
+ ),
93067
+ /**
93068
+ * Begin a curve-network (Gordon) surface — the class-A keystone. Chain
93069
+ * `.lengthwise(...)/.crosswise(...)` (or `.alongRails(a,b).sections(...)`, or
93070
+ * `.cage(grid)`), then `.thicken(wall)` to get a solid Shape. Returns a fluent
93071
+ * `Sheet` builder with analytic point/normal/curvature queries and named edges.
93072
+ */
93073
+ Net() {
93074
+ return createCurveNet();
93075
+ }
92208
93076
  };
92209
93077
  var Blend = {
92210
93078
  Edge(options) {
@@ -92224,6 +93092,14 @@ var Blend = {
92224
93092
  Surface(options) {
92225
93093
  return Surface.Fill(options);
92226
93094
  },
93095
+ /**
93096
+ * Build a transition strip between two `Surface.Net` sheet edges. Chain
93097
+ * `.bulge(a, b)` then `.g0()/.g1()/.g2()` for the continuity order. Returns a
93098
+ * `Sheet`; verify the seam with `Analysis.EdgeMatch`.
93099
+ */
93100
+ Bridge(edgeA, edgeB) {
93101
+ return bridgeBetween(edgeA, edgeB);
93102
+ },
92227
93103
  /**
92228
93104
  * @alpha
92229
93105
  * Current implementation uses continuity-controlled edge fillets on solid edges.
@@ -92258,6 +93134,14 @@ var Analysis = {
92258
93134
  SurfaceContinuity(shape, options = {}) {
92259
93135
  return evaluateEdgeContinuityReport(shape, options, "Analysis.SurfaceContinuity()");
92260
93136
  },
93137
+ /**
93138
+ * Measure G0/G1/G2 agreement between two `Surface.Net` sheet edges: worst
93139
+ * position gap, cross-boundary tangent angle (0 = G1), and normal-curvature
93140
+ * mismatch (0 = G2). The reflection/fairness check for matched panel seams.
93141
+ */
93142
+ EdgeMatch(edgeA, edgeB, options = {}) {
93143
+ return edgeMatchReport(edgeA, edgeB, options.samples);
93144
+ },
92261
93145
  CurvatureComb(input, options = {}) {
92262
93146
  if (input instanceof NurbsCurve3D) {
92263
93147
  const count = Math.max(8, options.samples ?? 32);
@@ -96222,22 +97106,10 @@ function roundNum(n, digits = 4) {
96222
97106
  return Number.isFinite(n) ? n.toFixed(digits).replace(/\.?0+$/, "") : String(n);
96223
97107
  }
96224
97108
  var COLLISION_OVERLAP_VOLUME_TOLERANCE = 1e-6;
96225
- function meshDerivedManifoldBackend(shape) {
96226
- const mesh = getShapeRuntimeBackend(shape).getMesh();
96227
- return reconstructBackendFromMesh({
96228
- numProp: mesh.numProp,
96229
- triVerts: mesh.triVerts,
96230
- vertProperties: mesh.vertProperties,
96231
- mergeFromVert: mesh.mergeFromVert ?? new Uint32Array(),
96232
- mergeToVert: mesh.mergeToVert ?? new Uint32Array()
96233
- });
96234
- }
96235
97109
  function backendForMinGap(shape) {
96236
97110
  const backend = getShapeRuntimeBackend(shape);
96237
97111
  if (isManifoldCapableBackend(backend)) return { kind: "manifold", backend, method: "exact", dispose: false };
96238
- if (isSdfCapableBackend(backend) || getActiveBackend() === "sdf")
96239
- return { kind: "mesh", backend, method: "mesh-derived", dispose: false };
96240
- return { kind: "manifold", backend: meshDerivedManifoldBackend(shape), method: "mesh-derived", dispose: true };
97112
+ return { kind: "mesh", backend, method: "mesh-derived", dispose: false };
96241
97113
  }
96242
97114
  function meshHasPointInsideSdf(source, target) {
96243
97115
  const mesh = source.getMesh();
@@ -97528,7 +98400,6 @@ function resetExecutionSession(logs) {
97528
98400
  resetHighlights();
97529
98401
  resetBom();
97530
98402
  resetSheetStock();
97531
- resetRobotExport();
97532
98403
  resetCutPlanes();
97533
98404
  resetRenderLabels();
97534
98405
  resetCameraTrajectory();
@@ -97611,7 +98482,6 @@ function collectSuccessfulExecutionSnapshot(args) {
97611
98482
  jointsView: getCollectedJointsView(),
97612
98483
  viewConfig: getCollectedViewConfig(),
97613
98484
  sceneConfig: getCollectedScene(),
97614
- robotExport: getCollectedRobotExport(),
97615
98485
  quality: args.quality,
97616
98486
  logs: args.logs.slice(),
97617
98487
  verifications: getCollectedVerifications(),
@@ -97636,7 +98506,6 @@ function collectFailedExecutionSnapshot(args) {
97636
98506
  jointsView: getCollectedJointsView(),
97637
98507
  viewConfig: getCollectedViewConfig(),
97638
98508
  sceneConfig: getCollectedScene(),
97639
- robotExport: getCollectedRobotExport(),
97640
98509
  quality: args.quality,
97641
98510
  logs: args.logs.slice(),
97642
98511
  verifications: getCollectedVerifications(),
@@ -99609,7 +100478,6 @@ function executeFile(code, fileName, allFiles, visited, scope = {}, options, exe
99609
100478
  sketchToDxf,
99610
100479
  bom,
99611
100480
  sheetStock,
99612
- robotExport,
99613
100481
  Sim,
99614
100482
  group,
99615
100483
  ShapeGroup,
@@ -100849,7 +101717,6 @@ export {
100849
101717
  buildCompiledSceneReport,
100850
101718
  mapDimensionsToOwnerIds,
100851
101719
  generateReportPdf,
100852
- getCollectedRobotExport,
100853
101720
  collectSimulationModel,
100854
101721
  validateSimulationModel,
100855
101722
  buildSimReadyManifest,