forgecad 0.9.15 → 0.9.16

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 (70) hide show
  1. package/dist/assets/{AdminPage-CDyGUinA.js → AdminPage-CXvls4-J.js} +1 -1
  2. package/dist/assets/{BenchmarkPage-DfPMY_-d.js → BenchmarkPage-B27zk8xL.js} +1 -1
  3. package/dist/assets/{BlogPage-kF0fkdJT.js → BlogPage-CMAVvgQL.js} +1 -1
  4. package/dist/assets/{DocsPage-B954L3YN.js → DocsPage-knf4I4h7.js} +1 -1
  5. package/dist/assets/EditorApp-BHMQlJ-D.js +14686 -0
  6. package/dist/assets/{EditorApp-CuDLxKqL.css → EditorApp-BpjZgzk0.css} +148 -0
  7. package/dist/assets/{EmbedViewer-C77B-TrF.js → EmbedViewer-D7ZGlFjx.js} +2 -2
  8. package/dist/assets/{LandingPageProofDriven-Cr6fXMDj.js → LandingPageProofDriven-CnevhTE8.js} +2 -2
  9. package/dist/assets/{LegalPage-Dzklqmmg.js → LegalPage-BPTUmqeg.js} +1 -1
  10. package/dist/assets/{PricingPage-zWXkvlwl.js → PricingPage-B0D4goG_.js} +1 -1
  11. package/dist/assets/{SettingsPage-Bz0of4KQ.js → SettingsPage-CFF-UgjI.js} +1 -1
  12. package/dist/assets/{app-D3kDkggg.js → app-T0pDcSX4.js} +1184 -218
  13. package/dist/assets/cli/{render-DSY3mMQa.js → render-C5pcIISc.js} +144 -26
  14. package/dist/assets/{constructionHistoryWorker-gpDo-uH2.js → constructionHistoryWorker-Ba2Hm58b.js} +1 -0
  15. package/dist/assets/{evalWorker-CU0Ke6DP.js → evalWorker-vkx310U2.js} +1380 -2173
  16. package/dist/assets/{inspectWorker-COyp8XXA.js → inspectWorker-BuTJDVX6.js} +252 -30
  17. package/dist/assets/{targets-B9sGB5nB.js → jointPose-B_Cgedn9.js} +71 -3
  18. package/dist/assets/{manifold-DNkrUWpA.js → manifold-BWgsjmAM.js} +1 -1
  19. package/dist/assets/{manifold-C-3h2M7p.js → manifold-D6IFSkhH.js} +2 -2
  20. package/dist/assets/{manifold-BRI5prcH.js → manifold-rZexZI0G.js} +1 -1
  21. package/dist/assets/{reportWorker-CdBz5bNg.js → reportWorker-0AGij1Ru.js} +1373 -2166
  22. package/dist/assets/{scalar-sampling-budget-wJF98aY9.js → scalar-sampling-budget-J5cuzxT1.js} +1494 -2251
  23. package/dist/assets/{scanProxyWorker-B-9VbLIs.js → scanProxyWorker-Vl4Wxa1y.js} +18 -5
  24. package/dist/cli/render.html +1 -1
  25. package/dist/docs/index.html +1 -1
  26. package/dist/docs-raw/AI/usage.md +2 -0
  27. package/dist/docs-raw/CLI.md +4 -0
  28. package/dist/docs-raw/generated/assembly.md +104 -6
  29. package/dist/docs-raw/generated/concepts.md +14 -4
  30. package/dist/docs-raw/generated/lib.md +2 -18
  31. package/dist/docs-raw/generated/output.md +14 -4
  32. package/dist/docs-raw/generated/runtime-names.md +27 -19
  33. package/dist/docs-raw/skills/forgecad-make-a-model.md +39 -38
  34. package/dist/docs-raw/skills/forgecad-project.md +2 -0
  35. package/dist/docs-raw/welcome.md +2 -0
  36. package/dist/index.html +1 -1
  37. package/dist/sitemap.xml +13 -13
  38. package/dist-cli/{check-compiler-SDX5QIXI.js → check-compiler-SYQ2PWOB.js} +1 -1
  39. package/dist-cli/{check-query-propagation-EAYEFT77.js → check-query-propagation-HIAGV62W.js} +1 -1
  40. package/dist-cli/{chunk-N4O47JLF.js → chunk-SPZE3DUY.js} +1591 -2356
  41. package/dist-cli/forgecad.js +1698 -487
  42. package/dist-skill/CONTEXT.md +117 -46
  43. package/dist-skill/docs/CLI.md +4 -0
  44. package/dist-skill/docs/generated/assembly.md +83 -5
  45. package/dist-skill/docs/generated/lib.md +2 -18
  46. package/dist-skill/docs/generated/output.md +14 -4
  47. package/dist-skill/docs/generated/runtime-names.md +18 -19
  48. package/dist-skill/library/forgecad-make-a-model/SKILL.md +39 -38
  49. package/dist-skill/library/forgecad-project/SKILL.md +2 -0
  50. package/examples/api/helix-basics.forge.js +2 -2
  51. package/examples/api/route3d-elbow.forge.js +3 -0
  52. package/examples/api/variable-sweep-test.forge.js +3 -1
  53. package/package.json +4 -1
  54. package/dist/assets/EditorApp-Beb-IZ0y.js +0 -14014
  55. package/examples/api/bolted-service-cover.forge.js +0 -17
  56. package/examples/api/cable-gland-anchor.forge.js +0 -14
  57. package/examples/api/captured-cartridge-guide.forge.js +0 -14
  58. package/examples/api/captured-linear-slide.forge.js +0 -13
  59. package/examples/api/clevis-pin-joint.forge.js +0 -13
  60. package/examples/api/datum-enclosure.forge.js +0 -16
  61. package/examples/api/hose-barb-port.forge.js +0 -14
  62. package/examples/api/knuckled-hinge-assembly.forge.js +0 -15
  63. package/examples/api/living-hinge-cover.forge.js +0 -14
  64. package/examples/api/pcb-terminal-block.forge.js +0 -22
  65. package/examples/api/pinned-lever-pivot-stack.forge.js +0 -14
  66. package/examples/api/retained-shaft-knob-stack.forge.js +0 -15
  67. package/examples/api/routed-tube-clip.forge.js +0 -15
  68. package/examples/api/seated-bearing-stack.forge.js +0 -30
  69. package/examples/api/snap-latch-cover.forge.js +0 -14
  70. package/examples/api/thumb-screw-clamp.forge.js +0 -15
@@ -12589,6 +12589,7 @@ class Transform {
12589
12589
  return this.rotateAxis([0, 0, 1], angleDeg, pivot);
12590
12590
  }
12591
12591
  /** Scale after the current transform. */
12592
+ // biome-ignore lint/suspicious/useAdjacentOverloadSignatures: Static Transform.scale() and chainable instance scale() intentionally share the CAD API name.
12592
12593
  scale(v) {
12593
12594
  return this.mul(Transform.scale(v));
12594
12595
  }
@@ -30024,18 +30025,18 @@ function faceAxes(face) {
30024
30025
  }
30025
30026
  return {};
30026
30027
  }
30027
- function edgeKey$1(start, end) {
30028
+ function edgeKey$2(start, end) {
30028
30029
  const encode = (p2) => p2.map((value) => value.toFixed(9)).join(",");
30029
30030
  const a2 = encode(start);
30030
30031
  const b = encode(end);
30031
30032
  return a2 < b ? `${a2}|${b}` : `${b}|${a2}`;
30032
30033
  }
30033
30034
  function faceEdgeIndex(face, start, end) {
30034
- const target = edgeKey$1(start, end);
30035
+ const target = edgeKey$2(start, end);
30035
30036
  for (let i = 0; i < face.vertices.length; i++) {
30036
30037
  const faceStart = face.vertices[i];
30037
30038
  const faceEnd = face.vertices[(i + 1) % face.vertices.length];
30038
- if (faceStart && faceEnd && edgeKey$1(faceStart, faceEnd) === target) return i;
30039
+ if (faceStart && faceEnd && edgeKey$2(faceStart, faceEnd) === target) return i;
30039
30040
  }
30040
30041
  return null;
30041
30042
  }
@@ -30236,7 +30237,7 @@ function topologyPayloadToTopology(payload) {
30236
30237
  const start = explicitVertices[explicitEdge.vertices[0]];
30237
30238
  const end = explicitVertices[explicitEdge.vertices[1]];
30238
30239
  if (!isVec3(start) || !isVec3(end)) continue;
30239
- const key = edgeKey$1(start, end);
30240
+ const key = edgeKey$2(start, end);
30240
30241
  if (seenEdges.has(key)) continue;
30241
30242
  seenEdges.set(key, edges.size);
30242
30243
  const display = explicitEdgeDisplayName(payload, explicitEdge, explicitEdgeIndex, start, end);
@@ -30256,7 +30257,7 @@ function topologyPayloadToTopology(payload) {
30256
30257
  const start = face.vertices[i];
30257
30258
  const end = face.vertices[(i + 1) % face.vertices.length];
30258
30259
  if (!start || !end) continue;
30259
- const key = edgeKey$1(start, end);
30260
+ const key = edgeKey$2(start, end);
30260
30261
  if (seenEdges.has(key)) continue;
30261
30262
  seenEdges.set(key, edges.size);
30262
30263
  edges.set(`${face.id}:edge-${i}`, {
@@ -48788,6 +48789,92 @@ function percentile(sorted, q) {
48788
48789
  const index2 = MathUtils.clamp(Math.floor(sorted.length * q), 0, sorted.length - 1);
48789
48790
  return Number(sorted[index2].toFixed(2));
48790
48791
  }
48792
+ class DisjointSet {
48793
+ constructor(size) {
48794
+ __publicField(this, "parent");
48795
+ __publicField(this, "rank");
48796
+ this.parent = Array.from({ length: size }, (_2, index2) => index2);
48797
+ this.rank = Array.from({ length: size }, () => 0);
48798
+ }
48799
+ find(index2) {
48800
+ const parent = this.parent[index2];
48801
+ if (parent === index2) return index2;
48802
+ const root = this.find(parent);
48803
+ this.parent[index2] = root;
48804
+ return root;
48805
+ }
48806
+ union(a2, b) {
48807
+ const rootA = this.find(a2);
48808
+ const rootB = this.find(b);
48809
+ if (rootA === rootB) return;
48810
+ if (this.rank[rootA] < this.rank[rootB]) {
48811
+ this.parent[rootA] = rootB;
48812
+ } else if (this.rank[rootA] > this.rank[rootB]) {
48813
+ this.parent[rootB] = rootA;
48814
+ } else {
48815
+ this.parent[rootB] = rootA;
48816
+ this.rank[rootA] += 1;
48817
+ }
48818
+ }
48819
+ }
48820
+ function connectedCoplanarSurfacePatches(triangles) {
48821
+ const snap = surfacePatchSnap(triangles);
48822
+ const planeKeys = triangles.map((triangle) => planeKey(triangle, snap));
48823
+ const edgeOwners = /* @__PURE__ */ new Map();
48824
+ const sets = new DisjointSet(triangles.length);
48825
+ triangles.forEach((triangle, index2) => {
48826
+ for (const key of triangleEdgeKeys(triangle, snap)) {
48827
+ const owners = edgeOwners.get(key);
48828
+ if (owners) {
48829
+ for (const owner of owners) {
48830
+ if (planeKeys[owner] === planeKeys[index2]) sets.union(owner, index2);
48831
+ }
48832
+ owners.push(index2);
48833
+ } else {
48834
+ edgeOwners.set(key, [index2]);
48835
+ }
48836
+ }
48837
+ });
48838
+ const patchByRoot = /* @__PURE__ */ new Map();
48839
+ triangles.forEach((triangle, index2) => {
48840
+ const root = sets.find(index2);
48841
+ const patch = patchByRoot.get(root) ?? { triangleIndexes: [], area: 0 };
48842
+ patch.triangleIndexes.push(index2);
48843
+ patch.area += triangle.area;
48844
+ patchByRoot.set(root, patch);
48845
+ });
48846
+ return [...patchByRoot.values()];
48847
+ }
48848
+ function surfacePatchSnap(triangles) {
48849
+ const bounds = new Box3();
48850
+ for (const triangle of triangles) {
48851
+ bounds.expandByPoint(triangle.a);
48852
+ bounds.expandByPoint(triangle.b);
48853
+ bounds.expandByPoint(triangle.c);
48854
+ }
48855
+ const size = bounds.getSize(new Vector3());
48856
+ return Math.max(1e-6, size.length() * 1e-8);
48857
+ }
48858
+ function planeKey(triangle, snap) {
48859
+ const normalSnap = 1e-6;
48860
+ const distance = triangle.normal.dot(triangle.a);
48861
+ return [
48862
+ Math.round(triangle.normal.x / normalSnap),
48863
+ Math.round(triangle.normal.y / normalSnap),
48864
+ Math.round(triangle.normal.z / normalSnap),
48865
+ Math.round(distance / snap)
48866
+ ].join(",");
48867
+ }
48868
+ function triangleEdgeKeys(triangle, snap) {
48869
+ const vertices = [vertexKey$2(triangle.a, snap), vertexKey$2(triangle.b, snap), vertexKey$2(triangle.c, snap)];
48870
+ return [edgeKey$1(vertices[0], vertices[1]), edgeKey$1(vertices[1], vertices[2]), edgeKey$1(vertices[2], vertices[0])];
48871
+ }
48872
+ function vertexKey$2(point, snap) {
48873
+ return `${Math.round(point.x / snap)},${Math.round(point.y / snap)},${Math.round(point.z / snap)}`;
48874
+ }
48875
+ function edgeKey$1(a2, b) {
48876
+ return a2 < b ? `${a2}|${b}` : `${b}|${a2}`;
48877
+ }
48791
48878
  const MIN_TRIANGLE_AREA = 1e-12;
48792
48879
  const R2_ALPHA = 0.7548776662466927;
48793
48880
  const R2_BETA = 0.5698402909980532;
@@ -48840,7 +48927,7 @@ function allocateAreaSampleCounts(triangles, maxSamples) {
48840
48927
  return counts;
48841
48928
  }
48842
48929
  function sampleSurfaceTriangles(triangles, maxSamples) {
48843
- const counts = allocateAreaSampleCounts(triangles, maxSamples);
48930
+ const counts = allocateSurfacePatchSampleCounts(triangles, maxSamples);
48844
48931
  const samples = [];
48845
48932
  const position = new Vector3();
48846
48933
  let sampleIndex = 0;
@@ -48865,6 +48952,35 @@ function sampleSurfaceTriangles(triangles, maxSamples) {
48865
48952
  });
48866
48953
  return samples;
48867
48954
  }
48955
+ function allocateSurfacePatchSampleCounts(triangles, maxSamples) {
48956
+ const counts = new Array(triangles.length).fill(0);
48957
+ if (triangles.length === 0) return counts;
48958
+ const patches = connectedCoplanarSurfacePatches(triangles);
48959
+ const patchCounts = allocateAreaSampleCounts(
48960
+ patches.map((patch, index2) => {
48961
+ var _a3, _b3, _c2, _d2;
48962
+ return {
48963
+ index: index2,
48964
+ a: ((_a3 = triangles[patch.triangleIndexes[0]]) == null ? void 0 : _a3.a) ?? new Vector3(),
48965
+ b: ((_b3 = triangles[patch.triangleIndexes[0]]) == null ? void 0 : _b3.b) ?? new Vector3(),
48966
+ c: ((_c2 = triangles[patch.triangleIndexes[0]]) == null ? void 0 : _c2.c) ?? new Vector3(),
48967
+ normal: ((_d2 = triangles[patch.triangleIndexes[0]]) == null ? void 0 : _d2.normal) ?? new Vector3(0, 0, 1),
48968
+ area: patch.area
48969
+ };
48970
+ }),
48971
+ maxSamples
48972
+ );
48973
+ patches.forEach((patch, patchIndex) => {
48974
+ const patchBudget = patchCounts[patchIndex] ?? 0;
48975
+ if (patchBudget <= 0) return;
48976
+ const patchTriangles = patch.triangleIndexes.map((index2) => triangles[index2]);
48977
+ const localCounts = allocateAreaSampleCounts(patchTriangles, patchBudget);
48978
+ patch.triangleIndexes.forEach((triangleIndex, localIndex) => {
48979
+ counts[triangleIndex] += localCounts[localIndex] ?? 0;
48980
+ });
48981
+ });
48982
+ return counts;
48983
+ }
48868
48984
  function totalSurfaceArea(triangles) {
48869
48985
  return triangles.reduce((sum2, triangle) => sum2 + triangle.area, 0);
48870
48986
  }
@@ -49045,16 +49161,23 @@ const DEFAULT_THICKNESS_INSPECTION_OPTIONS = {
49045
49161
  minThickness: 1.2,
49046
49162
  warnThickness: 2,
49047
49163
  maxThickness: 6,
49164
+ colorMinThickness: 0,
49165
+ colorMaxThickness: 6,
49048
49166
  maxSamplesPerObject: 5e3,
49049
49167
  contactTolerance: DEFAULT_PHYSICAL_CONNECTIVITY_OPTIONS.contactTolerance
49050
49168
  };
49051
49169
  const THICKNESS_COLORS = {
49052
49170
  critical: [255, 28, 28],
49053
- warning: [255, 150, 0],
49054
49171
  ok: [60, 220, 90],
49055
49172
  thick: [70, 145, 255],
49056
49173
  unknown: [90, 90, 90]
49057
49174
  };
49175
+ const THICKNESS_GRADIENT_COLORS = [
49176
+ THICKNESS_COLORS.critical,
49177
+ [255, 222, 0],
49178
+ THICKNESS_COLORS.ok,
49179
+ THICKNESS_COLORS.thick
49180
+ ];
49058
49181
  function finitePositive(value, fallback, label) {
49059
49182
  if (value === void 0) return fallback;
49060
49183
  if (!Number.isFinite(value) || value <= 0) {
@@ -49073,6 +49196,16 @@ function resolveThicknessInspectionOptions(raw = {}) {
49073
49196
  const minThickness = finitePositive(raw.minThickness, DEFAULT_THICKNESS_INSPECTION_OPTIONS.minThickness, "minThickness");
49074
49197
  const warnThickness = finitePositive(raw.warnThickness, DEFAULT_THICKNESS_INSPECTION_OPTIONS.warnThickness, "warnThickness");
49075
49198
  const maxThickness = finitePositive(raw.maxThickness, DEFAULT_THICKNESS_INSPECTION_OPTIONS.maxThickness, "maxThickness");
49199
+ const colorMinThickness = finiteNonNegative(
49200
+ raw.colorMinThickness,
49201
+ DEFAULT_THICKNESS_INSPECTION_OPTIONS.colorMinThickness,
49202
+ "colorMinThickness"
49203
+ );
49204
+ const colorMaxThickness = finitePositive(
49205
+ raw.colorMaxThickness,
49206
+ DEFAULT_THICKNESS_INSPECTION_OPTIONS.colorMaxThickness,
49207
+ "colorMaxThickness"
49208
+ );
49076
49209
  const maxSamplesPerObject = finitePositive(
49077
49210
  raw.maxSamplesPerObject,
49078
49211
  DEFAULT_THICKNESS_INSPECTION_OPTIONS.maxSamplesPerObject,
@@ -49089,10 +49222,15 @@ function resolveThicknessInspectionOptions(raw = {}) {
49089
49222
  if (warnThickness > maxThickness) {
49090
49223
  throw new Error("warnThickness must be less than or equal to maxThickness.");
49091
49224
  }
49225
+ if (colorMinThickness >= colorMaxThickness) {
49226
+ throw new Error("colorMinThickness must be less than colorMaxThickness.");
49227
+ }
49092
49228
  return {
49093
49229
  minThickness,
49094
49230
  warnThickness,
49095
49231
  maxThickness,
49232
+ colorMinThickness,
49233
+ colorMaxThickness,
49096
49234
  maxSamplesPerObject: Math.max(1, Math.floor(maxSamplesPerObject)),
49097
49235
  contactTolerance
49098
49236
  };
@@ -49103,6 +49241,16 @@ function lerp$1(a2, b, t) {
49103
49241
  function lerpColor(a2, b, t) {
49104
49242
  return [Math.round(lerp$1(a2[0], b[0], t)), Math.round(lerp$1(a2[1], b[1], t)), Math.round(lerp$1(a2[2], b[2], t))];
49105
49243
  }
49244
+ function gradientColor(stops, t) {
49245
+ if (stops.length === 0) return THICKNESS_COLORS.unknown;
49246
+ if (stops.length === 1) return stops[0] ?? THICKNESS_COLORS.unknown;
49247
+ const clamped = Math.max(0, Math.min(1, t));
49248
+ const scaled = clamped * (stops.length - 1);
49249
+ const leftIndex = Math.min(stops.length - 2, Math.floor(scaled));
49250
+ const left = stops[leftIndex] ?? THICKNESS_COLORS.unknown;
49251
+ const right = stops[leftIndex + 1] ?? left;
49252
+ return lerpColor(left, right, scaled - leftIndex);
49253
+ }
49106
49254
  function thicknessClass(thickness, options) {
49107
49255
  if (thickness == null || !Number.isFinite(thickness) || thickness <= 0) return "unknown";
49108
49256
  if (thickness <= options.minThickness) return "critical";
@@ -49111,18 +49259,9 @@ function thicknessClass(thickness, options) {
49111
49259
  return "thick";
49112
49260
  }
49113
49261
  function thicknessColor(thickness, options) {
49114
- const cls = thicknessClass(thickness, options);
49115
- if (cls === "unknown") return THICKNESS_COLORS.unknown;
49116
- if (cls === "critical") return THICKNESS_COLORS.critical;
49117
- if (cls === "warning") {
49118
- const span = Math.max(1e-9, options.warnThickness - options.minThickness);
49119
- return lerpColor(THICKNESS_COLORS.critical, THICKNESS_COLORS.warning, ((thickness ?? 0) - options.minThickness) / span);
49120
- }
49121
- if (cls === "ok") {
49122
- const span = Math.max(1e-9, options.maxThickness - options.warnThickness);
49123
- return lerpColor(THICKNESS_COLORS.ok, THICKNESS_COLORS.thick, ((thickness ?? 0) - options.warnThickness) / span);
49124
- }
49125
- return THICKNESS_COLORS.thick;
49262
+ if (thickness == null || !Number.isFinite(thickness) || thickness <= 0) return THICKNESS_COLORS.unknown;
49263
+ const span = Math.max(1e-9, options.colorMaxThickness - options.colorMinThickness);
49264
+ return gradientColor(THICKNESS_GRADIENT_COLORS, (thickness - options.colorMinThickness) / span);
49126
49265
  }
49127
49266
  function cloneGeometryForFaceColors(geometry) {
49128
49267
  return geometry.index ? geometry.toNonIndexed() : geometry.clone();
@@ -49986,7 +50125,7 @@ for (let radius = 0; radius <= MAX_SEARCH_RADIUS; radius += 1) {
49986
50125
  function gridKey(x2, y2, z2) {
49987
50126
  return `${x2},${y2},${z2}`;
49988
50127
  }
49989
- function buildGeometryBounds(geometry) {
50128
+ function buildInspectHeatmapFieldBounds(geometry) {
49990
50129
  const position = geometry.getAttribute("position");
49991
50130
  if (!position || position.count === 0) return null;
49992
50131
  const bounds = new Box3().setFromBufferAttribute(position);
@@ -49994,7 +50133,11 @@ function buildGeometryBounds(geometry) {
49994
50133
  const size = bounds.getSize(new Vector3());
49995
50134
  const pad = Math.max(size.x, size.y, size.z, 1) * 1e-5;
49996
50135
  bounds.expandByScalar(pad);
49997
- return bounds;
50136
+ const boundsSize = bounds.getSize(new Vector3());
50137
+ return {
50138
+ boundsMin: [bounds.min.x, bounds.min.y, bounds.min.z],
50139
+ boundsSize: [boundsSize.x, boundsSize.y, boundsSize.z]
50140
+ };
49998
50141
  }
49999
50142
  function buildSampleGrid(pointCloud) {
50000
50143
  const sampleCount = Math.floor(pointCloud.positions.length / 3);
@@ -50078,11 +50221,15 @@ function blendedColorAt(point, pointCloud, sampleGrid) {
50078
50221
  return [r / weightSum, g2 / weightSum, b / weightSum];
50079
50222
  }
50080
50223
  function buildInspectHeatmapFieldData(geometry, pointCloud) {
50081
- const bounds = buildGeometryBounds(geometry);
50224
+ const bounds = buildInspectHeatmapFieldBounds(geometry);
50225
+ return bounds ? buildInspectHeatmapFieldDataFromBounds(bounds, pointCloud) : null;
50226
+ }
50227
+ function buildInspectHeatmapFieldDataFromBounds(bounds, pointCloud) {
50082
50228
  const sampleGrid = buildSampleGrid(pointCloud);
50083
50229
  if (!bounds || !sampleGrid) return null;
50084
50230
  const gridSize = fieldGridSizeForSampleCount(Math.floor(pointCloud.positions.length / 3));
50085
- const boundsSize = bounds.getSize(new Vector3());
50231
+ const boundsMin = new Vector3(...bounds.boundsMin);
50232
+ const boundsSize = new Vector3(...bounds.boundsSize);
50086
50233
  const data = new Uint8Array(gridSize * gridSize * gridSize * 4);
50087
50234
  const point = new Vector3();
50088
50235
  let dataOffset = 0;
@@ -50090,9 +50237,9 @@ function buildInspectHeatmapFieldData(geometry, pointCloud) {
50090
50237
  for (let z2 = 0; z2 < gridSize; z2 += 1) {
50091
50238
  for (let x2 = 0; x2 < gridSize; x2 += 1) {
50092
50239
  point.set(
50093
- bounds.min.x + boundsSize.x * x2 / (gridSize - 1),
50094
- bounds.min.y + boundsSize.y * y2 / (gridSize - 1),
50095
- bounds.min.z + boundsSize.z * z2 / (gridSize - 1)
50240
+ boundsMin.x + boundsSize.x * x2 / (gridSize - 1),
50241
+ boundsMin.y + boundsSize.y * y2 / (gridSize - 1),
50242
+ boundsMin.z + boundsSize.z * z2 / (gridSize - 1)
50096
50243
  );
50097
50244
  const [r, g2, b] = blendedColorAt(point, pointCloud, sampleGrid);
50098
50245
  data[dataOffset] = Math.max(0, Math.min(255, Math.round(r)));
@@ -50105,13 +50252,14 @@ function buildInspectHeatmapFieldData(geometry, pointCloud) {
50105
50252
  }
50106
50253
  return {
50107
50254
  data,
50108
- boundsMin: [bounds.min.x, bounds.min.y, bounds.min.z],
50255
+ boundsMin: bounds.boundsMin,
50109
50256
  boundsSize: [boundsSize.x, boundsSize.y, boundsSize.z],
50110
50257
  gridSize
50111
50258
  };
50112
50259
  }
50113
50260
  const workerScope = self;
50114
50261
  let manifoldReadyPromise = null;
50262
+ let cachedThicknessColorizeAnalysis = null;
50115
50263
  function ensureManifoldReady() {
50116
50264
  if (!manifoldReadyPromise) manifoldReadyPromise = initKernelManifoldOnly();
50117
50265
  return manifoldReadyPromise;
@@ -50157,6 +50305,7 @@ function geometryFromPositions(positions) {
50157
50305
  function pointBuffers(samples) {
50158
50306
  const positions = new Float32Array(samples.length * 3);
50159
50307
  const colors = new Float32Array(samples.length * 3);
50308
+ const values = new Float32Array(samples.length);
50160
50309
  samples.forEach((sample, index2) => {
50161
50310
  const base = index2 * 3;
50162
50311
  positions[base] = sample.position[0] + sample.normal[0] * 0.025;
@@ -50165,8 +50314,9 @@ function pointBuffers(samples) {
50165
50314
  colors[base] = sample.color[0] / 255;
50166
50315
  colors[base + 1] = sample.color[1] / 255;
50167
50316
  colors[base + 2] = sample.color[2] / 255;
50317
+ values[index2] = sample.value ?? Number.NaN;
50168
50318
  });
50169
- return { positions, colors };
50319
+ return { positions, colors, values };
50170
50320
  }
50171
50321
  function rgbFloatsForHex(hex) {
50172
50322
  const color = new Color(hex);
@@ -50191,6 +50341,7 @@ function analyzeScalarChannel(request) {
50191
50341
  const pointObjects = [];
50192
50342
  const heatmapFieldObjects = [];
50193
50343
  const warnings = [];
50344
+ const thicknessCacheObjects = [];
50194
50345
  for (const object of request.objects) {
50195
50346
  if (!object.positions || object.positions.length < 9) continue;
50196
50347
  const geometry = geometryFromPositions(object.positions);
@@ -50198,13 +50349,22 @@ function analyzeScalarChannel(request) {
50198
50349
  const analysis = request.channel === "thickness" ? analyzeThicknessGeometry(geometry, request.thickness) : analyzeRoughnessGeometry(geometry, request.roughness);
50199
50350
  analysis.warnings.forEach((warning) => warnings.push(`${object.name}: ${warning}`));
50200
50351
  const buffers = pointBuffers(analysis.pointSamples);
50201
- const field = buildInspectHeatmapFieldData(analysis.geometry, buffers);
50352
+ const heatmapBounds = buildInspectHeatmapFieldBounds(analysis.geometry);
50353
+ const field = heatmapBounds ? buildInspectHeatmapFieldDataFromBounds(heatmapBounds, buffers) : buildInspectHeatmapFieldData(analysis.geometry, buffers);
50202
50354
  if (field) {
50203
50355
  heatmapFieldObjects.push({
50204
50356
  objectId: object.id,
50205
50357
  ...field
50206
50358
  });
50207
50359
  }
50360
+ if (request.channel === "thickness" && buffers.values) {
50361
+ thicknessCacheObjects.push({
50362
+ objectId: object.id,
50363
+ positions: new Float32Array(buffers.positions),
50364
+ values: new Float32Array(buffers.values),
50365
+ heatmapBounds
50366
+ });
50367
+ }
50208
50368
  pointObjects.push({
50209
50369
  objectId: object.id,
50210
50370
  sampleCount: analysis.pointSamples.length,
@@ -50215,7 +50375,9 @@ function analyzeScalarChannel(request) {
50215
50375
  geometry.dispose();
50216
50376
  }
50217
50377
  }
50378
+ cachedThicknessColorizeAnalysis = request.channel === "thickness" ? { analysisId: request.reqId, objects: thicknessCacheObjects } : null;
50218
50379
  return {
50380
+ analysisId: request.reqId,
50219
50381
  channel: request.channel,
50220
50382
  objectColors: {},
50221
50383
  pointObjects,
@@ -50225,6 +50387,47 @@ function analyzeScalarChannel(request) {
50225
50387
  warnings
50226
50388
  };
50227
50389
  }
50390
+ function thicknessColorsForValues(values, colorMinThickness, colorMaxThickness) {
50391
+ const options = resolveThicknessInspectionOptions({ colorMinThickness, colorMaxThickness });
50392
+ const colors = new Float32Array(values.length * 3);
50393
+ for (let index2 = 0; index2 < values.length; index2 += 1) {
50394
+ const color = thicknessColor(values[index2], options);
50395
+ const offset = index2 * 3;
50396
+ colors[offset] = color[0] / 255;
50397
+ colors[offset + 1] = color[1] / 255;
50398
+ colors[offset + 2] = color[2] / 255;
50399
+ }
50400
+ return colors;
50401
+ }
50402
+ function colorizeThicknessAnalysis(request) {
50403
+ const cached = cachedThicknessColorizeAnalysis;
50404
+ if (!cached || cached.analysisId !== request.analysisId) {
50405
+ throw new Error("Thickness colorize cache is no longer available.");
50406
+ }
50407
+ const pointObjects = [];
50408
+ const heatmapFieldObjects = [];
50409
+ for (const object of cached.objects) {
50410
+ const colors = thicknessColorsForValues(object.values, request.colorMinThickness, request.colorMaxThickness);
50411
+ pointObjects.push({ objectId: object.objectId, colors });
50412
+ if (object.heatmapBounds) {
50413
+ const field = buildInspectHeatmapFieldDataFromBounds(object.heatmapBounds, {
50414
+ positions: object.positions,
50415
+ colors
50416
+ });
50417
+ if (field) {
50418
+ heatmapFieldObjects.push({
50419
+ objectId: object.objectId,
50420
+ ...field
50421
+ });
50422
+ }
50423
+ }
50424
+ }
50425
+ return {
50426
+ analysisId: cached.analysisId,
50427
+ pointObjects,
50428
+ heatmapFieldObjects
50429
+ };
50430
+ }
50228
50431
  function analyzeConnectivityChannel(request) {
50229
50432
  const bodyInput = buildMeshBodyConnectivityInput(
50230
50433
  request.objects.map((object) => ({
@@ -50391,7 +50594,11 @@ function analyzeCollisionChannel(request) {
50391
50594
  }
50392
50595
  function transferFor(result) {
50393
50596
  return [
50394
- ...result.pointObjects.flatMap((object) => [object.positions.buffer, object.colors.buffer]),
50597
+ ...result.pointObjects.flatMap((object) => [
50598
+ object.positions.buffer,
50599
+ object.colors.buffer,
50600
+ ...object.values ? [object.values.buffer] : []
50601
+ ]),
50395
50602
  ...result.meshColorObjects.map((object) => object.colors.buffer),
50396
50603
  ...result.heatmapFieldObjects.map((object) => object.data.buffer),
50397
50604
  ...result.collisionGeometryObjects.flatMap((object) => [object.positions.buffer, object.normals.buffer, object.edgePositions.buffer])
@@ -50399,6 +50606,21 @@ function transferFor(result) {
50399
50606
  }
50400
50607
  async function handleRequest(request) {
50401
50608
  try {
50609
+ if (request.type === "colorize-thickness") {
50610
+ const result2 = colorizeThicknessAnalysis(request.payload);
50611
+ const response2 = {
50612
+ type: "inspect-colorize-success",
50613
+ payload: {
50614
+ reqId: request.payload.reqId,
50615
+ result: result2
50616
+ }
50617
+ };
50618
+ workerScope.postMessage(response2, [
50619
+ ...result2.pointObjects.map((object) => object.colors.buffer),
50620
+ ...result2.heatmapFieldObjects.map((object) => object.data.buffer)
50621
+ ]);
50622
+ return;
50623
+ }
50402
50624
  if (request.payload.channel === "collisions") await ensureManifoldReady();
50403
50625
  const result = request.payload.channel === "thickness" || request.payload.channel === "roughness" ? analyzeScalarChannel(request.payload) : request.payload.channel === "collisions" ? analyzeCollisionChannel(request.payload) : analyzeConnectivityChannel(request.payload);
50404
50626
  const response = {
@@ -1,4 +1,4 @@
1
- import { c2 as parseViewportCameraState } from "./scalar-sampling-budget-wJF98aY9.js";
1
+ import { c1 as parseViewportCameraState } from "./scalar-sampling-budget-J5cuzxT1.js";
2
2
  const roundNumber = (value, digits) => {
3
3
  const scale = 10 ** digits;
4
4
  return Math.round(value * scale) / scale;
@@ -148,10 +148,78 @@ function getSceneObjectKind(object) {
148
148
  if (object.shape) return "shape";
149
149
  return "object";
150
150
  }
151
+ function hasJointOverrides(overrides) {
152
+ return Boolean(overrides && Object.keys(overrides).length > 0);
153
+ }
154
+ function resolveJointSliderRange(joint) {
155
+ return {
156
+ min: joint.min ?? (joint.type === "prismatic" ? -100 : 0),
157
+ max: joint.max ?? (joint.type === "prismatic" ? 100 : 360)
158
+ };
159
+ }
160
+ function editableJointNames(view) {
161
+ const coupledTargets = new Set(view.couplings.map((coupling) => coupling.joint));
162
+ return view.joints.filter((joint) => !joint.hidden && !coupledTargets.has(joint.name)).map((joint) => joint.name);
163
+ }
164
+ function availableText(names) {
165
+ return names.length > 0 ? names.join(", ") : "(none)";
166
+ }
167
+ function validateJointOverrides(view, overrides, flag = "--joint") {
168
+ var _a;
169
+ if (!hasJointOverrides(overrides)) return;
170
+ if (!view || view.enabled === false || view.joints.length === 0) {
171
+ throw new Error(`${flag} requires a Forge script that exposes Motion tab joints. Available joints: (none).`);
172
+ }
173
+ const jointsByName = new Map(view.joints.map((joint) => [joint.name, joint]));
174
+ const coupledTargets = new Set(view.couplings.map((coupling) => coupling.joint));
175
+ const available = editableJointNames(view);
176
+ for (const [name, value] of Object.entries(overrides)) {
177
+ const joint = jointsByName.get(name);
178
+ if (!joint) {
179
+ throw new Error(`Unknown joint "${name}" for ${flag}. Available editable joints: ${availableText(available)}.`);
180
+ }
181
+ if (joint.hidden) {
182
+ throw new Error(`Joint "${name}" is hidden and cannot be set with ${flag}. Available editable joints: ${availableText(available)}.`);
183
+ }
184
+ if (coupledTargets.has(name)) {
185
+ const drivers = (_a = view.couplings.find((coupling) => coupling.joint === name)) == null ? void 0 : _a.terms.map((term) => term.joint).join(", ");
186
+ throw new Error(
187
+ `Joint "${name}" is driven by a coupling and cannot be set with ${flag}.` + (drivers ? ` Set driver joint(s): ${drivers}.` : ` Available editable joints: ${availableText(available)}.`)
188
+ );
189
+ }
190
+ const { min, max } = resolveJointSliderRange(joint);
191
+ if (value < min || value > max) {
192
+ throw new Error(`Joint "${name}" value ${value} is outside Motion tab range [${min}, ${max}].`);
193
+ }
194
+ }
195
+ }
196
+ function buildBaseJointValues(joints, overrides = {}) {
197
+ const values = {};
198
+ joints.forEach((joint) => {
199
+ values[joint.name] = overrides[joint.name] ?? joint.defaultValue;
200
+ });
201
+ return values;
202
+ }
203
+ function formatJointNumber(value) {
204
+ const rounded = Number(value.toFixed(6));
205
+ return Object.is(rounded, -0) ? "0" : String(rounded);
206
+ }
207
+ function quoteForCli(value) {
208
+ return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
209
+ }
210
+ function formatCliJointPoseFlags(joints, displayedValues, coupledJointNames) {
211
+ return joints.filter((joint) => !joint.hidden && !coupledJointNames.has(joint.name)).map((joint) => {
212
+ const value = displayedValues[joint.name] ?? joint.defaultValue;
213
+ return `--joint ${quoteForCli(`${joint.name}=${formatJointNumber(value)}`)}`;
214
+ }).join(" ");
215
+ }
151
216
  export {
152
217
  getSceneObjectKind as a,
153
- formatRenderSceneCliSpec as f,
218
+ buildBaseJointValues as b,
219
+ formatRenderSceneCliSpec as c,
220
+ formatCliJointPoseFlags as f,
154
221
  getSceneObjectTreePath as g,
155
222
  mergeViewportRenderSceneStates as m,
156
- parseRenderSceneCliSpec as p
223
+ parseRenderSceneCliSpec as p,
224
+ validateJointOverrides as v
157
225
  };
@@ -12,7 +12,7 @@ var Module = (() => {
12
12
  var ENVIRONMENT_IS_WORKER = typeof WorkerGlobalScope != "undefined";
13
13
  var ENVIRONMENT_IS_NODE = typeof process == "object" && typeof process.versions == "object" && typeof process.versions.node == "string" && process.type != "renderer";
14
14
  if (ENVIRONMENT_IS_NODE) {
15
- const { createRequire } = await import("./evalWorker-CU0Ke6DP.js").then(function(n) {
15
+ const { createRequire } = await import("./evalWorker-vkx310U2.js").then(function(n) {
16
16
  return n._;
17
17
  });
18
18
  var require2 = createRequire(import.meta.url);
@@ -1,4 +1,4 @@
1
- import { _ as __vitePreload } from "./scalar-sampling-budget-wJF98aY9.js";
1
+ import { _ as __vitePreload } from "./scalar-sampling-budget-J5cuzxT1.js";
2
2
  var Module = (() => {
3
3
  var _scriptName = import.meta.url;
4
4
  return (async function(moduleArg = {}) {
@@ -14,7 +14,7 @@ var Module = (() => {
14
14
  var ENVIRONMENT_IS_NODE = typeof process == "object" && typeof process.versions == "object" && typeof process.versions.node == "string" && process.type != "renderer";
15
15
  if (ENVIRONMENT_IS_NODE) {
16
16
  const { createRequire } = await __vitePreload(async () => {
17
- const { createRequire: createRequire2 } = await import("./scalar-sampling-budget-wJF98aY9.js").then((n) => n.df);
17
+ const { createRequire: createRequire2 } = await import("./scalar-sampling-budget-J5cuzxT1.js").then((n) => n.dg);
18
18
  return { createRequire: createRequire2 };
19
19
  }, true ? [] : void 0);
20
20
  var require2 = createRequire(import.meta.url);
@@ -12,7 +12,7 @@ var Module = (() => {
12
12
  var ENVIRONMENT_IS_WORKER = typeof WorkerGlobalScope != "undefined";
13
13
  var ENVIRONMENT_IS_NODE = typeof process == "object" && typeof process.versions == "object" && typeof process.versions.node == "string" && process.type != "renderer";
14
14
  if (ENVIRONMENT_IS_NODE) {
15
- const { createRequire } = await import("./reportWorker-CdBz5bNg.js").then(function(n) {
15
+ const { createRequire } = await import("./reportWorker-0AGij1Ru.js").then(function(n) {
16
16
  return n._;
17
17
  });
18
18
  var require2 = createRequire(import.meta.url);