cyclecad 1.1.2 → 1.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.
- package/.github/scripts/cad-diff.js +590 -0
- package/.github/workflows/cad-diff.yml +117 -0
- package/KILLER-README.md +377 -0
- package/app/index.html +88 -30
- package/app/js/ai-copilot.js +53 -18
- package/app/js/brep-engine.js +661 -0
- package/app/js/multiplayer.js +465 -0
- package/app/js/parts-library.js +778 -0
- package/app/js/step-viewer.js +584 -0
- package/app/js/text-to-brep.js +585 -0
- package/docs/ARCHITECTURE.html +1429 -0
- package/package.json +1 -1
package/app/js/ai-copilot.js
CHANGED
|
@@ -947,19 +947,62 @@ export async function executeTextCommand(prompt) {
|
|
|
947
947
|
|
|
948
948
|
addMessage('ai', `Got it! ${preview}. Creating now...`);
|
|
949
949
|
|
|
950
|
-
//
|
|
950
|
+
// ── B-REP ENGINE PATH ──────────────────────────────────────────────
|
|
951
|
+
// Try real OpenCascade.js B-rep first. Falls back to mesh preview.
|
|
952
|
+
const brep = window.brepEngine;
|
|
953
|
+
const brepCommands = commands.map(cmd => {
|
|
954
|
+
const method = cmd.method || '';
|
|
955
|
+
const type = method.replace('shape.', '').replace('feature.', '');
|
|
956
|
+
return { type, params: { ...cmd.params } };
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
// Attempt B-rep execution
|
|
960
|
+
if (brep) {
|
|
961
|
+
try {
|
|
962
|
+
if (!brep.isReady()) {
|
|
963
|
+
addMessage('ai', '⏳ Loading OpenCascade.js B-rep kernel (~50MB WASM)... first time only.');
|
|
964
|
+
await brep.initBRep();
|
|
965
|
+
addMessage('ai', '✅ B-rep kernel loaded!');
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// Remove previous B-rep mesh from scene
|
|
969
|
+
if (window._brepMesh) {
|
|
970
|
+
if (window._brepMesh.mesh?.parent) window._brepMesh.mesh.parent.remove(window._brepMesh.mesh);
|
|
971
|
+
if (window._brepMesh.wireframe?.parent) window._brepMesh.wireframe.parent.remove(window._brepMesh.wireframe);
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
const result = await brep.executeCommands(brepCommands);
|
|
975
|
+
if (result && result.mesh) {
|
|
976
|
+
// Add to Three.js scene
|
|
977
|
+
const addFn = window.addToScene || ((m) => {
|
|
978
|
+
const scene = window._scene || window.getScene?.();
|
|
979
|
+
if (scene) scene.add(m);
|
|
980
|
+
});
|
|
981
|
+
addFn(result.mesh);
|
|
982
|
+
addFn(result.wireframe);
|
|
983
|
+
window._brepMesh = result;
|
|
984
|
+
|
|
985
|
+
addMessage('ai', `🔧 **Real B-rep**: ${result.description} — solid model with true edges and faces.`);
|
|
986
|
+
return { ok: true, brep: true, description: result.description };
|
|
987
|
+
}
|
|
988
|
+
} catch (brepErr) {
|
|
989
|
+
console.warn('[Copilot] B-rep execution failed, falling back to mesh preview:', brepErr);
|
|
990
|
+
addMessage('ai', `⚠️ B-rep kernel error: ${brepErr.message}. Using mesh preview instead.`);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
// ── MESH PREVIEW FALLBACK ──────────────────────────────────────────
|
|
951
995
|
const results = [];
|
|
952
996
|
for (const cmd of commands) {
|
|
953
997
|
try {
|
|
954
|
-
// Convert Agent API format {method: 'shape.cylinder', params: {}} to executeParsedPrompt format {type: 'cylinder', params: {}}
|
|
955
998
|
if (window._executeParsedPrompt) {
|
|
956
999
|
const method = cmd.method || '';
|
|
957
1000
|
const type = method.replace('shape.', '').replace('feature.', '');
|
|
958
1001
|
|
|
959
|
-
// Operations that modify existing geometry
|
|
1002
|
+
// Operations that modify existing geometry
|
|
960
1003
|
const modifyOps = ['fillet', 'chamfer', 'pattern', 'mirror', 'shell'];
|
|
961
1004
|
if (modifyOps.includes(type)) {
|
|
962
|
-
addMessage('ai', `⚡ ${type}
|
|
1005
|
+
addMessage('ai', `⚡ ${type} — install B-rep kernel for real edge operations. Using visual preview.`);
|
|
963
1006
|
results.push({ ok: true, method, note: 'modify-op' });
|
|
964
1007
|
continue;
|
|
965
1008
|
}
|
|
@@ -968,23 +1011,15 @@ export async function executeTextCommand(prompt) {
|
|
|
968
1011
|
const count = cmd.params?.count || 1;
|
|
969
1012
|
for (let ci = 0; ci < count; ci++) {
|
|
970
1013
|
const p = Object.assign({}, cmd.params);
|
|
971
|
-
// Position holes at 4 corners of a typical cube face
|
|
972
1014
|
if (count > 1 && type === 'hole') {
|
|
973
|
-
const cornerSpread = 3.5;
|
|
974
|
-
const corners = [
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
[ cornerSpread, cornerSpread],
|
|
978
|
-
[-cornerSpread, cornerSpread],
|
|
979
|
-
];
|
|
980
|
-
const idx = ci % corners.length;
|
|
981
|
-
p._offsetX = corners[idx][0];
|
|
982
|
-
p._offsetZ = corners[idx][1];
|
|
1015
|
+
const cornerSpread = 3.5;
|
|
1016
|
+
const corners = [[-cornerSpread, -cornerSpread], [cornerSpread, -cornerSpread], [cornerSpread, cornerSpread], [-cornerSpread, cornerSpread]];
|
|
1017
|
+
p._offsetX = corners[ci % 4][0];
|
|
1018
|
+
p._offsetZ = corners[ci % 4][1];
|
|
983
1019
|
} else if (count > 1) {
|
|
984
1020
|
const angle = (ci / count) * Math.PI * 2;
|
|
985
|
-
|
|
986
|
-
p.
|
|
987
|
-
p._offsetZ = Math.sin(angle) * spread;
|
|
1021
|
+
p._offsetX = Math.cos(angle) * (p.radius || 5) * 3 * 0.1;
|
|
1022
|
+
p._offsetZ = Math.sin(angle) * (p.radius || 5) * 3 * 0.1;
|
|
988
1023
|
}
|
|
989
1024
|
window._executeParsedPrompt({ type, params: p });
|
|
990
1025
|
}
|