cyclecad 3.9.14 → 3.9.18
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/app/index.html +558 -25
- package/app/js/explodeview-full.js +1141 -0
- package/app/js/modules/image-to-cad.js +1184 -0
- package/app/js/modules/openscad-engine.js +817 -0
- package/app/js/modules/parametric-sliders.js +1322 -0
- package/app/js/modules/scad-export.js +643 -0
- package/app/js/test-compat-shim.js +121 -0
- package/app/tests/killer-features-visual-test.html +71 -49
- package/package.json +1 -1
package/app/index.html
CHANGED
|
@@ -962,6 +962,47 @@
|
|
|
962
962
|
</div>
|
|
963
963
|
</div>
|
|
964
964
|
|
|
965
|
+
<div class="menu-item" style="color:#00ccff;">
|
|
966
|
+
ExplodeView
|
|
967
|
+
<div class="menu-dropdown" style="min-width:220px;">
|
|
968
|
+
<button class="menu-item-link" data-action="ev-load-model">Load Model (STL/OBJ/glTF/STEP)</button>
|
|
969
|
+
<div class="menu-separator"></div>
|
|
970
|
+
<button class="menu-item-link" data-action="ev-explode">Explode / Collapse</button>
|
|
971
|
+
<button class="menu-item-link" data-action="ev-section-cut">Section Cut (X/Y/Z)</button>
|
|
972
|
+
<button class="menu-item-link" data-action="ev-assembly-tree">Assembly Tree</button>
|
|
973
|
+
<div class="menu-separator"></div>
|
|
974
|
+
<button class="menu-item-link" data-action="ev-measure-distance">Measure Distance</button>
|
|
975
|
+
<button class="menu-item-link" data-action="ev-measure-angle">Measure Angle</button>
|
|
976
|
+
<button class="menu-item-link" data-action="ev-measure-volume">Measure Volume</button>
|
|
977
|
+
<button class="menu-item-link" data-action="ev-measure-area">Measure Surface Area</button>
|
|
978
|
+
<div class="menu-separator"></div>
|
|
979
|
+
<button class="menu-item-link" data-action="ev-wall-thickness">Wall Thickness Analysis</button>
|
|
980
|
+
<button class="menu-item-link" data-action="ev-draft-angle">Draft Angle Analysis</button>
|
|
981
|
+
<button class="menu-item-link" data-action="ev-interference">Interference Check</button>
|
|
982
|
+
<button class="menu-item-link" data-action="ev-mass-properties">Mass Properties / CoG</button>
|
|
983
|
+
<div class="menu-separator"></div>
|
|
984
|
+
<button class="menu-item-link" data-action="ev-bom">Bill of Materials (BOM)</button>
|
|
985
|
+
<button class="menu-item-link" data-action="ev-annotations">Add Annotation</button>
|
|
986
|
+
<button class="menu-item-link" data-action="ev-gdt">GD&T Annotation</button>
|
|
987
|
+
<div class="menu-separator"></div>
|
|
988
|
+
<button class="menu-item-link" data-action="ev-wireframe">Toggle Wireframe</button>
|
|
989
|
+
<button class="menu-item-link" data-action="ev-transparency">Toggle Transparency</button>
|
|
990
|
+
<button class="menu-item-link" data-action="ev-xray">Toggle X-Ray</button>
|
|
991
|
+
<div class="menu-separator"></div>
|
|
992
|
+
<button class="menu-item-link" data-action="ev-screenshot">Screenshot (2x Retina)</button>
|
|
993
|
+
<button class="menu-item-link" data-action="ev-export-stl">Export Part as STL</button>
|
|
994
|
+
<button class="menu-item-link" data-action="ev-export-obj">Export Scene as OBJ</button>
|
|
995
|
+
<div class="menu-separator"></div>
|
|
996
|
+
<button class="menu-item-link" data-action="ev-ai-analyze">AI Model Analysis</button>
|
|
997
|
+
<button class="menu-item-link" data-action="ev-ai-narrator">AI Part Narrator</button>
|
|
998
|
+
<button class="menu-item-link" data-action="ev-assembly-anim">Animated Assembly Guide</button>
|
|
999
|
+
<button class="menu-item-link" data-action="ev-search">Smart Part Search</button>
|
|
1000
|
+
<div class="menu-separator"></div>
|
|
1001
|
+
<button class="menu-item-link" data-action="ev-ar-mode">AR Mode (WebXR)</button>
|
|
1002
|
+
<button class="menu-item-link" data-action="ev-language">Language (EN/DE/FR/ES/IT/PT)</button>
|
|
1003
|
+
</div>
|
|
1004
|
+
</div>
|
|
1005
|
+
|
|
965
1006
|
<div class="menu-item">
|
|
966
1007
|
Tools
|
|
967
1008
|
<div class="menu-dropdown">
|
|
@@ -982,6 +1023,11 @@
|
|
|
982
1023
|
<button class="menu-item-link" data-action="tools-auto-assembly">Auto-Assemble Parts</button>
|
|
983
1024
|
<button class="menu-item-link" data-action="tools-parametric">Parametric from Example</button>
|
|
984
1025
|
<div class="menu-separator"></div>
|
|
1026
|
+
<button class="menu-item-link" data-action="tools-image-to-cad">Image-to-CAD (AI Vision)</button>
|
|
1027
|
+
<button class="menu-item-link" data-action="tools-openscad">OpenSCAD Editor</button>
|
|
1028
|
+
<button class="menu-item-link" data-action="tools-param-sliders">Parametric Sliders</button>
|
|
1029
|
+
<button class="menu-item-link" data-action="tools-scad-export">SCAD Export / Import</button>
|
|
1030
|
+
<div class="menu-separator"></div>
|
|
985
1031
|
<button class="menu-item-link" data-action="tools-settings">Settings...</button>
|
|
986
1032
|
</div>
|
|
987
1033
|
</div>
|
|
@@ -1011,6 +1057,7 @@
|
|
|
1011
1057
|
<button class="workspace-tab" data-workspace="manufacture">Manufacture</button>
|
|
1012
1058
|
<button class="workspace-tab" data-workspace="render">Render</button>
|
|
1013
1059
|
<button class="workspace-tab" data-workspace="animation">Animation</button>
|
|
1060
|
+
<button class="workspace-tab" data-workspace="explodeview" style="color:#00ccff;">ExplodeView</button>
|
|
1014
1061
|
|
|
1015
1062
|
<div class="toolbar-divider"></div>
|
|
1016
1063
|
|
|
@@ -1048,6 +1095,23 @@
|
|
|
1048
1095
|
<button class="toolbar-icon-btn" title="Explode" data-action="assembly-explode">💥</button>
|
|
1049
1096
|
</div>
|
|
1050
1097
|
|
|
1098
|
+
<!-- ExplodeView Workspace Tools -->
|
|
1099
|
+
<div id="explodeview-tools" class="workspace-tools" style="display: none;">
|
|
1100
|
+
<button class="toolbar-icon-btn" title="Load Model" data-action="ev-load-model">📂</button>
|
|
1101
|
+
<button class="toolbar-icon-btn" title="Explode" data-action="ev-explode">💥</button>
|
|
1102
|
+
<button class="toolbar-icon-btn" title="Section Cut" data-action="ev-section-cut">✂</button>
|
|
1103
|
+
<button class="toolbar-icon-btn" title="Measure" data-action="ev-measure-distance">📏</button>
|
|
1104
|
+
<button class="toolbar-icon-btn" title="Wall Thickness" data-action="ev-wall-thickness">🧱</button>
|
|
1105
|
+
<button class="toolbar-icon-btn" title="Mass Properties" data-action="ev-mass-properties">⚖</button>
|
|
1106
|
+
<button class="toolbar-icon-btn" title="BOM" data-action="ev-bom">📊</button>
|
|
1107
|
+
<button class="toolbar-icon-btn" title="Annotate" data-action="ev-annotations">📌</button>
|
|
1108
|
+
<button class="toolbar-icon-btn" title="Wireframe" data-action="ev-wireframe">🔲</button>
|
|
1109
|
+
<button class="toolbar-icon-btn" title="X-Ray" data-action="ev-xray">👁</button>
|
|
1110
|
+
<button class="toolbar-icon-btn" title="Screenshot" data-action="ev-screenshot">📸</button>
|
|
1111
|
+
<button class="toolbar-icon-btn" title="AI Analyze" data-action="ev-ai-analyze">🤖</button>
|
|
1112
|
+
<button class="toolbar-icon-btn" title="Search Parts" data-action="ev-search">🔍</button>
|
|
1113
|
+
</div>
|
|
1114
|
+
|
|
1051
1115
|
<!-- Drawing Workspace Tools -->
|
|
1052
1116
|
<div id="drawing-tools" class="workspace-tools" style="display: none;">
|
|
1053
1117
|
<button class="toolbar-icon-btn" title="New Drawing" data-action="drawing-new">📄</button>
|
|
@@ -1154,6 +1218,42 @@
|
|
|
1154
1218
|
<canvas id="viewport"></canvas>
|
|
1155
1219
|
<!-- ViewCube -->
|
|
1156
1220
|
<div id="viewcube" style="position:absolute;top:12px;right:12px;width:120px;height:120px;pointer-events:auto;z-index:100;"></div>
|
|
1221
|
+
<!-- Viewer Mode Toggle -->
|
|
1222
|
+
<button id="btn-viewer-mode-toggle" style="position:absolute;top:12px;left:12px;z-index:100;background:#0284C7;color:#fff;border:none;border-radius:6px;padding:8px 14px;font-size:12px;font-weight:600;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,0.4);display:flex;align-items:center;gap:6px;" title="Toggle Viewer Mode (ExplodeView)">
|
|
1223
|
+
<span style="font-size:16px;">👁</span> Viewer Mode
|
|
1224
|
+
</button>
|
|
1225
|
+
<!-- Viewer Mode Control Panel (hidden by default) -->
|
|
1226
|
+
<div id="viewer-panel" style="display:none;position:absolute;bottom:12px;left:12px;right:12px;z-index:90;background:rgba(30,30,30,0.95);border:1px solid #3e3e42;border-radius:8px;padding:12px 16px;box-shadow:0 4px 16px rgba(0,0,0,0.5);">
|
|
1227
|
+
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;">
|
|
1228
|
+
<span style="font-weight:600;font-size:13px;color:#e0e0e0;">ExplodeView Controls</span>
|
|
1229
|
+
<div style="display:flex;gap:6px;">
|
|
1230
|
+
<button id="viewer-load-file" style="background:#10b981;color:#fff;border:none;border-radius:4px;padding:4px 10px;font-size:11px;cursor:pointer;font-weight:600;">Load File</button>
|
|
1231
|
+
<button id="viewer-export-bom" style="background:#0284C7;color:#fff;border:none;border-radius:4px;padding:4px 10px;font-size:11px;cursor:pointer;font-weight:600;">Export BOM</button>
|
|
1232
|
+
<button id="viewer-screenshot" style="background:#6366f1;color:#fff;border:none;border-radius:4px;padding:4px 10px;font-size:11px;cursor:pointer;font-weight:600;">Screenshot</button>
|
|
1233
|
+
</div>
|
|
1234
|
+
</div>
|
|
1235
|
+
<!-- Explode Slider -->
|
|
1236
|
+
<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px;">
|
|
1237
|
+
<span style="font-size:11px;color:#a0a0a0;min-width:50px;">Explode:</span>
|
|
1238
|
+
<input id="viewer-explode-slider" type="range" min="0" max="100" value="0" style="flex:1;accent-color:#0284C7;height:6px;">
|
|
1239
|
+
<span id="viewer-explode-value" style="font-size:11px;color:#e0e0e0;min-width:30px;">0%</span>
|
|
1240
|
+
</div>
|
|
1241
|
+
<!-- Section Cut -->
|
|
1242
|
+
<div style="display:flex;align-items:center;gap:10px;margin-bottom:6px;">
|
|
1243
|
+
<span style="font-size:11px;color:#a0a0a0;min-width:50px;">Section:</span>
|
|
1244
|
+
<button id="viewer-section-x" style="background:#374151;color:#ef4444;border:1px solid #4b5563;border-radius:4px;padding:2px 8px;font-size:11px;cursor:pointer;font-weight:600;">X</button>
|
|
1245
|
+
<button id="viewer-section-y" style="background:#374151;color:#10b981;border:1px solid #4b5563;border-radius:4px;padding:2px 8px;font-size:11px;cursor:pointer;font-weight:600;">Y</button>
|
|
1246
|
+
<button id="viewer-section-z" style="background:#374151;color:#3b82f6;border:1px solid #4b5563;border-radius:4px;padding:2px 8px;font-size:11px;cursor:pointer;font-weight:600;">Z</button>
|
|
1247
|
+
<button id="viewer-section-off" style="background:#374151;color:#a0a0a0;border:1px solid #4b5563;border-radius:4px;padding:2px 8px;font-size:11px;cursor:pointer;">Off</button>
|
|
1248
|
+
<input id="viewer-section-slider" type="range" min="-100" max="100" value="0" style="flex:1;accent-color:#f59e0b;height:6px;">
|
|
1249
|
+
</div>
|
|
1250
|
+
<!-- Part Info -->
|
|
1251
|
+
<div id="viewer-part-info" style="font-size:11px;color:#888;margin-top:4px;">
|
|
1252
|
+
<span id="viewer-parts-count">No model loaded</span> · <span id="viewer-selected-part">Select a part to inspect</span>
|
|
1253
|
+
</div>
|
|
1254
|
+
<!-- Hidden file input -->
|
|
1255
|
+
<input type="file" id="viewer-file-input" accept=".stl,.obj,.gltf,.glb,.step,.stp" style="display:none;" multiple>
|
|
1256
|
+
</div>
|
|
1157
1257
|
</div>
|
|
1158
1258
|
|
|
1159
1259
|
<!-- Right Panel (Properties) -->
|
|
@@ -1336,13 +1436,15 @@ window._dismissSplash = function(action) {
|
|
|
1336
1436
|
<div style="background:#252526;border:1px solid #3c3c3c;border-radius:12px;padding:40px 48px;max-width:560px;width:90%;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,0.5);">
|
|
1337
1437
|
<div style="font-size:36px;margin-bottom:4px;"><span style="color:#0284C7;font-weight:700;">cycle</span><span style="color:#e0e0e0;font-weight:300;">CAD</span></div>
|
|
1338
1438
|
<div style="color:#888;font-size:13px;margin-bottom:28px;">Agent-First Parametric 3D CAD Modeler</div>
|
|
1339
|
-
<div style="display:grid;grid-template-columns:1fr 1fr;gap:
|
|
1340
|
-
<button id="splash-sketch" style="background:#0284C7;color:#fff;border:none;border-radius:8px;padding:
|
|
1341
|
-
<button id="splash-
|
|
1342
|
-
<button id="splash-
|
|
1343
|
-
<button id="splash-
|
|
1439
|
+
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;margin-bottom:20px;">
|
|
1440
|
+
<button id="splash-sketch" style="background:#0284C7;color:#fff;border:none;border-radius:8px;padding:16px 12px;cursor:pointer;font-size:13px;font-weight:600;"><div style="font-size:22px;margin-bottom:4px;">✏️</div>New Sketch<div style="font-size:10px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:3px;">2D sketch</div></button>
|
|
1441
|
+
<button id="splash-textcad" style="background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:16px 12px;cursor:pointer;font-size:13px;font-weight:600;"><div style="font-size:22px;margin-bottom:4px;">🤖</div>Text-to-CAD<div style="font-size:10px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:3px;">Describe in English</div></button>
|
|
1442
|
+
<button id="splash-imagecad" style="background:#374151;color:#fff;border:1px solid #10b981;border-radius:8px;padding:16px 12px;cursor:pointer;font-size:13px;font-weight:600;"><div style="font-size:22px;margin-bottom:4px;">📷</div>Image-to-CAD<div style="font-size:10px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:3px;">Photo or sketch</div></button>
|
|
1443
|
+
<button id="splash-import" style="background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:16px 12px;cursor:pointer;font-size:13px;font-weight:600;"><div style="font-size:22px;margin-bottom:4px;">📂</div>Open / Import<div style="font-size:10px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:3px;">STEP, STL, SCAD</div></button>
|
|
1444
|
+
<button id="splash-openscad" style="background:#374151;color:#fff;border:1px solid #f59e0b;border-radius:8px;padding:16px 12px;cursor:pointer;font-size:13px;font-weight:600;"><div style="font-size:22px;margin-bottom:4px;">💻</div>OpenSCAD<div style="font-size:10px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:3px;">Code editor + WASM</div></button>
|
|
1445
|
+
<button id="splash-inventor" style="background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:16px 12px;cursor:pointer;font-size:13px;font-weight:600;"><div style="font-size:22px;margin-bottom:4px;">🏭</div>Inventor Project<div style="font-size:10px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:3px;">.ipj / .ipt / .iam</div></button>
|
|
1344
1446
|
</div>
|
|
1345
|
-
<div style="color:#666;font-size:11px;">
|
|
1447
|
+
<div style="color:#666;font-size:11px;">16 killer features · 50 modules · <a href="https://github.com/vvlars-cmd/cyclecad" target="_blank" style="color:#0284C7;text-decoration:none;">GitHub</a></div>
|
|
1346
1448
|
</div>
|
|
1347
1449
|
</div>
|
|
1348
1450
|
<script>
|
|
@@ -1350,7 +1452,9 @@ window._dismissSplash = function(action) {
|
|
|
1350
1452
|
function dismiss() { document.getElementById("welcome-panel").style.display = "none"; }
|
|
1351
1453
|
document.getElementById("splash-sketch").addEventListener("click", dismiss);
|
|
1352
1454
|
document.getElementById("splash-import").addEventListener("click", dismiss);
|
|
1353
|
-
document.getElementById("splash-textcad").addEventListener("click", dismiss);
|
|
1455
|
+
document.getElementById("splash-textcad").addEventListener("click", function() { dismiss(); setTimeout(() => { if (window.app) window.app.handleMenuAction('tools-text-to-cad'); }, 100); });
|
|
1456
|
+
document.getElementById("splash-imagecad").addEventListener("click", function() { dismiss(); setTimeout(() => { if (window.app) window.app.handleMenuAction('tools-image-to-cad'); }, 100); });
|
|
1457
|
+
document.getElementById("splash-openscad").addEventListener("click", function() { dismiss(); setTimeout(() => { if (window.app) window.app.handleMenuAction('tools-openscad'); }, 100); });
|
|
1354
1458
|
document.getElementById("splash-inventor").addEventListener("click", dismiss);
|
|
1355
1459
|
})();
|
|
1356
1460
|
</script>
|
|
@@ -1425,30 +1529,40 @@ window._dismissSplash = function(action) {
|
|
|
1425
1529
|
<div id="toast-container"></div>
|
|
1426
1530
|
|
|
1427
1531
|
<!-- Token Engine & Marketplace Scripts (IIFE-based) -->
|
|
1428
|
-
<script src="js/token-engine.js"></script>
|
|
1429
|
-
<script src="js/marketplace.js"></script>
|
|
1532
|
+
<script src="/app/js/token-engine.js"></script>
|
|
1533
|
+
<script type="module" src="/app/js/marketplace.js"></script>
|
|
1430
1534
|
|
|
1431
1535
|
<!-- Killer Feature Modules -->
|
|
1432
|
-
<script src="js/modules/text-to-cad.js"></script>
|
|
1433
|
-
<script src="js/modules/photo-to-cad.js"></script>
|
|
1434
|
-
<script src="js/modules/manufacturability.js"></script>
|
|
1435
|
-
<script src="js/modules/generative-design.js"></script>
|
|
1436
|
-
<script src="js/modules/multi-physics.js"></script>
|
|
1437
|
-
<script src="js/modules/smart-parts.js"></script>
|
|
1438
|
-
<script src="js/modules/smart-assembly.js"></script>
|
|
1439
|
-
<script src="js/modules/digital-twin.js"></script>
|
|
1440
|
-
<script src="js/modules/machine-control.js"></script>
|
|
1441
|
-
<script src="js/modules/engineering-notebook.js"></script>
|
|
1442
|
-
<script src="js/modules/auto-assembly.js"></script>
|
|
1443
|
-
<script src="js/modules/parametric-from-example.js"></script>
|
|
1536
|
+
<script src="/app/js/modules/text-to-cad.js"></script>
|
|
1537
|
+
<script src="/app/js/modules/photo-to-cad.js"></script>
|
|
1538
|
+
<script src="/app/js/modules/manufacturability.js"></script>
|
|
1539
|
+
<script src="/app/js/modules/generative-design.js"></script>
|
|
1540
|
+
<script src="/app/js/modules/multi-physics.js"></script>
|
|
1541
|
+
<script src="/app/js/modules/smart-parts.js"></script>
|
|
1542
|
+
<script src="/app/js/modules/smart-assembly.js"></script>
|
|
1543
|
+
<script src="/app/js/modules/digital-twin.js"></script>
|
|
1544
|
+
<script src="/app/js/modules/machine-control.js"></script>
|
|
1545
|
+
<script src="/app/js/modules/engineering-notebook.js"></script>
|
|
1546
|
+
<script src="/app/js/modules/auto-assembly.js"></script>
|
|
1547
|
+
<script src="/app/js/modules/parametric-from-example.js"></script>
|
|
1548
|
+
|
|
1549
|
+
<!-- CADAM-Beating Modules -->
|
|
1550
|
+
<script src="/app/js/modules/image-to-cad.js"></script>
|
|
1551
|
+
<script src="/app/js/modules/openscad-engine.js"></script>
|
|
1552
|
+
<script src="/app/js/modules/parametric-sliders.js"></script>
|
|
1553
|
+
<script src="/app/js/modules/scad-export.js"></script>
|
|
1554
|
+
|
|
1555
|
+
<!-- Test compatibility shim — bridges method-name mismatches between tests and modules -->
|
|
1556
|
+
<script src="/app/js/test-compat-shim.js"></script>
|
|
1444
1557
|
|
|
1445
1558
|
<script type="module">
|
|
1446
1559
|
// ===== Three.js Imports =====
|
|
1447
1560
|
import * as THREE from 'three';
|
|
1448
1561
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
1449
|
-
import {
|
|
1450
|
-
import {
|
|
1451
|
-
import {
|
|
1562
|
+
import { KillerFeatures } from '/app/js/killer-features.js';
|
|
1563
|
+
import { startSketch, endSketch, setTool, getEntities, clearSketch, entitiesToGeometry } from '/app/js/sketch.js';
|
|
1564
|
+
import { initViewerMode } from '/app/js/viewer-mode.js';
|
|
1565
|
+
import { initExplodeView } from '/app/js/explodeview-full.js';
|
|
1452
1566
|
|
|
1453
1567
|
// ===== Three.js Viewport Setup =====
|
|
1454
1568
|
const scene = new THREE.Scene();
|
|
@@ -1649,6 +1763,7 @@ window._dismissSplash = function(action) {
|
|
|
1649
1763
|
units: 'mm',
|
|
1650
1764
|
gridEnabled: true,
|
|
1651
1765
|
snapEnabled: true,
|
|
1766
|
+
featureTree: [],
|
|
1652
1767
|
};
|
|
1653
1768
|
|
|
1654
1769
|
// ===== DOM Queries =====
|
|
@@ -1680,7 +1795,7 @@ window._dismissSplash = function(action) {
|
|
|
1680
1795
|
const timelineContent = document.getElementById('timeline-content');
|
|
1681
1796
|
|
|
1682
1797
|
// ===== Menu Actions Handler =====
|
|
1683
|
-
function handleMenuAction(action) {
|
|
1798
|
+
async function handleMenuAction(action) {
|
|
1684
1799
|
switch (action) {
|
|
1685
1800
|
case 'file-new':
|
|
1686
1801
|
showToast('New project created', 'success');
|
|
@@ -1923,6 +2038,265 @@ window._dismissSplash = function(action) {
|
|
|
1923
2038
|
if (db12) { db12.innerHTML = ''; db12.appendChild(window.CycleCAD.ParametricFromExample.getUI()); db12.style.maxHeight = '500px'; db12.style.overflow = 'auto'; }
|
|
1924
2039
|
} else { showToast('ParametricFromExample module not loaded', 'error'); }
|
|
1925
2040
|
break;
|
|
2041
|
+
case 'tools-image-to-cad':
|
|
2042
|
+
if (window.CycleCAD && window.CycleCAD.ImageToCAD) {
|
|
2043
|
+
showDialog('Image-to-CAD (AI Vision)', '');
|
|
2044
|
+
const dbImg = document.getElementById('dialog-body');
|
|
2045
|
+
if (dbImg) { dbImg.innerHTML = ''; dbImg.appendChild(window.CycleCAD.ImageToCAD.getUI()); dbImg.style.maxHeight = '600px'; dbImg.style.overflow = 'auto'; }
|
|
2046
|
+
} else { showToast('ImageToCAD module not loaded', 'error'); }
|
|
2047
|
+
break;
|
|
2048
|
+
case 'tools-openscad':
|
|
2049
|
+
if (window.CycleCAD && window.CycleCAD.OpenSCADEngine) {
|
|
2050
|
+
showDialog('OpenSCAD Editor', '');
|
|
2051
|
+
const dbScad = document.getElementById('dialog-body');
|
|
2052
|
+
if (dbScad) { dbScad.innerHTML = ''; dbScad.appendChild(window.CycleCAD.OpenSCADEngine.getUI()); dbScad.style.maxHeight = '600px'; dbScad.style.overflow = 'auto'; }
|
|
2053
|
+
} else { showToast('OpenSCADEngine module not loaded', 'error'); }
|
|
2054
|
+
break;
|
|
2055
|
+
case 'tools-param-sliders':
|
|
2056
|
+
if (window.CycleCAD && window.CycleCAD.ParametricSliders) {
|
|
2057
|
+
showDialog('Parametric Sliders', '');
|
|
2058
|
+
const dbSlider = document.getElementById('dialog-body');
|
|
2059
|
+
if (dbSlider) { dbSlider.innerHTML = ''; dbSlider.appendChild(window.CycleCAD.ParametricSliders.getUI()); dbSlider.style.maxHeight = '600px'; dbSlider.style.overflow = 'auto'; }
|
|
2060
|
+
} else { showToast('ParametricSliders module not loaded', 'error'); }
|
|
2061
|
+
break;
|
|
2062
|
+
case 'tools-scad-export':
|
|
2063
|
+
if (window.CycleCAD && window.CycleCAD.SCADExport) {
|
|
2064
|
+
showDialog('SCAD Export / Import', '');
|
|
2065
|
+
const dbExp = document.getElementById('dialog-body');
|
|
2066
|
+
if (dbExp) { dbExp.innerHTML = ''; dbExp.appendChild(window.CycleCAD.SCADExport.getUI()); dbExp.style.maxHeight = '600px'; dbExp.style.overflow = 'auto'; }
|
|
2067
|
+
} else { showToast('SCADExport module not loaded', 'error'); }
|
|
2068
|
+
break;
|
|
2069
|
+
// ===== ExplodeView Menu Actions =====
|
|
2070
|
+
case 'ev-load-model': {
|
|
2071
|
+
const input = document.createElement('input');
|
|
2072
|
+
input.type = 'file';
|
|
2073
|
+
input.accept = '.stl,.obj,.gltf,.glb,.step,.stp';
|
|
2074
|
+
input.addEventListener('change', async (e) => {
|
|
2075
|
+
if (e.target.files[0] && window.app.explodeView) {
|
|
2076
|
+
showToast('Loading ' + e.target.files[0].name + '...', 'info');
|
|
2077
|
+
const ext = e.target.files[0].name.split('.').pop().toLowerCase();
|
|
2078
|
+
if (ext === 'step' || ext === 'stp') {
|
|
2079
|
+
await window.app.explodeView.loadSTEP(e.target.files[0]);
|
|
2080
|
+
} else {
|
|
2081
|
+
await window.app.explodeView.loadModel(e.target.files[0]);
|
|
2082
|
+
}
|
|
2083
|
+
showToast('Model loaded', 'success');
|
|
2084
|
+
}
|
|
2085
|
+
});
|
|
2086
|
+
input.click();
|
|
2087
|
+
break;
|
|
2088
|
+
}
|
|
2089
|
+
case 'ev-explode': {
|
|
2090
|
+
showDialog('Explode / Collapse', `
|
|
2091
|
+
<div style="padding:16px;">
|
|
2092
|
+
<label style="display:block;margin-bottom:8px;font-weight:bold;">Explode Amount</label>
|
|
2093
|
+
<input type="range" id="ev-explode-range" min="0" max="100" value="0" style="width:100%;accent-color:#00ccff;">
|
|
2094
|
+
<div id="ev-explode-val" style="text-align:center;margin-top:8px;font-size:18px;color:#00ccff;">0%</div>
|
|
2095
|
+
</div>
|
|
2096
|
+
`);
|
|
2097
|
+
const slider = document.getElementById('ev-explode-range');
|
|
2098
|
+
const val = document.getElementById('ev-explode-val');
|
|
2099
|
+
if (slider && window.app.explodeView) {
|
|
2100
|
+
slider.addEventListener('input', (e) => {
|
|
2101
|
+
const pct = parseInt(e.target.value);
|
|
2102
|
+
window.app.explodeView.explodeParts(pct / 100);
|
|
2103
|
+
val.textContent = pct + '%';
|
|
2104
|
+
});
|
|
2105
|
+
}
|
|
2106
|
+
break;
|
|
2107
|
+
}
|
|
2108
|
+
case 'ev-section-cut': {
|
|
2109
|
+
showDialog('Section Cut', `
|
|
2110
|
+
<div style="padding:16px;">
|
|
2111
|
+
<div style="display:flex;gap:8px;margin-bottom:12px;">
|
|
2112
|
+
<button id="ev-sec-x" style="flex:1;padding:8px;background:#374151;color:#fff;border:none;border-radius:4px;cursor:pointer;">X Axis</button>
|
|
2113
|
+
<button id="ev-sec-y" style="flex:1;padding:8px;background:#374151;color:#fff;border:none;border-radius:4px;cursor:pointer;">Y Axis</button>
|
|
2114
|
+
<button id="ev-sec-z" style="flex:1;padding:8px;background:#374151;color:#fff;border:none;border-radius:4px;cursor:pointer;">Z Axis</button>
|
|
2115
|
+
</div>
|
|
2116
|
+
<input type="range" id="ev-sec-pos" min="-200" max="200" value="0" style="width:100%;accent-color:#00ccff;">
|
|
2117
|
+
<button id="ev-sec-off" style="margin-top:12px;padding:8px 16px;background:#ef4444;color:#fff;border:none;border-radius:4px;cursor:pointer;">Disable Section Cut</button>
|
|
2118
|
+
</div>
|
|
2119
|
+
`);
|
|
2120
|
+
let activeAxis = null;
|
|
2121
|
+
['x','y','z'].forEach(axis => {
|
|
2122
|
+
const btn = document.getElementById('ev-sec-' + axis);
|
|
2123
|
+
if (btn && window.app.explodeView) {
|
|
2124
|
+
btn.addEventListener('click', () => {
|
|
2125
|
+
activeAxis = axis;
|
|
2126
|
+
window.app.explodeView.setSectionCut(axis, 0, true);
|
|
2127
|
+
['x','y','z'].forEach(a => { document.getElementById('ev-sec-' + a).style.background = a === axis ? '#00ccff' : '#374151'; });
|
|
2128
|
+
});
|
|
2129
|
+
}
|
|
2130
|
+
});
|
|
2131
|
+
const posSlider = document.getElementById('ev-sec-pos');
|
|
2132
|
+
if (posSlider && window.app.explodeView) {
|
|
2133
|
+
posSlider.addEventListener('input', (e) => {
|
|
2134
|
+
if (activeAxis) window.app.explodeView.setSectionCut(activeAxis, parseInt(e.target.value), true);
|
|
2135
|
+
});
|
|
2136
|
+
}
|
|
2137
|
+
const offBtn = document.getElementById('ev-sec-off');
|
|
2138
|
+
if (offBtn && window.app.explodeView) {
|
|
2139
|
+
offBtn.addEventListener('click', () => {
|
|
2140
|
+
['x','y','z'].forEach(a => window.app.explodeView.setSectionCut(a, 0, false));
|
|
2141
|
+
['x','y','z'].forEach(a => { document.getElementById('ev-sec-' + a).style.background = '#374151'; });
|
|
2142
|
+
activeAxis = null;
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2145
|
+
break;
|
|
2146
|
+
}
|
|
2147
|
+
case 'ev-assembly-tree':
|
|
2148
|
+
if (window.app.explodeView) {
|
|
2149
|
+
const tree = window.app.explodeView.getAssemblyTree();
|
|
2150
|
+
showDialog('Assembly Tree', `<pre style="padding:16px;max-height:400px;overflow:auto;font-size:12px;color:#e0e0e0;">${tree ? JSON.stringify(tree, null, 2) : 'No assembly loaded. Load a model first.'}</pre>`);
|
|
2151
|
+
}
|
|
2152
|
+
break;
|
|
2153
|
+
case 'ev-measure-distance':
|
|
2154
|
+
showToast('Click two points in the viewport to measure distance', 'info');
|
|
2155
|
+
if (window.app.explodeView) window.app.explodeView.startMeasureMode('distance');
|
|
2156
|
+
break;
|
|
2157
|
+
case 'ev-measure-angle':
|
|
2158
|
+
showToast('Click three points (start, center, end) to measure angle', 'info');
|
|
2159
|
+
if (window.app.explodeView) window.app.explodeView.startMeasureMode('angle');
|
|
2160
|
+
break;
|
|
2161
|
+
case 'ev-measure-volume':
|
|
2162
|
+
if (window.app.explodeView) {
|
|
2163
|
+
const vol = window.app.explodeView.computeVolumeOfSelected();
|
|
2164
|
+
showDialog('Volume', `<div style="padding:16px;font-size:16px;color:#00ccff;">${vol != null ? vol.toFixed(2) + ' mm³' : 'Select a part first'}</div>`);
|
|
2165
|
+
}
|
|
2166
|
+
break;
|
|
2167
|
+
case 'ev-measure-area':
|
|
2168
|
+
if (window.app.explodeView) {
|
|
2169
|
+
const area = window.app.explodeView.computeAreaOfSelected();
|
|
2170
|
+
showDialog('Surface Area', `<div style="padding:16px;font-size:16px;color:#00ccff;">${area != null ? area.toFixed(2) + ' mm²' : 'Select a part first'}</div>`);
|
|
2171
|
+
}
|
|
2172
|
+
break;
|
|
2173
|
+
case 'ev-wall-thickness':
|
|
2174
|
+
showToast('Analyzing wall thickness...', 'info');
|
|
2175
|
+
if (window.app.explodeView) window.app.explodeView.analyzeWallThicknessOfSelected();
|
|
2176
|
+
break;
|
|
2177
|
+
case 'ev-draft-angle':
|
|
2178
|
+
showToast('Analyzing draft angles...', 'info');
|
|
2179
|
+
if (window.app.explodeView) window.app.explodeView.analyzeDraftAngleOfSelected();
|
|
2180
|
+
break;
|
|
2181
|
+
case 'ev-interference':
|
|
2182
|
+
if (window.app.explodeView) {
|
|
2183
|
+
const results = window.app.explodeView.checkInterference();
|
|
2184
|
+
showDialog('Interference Check', `<div style="padding:16px;color:#e0e0e0;">${results.length > 0 ? results.map(r => `<div style="margin:4px 0;padding:8px;background:#2d2d30;border-radius:4px;border-left:3px solid #ef4444;">${r}</div>`).join('') : '<div style="color:#10b981;">No interference detected</div>'}</div>`);
|
|
2185
|
+
}
|
|
2186
|
+
break;
|
|
2187
|
+
case 'ev-mass-properties':
|
|
2188
|
+
if (window.app.explodeView) {
|
|
2189
|
+
const props = window.app.explodeView.getMassProperties();
|
|
2190
|
+
showDialog('Mass Properties', `<div style="padding:16px;font-family:monospace;font-size:13px;color:#e0e0e0;line-height:1.8;">
|
|
2191
|
+
<div>Total Mass: <span style="color:#00ccff;">${props.totalMass.toFixed(2)} kg</span></div>
|
|
2192
|
+
<div>Center of Gravity: <span style="color:#00ccff;">(${props.cog.x.toFixed(1)}, ${props.cog.y.toFixed(1)}, ${props.cog.z.toFixed(1)})</span></div>
|
|
2193
|
+
<div>Total Parts: <span style="color:#00ccff;">${props.partCount}</span></div>
|
|
2194
|
+
</div>`);
|
|
2195
|
+
}
|
|
2196
|
+
break;
|
|
2197
|
+
case 'ev-bom':
|
|
2198
|
+
if (window.app.explodeView) {
|
|
2199
|
+
window.app.explodeView.exportBOMcsv();
|
|
2200
|
+
showToast('BOM exported as CSV', 'success');
|
|
2201
|
+
}
|
|
2202
|
+
break;
|
|
2203
|
+
case 'ev-annotations':
|
|
2204
|
+
showToast('Click a point on the model to add an annotation', 'info');
|
|
2205
|
+
if (window.app.explodeView) window.app.explodeView.startAnnotationMode();
|
|
2206
|
+
break;
|
|
2207
|
+
case 'ev-gdt':
|
|
2208
|
+
showToast('Click a surface to add GD&T annotation', 'info');
|
|
2209
|
+
if (window.app.explodeView) window.app.explodeView.startGDTMode();
|
|
2210
|
+
break;
|
|
2211
|
+
case 'ev-wireframe':
|
|
2212
|
+
if (window.app.explodeView) {
|
|
2213
|
+
window.app.explodeView.toggleWireframe();
|
|
2214
|
+
showToast('Wireframe toggled', 'success');
|
|
2215
|
+
}
|
|
2216
|
+
break;
|
|
2217
|
+
case 'ev-transparency':
|
|
2218
|
+
if (window.app.explodeView) {
|
|
2219
|
+
window.app.explodeView.toggleTransparency();
|
|
2220
|
+
showToast('Transparency toggled', 'success');
|
|
2221
|
+
}
|
|
2222
|
+
break;
|
|
2223
|
+
case 'ev-xray':
|
|
2224
|
+
if (window.app.explodeView) {
|
|
2225
|
+
window.app.explodeView.toggleXray();
|
|
2226
|
+
showToast('X-Ray mode toggled', 'success');
|
|
2227
|
+
}
|
|
2228
|
+
break;
|
|
2229
|
+
case 'ev-screenshot':
|
|
2230
|
+
if (window.app.explodeView) {
|
|
2231
|
+
window.app.explodeView.captureScreenshot();
|
|
2232
|
+
showToast('Screenshot saved', 'success');
|
|
2233
|
+
}
|
|
2234
|
+
break;
|
|
2235
|
+
case 'ev-export-stl':
|
|
2236
|
+
if (window.app.explodeView) {
|
|
2237
|
+
window.app.explodeView.exportSTLSelected();
|
|
2238
|
+
showToast('STL exported', 'success');
|
|
2239
|
+
}
|
|
2240
|
+
break;
|
|
2241
|
+
case 'ev-export-obj':
|
|
2242
|
+
if (window.app.explodeView) {
|
|
2243
|
+
window.app.explodeView.exportOBJ();
|
|
2244
|
+
showToast('OBJ exported', 'success');
|
|
2245
|
+
}
|
|
2246
|
+
break;
|
|
2247
|
+
case 'ev-ai-analyze':
|
|
2248
|
+
showToast('AI analyzing model...', 'info');
|
|
2249
|
+
if (window.app.explodeView) {
|
|
2250
|
+
const analysis = await window.app.explodeView.aiAnalyzeModel();
|
|
2251
|
+
showDialog('AI Model Analysis', `<div style="padding:16px;color:#e0e0e0;line-height:1.6;max-height:400px;overflow:auto;">${analysis}</div>`);
|
|
2252
|
+
}
|
|
2253
|
+
break;
|
|
2254
|
+
case 'ev-ai-narrator':
|
|
2255
|
+
showToast('Select a part, then AI will describe its function', 'info');
|
|
2256
|
+
if (window.app.explodeView) {
|
|
2257
|
+
const narration = await window.app.explodeView.aiNarrateSelected();
|
|
2258
|
+
if (narration) showDialog('AI Part Narrator', `<div style="padding:16px;color:#e0e0e0;line-height:1.6;">${narration}</div>`);
|
|
2259
|
+
}
|
|
2260
|
+
break;
|
|
2261
|
+
case 'ev-assembly-anim':
|
|
2262
|
+
if (window.app.explodeView) {
|
|
2263
|
+
window.app.explodeView.playAssemblyAnimation();
|
|
2264
|
+
showToast('Playing assembly animation...', 'info');
|
|
2265
|
+
}
|
|
2266
|
+
break;
|
|
2267
|
+
case 'ev-search': {
|
|
2268
|
+
showDialog('Smart Part Search', `
|
|
2269
|
+
<div style="padding:16px;">
|
|
2270
|
+
<input type="text" id="ev-search-input" placeholder="Search parts (e.g. 'bolt', 'plate', 'shaft')..." style="width:100%;padding:10px;background:#1e1e1e;border:1px solid #3e3e42;color:#e0e0e0;border-radius:4px;font-size:14px;">
|
|
2271
|
+
<div id="ev-search-results" style="margin-top:12px;max-height:300px;overflow:auto;"></div>
|
|
2272
|
+
</div>
|
|
2273
|
+
`);
|
|
2274
|
+
const searchInput = document.getElementById('ev-search-input');
|
|
2275
|
+
if (searchInput && window.app.explodeView) {
|
|
2276
|
+
searchInput.addEventListener('input', (e) => {
|
|
2277
|
+
const results = window.app.explodeView.searchParts(e.target.value);
|
|
2278
|
+
const container = document.getElementById('ev-search-results');
|
|
2279
|
+
container.innerHTML = results.map(r => `<div style="padding:6px 10px;margin:4px 0;background:#2d2d30;border-radius:4px;cursor:pointer;color:#e0e0e0;" onclick="window.app.explodeView.highlightPart('${r.uuid}')">${r.name}</div>`).join('') || '<div style="color:#666;">No matches</div>';
|
|
2280
|
+
});
|
|
2281
|
+
}
|
|
2282
|
+
break;
|
|
2283
|
+
}
|
|
2284
|
+
case 'ev-ar-mode':
|
|
2285
|
+
if (window.app.explodeView) {
|
|
2286
|
+
window.app.explodeView.enterARMode();
|
|
2287
|
+
}
|
|
2288
|
+
break;
|
|
2289
|
+
case 'ev-language': {
|
|
2290
|
+
showDialog('Language', `
|
|
2291
|
+
<div style="padding:16px;display:grid;grid-template-columns:1fr 1fr;gap:8px;">
|
|
2292
|
+
${['en:English','de:Deutsch','fr:Français','es:Español','it:Italiano','pt:Português'].map(l => {
|
|
2293
|
+
const [code, name] = l.split(':');
|
|
2294
|
+
return `<button onclick="window.app.explodeView && window.app.explodeView.setLanguage('${code}'); document.getElementById('dialog-overlay').style.display='none';" style="padding:12px;background:#2d2d30;border:1px solid #3e3e42;color:#e0e0e0;border-radius:4px;cursor:pointer;font-size:14px;">${name}</button>`;
|
|
2295
|
+
}).join('')}
|
|
2296
|
+
</div>
|
|
2297
|
+
`);
|
|
2298
|
+
break;
|
|
2299
|
+
}
|
|
1926
2300
|
default:
|
|
1927
2301
|
showToast(`${action}`, 'success');
|
|
1928
2302
|
}
|
|
@@ -2313,6 +2687,165 @@ window._dismissSplash = function(action) {
|
|
|
2313
2687
|
window.CycleCAD.ParametricFromExample.init(scene);
|
|
2314
2688
|
console.log('[cycleCAD] Parametric from Example initialized');
|
|
2315
2689
|
}
|
|
2690
|
+
|
|
2691
|
+
// ===== CADAM-Beating Modules =====
|
|
2692
|
+
|
|
2693
|
+
// Initialize Image-to-CAD (AI Vision)
|
|
2694
|
+
if (window.CycleCAD && window.CycleCAD.ImageToCAD) {
|
|
2695
|
+
window.CycleCAD.ImageToCAD.init(scene, renderer, camera);
|
|
2696
|
+
console.log('[cycleCAD] Image-to-CAD (AI Vision) initialized');
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2699
|
+
// Initialize OpenSCAD Engine
|
|
2700
|
+
if (window.CycleCAD && window.CycleCAD.OpenSCADEngine) {
|
|
2701
|
+
window.CycleCAD.OpenSCADEngine.init(scene, renderer);
|
|
2702
|
+
console.log('[cycleCAD] OpenSCAD Engine initialized');
|
|
2703
|
+
}
|
|
2704
|
+
|
|
2705
|
+
// Initialize Parametric Sliders
|
|
2706
|
+
if (window.CycleCAD && window.CycleCAD.ParametricSliders) {
|
|
2707
|
+
window.CycleCAD.ParametricSliders.init(scene, renderer);
|
|
2708
|
+
console.log('[cycleCAD] Parametric Sliders initialized');
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
// Initialize SCAD Export/Import
|
|
2712
|
+
if (window.CycleCAD && window.CycleCAD.SCADExport) {
|
|
2713
|
+
window.CycleCAD.SCADExport.init(scene, camera);
|
|
2714
|
+
console.log('[cycleCAD] SCAD Export/Import initialized');
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
// ===== ExplodeView / Viewer Mode =====
|
|
2718
|
+
let viewerAPI = null;
|
|
2719
|
+
try {
|
|
2720
|
+
viewerAPI = initViewerMode({
|
|
2721
|
+
getScene: () => scene,
|
|
2722
|
+
getCamera: () => camera,
|
|
2723
|
+
getRenderer: () => renderer,
|
|
2724
|
+
getControls: () => controls
|
|
2725
|
+
});
|
|
2726
|
+
console.log('[cycleCAD] ExplodeView Viewer Mode initialized');
|
|
2727
|
+
} catch(e) {
|
|
2728
|
+
console.warn('[cycleCAD] Viewer Mode init failed:', e);
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
// Viewer Mode toggle button
|
|
2732
|
+
const vmToggle = document.getElementById('btn-viewer-mode-toggle');
|
|
2733
|
+
const vmPanel = document.getElementById('viewer-panel');
|
|
2734
|
+
let viewerActive = false;
|
|
2735
|
+
|
|
2736
|
+
if (vmToggle) {
|
|
2737
|
+
vmToggle.addEventListener('click', () => {
|
|
2738
|
+
viewerActive = !viewerActive;
|
|
2739
|
+
if (viewerAPI) viewerAPI.toggleViewerMode(viewerActive);
|
|
2740
|
+
vmPanel.style.display = viewerActive ? 'block' : 'none';
|
|
2741
|
+
vmToggle.style.background = viewerActive ? '#f59e0b' : '#0284C7';
|
|
2742
|
+
vmToggle.innerHTML = viewerActive
|
|
2743
|
+
? '<span style="font-size:16px;">✎</span> Edit Mode'
|
|
2744
|
+
: '<span style="font-size:16px;">👁</span> Viewer Mode';
|
|
2745
|
+
showToast(viewerActive ? 'Viewer Mode — Load a file to inspect' : 'Edit Mode', 'success');
|
|
2746
|
+
});
|
|
2747
|
+
}
|
|
2748
|
+
|
|
2749
|
+
// Explode slider
|
|
2750
|
+
const explodeSlider = document.getElementById('viewer-explode-slider');
|
|
2751
|
+
const explodeValue = document.getElementById('viewer-explode-value');
|
|
2752
|
+
if (explodeSlider && viewerAPI) {
|
|
2753
|
+
explodeSlider.addEventListener('input', (e) => {
|
|
2754
|
+
const val = parseInt(e.target.value) / 100;
|
|
2755
|
+
viewerAPI.explodeParts(val);
|
|
2756
|
+
explodeValue.textContent = e.target.value + '%';
|
|
2757
|
+
});
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
// Section cut buttons
|
|
2761
|
+
['x','y','z'].forEach(axis => {
|
|
2762
|
+
const btn = document.getElementById('viewer-section-' + axis);
|
|
2763
|
+
if (btn && viewerAPI) {
|
|
2764
|
+
btn.addEventListener('click', () => {
|
|
2765
|
+
viewerAPI.setSectionCut(axis, parseInt(document.getElementById('viewer-section-slider').value));
|
|
2766
|
+
btn.style.background = '#0284C7';
|
|
2767
|
+
['x','y','z'].filter(a => a !== axis).forEach(a => {
|
|
2768
|
+
document.getElementById('viewer-section-' + a).style.background = '#374151';
|
|
2769
|
+
});
|
|
2770
|
+
});
|
|
2771
|
+
}
|
|
2772
|
+
});
|
|
2773
|
+
const sectionOff = document.getElementById('viewer-section-off');
|
|
2774
|
+
if (sectionOff && viewerAPI) {
|
|
2775
|
+
sectionOff.addEventListener('click', () => {
|
|
2776
|
+
viewerAPI.setSectionCut(null);
|
|
2777
|
+
['x','y','z'].forEach(a => {
|
|
2778
|
+
document.getElementById('viewer-section-' + a).style.background = '#374151';
|
|
2779
|
+
});
|
|
2780
|
+
});
|
|
2781
|
+
}
|
|
2782
|
+
const sectionSlider = document.getElementById('viewer-section-slider');
|
|
2783
|
+
if (sectionSlider && viewerAPI) {
|
|
2784
|
+
sectionSlider.addEventListener('input', (e) => {
|
|
2785
|
+
const activeBtn = ['x','y','z'].find(a => document.getElementById('viewer-section-' + a).style.background === 'rgb(2, 132, 199)');
|
|
2786
|
+
if (activeBtn) viewerAPI.setSectionCut(activeBtn, parseInt(e.target.value));
|
|
2787
|
+
});
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2790
|
+
// Load file button
|
|
2791
|
+
const loadFileBtn = document.getElementById('viewer-load-file');
|
|
2792
|
+
const fileInput = document.getElementById('viewer-file-input');
|
|
2793
|
+
if (loadFileBtn && fileInput && viewerAPI) {
|
|
2794
|
+
loadFileBtn.addEventListener('click', () => fileInput.click());
|
|
2795
|
+
fileInput.addEventListener('change', async (e) => {
|
|
2796
|
+
const files = Array.from(e.target.files);
|
|
2797
|
+
for (const file of files) {
|
|
2798
|
+
showToast('Loading ' + file.name + '...', 'info');
|
|
2799
|
+
const url = URL.createObjectURL(file);
|
|
2800
|
+
await viewerAPI.loadFile(url, file.name);
|
|
2801
|
+
}
|
|
2802
|
+
const state = viewerAPI.getViewerState();
|
|
2803
|
+
document.getElementById('viewer-parts-count').textContent = state.allParts.length + ' parts loaded';
|
|
2804
|
+
showToast(files.length + ' file(s) loaded', 'success');
|
|
2805
|
+
fileInput.value = '';
|
|
2806
|
+
});
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2809
|
+
// Export BOM button
|
|
2810
|
+
const bomBtn = document.getElementById('viewer-export-bom');
|
|
2811
|
+
if (bomBtn && viewerAPI) {
|
|
2812
|
+
bomBtn.addEventListener('click', () => {
|
|
2813
|
+
viewerAPI.exportBOM();
|
|
2814
|
+
showToast('BOM exported as CSV', 'success');
|
|
2815
|
+
});
|
|
2816
|
+
}
|
|
2817
|
+
|
|
2818
|
+
// Screenshot button
|
|
2819
|
+
const ssBtn = document.getElementById('viewer-screenshot');
|
|
2820
|
+
if (ssBtn) {
|
|
2821
|
+
ssBtn.addEventListener('click', () => {
|
|
2822
|
+
renderer.render(scene, camera);
|
|
2823
|
+
const dataURL = renderer.domElement.toDataURL('image/png');
|
|
2824
|
+
const a = document.createElement('a');
|
|
2825
|
+
a.href = dataURL;
|
|
2826
|
+
a.download = 'cyclecad-viewer-screenshot.png';
|
|
2827
|
+
a.click();
|
|
2828
|
+
showToast('Screenshot saved', 'success');
|
|
2829
|
+
});
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
// Expose viewer API globally
|
|
2833
|
+
window.app.viewerMode = viewerAPI;
|
|
2834
|
+
|
|
2835
|
+
// ===== ExplodeView FULL Integration (40+ tools) =====
|
|
2836
|
+
let evAPI = null;
|
|
2837
|
+
try {
|
|
2838
|
+
evAPI = initExplodeView({
|
|
2839
|
+
getScene: () => scene,
|
|
2840
|
+
getCamera: () => camera,
|
|
2841
|
+
getRenderer: () => renderer,
|
|
2842
|
+
getControls: () => controls
|
|
2843
|
+
});
|
|
2844
|
+
window.app.explodeView = evAPI;
|
|
2845
|
+
console.log('[cycleCAD] ExplodeView Full initialized — 40+ tools ready');
|
|
2846
|
+
} catch(e) {
|
|
2847
|
+
console.warn('[cycleCAD] ExplodeView Full init failed:', e);
|
|
2848
|
+
}
|
|
2316
2849
|
}
|
|
2317
2850
|
|
|
2318
2851
|
// ===== Export Global Functions =====
|