@vcad/mcp 0.9.4-main.40 → 0.9.4-main.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/index.mjs +355 -76
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -6,4 +6,4 @@ vcad's MCP server as a self-contained bundle (server + BRep kernel WASM).
|
|
|
6
6
|
{ "mcpServers": { "vcad": { "command": "npx", "args": ["-y", "@vcad/mcp"] } } }
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
Built from
|
|
9
|
+
Built from b00f071e4b839c6448769107ed84182597d763ed at 2026-07-26T14:41:39.682Z. Source: https://github.com/ecto/vcad
|
package/index.mjs
CHANGED
|
@@ -8996,6 +8996,38 @@ function costSheetMetalChain(chain, kernel2, rates, quantity2 = 1) {
|
|
|
8996
8996
|
}
|
|
8997
8997
|
return { breakdown: parsed.breakdown, rates: parsed.rates };
|
|
8998
8998
|
}
|
|
8999
|
+
function flattenSolidToSheetMetal(mesh, kernel2, options = {}) {
|
|
9000
|
+
if (!kernel2.flattenSolidToSheetMetal) {
|
|
9001
|
+
throw new Error("kernel.flattenSolidToSheetMetal not available \u2014 rebuild @vcad/kernel-wasm");
|
|
9002
|
+
}
|
|
9003
|
+
const request = {
|
|
9004
|
+
positions: Array.from(mesh.positions),
|
|
9005
|
+
indices: Array.from(mesh.indices),
|
|
9006
|
+
material: options.material,
|
|
9007
|
+
manualK: options.manualK,
|
|
9008
|
+
shopProfile: options.shopProfile,
|
|
9009
|
+
volumeTolFrac: options.volumeTolFrac,
|
|
9010
|
+
planeAngleTolDeg: options.planeAngleTolDeg,
|
|
9011
|
+
planeOffsetTol: options.planeOffsetTol
|
|
9012
|
+
};
|
|
9013
|
+
const parsed = JSON.parse(kernel2.flattenSolidToSheetMetal(JSON.stringify(request)));
|
|
9014
|
+
if (parsed.error) {
|
|
9015
|
+
throw new Error(parsed.error);
|
|
9016
|
+
}
|
|
9017
|
+
return {
|
|
9018
|
+
flatPattern: parsed.flat_pattern,
|
|
9019
|
+
model: parsed.model,
|
|
9020
|
+
dxf: parsed.dxf,
|
|
9021
|
+
violations: parsed.violations,
|
|
9022
|
+
thickness: parsed.thickness,
|
|
9023
|
+
panels: parsed.panels,
|
|
9024
|
+
bends: parsed.bends,
|
|
9025
|
+
solidVolumeMm3: parsed.solid_volume_mm3,
|
|
9026
|
+
recoveredVolumeMm3: parsed.recovered_volume_mm3,
|
|
9027
|
+
volumeErrorFrac: parsed.volume_error_frac,
|
|
9028
|
+
warnings: parsed.warnings ?? []
|
|
9029
|
+
};
|
|
9030
|
+
}
|
|
8999
9031
|
var DEFAULT_SHOP_PROFILE, DEFAULT_COST_RATES, DEFAULT_NESTING_PARAMS;
|
|
9000
9032
|
var init_sheet_metal = __esm({
|
|
9001
9033
|
"../engine/dist/sheet-metal.js"() {
|
|
@@ -11564,6 +11596,7 @@ var init_dist = __esm({
|
|
|
11564
11596
|
getSheetMetalBendTable: wasmModule7.getSheetMetalBendTable,
|
|
11565
11597
|
getSheetMetalShopCatalog: wasmModule7.getSheetMetalShopCatalog,
|
|
11566
11598
|
sheetMetalFoldedStep: wasmModule7.sheetMetalFoldedStep,
|
|
11599
|
+
flattenSolidToSheetMetal: wasmModule7.flattenSolidToSheetMetal,
|
|
11567
11600
|
mesh_clearance: wasmModule7.mesh_clearance,
|
|
11568
11601
|
topologyOptimizeBox: wasmModule7.topologyOptimizeBox,
|
|
11569
11602
|
topologyOptimizeMesh: wasmModule7.topologyOptimizeMesh,
|
|
@@ -12140,6 +12173,12 @@ var init_dist = __esm({
|
|
|
12140
12173
|
nestSheetMetalParts(parts, params) {
|
|
12141
12174
|
return nestSheetMetalParts2(parts, this.kernel, params);
|
|
12142
12175
|
}
|
|
12176
|
+
/** Recover a flat pattern (panels, bends, DXF) from a solid part's mesh —
|
|
12177
|
+
* the mechanical counterpart of `board_from_solid`. Throws when the solid
|
|
12178
|
+
* is not constant-thickness sheet. */
|
|
12179
|
+
flattenSolidToSheetMetal(mesh, options) {
|
|
12180
|
+
return flattenSolidToSheetMetal(mesh, this.kernel, options);
|
|
12181
|
+
}
|
|
12143
12182
|
/** Compute a feasible bend sequence (outermost-first) for the
|
|
12144
12183
|
* sheet-metal part in `doc`. Returns `null` if there is none. */
|
|
12145
12184
|
sheetMetalSequence(doc) {
|
|
@@ -41105,6 +41144,24 @@ var init_CHANGELOG = __esm({
|
|
|
41105
41144
|
"export_cad"
|
|
41106
41145
|
]
|
|
41107
41146
|
},
|
|
41147
|
+
{
|
|
41148
|
+
id: "2026-07-26-flat-pattern-from-solid",
|
|
41149
|
+
version: "0.9.4",
|
|
41150
|
+
date: "2026-07-26",
|
|
41151
|
+
category: "feat",
|
|
41152
|
+
title: "Flat patterns from parts you already modelled",
|
|
41153
|
+
summary: "flat_pattern_from_solid turns any constant-thickness solid into a fab-ready DXF plus a bend table \u2014 no sheet-metal re-authoring \u2014 batching a whole assembly into unique patterns x quantity.",
|
|
41154
|
+
features: [
|
|
41155
|
+
"sheet-metal",
|
|
41156
|
+
"manufacturing",
|
|
41157
|
+
"mcp"
|
|
41158
|
+
],
|
|
41159
|
+
mcpTools: [
|
|
41160
|
+
"flat_pattern_from_solid",
|
|
41161
|
+
"sheet_metal_nest",
|
|
41162
|
+
"sheet_metal_cost"
|
|
41163
|
+
]
|
|
41164
|
+
},
|
|
41108
41165
|
{
|
|
41109
41166
|
id: "2026-07-26-fastener-forms",
|
|
41110
41167
|
version: "0.9.4",
|
|
@@ -47931,13 +47988,13 @@ function computeIntegrity(doc, engine) {
|
|
|
47931
47988
|
cx += v * (p1[0] + p2[0] + p3[0]) / 4;
|
|
47932
47989
|
cy += v * (p1[1] + p2[1] + p3[1]) / 4;
|
|
47933
47990
|
cz += v * (p1[2] + p2[2] + p3[2]) / 4;
|
|
47934
|
-
for (const
|
|
47935
|
-
bbox.min.x = Math.min(bbox.min.x,
|
|
47936
|
-
bbox.min.y = Math.min(bbox.min.y,
|
|
47937
|
-
bbox.min.z = Math.min(bbox.min.z,
|
|
47938
|
-
bbox.max.x = Math.max(bbox.max.x,
|
|
47939
|
-
bbox.max.y = Math.max(bbox.max.y,
|
|
47940
|
-
bbox.max.z = Math.max(bbox.max.z,
|
|
47991
|
+
for (const q2 of [p1, p2, p3]) {
|
|
47992
|
+
bbox.min.x = Math.min(bbox.min.x, q2[0]);
|
|
47993
|
+
bbox.min.y = Math.min(bbox.min.y, q2[1]);
|
|
47994
|
+
bbox.min.z = Math.min(bbox.min.z, q2[2]);
|
|
47995
|
+
bbox.max.x = Math.max(bbox.max.x, q2[0]);
|
|
47996
|
+
bbox.max.y = Math.max(bbox.max.y, q2[1]);
|
|
47997
|
+
bbox.max.z = Math.max(bbox.max.z, q2[2]);
|
|
47941
47998
|
}
|
|
47942
47999
|
}
|
|
47943
48000
|
const label = labels[partIndex] ?? `part ${partIndex + 1}`;
|
|
@@ -50352,8 +50409,8 @@ function buildPartLabels(doc) {
|
|
|
50352
50409
|
});
|
|
50353
50410
|
}
|
|
50354
50411
|
function eulerXyzDegToQuat2(rotation) {
|
|
50355
|
-
const
|
|
50356
|
-
return [
|
|
50412
|
+
const q2 = exportKernel().eulerXyzDegToQuat(rotation.x, rotation.y, rotation.z);
|
|
50413
|
+
return [q2[0], q2[1], q2[2], q2[3]];
|
|
50357
50414
|
}
|
|
50358
50415
|
function buildGlb(inputMeshes, name, animation) {
|
|
50359
50416
|
let f32Len = 0;
|
|
@@ -51182,9 +51239,9 @@ var init_store = __esm({
|
|
|
51182
51239
|
async listOrders(userId, filter) {
|
|
51183
51240
|
try {
|
|
51184
51241
|
const limit = typeof filter.limit === "number" ? filter.limit : 50;
|
|
51185
|
-
let
|
|
51186
|
-
if (filter.status)
|
|
51187
|
-
const res = await fabricateFetch(this.url("orders",
|
|
51242
|
+
let q2 = `?user_id=eq.${encodeURIComponent(userId)}&order=created_at.desc&limit=${limit}`;
|
|
51243
|
+
if (filter.status) q2 += `&state=eq.${encodeURIComponent(filter.status)}`;
|
|
51244
|
+
const res = await fabricateFetch(this.url("orders", q2), {
|
|
51188
51245
|
method: "GET",
|
|
51189
51246
|
headers: this.headers()
|
|
51190
51247
|
});
|
|
@@ -51388,8 +51445,8 @@ var init_store = __esm({
|
|
|
51388
51445
|
* can retry before deciding what to say. Never throws. */
|
|
51389
51446
|
async postRow(table, row, onConflict) {
|
|
51390
51447
|
try {
|
|
51391
|
-
const
|
|
51392
|
-
const res = await fabricateFetch(this.url(table,
|
|
51448
|
+
const q2 = onConflict ? `?on_conflict=${onConflict}` : "";
|
|
51449
|
+
const res = await fabricateFetch(this.url(table, q2), {
|
|
51393
51450
|
method: "POST",
|
|
51394
51451
|
headers: this.headers({
|
|
51395
51452
|
Prefer: onConflict ? "resolution=merge-duplicates,return=minimal" : "return=minimal"
|
|
@@ -53435,6 +53492,7 @@ var init_tool_metadata = __esm({
|
|
|
53435
53492
|
// ── Sheet metal ────────────────────────────────────────────────────────
|
|
53436
53493
|
sheet_metal_create: { title: "Create Sheet Metal", annotations: RW },
|
|
53437
53494
|
sheet_metal_unfold: { title: "Unfold Sheet Metal", annotations: RO },
|
|
53495
|
+
flat_pattern_from_solid: { title: "Flat Pattern From Solid", annotations: RO },
|
|
53438
53496
|
sheet_metal_check: { title: "Check Sheet Metal", annotations: RO },
|
|
53439
53497
|
sheet_metal_materials: { title: "Sheet Metal Materials", annotations: RO },
|
|
53440
53498
|
sheet_metal_bend_table: { title: "Sheet Metal Bend Table", annotations: RO },
|
|
@@ -54750,34 +54808,34 @@ var init_broker = __esm({
|
|
|
54750
54808
|
);
|
|
54751
54809
|
const built = [];
|
|
54752
54810
|
for (const adapter of eligible) {
|
|
54753
|
-
let
|
|
54811
|
+
let q2;
|
|
54754
54812
|
try {
|
|
54755
|
-
|
|
54813
|
+
q2 = await adapter.quote(req);
|
|
54756
54814
|
} catch {
|
|
54757
|
-
|
|
54815
|
+
q2 = null;
|
|
54758
54816
|
}
|
|
54759
|
-
if (!
|
|
54817
|
+
if (!q2) continue;
|
|
54760
54818
|
const landed2 = estimateLandedCost({
|
|
54761
54819
|
region: adapter.region,
|
|
54762
54820
|
supportsDdp: adapter.supportsDdp
|
|
54763
54821
|
});
|
|
54764
|
-
const markedUp = applyMargin(
|
|
54822
|
+
const markedUp = applyMargin(q2.fab_cost_minor);
|
|
54765
54823
|
const totalMinor = markedUp + landed2.shipping_minor + landed2.duty_minor;
|
|
54766
|
-
const orderable = CONTRACTED_FABS.has(adapter.key) &&
|
|
54824
|
+
const orderable = CONTRACTED_FABS.has(adapter.key) && q2.pricing_basis === "binding";
|
|
54767
54825
|
built.push({
|
|
54768
|
-
fabCost:
|
|
54826
|
+
fabCost: q2.fab_cost_minor,
|
|
54769
54827
|
option: {
|
|
54770
54828
|
fab: adapter.key,
|
|
54771
54829
|
fab_label: adapter.label,
|
|
54772
54830
|
region: adapter.region,
|
|
54773
54831
|
unit_price_minor: Math.round(totalMinor / req.quantity),
|
|
54774
54832
|
total_minor: totalMinor,
|
|
54775
|
-
lead_time_days:
|
|
54776
|
-
in_spec:
|
|
54777
|
-
pricing_basis:
|
|
54833
|
+
lead_time_days: q2.lead_time_days,
|
|
54834
|
+
in_spec: q2.in_spec,
|
|
54835
|
+
pricing_basis: q2.pricing_basis,
|
|
54778
54836
|
supports_ddp: adapter.supportsDdp,
|
|
54779
54837
|
orderable,
|
|
54780
|
-
notes:
|
|
54838
|
+
notes: q2.notes
|
|
54781
54839
|
}
|
|
54782
54840
|
});
|
|
54783
54841
|
}
|
|
@@ -55967,7 +56025,7 @@ function measureGroups(engine, candidates, groupA, groupB) {
|
|
|
55967
56025
|
error: `No part with id or name ${missing.map((m) => `"${m}"`).join(", ")}. Available: ${available || "none"}`
|
|
55968
56026
|
};
|
|
55969
56027
|
}
|
|
55970
|
-
const overlap = a.parts.filter((p) => b.parts.some((
|
|
56028
|
+
const overlap = a.parts.filter((p) => b.parts.some((q2) => q2.id === p.id));
|
|
55971
56029
|
if (overlap.length > 0) {
|
|
55972
56030
|
return {
|
|
55973
56031
|
error: `Parts cannot appear in both groups: ${overlap.map((p) => p.id).join(", ")}`
|
|
@@ -60127,10 +60185,10 @@ function detectStaleNets(pcb) {
|
|
|
60127
60185
|
const onSegBody = (p, t2) => segDist(p, t2) <= t2.width / 2 + 0.02;
|
|
60128
60186
|
const anchored = (p, selfIdx, halfW) => {
|
|
60129
60187
|
if (pads.some(
|
|
60130
|
-
(
|
|
60188
|
+
(q2) => Math.hypot(p.x - q2.pos.x, p.y - q2.pos.y) <= q2.halfExtent + halfW + 0.02
|
|
60131
60189
|
))
|
|
60132
60190
|
return true;
|
|
60133
|
-
if (vias.some((
|
|
60191
|
+
if (vias.some((q2) => Math.hypot(p.x - q2.x, p.y - q2.y) <= halfW + 0.42)) return true;
|
|
60134
60192
|
for (let j = 0; j < traces.length; j++) {
|
|
60135
60193
|
if (j === selfIdx) continue;
|
|
60136
60194
|
if (segDist(p, traces[j]) <= traces[j].width / 2 + halfW + 0.02) return true;
|
|
@@ -61930,7 +61988,7 @@ function seedScan(residual, lo, hi, perDim = 12) {
|
|
|
61930
61988
|
const idx = new Array(n).fill(0);
|
|
61931
61989
|
const total = Math.pow(perDim, n);
|
|
61932
61990
|
for (let c = 0; c < total; c++) {
|
|
61933
|
-
const x = idx.map((
|
|
61991
|
+
const x = idx.map((q2, d) => lo[d] + (hi[d] - lo[d]) * q2 / (perDim - 1));
|
|
61934
61992
|
const cost = sumSq(residual(x));
|
|
61935
61993
|
if (cost < best.cost) {
|
|
61936
61994
|
best.cost = cost;
|
|
@@ -62471,7 +62529,7 @@ function calcRf(args) {
|
|
|
62471
62529
|
return fail2("topology must be 'series_rlc' or 'parallel_rlc'");
|
|
62472
62530
|
}
|
|
62473
62531
|
const f0 = 1 / (2 * Math.PI * Math.sqrt(l * c));
|
|
62474
|
-
const
|
|
62532
|
+
const q2 = topology === "series_rlc" ? r > 0 ? 1 / r * Math.sqrt(l / c) : Infinity : r * Math.sqrt(c / l);
|
|
62475
62533
|
const fStart = args.f_start_hz ?? f0 * 0.1;
|
|
62476
62534
|
const fStop = args.f_stop_hz ?? f0 * 10;
|
|
62477
62535
|
const points = Math.min(256, Math.max(3, Math.round(args.points ?? 21)));
|
|
@@ -62517,7 +62575,7 @@ function calcRf(args) {
|
|
|
62517
62575
|
success: true,
|
|
62518
62576
|
topology,
|
|
62519
62577
|
resonance_hz: Math.round(f0),
|
|
62520
|
-
q_factor: Number.isFinite(
|
|
62578
|
+
q_factor: Number.isFinite(q2) ? r3(q2) : 999999,
|
|
62521
62579
|
z_at_resonance_ohm: r3(mag(z0re, z0im)),
|
|
62522
62580
|
best_match: {
|
|
62523
62581
|
f_hz: best.f_hz,
|
|
@@ -62532,8 +62590,8 @@ function calcRf(args) {
|
|
|
62532
62590
|
l_henry: l,
|
|
62533
62591
|
c_farad: c
|
|
62534
62592
|
}),
|
|
62535
|
-
...Number.isFinite(
|
|
62536
|
-
emClaim("q_factor", r3(
|
|
62593
|
+
...Number.isFinite(q2) ? [
|
|
62594
|
+
emClaim("q_factor", r3(q2), "dimensionless", "rlc-analytic", {
|
|
62537
62595
|
topology,
|
|
62538
62596
|
r_ohm: r,
|
|
62539
62597
|
l_henry: l,
|
|
@@ -63112,17 +63170,17 @@ async function setBoardOutline(args) {
|
|
|
63112
63170
|
};
|
|
63113
63171
|
}
|
|
63114
63172
|
function scoreFootprintFamily(query, fam) {
|
|
63115
|
-
const
|
|
63116
|
-
if (!
|
|
63173
|
+
const q2 = query.toLowerCase().trim();
|
|
63174
|
+
if (!q2) return 0;
|
|
63117
63175
|
const cands = [fam.family, fam.label, ...fam.aliases].map((s) => s.toLowerCase());
|
|
63118
63176
|
let best = 0;
|
|
63119
63177
|
for (const c of cands) {
|
|
63120
|
-
if (c ===
|
|
63178
|
+
if (c === q2) {
|
|
63121
63179
|
best = Math.max(best, 1);
|
|
63122
|
-
} else if (c.includes(
|
|
63180
|
+
} else if (c.includes(q2) || q2.includes(c)) {
|
|
63123
63181
|
best = Math.max(best, 0.75);
|
|
63124
63182
|
} else {
|
|
63125
|
-
const sim = 1 - editDistance(
|
|
63183
|
+
const sim = 1 - editDistance(q2, c) / Math.max(q2.length, c.length);
|
|
63126
63184
|
best = Math.max(best, sim * 0.6);
|
|
63127
63185
|
}
|
|
63128
63186
|
}
|
|
@@ -64755,16 +64813,16 @@ function windSegClosestPt(a, b, c, d) {
|
|
|
64755
64813
|
};
|
|
64756
64814
|
let best = [a, clamp(a, c, d)];
|
|
64757
64815
|
let bestD = Infinity;
|
|
64758
|
-
for (const [p,
|
|
64816
|
+
for (const [p, q2] of [
|
|
64759
64817
|
[a, clamp(a, c, d)],
|
|
64760
64818
|
[b, clamp(b, c, d)],
|
|
64761
64819
|
[c, clamp(c, a, b)],
|
|
64762
64820
|
[d, clamp(d, a, b)]
|
|
64763
64821
|
]) {
|
|
64764
|
-
const dd = Math.hypot(p.x -
|
|
64822
|
+
const dd = Math.hypot(p.x - q2.x, p.y - q2.y);
|
|
64765
64823
|
if (dd < bestD) {
|
|
64766
64824
|
bestD = dd;
|
|
64767
|
-
best = [p,
|
|
64825
|
+
best = [p, q2];
|
|
64768
64826
|
}
|
|
64769
64827
|
}
|
|
64770
64828
|
return { x: (best[0].x + best[1].x) / 2, y: (best[0].y + best[1].y) / 2 };
|
|
@@ -65382,7 +65440,7 @@ function calcMotorInduction(args, polePairs, turnsPerPhase, windingFactor, inner
|
|
|
65382
65440
|
outer_radius_mm: outerR,
|
|
65383
65441
|
end_effect_factor: endEffect
|
|
65384
65442
|
};
|
|
65385
|
-
const claim2 = (
|
|
65443
|
+
const claim2 = (q2, v, unit) => emClaim(q2, sig6(v), unit, "thin-sheet-induction", inductionInputs);
|
|
65386
65444
|
return {
|
|
65387
65445
|
content: [
|
|
65388
65446
|
{
|
|
@@ -65810,11 +65868,11 @@ function polygonCentroid(poly) {
|
|
|
65810
65868
|
let cy = 0;
|
|
65811
65869
|
for (let i = 0; i < poly.length; i++) {
|
|
65812
65870
|
const p = poly[i];
|
|
65813
|
-
const
|
|
65814
|
-
const cross = p.x *
|
|
65871
|
+
const q2 = poly[(i + 1) % poly.length];
|
|
65872
|
+
const cross = p.x * q2.y - q2.x * p.y;
|
|
65815
65873
|
a += cross;
|
|
65816
|
-
cx += (p.x +
|
|
65817
|
-
cy += (p.y +
|
|
65874
|
+
cx += (p.x + q2.x) * cross;
|
|
65875
|
+
cy += (p.y + q2.y) * cross;
|
|
65818
65876
|
}
|
|
65819
65877
|
if (Math.abs(a) < 1e-12) {
|
|
65820
65878
|
const n = poly.length || 1;
|
|
@@ -74210,8 +74268,8 @@ function normaliseEngravings(raw) {
|
|
|
74210
74268
|
if (Array.isArray(p) && p.length >= 2)
|
|
74211
74269
|
return { x: Number(p[0]), y: Number(p[1]) };
|
|
74212
74270
|
if (p && typeof p === "object" && "x" in p && "y" in p) {
|
|
74213
|
-
const
|
|
74214
|
-
return { x: Number(
|
|
74271
|
+
const q2 = p;
|
|
74272
|
+
return { x: Number(q2.x), y: Number(q2.y) };
|
|
74215
74273
|
}
|
|
74216
74274
|
return null;
|
|
74217
74275
|
}).filter((p) => p !== null) : [];
|
|
@@ -74876,6 +74934,222 @@ var init_sheet_metal2 = __esm({
|
|
|
74876
74934
|
}
|
|
74877
74935
|
});
|
|
74878
74936
|
|
|
74937
|
+
// src/tools/flat-pattern.ts
|
|
74938
|
+
function q(v, dp = 3) {
|
|
74939
|
+
const f = 10 ** dp;
|
|
74940
|
+
const r = Math.round(v * f) / f;
|
|
74941
|
+
return r === 0 ? 0 : r;
|
|
74942
|
+
}
|
|
74943
|
+
function ringSignature(ring) {
|
|
74944
|
+
const n = ring.length;
|
|
74945
|
+
if (n < 3) return "";
|
|
74946
|
+
const terms = [];
|
|
74947
|
+
for (let i = 0; i < n; i++) {
|
|
74948
|
+
const a = ring[(i + n - 1) % n];
|
|
74949
|
+
const b = ring[i];
|
|
74950
|
+
const c = ring[(i + 1) % n];
|
|
74951
|
+
const e1 = [b[0] - a[0], b[1] - a[1]];
|
|
74952
|
+
const e2 = [c[0] - b[0], c[1] - b[1]];
|
|
74953
|
+
const len = Math.hypot(e2[0], e2[1]);
|
|
74954
|
+
const turn = Math.atan2(
|
|
74955
|
+
e1[0] * e2[1] - e1[1] * e2[0],
|
|
74956
|
+
e1[0] * e2[0] + e1[1] * e2[1]
|
|
74957
|
+
);
|
|
74958
|
+
terms.push(`${q(len, 2)}:${q(turn, 3)}`);
|
|
74959
|
+
}
|
|
74960
|
+
let best = null;
|
|
74961
|
+
for (let r = 0; r < n; r++) {
|
|
74962
|
+
const cand = terms.slice(r).concat(terms.slice(0, r)).join(",");
|
|
74963
|
+
if (best === null || cand < best) best = cand;
|
|
74964
|
+
}
|
|
74965
|
+
return best ?? "";
|
|
74966
|
+
}
|
|
74967
|
+
function patternSignature(r) {
|
|
74968
|
+
const sil = r.flatPattern.silhouette_2d ?? [];
|
|
74969
|
+
const outer = ringSignature(sil[0] ?? []);
|
|
74970
|
+
const holes = sil.slice(1).map((h) => ringSignature(h)).sort().join("|");
|
|
74971
|
+
const bends = r.bends.map((b) => `${q(b.angle_deg, 2)}/${q(b.radius, 3)}/${q(b.length, 2)}/${b.direction}`).sort().join("|");
|
|
74972
|
+
return `t${q(r.thickness)}|${outer}|H:${holes}|B:${bends}`;
|
|
74973
|
+
}
|
|
74974
|
+
function solidParts(doc, engine) {
|
|
74975
|
+
const scene = engine.evaluate(doc);
|
|
74976
|
+
const visibleRoots = doc.roots.filter((e) => e.visible !== false);
|
|
74977
|
+
const out = [];
|
|
74978
|
+
for (let i = 0; i < scene.parts.length && i < visibleRoots.length; i++) {
|
|
74979
|
+
const rootId = visibleRoots[i].root;
|
|
74980
|
+
const node = doc.nodes[String(rootId)];
|
|
74981
|
+
const mesh = scene.parts[i].mesh;
|
|
74982
|
+
if (!mesh || mesh.positions.length === 0) continue;
|
|
74983
|
+
out.push({ rootId, name: node?.name ?? void 0, mesh });
|
|
74984
|
+
}
|
|
74985
|
+
return out;
|
|
74986
|
+
}
|
|
74987
|
+
function flatPatternFromSolid(input, engine) {
|
|
74988
|
+
const a = input ?? {};
|
|
74989
|
+
const doc = getSession(String(a.document_id ?? ""));
|
|
74990
|
+
const includeDxf = a.include_dxf !== false;
|
|
74991
|
+
const options = {
|
|
74992
|
+
material: typeof a.material === "string" ? a.material : void 0,
|
|
74993
|
+
manualK: typeof a.manual_k === "number" ? a.manual_k : void 0,
|
|
74994
|
+
shopProfile: typeof a.shop_profile === "string" ? a.shop_profile : void 0,
|
|
74995
|
+
volumeTolFrac: typeof a.volume_tolerance === "number" ? a.volume_tolerance : void 0
|
|
74996
|
+
};
|
|
74997
|
+
const all = solidParts(doc, engine);
|
|
74998
|
+
if (all.length === 0) {
|
|
74999
|
+
throw new Error("Document has no solid parts to flatten");
|
|
75000
|
+
}
|
|
75001
|
+
const partId = a.part_id !== void 0 ? String(a.part_id) : void 0;
|
|
75002
|
+
const targets = partId ? all.filter((p) => String(p.rootId) === partId) : all;
|
|
75003
|
+
if (targets.length === 0) {
|
|
75004
|
+
throw new Error(
|
|
75005
|
+
`No part with id "${partId}". Available: ${all.map((c) => `${c.rootId}${c.name ? ` (${c.name})` : ""}`).join(", ")}`
|
|
75006
|
+
);
|
|
75007
|
+
}
|
|
75008
|
+
const groups = /* @__PURE__ */ new Map();
|
|
75009
|
+
const failed = [];
|
|
75010
|
+
for (const part of targets) {
|
|
75011
|
+
let r;
|
|
75012
|
+
try {
|
|
75013
|
+
r = engine.flattenSolidToSheetMetal(part.mesh, options);
|
|
75014
|
+
} catch (e) {
|
|
75015
|
+
failed.push({
|
|
75016
|
+
root_id: part.rootId,
|
|
75017
|
+
...part.name ? { name: part.name } : {},
|
|
75018
|
+
reason: e instanceof Error ? e.message : String(e)
|
|
75019
|
+
});
|
|
75020
|
+
continue;
|
|
75021
|
+
}
|
|
75022
|
+
const sig = patternSignature(r);
|
|
75023
|
+
const existing = groups.get(sig);
|
|
75024
|
+
if (existing) {
|
|
75025
|
+
existing.quantity += 1;
|
|
75026
|
+
existing.parts.push({
|
|
75027
|
+
root_id: part.rootId,
|
|
75028
|
+
...part.name ? { name: part.name } : {}
|
|
75029
|
+
});
|
|
75030
|
+
continue;
|
|
75031
|
+
}
|
|
75032
|
+
const [minX, minY, maxX, maxY] = r.flatPattern.bbox;
|
|
75033
|
+
const creases = r.flatPattern.creases ?? [];
|
|
75034
|
+
groups.set(sig, {
|
|
75035
|
+
pattern_id: `pattern-${groups.size + 1}`,
|
|
75036
|
+
quantity: 1,
|
|
75037
|
+
parts: [{ root_id: part.rootId, ...part.name ? { name: part.name } : {} }],
|
|
75038
|
+
thickness_mm: r.thickness,
|
|
75039
|
+
flat: {
|
|
75040
|
+
bbox: r.flatPattern.bbox,
|
|
75041
|
+
area_mm2: r.flatPattern.area_mm2,
|
|
75042
|
+
width_mm: Math.abs(maxX - minX),
|
|
75043
|
+
height_mm: Math.abs(maxY - minY)
|
|
75044
|
+
},
|
|
75045
|
+
panels: r.panels,
|
|
75046
|
+
bend_table: r.bends.map((b, i) => ({
|
|
75047
|
+
bend: b.bend,
|
|
75048
|
+
angle_deg: b.angle_deg,
|
|
75049
|
+
radius_mm: b.radius,
|
|
75050
|
+
length_mm: b.length,
|
|
75051
|
+
direction: b.direction,
|
|
75052
|
+
k_factor: b.k_factor,
|
|
75053
|
+
allowance_mm: b.angle_deg * Math.PI / 180 * (b.radius + b.k_factor * r.thickness),
|
|
75054
|
+
line: creases[i] ? creases[i].line : []
|
|
75055
|
+
})),
|
|
75056
|
+
verification: {
|
|
75057
|
+
solid_volume_mm3: r.solidVolumeMm3,
|
|
75058
|
+
recovered_volume_mm3: r.recoveredVolumeMm3,
|
|
75059
|
+
error_frac: r.volumeErrorFrac,
|
|
75060
|
+
status: "verified"
|
|
75061
|
+
},
|
|
75062
|
+
violations: r.violations,
|
|
75063
|
+
warnings: r.warnings,
|
|
75064
|
+
...includeDxf ? { dxf: r.dxf } : {}
|
|
75065
|
+
});
|
|
75066
|
+
}
|
|
75067
|
+
const patterns = [...groups.values()];
|
|
75068
|
+
if (patterns.length === 0) {
|
|
75069
|
+
throw new Error(
|
|
75070
|
+
`No part in this document is constant-thickness sheet. ${failed.map((f) => `${f.name ?? f.root_id}: ${f.reason}`).join("; ")}`
|
|
75071
|
+
);
|
|
75072
|
+
}
|
|
75073
|
+
const totalParts = patterns.reduce((s, p) => s + p.quantity, 0);
|
|
75074
|
+
const bends = patterns.reduce((s, p) => s + p.bend_table.length * p.quantity, 0);
|
|
75075
|
+
return {
|
|
75076
|
+
content: [
|
|
75077
|
+
{
|
|
75078
|
+
type: "text",
|
|
75079
|
+
text: JSON.stringify(
|
|
75080
|
+
{
|
|
75081
|
+
success: true,
|
|
75082
|
+
patterns,
|
|
75083
|
+
...failed.length > 0 ? { not_sheet_metal: failed } : {},
|
|
75084
|
+
nest_input: patterns.map((p) => ({
|
|
75085
|
+
name: p.pattern_id,
|
|
75086
|
+
width_mm: p.flat.width_mm,
|
|
75087
|
+
height_mm: p.flat.height_mm,
|
|
75088
|
+
quantity: p.quantity
|
|
75089
|
+
})),
|
|
75090
|
+
summary: `${patterns.length} unique pattern(s) covering ${totalParts} part(s), ${bends} bend(s) total${failed.length > 0 ? `; ${failed.length} part(s) are not sheet metal` : ""}. Every pattern is volume-verified against its solid.`,
|
|
75091
|
+
note: "Each DXF is a fab-ready merged silhouette in millimetres: one closed exterior polyline + hole loops on CUT, DASHED bend centerlines on BEND_UP/BEND_DOWN. DXF carries no bend angles \u2014 use `bend_table` (angle, inside radius, direction, allowance, crease line) when entering them in the fab's UI. Hand `nest_input` to sheet_metal_nest, then sheet_metal_cost, for a quote."
|
|
75092
|
+
},
|
|
75093
|
+
null,
|
|
75094
|
+
2
|
|
75095
|
+
)
|
|
75096
|
+
}
|
|
75097
|
+
]
|
|
75098
|
+
};
|
|
75099
|
+
}
|
|
75100
|
+
var flatPatternFromSolidSchema, toolDefs41;
|
|
75101
|
+
var init_flat_pattern = __esm({
|
|
75102
|
+
"src/tools/flat-pattern.ts"() {
|
|
75103
|
+
"use strict";
|
|
75104
|
+
init_session2();
|
|
75105
|
+
init_tool_def();
|
|
75106
|
+
flatPatternFromSolidSchema = {
|
|
75107
|
+
type: "object",
|
|
75108
|
+
properties: {
|
|
75109
|
+
document_id: {
|
|
75110
|
+
type: "string",
|
|
75111
|
+
description: "CAD session id (open_document / create_cad_loon) holding the solid(s)."
|
|
75112
|
+
},
|
|
75113
|
+
part_id: {
|
|
75114
|
+
type: "string",
|
|
75115
|
+
description: "Root id of a single part to flatten (from `read`). Omit to flatten every solid part in the document and group identical parts into one pattern \xD7 quantity."
|
|
75116
|
+
},
|
|
75117
|
+
material: {
|
|
75118
|
+
type: "string",
|
|
75119
|
+
description: 'Material key for K-factor lookup (e.g. "al-soft", "steel-mild"). Default "al-soft". See sheet_metal_materials.'
|
|
75120
|
+
},
|
|
75121
|
+
manual_k: {
|
|
75122
|
+
type: "number",
|
|
75123
|
+
description: "Override the K-factor for every recovered bend (skips the bend table)."
|
|
75124
|
+
},
|
|
75125
|
+
shop_profile: {
|
|
75126
|
+
type: "string",
|
|
75127
|
+
description: 'Fab-service catalog id (e.g. "sendcutsend") to run manufacturability against. Default: generic shop.'
|
|
75128
|
+
},
|
|
75129
|
+
volume_tolerance: {
|
|
75130
|
+
type: "number",
|
|
75131
|
+
description: "Max relative volume error the round-trip check accepts (default 0.02). Re-extruding the emitted profile by the detected thickness must reproduce the solid's volume; a mismatch fails the part."
|
|
75132
|
+
},
|
|
75133
|
+
include_dxf: {
|
|
75134
|
+
type: "boolean",
|
|
75135
|
+
description: "Include the DXF string per pattern. Default true."
|
|
75136
|
+
}
|
|
75137
|
+
},
|
|
75138
|
+
required: ["document_id"]
|
|
75139
|
+
};
|
|
75140
|
+
toolDefs41 = [
|
|
75141
|
+
{
|
|
75142
|
+
name: "flat_pattern_from_solid",
|
|
75143
|
+
pack: "sheet_metal",
|
|
75144
|
+
description: "Emit the flat pattern (DXF + bend table) for a part that was modelled as an ordinary solid \u2014 extruded sketch, boolean, imported STEP \u2014 with no sheet-metal authoring required. The mechanical counterpart of board_from_solid: it detects the constant-thickness walls and the cylindrical bends between them, returns the cut profile (outer boundary + every interior hole) with the detected thickness, and a bend table (line positions, angles, direction, allowance) for bent parts. Called without part_id it batches the whole document and groups identical parts into one pattern \xD7 quantity (mirror images stay separate). Fails closed: re-extruding the emitted profile by the detected thickness must reproduce the solid's volume, so a part that isn't really constant-thickness sheet is reported as such instead of yielding a wrong outline.",
|
|
75145
|
+
inputSchema: flatPatternFromSolidSchema,
|
|
75146
|
+
handler: (a, c) => flatPatternFromSolid(a, c.engine),
|
|
75147
|
+
behavior: behavior({ geometry: true })
|
|
75148
|
+
}
|
|
75149
|
+
];
|
|
75150
|
+
}
|
|
75151
|
+
});
|
|
75152
|
+
|
|
74879
75153
|
// src/tools/acoustics.ts
|
|
74880
75154
|
import { writeFileSync as writeFileSync5 } from "node:fs";
|
|
74881
75155
|
function lookupMaterial(engine, name) {
|
|
@@ -75044,7 +75318,7 @@ function simulateStrike(input, engine) {
|
|
|
75044
75318
|
}
|
|
75045
75319
|
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
75046
75320
|
}
|
|
75047
|
-
var cents, MATERIAL_ALIASES, simulateStrikeSchema, round23, round4,
|
|
75321
|
+
var cents, MATERIAL_ALIASES, simulateStrikeSchema, round23, round4, toolDefs42;
|
|
75048
75322
|
var init_acoustics = __esm({
|
|
75049
75323
|
"src/tools/acoustics.ts"() {
|
|
75050
75324
|
"use strict";
|
|
@@ -75118,7 +75392,7 @@ var init_acoustics = __esm({
|
|
|
75118
75392
|
};
|
|
75119
75393
|
round23 = (x) => Math.round(x * 100) / 100;
|
|
75120
75394
|
round4 = (x) => Math.round(x * 1e4) / 1e4;
|
|
75121
|
-
|
|
75395
|
+
toolDefs42 = [
|
|
75122
75396
|
{
|
|
75123
75397
|
name: "simulate_strike",
|
|
75124
75398
|
pack: "sheet_metal",
|
|
@@ -75269,7 +75543,7 @@ function importStep(input, engine) {
|
|
|
75269
75543
|
]
|
|
75270
75544
|
};
|
|
75271
75545
|
}
|
|
75272
|
-
var MAX_STEP_BYTES, importStepSchema,
|
|
75546
|
+
var MAX_STEP_BYTES, importStepSchema, toolDefs43;
|
|
75273
75547
|
var init_import = __esm({
|
|
75274
75548
|
"src/tools/import.ts"() {
|
|
75275
75549
|
"use strict";
|
|
@@ -75301,7 +75575,7 @@ var init_import = __esm({
|
|
|
75301
75575
|
}
|
|
75302
75576
|
}
|
|
75303
75577
|
};
|
|
75304
|
-
|
|
75578
|
+
toolDefs43 = [
|
|
75305
75579
|
{
|
|
75306
75580
|
name: "import_step",
|
|
75307
75581
|
pack: null,
|
|
@@ -75429,7 +75703,7 @@ function importEagle(_input) {
|
|
|
75429
75703
|
"Eagle (.brd) import is not yet supported. Export your board from Eagle as KiCad (File > Export > KiCad .kicad_pcb) and use import_kicad instead."
|
|
75430
75704
|
);
|
|
75431
75705
|
}
|
|
75432
|
-
var MAX_PCB_BYTES, importKicadSchema, importEagleSchema,
|
|
75706
|
+
var MAX_PCB_BYTES, importKicadSchema, importEagleSchema, toolDefs44;
|
|
75433
75707
|
var init_import_pcb = __esm({
|
|
75434
75708
|
"src/tools/import-pcb.ts"() {
|
|
75435
75709
|
"use strict";
|
|
@@ -75470,7 +75744,7 @@ var init_import_pcb = __esm({
|
|
|
75470
75744
|
}
|
|
75471
75745
|
}
|
|
75472
75746
|
};
|
|
75473
|
-
|
|
75747
|
+
toolDefs44 = [
|
|
75474
75748
|
{
|
|
75475
75749
|
name: "import_kicad",
|
|
75476
75750
|
pack: "ecad",
|
|
@@ -75566,7 +75840,7 @@ Compressed URL param: ${encoded.length} bytes`
|
|
|
75566
75840
|
]
|
|
75567
75841
|
};
|
|
75568
75842
|
}
|
|
75569
|
-
var openInBrowserSchema,
|
|
75843
|
+
var openInBrowserSchema, toolDefs45;
|
|
75570
75844
|
var init_share = __esm({
|
|
75571
75845
|
"src/tools/share.ts"() {
|
|
75572
75846
|
"use strict";
|
|
@@ -75590,7 +75864,7 @@ var init_share = __esm({
|
|
|
75590
75864
|
}
|
|
75591
75865
|
}
|
|
75592
75866
|
};
|
|
75593
|
-
|
|
75867
|
+
toolDefs45 = [
|
|
75594
75868
|
{
|
|
75595
75869
|
name: "open_in_browser",
|
|
75596
75870
|
pack: null,
|
|
@@ -76023,7 +76297,7 @@ function batchReset(input) {
|
|
|
76023
76297
|
};
|
|
76024
76298
|
}
|
|
76025
76299
|
}
|
|
76026
|
-
var simulations, MAX_TRAJECTORY, envRecords, batchGroups, INLINE_DOC_DESC, SESSION_DOC_DESC, createRobotEnvSchema, gymStepSchema, gymResetSchema, gymObserveSchema, gymCloseSchema, batchCreateEnvsSchema, batchStepSchema, batchResetSchema,
|
|
76300
|
+
var simulations, MAX_TRAJECTORY, envRecords, batchGroups, INLINE_DOC_DESC, SESSION_DOC_DESC, createRobotEnvSchema, gymStepSchema, gymResetSchema, gymObserveSchema, gymCloseSchema, batchCreateEnvsSchema, batchStepSchema, batchResetSchema, toolDefs46;
|
|
76027
76301
|
var init_gym = __esm({
|
|
76028
76302
|
"src/tools/gym.ts"() {
|
|
76029
76303
|
"use strict";
|
|
@@ -76185,7 +76459,7 @@ var init_gym = __esm({
|
|
|
76185
76459
|
},
|
|
76186
76460
|
required: ["batch_id"]
|
|
76187
76461
|
};
|
|
76188
|
-
|
|
76462
|
+
toolDefs46 = [
|
|
76189
76463
|
{
|
|
76190
76464
|
name: "create_robot_env",
|
|
76191
76465
|
pack: "physics",
|
|
@@ -76543,7 +76817,7 @@ async function renderMolecule(input) {
|
|
|
76543
76817
|
return fail(err7);
|
|
76544
76818
|
}
|
|
76545
76819
|
}
|
|
76546
|
-
var loadStructureSchema, inspectMoleculeSchema, configSchema, minimizeEnergySchema, mdRunSchema, designMaterialSchema, homogenizeMaterialSchema, renderMoleculeSchema, CPK, DEFAULT_RADIUS,
|
|
76820
|
+
var loadStructureSchema, inspectMoleculeSchema, configSchema, minimizeEnergySchema, mdRunSchema, designMaterialSchema, homogenizeMaterialSchema, renderMoleculeSchema, CPK, DEFAULT_RADIUS, toolDefs47;
|
|
76547
76821
|
var init_atoms2 = __esm({
|
|
76548
76822
|
"src/tools/atoms.ts"() {
|
|
76549
76823
|
"use strict";
|
|
@@ -76678,7 +76952,7 @@ var init_atoms2 = __esm({
|
|
|
76678
76952
|
S: 1.05,
|
|
76679
76953
|
P: 1.07
|
|
76680
76954
|
};
|
|
76681
|
-
|
|
76955
|
+
toolDefs47 = [
|
|
76682
76956
|
{
|
|
76683
76957
|
name: "load_structure",
|
|
76684
76958
|
pack: "atoms",
|
|
@@ -77886,7 +78160,7 @@ function errorResult(text) {
|
|
|
77886
78160
|
isError: true
|
|
77887
78161
|
};
|
|
77888
78162
|
}
|
|
77889
|
-
var recordSimulationSchema, SVG_SCALE2, MAX_STEPS, MIN_STEPS, DEFAULT_WIDTH_PX2, MIN_WIDTH_PX, MAX_WIDTH_PX, DEFAULT_FPS, MIN_FPS, MAX_FPS,
|
|
78163
|
+
var recordSimulationSchema, SVG_SCALE2, MAX_STEPS, MIN_STEPS, DEFAULT_WIDTH_PX2, MIN_WIDTH_PX, MAX_WIDTH_PX, DEFAULT_FPS, MIN_FPS, MAX_FPS, toolDefs48;
|
|
77890
78164
|
var init_record2 = __esm({
|
|
77891
78165
|
"src/tools/record.ts"() {
|
|
77892
78166
|
"use strict";
|
|
@@ -77950,7 +78224,7 @@ var init_record2 = __esm({
|
|
|
77950
78224
|
DEFAULT_FPS = 30;
|
|
77951
78225
|
MIN_FPS = 1;
|
|
77952
78226
|
MAX_FPS = 60;
|
|
77953
|
-
|
|
78227
|
+
toolDefs48 = [
|
|
77954
78228
|
{
|
|
77955
78229
|
name: "record_simulation",
|
|
77956
78230
|
pack: "physics",
|
|
@@ -78043,7 +78317,7 @@ ${text}`
|
|
|
78043
78317
|
]
|
|
78044
78318
|
};
|
|
78045
78319
|
}
|
|
78046
|
-
var getChangelogSchema,
|
|
78320
|
+
var getChangelogSchema, toolDefs49;
|
|
78047
78321
|
var init_changelog2 = __esm({
|
|
78048
78322
|
"src/tools/changelog.ts"() {
|
|
78049
78323
|
"use strict";
|
|
@@ -78083,7 +78357,7 @@ var init_changelog2 = __esm({
|
|
|
78083
78357
|
}
|
|
78084
78358
|
}
|
|
78085
78359
|
};
|
|
78086
|
-
|
|
78360
|
+
toolDefs49 = [
|
|
78087
78361
|
{
|
|
78088
78362
|
name: "get_changelog",
|
|
78089
78363
|
pack: null,
|
|
@@ -78232,7 +78506,7 @@ function getSimVersion(input) {
|
|
|
78232
78506
|
]
|
|
78233
78507
|
};
|
|
78234
78508
|
}
|
|
78235
|
-
var getSimReplaySchema, getSimVersionSchema,
|
|
78509
|
+
var getSimReplaySchema, getSimVersionSchema, toolDefs50;
|
|
78236
78510
|
var init_sim_replay = __esm({
|
|
78237
78511
|
"src/tools/sim-replay.ts"() {
|
|
78238
78512
|
"use strict";
|
|
@@ -78260,7 +78534,7 @@ var init_sim_replay = __esm({
|
|
|
78260
78534
|
},
|
|
78261
78535
|
required: ["env_id"]
|
|
78262
78536
|
};
|
|
78263
|
-
|
|
78537
|
+
toolDefs50 = [
|
|
78264
78538
|
{
|
|
78265
78539
|
name: "get_sim_replay",
|
|
78266
78540
|
pack: null,
|
|
@@ -78379,7 +78653,7 @@ async function getOrderFeed(input, store, user) {
|
|
|
78379
78653
|
version: version2
|
|
78380
78654
|
});
|
|
78381
78655
|
}
|
|
78382
|
-
var toUsd4, getOrderFeedSchema,
|
|
78656
|
+
var toUsd4, getOrderFeedSchema, toolDefs51;
|
|
78383
78657
|
var init_order_feed = __esm({
|
|
78384
78658
|
"src/tools/order-feed.ts"() {
|
|
78385
78659
|
"use strict";
|
|
@@ -78398,7 +78672,7 @@ var init_order_feed = __esm({
|
|
|
78398
78672
|
},
|
|
78399
78673
|
required: ["document_id"]
|
|
78400
78674
|
};
|
|
78401
|
-
|
|
78675
|
+
toolDefs51 = [
|
|
78402
78676
|
{
|
|
78403
78677
|
name: "get_order_feed",
|
|
78404
78678
|
pack: "fabricate",
|
|
@@ -79300,7 +79574,7 @@ ${issues.map((i) => i.problem).join("\n")}`
|
|
|
79300
79574
|
note: "Rollout compiled to the document timeline. Preview with render_sequence, ship with export_video."
|
|
79301
79575
|
});
|
|
79302
79576
|
}
|
|
79303
|
-
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,
|
|
79577
|
+
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, toolDefs52;
|
|
79304
79578
|
var init_animate = __esm({
|
|
79305
79579
|
"src/tools/animate.ts"() {
|
|
79306
79580
|
"use strict";
|
|
@@ -79452,7 +79726,7 @@ var init_animate = __esm({
|
|
|
79452
79726
|
},
|
|
79453
79727
|
required: ["env_id", "document_id"]
|
|
79454
79728
|
};
|
|
79455
|
-
|
|
79729
|
+
toolDefs52 = [
|
|
79456
79730
|
{
|
|
79457
79731
|
name: "animate",
|
|
79458
79732
|
pack: null,
|
|
@@ -79514,6 +79788,7 @@ function buildInstructions(kernelPrompt) {
|
|
|
79514
79788
|
"- See your work with `render_view` (isometric PNG); measure with `inspect_cad` (whole-document volume, area, bbox, center of mass), `inspect_part` / `describe_scene` (per-part bbox, size, center, material), and `measure` (min distance/overlap between two parts, or one part's bbox+volume+center of mass).",
|
|
79515
79789
|
"- Ship with `export_cad` (STL/GLB/STEP) or `open_in_browser` (vcad.io deep link).",
|
|
79516
79790
|
"- Fix in place: when geometry is wrong, prefer `update` on the offending node over deleting parts and starting over.",
|
|
79791
|
+
"- Cut sheet parts you already modelled: `flat_pattern_from_solid` turns any constant-thickness solid \u2014 extruded sketch, boolean, imported STEP \u2014 into a fab-ready DXF plus a bend table, no sheet-metal re-authoring. Called without `part_id` it does the whole document and groups identical parts into one pattern \xD7 quantity; hand its `nest_input` to `sheet_metal_nest`, then `sheet_metal_cost`, for a quote. It fails closed: the emitted profile re-extruded by the detected thickness must reproduce the solid's volume.",
|
|
79517
79792
|
"- Deliver the project bill of materials with `bom_create` \u2192 `bom_export` (markdown/CSV/JSON with landed-cost totals): link quote_manufacturing quotes on manufactured lines, and source COTS hardware (bearings, shafts, standoffs, screws, ferrite magnets) with `search_mechanical_parts`. All BOM prices are estimates and flagged as such.",
|
|
79518
79793
|
"",
|
|
79519
79794
|
"",
|
|
@@ -80130,6 +80405,7 @@ async function createServer(existingEngine, context = { user: null }) {
|
|
|
80130
80405
|
function slimPreviewForInlineUi(result, docId, toolName, clientHasInlineUi) {
|
|
80131
80406
|
if (!clientHasInlineUi) return;
|
|
80132
80407
|
if (toolName === "sheet_metal_unfold") return;
|
|
80408
|
+
if (toolName === "flat_pattern_from_solid") return;
|
|
80133
80409
|
if (PURE_JSON_RESULT_TOOLS.has(toolName)) return;
|
|
80134
80410
|
const alwaysSlim = toolName === "create_cad_loon";
|
|
80135
80411
|
const totalChars = result.content.reduce(
|
|
@@ -80368,6 +80644,7 @@ var init_server3 = __esm({
|
|
|
80368
80644
|
init_loon_macros();
|
|
80369
80645
|
init_dfm2();
|
|
80370
80646
|
init_sheet_metal2();
|
|
80647
|
+
init_flat_pattern();
|
|
80371
80648
|
init_acoustics();
|
|
80372
80649
|
init_import();
|
|
80373
80650
|
init_import_pcb();
|
|
@@ -80382,8 +80659,8 @@ var init_server3 = __esm({
|
|
|
80382
80659
|
init_order_feed();
|
|
80383
80660
|
init_animate();
|
|
80384
80661
|
PKG_VERSION = (() => {
|
|
80385
|
-
if ("0.9.4-main.
|
|
80386
|
-
return "0.9.4-main.
|
|
80662
|
+
if ("0.9.4-main.41") {
|
|
80663
|
+
return "0.9.4-main.41";
|
|
80387
80664
|
}
|
|
80388
80665
|
try {
|
|
80389
80666
|
const req = createRequire2(import.meta.url);
|
|
@@ -80392,8 +80669,8 @@ var init_server3 = __esm({
|
|
|
80392
80669
|
return "0.0.0";
|
|
80393
80670
|
}
|
|
80394
80671
|
})();
|
|
80395
|
-
BUILD_SHA = "
|
|
80396
|
-
BUILD_TIME = "2026-07-26T14:
|
|
80672
|
+
BUILD_SHA = "b00f071e4b839c6448769107ed84182597d763ed";
|
|
80673
|
+
BUILD_TIME = "2026-07-26T14:41:39.682Z";
|
|
80397
80674
|
SHORT_SHA = BUILD_SHA === "unknown" ? "unknown" : BUILD_SHA.slice(0, 7);
|
|
80398
80675
|
VERSION_WITH_BUILD = SHORT_SHA === "unknown" ? PKG_VERSION : `${PKG_VERSION}+${SHORT_SHA}`;
|
|
80399
80676
|
INSTANCE_ID = randomUUID6().slice(0, 8);
|
|
@@ -80466,11 +80743,12 @@ var init_server3 = __esm({
|
|
|
80466
80743
|
...toolDefs46,
|
|
80467
80744
|
...toolDefs47,
|
|
80468
80745
|
...toolDefs48,
|
|
80746
|
+
...toolDefs49,
|
|
80469
80747
|
...toolDefs20,
|
|
80470
80748
|
...toolDefs19,
|
|
80471
|
-
...toolDefs49,
|
|
80472
80749
|
...toolDefs50,
|
|
80473
|
-
...toolDefs51
|
|
80750
|
+
...toolDefs51,
|
|
80751
|
+
...toolDefs52
|
|
80474
80752
|
];
|
|
80475
80753
|
LIST_TOOL_ORDER = [
|
|
80476
80754
|
// ── Session lifecycle ──────────────────────────────────────
|
|
@@ -80577,6 +80855,7 @@ var init_server3 = __esm({
|
|
|
80577
80855
|
"sheet_metal_suggest_fix",
|
|
80578
80856
|
"sheet_metal_sequence",
|
|
80579
80857
|
"sheet_metal_nest",
|
|
80858
|
+
"flat_pattern_from_solid",
|
|
80580
80859
|
"simulate_strike",
|
|
80581
80860
|
// ── Import + share ─────────────────────────────────────────
|
|
80582
80861
|
"import_step",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vcad/mcp",
|
|
3
|
-
"version": "0.9.4-main.
|
|
3
|
+
"version": "0.9.4-main.41",
|
|
4
4
|
"description": "vcad MCP server — parametric CAD + PCB design tools for AI agents (self-contained: bundled server + kernel WASM)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"homepage": "https://vcad.io",
|
|
21
21
|
"vcadBuild": {
|
|
22
|
-
"sha": "
|
|
23
|
-
"builtAt": "2026-07-26T14:
|
|
22
|
+
"sha": "b00f071e4b839c6448769107ed84182597d763ed",
|
|
23
|
+
"builtAt": "2026-07-26T14:41:39.682Z"
|
|
24
24
|
}
|
|
25
25
|
}
|