cyclecad 3.2.0 → 3.4.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 (65) hide show
  1. package/DOCKER-SETUP-VERIFICATION.md +399 -0
  2. package/DOCKER-TESTING.md +463 -0
  3. package/FUSION360_MODULES.md +478 -0
  4. package/FUSION_MODULES_README.md +352 -0
  5. package/INTEGRATION_SNIPPETS.md +608 -0
  6. package/KILLER-FEATURES-DELIVERY.md +469 -0
  7. package/MODULES_SUMMARY.txt +337 -0
  8. package/QUICK_REFERENCE.txt +298 -0
  9. package/README-DOCKER-TESTING.txt +438 -0
  10. package/app/index.html +23 -10
  11. package/app/js/fusion-help.json +1808 -0
  12. package/app/js/help-module-v3.js +1096 -0
  13. package/app/js/killer-features-help.json +395 -0
  14. package/app/js/killer-features.js +1508 -0
  15. package/app/js/modules/fusion-assembly.js +842 -0
  16. package/app/js/modules/fusion-cam.js +785 -0
  17. package/app/js/modules/fusion-data.js +814 -0
  18. package/app/js/modules/fusion-drawing.js +844 -0
  19. package/app/js/modules/fusion-inspection.js +756 -0
  20. package/app/js/modules/fusion-render.js +774 -0
  21. package/app/js/modules/fusion-simulation.js +986 -0
  22. package/app/js/modules/fusion-sketch.js +1044 -0
  23. package/app/js/modules/fusion-solid.js +1095 -0
  24. package/app/js/modules/fusion-surface.js +949 -0
  25. package/app/tests/FUSION_TEST_SUITE.md +266 -0
  26. package/app/tests/README.md +77 -0
  27. package/app/tests/TESTING-CHECKLIST.md +177 -0
  28. package/app/tests/TEST_SUITE_SUMMARY.txt +236 -0
  29. package/app/tests/brep-live-test.html +848 -0
  30. package/app/tests/docker-integration-test.html +811 -0
  31. package/app/tests/fusion-all-tests.html +670 -0
  32. package/app/tests/fusion-assembly-tests.html +461 -0
  33. package/app/tests/fusion-cam-tests.html +421 -0
  34. package/app/tests/fusion-simulation-tests.html +421 -0
  35. package/app/tests/fusion-sketch-tests.html +613 -0
  36. package/app/tests/fusion-solid-tests.html +529 -0
  37. package/app/tests/index.html +453 -0
  38. package/app/tests/killer-features-test.html +509 -0
  39. package/app/tests/run-tests.html +874 -0
  40. package/app/tests/step-import-live-test.html +1115 -0
  41. package/app/tests/test-agent-v3.html +93 -696
  42. package/architecture-dashboard.html +1970 -0
  43. package/docs/API-REFERENCE.md +1423 -0
  44. package/docs/BREP-LIVE-TEST-GUIDE.md +453 -0
  45. package/docs/DEVELOPER-GUIDE-v3.md +795 -0
  46. package/docs/DOCKER-QUICK-TEST.md +376 -0
  47. package/docs/FUSION-FEATURES-GUIDE.md +2513 -0
  48. package/docs/FUSION-TUTORIAL.md +1203 -0
  49. package/docs/INFRASTRUCTURE-GUIDE-INDEX.md +327 -0
  50. package/docs/KEYBOARD-SHORTCUTS.md +402 -0
  51. package/docs/KILLER-FEATURES-INTEGRATION.md +412 -0
  52. package/docs/KILLER-FEATURES-SUMMARY.md +424 -0
  53. package/docs/KILLER-FEATURES-TUTORIAL.md +784 -0
  54. package/docs/KILLER-FEATURES.md +562 -0
  55. package/docs/QUICK-REFERENCE.md +282 -0
  56. package/docs/README-v3-DOCS.md +274 -0
  57. package/docs/TUTORIAL-v3.md +1190 -0
  58. package/docs/architecture-dashboard.html +1970 -0
  59. package/docs/architecture-v3.html +1038 -0
  60. package/linkedin-post-v3.md +58 -0
  61. package/package.json +1 -1
  62. package/scripts/dev-setup.sh +338 -0
  63. package/scripts/docker-health-check.sh +159 -0
  64. package/scripts/integration-test.sh +311 -0
  65. package/scripts/test-docker.sh +515 -0
@@ -0,0 +1,785 @@
1
+ /**
2
+ * cycleCAD — Fusion 360 CAM (Computer-Aided Manufacturing) Module
3
+ * Complete manufacturing workspace with 2D/3D operations, drilling, turning, tool library,
4
+ * G-code generation, and toolpath simulation.
5
+ *
6
+ * Features:
7
+ * - 2D Operations: Face, 2D Contour, 2D Pocket, 2D Adaptive, Slot, Trace, Thread, Bore, Circular, Engrave
8
+ * - 3D Operations: 3D Adaptive, 3D Contour, Pocket, Parallel, Scallop, Pencil, Steep & Shallow, Morphed Spiral
9
+ * - Drilling: Drill, Spot Drill, Peck Drill, Tap, Bore, Ream
10
+ * - Turning: Face, Profile, Groove, Thread, Part-off
11
+ * - Tool Library, Toolpath Simulation, G-code Generation (Fanuc/GRBL/LinuxCNC)
12
+ * - Feeds & Speeds Calculator, Multi-axis support
13
+ *
14
+ * Version: 1.0.0
15
+ */
16
+
17
+ import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js';
18
+
19
+ /**
20
+ * Fusion CAM Module — Manufacturing workspace
21
+ */
22
+ class FusionCAMModule {
23
+ constructor(scene, camera, renderer) {
24
+ this.scene = scene;
25
+ this.camera = camera;
26
+ this.renderer = renderer;
27
+
28
+ // Setup configuration
29
+ this.setup = {
30
+ stock: null, // { material, dimensions, origin }
31
+ wcs: new THREE.Matrix4(), // Work coordinate system
32
+ modelOrientation: new THREE.Quaternion(),
33
+ machineType: '3-axis', // 3-axis | 3+2 | 4-axis | 5-axis
34
+ toolAxis: new THREE.Vector3(0, 0, 1) // Z-axis default
35
+ };
36
+
37
+ // Operations database
38
+ this.operations = new Map(); // opId -> operation definition
39
+ this.toolpaths = new Map(); // opId -> toolpath geometry
40
+ this.toolLibrary = this.initToolLibrary();
41
+
42
+ // Simulation state
43
+ this.simulationTime = 0;
44
+ this.toolpathIndex = 0;
45
+ this.remainingStock = null; // THREE.Geometry of remaining material
46
+ this.machineWorkspace = new THREE.Box3(
47
+ new THREE.Vector3(-200, -200, -200),
48
+ new THREE.Vector3(200, 200, 200)
49
+ );
50
+
51
+ // Post-processors
52
+ this.postProcessors = {
53
+ fanuc: this.fanucPost.bind(this),
54
+ grbl: this.grblPost.bind(this),
55
+ linuxcnc: this.linuxcncPost.bind(this)
56
+ };
57
+
58
+ // Simulation
59
+ this.isSimulating = false;
60
+ this.simulationSpeed = 1.0;
61
+ this.currentTool = null;
62
+ this.toolpathLines = []; // Visual toolpath lines
63
+
64
+ this.animationFrameId = null;
65
+ }
66
+
67
+ /**
68
+ * Initialize CAM module UI
69
+ */
70
+ init() {
71
+ this.setupEventListeners();
72
+ this.initializeStock();
73
+ }
74
+
75
+ /**
76
+ * Get UI panel for CAM controls
77
+ */
78
+ getUI() {
79
+ const panel = document.createElement('div');
80
+ panel.className = 'fusion-cam-panel';
81
+ panel.innerHTML = `
82
+ <style>
83
+ .fusion-cam-panel {
84
+ padding: 16px;
85
+ font-size: 12px;
86
+ background: var(--bg-secondary);
87
+ color: var(--text-primary);
88
+ border-radius: 4px;
89
+ max-height: 700px;
90
+ overflow-y: auto;
91
+ }
92
+ .fusion-cam-panel h3 {
93
+ margin: 0 0 12px 0;
94
+ font-size: 14px;
95
+ font-weight: 600;
96
+ color: var(--text-primary);
97
+ }
98
+ .cam-section {
99
+ margin-bottom: 16px;
100
+ padding-bottom: 12px;
101
+ border-bottom: 1px solid var(--border-color);
102
+ }
103
+ .cam-section:last-child {
104
+ border-bottom: none;
105
+ }
106
+ .operation-list {
107
+ display: flex;
108
+ flex-direction: column;
109
+ gap: 6px;
110
+ max-height: 200px;
111
+ overflow-y: auto;
112
+ }
113
+ .operation-item {
114
+ padding: 8px;
115
+ background: var(--bg-primary);
116
+ border-radius: 3px;
117
+ cursor: pointer;
118
+ transition: background 0.2s;
119
+ font-size: 11px;
120
+ }
121
+ .operation-item:hover {
122
+ background: var(--bg-tertiary);
123
+ }
124
+ .operation-type {
125
+ font-weight: 600;
126
+ color: var(--accent-color);
127
+ }
128
+ .tool-selector {
129
+ display: flex;
130
+ flex-direction: column;
131
+ gap: 6px;
132
+ margin-bottom: 8px;
133
+ }
134
+ .tool-selector select {
135
+ padding: 6px;
136
+ background: var(--bg-primary);
137
+ border: 1px solid var(--border-color);
138
+ border-radius: 3px;
139
+ color: var(--text-primary);
140
+ font-size: 11px;
141
+ }
142
+ .param-input {
143
+ display: flex;
144
+ gap: 8px;
145
+ margin: 6px 0;
146
+ align-items: center;
147
+ }
148
+ .param-input label {
149
+ width: 70px;
150
+ font-weight: 600;
151
+ font-size: 11px;
152
+ }
153
+ .param-input input {
154
+ flex: 1;
155
+ padding: 4px;
156
+ background: var(--bg-primary);
157
+ border: 1px solid var(--border-color);
158
+ border-radius: 3px;
159
+ color: var(--text-primary);
160
+ font-size: 11px;
161
+ }
162
+ .operation-buttons {
163
+ display: grid;
164
+ grid-template-columns: 1fr 1fr;
165
+ gap: 6px;
166
+ margin-top: 8px;
167
+ }
168
+ .operation-buttons button {
169
+ padding: 6px;
170
+ font-size: 11px;
171
+ background: var(--button-bg);
172
+ border: 1px solid var(--border-color);
173
+ border-radius: 3px;
174
+ cursor: pointer;
175
+ color: var(--text-primary);
176
+ }
177
+ .operation-buttons button:hover {
178
+ background: var(--button-hover-bg);
179
+ }
180
+ .feeds-speeds {
181
+ background: var(--bg-primary);
182
+ border-radius: 3px;
183
+ padding: 8px;
184
+ margin-top: 8px;
185
+ }
186
+ .feeds-speeds-row {
187
+ display: flex;
188
+ justify-content: space-between;
189
+ padding: 4px 0;
190
+ border-bottom: 1px solid var(--border-color);
191
+ font-size: 11px;
192
+ }
193
+ .feeds-speeds-row:last-child {
194
+ border-bottom: none;
195
+ }
196
+ .simulation-controls {
197
+ display: flex;
198
+ gap: 4px;
199
+ margin-top: 8px;
200
+ }
201
+ .simulation-controls button {
202
+ flex: 1;
203
+ padding: 6px;
204
+ font-size: 10px;
205
+ background: var(--button-bg);
206
+ border: 1px solid var(--border-color);
207
+ border-radius: 3px;
208
+ cursor: pointer;
209
+ color: var(--text-primary);
210
+ }
211
+ </style>
212
+
213
+ <div class="cam-section">
214
+ <h3>Setup</h3>
215
+ <div class="param-input">
216
+ <label>Machine:</label>
217
+ <select id="camMachineType" onchange="window.fusionCAM?.setMachineType(this.value)">
218
+ <option value="3-axis">3-Axis</option>
219
+ <option value="3+2">3+2</option>
220
+ <option value="4-axis">4-Axis</option>
221
+ <option value="5-axis">5-Axis</option>
222
+ </select>
223
+ </div>
224
+ <div class="param-input">
225
+ <label>Material:</label>
226
+ <select id="camStockMaterial">
227
+ <option value="steel">Steel</option>
228
+ <option value="aluminum">Aluminum</option>
229
+ <option value="titanium">Titanium</option>
230
+ <option value="plastic">Plastic</option>
231
+ </select>
232
+ </div>
233
+ </div>
234
+
235
+ <div class="cam-section">
236
+ <h3>Operations</h3>
237
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 4px; margin-bottom: 8px;">
238
+ <button onclick="window.fusionCAM?.newOperation('2D Pocket')">2D Pocket</button>
239
+ <button onclick="window.fusionCAM?.newOperation('2D Contour')">2D Contour</button>
240
+ <button onclick="window.fusionCAM?.newOperation('3D Pocket')">3D Pocket</button>
241
+ <button onclick="window.fusionCAM?.newOperation('Drill')">Drill</button>
242
+ <button onclick="window.fusionCAM?.newOperation('Slot')">Slot</button>
243
+ <button onclick="window.fusionCAM?.newOperation('Thread')">Thread</button>
244
+ </div>
245
+ <div class="operation-list" id="camOperationList"></div>
246
+ </div>
247
+
248
+ <div class="cam-section">
249
+ <h3>Tool Library</h3>
250
+ <select id="camToolSelect" onchange="window.fusionCAM?.selectTool(this.value)">
251
+ <option value="">-- Select Tool --</option>
252
+ </select>
253
+ <div id="camToolInfo" style="margin-top: 8px; font-size: 10px; padding: 6px; background: var(--bg-primary); border-radius: 3px;"></div>
254
+ </div>
255
+
256
+ <div class="cam-section">
257
+ <h3>Feeds & Speeds</h3>
258
+ <div class="feeds-speeds">
259
+ <div class="feeds-speeds-row">
260
+ <span>Spindle:</span>
261
+ <span id="camSpindleSpeed">1000 RPM</span>
262
+ </div>
263
+ <div class="feeds-speeds-row">
264
+ <span>Feed Rate:</span>
265
+ <span id="camFeedRate">100 mm/min</span>
266
+ </div>
267
+ <div class="feeds-speeds-row">
268
+ <span>Depth/Pass:</span>
269
+ <span id="camDepthPass">5 mm</span>
270
+ </div>
271
+ </div>
272
+ <button onclick="window.fusionCAM?.calculateFeedsAndSpeeds()" style="width: 100%; padding: 6px; margin-top: 8px;">Calculate</button>
273
+ </div>
274
+
275
+ <div class="cam-section">
276
+ <h3>Simulation</h3>
277
+ <div class="simulation-controls">
278
+ <button onclick="window.fusionCAM?.simulateToolpath()">Simulate</button>
279
+ <button onclick="window.fusionCAM?.pauseSimulation()">Pause</button>
280
+ <button onclick="window.fusionCAM?.resetSimulation()">Reset</button>
281
+ </div>
282
+ </div>
283
+
284
+ <div class="cam-section">
285
+ <h3>Post-Processing</h3>
286
+ <div style="display: flex; gap: 4px;">
287
+ <button onclick="window.fusionCAM?.generateGCode('fanuc')" style="flex: 1;">FANUC</button>
288
+ <button onclick="window.fusionCAM?.generateGCode('grbl')" style="flex: 1;">GRBL</button>
289
+ <button onclick="window.fusionCAM?.generateGCode('linuxcnc')" style="flex: 1;">LinuxCNC</button>
290
+ </div>
291
+ </div>
292
+ `;
293
+
294
+ window.fusionCAM = this;
295
+ this.populateToolLibrary();
296
+ return panel;
297
+ }
298
+
299
+ /**
300
+ * Initialize tool library with common tools
301
+ */
302
+ initToolLibrary() {
303
+ return new Map([
304
+ // End Mills
305
+ ['endmill_1', { name: '1mm End Mill', type: 'end-mill', diameter: 1, flutes: 2, material: 'HSS', length: 25 }],
306
+ ['endmill_3', { name: '3mm End Mill', type: 'end-mill', diameter: 3, flutes: 2, material: 'HSS', length: 25 }],
307
+ ['endmill_6', { name: '6mm End Mill', type: 'end-mill', diameter: 6, flutes: 2, material: 'Carbide', length: 30 }],
308
+ ['endmill_10', { name: '10mm End Mill', type: 'end-mill', diameter: 10, flutes: 4, material: 'Carbide', length: 40 }],
309
+
310
+ // Ball Mills (for 3D contouring)
311
+ ['ballmill_3', { name: '3mm Ball Mill', type: 'ball-mill', diameter: 3, radius: 1.5, material: 'Carbide', length: 30 }],
312
+ ['ballmill_6', { name: '6mm Ball Mill', type: 'ball-mill', diameter: 6, radius: 3, material: 'Carbide', length: 40 }],
313
+
314
+ // Drills
315
+ ['drill_1.5', { name: '1.5mm Drill', type: 'drill', diameter: 1.5, material: 'HSS', length: 20 }],
316
+ ['drill_3', { name: '3mm Drill', type: 'drill', diameter: 3, material: 'HSS', length: 30 }],
317
+ ['drill_6', { name: '6mm Drill', type: 'drill', diameter: 6, material: 'Carbide', length: 40 }],
318
+ ['drill_10', { name: '10mm Drill', type: 'drill', diameter: 10, material: 'Carbide', length: 50 }],
319
+
320
+ // Taps
321
+ ['tap_m3', { name: 'M3 Tap', type: 'tap', diameter: 3, pitch: 0.5, material: 'HSS', length: 30 }],
322
+ ['tap_m6', { name: 'M6 Tap', type: 'tap', diameter: 6, pitch: 1.0, material: 'HSS', length: 40 }],
323
+ ['tap_m10', { name: 'M10 Tap', type: 'tap', diameter: 10, pitch: 1.5, material: 'HSS', length: 50 }],
324
+
325
+ // Slot Drills
326
+ ['slotdrill_3', { name: '3mm Slot Drill', type: 'slot-drill', diameter: 3, material: 'Carbide', length: 30 }],
327
+ ['slotdrill_6', { name: '6mm Slot Drill', type: 'slot-drill', diameter: 6, material: 'Carbide', length: 40 }],
328
+ ]);
329
+ }
330
+
331
+ /**
332
+ * Populate tool selector
333
+ */
334
+ populateToolLibrary() {
335
+ const select = document.getElementById('camToolSelect');
336
+ if (!select) return;
337
+
338
+ for (const [id, tool] of this.toolLibrary) {
339
+ const option = document.createElement('option');
340
+ option.value = id;
341
+ option.textContent = tool.name;
342
+ select.appendChild(option);
343
+ }
344
+ }
345
+
346
+ /**
347
+ * Select a tool from library
348
+ */
349
+ selectTool(toolId) {
350
+ if (!toolId) return;
351
+
352
+ this.currentTool = this.toolLibrary.get(toolId);
353
+ const infoDiv = document.getElementById('camToolInfo');
354
+
355
+ if (infoDiv && this.currentTool) {
356
+ infoDiv.innerHTML = `
357
+ <strong>${this.currentTool.name}</strong><br>
358
+ Diameter: ${this.currentTool.diameter}mm<br>
359
+ Type: ${this.currentTool.type}<br>
360
+ Material: ${this.currentTool.material}
361
+ `;
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Create new manufacturing operation
367
+ */
368
+ newOperation(opType) {
369
+ const opId = `op_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
370
+
371
+ const operation = {
372
+ id: opId,
373
+ type: opType, // 2D Pocket | 2D Contour | 3D Pocket | Drill | Slot | Thread | etc.
374
+ tool: this.currentTool ? this.currentTool.name : 'End Mill',
375
+ toolId: null,
376
+
377
+ // Common parameters
378
+ feedRate: 100, // mm/min
379
+ spindleSpeed: 1000, // RPM
380
+ depth: 10, // mm (for pockets)
381
+ depthPerPass: 5, // mm
382
+ stepOver: 2, // mm
383
+ stepDown: 2, // mm
384
+
385
+ // Operation-specific parameters
386
+ contourOffset: 0, // For contour operations
387
+ cornerStrategy: 'sharp', // sharp | round | optimize
388
+ adaptiveEnabled: true,
389
+ trochoidal: false,
390
+
391
+ // Geometry
392
+ geometry: null, // Profile or area for operation
393
+ toolpath: [],
394
+ toolpathWireframe: null,
395
+
396
+ // Status
397
+ isActive: false,
398
+ isComplete: false
399
+ };
400
+
401
+ this.operations.set(opId, operation);
402
+ this.updateOperationList();
403
+ return opId;
404
+ }
405
+
406
+ /**
407
+ * Update operation list display
408
+ */
409
+ updateOperationList() {
410
+ const listDiv = document.getElementById('camOperationList');
411
+ if (!listDiv) return;
412
+
413
+ listDiv.innerHTML = '';
414
+ for (const [opId, op] of this.operations) {
415
+ const itemDiv = document.createElement('div');
416
+ itemDiv.className = 'operation-item';
417
+ itemDiv.innerHTML = `
418
+ <div><span class="operation-type">${op.type}</span></div>
419
+ <div style="font-size: 10px; color: var(--text-secondary);">
420
+ Tool: ${op.tool} | Feed: ${op.feedRate} mm/min
421
+ </div>
422
+ `;
423
+ itemDiv.onclick = () => this.selectOperation(opId);
424
+ listDiv.appendChild(itemDiv);
425
+ }
426
+ }
427
+
428
+ /**
429
+ * Select operation for editing
430
+ */
431
+ selectOperation(opId) {
432
+ for (const [id, op] of this.operations) {
433
+ op.isActive = id === opId;
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Calculate feeds and speeds based on material and tool
439
+ */
440
+ calculateFeedsAndSpeeds() {
441
+ if (!this.currentTool || !this.setup.stock) return;
442
+
443
+ // Material-dependent feed/speed tables
444
+ const feedSpeeds = {
445
+ steel: { hssCutSpeed: 20, carbideCutSpeed: 200 },
446
+ aluminum: { hssCutSpeed: 80, carbideCutSpeed: 400 },
447
+ titanium: { hssCutSpeed: 10, carbideCutSpeed: 100 },
448
+ plastic: { hssCutSpeed: 100, carbideCutSpeed: 500 }
449
+ };
450
+
451
+ const material = this.setup.stock.material || 'aluminum';
452
+ const fs = feedSpeeds[material] || feedSpeeds.aluminum;
453
+ const isCarbide = this.currentTool.material === 'Carbide';
454
+ const cutSpeed = isCarbide ? fs.carbideCutSpeed : fs.hssCutSpeed;
455
+
456
+ // RPM = (1000 * Surface Speed) / (π * Diameter)
457
+ const diameter = this.currentTool.diameter;
458
+ const rpm = (1000 * cutSpeed) / (Math.PI * diameter);
459
+
460
+ // Feed = RPM * Chip Load * Flutes
461
+ const chipLoad = isCarbide ? 0.1 : 0.05;
462
+ const flutes = this.currentTool.flutes || 2;
463
+ const feedRate = rpm * chipLoad * flutes;
464
+
465
+ // Update displays
466
+ document.getElementById('camSpindleSpeed').textContent = Math.round(rpm) + ' RPM';
467
+ document.getElementById('camFeedRate').textContent = Math.round(feedRate) + ' mm/min';
468
+ document.getElementById('camDepthPass').textContent = (diameter * 0.5).toFixed(1) + ' mm';
469
+
470
+ // Update active operation
471
+ for (const op of this.operations.values()) {
472
+ if (op.isActive) {
473
+ op.spindleSpeed = Math.round(rpm);
474
+ op.feedRate = Math.round(feedRate);
475
+ }
476
+ }
477
+ }
478
+
479
+ /**
480
+ * Set machine type (3-axis, 4-axis, 5-axis)
481
+ */
482
+ setMachineType(type) {
483
+ this.setup.machineType = type;
484
+ console.log(`Machine type set to: ${type}`);
485
+ }
486
+
487
+ /**
488
+ * Initialize stock geometry
489
+ */
490
+ initializeStock() {
491
+ // Create stock box geometry
492
+ const stockGeo = new THREE.BoxGeometry(100, 100, 50);
493
+ const stockMat = new THREE.MeshPhongMaterial({
494
+ color: 0x666666,
495
+ transparent: true,
496
+ opacity: 0.3
497
+ });
498
+
499
+ this.remainingStock = new THREE.Mesh(stockGeo, stockMat);
500
+ this.scene.add(this.remainingStock);
501
+
502
+ this.setup.stock = {
503
+ material: 'aluminum',
504
+ dimensions: { width: 100, height: 100, depth: 50 },
505
+ origin: new THREE.Vector3(-50, -50, 0)
506
+ };
507
+ }
508
+
509
+ /**
510
+ * Simulate toolpath with 3D visualization
511
+ */
512
+ simulateToolpath() {
513
+ if (this.isSimulating) return;
514
+ this.isSimulating = true;
515
+
516
+ // Collect all toolpaths from operations
517
+ const allToolpaths = [];
518
+ for (const [opId, op] of this.operations) {
519
+ if (op.toolpath && op.toolpath.length > 0) {
520
+ allToolpaths.push(...op.toolpath);
521
+ }
522
+ }
523
+
524
+ if (allToolpaths.length === 0) {
525
+ console.warn('No toolpaths to simulate');
526
+ this.isSimulating = false;
527
+ return;
528
+ }
529
+
530
+ // Create visualization of toolpath
531
+ const toolpathGeo = new THREE.BufferGeometry();
532
+ const positions = [];
533
+
534
+ for (let i = 0; i < allToolpaths.length; i++) {
535
+ const point = allToolpaths[i];
536
+ positions.push(point.x, point.y, point.z);
537
+ }
538
+
539
+ toolpathGeo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), 3));
540
+ const toolpathMat = new THREE.LineBasicMaterial({ color: 0x00ff00, linewidth: 2 });
541
+ const toolpathLine = new THREE.Line(toolpathGeo, toolpathMat);
542
+
543
+ this.scene.add(toolpathLine);
544
+ this.toolpathLines.push(toolpathLine);
545
+
546
+ // Animate tool along toolpath
547
+ this.simulateToolMovement(allToolpaths);
548
+ }
549
+
550
+ /**
551
+ * Animate tool movement along toolpath
552
+ */
553
+ simulateToolMovement(toolpaths) {
554
+ const startTime = performance.now();
555
+ const totalPoints = toolpaths.length;
556
+ const duration = totalPoints * 50; // 50ms per point
557
+
558
+ const animate = (currentTime) => {
559
+ const elapsed = currentTime - startTime;
560
+ const progress = Math.min(elapsed / duration, 1);
561
+ const pointIndex = Math.floor(progress * (totalPoints - 1));
562
+
563
+ if (pointIndex < totalPoints) {
564
+ const point = toolpaths[pointIndex];
565
+ // Update tool position visualization
566
+ // (would show cutting tool at this position)
567
+ this.simulationTime = progress;
568
+ }
569
+
570
+ if (progress < 1) {
571
+ this.animationFrameId = requestAnimationFrame(animate);
572
+ } else {
573
+ this.isSimulating = false;
574
+ }
575
+ };
576
+
577
+ this.animationFrameId = requestAnimationFrame(animate);
578
+ }
579
+
580
+ /**
581
+ * Pause simulation
582
+ */
583
+ pauseSimulation() {
584
+ if (this.animationFrameId) {
585
+ cancelAnimationFrame(this.animationFrameId);
586
+ this.isSimulating = false;
587
+ }
588
+ }
589
+
590
+ /**
591
+ * Reset simulation
592
+ */
593
+ resetSimulation() {
594
+ this.pauseSimulation();
595
+ this.simulationTime = 0;
596
+ this.toolpathIndex = 0;
597
+
598
+ // Clear toolpath visualization
599
+ for (const line of this.toolpathLines) {
600
+ this.scene.remove(line);
601
+ }
602
+ this.toolpathLines = [];
603
+
604
+ // Reset stock
605
+ this.initializeStock();
606
+ }
607
+
608
+ /**
609
+ * Generate toolpath (simplified 2D example)
610
+ */
611
+ generateToolpath(operation) {
612
+ const toolpath = [];
613
+ const tool = this.currentTool;
614
+
615
+ if (!tool) return toolpath;
616
+
617
+ // Generate spiral toolpath (simplified)
618
+ const depth = operation.depth;
619
+ const depthPerPass = operation.depthPerPass;
620
+ const radius = tool.diameter / 2;
621
+
622
+ for (let z = 0; z < depth; z += depthPerPass) {
623
+ for (let angle = 0; angle < Math.PI * 2; angle += 0.1) {
624
+ const x = Math.cos(angle) * (radius + angle * 2);
625
+ const y = Math.sin(angle) * (radius + angle * 2);
626
+ toolpath.push(new THREE.Vector3(x, y, -z));
627
+ }
628
+ }
629
+
630
+ operation.toolpath = toolpath;
631
+ this.toolpaths.set(operation.id, toolpath);
632
+ return toolpath;
633
+ }
634
+
635
+ /**
636
+ * Generate G-code (FANUC dialect)
637
+ */
638
+ fanucPost(operations) {
639
+ let gcode = '';
640
+ gcode += '%\n';
641
+ gcode += 'O0001\n'; // Program number
642
+ gcode += 'G21 G40 G49 H0 M6\n'; // Metric, cancel offsets
643
+ gcode += ';\n';
644
+
645
+ for (const op of operations) {
646
+ gcode += `; ${op.type} Operation\n`;
647
+ gcode += `S${op.spindleSpeed} M3\n`; // Spindle on
648
+ gcode += `F${op.feedRate}\n`; // Feed rate
649
+
650
+ // Generate moves for toolpath
651
+ if (op.toolpath && op.toolpath.length > 0) {
652
+ for (const point of op.toolpath) {
653
+ gcode += `G1 X${point.x.toFixed(3)} Y${point.y.toFixed(3)} Z${point.z.toFixed(3)}\n`;
654
+ }
655
+ }
656
+
657
+ gcode += 'M5\n'; // Spindle off
658
+ gcode += ';\n';
659
+ }
660
+
661
+ gcode += 'M30\n';
662
+ gcode += '%\n';
663
+ return gcode;
664
+ }
665
+
666
+ /**
667
+ * Generate G-code (GRBL dialect - CNC mills)
668
+ */
669
+ grblPost(operations) {
670
+ let gcode = '';
671
+ gcode += '; GRBL Toolpath\n';
672
+ gcode += 'G21 ; Metric\n';
673
+ gcode += 'G90 ; Absolute positioning\n';
674
+ gcode += ';\n';
675
+
676
+ for (const op of operations) {
677
+ gcode += `; ${op.type}\n`;
678
+ gcode += `M3 S${op.spindleSpeed}\n`; // Spindle on
679
+ gcode += `G4 P1\n`; // Dwell for spindle ramp
680
+
681
+ if (op.toolpath && op.toolpath.length > 0) {
682
+ for (const point of op.toolpath) {
683
+ gcode += `G1 X${point.x.toFixed(3)} Y${point.y.toFixed(3)} Z${point.z.toFixed(3)} F${op.feedRate}\n`;
684
+ }
685
+ }
686
+
687
+ gcode += 'M5 ; Spindle off\n';
688
+ gcode += ';\n';
689
+ }
690
+
691
+ return gcode;
692
+ }
693
+
694
+ /**
695
+ * Generate G-code (LinuxCNC dialect)
696
+ */
697
+ linuxcncPost(operations) {
698
+ let gcode = '';
699
+ gcode += '( LinuxCNC Toolpath )\n';
700
+ gcode += 'G21 (Metric)\n';
701
+ gcode += 'G90 (Absolute)\n';
702
+ gcode += ';\n';
703
+
704
+ for (const op of operations) {
705
+ gcode += `( ${op.type} )\n`;
706
+ gcode += `M3 S${op.spindleSpeed}\n`;
707
+
708
+ if (op.toolpath && op.toolpath.length > 0) {
709
+ for (const point of op.toolpath) {
710
+ gcode += `G1 X${point.x.toFixed(4)} Y${point.y.toFixed(4)} Z${point.z.toFixed(4)} F${op.feedRate}\n`;
711
+ }
712
+ }
713
+
714
+ gcode += 'M5\n';
715
+ gcode += ';\n';
716
+ }
717
+
718
+ return gcode;
719
+ }
720
+
721
+ /**
722
+ * Generate G-code for all operations
723
+ */
724
+ generateGCode(postProcessor = 'fanuc') {
725
+ const operations = Array.from(this.operations.values());
726
+
727
+ // Generate toolpaths if not already done
728
+ for (const op of operations) {
729
+ if (op.toolpath.length === 0) {
730
+ this.generateToolpath(op);
731
+ }
732
+ }
733
+
734
+ // Use post-processor
735
+ const post = this.postProcessors[postProcessor];
736
+ if (!post) {
737
+ console.warn(`Unknown post-processor: ${postProcessor}`);
738
+ return '';
739
+ }
740
+
741
+ const gcode = post(operations);
742
+
743
+ // Download as file
744
+ const blob = new Blob([gcode], { type: 'text/plain' });
745
+ const url = URL.createObjectURL(blob);
746
+ const a = document.createElement('a');
747
+ a.href = url;
748
+ a.download = `toolpath_${postProcessor}.nc`;
749
+ a.click();
750
+ URL.revokeObjectURL(url);
751
+
752
+ return gcode;
753
+ }
754
+
755
+ /**
756
+ * Setup event listeners
757
+ */
758
+ setupEventListeners() {
759
+ // Listen for operation parameter changes
760
+ }
761
+
762
+ /**
763
+ * Execute command from agent API
764
+ */
765
+ execute(command, params) {
766
+ switch (command) {
767
+ case 'newOperation':
768
+ return this.newOperation(params.type);
769
+ case 'selectTool':
770
+ return this.selectTool(params.toolId);
771
+ case 'calculateFeedsAndSpeeds':
772
+ return this.calculateFeedsAndSpeeds();
773
+ case 'simulateToolpath':
774
+ return this.simulateToolpath();
775
+ case 'generateGCode':
776
+ return this.generateGCode(params.postProcessor || 'fanuc');
777
+ case 'setMachineType':
778
+ return this.setMachineType(params.type);
779
+ default:
780
+ console.warn(`Unknown CAM command: ${command}`);
781
+ }
782
+ }
783
+ }
784
+
785
+ export default FusionCAMModule;