@vcad/mcp 0.9.4-main.15 → 0.9.4-main.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.mjs CHANGED
@@ -150,6 +150,15 @@ __export(vcad_kernel_wasm_exports, {
150
150
  ecadVerifyReceipt: () => ecadVerifyReceipt,
151
151
  ecadVerifySubstitution: () => ecadVerifySubstitution,
152
152
  emSimulate: () => emSimulate,
153
+ embroideryDesignToMesh: () => embroideryDesignToMesh,
154
+ enclosure_component_extents: () => enclosure_component_extents,
155
+ enclosure_connectors: () => enclosure_connectors,
156
+ enclosure_derive_board: () => enclosure_derive_board,
157
+ enclosure_features: () => enclosure_features,
158
+ enclosure_fit: () => enclosure_fit,
159
+ enclosure_mounting_holes: () => enclosure_mounting_holes,
160
+ enclosure_outline_aabb: () => enclosure_outline_aabb,
161
+ enclosure_to_world: () => enclosure_to_world,
153
162
  estimatePrintCost: () => estimatePrintCost,
154
163
  estimate_cost_for_process: () => estimate_cost_for_process,
155
164
  eulerXyzDegToQuat: () => eulerXyzDegToQuat,
@@ -257,6 +266,7 @@ __export(vcad_kernel_wasm_exports, {
257
266
  toleranceAnalyze: () => toleranceAnalyze,
258
267
  topologyOptimizeBox: () => topologyOptimizeBox,
259
268
  topologyOptimizeMesh: () => topologyOptimizeMesh,
269
+ transformMeshBuffers: () => transformMeshBuffers,
260
270
  writeEmbroideryDst: () => writeEmbroideryDst,
261
271
  writeEmbroideryPes: () => writeEmbroideryPes
262
272
  });
@@ -1519,6 +1529,193 @@ function emSimulate(spec_json, params_json, options_json) {
1519
1529
  }
1520
1530
  return takeFromExternrefTable0(ret[0]);
1521
1531
  }
1532
+ function embroideryDesignToMesh(design_json) {
1533
+ const ptr0 = passStringToWasm0(design_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1534
+ const len0 = WASM_VECTOR_LEN;
1535
+ const ret = wasm.embroideryDesignToMesh(ptr0, len0);
1536
+ if (ret[2]) {
1537
+ throw takeFromExternrefTable0(ret[1]);
1538
+ }
1539
+ return takeFromExternrefTable0(ret[0]);
1540
+ }
1541
+ function enclosure_component_extents(meshes_json, pcb_json) {
1542
+ let deferred4_0;
1543
+ let deferred4_1;
1544
+ try {
1545
+ const ptr0 = passStringToWasm0(meshes_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1546
+ const len0 = WASM_VECTOR_LEN;
1547
+ const ptr1 = passStringToWasm0(pcb_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1548
+ const len1 = WASM_VECTOR_LEN;
1549
+ const ret = wasm.enclosure_component_extents(ptr0, len0, ptr1, len1);
1550
+ var ptr3 = ret[0];
1551
+ var len3 = ret[1];
1552
+ if (ret[3]) {
1553
+ ptr3 = 0;
1554
+ len3 = 0;
1555
+ throw takeFromExternrefTable0(ret[2]);
1556
+ }
1557
+ deferred4_0 = ptr3;
1558
+ deferred4_1 = len3;
1559
+ return getStringFromWasm0(ptr3, len3);
1560
+ } finally {
1561
+ wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
1562
+ }
1563
+ }
1564
+ function enclosure_connectors(pcb_json, outline_json) {
1565
+ let deferred4_0;
1566
+ let deferred4_1;
1567
+ try {
1568
+ const ptr0 = passStringToWasm0(pcb_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1569
+ const len0 = WASM_VECTOR_LEN;
1570
+ const ptr1 = passStringToWasm0(outline_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1571
+ const len1 = WASM_VECTOR_LEN;
1572
+ const ret = wasm.enclosure_connectors(ptr0, len0, ptr1, len1);
1573
+ var ptr3 = ret[0];
1574
+ var len3 = ret[1];
1575
+ if (ret[3]) {
1576
+ ptr3 = 0;
1577
+ len3 = 0;
1578
+ throw takeFromExternrefTable0(ret[2]);
1579
+ }
1580
+ deferred4_0 = ptr3;
1581
+ deferred4_1 = len3;
1582
+ return getStringFromWasm0(ptr3, len3);
1583
+ } finally {
1584
+ wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
1585
+ }
1586
+ }
1587
+ function enclosure_derive_board(cavity_json, standoffs_json, options_json) {
1588
+ let deferred5_0;
1589
+ let deferred5_1;
1590
+ try {
1591
+ const ptr0 = passStringToWasm0(cavity_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1592
+ const len0 = WASM_VECTOR_LEN;
1593
+ const ptr1 = passStringToWasm0(standoffs_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1594
+ const len1 = WASM_VECTOR_LEN;
1595
+ const ptr2 = passStringToWasm0(options_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1596
+ const len2 = WASM_VECTOR_LEN;
1597
+ const ret = wasm.enclosure_derive_board(ptr0, len0, ptr1, len1, ptr2, len2);
1598
+ var ptr4 = ret[0];
1599
+ var len4 = ret[1];
1600
+ if (ret[3]) {
1601
+ ptr4 = 0;
1602
+ len4 = 0;
1603
+ throw takeFromExternrefTable0(ret[2]);
1604
+ }
1605
+ deferred5_0 = ptr4;
1606
+ deferred5_1 = len4;
1607
+ return getStringFromWasm0(ptr4, len4);
1608
+ } finally {
1609
+ wasm.__wbindgen_free(deferred5_0, deferred5_1, 1);
1610
+ }
1611
+ }
1612
+ function enclosure_features(positions, indices) {
1613
+ let deferred4_0;
1614
+ let deferred4_1;
1615
+ try {
1616
+ const ptr0 = passArrayF64ToWasm0(positions, wasm.__wbindgen_malloc);
1617
+ const len0 = WASM_VECTOR_LEN;
1618
+ const ptr1 = passArray32ToWasm0(indices, wasm.__wbindgen_malloc);
1619
+ const len1 = WASM_VECTOR_LEN;
1620
+ const ret = wasm.enclosure_features(ptr0, len0, ptr1, len1);
1621
+ var ptr3 = ret[0];
1622
+ var len3 = ret[1];
1623
+ if (ret[3]) {
1624
+ ptr3 = 0;
1625
+ len3 = 0;
1626
+ throw takeFromExternrefTable0(ret[2]);
1627
+ }
1628
+ deferred4_0 = ptr3;
1629
+ deferred4_1 = len3;
1630
+ return getStringFromWasm0(ptr3, len3);
1631
+ } finally {
1632
+ wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
1633
+ }
1634
+ }
1635
+ function enclosure_fit(input_json) {
1636
+ let deferred3_0;
1637
+ let deferred3_1;
1638
+ try {
1639
+ const ptr0 = passStringToWasm0(input_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1640
+ const len0 = WASM_VECTOR_LEN;
1641
+ const ret = wasm.enclosure_fit(ptr0, len0);
1642
+ var ptr2 = ret[0];
1643
+ var len2 = ret[1];
1644
+ if (ret[3]) {
1645
+ ptr2 = 0;
1646
+ len2 = 0;
1647
+ throw takeFromExternrefTable0(ret[2]);
1648
+ }
1649
+ deferred3_0 = ptr2;
1650
+ deferred3_1 = len2;
1651
+ return getStringFromWasm0(ptr2, len2);
1652
+ } finally {
1653
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
1654
+ }
1655
+ }
1656
+ function enclosure_mounting_holes(pcb_json) {
1657
+ let deferred3_0;
1658
+ let deferred3_1;
1659
+ try {
1660
+ const ptr0 = passStringToWasm0(pcb_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1661
+ const len0 = WASM_VECTOR_LEN;
1662
+ const ret = wasm.enclosure_mounting_holes(ptr0, len0);
1663
+ var ptr2 = ret[0];
1664
+ var len2 = ret[1];
1665
+ if (ret[3]) {
1666
+ ptr2 = 0;
1667
+ len2 = 0;
1668
+ throw takeFromExternrefTable0(ret[2]);
1669
+ }
1670
+ deferred3_0 = ptr2;
1671
+ deferred3_1 = len2;
1672
+ return getStringFromWasm0(ptr2, len2);
1673
+ } finally {
1674
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
1675
+ }
1676
+ }
1677
+ function enclosure_outline_aabb(outline_json) {
1678
+ let deferred3_0;
1679
+ let deferred3_1;
1680
+ try {
1681
+ const ptr0 = passStringToWasm0(outline_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1682
+ const len0 = WASM_VECTOR_LEN;
1683
+ const ret = wasm.enclosure_outline_aabb(ptr0, len0);
1684
+ var ptr2 = ret[0];
1685
+ var len2 = ret[1];
1686
+ if (ret[3]) {
1687
+ ptr2 = 0;
1688
+ len2 = 0;
1689
+ throw takeFromExternrefTable0(ret[2]);
1690
+ }
1691
+ deferred3_0 = ptr2;
1692
+ deferred3_1 = len2;
1693
+ return getStringFromWasm0(ptr2, len2);
1694
+ } finally {
1695
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
1696
+ }
1697
+ }
1698
+ function enclosure_to_world(x, y, z, placement_json) {
1699
+ let deferred3_0;
1700
+ let deferred3_1;
1701
+ try {
1702
+ const ptr0 = passStringToWasm0(placement_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1703
+ const len0 = WASM_VECTOR_LEN;
1704
+ const ret = wasm.enclosure_to_world(x, y, z, ptr0, len0);
1705
+ var ptr2 = ret[0];
1706
+ var len2 = ret[1];
1707
+ if (ret[3]) {
1708
+ ptr2 = 0;
1709
+ len2 = 0;
1710
+ throw takeFromExternrefTable0(ret[2]);
1711
+ }
1712
+ deferred3_0 = ptr2;
1713
+ deferred3_1 = len2;
1714
+ return getStringFromWasm0(ptr2, len2);
1715
+ } finally {
1716
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
1717
+ }
1718
+ }
1522
1719
  function estimatePrintCost(volume_mm3, infill_density, wall_count, line_width, material_name) {
1523
1720
  const ptr0 = passStringToWasm0(material_name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1524
1721
  const len0 = WASM_VECTOR_LEN;
@@ -2946,6 +3143,19 @@ function topologyOptimizeMesh(spec_json, positions, indices) {
2946
3143
  }
2947
3144
  return takeFromExternrefTable0(ret[0]);
2948
3145
  }
3146
+ function transformMeshBuffers(positions, normals, transform_json) {
3147
+ const ptr0 = passArrayF32ToWasm0(positions, wasm.__wbindgen_malloc);
3148
+ const len0 = WASM_VECTOR_LEN;
3149
+ var ptr1 = isLikeNone(normals) ? 0 : passArrayF32ToWasm0(normals, wasm.__wbindgen_malloc);
3150
+ var len1 = WASM_VECTOR_LEN;
3151
+ const ptr2 = passStringToWasm0(transform_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
3152
+ const len2 = WASM_VECTOR_LEN;
3153
+ const ret = wasm.transformMeshBuffers(ptr0, len0, ptr1, len1, ptr2, len2);
3154
+ if (ret[2]) {
3155
+ throw takeFromExternrefTable0(ret[1]);
3156
+ }
3157
+ return takeFromExternrefTable0(ret[0]);
3158
+ }
2949
3159
  function writeEmbroideryDst(json) {
2950
3160
  const ptr0 = passStringToWasm0(json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
2951
3161
  const len0 = WASM_VECTOR_LEN;
@@ -10895,753 +11105,57 @@ var init_atoms = __esm({
10895
11105
  });
10896
11106
 
10897
11107
  // ../engine/dist/enclosure-fit.js
10898
- function outlineAabb(outline) {
10899
- let minX = Infinity;
10900
- let maxX = -Infinity;
10901
- let minY = Infinity;
10902
- let maxY = -Infinity;
10903
- for (const v of outline.vertices) {
10904
- if (v.x < minX)
10905
- minX = v.x;
10906
- if (v.x > maxX)
10907
- maxX = v.x;
10908
- if (v.y < minY)
10909
- minY = v.y;
10910
- if (v.y > maxY)
10911
- maxY = v.y;
10912
- }
10913
- return { minX, maxX, minY, maxY };
11108
+ function enclosureWasm() {
11109
+ const wasm3 = getKernelWasmSync();
11110
+ if (!wasm3?.enclosure_fit) {
11111
+ throw new Error("enclosure checks require the kernel WASM to be initialized \u2014 await getKernelWasm() (or Engine.init()) first");
11112
+ }
11113
+ return wasm3;
10914
11114
  }
10915
- function toWorld(p, placement) {
10916
- const t2 = placement.rotationDeg * Math.PI / 180;
10917
- const cos = Math.cos(t2);
10918
- const sin = Math.sin(t2);
10919
- return {
10920
- x: placement.offset.x + p.x * cos - p.y * sin,
10921
- y: placement.offset.y + p.x * sin + p.y * cos,
10922
- z: placement.offset.z + (p.z ?? 0)
10923
- };
11115
+ function compact(obj) {
11116
+ return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== void 0));
10924
11117
  }
10925
- function nearestEdge(x, y, b) {
10926
- const d = [
10927
- ["minX", Math.abs(x - b.minX)],
10928
- ["maxX", Math.abs(x - b.maxX)],
10929
- ["minY", Math.abs(y - b.minY)],
10930
- ["maxY", Math.abs(y - b.maxY)]
10931
- ];
10932
- d.sort((a, c) => a[1] - c[1]);
10933
- return d[0][0];
11118
+ function outlineAabb(outline) {
11119
+ return JSON.parse(enclosureWasm().enclosure_outline_aabb(JSON.stringify(outline)));
10934
11120
  }
10935
- function padWorld(fp, pad) {
10936
- const t2 = (fp.rotation ?? 0) * Math.PI / 180;
10937
- const cos = Math.cos(t2);
10938
- const sin = Math.sin(t2);
10939
- return {
10940
- x: fp.position.x + pad.position.x * cos - pad.position.y * sin,
10941
- y: fp.position.y + pad.position.x * sin + pad.position.y * cos
10942
- };
11121
+ function toWorld(p, placement) {
11122
+ return JSON.parse(enclosureWasm().enclosure_to_world(p.x, p.y, p.z ?? 0, JSON.stringify(placement)));
10943
11123
  }
10944
11124
  function mountingHolesFromPcb(pcb) {
10945
- const holes = [];
10946
- for (const fp of pcb.footprints) {
10947
- const isMount = MOUNT_RE.test(fp.footprintName) || MOUNT_RE.test(fp.ref);
10948
- if (isMount) {
10949
- const pad = fp.pads[0];
10950
- let dia = 3.2;
10951
- if (pad) {
10952
- if (pad.drill && typeof pad.drill === "object") {
10953
- const dd = pad.drill;
10954
- if (typeof dd.diameter === "number")
10955
- dia = dd.diameter;
10956
- } else if (pad.shape.type === "Circle") {
10957
- dia = pad.shape.diameter;
10958
- }
10959
- }
10960
- holes.push({ x: round2(fp.position.x), y: round2(fp.position.y), diameter: round2(dia), ref: fp.ref });
10961
- continue;
10962
- }
10963
- for (const pad of fp.pads) {
10964
- if (pad.padType !== "NPTH")
10965
- continue;
10966
- const w = padWorld(fp, pad);
10967
- let dia = 3.2;
10968
- const dd = pad.drill;
10969
- if (dd && typeof dd.diameter === "number")
10970
- dia = dd.diameter;
10971
- else if (pad.shape.type === "Circle")
10972
- dia = pad.shape.diameter;
10973
- holes.push({ x: round2(w.x), y: round2(w.y), diameter: round2(dia), ref: fp.ref });
10974
- }
10975
- }
10976
- return holes;
11125
+ return JSON.parse(enclosureWasm().enclosure_mounting_holes(JSON.stringify(pcb)));
10977
11126
  }
10978
11127
  function componentExtentsFromMeshes(meshes, pcb) {
10979
- const frontByRef = new Map(pcb.footprints.map((fp) => [fp.ref, fp.front ?? true]));
10980
- const out = [];
10981
- for (const m of meshes) {
10982
- let minZ = Infinity;
10983
- let maxZ = -Infinity;
10984
- for (let i = 2; i < m.positions.length; i += 3) {
10985
- const z = m.positions[i];
10986
- if (z < minZ)
10987
- minZ = z;
10988
- if (z > maxZ)
10989
- maxZ = z;
10990
- }
10991
- if (!Number.isFinite(minZ))
10992
- continue;
10993
- out.push({
10994
- ref: m.footprint_ref,
10995
- front: frontByRef.get(m.footprint_ref) ?? true,
10996
- topZ: round2(maxZ),
10997
- bottomZ: round2(minZ)
10998
- });
10999
- }
11000
- return out;
11128
+ const payload = meshes.map((m) => ({
11129
+ footprint_ref: m.footprint_ref,
11130
+ positions: Array.from(m.positions)
11131
+ }));
11132
+ return JSON.parse(enclosureWasm().enclosure_component_extents(JSON.stringify(payload), JSON.stringify(pcb)));
11001
11133
  }
11002
11134
  function connectorsFromPcb(pcb, outline) {
11003
- const aabb = outlineAabb(outline);
11004
- const out = [];
11005
- for (const fp of pcb.footprints) {
11006
- const isConn = CONNECTOR_REF_RE.test(fp.ref) || CONNECTOR_NAME_RE.test(fp.footprintName) || CONNECTOR_NAME_RE.test(fp.value ?? "");
11007
- if (!isConn)
11008
- continue;
11009
- out.push({
11010
- ref: fp.ref,
11011
- x: round2(fp.position.x),
11012
- y: round2(fp.position.y),
11013
- edge: nearestEdge(fp.position.x, fp.position.y, aabb),
11014
- height: 0
11015
- });
11016
- }
11017
- return out;
11018
- }
11019
- function autoPlacement(input, clearance) {
11020
- const { cavity } = input;
11021
- const a = outlineAabb(input.outline);
11022
- const boardW = a.maxX - a.minX;
11023
- const boardH = a.maxY - a.minY;
11024
- const cavW = cavity.maxX - cavity.minX;
11025
- const cavH = cavity.maxY - cavity.minY;
11026
- const offX = cavity.minX + (cavW - boardW) / 2 - a.minX;
11027
- const offY = cavity.minY + (cavH - boardH) / 2 - a.minY;
11028
- const standoffTop = input.standoffs && input.standoffs.length > 0 ? Math.max(...input.standoffs.map((s) => s.topZ)) : cavity.floorZ + (input.standoffHeight ?? 0);
11029
- void clearance;
11030
- return { offset: { x: round2(offX), y: round2(offY), z: round2(standoffTop) }, rotationDeg: 0 };
11031
- }
11032
- function checkBoardFit(input, placement, clearance) {
11033
- const { cavity } = input;
11034
- let minX = Infinity;
11035
- let maxX = -Infinity;
11036
- let minY = Infinity;
11037
- let maxY = -Infinity;
11038
- for (const v of input.outline.vertices) {
11039
- const w = toWorld(v, placement);
11040
- if (w.x < minX)
11041
- minX = w.x;
11042
- if (w.x > maxX)
11043
- maxX = w.x;
11044
- if (w.y < minY)
11045
- minY = w.y;
11046
- if (w.y > maxY)
11047
- maxY = w.y;
11048
- }
11049
- const marginMinX = minX - cavity.minX;
11050
- const marginMaxX = cavity.maxX - maxX;
11051
- const marginMinY = minY - cavity.minY;
11052
- const marginMaxY = cavity.maxY - maxY;
11053
- const worst2 = Math.min(marginMinX, marginMaxX, marginMinY, marginMaxY);
11054
- const sides = [
11055
- ["-X", marginMinX],
11056
- ["+X", marginMaxX],
11057
- ["-Y", marginMinY],
11058
- ["+Y", marginMaxY]
11059
- ];
11060
- const tight = sides.filter(([, m]) => m < clearance).map(([s]) => s);
11061
- const ok4 = worst2 >= clearance - 1e-6;
11062
- return {
11063
- id: "board_fit",
11064
- label: "Board fits cavity with clearance",
11065
- status: ok4 ? "pass" : "fail",
11066
- detail: ok4 ? `Board fits with ${round2(worst2)}mm worst-case clearance (need ${clearance}mm)` : worst2 < 0 ? `Board overhangs the cavity by ${round2(-worst2)}mm on ${tight.join(", ")}` : `Clearance on ${tight.join(", ")} is ${round2(worst2)}mm < required ${clearance}mm`,
11067
- measurements: {
11068
- worst_clearance_mm: round2(worst2),
11069
- margin_minus_x: round2(marginMinX),
11070
- margin_plus_x: round2(marginMaxX),
11071
- margin_minus_y: round2(marginMinY),
11072
- margin_plus_y: round2(marginMaxY),
11073
- board_w: round2(maxX - minX),
11074
- board_h: round2(maxY - minY),
11075
- cavity_w: round2(cavity.maxX - cavity.minX),
11076
- cavity_h: round2(cavity.maxY - cavity.minY)
11077
- }
11078
- };
11079
- }
11080
- function checkLidClearance(input, placement, clearance) {
11081
- const { cavity } = input;
11082
- const extents = input.componentExtents ?? [];
11083
- if (extents.length === 0) {
11084
- return {
11085
- id: "lid_clearance",
11086
- label: "Components clear the lid",
11087
- status: "skip",
11088
- detail: "No component heights available (kernel component meshes unavailable)"
11089
- };
11090
- }
11091
- const cavityDepth = cavity.ceilZ - cavity.floorZ;
11092
- const front = extents.filter((e) => e.front);
11093
- const back = extents.filter((e) => !e.front);
11094
- let tallest = { ref: "", top: -Infinity };
11095
- for (const e of front) {
11096
- const top = placement.offset.z + e.topZ;
11097
- if (top > tallest.top)
11098
- tallest = { ref: e.ref, top };
11099
- }
11100
- const lidGap = cavity.ceilZ - tallest.top;
11101
- const topOk = front.length === 0 || lidGap >= clearance - 1e-6;
11102
- let lowest = { ref: "", bottom: Infinity };
11103
- for (const e of back) {
11104
- const bot = placement.offset.z + e.bottomZ;
11105
- if (bot < lowest.bottom)
11106
- lowest = { ref: e.ref, bottom: bot };
11107
- }
11108
- const floorGap = back.length > 0 ? lowest.bottom - cavity.floorZ : Infinity;
11109
- const botOk = back.length === 0 || floorGap >= -1e-6;
11110
- const ok4 = topOk && botOk;
11111
- let detail;
11112
- if (ok4) {
11113
- detail = `Tallest part ${tallest.ref || "\u2014"} leaves ${round2(lidGap)}mm under the lid (cavity depth ${round2(cavityDepth)}mm)`;
11114
- if (back.length > 0 && Number.isFinite(floorGap)) {
11115
- detail += `; back-side ${lowest.ref} clears floor by ${round2(floorGap)}mm`;
11116
- }
11117
- } else if (!topOk) {
11118
- detail = `${tallest.ref} is ${round2(-lidGap + clearance)}mm too tall \u2014 it ${lidGap < 0 ? "punches through" : "is within clearance of"} the lid (cavity depth ${round2(cavityDepth)}mm)`;
11119
- } else {
11120
- detail = `Back-side ${lowest.ref} collides with the floor by ${round2(-floorGap)}mm \u2014 raise the standoffs`;
11121
- }
11122
- return {
11123
- id: "lid_clearance",
11124
- label: "Components clear the lid",
11125
- status: ok4 ? "pass" : "fail",
11126
- detail,
11127
- measurements: {
11128
- cavity_depth_mm: round2(cavityDepth),
11129
- tallest_ref: tallest.ref || "none",
11130
- lid_gap_mm: Number.isFinite(lidGap) ? round2(lidGap) : "n/a",
11131
- stack_top_z: Number.isFinite(tallest.top) ? round2(tallest.top) : "n/a",
11132
- floor_gap_mm: Number.isFinite(floorGap) ? round2(floorGap) : "n/a"
11133
- }
11134
- };
11135
- }
11136
- function checkMountingHoles(input, placement) {
11137
- const holes = input.mountingHoles ?? [];
11138
- const standoffs = input.standoffs ?? [];
11139
- const tol = input.holeTolerance ?? DEFAULT_HOLE_TOL;
11140
- if (holes.length === 0) {
11141
- return {
11142
- id: "mounting_holes",
11143
- label: "Mounting holes land on standoffs",
11144
- status: "skip",
11145
- detail: "Board declares no mounting holes"
11146
- };
11147
- }
11148
- if (standoffs.length === 0) {
11149
- return {
11150
- id: "mounting_holes",
11151
- label: "Mounting holes land on standoffs",
11152
- status: "skip",
11153
- detail: `Board has ${holes.length} mounting hole(s) but no standoffs were detected in the enclosure`
11154
- };
11155
- }
11156
- let matched = 0;
11157
- let worst2 = 0;
11158
- const misses = [];
11159
- for (const h of holes) {
11160
- const w = toWorld(h, placement);
11161
- let best = Infinity;
11162
- for (const s of standoffs) {
11163
- const d = Math.hypot(w.x - s.x, w.y - s.y);
11164
- if (d < best)
11165
- best = d;
11166
- }
11167
- if (best <= tol) {
11168
- matched++;
11169
- if (best > worst2)
11170
- worst2 = best;
11171
- } else {
11172
- misses.push(`${h.ref ?? "hole"}@(${round2(w.x)},${round2(w.y)}) is ${round2(best)}mm off`);
11173
- }
11174
- }
11175
- const ok4 = matched === holes.length;
11176
- return {
11177
- id: "mounting_holes",
11178
- label: "Mounting holes land on standoffs",
11179
- status: ok4 ? "pass" : "fail",
11180
- detail: ok4 ? `All ${holes.length} mounting holes align to standoffs (worst offset ${round2(worst2)}mm, tol ${tol}mm)` : `${matched}/${holes.length} holes align \u2014 ${misses.join("; ")}`,
11181
- measurements: {
11182
- holes_total: holes.length,
11183
- holes_matched: matched,
11184
- standoffs: standoffs.length,
11185
- tolerance_mm: tol,
11186
- worst_offset_mm: round2(worst2)
11187
- }
11188
- };
11189
- }
11190
- function checkConnectors(input, placement, clearance) {
11191
- const conns = input.connectors ?? [];
11192
- const openings = input.openings ?? [];
11193
- if (conns.length === 0) {
11194
- return {
11195
- id: "connector_cutouts",
11196
- label: "Connectors align to wall cutouts",
11197
- status: "skip",
11198
- detail: "Board declares no edge connectors"
11199
- };
11200
- }
11201
- const cav = input.cavity;
11202
- let aligned = 0;
11203
- const problems = [];
11204
- for (const c of conns) {
11205
- const w = toWorld(c, placement);
11206
- const wallEdge = nearestEdge(w.x, w.y, cav);
11207
- const along = wallEdge === "minX" || wallEdge === "maxX" ? w.y : w.x;
11208
- const onWall = openings.filter((o) => o.edge === wallEdge);
11209
- if (onWall.length === 0) {
11210
- problems.push(`${c.ref} faces the ${wallEdge} wall but it has no cutout`);
11211
- continue;
11212
- }
11213
- const hit = onWall.find((o) => {
11214
- const oc = o.edge === "minX" || o.edge === "maxX" ? o.center.y : o.center.x;
11215
- return Math.abs(along - oc) <= o.width / 2 + clearance;
11216
- });
11217
- if (hit) {
11218
- aligned++;
11219
- } else {
11220
- const nearest = onWall.reduce((best, o) => {
11221
- const oc = o.edge === "minX" || o.edge === "maxX" ? o.center.y : o.center.x;
11222
- const off = Math.abs(along - oc) - o.width / 2;
11223
- return off < best ? off : best;
11224
- }, Infinity);
11225
- problems.push(`${c.ref} on ${wallEdge} misses its cutout by ${round2(nearest)}mm`);
11226
- }
11227
- }
11228
- const ok4 = aligned === conns.length;
11229
- const status = ok4 ? "pass" : openings.length === 0 ? "warn" : "fail";
11230
- return {
11231
- id: "connector_cutouts",
11232
- label: "Connectors align to wall cutouts",
11233
- status,
11234
- detail: ok4 ? `All ${conns.length} connector(s) line up with wall cutouts` : openings.length === 0 ? `No wall cutouts detected; ${conns.length} connector(s) would be enclosed: ${problems.join("; ")}` : `${aligned}/${conns.length} connectors aligned \u2014 ${problems.join("; ")}`,
11235
- measurements: {
11236
- connectors_total: conns.length,
11237
- connectors_aligned: aligned,
11238
- wall_openings: openings.length
11239
- }
11240
- };
11135
+ return JSON.parse(enclosureWasm().enclosure_connectors(JSON.stringify(pcb), JSON.stringify(outline)));
11241
11136
  }
11242
11137
  function checkEnclosureFit(input) {
11243
- const clearance = input.clearance ?? DEFAULT_CLEARANCE;
11244
- const placement = input.placement ?? autoPlacement(input, clearance);
11245
- const checks = [
11246
- checkBoardFit(input, placement, clearance),
11247
- checkLidClearance(input, placement, clearance),
11248
- checkMountingHoles(input, placement),
11249
- checkConnectors(input, placement, clearance)
11250
- ];
11251
- const failed = checks.filter((c) => c.status === "fail");
11252
- const warned = checks.filter((c) => c.status === "warn");
11253
- const passed = checks.filter((c) => c.status === "pass");
11254
- const ok4 = failed.length === 0;
11255
- const verified = ok4 && warned.length === 0;
11256
- let summary;
11257
- if (failed.length > 0) {
11258
- summary = `Enclosure fit: FAIL \u2014 ${failed.map((c) => c.label.toLowerCase()).join("; ")}`;
11259
- } else if (warned.length > 0) {
11260
- summary = `Enclosure fit: UNVERIFIED \u2014 ${passed.length} passed, ${warned.length} warning(s): ${warned.map((c) => c.detail).join("; ")}`;
11261
- } else {
11262
- summary = `Enclosure fit: PASS \u2014 ${passed.length}/${checks.length} checks (${checks.filter((c) => c.status === "pass").map((c) => c.label.toLowerCase()).join(", ")})`;
11263
- }
11264
- return { ok: ok4, verified, summary, clearance, placement, checks };
11138
+ return JSON.parse(enclosureWasm().enclosure_fit(JSON.stringify(compact(input))));
11265
11139
  }
11266
11140
  function deriveBoardFromCavity(cavity, standoffs, opts = {}) {
11267
- const clearance = opts.clearance ?? DEFAULT_CLEARANCE;
11268
- const thickness = opts.thickness ?? 1.6;
11269
- const holeDia = opts.holeDiameter ?? 3.2;
11270
- const w = round2(cavity.maxX - cavity.minX - 2 * clearance);
11271
- const h = round2(cavity.maxY - cavity.minY - 2 * clearance);
11272
- const outline = {
11273
- vertices: [
11274
- { x: 0, y: 0 },
11275
- { x: w, y: 0 },
11276
- { x: w, y: h },
11277
- { x: 0, y: h }
11278
- ],
11279
- thickness
11280
- };
11281
- const standoffTop = standoffs.length > 0 ? Math.max(...standoffs.map((s) => s.topZ)) : cavity.floorZ + (opts.standoffHeight ?? 0);
11282
- const offX = cavity.minX + clearance;
11283
- const offY = cavity.minY + clearance;
11284
- const placement = {
11285
- offset: { x: round2(offX), y: round2(offY), z: round2(standoffTop) },
11286
- rotationDeg: 0
11287
- };
11288
- const mountingHoles = [];
11289
- for (const s of standoffs) {
11290
- const lx = round2(s.x - offX);
11291
- const ly = round2(s.y - offY);
11292
- if (lx >= 0 && lx <= w && ly >= 0 && ly <= h) {
11293
- mountingHoles.push({ x: lx, y: ly, diameter: holeDia });
11294
- }
11295
- }
11296
- return { outline, mountingHoles, placement };
11141
+ return JSON.parse(enclosureWasm().enclosure_derive_board(JSON.stringify(cavity), JSON.stringify(standoffs), JSON.stringify(compact(opts))));
11297
11142
  }
11298
- var DEFAULT_CLEARANCE, DEFAULT_HOLE_TOL, round2, MOUNT_RE, CONNECTOR_REF_RE, CONNECTOR_NAME_RE;
11299
11143
  var init_enclosure_fit = __esm({
11300
11144
  "../engine/dist/enclosure-fit.js"() {
11301
11145
  "use strict";
11302
- DEFAULT_CLEARANCE = 0.5;
11303
- DEFAULT_HOLE_TOL = 0.6;
11304
- round2 = (n) => Math.round(n * 100) / 100;
11305
- MOUNT_RE = /mount(ing)?[_-]?hole|mountingpad|mounthole/i;
11306
- CONNECTOR_REF_RE = /^(J|CN|CON|USB|P)\d/i;
11307
- CONNECTOR_NAME_RE = /usb|type[-_]?c|micro|mini|conn|header|jst|molex|rj45|hdr|terminal|socket|receptacle|barrel|dcjack/i;
11146
+ init_wasm_singleton();
11308
11147
  }
11309
11148
  });
11310
11149
 
11311
11150
  // ../engine/dist/enclosure-mesh.js
11312
- function gwn(positions, indices, qx, qy, qz) {
11313
- let w = 0;
11314
- const tris = indices.length;
11315
- for (let t2 = 0; t2 < tris; t2 += 3) {
11316
- const i0 = indices[t2] * 3;
11317
- const i1 = indices[t2 + 1] * 3;
11318
- const i2 = indices[t2 + 2] * 3;
11319
- const ax = positions[i0] - qx;
11320
- const ay = positions[i0 + 1] - qy;
11321
- const az = positions[i0 + 2] - qz;
11322
- const bx = positions[i1] - qx;
11323
- const by = positions[i1 + 1] - qy;
11324
- const bz = positions[i1 + 2] - qz;
11325
- const cx = positions[i2] - qx;
11326
- const cy = positions[i2 + 1] - qy;
11327
- const cz = positions[i2 + 2] - qz;
11328
- const la = Math.sqrt(ax * ax + ay * ay + az * az);
11329
- const lb = Math.sqrt(bx * bx + by * by + bz * bz);
11330
- const lc = Math.sqrt(cx * cx + cy * cy + cz * cz);
11331
- const cbx = by * cz - bz * cy;
11332
- const cby = bz * cx - bx * cz;
11333
- const cbz = bx * cy - by * cx;
11334
- const num2 = ax * cbx + ay * cby + az * cbz;
11335
- const den = la * lb * lc + (ax * bx + ay * by + az * bz) * lc + (bx * cx + by * cy + bz * cz) * la + (cx * ax + cy * ay + cz * az) * lb;
11336
- w += Math.atan2(num2, den);
11337
- }
11338
- return w / (2 * Math.PI);
11339
- }
11340
- function buildOccupancy(positions, indices) {
11341
- let minX = Infinity;
11342
- let maxX = -Infinity;
11343
- let minY = Infinity;
11344
- let maxY = -Infinity;
11345
- let minZ = Infinity;
11346
- let maxZ = -Infinity;
11347
- for (let i = 0; i < positions.length; i += 3) {
11348
- const x = positions[i];
11349
- const y = positions[i + 1];
11350
- const z = positions[i + 2];
11351
- if (x < minX)
11352
- minX = x;
11353
- if (x > maxX)
11354
- maxX = x;
11355
- if (y < minY)
11356
- minY = y;
11357
- if (y > maxY)
11358
- maxY = y;
11359
- if (z < minZ)
11360
- minZ = z;
11361
- if (z > maxZ)
11362
- maxZ = z;
11363
- }
11364
- if (!(maxX > minX) || !(maxY > minY) || !(maxZ > minZ))
11365
- return null;
11366
- const gw = GRID_XY;
11367
- const gh = GRID_XY;
11368
- const gz = GRID_Z;
11369
- const dx = (maxX - minX) / gw;
11370
- const dy = (maxY - minY) / gh;
11371
- const dz = (maxZ - minZ) / gz;
11372
- const occ = new Uint8Array(gw * gh * gz);
11373
- for (let k = 0; k < gz; k++) {
11374
- const qz = minZ + (k + JZ) * dz;
11375
- for (let j = 0; j < gh; j++) {
11376
- const qy = minY + (j + JY) * dy;
11377
- for (let i = 0; i < gw; i++) {
11378
- const qx = minX + (i + JX) * dx;
11379
- if (Math.abs(gwn(positions, indices, qx, qy, qz)) > INSIDE) {
11380
- occ[i + gw * (j + gh * k)] = 1;
11381
- }
11382
- }
11383
- }
11384
- }
11385
- return { gw, gh, gz, ox: minX, oy: minY, oz: minZ, dx, dy, dz, minZ, maxZ, occ };
11386
- }
11387
- function bottomRunTopZ(o, i, j) {
11388
- const base = i + o.gw * j;
11389
- if (o.occ[base] !== 1)
11390
- return null;
11391
- let k = 1;
11392
- while (k < o.gz && o.occ[base + o.gw * o.gh * k] === 1)
11393
- k++;
11394
- return o.oz + k * o.dz;
11395
- }
11396
- function openAtTop(o, i, j) {
11397
- return o.occ[i + o.gw * (j + o.gh * (o.gz - 1))] !== 1;
11398
- }
11399
- function hasGap(o, i, j, kLo, kHi) {
11400
- for (let k = kLo; k <= kHi; k++) {
11401
- if (o.occ[i + o.gw * (j + o.gh * k)] !== 1)
11402
- return true;
11403
- }
11404
- return false;
11405
- }
11406
11151
  function extractEnclosureFeatures(positions, indices) {
11407
- const o = buildOccupancy(positions, indices);
11408
- if (!o) {
11409
- return {
11410
- outer: { minX: 0, maxX: 0, minY: 0, maxY: 0, minZ: 0, maxZ: 0 },
11411
- cavity: null,
11412
- standoffs: [],
11413
- openings: []
11414
- };
11415
- }
11416
- const outer = {
11417
- minX: o.ox,
11418
- maxX: o.ox + o.gw * o.dx,
11419
- minY: o.oy,
11420
- maxY: o.oy + o.gh * o.dy,
11421
- minZ: o.minZ,
11422
- maxZ: o.maxZ
11423
- };
11424
- const isPocket = new Uint8Array(o.gw * o.gh);
11425
- const firstTops = [];
11426
- const countX = new Int32Array(o.gw);
11427
- const countY = new Int32Array(o.gh);
11428
- let pocketCount = 0;
11429
- for (let j = 0; j < o.gh; j++) {
11430
- for (let i = 0; i < o.gw; i++) {
11431
- const top = bottomRunTopZ(o, i, j);
11432
- if (top != null && openAtTop(o, i, j)) {
11433
- isPocket[i + o.gw * j] = 1;
11434
- pocketCount++;
11435
- firstTops.push(top);
11436
- countX[i]++;
11437
- countY[j]++;
11438
- }
11439
- }
11440
- }
11441
- if (pocketCount === 0) {
11442
- return { outer, cavity: null, standoffs: [], openings: [] };
11443
- }
11444
- const core = (counts) => {
11445
- let peak = 0;
11446
- for (const c of counts)
11447
- if (c > peak)
11448
- peak = c;
11449
- const thr = peak * 0.5;
11450
- let lo = 0;
11451
- let hi = counts.length - 1;
11452
- while (lo < counts.length && counts[lo] < thr)
11453
- lo++;
11454
- while (hi >= 0 && counts[hi] < thr)
11455
- hi--;
11456
- return [lo, hi];
11457
- };
11458
- const [pminI, pmaxI] = core(countX);
11459
- const [pminJ, pmaxJ] = core(countY);
11460
- const sortedTops = [...firstTops].sort((a, b) => a - b);
11461
- const floorZ = sortedTops[Math.floor(sortedTops.length / 2)] ?? o.minZ;
11462
- const ceilZ = o.maxZ;
11463
- const cavity = {
11464
- minX: o.ox + pminI * o.dx,
11465
- maxX: o.ox + (pmaxI + 1) * o.dx,
11466
- minY: o.oy + pminJ * o.dy,
11467
- maxY: o.oy + (pmaxJ + 1) * o.dy,
11468
- floorZ: round22(floorZ),
11469
- ceilZ: round22(ceilZ),
11470
- hasLid: false
11471
- };
11472
- const standoffs = extractStandoffs(o, isPocket, floorZ);
11473
- const openings = extractOpenings(o, cavity, floorZ, ceilZ);
11474
- return { outer, cavity, standoffs, openings };
11475
- }
11476
- function extractStandoffs(o, isPocket, floorZ) {
11477
- const isPost = new Uint8Array(o.gw * o.gh);
11478
- for (let j = 0; j < o.gh; j++) {
11479
- for (let i = 0; i < o.gw; i++) {
11480
- if (!isPocket[i + o.gw * j])
11481
- continue;
11482
- const top = bottomRunTopZ(o, i, j);
11483
- if (top != null && top > floorZ + MIN_POST_HEIGHT)
11484
- isPost[i + o.gw * j] = 1;
11485
- }
11486
- }
11487
- const seen = new Uint8Array(o.gw * o.gh);
11488
- const standoffs = [];
11489
- for (let j = 0; j < o.gh; j++) {
11490
- for (let i = 0; i < o.gw; i++) {
11491
- const k0 = i + o.gw * j;
11492
- if (!isPost[k0] || seen[k0])
11493
- continue;
11494
- const stack = [[i, j]];
11495
- seen[k0] = 1;
11496
- let sumX = 0;
11497
- let sumY = 0;
11498
- let n = 0;
11499
- let topMax = -Infinity;
11500
- let minI = i;
11501
- let maxI = i;
11502
- let minJ = j;
11503
- let maxJ = j;
11504
- while (stack.length) {
11505
- const [ci, cj] = stack.pop();
11506
- sumX += o.ox + (ci + 0.5) * o.dx;
11507
- sumY += o.oy + (cj + 0.5) * o.dy;
11508
- n++;
11509
- const t2 = bottomRunTopZ(o, ci, cj);
11510
- if (t2 != null && t2 > topMax)
11511
- topMax = t2;
11512
- if (ci < minI)
11513
- minI = ci;
11514
- if (ci > maxI)
11515
- maxI = ci;
11516
- if (cj < minJ)
11517
- minJ = cj;
11518
- if (cj > maxJ)
11519
- maxJ = cj;
11520
- for (const [ni, nj] of [
11521
- [ci - 1, cj],
11522
- [ci + 1, cj],
11523
- [ci, cj - 1],
11524
- [ci, cj + 1]
11525
- ]) {
11526
- if (ni < 0 || nj < 0 || ni >= o.gw || nj >= o.gh)
11527
- continue;
11528
- const nk = ni + o.gw * nj;
11529
- if (isPost[nk] && !seen[nk]) {
11530
- seen[nk] = 1;
11531
- stack.push([ni, nj]);
11532
- }
11533
- }
11534
- }
11535
- if (n < 2)
11536
- continue;
11537
- const radius = Math.max(((maxI - minI + 1) * o.dx + (maxJ - minJ + 1) * o.dy) / 4, o.dx);
11538
- standoffs.push({
11539
- x: round22(sumX / n),
11540
- y: round22(sumY / n),
11541
- topZ: round22(topMax),
11542
- radius: round22(radius)
11543
- });
11544
- }
11545
- }
11546
- return standoffs;
11547
- }
11548
- function extractOpenings(o, cavity, floorZ, ceilZ) {
11549
- const pIminI = Math.round((cavity.minX - o.ox) / o.dx);
11550
- const pImaxI = Math.round((cavity.maxX - o.ox) / o.dx) - 1;
11551
- const pIminJ = Math.round((cavity.minY - o.oy) / o.dy);
11552
- const pImaxJ = Math.round((cavity.maxY - o.oy) / o.dy) - 1;
11553
- const kLo = Math.max(0, Math.floor((floorZ - o.oz) / o.dz) + 1);
11554
- const kHi = Math.min(o.gz - 1, Math.ceil((ceilZ - o.oz) / o.dz) - 1);
11555
- const openings = [];
11556
- const wallOpen = (i, j) => {
11557
- if (i < 0 || j < 0 || i >= o.gw || j >= o.gh)
11558
- return true;
11559
- return hasGap(o, i, j, kLo, kHi);
11560
- };
11561
- const scans = [];
11562
- for (const [edge, wi] of [
11563
- ["minX", pIminI - 1],
11564
- ["maxX", pImaxI + 1]
11565
- ]) {
11566
- const cells = [];
11567
- for (let j = pIminJ; j <= pImaxJ; j++) {
11568
- cells.push({ open: wallOpen(wi, j), x: o.ox + (wi + 0.5) * o.dx, y: o.oy + (j + 0.5) * o.dy, i: wi, j });
11569
- }
11570
- scans.push({ edge, cells });
11571
- }
11572
- for (const [edge, wj] of [
11573
- ["minY", pIminJ - 1],
11574
- ["maxY", pImaxJ + 1]
11575
- ]) {
11576
- const cells = [];
11577
- for (let i = pIminI; i <= pImaxI; i++) {
11578
- cells.push({ open: wallOpen(i, wj), x: o.ox + (i + 0.5) * o.dx, y: o.oy + (wj + 0.5) * o.dy, i, j: wj });
11579
- }
11580
- scans.push({ edge, cells });
11581
- }
11582
- for (const scan of scans) {
11583
- let run = [];
11584
- const flush = () => {
11585
- if (run.length >= MIN_OPENING_CELLS) {
11586
- const xs = run.map((p) => p.x);
11587
- const ys = run.map((p) => p.y);
11588
- const horiz = scan.edge === "minY" || scan.edge === "maxY";
11589
- const center = {
11590
- x: (Math.min(...xs) + Math.max(...xs)) / 2,
11591
- y: (Math.min(...ys) + Math.max(...ys)) / 2
11592
- };
11593
- const width = horiz ? Math.max(...xs) - Math.min(...xs) + o.dx : Math.max(...ys) - Math.min(...ys) + o.dy;
11594
- const mid = run[Math.floor(run.length / 2)];
11595
- const { zMin, zMax } = openingZSpan(o, mid.i, mid.j, kLo, kHi);
11596
- openings.push({
11597
- edge: scan.edge,
11598
- center: { x: round22(center.x), y: round22(center.y) },
11599
- width: round22(width),
11600
- zMin: round22(zMin),
11601
- zMax: round22(zMax)
11602
- });
11603
- }
11604
- run = [];
11605
- };
11606
- for (const c of scan.cells) {
11607
- if (c.open)
11608
- run.push({ x: c.x, y: c.y, i: c.i, j: c.j });
11609
- else
11610
- flush();
11611
- }
11612
- flush();
11613
- }
11614
- return openings;
11615
- }
11616
- function openingZSpan(o, i, j, kLo, kHi) {
11617
- let zMin = Infinity;
11618
- let zMax = -Infinity;
11619
- for (let k = kLo; k <= kHi; k++) {
11620
- if (o.occ[i + o.gw * (j + o.gh * k)] !== 1) {
11621
- const z = o.oz + (k + 0.5) * o.dz;
11622
- if (z < zMin)
11623
- zMin = z;
11624
- if (z > zMax)
11625
- zMax = z;
11626
- }
11627
- }
11628
- if (!Number.isFinite(zMin))
11629
- return { zMin: o.oz, zMax: o.maxZ };
11630
- return { zMin, zMax };
11152
+ const json = enclosureWasm().enclosure_features(Float64Array.from(positions), Uint32Array.from(indices));
11153
+ return JSON.parse(json);
11631
11154
  }
11632
- var GRID_XY, GRID_Z, INSIDE, MIN_POST_HEIGHT, MIN_OPENING_CELLS, JX, JY, JZ, round22;
11633
11155
  var init_enclosure_mesh = __esm({
11634
11156
  "../engine/dist/enclosure-mesh.js"() {
11635
11157
  "use strict";
11636
- GRID_XY = 48;
11637
- GRID_Z = 28;
11638
- INSIDE = 0.5;
11639
- MIN_POST_HEIGHT = 0.8;
11640
- MIN_OPENING_CELLS = 2;
11641
- JX = 0.5 + 0.137;
11642
- JY = 0.5 - 0.077;
11643
- JZ = 0.5 + 0.041;
11644
- round22 = (n) => Math.round(n * 100) / 100;
11158
+ init_enclosure_fit();
11645
11159
  }
11646
11160
  });
11647
11161
 
@@ -11955,6 +11469,7 @@ var init_dist = __esm({
11955
11469
  antennaAnalyze: wasmModule7.antennaAnalyze,
11956
11470
  photonicsSimulate: wasmModule7.photonicsSimulate,
11957
11471
  neutronicsSimulate: wasmModule7.neutronicsSimulate,
11472
+ latticeGaugeSimulate: wasmModule7.latticeGaugeSimulate,
11958
11473
  analyzeStaticsBox: wasmModule7.analyzeStaticsBox,
11959
11474
  analyzeStaticsMesh: wasmModule7.analyzeStaticsMesh
11960
11475
  }, compiledWasmModule);
@@ -12315,6 +11830,19 @@ var init_dist = __esm({
12315
11830
  }
12316
11831
  return fn(specJson, paramsJson);
12317
11832
  }
11833
+ /**
11834
+ * Lattice gauge theory Monte Carlo (quenched SU(2)/SU(3) Wilson
11835
+ * action): plaquette, Wilson loops, string tension, Polyakov order
11836
+ * parameter, flux-tube profile, field snapshots — jackknife errors
11837
+ * throughout; see `vcad-kernel-qcd`.
11838
+ */
11839
+ latticeGaugeSimulate(specJson) {
11840
+ const fn = this.kernel.latticeGaugeSimulate;
11841
+ if (typeof fn !== "function") {
11842
+ throw new Error("latticeGaugeSimulate is not exported by this kernel WASM build \u2014 rebuild packages/kernel-wasm");
11843
+ }
11844
+ return fn(specJson);
11845
+ }
12318
11846
  topologyOptimizeBox(min, max, spec) {
12319
11847
  const fn = this.kernel.topologyOptimizeBox;
12320
11848
  if (typeof fn !== "function") {
@@ -32264,13 +31792,13 @@ var init_document_store = __esm({
32264
31792
  const ang = (fp.rotation ?? 0) * Math.PI / 180;
32265
31793
  const cos = Math.cos(ang);
32266
31794
  const sin = Math.sin(ang);
32267
- const padWorld3 = fp.pads.map((p) => ({
31795
+ const padWorld2 = fp.pads.map((p) => ({
32268
31796
  x: fp.position.x + (p.position.x * cos - p.position.y * sin),
32269
31797
  y: fp.position.y + (p.position.x * sin + p.position.y * cos)
32270
31798
  }));
32271
31799
  for (const tr of pcb.traces) {
32272
31800
  for (const end of [tr.start, tr.end]) {
32273
- if (padWorld3.some((pw) => Math.abs(end.x - pw.x) < TOL && Math.abs(end.y - pw.y) < TOL)) {
31801
+ if (padWorld2.some((pw) => Math.abs(end.x - pw.x) < TOL && Math.abs(end.y - pw.y) < TOL)) {
32274
31802
  end.x += dx;
32275
31803
  end.y += dy;
32276
31804
  }
@@ -41281,6 +40809,21 @@ var init_CHANGELOG = __esm({
41281
40809
  "validate_for_fab"
41282
40810
  ]
41283
40811
  },
40812
+ {
40813
+ id: "2026-07-24-lattice-gauge-mcp",
40814
+ version: "0.9.4",
40815
+ date: "2026-07-24",
40816
+ category: "feat",
40817
+ title: "Lattice gauge theory simulation",
40818
+ summary: "New simulate_lattice_gauge tool: quenched SU(2)/SU(3) Monte Carlo \u2014 confinement, string tension, deconfinement, flux tubes and instantons, with jackknife error bars on every number.",
40819
+ features: [
40820
+ "simulation",
40821
+ "physics"
40822
+ ],
40823
+ mcpTools: [
40824
+ "simulate_lattice_gauge"
40825
+ ]
40826
+ },
41284
40827
  {
41285
40828
  id: "2026-07-23-router-post-route-legalization",
41286
40829
  version: "0.9.4",
@@ -52309,6 +51852,10 @@ var init_tool_metadata = __esm({
52309
51852
  title: "Simulate Neutron Shield",
52310
51853
  annotations: RO
52311
51854
  },
51855
+ simulate_lattice_gauge: {
51856
+ title: "Simulate Lattice Gauge Theory",
51857
+ annotations: RO
51858
+ },
52312
51859
  analyze_tolerance_stackup: {
52313
51860
  title: "Analyze Tolerance Stackup",
52314
51861
  annotations: RO
@@ -58340,7 +57887,7 @@ function computeUtilization(footprints, vertices, cutouts, shape, innerRadius, e
58340
57887
  }
58341
57888
  const boardArea = Math.abs(loopSignedArea(vertices)) - (cutouts ?? []).reduce((s, c) => s + Math.abs(loopSignedArea(c)), 0);
58342
57889
  if (!Number.isFinite(occMinX) || boardArea <= 0) return void 0;
58343
- const round25 = (v) => Math.round(v * 100) / 100;
57890
+ const round23 = (v) => Math.round(v * 100) / 100;
58344
57891
  const ceilHalf = (v) => Math.ceil(v * 2) / 2;
58345
57892
  const bbW = occMaxX - occMinX;
58346
57893
  const bbH = occMaxY - occMinY;
@@ -58353,8 +57900,8 @@ function computeUtilization(footprints, vertices, cutouts, shape, innerRadius, e
58353
57900
  suggested = {
58354
57901
  type: "circle",
58355
57902
  outer_diameter: od,
58356
- center: { x: round25(cx), y: round25(cy) },
58357
- ...innerRadius > 0 ? { inner_diameter: round25(2 * innerRadius) } : {},
57903
+ center: { x: round23(cx), y: round23(cy) },
57904
+ ...innerRadius > 0 ? { inner_diameter: round23(2 * innerRadius) } : {},
58358
57905
  note: `Minimum enclosing circle with ${margin}mm edge clearance`
58359
57906
  };
58360
57907
  } else {
@@ -58362,19 +57909,19 @@ function computeUtilization(footprints, vertices, cutouts, shape, innerRadius, e
58362
57909
  type: "rect",
58363
57910
  width: ceilHalf(bbW + 2 * margin),
58364
57911
  height: ceilHalf(bbH + 2 * margin),
58365
- origin: { x: round25(occMinX - margin), y: round25(occMinY - margin) },
57912
+ origin: { x: round23(occMinX - margin), y: round23(occMinY - margin) },
58366
57913
  note: `Minimum enclosing rectangle with ${margin}mm edge clearance`
58367
57914
  };
58368
57915
  }
58369
57916
  return {
58370
- board_area_mm2: round25(boardArea),
58371
- component_area_mm2: round25(componentArea),
57917
+ board_area_mm2: round23(boardArea),
57918
+ component_area_mm2: round23(componentArea),
58372
57919
  utilization_pct: Math.round(componentArea / boardArea * 1e3) / 10,
58373
- bounding_box: { x: round25(occMinX), y: round25(occMinY), w: round25(bbW), h: round25(bbH) },
57920
+ bounding_box: { x: round23(occMinX), y: round23(occMinY), w: round23(bbW), h: round23(bbH) },
58374
57921
  suggested_outline: suggested
58375
57922
  };
58376
57923
  }
58377
- function padWorld2(fp, pad) {
57924
+ function padWorld(fp, pad) {
58378
57925
  const ang = (fp.rotation ?? 0) * Math.PI / 180;
58379
57926
  const cos = Math.cos(ang);
58380
57927
  const sin = Math.sin(ang);
@@ -58393,7 +57940,7 @@ function detectStaleNets(pcb) {
58393
57940
  const arr = padsByNet.get(pad.net) ?? [];
58394
57941
  const sh = pad.shape;
58395
57942
  const halfExtent = sh.diameter ? sh.diameter / 2 : Math.hypot(sh.width ?? 0, sh.height ?? 0) / 2;
58396
- arr.push({ pos: padWorld2(fp, pad), layers: pad.layers, halfExtent });
57943
+ arr.push({ pos: padWorld(fp, pad), layers: pad.layers, halfExtent });
58397
57944
  padsByNet.set(pad.net, arr);
58398
57945
  }
58399
57946
  }
@@ -59277,7 +58824,7 @@ function layoutLint(pcb) {
59277
58824
  for (const fp of fps) {
59278
58825
  const prefix = refPrefix(fp.ref);
59279
58826
  for (const pad of fp.pads) {
59280
- const w = { ref: fp.ref, prefix, pad, pos: padWorld2(fp, pad) };
58827
+ const w = { ref: fp.ref, prefix, pad, pos: padWorld(fp, pad) };
59281
58828
  allPads.push(w);
59282
58829
  if (pad.net) {
59283
58830
  const arr = byNet.get(pad.net) ?? [];
@@ -59293,7 +58840,7 @@ function layoutLint(pcb) {
59293
58840
  for (const pad of fp.pads) {
59294
58841
  const net = pad.net;
59295
58842
  if (!net || LINT_GROUND_NET_RE.test(net) || LINT_SUPPLY_NET_RE.test(net)) continue;
59296
- const from = padWorld2(fp, pad);
58843
+ const from = padWorld(fp, pad);
59297
58844
  let nearest = null;
59298
58845
  for (const other of byNet.get(net) ?? []) {
59299
58846
  if (other.ref === fp.ref || other.prefix !== "U") continue;
@@ -59308,9 +58855,9 @@ function layoutLint(pcb) {
59308
58855
  warnings.push({
59309
58856
  kind: "crystal_far_from_ic",
59310
58857
  refs: [fp.ref, worst2.icRef],
59311
- distance_mm: round23(worst2.dist),
58858
+ distance_mm: round2(worst2.dist),
59312
58859
  threshold_mm: LINT_CRYSTAL_MAX_MM,
59313
- message: `Crystal ${fp.ref} is ${round23(worst2.dist)}mm from ${worst2.icRef} pad ${worst2.icPad} (net '${worst2.net}') \u2014 oscillator loops want <${LINT_CRYSTAL_MAX_MM}mm; move ${fp.ref} next to its XIN/XOUT pins.`
58860
+ message: `Crystal ${fp.ref} is ${round2(worst2.dist)}mm from ${worst2.icRef} pad ${worst2.icPad} (net '${worst2.net}') \u2014 oscillator loops want <${LINT_CRYSTAL_MAX_MM}mm; move ${fp.ref} next to its XIN/XOUT pins.`
59314
58861
  });
59315
58862
  }
59316
58863
  }
@@ -59321,7 +58868,7 @@ function layoutLint(pcb) {
59321
58868
  const pwrIdx = nets.findIndex((n) => n && LINT_SUPPLY_NET_RE.test(n) && !LINT_GROUND_NET_RE.test(n));
59322
58869
  if (gndIdx < 0 || pwrIdx < 0) continue;
59323
58870
  const pwrPad = fp.pads[pwrIdx];
59324
- const from = padWorld2(fp, pwrPad);
58871
+ const from = padWorld(fp, pwrPad);
59325
58872
  let nearest = null;
59326
58873
  for (const other of byNet.get(pwrPad.net) ?? []) {
59327
58874
  if (other.ref === fp.ref || other.prefix !== "U") continue;
@@ -59334,9 +58881,9 @@ function layoutLint(pcb) {
59334
58881
  warnings.push({
59335
58882
  kind: "decoupling_cap_far_from_pin",
59336
58883
  refs: [fp.ref, nearest.icRef],
59337
- distance_mm: round23(nearest.dist),
58884
+ distance_mm: round2(nearest.dist),
59338
58885
  threshold_mm: LINT_DECAP_MAX_MM,
59339
- message: `Decoupling cap ${fp.ref} is ${round23(nearest.dist)}mm from ${nearest.icRef} pad ${nearest.icPad} (net '${pwrPad.net}') \u2014 decouplers want <${LINT_DECAP_MAX_MM}mm to the pin they decouple.`
58886
+ message: `Decoupling cap ${fp.ref} is ${round2(nearest.dist)}mm from ${nearest.icRef} pad ${nearest.icPad} (net '${pwrPad.net}') \u2014 decouplers want <${LINT_DECAP_MAX_MM}mm to the pin they decouple.`
59340
58887
  });
59341
58888
  }
59342
58889
  }
@@ -59354,15 +58901,15 @@ function layoutLint(pcb) {
59354
58901
  for (const fp of fps) {
59355
58902
  const prefix = refPrefix(fp.ref);
59356
58903
  if (prefix !== "J" && prefix !== "P" && prefix !== "CN" && prefix !== "USB") continue;
59357
- const pts = fp.pads.length > 0 ? fp.pads.map((p) => padWorld2(fp, p)) : [fp.position];
58904
+ const pts = fp.pads.length > 0 ? fp.pads.map((p) => padWorld(fp, p)) : [fp.position];
59358
58905
  const d = Math.min(...pts.map(edgeDist));
59359
58906
  if (d > LINT_CONNECTOR_EDGE_MAX_MM) {
59360
58907
  warnings.push({
59361
58908
  kind: "connector_not_on_edge",
59362
58909
  refs: [fp.ref],
59363
- distance_mm: round23(d),
58910
+ distance_mm: round2(d),
59364
58911
  threshold_mm: LINT_CONNECTOR_EDGE_MAX_MM,
59365
- message: `Connector ${fp.ref} sits ${round23(d)}mm from the board edge \u2014 connectors want edge access (<${LINT_CONNECTOR_EDGE_MAX_MM}mm); move it to the outline.`
58912
+ message: `Connector ${fp.ref} sits ${round2(d)}mm from the board edge \u2014 connectors want edge access (<${LINT_CONNECTOR_EDGE_MAX_MM}mm); move it to the outline.`
59366
58913
  });
59367
58914
  }
59368
58915
  }
@@ -59398,9 +58945,9 @@ function layoutLint(pcb) {
59398
58945
  warnings.push({
59399
58946
  kind: "high_current_near_sensitive",
59400
58947
  refs: [c.hi.ref, c.lo.ref],
59401
- distance_mm: round23(c.dist),
58948
+ distance_mm: round2(c.dist),
59402
58949
  threshold_mm: LINT_HIGH_CURRENT_SEP_MIN_MM,
59403
- message: `High-current net '${c.hiNet}' (${c.hi.ref} pad ${c.hi.pad.number}) is ${round23(c.dist)}mm from '${c.loNet}' (${c.lo.ref} pad ${c.lo.pad.number}) \u2014 keep \u2265${LINT_HIGH_CURRENT_SEP_MIN_MM}mm between high-current and USB/analog pads.`
58950
+ message: `High-current net '${c.hiNet}' (${c.hi.ref} pad ${c.hi.pad.number}) is ${round2(c.dist)}mm from '${c.loNet}' (${c.lo.ref} pad ${c.lo.pad.number}) \u2014 keep \u2265${LINT_HIGH_CURRENT_SEP_MIN_MM}mm between high-current and USB/analog pads.`
59404
58951
  });
59405
58952
  }
59406
58953
  }
@@ -63009,7 +62556,7 @@ async function addMotorWinding(args) {
63009
62556
  if (!pad.net) continue;
63010
62557
  const arr = padsByNet.get(pad.net) ?? [];
63011
62558
  arr.push({
63012
- pos: padWorld2(fp, pad),
62559
+ pos: padWorld(fp, pad),
63013
62560
  onReturnLayer: pad.layers.includes(returnLayer),
63014
62561
  size: padApproxRadius(pad)
63015
62562
  });
@@ -63352,7 +62899,7 @@ async function addMotorWinding(args) {
63352
62899
  for (const fp of pcb.footprints) {
63353
62900
  for (const pad of fp.pads) {
63354
62901
  if (pad.net === net) continue;
63355
- const pw = padWorld2(fp, pad);
62902
+ const pw = padWorld(fp, pad);
63356
62903
  if (pointSegDist(pw, t2.a, t2.b) < padApproxRadius(pad) + halfW + clearance) return false;
63357
62904
  }
63358
62905
  }
@@ -65045,7 +64592,7 @@ async function fixDrc(args) {
65045
64592
  let corridorFail = null;
65046
64593
  for (const pt of movePoints) {
65047
64594
  const onPad = pcb.footprints.some(
65048
- (fp) => fp.pads.some((pad) => samePoint(padWorld2(fp, pad), pt))
64595
+ (fp) => fp.pads.some((pad) => samePoint(padWorld(fp, pad), pt))
65049
64596
  );
65050
64597
  if (onPad) {
65051
64598
  corridorFail = "endpoint lands on a component pad \u2014 fixing requires moving the component";
@@ -65195,7 +64742,7 @@ async function fixDrc(args) {
65195
64742
  };
65196
64743
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
65197
64744
  }
65198
- var PCB_LAYER_SET, COPPER_LAYERS, COPPER_LAYER_SET, POSITION_TOLERANCE, PIN_SEP, pinKey, createSchematicSchema, docInputProperties, placeComponentsSchema, routeNetsSchema, runDrcSchema, runErcSchema, critiqueRouteSchema, routeDiffPairSchema, lengthMatchTracesSchema, exportGerberSchema, validateForFabSchema, exportKicadSchema, addCoilSchema, boardFromSolidSchema, addCoilArraySchema, windingLayoutSchema, calcImpedanceSchema, sizeImpedanceSchema, sizePdnSchema, calcCoilSchema, sizeCoilSchema, calcRfSchema, COIL_GEOMETRY, MU0, RAIL_PIN_NAMES, PIN_TYPES, CHIP_SIZE_CODES, CONNECTIVITY_GUARD_MAX_NETS, POWER_NET_RE, DRC_CATEGORY, DRC_DELTA_SAMPLE_CAP, DRC_DELTA_FULL_BOARD_MAX, LINT_CRYSTAL_MAX_MM, LINT_DECAP_MAX_MM, LINT_CONNECTOR_EDGE_MAX_MM, LINT_HIGH_CURRENT_SEP_MIN_MM, LINT_GROUND_NET_RE, LINT_SUPPLY_NET_RE, SENSITIVE_NET_RE, HIGH_CURRENT_CLASS_RE, refPrefix, dist2d, round23, sumSq, round33, vec2Schema, setBoardOutlineSchema, FOOTPRINT_FAMILIES, listFootprintsSchema, searchFootprintsSchema, getPadPositionsSchema, getFootprintSchema, ROTATION_CONVENTION, describePcbSchema, DESCRIBE_NET_NAME_CAP, addTraceSchema, addViaSchema, OZ_TO_MM, setStackupSchema, setPlacementSchema, addZoneSchema, deleteElementProps, deleteZoneSchema, deleteTraceSchema, deleteViaSchema, COPPER_KINDS, GET_COPPER_CAP, getCopperSchema, TIE_POSITION_TOL, addNetTieSchema, deleteNetTieSchema, undoSchema, setDesignRulesSchema, sizeTraceForCurrentSchema, addViaArraySchema, MAX_VIA_ARRAY, addMotorWindingSchema, calcMotorSchema, sig6, BEARING_FRICTION_MNM, checkSelfStartSchema, solidFromBoardSchema, FR4_DENSITY_KG_M3, COPPER_DENSITY_KG_M3, DEFAULT_COPPER_THICKNESS_MM, loopArea, MOUNTING_FP_RE, KEEPOUT_ECHO_CAP, INLINE_DOC_ECHO_CAP, searchElectronicPartsSchema, resolvePartSchema, findAlternativesSchema, verifySubstitutionSchema, buildReceiptSchema, verifyReceiptSchema, FIX_EPS, FIX_DRC_NEVER, fixDrcSchema, toolDefs20;
64745
+ var PCB_LAYER_SET, COPPER_LAYERS, COPPER_LAYER_SET, POSITION_TOLERANCE, PIN_SEP, pinKey, createSchematicSchema, docInputProperties, placeComponentsSchema, routeNetsSchema, runDrcSchema, runErcSchema, critiqueRouteSchema, routeDiffPairSchema, lengthMatchTracesSchema, exportGerberSchema, validateForFabSchema, exportKicadSchema, addCoilSchema, boardFromSolidSchema, addCoilArraySchema, windingLayoutSchema, calcImpedanceSchema, sizeImpedanceSchema, sizePdnSchema, calcCoilSchema, sizeCoilSchema, calcRfSchema, COIL_GEOMETRY, MU0, RAIL_PIN_NAMES, PIN_TYPES, CHIP_SIZE_CODES, CONNECTIVITY_GUARD_MAX_NETS, POWER_NET_RE, DRC_CATEGORY, DRC_DELTA_SAMPLE_CAP, DRC_DELTA_FULL_BOARD_MAX, LINT_CRYSTAL_MAX_MM, LINT_DECAP_MAX_MM, LINT_CONNECTOR_EDGE_MAX_MM, LINT_HIGH_CURRENT_SEP_MIN_MM, LINT_GROUND_NET_RE, LINT_SUPPLY_NET_RE, SENSITIVE_NET_RE, HIGH_CURRENT_CLASS_RE, refPrefix, dist2d, round2, sumSq, round33, vec2Schema, setBoardOutlineSchema, FOOTPRINT_FAMILIES, listFootprintsSchema, searchFootprintsSchema, getPadPositionsSchema, getFootprintSchema, ROTATION_CONVENTION, describePcbSchema, DESCRIBE_NET_NAME_CAP, addTraceSchema, addViaSchema, OZ_TO_MM, setStackupSchema, setPlacementSchema, addZoneSchema, deleteElementProps, deleteZoneSchema, deleteTraceSchema, deleteViaSchema, COPPER_KINDS, GET_COPPER_CAP, getCopperSchema, TIE_POSITION_TOL, addNetTieSchema, deleteNetTieSchema, undoSchema, setDesignRulesSchema, sizeTraceForCurrentSchema, addViaArraySchema, MAX_VIA_ARRAY, addMotorWindingSchema, calcMotorSchema, sig6, BEARING_FRICTION_MNM, checkSelfStartSchema, solidFromBoardSchema, FR4_DENSITY_KG_M3, COPPER_DENSITY_KG_M3, DEFAULT_COPPER_THICKNESS_MM, loopArea, MOUNTING_FP_RE, KEEPOUT_ECHO_CAP, INLINE_DOC_ECHO_CAP, searchElectronicPartsSchema, resolvePartSchema, findAlternativesSchema, verifySubstitutionSchema, buildReceiptSchema, verifyReceiptSchema, FIX_EPS, FIX_DRC_NEVER, fixDrcSchema, toolDefs20;
65199
64746
  var init_ecad2 = __esm({
65200
64747
  "src/tools/ecad.ts"() {
65201
64748
  "use strict";
@@ -65997,7 +65544,7 @@ var init_ecad2 = __esm({
65997
65544
  HIGH_CURRENT_CLASS_RE = /pwr|power|high[-_ ]?current|motor|\bhv\b|hi[-_ ]?amp/i;
65998
65545
  refPrefix = (ref) => /^[A-Za-z]+/.exec(ref)?.[0]?.toUpperCase() ?? "";
65999
65546
  dist2d = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
66000
- round23 = (n) => Math.round(n * 100) / 100;
65547
+ round2 = (n) => Math.round(n * 100) / 100;
66001
65548
  sumSq = (v) => v.reduce((s, x) => s + x * x, 0);
66002
65549
  round33 = (v) => Math.round(v * 1e3) / 1e3;
66003
65550
  vec2Schema = {
@@ -71074,6 +70621,128 @@ var init_neutronics = __esm({
71074
70621
  }
71075
70622
  });
71076
70623
 
70624
+ // src/tools/qcd.ts
70625
+ function textResult11(payload) {
70626
+ return {
70627
+ content: [
70628
+ {
70629
+ type: "text",
70630
+ text: JSON.stringify(payload, null, 2)
70631
+ }
70632
+ ]
70633
+ };
70634
+ }
70635
+ var simSpecSchema, toolDefs36;
70636
+ var init_qcd = __esm({
70637
+ "src/tools/qcd.ts"() {
70638
+ "use strict";
70639
+ init_tool_def();
70640
+ simSpecSchema = {
70641
+ type: "object",
70642
+ required: [
70643
+ "dims",
70644
+ "beta",
70645
+ "thermalization_sweeps",
70646
+ "measurement_sweeps",
70647
+ "overrelax_per_heatbath",
70648
+ "bin_size",
70649
+ "max_wilson_extent",
70650
+ "seed",
70651
+ "hot_start"
70652
+ ],
70653
+ description: "Lattice simulation spec. Deterministic per seed. Direction 3 is time \u2014 shorten it (e.g. [6,6,6,2]) for finite temperature / deconfinement studies.",
70654
+ properties: {
70655
+ gauge: {
70656
+ type: "string",
70657
+ enum: ["Su2", "Su3"],
70658
+ description: "Gauge group. Su2 (default): quaternion links, exact Kennedy-Pendleton heatbath, fastest. Su3: the real QCD gauge group, Cabibbo-Marinari updates, ~6x cost per link."
70659
+ },
70660
+ dims: {
70661
+ type: "array",
70662
+ items: { type: "number" },
70663
+ minItems: 4,
70664
+ maxItems: 4,
70665
+ description: "Lattice extents [n0,n1,n2,n3], each >= 2. 4^4-8^4 are seconds-scale; 12^4+ is where the cost gate starts to bite."
70666
+ },
70667
+ beta: {
70668
+ type: "number",
70669
+ description: "Inverse coupling beta = 2N/g^2 (Wilson action). SU(2): strong coupling < ~1.5, crossover ~2.2-2.5, weak > 4. SU(3): strong < ~4, deconfinement at N_t=2 near 5.1, weak > 7."
70670
+ },
70671
+ thermalization_sweeps: {
70672
+ type: "number",
70673
+ description: "Discarded equilibration sweeps (>= 1). 50 is typical."
70674
+ },
70675
+ measurement_sweeps: {
70676
+ type: "number",
70677
+ description: "Measured sweeps. Must give >= 2 full jackknife bins (>= 5 bins to mint claims). 100-200 is typical."
70678
+ },
70679
+ overrelax_per_heatbath: {
70680
+ type: "number",
70681
+ description: "Overrelaxation sweeps interleaved per heatbath sweep (1-3 decorrelates much faster per unit work)."
70682
+ },
70683
+ bin_size: {
70684
+ type: "number",
70685
+ description: "Jackknife bin size in measurements (10-20 typical)."
70686
+ },
70687
+ max_wilson_extent: {
70688
+ type: "number",
70689
+ description: "Largest square Wilson loop W(r,t) to measure; 0 = plaquette only. Capped at half the smallest lattice extent."
70690
+ },
70691
+ seed: { type: "number" },
70692
+ hot_start: {
70693
+ type: "boolean",
70694
+ description: "Start from random links instead of cold (identity)."
70695
+ },
70696
+ smear: {
70697
+ type: "object",
70698
+ required: ["alpha", "iterations"],
70699
+ description: "APE spatial smearing applied to a measurement copy before loop observables (lifts the string-tension signal at intermediate coupling; plaquette stays unsmeared). alpha 0.5, 2-3 iterations is customary.",
70700
+ properties: {
70701
+ alpha: { type: "number" },
70702
+ iterations: { type: "number" }
70703
+ }
70704
+ },
70705
+ measure_temporal_loops: {
70706
+ type: "boolean",
70707
+ description: "Also measure spatial x temporal Wilson loops and derive the static quark potential V(r) (+ Cornell fit when >= 3 points resolve)."
70708
+ },
70709
+ measure_polyakov: {
70710
+ type: "boolean",
70711
+ description: "Measure <|L|>, the Polyakov-loop deconfinement order parameter (small = confined, O(1) = deconfined)."
70712
+ },
70713
+ flux_tube: {
70714
+ type: "object",
70715
+ required: ["separation"],
70716
+ description: "Measure the chromoelectric flux-tube profile between a static quark-antiquark pair at this separation (lattice units, spatial axis 0): the connected (Polyakov-pair x action-density) 3D excess field plus the pair correlator whose decay with separation IS confinement. Use a short time axis (N_t 2-4).",
70717
+ properties: { separation: { type: "number" } }
70718
+ },
70719
+ snapshot_cooling: {
70720
+ type: "number",
70721
+ description: "Export a rendering FieldSnapshot (action density per site + complex Polyakov field per spatial site) of the final configuration after this many cooling sweeps (0 = raw vacuum boil; 20-30 = classical lumps). SU(2) runs with cooling >= 1 also report the clover topological charge (near-integer = instanton content)."
70722
+ }
70723
+ }
70724
+ };
70725
+ toolDefs36 = [
70726
+ {
70727
+ name: "simulate_lattice_gauge",
70728
+ pack: null,
70729
+ description: "Lattice gauge theory Monte Carlo (quenched Wilson action, SU(2) or SU(3)): confinement from first principles on a laptop-scale 4D lattice. Returns the average plaquette, Wilson loops, Creutz ratios + static potential + Cornell fit (the string tension sigma*a^2), the Polyakov deconfinement order parameter, and optionally viewport-ready field snapshots (action density, Polyakov field, cooled topological charge) and the static-pair flux-tube profile \u2014 every observable a binned-jackknife mean +- error, bit-deterministic per seed. Fail-closed: starved statistics refuse to mint vcad.qcd-claims/1 (raise measurement_sweeps); logs require loops resolved >= 3-sigma from zero. Honesty bounds ride every claim: quenched (no dynamical fermions), lattice units at fixed coupling (no continuum limit) \u2014 model physics, not physical-QCD numbers. Cost ~ volume x sweeps (x6 for SU(3)); 6^4 with 150 sweeps runs in seconds. Recipes: confinement demo = Su2, [6,6,6,4], beta 2.2, flux_tube {separation 2}; deconfinement = [6,6,6,2], measure_polyakov, beta scan across ~1.88 (Su2) / ~5.1 (Su3); instantons = Su2, beta 2.4, snapshot_cooling 30.",
70730
+ inputSchema: {
70731
+ type: "object",
70732
+ required: ["spec"],
70733
+ properties: { spec: simSpecSchema }
70734
+ },
70735
+ handler: (args, ctx) => {
70736
+ const a = args;
70737
+ const out = ctx.engine.latticeGaugeSimulate(JSON.stringify(a.spec));
70738
+ return textResult11(out);
70739
+ },
70740
+ behavior: behavior({})
70741
+ }
70742
+ ];
70743
+ }
70744
+ });
70745
+
71077
70746
  // src/tools/physics.ts
71078
70747
  function limitClaim(id, description, subject, limit, actual, unit, basis, converged) {
71079
70748
  if (!converged || !Number.isFinite(actual)) {
@@ -71195,7 +70864,7 @@ function predictPhysicsTool(args, engine) {
71195
70864
  ]
71196
70865
  };
71197
70866
  }
71198
- var PHYSICS_DOMAIN, ORACLE, TIER_RESOLUTION, regionSchema3, predictPhysicsSchema, round52, toolDefs36;
70867
+ var PHYSICS_DOMAIN, ORACLE, TIER_RESOLUTION, regionSchema3, predictPhysicsSchema, round52, toolDefs37;
71199
70868
  var init_physics2 = __esm({
71200
70869
  "src/tools/physics.ts"() {
71201
70870
  "use strict";
@@ -71308,7 +70977,7 @@ var init_physics2 = __esm({
71308
70977
  }
71309
70978
  };
71310
70979
  round52 = (v) => Number(v.toPrecision(5));
71311
- toolDefs36 = [
70980
+ toolDefs37 = [
71312
70981
  {
71313
70982
  name: "predict_physics",
71314
70983
  pack: null,
@@ -71550,7 +71219,7 @@ function applySetParam(doc, node, path, value) {
71550
71219
  }
71551
71220
  cursor[parts[parts.length - 1]] = value;
71552
71221
  }
71553
- var lastReports, mechanicalProcessEnum, pcbProcessEnum, processEnum, dfmCheckSchema, dfmExplainSchema, dfmSuggestFixSchema, dfmApplyFixSchema, toolDefs37;
71222
+ var lastReports, mechanicalProcessEnum, pcbProcessEnum, processEnum, dfmCheckSchema, dfmExplainSchema, dfmSuggestFixSchema, dfmApplyFixSchema, toolDefs38;
71554
71223
  var init_dfm2 = __esm({
71555
71224
  "src/tools/dfm.ts"() {
71556
71225
  "use strict";
@@ -71647,7 +71316,7 @@ var init_dfm2 = __esm({
71647
71316
  },
71648
71317
  required: ["issue_id"]
71649
71318
  };
71650
- toolDefs37 = [
71319
+ toolDefs38 = [
71651
71320
  {
71652
71321
  name: "dfm_check",
71653
71322
  pack: "dfm",
@@ -71846,7 +71515,7 @@ function renderedOf(engine, doc) {
71846
71515
  "document has no sheet-metal part (evaluation produced no sheetMetal bundle)"
71847
71516
  );
71848
71517
  }
71849
- function textResult11(payload) {
71518
+ function textResult12(payload) {
71850
71519
  return {
71851
71520
  content: [{ type: "text", text: JSON.stringify(payload, null, 2) }]
71852
71521
  };
@@ -71888,7 +71557,7 @@ function sheetMetalCreate(input, engine) {
71888
71557
  const errors = rendered.violations.filter(
71889
71558
  (v) => v.severity === "Error"
71890
71559
  ).length;
71891
- return textResult11({
71560
+ return textResult12({
71892
71561
  document_id: documentId,
71893
71562
  model: rendered.model,
71894
71563
  flat: {
@@ -71904,7 +71573,7 @@ function sheetMetalUnfold(input, engine) {
71904
71573
  const doc = getSession(String(a.document_id ?? ""));
71905
71574
  const rendered = renderedOf(engine, doc);
71906
71575
  const includeDxf = a.include_dxf !== false;
71907
- return textResult11({
71576
+ return textResult12({
71908
71577
  flat_pattern: rendered.flatPattern,
71909
71578
  ...includeDxf ? { dxf: rendered.dxf } : {},
71910
71579
  note: "The DXF is a fab-ready merged silhouette: one closed exterior LWPOLYLINE plus hole loops on CUT, DASHED bend centerlines (on the allowance midline) on BEND_UP/BEND_DOWN, and any surface markings as open polylines on ENGRAVE (select the laser-marking/engraving service for that layer in the fab's UI). DXF carries no bend angles \u2014 you enter those in the fab's UI. For zero data entry, export the folded body as STEP instead (export_cad with a .step filename); bends are auto-detected by the shop, but radii/K must match their tooling at model time (use shop_profile in sheet_metal_create)."
@@ -71912,7 +71581,7 @@ function sheetMetalUnfold(input, engine) {
71912
71581
  }
71913
71582
  function sheetMetalMaterials(_input, engine) {
71914
71583
  const materials = engine.getSheetMetalMaterials();
71915
- return textResult11({
71584
+ return textResult12({
71916
71585
  materials,
71917
71586
  note: 'Pass any `name` (e.g. `"al-soft"`, `"steel-mild"`) as the `material` field of `sheet_metal_create`. Aliases like `"aluminum"`, `"stainless"`, `"6061-T6"` also resolve.'
71918
71587
  });
@@ -71921,13 +71590,13 @@ function sheetMetalBendTable(input, engine) {
71921
71590
  const a = input ?? {};
71922
71591
  if (typeof a.shop_profile === "string" && a.shop_profile.length > 0) {
71923
71592
  const catalog = engine.getSheetMetalShopCatalog(a.shop_profile);
71924
- return textResult11({
71593
+ return textResult12({
71925
71594
  catalog,
71926
71595
  note: `Published catalog for ${catalog.name} (retrieved ${catalog.retrieved}). inside_radius_mm is FIXED per material/thickness \u2014 pass shop_profile: "${catalog.id}" to sheet_metal_create and omit per-bend radius to use it.`
71927
71596
  });
71928
71597
  }
71929
71598
  const table = engine.getSheetMetalBendTable();
71930
- return textResult11({
71599
+ return textResult12({
71931
71600
  table,
71932
71601
  note: "K-factors are interpolated by closest R/t for the chosen material. To override K for a specific bend, pass `manual_k` on that flange in sheet_metal_create."
71933
71602
  });
@@ -71949,7 +71618,7 @@ function sheetMetalCheck(input, engine) {
71949
71618
  const errors = result.violations.filter(
71950
71619
  (v) => v.severity === "Error"
71951
71620
  ).length;
71952
- return textResult11({
71621
+ return textResult12({
71953
71622
  shop: result.shop,
71954
71623
  violations: result.violations,
71955
71624
  shop_ready: result.violations.length === 0,
@@ -72001,7 +71670,7 @@ function sheetMetalNest(input, engine) {
72001
71670
  footprints,
72002
71671
  params
72003
71672
  );
72004
- return textResult11({
71673
+ return textResult12({
72005
71674
  parts: footprints,
72006
71675
  result,
72007
71676
  summary: `${result.placements.length} placements across ${result.sheets_used} sheet(s); ${result.utilization_pct.toFixed(1)}% utilization${result.unplaceable.length > 0 ? `; ${result.unplaceable.length} unplaceable (oversize)` : ""}.`
@@ -72014,7 +71683,7 @@ function sheetMetalSequence3(input, engine) {
72014
71683
  if (!steps) {
72015
71684
  throw new Error("document has no sheet-metal part");
72016
71685
  }
72017
- return textResult11({
71686
+ return textResult12({
72018
71687
  count: steps.length,
72019
71688
  steps,
72020
71689
  note: "Form each bend in order. compensated_angle_rad is the angle to dial in on the brake \u2014 springs back to angle_rad once released."
@@ -72037,7 +71706,7 @@ function sheetMetalSuggestFix(input, engine) {
72037
71706
  violation: v,
72038
71707
  fix: suggestFix(v)
72039
71708
  }));
72040
- return textResult11({
71709
+ return textResult12({
72041
71710
  shop: result.shop,
72042
71711
  count: suggestions.length,
72043
71712
  suggestions,
@@ -72140,13 +71809,13 @@ function sheetMetalCost(input, engine) {
72140
71809
  if (!result) {
72141
71810
  throw new Error("document has no sheet-metal part");
72142
71811
  }
72143
- return textResult11({
71812
+ return textResult12({
72144
71813
  breakdown: result.breakdown,
72145
71814
  rates: result.rates,
72146
71815
  summary: `${result.breakdown.currency} ${result.breakdown.total_each.toFixed(2)} each @ qty ${result.breakdown.quantity} (mass ${result.breakdown.mass_kg_each.toFixed(3)} kg, ${result.breakdown.cut_length_m.toFixed(2)} m cut, ${result.breakdown.bends} bend(s)).`
72147
71816
  });
72148
71817
  }
72149
- var sheetMetalCreateSchema, sheetMetalUnfoldSchema, sheetMetalMaterialsSchema, sheetMetalBendTableSchema, sheetMetalCheckSchema, sheetMetalNestSchema, sheetMetalSequenceSchema, sheetMetalSuggestFixSchema, sheetMetalCostSchema, toolDefs38;
71818
+ var sheetMetalCreateSchema, sheetMetalUnfoldSchema, sheetMetalMaterialsSchema, sheetMetalBendTableSchema, sheetMetalCheckSchema, sheetMetalNestSchema, sheetMetalSequenceSchema, sheetMetalSuggestFixSchema, sheetMetalCostSchema, toolDefs39;
72150
71819
  var init_sheet_metal2 = __esm({
72151
71820
  "src/tools/sheet-metal.ts"() {
72152
71821
  "use strict";
@@ -72407,7 +72076,7 @@ var init_sheet_metal2 = __esm({
72407
72076
  },
72408
72077
  required: ["document_id"]
72409
72078
  };
72410
- toolDefs38 = [
72079
+ toolDefs39 = [
72411
72080
  {
72412
72081
  name: "sheet_metal_create",
72413
72082
  pack: "sheet_metal",
@@ -72618,12 +72287,12 @@ function simulateStrike(input, engine) {
72618
72287
  },
72619
72288
  modes: result.modes.map((m) => ({
72620
72289
  n: m.n,
72621
- hz: round24(m.hz),
72290
+ hz: round22(m.hz),
72622
72291
  gain: round4(m.gain),
72623
72292
  q: Math.round(m.q),
72624
- t60_s: round24(m.t60_s)
72293
+ t60_s: round22(m.t60_s)
72625
72294
  })),
72626
- spectrum_peaks: result.spectrum_peaks.map((p) => ({ hz: round24(p.hz), db: round24(p.db) })),
72295
+ spectrum_peaks: result.spectrum_peaks.map((p) => ({ hz: round22(p.hz), db: round22(p.db) })),
72627
72296
  ...result.verdict ? { verdict: result.verdict } : {},
72628
72297
  limits: "1-D transverse bending only (no torsional/lateral modes); decay Q is a heuristic (material + cord-at-holes), frequencies are not"
72629
72298
  };
@@ -72652,7 +72321,7 @@ function simulateStrike(input, engine) {
72652
72321
  }
72653
72322
  return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
72654
72323
  }
72655
- var cents, MATERIAL_ALIASES, simulateStrikeSchema, round24, round4, toolDefs39;
72324
+ var cents, MATERIAL_ALIASES, simulateStrikeSchema, round22, round4, toolDefs40;
72656
72325
  var init_acoustics = __esm({
72657
72326
  "src/tools/acoustics.ts"() {
72658
72327
  "use strict";
@@ -72724,9 +72393,9 @@ var init_acoustics = __esm({
72724
72393
  }
72725
72394
  }
72726
72395
  };
72727
- round24 = (x) => Math.round(x * 100) / 100;
72396
+ round22 = (x) => Math.round(x * 100) / 100;
72728
72397
  round4 = (x) => Math.round(x * 1e4) / 1e4;
72729
- toolDefs39 = [
72398
+ toolDefs40 = [
72730
72399
  {
72731
72400
  name: "simulate_strike",
72732
72401
  pack: "sheet_metal",
@@ -72877,7 +72546,7 @@ function importStep(input, engine) {
72877
72546
  ]
72878
72547
  };
72879
72548
  }
72880
- var MAX_STEP_BYTES, importStepSchema, toolDefs40;
72549
+ var MAX_STEP_BYTES, importStepSchema, toolDefs41;
72881
72550
  var init_import = __esm({
72882
72551
  "src/tools/import.ts"() {
72883
72552
  "use strict";
@@ -72909,7 +72578,7 @@ var init_import = __esm({
72909
72578
  }
72910
72579
  }
72911
72580
  };
72912
- toolDefs40 = [
72581
+ toolDefs41 = [
72913
72582
  {
72914
72583
  name: "import_step",
72915
72584
  pack: null,
@@ -73037,7 +72706,7 @@ function importEagle(_input) {
73037
72706
  "Eagle (.brd) import is not yet supported. Export your board from Eagle as KiCad (File > Export > KiCad .kicad_pcb) and use import_kicad instead."
73038
72707
  );
73039
72708
  }
73040
- var MAX_PCB_BYTES, importKicadSchema, importEagleSchema, toolDefs41;
72709
+ var MAX_PCB_BYTES, importKicadSchema, importEagleSchema, toolDefs42;
73041
72710
  var init_import_pcb = __esm({
73042
72711
  "src/tools/import-pcb.ts"() {
73043
72712
  "use strict";
@@ -73078,7 +72747,7 @@ var init_import_pcb = __esm({
73078
72747
  }
73079
72748
  }
73080
72749
  };
73081
- toolDefs41 = [
72750
+ toolDefs42 = [
73082
72751
  {
73083
72752
  name: "import_kicad",
73084
72753
  pack: "ecad",
@@ -73128,8 +72797,8 @@ function parseDocument(input) {
73128
72797
  }
73129
72798
  throw new Error("Document must be JSON (starting with '{') or VCode (starting with '#')");
73130
72799
  }
73131
- function compressForUrl(compact) {
73132
- const compressed = gzipSync(Buffer.from(compact, "utf-8"), { level: 9 });
72800
+ function compressForUrl(compact2) {
72801
+ const compressed = gzipSync(Buffer.from(compact2, "utf-8"), { level: 9 });
73133
72802
  return base64urlEncode(compressed);
73134
72803
  }
73135
72804
  function openInBrowser(input) {
@@ -73174,7 +72843,7 @@ Compressed URL param: ${encoded.length} bytes`
73174
72843
  ]
73175
72844
  };
73176
72845
  }
73177
- var openInBrowserSchema, toolDefs42;
72846
+ var openInBrowserSchema, toolDefs43;
73178
72847
  var init_share = __esm({
73179
72848
  "src/tools/share.ts"() {
73180
72849
  "use strict";
@@ -73198,7 +72867,7 @@ var init_share = __esm({
73198
72867
  }
73199
72868
  }
73200
72869
  };
73201
- toolDefs42 = [
72870
+ toolDefs43 = [
73202
72871
  {
73203
72872
  name: "open_in_browser",
73204
72873
  pack: null,
@@ -73523,7 +73192,7 @@ function batchReset(input) {
73523
73192
  };
73524
73193
  }
73525
73194
  }
73526
- var simulations, MAX_TRAJECTORY, envRecords, batchGroups, createRobotEnvSchema, gymStepSchema, gymResetSchema, gymObserveSchema, gymCloseSchema, batchCreateEnvsSchema, batchStepSchema, batchResetSchema, toolDefs43;
73195
+ var simulations, MAX_TRAJECTORY, envRecords, batchGroups, createRobotEnvSchema, gymStepSchema, gymResetSchema, gymObserveSchema, gymCloseSchema, batchCreateEnvsSchema, batchStepSchema, batchResetSchema, toolDefs44;
73527
73196
  var init_gym = __esm({
73528
73197
  "src/tools/gym.ts"() {
73529
73198
  "use strict";
@@ -73675,7 +73344,7 @@ var init_gym = __esm({
73675
73344
  },
73676
73345
  required: ["batch_id"]
73677
73346
  };
73678
- toolDefs43 = [
73347
+ toolDefs44 = [
73679
73348
  {
73680
73349
  name: "create_robot_env",
73681
73350
  pack: "physics",
@@ -74033,7 +73702,7 @@ async function renderMolecule(input) {
74033
73702
  return fail(err7);
74034
73703
  }
74035
73704
  }
74036
- var loadStructureSchema, inspectMoleculeSchema, configSchema, minimizeEnergySchema, mdRunSchema, designMaterialSchema, homogenizeMaterialSchema, renderMoleculeSchema, CPK, DEFAULT_RADIUS, toolDefs44;
73705
+ var loadStructureSchema, inspectMoleculeSchema, configSchema, minimizeEnergySchema, mdRunSchema, designMaterialSchema, homogenizeMaterialSchema, renderMoleculeSchema, CPK, DEFAULT_RADIUS, toolDefs45;
74037
73706
  var init_atoms2 = __esm({
74038
73707
  "src/tools/atoms.ts"() {
74039
73708
  "use strict";
@@ -74168,7 +73837,7 @@ var init_atoms2 = __esm({
74168
73837
  S: 1.05,
74169
73838
  P: 1.07
74170
73839
  };
74171
- toolDefs44 = [
73840
+ toolDefs45 = [
74172
73841
  {
74173
73842
  name: "load_structure",
74174
73843
  pack: "atoms",
@@ -75376,7 +75045,7 @@ function errorResult(text) {
75376
75045
  isError: true
75377
75046
  };
75378
75047
  }
75379
- var recordSimulationSchema, SVG_SCALE2, MAX_STEPS, MIN_STEPS, DEFAULT_WIDTH_PX2, MIN_WIDTH_PX, MAX_WIDTH_PX, DEFAULT_FPS, MIN_FPS, MAX_FPS, toolDefs45;
75048
+ var recordSimulationSchema, SVG_SCALE2, MAX_STEPS, MIN_STEPS, DEFAULT_WIDTH_PX2, MIN_WIDTH_PX, MAX_WIDTH_PX, DEFAULT_FPS, MIN_FPS, MAX_FPS, toolDefs46;
75380
75049
  var init_record2 = __esm({
75381
75050
  "src/tools/record.ts"() {
75382
75051
  "use strict";
@@ -75440,7 +75109,7 @@ var init_record2 = __esm({
75440
75109
  DEFAULT_FPS = 30;
75441
75110
  MIN_FPS = 1;
75442
75111
  MAX_FPS = 60;
75443
- toolDefs45 = [
75112
+ toolDefs46 = [
75444
75113
  {
75445
75114
  name: "record_simulation",
75446
75115
  pack: "physics",
@@ -75533,7 +75202,7 @@ ${text}`
75533
75202
  ]
75534
75203
  };
75535
75204
  }
75536
- var getChangelogSchema, toolDefs46;
75205
+ var getChangelogSchema, toolDefs47;
75537
75206
  var init_changelog2 = __esm({
75538
75207
  "src/tools/changelog.ts"() {
75539
75208
  "use strict";
@@ -75573,7 +75242,7 @@ var init_changelog2 = __esm({
75573
75242
  }
75574
75243
  }
75575
75244
  };
75576
- toolDefs46 = [
75245
+ toolDefs47 = [
75577
75246
  {
75578
75247
  name: "get_changelog",
75579
75248
  pack: null,
@@ -75722,7 +75391,7 @@ function getSimVersion(input) {
75722
75391
  ]
75723
75392
  };
75724
75393
  }
75725
- var getSimReplaySchema, getSimVersionSchema, toolDefs47;
75394
+ var getSimReplaySchema, getSimVersionSchema, toolDefs48;
75726
75395
  var init_sim_replay = __esm({
75727
75396
  "src/tools/sim-replay.ts"() {
75728
75397
  "use strict";
@@ -75750,7 +75419,7 @@ var init_sim_replay = __esm({
75750
75419
  },
75751
75420
  required: ["env_id"]
75752
75421
  };
75753
- toolDefs47 = [
75422
+ toolDefs48 = [
75754
75423
  {
75755
75424
  name: "get_sim_replay",
75756
75425
  pack: null,
@@ -75869,7 +75538,7 @@ async function getOrderFeed(input, store, user) {
75869
75538
  version: version2
75870
75539
  });
75871
75540
  }
75872
- var toUsd4, getOrderFeedSchema, toolDefs48;
75541
+ var toUsd4, getOrderFeedSchema, toolDefs49;
75873
75542
  var init_order_feed = __esm({
75874
75543
  "src/tools/order-feed.ts"() {
75875
75544
  "use strict";
@@ -75888,7 +75557,7 @@ var init_order_feed = __esm({
75888
75557
  },
75889
75558
  required: ["document_id"]
75890
75559
  };
75891
- toolDefs48 = [
75560
+ toolDefs49 = [
75892
75561
  {
75893
75562
  name: "get_order_feed",
75894
75563
  pack: "fabricate",
@@ -76790,7 +76459,7 @@ ${issues.map((i) => i.problem).join("\n")}`
76790
76459
  note: "Rollout compiled to the document timeline. Preview with render_sequence, ship with export_video."
76791
76460
  });
76792
76461
  }
76793
- var MAX_FRAMES, MAX_GEO_SAMPLES, MAX_VERIFY_FRAMES, INLINE_GLB_MAX_BASE64, DEFAULT_WIDTH_PX3, MIN_WIDTH_PX2, MAX_WIDTH_PX2, SVG_SCALE3, KEYFRAMES_DESCRIPTION, animateSchema, renderSequenceSchema, CAMERA_NODE, exportVideoSchema, ffmpegAvailable, timelineFromSimulationSchema, toolDefs49;
76462
+ var MAX_FRAMES, MAX_GEO_SAMPLES, MAX_VERIFY_FRAMES, INLINE_GLB_MAX_BASE64, DEFAULT_WIDTH_PX3, MIN_WIDTH_PX2, MAX_WIDTH_PX2, SVG_SCALE3, KEYFRAMES_DESCRIPTION, animateSchema, renderSequenceSchema, CAMERA_NODE, exportVideoSchema, ffmpegAvailable, timelineFromSimulationSchema, toolDefs50;
76794
76463
  var init_animate = __esm({
76795
76464
  "src/tools/animate.ts"() {
76796
76465
  "use strict";
@@ -76942,7 +76611,7 @@ var init_animate = __esm({
76942
76611
  },
76943
76612
  required: ["env_id", "document_id"]
76944
76613
  };
76945
- toolDefs49 = [
76614
+ toolDefs50 = [
76946
76615
  {
76947
76616
  name: "animate",
76948
76617
  pack: null,
@@ -77787,6 +77456,7 @@ var init_server3 = __esm({
77787
77456
  init_antenna();
77788
77457
  init_photonics();
77789
77458
  init_neutronics();
77459
+ init_qcd();
77790
77460
  init_physics2();
77791
77461
  init_loon_macros();
77792
77462
  init_dfm2();
@@ -77805,8 +77475,8 @@ var init_server3 = __esm({
77805
77475
  init_order_feed();
77806
77476
  init_animate();
77807
77477
  PKG_VERSION = (() => {
77808
- if ("0.9.4-main.15") {
77809
- return "0.9.4-main.15";
77478
+ if ("0.9.4-main.17") {
77479
+ return "0.9.4-main.17";
77810
77480
  }
77811
77481
  try {
77812
77482
  const req = createRequire2(import.meta.url);
@@ -77815,8 +77485,8 @@ var init_server3 = __esm({
77815
77485
  return "0.0.0";
77816
77486
  }
77817
77487
  })();
77818
- BUILD_SHA = "ba0bbea017e3e720ec8796167b6824f3cdf235cf";
77819
- BUILD_TIME = "2026-07-24T17:18:07.764Z";
77488
+ BUILD_SHA = "cfb504c93d13b7e0339f79b4b8863ffeb1ca83b1";
77489
+ BUILD_TIME = "2026-07-24T20:18:23.601Z";
77820
77490
  SHORT_SHA = BUILD_SHA === "unknown" ? "unknown" : BUILD_SHA.slice(0, 7);
77821
77491
  VERSION_WITH_BUILD = SHORT_SHA === "unknown" ? PKG_VERSION : `${PKG_VERSION}+${SHORT_SHA}`;
77822
77492
  INSTANCE_ID = randomUUID6().slice(0, 8);
@@ -77876,8 +77546,8 @@ var init_server3 = __esm({
77876
77546
  ...toolDefs34,
77877
77547
  ...toolDefs35,
77878
77548
  ...toolDefs36,
77879
- ...toolDefs2,
77880
77549
  ...toolDefs37,
77550
+ ...toolDefs2,
77881
77551
  ...toolDefs38,
77882
77552
  ...toolDefs39,
77883
77553
  ...toolDefs40,
@@ -77887,11 +77557,12 @@ var init_server3 = __esm({
77887
77557
  ...toolDefs44,
77888
77558
  ...toolDefs45,
77889
77559
  ...toolDefs46,
77560
+ ...toolDefs47,
77890
77561
  ...toolDefs20,
77891
77562
  ...toolDefs19,
77892
- ...toolDefs47,
77893
77563
  ...toolDefs48,
77894
- ...toolDefs49
77564
+ ...toolDefs49,
77565
+ ...toolDefs50
77895
77566
  ];
77896
77567
  LIST_TOOL_ORDER = [
77897
77568
  // ── Session lifecycle ──────────────────────────────────────
@@ -77962,6 +77633,7 @@ var init_server3 = __esm({
77962
77633
  "solve_thermal",
77963
77634
  "analyze_structure",
77964
77635
  "simulate_neutron_shield",
77636
+ "simulate_lattice_gauge",
77965
77637
  "analyze_tolerance_stackup",
77966
77638
  "simulate_circuit",
77967
77639
  "tune_circuit",