cyclecad 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/API-BUILD-MANIFEST.txt +339 -0
  2. package/API-SERVER.md +535 -0
  3. package/Architecture-Deck.pptx +0 -0
  4. package/CLAUDE.md +172 -11
  5. package/CLI-BUILD-SUMMARY.md +504 -0
  6. package/CLI-INDEX.md +356 -0
  7. package/CLI-README.md +466 -0
  8. package/COLLABORATION-INTEGRATION-GUIDE.md +325 -0
  9. package/CONNECTED_FABS_GUIDE.md +612 -0
  10. package/CONNECTED_FABS_README.md +310 -0
  11. package/DELIVERABLES.md +343 -0
  12. package/DFM-ANALYZER-INTEGRATION.md +368 -0
  13. package/DFM-QUICK-START.js +253 -0
  14. package/Dockerfile +69 -0
  15. package/IMPLEMENTATION.md +327 -0
  16. package/LICENSE +31 -0
  17. package/MARKETPLACE_QUICK_REFERENCE.txt +294 -0
  18. package/MCP-INDEX.md +264 -0
  19. package/QUICKSTART-API.md +388 -0
  20. package/QUICKSTART-CLI.md +211 -0
  21. package/QUICKSTART-MCP.md +196 -0
  22. package/README-MCP.md +208 -0
  23. package/TEST-TOKEN-ENGINE.md +319 -0
  24. package/TOKEN-ENGINE-SUMMARY.md +266 -0
  25. package/TOKENS-README.md +263 -0
  26. package/TOOLS-REFERENCE.md +254 -0
  27. package/app/index.html +373 -3
  28. package/app/js/TOKEN-INTEGRATION.md +391 -0
  29. package/app/js/agent-api.js +3 -3
  30. package/app/js/ai-copilot.js +1435 -0
  31. package/app/js/cad-vr.js +917 -0
  32. package/app/js/cam-operations.js +638 -0
  33. package/app/js/cam-pipeline.js +840 -0
  34. package/app/js/collaboration-ui.js +995 -0
  35. package/app/js/collaboration.js +1116 -0
  36. package/app/js/connected-fabs-example.js +404 -0
  37. package/app/js/connected-fabs.js +1449 -0
  38. package/app/js/dfm-analyzer.js +1760 -0
  39. package/app/js/gcode-generator.js +485 -0
  40. package/app/js/gdt-training.js +1144 -0
  41. package/app/js/machine-profiles.js +534 -0
  42. package/app/js/marketplace-v2.js +766 -0
  43. package/app/js/marketplace.js +1994 -0
  44. package/app/js/material-library.js +2115 -0
  45. package/app/js/misumi-catalog.js +904 -0
  46. package/app/js/section-view.js +666 -0
  47. package/app/js/sketch-enhance.js +779 -0
  48. package/app/js/stock-manager.js +482 -0
  49. package/app/js/text-to-cad.js +806 -0
  50. package/app/js/token-dashboard.js +563 -0
  51. package/app/js/token-engine.js +743 -0
  52. package/app/js/tool-library.js +593 -0
  53. package/app/test-agent.html +1801 -0
  54. package/app/tutorials/advanced.html +1924 -0
  55. package/app/tutorials/basic.html +1160 -0
  56. package/app/tutorials/intermediate.html +1456 -0
  57. package/bin/cyclecad-cli.js +662 -0
  58. package/bin/cyclecad-mcp +2 -0
  59. package/bin/server.js +242 -0
  60. package/cycleCAD-Architecture.pptx +0 -0
  61. package/cycleCAD-Investor-Deck.pptx +0 -0
  62. package/demo-mcp.sh +60 -0
  63. package/docs/API-SERVER-SUMMARY.md +375 -0
  64. package/docs/API-SERVER.md +667 -0
  65. package/docs/CAM-EXAMPLES.md +344 -0
  66. package/docs/CAM-INTEGRATION.md +612 -0
  67. package/docs/CAM-QUICK-REFERENCE.md +199 -0
  68. package/docs/CLI-INTEGRATION.md +510 -0
  69. package/docs/CLI.md +872 -0
  70. package/docs/MARKETPLACE-API-SCHEMA.json +564 -0
  71. package/docs/MARKETPLACE-INTEGRATION.md +467 -0
  72. package/docs/MARKETPLACE-SETUP.html +439 -0
  73. package/docs/MCP-SERVER.md +403 -0
  74. package/examples/api-client-example.js +488 -0
  75. package/examples/api-client-example.py +359 -0
  76. package/examples/batch-manufacturing.txt +28 -0
  77. package/examples/batch-simple.txt +26 -0
  78. package/linkedin-post-combined.md +31 -0
  79. package/model-marketplace.html +1273 -0
  80. package/package.json +14 -3
  81. package/server/api-server.js +1120 -0
  82. package/server/mcp-server.js +1161 -0
  83. package/test-api-server.js +432 -0
  84. package/test-mcp.js +198 -0
  85. package/~$cycleCAD-Investor-Deck.pptx +0 -0
@@ -0,0 +1,482 @@
1
+ /**
2
+ * Stock/Workpiece Manager
3
+ * Manages stock material (rectangular, cylindrical, custom mesh) with 3D visualization
4
+ * Registers on window.cycleCAD.stock
5
+ *
6
+ * Usage:
7
+ * window.cycleCAD.stock.setStock({ type: 'rectangular', x: 100, y: 100, z: 50, material: 'aluminum' })
8
+ * const stock = window.cycleCAD.stock.getStock()
9
+ * window.cycleCAD.stock.updateVisualization()
10
+ */
11
+
12
+ (function() {
13
+ 'use strict';
14
+
15
+ // Material database (density in g/cm³)
16
+ const MATERIALS = {
17
+ 'aluminum': { name: 'Aluminum', density: 2.70, color: '#C0C0C0' },
18
+ 'steel': { name: 'Steel', density: 7.85, color: '#555555' },
19
+ 'stainless': { name: 'Stainless Steel', density: 8.0, color: '#808080' },
20
+ 'brass': { name: 'Brass', density: 8.53, color: '#D4AF37' },
21
+ 'copper': { name: 'Copper', density: 8.96, color: '#B87333' },
22
+ 'titanium': { name: 'Titanium', density: 4.51, color: '#E7DECD' },
23
+ 'cast-iron': { name: 'Cast Iron', density: 7.2, color: '#3C3C3C' },
24
+ 'plastic': { name: 'Plastic (ABS)', density: 1.05, color: '#E8E8E8' },
25
+ 'wood': { name: 'Wood', density: 0.75, color: '#8B4513' },
26
+ 'foam': { name: 'Foam', density: 0.25, color: '#FFFACD' }
27
+ };
28
+
29
+ // Default stock
30
+ let currentStock = {
31
+ type: 'rectangular',
32
+ x: 100,
33
+ y: 100,
34
+ z: 50,
35
+ material: 'aluminum',
36
+ origin: { x: 0, y: 0, z: 0 },
37
+ rotated: false,
38
+ indexed: false,
39
+ indexCount: 1,
40
+ indexAxis: 'Z'
41
+ };
42
+
43
+ // Three.js visualization objects
44
+ let stockGroup = null;
45
+ let stockMesh = null;
46
+ let boundingBox = null;
47
+
48
+ /**
49
+ * Stock manager API
50
+ */
51
+ const stockAPI = {
52
+ /**
53
+ * Set stock configuration
54
+ */
55
+ setStock(config) {
56
+ currentStock = {
57
+ ...currentStock,
58
+ ...config
59
+ };
60
+
61
+ // Validate
62
+ if (!MATERIALS[currentStock.material]) {
63
+ console.warn(`Material "${currentStock.material}" not found. Using aluminum.`);
64
+ currentStock.material = 'aluminum';
65
+ }
66
+
67
+ if (currentStock.type === 'rectangular') {
68
+ if (currentStock.x <= 0 || currentStock.y <= 0 || currentStock.z <= 0) {
69
+ console.warn('Stock dimensions must be positive.');
70
+ return false;
71
+ }
72
+ } else if (currentStock.type === 'cylindrical') {
73
+ if (currentStock.diameter <= 0 || currentStock.height <= 0) {
74
+ console.warn('Stock diameter and height must be positive.');
75
+ return false;
76
+ }
77
+ }
78
+
79
+ return true;
80
+ },
81
+
82
+ /**
83
+ * Get current stock
84
+ */
85
+ getStock() {
86
+ return { ...currentStock };
87
+ },
88
+
89
+ /**
90
+ * Get stock dimensions object
91
+ */
92
+ getDimensions() {
93
+ if (currentStock.type === 'rectangular') {
94
+ return {
95
+ x: currentStock.x,
96
+ y: currentStock.y,
97
+ z: currentStock.z
98
+ };
99
+ } else if (currentStock.type === 'cylindrical') {
100
+ return {
101
+ diameter: currentStock.diameter,
102
+ radius: currentStock.diameter / 2,
103
+ height: currentStock.height
104
+ };
105
+ }
106
+ return null;
107
+ },
108
+
109
+ /**
110
+ * Calculate stock volume (mm³)
111
+ */
112
+ calculateVolume() {
113
+ if (currentStock.type === 'rectangular') {
114
+ return currentStock.x * currentStock.y * currentStock.z;
115
+ } else if (currentStock.type === 'cylindrical') {
116
+ const radius = currentStock.diameter / 2;
117
+ return Math.PI * radius * radius * currentStock.height;
118
+ }
119
+ return 0;
120
+ },
121
+
122
+ /**
123
+ * Calculate stock weight (grams)
124
+ */
125
+ calculateWeight() {
126
+ const volumeMm3 = this.calculateVolume();
127
+ const volumeCm3 = volumeMm3 / 1000; // Convert mm³ to cm³
128
+ const density = MATERIALS[currentStock.material].density;
129
+ return volumeCm3 * density;
130
+ },
131
+
132
+ /**
133
+ * Get material info
134
+ */
135
+ getMaterial(materialId) {
136
+ return MATERIALS[materialId] || null;
137
+ },
138
+
139
+ /**
140
+ * List all materials
141
+ */
142
+ listMaterials() {
143
+ return Object.keys(MATERIALS).map(id => ({
144
+ id,
145
+ ...MATERIALS[id]
146
+ }));
147
+ },
148
+
149
+ /**
150
+ * Set material
151
+ */
152
+ setMaterial(materialId) {
153
+ if (!MATERIALS[materialId]) {
154
+ console.warn(`Material "${materialId}" not found.`);
155
+ return false;
156
+ }
157
+
158
+ currentStock.material = materialId;
159
+ return true;
160
+ },
161
+
162
+ /**
163
+ * Auto-size stock from part bounding box
164
+ * Requires window._scene to be set (Three.js scene)
165
+ */
166
+ autoSizeFromPart(offset = { x: 10, y: 10, z: 5 }) {
167
+ if (!window._scene) {
168
+ console.warn('Three.js scene not available.');
169
+ return false;
170
+ }
171
+
172
+ let minX = Infinity, maxX = -Infinity;
173
+ let minY = Infinity, maxY = -Infinity;
174
+ let minZ = Infinity, maxZ = -Infinity;
175
+
176
+ // Iterate through all meshes in scene
177
+ window._scene.traverse(obj => {
178
+ if (obj.isMesh && obj.geometry) {
179
+ obj.geometry.computeBoundingBox();
180
+ const bb = obj.geometry.boundingBox;
181
+
182
+ minX = Math.min(minX, bb.min.x);
183
+ maxX = Math.max(maxX, bb.max.x);
184
+ minY = Math.min(minY, bb.min.y);
185
+ maxY = Math.max(maxY, bb.max.y);
186
+ minZ = Math.min(minZ, bb.min.z);
187
+ maxZ = Math.max(maxZ, bb.max.z);
188
+ }
189
+ });
190
+
191
+ if (minX === Infinity) {
192
+ console.warn('No geometry found in scene.');
193
+ return false;
194
+ }
195
+
196
+ const width = maxX - minX + offset.x;
197
+ const depth = maxY - minY + offset.y;
198
+ const height = maxZ - minZ + offset.z;
199
+
200
+ currentStock.type = 'rectangular';
201
+ currentStock.x = width;
202
+ currentStock.y = depth;
203
+ currentStock.z = height;
204
+ currentStock.origin = {
205
+ x: minX - offset.x / 2,
206
+ y: minY - offset.y / 2,
207
+ z: minZ - offset.z / 2
208
+ };
209
+
210
+ return true;
211
+ },
212
+
213
+ /**
214
+ * Enable indexed stock (rotary axis)
215
+ */
216
+ setIndexed(enabled, count = 1, axis = 'Z') {
217
+ currentStock.indexed = enabled;
218
+ currentStock.indexCount = count;
219
+ currentStock.indexAxis = axis;
220
+ return true;
221
+ },
222
+
223
+ /**
224
+ * Update Three.js visualization
225
+ * Requires window._scene and window._renderer to be set
226
+ */
227
+ updateVisualization() {
228
+ if (!window._scene || !window._renderer) {
229
+ console.warn('Three.js scene or renderer not available.');
230
+ return false;
231
+ }
232
+
233
+ // Remove old stock group if it exists
234
+ if (stockGroup) {
235
+ window._scene.remove(stockGroup);
236
+ }
237
+
238
+ // Create new group
239
+ stockGroup = new window.THREE.Group();
240
+ stockGroup.name = 'StockWireframe';
241
+
242
+ const matColor = MATERIALS[currentStock.material].color;
243
+
244
+ if (currentStock.type === 'rectangular') {
245
+ // Create wireframe box
246
+ const geom = new window.THREE.BoxGeometry(
247
+ currentStock.x,
248
+ currentStock.y,
249
+ currentStock.z
250
+ );
251
+
252
+ const matWireframe = new window.THREE.LineBasicMaterial({
253
+ color: matColor,
254
+ linewidth: 2
255
+ });
256
+
257
+ const wireframe = new window.THREE.LineSegments(
258
+ new window.THREE.EdgesGeometry(geom),
259
+ matWireframe
260
+ );
261
+
262
+ wireframe.position.set(
263
+ currentStock.origin.x + currentStock.x / 2,
264
+ currentStock.origin.y + currentStock.y / 2,
265
+ currentStock.origin.z + currentStock.z / 2
266
+ );
267
+
268
+ stockGroup.add(wireframe);
269
+
270
+ // Optional: add semi-transparent box mesh
271
+ const matMesh = new window.THREE.MeshBasicMaterial({
272
+ color: matColor,
273
+ transparent: true,
274
+ opacity: 0.1,
275
+ depthWrite: false
276
+ });
277
+
278
+ stockMesh = new window.THREE.Mesh(geom, matMesh);
279
+ stockMesh.position.copy(wireframe.position);
280
+ stockGroup.add(stockMesh);
281
+
282
+ } else if (currentStock.type === 'cylindrical') {
283
+ // Create wireframe cylinder
284
+ const geom = new window.THREE.CylinderGeometry(
285
+ currentStock.diameter / 2,
286
+ currentStock.diameter / 2,
287
+ currentStock.height,
288
+ 32
289
+ );
290
+
291
+ const matWireframe = new window.THREE.LineBasicMaterial({
292
+ color: matColor,
293
+ linewidth: 2
294
+ });
295
+
296
+ const wireframe = new window.THREE.LineSegments(
297
+ new window.THREE.EdgesGeometry(geom),
298
+ matWireframe
299
+ );
300
+
301
+ wireframe.position.set(
302
+ currentStock.origin.x,
303
+ currentStock.origin.y + currentStock.height / 2,
304
+ currentStock.origin.z
305
+ );
306
+
307
+ stockGroup.add(wireframe);
308
+
309
+ // Semi-transparent cylinder
310
+ const matMesh = new window.THREE.MeshBasicMaterial({
311
+ color: matColor,
312
+ transparent: true,
313
+ opacity: 0.1,
314
+ depthWrite: false
315
+ });
316
+
317
+ stockMesh = new window.THREE.Mesh(geom, matMesh);
318
+ stockMesh.position.copy(wireframe.position);
319
+ stockGroup.add(stockMesh);
320
+ }
321
+
322
+ window._scene.add(stockGroup);
323
+ return true;
324
+ },
325
+
326
+ /**
327
+ * Hide stock visualization
328
+ */
329
+ hideVisualization() {
330
+ if (stockGroup) {
331
+ stockGroup.visible = false;
332
+ }
333
+ },
334
+
335
+ /**
336
+ * Show stock visualization
337
+ */
338
+ showVisualization() {
339
+ if (stockGroup) {
340
+ stockGroup.visible = true;
341
+ } else {
342
+ this.updateVisualization();
343
+ }
344
+ },
345
+
346
+ /**
347
+ * Get stock info panel HTML
348
+ */
349
+ getInfoHTML() {
350
+ const volume = this.calculateVolume();
351
+ const weight = this.calculateWeight();
352
+ const matInfo = MATERIALS[currentStock.material];
353
+
354
+ let dimStr = '';
355
+ if (currentStock.type === 'rectangular') {
356
+ dimStr = `${currentStock.x.toFixed(1)} × ${currentStock.y.toFixed(1)} × ${currentStock.z.toFixed(1)} mm`;
357
+ } else if (currentStock.type === 'cylindrical') {
358
+ dimStr = `Ø${currentStock.diameter.toFixed(1)} × ${currentStock.height.toFixed(1)} mm`;
359
+ }
360
+
361
+ return `
362
+ <div style="font-size: 12px; line-height: 1.6;">
363
+ <strong>Stock Type:</strong> ${currentStock.type.charAt(0).toUpperCase() + currentStock.type.slice(1)}<br/>
364
+ <strong>Dimensions:</strong> ${dimStr}<br/>
365
+ <strong>Material:</strong> ${matInfo.name}<br/>
366
+ <strong>Volume:</strong> ${volume.toFixed(0)} mm³<br/>
367
+ <strong>Weight:</strong> ${weight.toFixed(1)} g<br/>
368
+ ${currentStock.indexed ? `<strong>Indexed:</strong> ${currentStock.indexCount} positions (${currentStock.indexAxis})<br/>` : ''}
369
+ </div>
370
+ `;
371
+ },
372
+
373
+ /**
374
+ * Compare stock vs part (how much material to remove)
375
+ */
376
+ compareWithPart() {
377
+ if (!window._scene) {
378
+ console.warn('Three.js scene not available.');
379
+ return null;
380
+ }
381
+
382
+ let partVolume = 0;
383
+ let partBounds = {
384
+ minX: Infinity, maxX: -Infinity,
385
+ minY: Infinity, maxY: -Infinity,
386
+ minZ: Infinity, maxZ: -Infinity
387
+ };
388
+
389
+ window._scene.traverse(obj => {
390
+ if (obj.isMesh && obj.geometry && obj !== stockMesh) {
391
+ obj.geometry.computeBoundingBox();
392
+ const bb = obj.geometry.boundingBox;
393
+
394
+ // Rough volume estimate from bounding box
395
+ const w = bb.max.x - bb.min.x;
396
+ const h = bb.max.y - bb.min.y;
397
+ const d = bb.max.z - bb.min.z;
398
+ partVolume += w * h * d;
399
+
400
+ partBounds.minX = Math.min(partBounds.minX, bb.min.x);
401
+ partBounds.maxX = Math.max(partBounds.maxX, bb.max.x);
402
+ partBounds.minY = Math.min(partBounds.minY, bb.min.y);
403
+ partBounds.maxY = Math.max(partBounds.maxY, bb.max.y);
404
+ partBounds.minZ = Math.min(partBounds.minZ, bb.min.z);
405
+ partBounds.maxZ = Math.max(partBounds.maxZ, bb.max.z);
406
+ }
407
+ });
408
+
409
+ const stockVolume = this.calculateVolume();
410
+ const removalVolume = stockVolume - partVolume;
411
+ const removalPercent = (removalVolume / stockVolume * 100).toFixed(1);
412
+
413
+ return {
414
+ stockVolume: stockVolume,
415
+ partVolume: partVolume,
416
+ removalVolume: removalVolume,
417
+ removalPercent: removalPercent,
418
+ partBounds: partBounds
419
+ };
420
+ },
421
+
422
+ /**
423
+ * Clip toolpaths to stock bounds
424
+ */
425
+ clipToStock(toolpaths) {
426
+ if (!toolpaths || !Array.isArray(toolpaths)) return [];
427
+
428
+ const dims = this.getDimensions();
429
+ const origin = currentStock.origin;
430
+
431
+ return toolpaths.map(point => {
432
+ let clipped = { ...point };
433
+
434
+ if (currentStock.type === 'rectangular') {
435
+ clipped.x = Math.max(origin.x, Math.min(point.x, origin.x + dims.x));
436
+ clipped.y = Math.max(origin.y, Math.min(point.y, origin.y + dims.y));
437
+ clipped.z = Math.max(origin.z, Math.min(point.z, origin.z + dims.z));
438
+ } else if (currentStock.type === 'cylindrical') {
439
+ const cx = origin.x;
440
+ const cy = origin.y;
441
+ const rad = Math.sqrt((point.x - cx) ** 2 + (point.y - cy) ** 2);
442
+
443
+ if (rad > dims.radius) {
444
+ const angle = Math.atan2(point.y - cy, point.x - cx);
445
+ clipped.x = cx + Math.cos(angle) * dims.radius;
446
+ clipped.y = cy + Math.sin(angle) * dims.radius;
447
+ }
448
+
449
+ clipped.z = Math.max(origin.y, Math.min(point.z, origin.y + dims.height));
450
+ }
451
+
452
+ return clipped;
453
+ });
454
+ },
455
+
456
+ /**
457
+ * Export stock config as JSON
458
+ */
459
+ exportConfig() {
460
+ return JSON.stringify(currentStock, null, 2);
461
+ },
462
+
463
+ /**
464
+ * Import stock config from JSON
465
+ */
466
+ importConfig(jsonString) {
467
+ try {
468
+ const config = JSON.parse(jsonString);
469
+ return this.setStock(config);
470
+ } catch (err) {
471
+ console.error('Failed to import stock config:', err);
472
+ return false;
473
+ }
474
+ }
475
+ };
476
+
477
+ // Register on window.cycleCAD
478
+ window.cycleCAD = window.cycleCAD || {};
479
+ window.cycleCAD.stock = stockAPI;
480
+
481
+ console.log('[cycleCAD.stock] Module loaded. Stock manager ready.');
482
+ })();