cyclecad 1.3.2 → 1.3.3
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/DRAWING_MODULE_INTEGRATION.md +633 -0
- package/README.md +138 -317
- package/app/index.html +2 -0
- package/app/js/brep-kernel.js +853 -0
- package/app/js/kernel.js +684 -0
- package/app/js/modules/assembly-module.js +582 -0
- package/app/js/modules/brep-module.js +583 -0
- package/app/js/modules/drawing-module.js +883 -0
- package/app/js/modules/operations-module.js +660 -0
- package/app/js/modules/simulation-module.js +834 -0
- package/app/js/modules/sketch-module.js +720 -0
- package/app/js/modules/step-module.js +510 -0
- package/app/js/modules/viewport-module.js +530 -0
- package/fusion360-gap-analysis.html +636 -0
- package/package.json +1 -1
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BRepModule — LEGO-block solid modeling kernel for cycleCAD
|
|
3
|
+
*
|
|
4
|
+
* This module wraps OpenCascade.js WASM and provides real B-Rep geometry operations.
|
|
5
|
+
* It is OPTIONAL and can be hot-swapped with the mesh-based fallback.
|
|
6
|
+
*
|
|
7
|
+
* When loaded:
|
|
8
|
+
* - Downloads 50MB OpenCascade.js WASM from CDN (lazy-load on first use)
|
|
9
|
+
* - Provides BRepAPI interface to operations module
|
|
10
|
+
* - Transparently upgrades all geometry ops to real solid modeling
|
|
11
|
+
*
|
|
12
|
+
* When unloaded:
|
|
13
|
+
* - Frees WASM memory (garbage collection)
|
|
14
|
+
* - Operations fall back to mesh approximations (seamless)
|
|
15
|
+
*
|
|
16
|
+
* Module Definition (LEGO block):
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const BRepModule = {
|
|
20
|
+
id: 'brep-kernel',
|
|
21
|
+
name: 'B-Rep Kernel (OpenCascade)',
|
|
22
|
+
version: '1.0.0',
|
|
23
|
+
category: 'engine',
|
|
24
|
+
description: 'Real solid modeling with OpenCascade.js WASM',
|
|
25
|
+
author: 'cycleCAD Team',
|
|
26
|
+
license: 'MIT',
|
|
27
|
+
|
|
28
|
+
// Dependencies (optional — can work without other modules)
|
|
29
|
+
dependencies: [],
|
|
30
|
+
replaces: ['mesh-kernel'], // Can hot-swap with mesh fallback
|
|
31
|
+
|
|
32
|
+
// Memory estimate
|
|
33
|
+
memoryEstimate: 55, // MB (50MB WASM + overhead)
|
|
34
|
+
|
|
35
|
+
// Current state
|
|
36
|
+
state: {
|
|
37
|
+
isLoaded: false,
|
|
38
|
+
isInitializing: false,
|
|
39
|
+
initialized: false,
|
|
40
|
+
lastError: null,
|
|
41
|
+
downloadProgress: 0,
|
|
42
|
+
initTime: 0
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Module initialization (REQUIRED by microkernel)
|
|
47
|
+
* Called when module is registered with the microkernel
|
|
48
|
+
*/
|
|
49
|
+
async init() {
|
|
50
|
+
console.log(`[BRepModule] Initializing: ${this.name} v${this.version}`);
|
|
51
|
+
const startTime = performance.now();
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
// Initialize OpenCascade.js WASM (lazy-loaded on first op)
|
|
55
|
+
await this._initKernel();
|
|
56
|
+
|
|
57
|
+
this.state.initialized = true;
|
|
58
|
+
this.state.initTime = performance.now() - startTime;
|
|
59
|
+
console.log(`[BRepModule] ✓ Initialized in ${this.state.initTime.toFixed(1)}ms`);
|
|
60
|
+
|
|
61
|
+
return { ok: true, state: this.state };
|
|
62
|
+
} catch (err) {
|
|
63
|
+
this.state.lastError = err.message;
|
|
64
|
+
console.error(`[BRepModule] ✗ Init failed:`, err);
|
|
65
|
+
return { ok: false, error: err.message };
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Module activation (REQUIRED by microkernel)
|
|
71
|
+
* Called when module is activated (may be called multiple times)
|
|
72
|
+
*/
|
|
73
|
+
async activate() {
|
|
74
|
+
console.log(`[BRepModule] Activating...`);
|
|
75
|
+
this.state.isLoaded = true;
|
|
76
|
+
|
|
77
|
+
// Expose API to operations module
|
|
78
|
+
window.brepAPI = this.api();
|
|
79
|
+
|
|
80
|
+
// Subscribe to operation events (if operations module exists)
|
|
81
|
+
if (window.cycleCAD && window.cycleCAD.on) {
|
|
82
|
+
window.cycleCAD.on('operation:before-execute', this._onOperationStart.bind(this));
|
|
83
|
+
window.cycleCAD.on('operation:after-execute', this._onOperationEnd.bind(this));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.log(`[BRepModule] ✓ Activated. API exposed at window.brepAPI`);
|
|
87
|
+
return { ok: true };
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Module deactivation (REQUIRED by microkernel)
|
|
92
|
+
* Called when module is unloaded or swapped out
|
|
93
|
+
*/
|
|
94
|
+
async deactivate() {
|
|
95
|
+
console.log(`[BRepModule] Deactivating...`);
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
// Unsubscribe from events
|
|
99
|
+
if (window.cycleCAD && window.cycleCAD.off) {
|
|
100
|
+
window.cycleCAD.off('operation:before-execute', this._onOperationStart);
|
|
101
|
+
window.cycleCAD.off('operation:after-execute', this._onOperationEnd);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Free WASM memory
|
|
105
|
+
await this._cleanup();
|
|
106
|
+
|
|
107
|
+
this.state.isLoaded = false;
|
|
108
|
+
console.log(`[BRepModule] ✓ Deactivated. WASM memory released.`);
|
|
109
|
+
return { ok: true };
|
|
110
|
+
} catch (err) {
|
|
111
|
+
console.error(`[BRepModule] Deactivate error:`, err);
|
|
112
|
+
return { ok: false, error: err.message };
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get module UI (REQUIRED by microkernel)
|
|
118
|
+
* Returns HTML panel for module settings
|
|
119
|
+
*/
|
|
120
|
+
getUI() {
|
|
121
|
+
return `
|
|
122
|
+
<div id="brep-module-panel" class="module-panel">
|
|
123
|
+
<div class="module-header">
|
|
124
|
+
<h3>B-Rep Kernel</h3>
|
|
125
|
+
<span class="status ${this.state.initialized ? 'ready' : 'initializing'}">
|
|
126
|
+
${this.state.initialized ? '✓ Ready' : '⏳ Initializing...'}
|
|
127
|
+
</span>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<div class="module-stats">
|
|
131
|
+
<div class="stat">
|
|
132
|
+
<label>Status:</label>
|
|
133
|
+
<value>${this.state.isLoaded ? 'Active' : 'Inactive'}</value>
|
|
134
|
+
</div>
|
|
135
|
+
<div class="stat">
|
|
136
|
+
<label>Memory:</label>
|
|
137
|
+
<value>${this.memoryEstimate}MB</value>
|
|
138
|
+
</div>
|
|
139
|
+
<div class="stat">
|
|
140
|
+
<label>Init Time:</label>
|
|
141
|
+
<value>${this.state.initTime.toFixed(1)}ms</value>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<div class="module-features">
|
|
146
|
+
<h4>Capabilities</h4>
|
|
147
|
+
<ul>
|
|
148
|
+
<li>Real B-Rep geometry (no approximations)</li>
|
|
149
|
+
<li>Advanced fillets & chamfers</li>
|
|
150
|
+
<li>Boolean operations (union, cut, intersect)</li>
|
|
151
|
+
<li>STEP import/export</li>
|
|
152
|
+
<li>Mass properties & CG calculation</li>
|
|
153
|
+
<li>Mesh tessellation</li>
|
|
154
|
+
</ul>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div class="module-controls">
|
|
158
|
+
<button id="brep-clear-cache" class="btn-secondary">Clear Shape Cache</button>
|
|
159
|
+
<button id="brep-dump-stats" class="btn-secondary">Show Statistics</button>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
${this.state.lastError ? `
|
|
163
|
+
<div class="error-banner">
|
|
164
|
+
<strong>Error:</strong> ${this.state.lastError}
|
|
165
|
+
</div>
|
|
166
|
+
` : ''}
|
|
167
|
+
|
|
168
|
+
<style>
|
|
169
|
+
#brep-module-panel {
|
|
170
|
+
padding: 16px;
|
|
171
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
172
|
+
color: #333;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.module-header {
|
|
176
|
+
display: flex;
|
|
177
|
+
justify-content: space-between;
|
|
178
|
+
align-items: center;
|
|
179
|
+
margin-bottom: 16px;
|
|
180
|
+
border-bottom: 1px solid #e0e0e0;
|
|
181
|
+
padding-bottom: 12px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.module-header h3 {
|
|
185
|
+
margin: 0;
|
|
186
|
+
font-size: 16px;
|
|
187
|
+
font-weight: 600;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.status {
|
|
191
|
+
padding: 4px 8px;
|
|
192
|
+
border-radius: 4px;
|
|
193
|
+
font-size: 12px;
|
|
194
|
+
font-weight: 500;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.status.ready {
|
|
198
|
+
background: #d4edda;
|
|
199
|
+
color: #155724;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.status.initializing {
|
|
203
|
+
background: #fff3cd;
|
|
204
|
+
color: #856404;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.module-stats {
|
|
208
|
+
display: grid;
|
|
209
|
+
grid-template-columns: 1fr 1fr;
|
|
210
|
+
gap: 12px;
|
|
211
|
+
margin: 16px 0;
|
|
212
|
+
padding: 12px;
|
|
213
|
+
background: #f5f5f5;
|
|
214
|
+
border-radius: 4px;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.stat {
|
|
218
|
+
display: flex;
|
|
219
|
+
justify-content: space-between;
|
|
220
|
+
font-size: 13px;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.stat label {
|
|
224
|
+
font-weight: 500;
|
|
225
|
+
color: #666;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.stat value {
|
|
229
|
+
color: #333;
|
|
230
|
+
font-family: 'Courier New', monospace;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.module-features {
|
|
234
|
+
margin: 16px 0;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.module-features h4 {
|
|
238
|
+
margin: 0 0 8px 0;
|
|
239
|
+
font-size: 13px;
|
|
240
|
+
font-weight: 600;
|
|
241
|
+
color: #666;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.module-features ul {
|
|
245
|
+
margin: 0;
|
|
246
|
+
padding-left: 20px;
|
|
247
|
+
font-size: 12px;
|
|
248
|
+
color: #555;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.module-features li {
|
|
252
|
+
margin: 4px 0;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.module-controls {
|
|
256
|
+
display: flex;
|
|
257
|
+
gap: 8px;
|
|
258
|
+
margin: 16px 0;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.btn-secondary {
|
|
262
|
+
padding: 6px 12px;
|
|
263
|
+
font-size: 12px;
|
|
264
|
+
border: 1px solid #ccc;
|
|
265
|
+
background: #f9f9f9;
|
|
266
|
+
border-radius: 4px;
|
|
267
|
+
cursor: pointer;
|
|
268
|
+
transition: all 0.2s;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.btn-secondary:hover {
|
|
272
|
+
background: #e9e9e9;
|
|
273
|
+
border-color: #999;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.error-banner {
|
|
277
|
+
margin-top: 16px;
|
|
278
|
+
padding: 12px;
|
|
279
|
+
background: #f8d7da;
|
|
280
|
+
border: 1px solid #f5c6cb;
|
|
281
|
+
border-radius: 4px;
|
|
282
|
+
color: #721c24;
|
|
283
|
+
font-size: 12px;
|
|
284
|
+
}
|
|
285
|
+
</style>
|
|
286
|
+
</div>
|
|
287
|
+
`;
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Execute module command (REQUIRED by microkernel)
|
|
292
|
+
* Called via window.cycleCAD.execute() or direct module API
|
|
293
|
+
*/
|
|
294
|
+
async execute(command, params) {
|
|
295
|
+
console.log(`[BRepModule] Execute: ${command}`, params);
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
// Ensure kernel is initialized
|
|
299
|
+
await this._ensureInitialized();
|
|
300
|
+
|
|
301
|
+
// Route command to API
|
|
302
|
+
const api = this.api();
|
|
303
|
+
if (typeof api[command] === 'function') {
|
|
304
|
+
const result = await api[command](params);
|
|
305
|
+
return { ok: true, result };
|
|
306
|
+
} else {
|
|
307
|
+
return { ok: false, error: `Unknown command: ${command}` };
|
|
308
|
+
}
|
|
309
|
+
} catch (err) {
|
|
310
|
+
console.error(`[BRepModule] Execute failed:`, err);
|
|
311
|
+
return { ok: false, error: err.message };
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Get module API (exposed as window.brepAPI)
|
|
317
|
+
*/
|
|
318
|
+
api() {
|
|
319
|
+
return {
|
|
320
|
+
// Primitive operations
|
|
321
|
+
makeBox: async (w, h, d) => {
|
|
322
|
+
const kernel = await this._getKernel();
|
|
323
|
+
return kernel.makeBox(w, h, d);
|
|
324
|
+
},
|
|
325
|
+
|
|
326
|
+
makeCylinder: async (r, h) => {
|
|
327
|
+
const kernel = await this._getKernel();
|
|
328
|
+
return kernel.makeCylinder(r, h);
|
|
329
|
+
},
|
|
330
|
+
|
|
331
|
+
makeSphere: async (r) => {
|
|
332
|
+
const kernel = await this._getKernel();
|
|
333
|
+
return kernel.makeSphere(r);
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
makeCone: async (r1, r2, h) => {
|
|
337
|
+
const kernel = await this._getKernel();
|
|
338
|
+
return kernel.makeCone(r1, r2, h);
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
makeTorus: async (majorR, minorR) => {
|
|
342
|
+
const kernel = await this._getKernel();
|
|
343
|
+
return kernel.makeTorus(majorR, minorR);
|
|
344
|
+
},
|
|
345
|
+
|
|
346
|
+
// Shape operations
|
|
347
|
+
extrude: async (shapeId, direction, distance) => {
|
|
348
|
+
const kernel = await this._getKernel();
|
|
349
|
+
return kernel.extrude(shapeId, direction, distance);
|
|
350
|
+
},
|
|
351
|
+
|
|
352
|
+
revolve: async (shapeId, axis, angle) => {
|
|
353
|
+
const kernel = await this._getKernel();
|
|
354
|
+
return kernel.revolve(shapeId, axis, angle);
|
|
355
|
+
},
|
|
356
|
+
|
|
357
|
+
fillet: async (shapeId, edgeIndices, radius) => {
|
|
358
|
+
const kernel = await this._getKernel();
|
|
359
|
+
return kernel.fillet(shapeId, edgeIndices, radius);
|
|
360
|
+
},
|
|
361
|
+
|
|
362
|
+
chamfer: async (shapeId, edgeIndices, distance) => {
|
|
363
|
+
const kernel = await this._getKernel();
|
|
364
|
+
return kernel.chamfer(shapeId, edgeIndices, distance);
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
union: async (shapeId1, shapeId2) => {
|
|
368
|
+
const kernel = await this._getKernel();
|
|
369
|
+
return kernel.booleanUnion(shapeId1, shapeId2);
|
|
370
|
+
},
|
|
371
|
+
|
|
372
|
+
cut: async (shapeIdBase, shapeIdTool) => {
|
|
373
|
+
const kernel = await this._getKernel();
|
|
374
|
+
return kernel.booleanCut(shapeIdTool, shapeIdBase);
|
|
375
|
+
},
|
|
376
|
+
|
|
377
|
+
intersect: async (shapeId1, shapeId2) => {
|
|
378
|
+
const kernel = await this._getKernel();
|
|
379
|
+
return kernel.booleanIntersect(shapeId1, shapeId2);
|
|
380
|
+
},
|
|
381
|
+
|
|
382
|
+
shell: async (shapeId, faceIndices, thickness) => {
|
|
383
|
+
const kernel = await this._getKernel();
|
|
384
|
+
return kernel.shell(shapeId, faceIndices, thickness);
|
|
385
|
+
},
|
|
386
|
+
|
|
387
|
+
sweep: async (profileId, pathId) => {
|
|
388
|
+
const kernel = await this._getKernel();
|
|
389
|
+
return kernel.sweep(profileId, pathId);
|
|
390
|
+
},
|
|
391
|
+
|
|
392
|
+
loft: async (profileIds) => {
|
|
393
|
+
const kernel = await this._getKernel();
|
|
394
|
+
return kernel.loft(profileIds);
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
mirror: async (shapeId, plane) => {
|
|
398
|
+
const kernel = await this._getKernel();
|
|
399
|
+
return kernel.mirror(shapeId, plane);
|
|
400
|
+
},
|
|
401
|
+
|
|
402
|
+
draft: async (shapeId, faceIndices, angle, direction) => {
|
|
403
|
+
const kernel = await this._getKernel();
|
|
404
|
+
return kernel.draft(shapeId, faceIndices, angle, direction);
|
|
405
|
+
},
|
|
406
|
+
|
|
407
|
+
// Meshing
|
|
408
|
+
toMesh: async (shapeId, linearDeflection, angularDeflection) => {
|
|
409
|
+
const kernel = await this._getKernel();
|
|
410
|
+
return kernel.shapeToMesh(shapeId, linearDeflection, angularDeflection);
|
|
411
|
+
},
|
|
412
|
+
|
|
413
|
+
// STEP I/O
|
|
414
|
+
importSTEP: async (arrayBuffer) => {
|
|
415
|
+
const kernel = await this._getKernel();
|
|
416
|
+
return kernel.importSTEP(arrayBuffer);
|
|
417
|
+
},
|
|
418
|
+
|
|
419
|
+
exportSTEP: async (shapeIds) => {
|
|
420
|
+
const kernel = await this._getKernel();
|
|
421
|
+
return kernel.exportSTEP(shapeIds);
|
|
422
|
+
},
|
|
423
|
+
|
|
424
|
+
// Shape inspection
|
|
425
|
+
getEdges: async (shapeId) => {
|
|
426
|
+
const kernel = await this._getKernel();
|
|
427
|
+
return kernel.getEdges(shapeId);
|
|
428
|
+
},
|
|
429
|
+
|
|
430
|
+
getFaces: async (shapeId) => {
|
|
431
|
+
const kernel = await this._getKernel();
|
|
432
|
+
return kernel.getFaces(shapeId);
|
|
433
|
+
},
|
|
434
|
+
|
|
435
|
+
getMassProperties: async (shapeId) => {
|
|
436
|
+
const kernel = await this._getKernel();
|
|
437
|
+
return kernel.getMassProperties(shapeId);
|
|
438
|
+
},
|
|
439
|
+
|
|
440
|
+
getBoundingBox: async (shapeId) => {
|
|
441
|
+
const kernel = await this._getKernel();
|
|
442
|
+
return kernel.getBoundingBox(shapeId);
|
|
443
|
+
},
|
|
444
|
+
|
|
445
|
+
// Cache management
|
|
446
|
+
clearCache: async () => {
|
|
447
|
+
const kernel = await this._getKernel();
|
|
448
|
+
kernel.clearCache();
|
|
449
|
+
return { ok: true, message: 'Cache cleared' };
|
|
450
|
+
},
|
|
451
|
+
|
|
452
|
+
getStats: async () => {
|
|
453
|
+
const kernel = await this._getKernel();
|
|
454
|
+
return kernel.getCacheStats();
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
},
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* PRIVATE: Initialize kernel
|
|
461
|
+
*/
|
|
462
|
+
async _initKernel() {
|
|
463
|
+
if (this.state.isInitializing) {
|
|
464
|
+
// Wait for ongoing init
|
|
465
|
+
while (this.state.isInitializing) {
|
|
466
|
+
await new Promise(r => setTimeout(r, 100));
|
|
467
|
+
}
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
this.state.isInitializing = true;
|
|
472
|
+
|
|
473
|
+
try {
|
|
474
|
+
// Dynamic import of kernel
|
|
475
|
+
if (!window.brepKernel) {
|
|
476
|
+
// Load brep-kernel.js if not already loaded
|
|
477
|
+
const script = document.createElement('script');
|
|
478
|
+
script.type = 'module';
|
|
479
|
+
script.textContent = `import brepKernel from './brep-kernel.js'; window.brepKernel = brepKernel;`;
|
|
480
|
+
document.head.appendChild(script);
|
|
481
|
+
|
|
482
|
+
// Wait for it to load
|
|
483
|
+
while (!window.brepKernel) {
|
|
484
|
+
await new Promise(r => setTimeout(r, 50));
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Initialize the kernel (triggers WASM download)
|
|
489
|
+
await window.brepKernel.init();
|
|
490
|
+
} finally {
|
|
491
|
+
this.state.isInitializing = false;
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* PRIVATE: Get kernel instance (ensuring init)
|
|
497
|
+
*/
|
|
498
|
+
async _getKernel() {
|
|
499
|
+
if (!window.brepKernel) {
|
|
500
|
+
await this._initKernel();
|
|
501
|
+
}
|
|
502
|
+
return window.brepKernel;
|
|
503
|
+
},
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* PRIVATE: Ensure kernel is initialized
|
|
507
|
+
*/
|
|
508
|
+
async _ensureInitialized() {
|
|
509
|
+
if (!this.state.initialized) {
|
|
510
|
+
await this.init();
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* PRIVATE: Cleanup (free WASM memory)
|
|
516
|
+
*/
|
|
517
|
+
async _cleanup() {
|
|
518
|
+
if (window.brepKernel) {
|
|
519
|
+
window.brepKernel.clearCache();
|
|
520
|
+
// Note: WASM module stays loaded for garbage collection
|
|
521
|
+
// The browser will free it when the window is closed
|
|
522
|
+
}
|
|
523
|
+
},
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* PRIVATE: Operation lifecycle hooks
|
|
527
|
+
*/
|
|
528
|
+
_onOperationStart(event) {
|
|
529
|
+
console.log('[BRepModule] Operation starting:', event.type);
|
|
530
|
+
},
|
|
531
|
+
|
|
532
|
+
_onOperationEnd(event) {
|
|
533
|
+
if (event.error) {
|
|
534
|
+
console.error('[BRepModule] Operation failed:', event.error);
|
|
535
|
+
} else {
|
|
536
|
+
console.log('[BRepModule] Operation complete:', event.type);
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Get module schema (for introspection)
|
|
542
|
+
*/
|
|
543
|
+
getSchema() {
|
|
544
|
+
return {
|
|
545
|
+
id: this.id,
|
|
546
|
+
name: this.name,
|
|
547
|
+
version: this.version,
|
|
548
|
+
description: this.description,
|
|
549
|
+
category: this.category,
|
|
550
|
+
state: this.state,
|
|
551
|
+
memoryEstimate: this.memoryEstimate,
|
|
552
|
+
commands: Object.keys(this.api()).map(cmd => ({
|
|
553
|
+
name: cmd,
|
|
554
|
+
type: 'async'
|
|
555
|
+
})),
|
|
556
|
+
events: [
|
|
557
|
+
'operation:before-execute',
|
|
558
|
+
'operation:after-execute',
|
|
559
|
+
'kernel:initialized',
|
|
560
|
+
'kernel:error'
|
|
561
|
+
]
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
// ============================================================================
|
|
567
|
+
// Module Registration (LEGO block pattern)
|
|
568
|
+
// ============================================================================
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Register the BRepModule with cycleCAD's microkernel
|
|
572
|
+
* This makes it hot-swappable and allows other modules to depend on it
|
|
573
|
+
*/
|
|
574
|
+
if (typeof window !== 'undefined' && window.cycleCAD) {
|
|
575
|
+
window.cycleCAD.registerModule = window.cycleCAD.registerModule || function(module) {
|
|
576
|
+
console.log(`[Microkernel] Registered module: ${module.id}`);
|
|
577
|
+
};
|
|
578
|
+
window.cycleCAD.registerModule(BRepModule);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
console.log('[BRepModule] Loaded. Call BRepModule.init() to start, or register with microkernel.');
|
|
582
|
+
|
|
583
|
+
export default BRepModule;
|