cyclecad 3.0.0 → 3.2.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 (67) hide show
  1. package/BILLING-IMPLEMENTATION-SUMMARY.md +425 -0
  2. package/BILLING-INDEX.md +293 -0
  3. package/BILLING-INTEGRATION-GUIDE.md +414 -0
  4. package/COLLABORATION-INDEX.md +440 -0
  5. package/COLLABORATION-SYSTEM-SUMMARY.md +548 -0
  6. package/DOCKER-BUILD-MANIFEST.txt +483 -0
  7. package/DOCKER-FILES-REFERENCE.md +440 -0
  8. package/DOCKER-INFRASTRUCTURE.md +475 -0
  9. package/DOCKER-README.md +435 -0
  10. package/Dockerfile +33 -55
  11. package/PWA-FILES-CREATED.txt +350 -0
  12. package/QUICK-START-TESTING.md +126 -0
  13. package/STEP-IMPORT-QUICKSTART.md +347 -0
  14. package/STEP-IMPORT-SYSTEM-SUMMARY.md +502 -0
  15. package/app/css/mobile.css +1074 -0
  16. package/app/icons/generate-icons.js +203 -0
  17. package/app/index.html +93 -0
  18. package/app/js/billing-ui.js +990 -0
  19. package/app/js/brep-kernel.js +933 -981
  20. package/app/js/collab-client.js +750 -0
  21. package/app/js/mobile-nav.js +623 -0
  22. package/app/js/mobile-toolbar.js +476 -0
  23. package/app/js/modules/billing-module.js +724 -0
  24. package/app/js/modules/step-module-enhanced.js +938 -0
  25. package/app/js/offline-manager.js +705 -0
  26. package/app/js/responsive-init.js +360 -0
  27. package/app/js/touch-handler.js +429 -0
  28. package/app/manifest.json +211 -0
  29. package/app/offline.html +508 -0
  30. package/app/sw.js +571 -0
  31. package/app/tests/billing-tests.html +779 -0
  32. package/app/tests/brep-tests.html +980 -0
  33. package/app/tests/collab-tests.html +743 -0
  34. package/app/tests/mobile-tests.html +1299 -0
  35. package/app/tests/pwa-tests.html +1134 -0
  36. package/app/tests/step-tests.html +1042 -0
  37. package/app/tests/test-agent-v3.html +719 -0
  38. package/docker-compose.yml +225 -0
  39. package/docs/BILLING-HELP.json +260 -0
  40. package/docs/BILLING-README.md +639 -0
  41. package/docs/BILLING-TUTORIAL.md +736 -0
  42. package/docs/BREP-HELP.json +326 -0
  43. package/docs/BREP-TUTORIAL.md +802 -0
  44. package/docs/COLLABORATION-HELP.json +228 -0
  45. package/docs/COLLABORATION-TUTORIAL.md +818 -0
  46. package/docs/DOCKER-HELP.json +224 -0
  47. package/docs/DOCKER-TUTORIAL.md +974 -0
  48. package/docs/MOBILE-HELP.json +243 -0
  49. package/docs/MOBILE-RESPONSIVE-README.md +378 -0
  50. package/docs/MOBILE-TUTORIAL.md +747 -0
  51. package/docs/PWA-HELP.json +228 -0
  52. package/docs/PWA-README.md +662 -0
  53. package/docs/PWA-TUTORIAL.md +757 -0
  54. package/docs/STEP-HELP.json +481 -0
  55. package/docs/STEP-IMPORT-TUTORIAL.md +824 -0
  56. package/docs/TESTING-GUIDE.md +528 -0
  57. package/docs/TESTING-HELP.json +182 -0
  58. package/fusion-vs-cyclecad.html +1771 -0
  59. package/nginx.conf +237 -0
  60. package/package.json +1 -1
  61. package/server/Dockerfile.converter +51 -0
  62. package/server/Dockerfile.signaling +28 -0
  63. package/server/billing-server.js +487 -0
  64. package/server/converter-enhanced.py +528 -0
  65. package/server/requirements-converter.txt +29 -0
  66. package/server/signaling-server.js +801 -0
  67. package/tests/docker-tests.sh +389 -0
@@ -0,0 +1,802 @@
1
+ # B-Rep (Boundary Representation) Modeling in cycleCAD
2
+
3
+ A comprehensive tutorial on real solid modeling using OpenCascade.js WASM in the browser.
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [What is B-Rep and Why It Matters](#what-is-brep-and-why-it-matters)
8
+ 2. [How OpenCascade.js Works in the Browser](#how-opencascadejs-works-in-the-browser)
9
+ 3. [Getting Started with the B-Rep Kernel](#getting-started-with-the-brep-kernel)
10
+ 4. [Step-by-Step: Your First Solid](#step-by-step-your-first-solid)
11
+ 5. [Boolean Operations](#boolean-operations)
12
+ 6. [Advanced Operations](#advanced-operations)
13
+ 7. [STEP Import and Export](#step-import-and-export)
14
+ 8. [Analyzing Your Models](#analyzing-your-models)
15
+ 9. [Performance and Optimization](#performance-and-optimization)
16
+ 10. [Troubleshooting](#troubleshooting)
17
+ 11. [Complete API Reference](#complete-api-reference)
18
+
19
+ ---
20
+
21
+ ## What is B-Rep and Why It Matters
22
+
23
+ ### B-Rep Definition
24
+
25
+ **B-Rep** (Boundary Representation) is a solid modeling technique that represents 3D objects using their boundary surfaces. Unlike voxel grids or point clouds, B-Rep captures complete topological information:
26
+
27
+ - **Vertices** — 0D points (corners)
28
+ - **Edges** — 1D curves connecting vertices
29
+ - **Faces** — 2D surfaces bounded by edges
30
+ - **Shells** — Collections of faces forming closed volumes
31
+ - **Solids** — Complete 3D shapes with topology
32
+
33
+ ### Why B-Rep is Powerful
34
+
35
+ 1. **Precision** — Geometry is defined mathematically (NURBS curves, analytical surfaces) rather than approximations
36
+ 2. **Topology-Aware** — Knows which edges and faces belong to which solid, enabling intelligent operations
37
+ 3. **Manufacturing-Ready** — Compatible with CAM (CNC, 3D printing, laser cutting) because it captures exact geometry
38
+ 4. **Editable** — Can fillet specific edges, chamfer specific faces, or cut holes at precise locations
39
+ 5. **Interoperable** — STEP (ISO 10303) is the universal CAD format, and B-Rep is its native representation
40
+
41
+ ### B-Rep vs. Mesh
42
+
43
+ | Feature | B-Rep | Mesh |
44
+ |---------|-------|------|
45
+ | Topology | Full (edges, faces, vertices tracked) | Implicit (just triangles) |
46
+ | Precision | Infinite (mathematical surfaces) | Limited (tessellation dependent) |
47
+ | Editable | Can modify specific features | Difficult to modify cleanly |
48
+ | File Size | Compact (few curves) | Large (many triangles) |
49
+ | STEP Compatible | Yes (native) | No (conversion required) |
50
+ | Manufacturing | Direct CAM export | Requires healing/repair |
51
+
52
+ ---
53
+
54
+ ## How OpenCascade.js Works in the Browser
55
+
56
+ ### What is OpenCascade.js?
57
+
58
+ OpenCascade.js is a **WebAssembly (WASM) compilation** of the OpenCASCADE 3D geometry kernel — the same library used by FreeCAD, Salome, and many professional CAD systems.
59
+
60
+ ### Architecture
61
+
62
+ ```
63
+ ┌─────────────────────────────────────────────────────┐
64
+ │ Your cycleCAD Code (JavaScript) │
65
+ ├─────────────────────────────────────────────────────┤
66
+ │ BRepKernel (wrapper providing high-level API) │
67
+ ├─────────────────────────────────────────────────────┤
68
+ │ OpenCascade.js (WASM module, ~50MB) │
69
+ ├─────────────────────────────────────────────────────┤
70
+ │ Browser's WASM Runtime (V8, SpiderMonkey, etc.) │
71
+ ├─────────────────────────────────────────────────────┤
72
+ │ System Libraries (POSIX, memory management) │
73
+ └─────────────────────────────────────────────────────┘
74
+ ```
75
+
76
+ ### Loading the WASM File
77
+
78
+ The first time you use the B-Rep kernel, it downloads the OpenCascade.js library:
79
+
80
+ 1. **JavaScript** (~400KB) — loaded as a script tag, defines the Emscripten factory function
81
+ 2. **WASM binary** (~50MB) — downloaded on demand, provides the actual geometry algorithms
82
+ 3. **Initialization** — factory function creates an instance with proper memory setup
83
+
84
+ This happens **once per browser session** and is cached in IndexedDB if available.
85
+
86
+ ### Why Lazy Loading?
87
+
88
+ Because 50MB is large, the kernel uses **lazy initialization**:
89
+ - If you only load a model and don't create new geometry, WASM is never downloaded
90
+ - If you start modeling, WASM loads automatically (users see a progress message)
91
+ - Subsequent operations are fast (WASM stays in memory)
92
+
93
+ ---
94
+
95
+ ## Getting Started with the B-Rep Kernel
96
+
97
+ ### Initialization
98
+
99
+ ```javascript
100
+ import BRepKernel from '/app/js/brep-kernel.js';
101
+
102
+ // Create an instance
103
+ const kernel = new BRepKernel();
104
+
105
+ // Initialize (lazy — loads WASM only when needed)
106
+ await kernel.init();
107
+
108
+ // Or with progress feedback
109
+ await kernel.init((loaded, total, percent) => {
110
+ console.log(`Downloading: ${percent}%`);
111
+ });
112
+ ```
113
+
114
+ ### Basic Pattern
115
+
116
+ All B-Rep operations follow this pattern:
117
+
118
+ ```javascript
119
+ // Create a shape
120
+ const result = await kernel.makeBox({width: 100, height: 50, depth: 30});
121
+ console.log('Shape ID:', result.id); // 'shape_0'
122
+ console.log('Shape object:', result.shape); // OCC TopoDS_Solid
123
+
124
+ // Apply an operation
125
+ const filleted = await kernel.fillet({
126
+ shapeId: result.id,
127
+ edgeIndices: [0, 1, 2, 3],
128
+ radius: 5
129
+ });
130
+ // filleted = {id: 'shape_1', shape: <OCC object>}
131
+
132
+ // Convert to Three.js for visualization
133
+ const geometry = await kernel.shapeToMesh(filleted.id);
134
+ const mesh = new THREE.Mesh(
135
+ geometry,
136
+ new THREE.MeshStandardMaterial({color: 0x888888})
137
+ );
138
+ scene.add(mesh);
139
+ ```
140
+
141
+ ### Key Concepts
142
+
143
+ 1. **Shape IDs** — Every operation returns a new shape with a unique ID (`shape_0`, `shape_1`, etc.)
144
+ 2. **Immutability** — Operations don't modify the original; they return new shapes
145
+ 3. **Caching** — All shapes are cached internally for reuse and memory efficiency
146
+ 4. **Async/Await** — All operations are asynchronous (WASM can be slow for large geometries)
147
+
148
+ ---
149
+
150
+ ## Step-by-Step: Your First Solid
151
+
152
+ ### 1. Create a Box and Add a Rounded Edge
153
+
154
+ ```javascript
155
+ // Step 1: Create a box
156
+ const box = await kernel.makeBox({
157
+ width: 100, // X dimension
158
+ height: 50, // Y dimension
159
+ depth: 30 // Z dimension
160
+ });
161
+
162
+ // Step 2: Get the edges (to see what we can fillet)
163
+ const edges = await kernel.getEdges(box.id);
164
+ console.log(`Box has ${edges.length} edges`);
165
+ // Output: Box has 12 edges
166
+
167
+ // Step 3: Fillet the top 4 edges with a 5mm radius
168
+ const filleted = await kernel.fillet({
169
+ shapeId: box.id,
170
+ edgeIndices: [0, 1, 2, 3], // First 4 edges are typically the top edges
171
+ radius: 5 // 5mm radius
172
+ });
173
+
174
+ // Step 4: Visualize
175
+ const geometry = await kernel.shapeToMesh(filleted.id);
176
+ const material = new THREE.MeshStandardMaterial({
177
+ color: 0xcccccc,
178
+ metalness: 0.3,
179
+ roughness: 0.7
180
+ });
181
+ const mesh = new THREE.Mesh(geometry, material);
182
+ scene.add(mesh);
183
+ ```
184
+
185
+ ### 2. Add a Beveled Edge
186
+
187
+ ```javascript
188
+ // Chamfer bottom edges with a 2mm bevel
189
+ const chamfered = await kernel.chamfer({
190
+ shapeId: filleted.id, // Use the filleted shape as input
191
+ edgeIndices: [8, 9, 10, 11], // Bottom edges
192
+ distance: 2
193
+ });
194
+ ```
195
+
196
+ ### 3. Create a Hollow Shell
197
+
198
+ ```javascript
199
+ // Remove the top face and create a hollow box with 2mm walls
200
+ const hollow = await kernel.shell({
201
+ shapeId: chamfered.id,
202
+ removeFaceIndices: [4], // Remove top face
203
+ thickness: 2 // 2mm wall thickness
204
+ });
205
+
206
+ // Check the hollow box properties
207
+ const props = await kernel.getMassProperties({
208
+ shapeId: hollow.id,
209
+ density: 2.7 // Aluminum: 2.7 g/cm³
210
+ });
211
+
212
+ console.log(`Volume: ${props.volume} mm³`);
213
+ console.log(`Surface area: ${props.area} mm²`);
214
+ console.log(`Weight (aluminum): ${props.mass} grams`);
215
+ console.log(`Center of gravity:`, props.centerOfGravity);
216
+ ```
217
+
218
+ ---
219
+
220
+ ## Boolean Operations
221
+
222
+ Boolean operations combine two or more solids. They are the foundation of solid modeling.
223
+
224
+ ### Union (Fuse)
225
+
226
+ Combine two solids into one:
227
+
228
+ ```javascript
229
+ // Create a base box
230
+ const box = await kernel.makeBox({width: 100, height: 50, depth: 30});
231
+
232
+ // Create a cylindrical feature to add
233
+ const cyl = await kernel.makeCylinder({radius: 15, height: 40});
234
+
235
+ // Union them
236
+ const combined = await kernel.booleanUnion({
237
+ shapeA: box.id,
238
+ shapeB: cyl.id
239
+ });
240
+
241
+ // Visualize the combined shape
242
+ const geometry = await kernel.shapeToMesh(combined.id);
243
+ // ...render as before
244
+ ```
245
+
246
+ ### Cut (Difference)
247
+
248
+ Subtract one solid from another:
249
+
250
+ ```javascript
251
+ // Create a hole by subtracting a cylinder from the box
252
+ const withHole = await kernel.booleanCut({
253
+ shapeA: combined.id, // Base shape (box + cylinder)
254
+ shapeB: cyl.id // Tool shape (cylinder to subtract)
255
+ });
256
+
257
+ // Visualize
258
+ const geometry = await kernel.shapeToMesh(withHole.id);
259
+ // ...render
260
+ ```
261
+
262
+ ### Intersection (Common)
263
+
264
+ Keep only the overlapping region:
265
+
266
+ ```javascript
267
+ // Find the intersection of two spheres
268
+ const sphere1 = await kernel.makeSphere({radius: 30});
269
+ const sphere2 = await kernel.makeSphere({radius: 30});
270
+
271
+ // Move sphere2 (would need translation if they're at same position)
272
+ const intersection = await kernel.booleanIntersect({
273
+ shapeA: sphere1.id,
274
+ shapeB: sphere2.id
275
+ });
276
+
277
+ // This creates a lens-shaped solid where they overlap
278
+ const geometry = await kernel.shapeToMesh(intersection.id);
279
+ // ...render
280
+ ```
281
+
282
+ ### Error Recovery
283
+
284
+ Boolean operations can fail if shapes have:
285
+ - Tiny gaps (numerical precision issues)
286
+ - Overlapping surfaces
287
+ - Degenerate geometry
288
+
289
+ The B-Rep kernel uses **3-tier error recovery**:
290
+
291
+ ```javascript
292
+ try {
293
+ const result = await kernel.booleanCut({
294
+ shapeA: box.id,
295
+ shapeB: tool.id
296
+ });
297
+ } catch (err) {
298
+ if (err.name === 'BRepError') {
299
+ console.error('Operation failed:', err.message);
300
+ console.error('Diagnostic:', err.diagnostic);
301
+ // The error message will tell you which recovery tier failed
302
+ }
303
+ }
304
+ ```
305
+
306
+ The three tiers are:
307
+ 1. **Standard** — Normal operation without tolerance adjustments
308
+ 2. **Fuzzy Tolerance** — Allows small gaps/overlaps (0.01mm)
309
+ 3. **Shape Healing** — Repairs degenerate geometry before retrying
310
+
311
+ ---
312
+
313
+ ## Advanced Operations
314
+
315
+ ### Sweep (Profile Along Path)
316
+
317
+ Sweep a 2D profile along a 3D path:
318
+
319
+ ```javascript
320
+ // Create a circular profile
321
+ const profile = await kernel.makeSphere({radius: 5});
322
+
323
+ // Create a path (would be a wire in production)
324
+ // For this example, we'll use the axis of extrusion
325
+ const swept = await kernel.sweep({
326
+ profileId: profile.id,
327
+ pathId: lineWire.id // pathId should be a wire/curve
328
+ });
329
+
330
+ // Creates a shape that "drags" the profile along the path
331
+ const geometry = await kernel.shapeToMesh(swept.id);
332
+ // ...render
333
+ ```
334
+
335
+ ### Loft (Between Profiles)
336
+
337
+ Create a surface flowing between multiple 2D profiles:
338
+
339
+ ```javascript
340
+ // Create profiles at different sizes
341
+ const base = await kernel.makeBox({width: 50, height: 50, depth: 1});
342
+ const mid = await kernel.makeBox({width: 40, height: 40, depth: 1});
343
+ const top = await kernel.makeBox({width: 20, height: 20, depth: 1});
344
+
345
+ // Loft between them
346
+ const lofted = await kernel.loft({
347
+ profileIds: [base.id, mid.id, top.id],
348
+ isSolid: true // Create a solid, not just a shell
349
+ });
350
+
351
+ const geometry = await kernel.shapeToMesh(lofted.id);
352
+ // ...render
353
+ ```
354
+
355
+ ### Mirroring
356
+
357
+ Reflect a shape across a plane:
358
+
359
+ ```javascript
360
+ // Mirror a box across the YZ plane
361
+ const mirrored = await kernel.mirror({
362
+ shapeId: box.id,
363
+ plane: {
364
+ originX: 0, // Plane passes through (0, 0, 0)
365
+ originY: 0,
366
+ originZ: 0,
367
+ normalX: 1, // Normal points in X direction (mirror across YZ)
368
+ normalY: 0,
369
+ normalZ: 0
370
+ }
371
+ });
372
+
373
+ const geometry = await kernel.shapeToMesh(mirrored.id);
374
+ // ...render
375
+ ```
376
+
377
+ ---
378
+
379
+ ## STEP Import and Export
380
+
381
+ ### Export to STEP
382
+
383
+ Save your model as a standard STEP file (readable by any CAD software):
384
+
385
+ ```javascript
386
+ // Export a single shape
387
+ const box = await kernel.makeBox({width: 100, height: 50, depth: 30});
388
+ const stepData = await kernel.exportSTEP([box.id], {
389
+ fileName: 'mybox.stp',
390
+ writeAscii: false // Binary format (smaller file)
391
+ });
392
+
393
+ // Download the file
394
+ const blob = new Blob([stepData], {type: 'model/step'});
395
+ const url = URL.createObjectURL(blob);
396
+ const link = document.createElement('a');
397
+ link.href = url;
398
+ link.download = 'mybox.stp';
399
+ link.click();
400
+ URL.revokeObjectURL(url);
401
+ ```
402
+
403
+ ### Export Multiple Shapes
404
+
405
+ ```javascript
406
+ // Export an assembly of multiple shapes
407
+ const box1 = await kernel.makeBox({width: 100, height: 50, depth: 30});
408
+ const box2 = await kernel.makeBox({width: 50, height: 50, depth: 50});
409
+
410
+ const stepData = await kernel.exportSTEP([box1.id, box2.id], {
411
+ fileName: 'assembly.stp'
412
+ });
413
+
414
+ // ...download as shown above
415
+ ```
416
+
417
+ ### Import from STEP
418
+
419
+ Load a STEP file into cycleCAD:
420
+
421
+ ```javascript
422
+ // User selects a .stp file
423
+ const fileInput = document.createElement('input');
424
+ fileInput.type = 'file';
425
+ fileInput.accept = '.stp,.step';
426
+ fileInput.onchange = async (event) => {
427
+ const file = event.target.files[0];
428
+ const arrayBuffer = await file.arrayBuffer();
429
+
430
+ // Import the shape
431
+ const imported = await kernel.importSTEP(new Uint8Array(arrayBuffer));
432
+ console.log('Imported shape:', imported.id);
433
+
434
+ // Visualize it
435
+ const geometry = await kernel.shapeToMesh(imported.id);
436
+ const material = new THREE.MeshStandardMaterial({color: 0x888888});
437
+ const mesh = new THREE.Mesh(geometry, material);
438
+ scene.add(mesh);
439
+ };
440
+ fileInput.click();
441
+ ```
442
+
443
+ ### Workflow: Edit a CAD Model
444
+
445
+ ```javascript
446
+ // 1. Import a model from STEP
447
+ const imported = await kernel.importSTEP(stepData);
448
+
449
+ // 2. Analyze it
450
+ const props = await kernel.getMassProperties({shapeId: imported.id, density: 7.85});
451
+ console.log('Original weight:', props.mass, 'grams');
452
+
453
+ // 3. Modify it (e.g., add fillets)
454
+ const edges = await kernel.getEdges(imported.id);
455
+ const improved = await kernel.fillet({
456
+ shapeId: imported.id,
457
+ edgeIndices: edges.slice(0, 4).map(e => e.index),
458
+ radius: 2
459
+ });
460
+
461
+ // 4. Analyze the result
462
+ const newProps = await kernel.getMassProperties({shapeId: improved.id, density: 7.85});
463
+ console.log('Improved weight:', newProps.mass, 'grams');
464
+
465
+ // 5. Export back to STEP
466
+ const outputData = await kernel.exportSTEP([improved.id], {fileName: 'improved.stp'});
467
+ // ...download
468
+ ```
469
+
470
+ ---
471
+
472
+ ## Analyzing Your Models
473
+
474
+ ### Mass Properties
475
+
476
+ Calculate volume, area, weight, center of gravity, and moments of inertia:
477
+
478
+ ```javascript
479
+ const shape = await kernel.makeBox({width: 100, height: 50, depth: 30});
480
+
481
+ const props = await kernel.getMassProperties({
482
+ shapeId: shape.id,
483
+ density: 2.7 // g/cm³ (aluminum)
484
+ });
485
+
486
+ console.log(`Volume: ${props.volume.toFixed(2)} mm³`);
487
+ console.log(`Surface area: ${props.area.toFixed(2)} mm²`);
488
+ console.log(`Weight: ${props.mass.toFixed(2)} grams (${(props.mass / 1000).toFixed(2)} kg)`);
489
+ console.log(`Center of gravity:`, {
490
+ x: props.centerOfGravity.x.toFixed(2),
491
+ y: props.centerOfGravity.y.toFixed(2),
492
+ z: props.centerOfGravity.z.toFixed(2)
493
+ });
494
+ console.log(`Moments of inertia (kg·mm²):`, {
495
+ Ixx: props.momentOfInertia.xx.toFixed(0),
496
+ Iyy: props.momentOfInertia.yy.toFixed(0),
497
+ Izz: props.momentOfInertia.zz.toFixed(0)
498
+ });
499
+ ```
500
+
501
+ ### Bounding Box
502
+
503
+ Get the extents of a shape:
504
+
505
+ ```javascript
506
+ const bbox = await kernel.getBoundingBox(shape.id);
507
+
508
+ console.log(`Extents:`);
509
+ console.log(` X: ${bbox.minX.toFixed(2)} to ${bbox.maxX.toFixed(2)} (width: ${bbox.width.toFixed(2)} mm)`);
510
+ console.log(` Y: ${bbox.minY.toFixed(2)} to ${bbox.maxY.toFixed(2)} (height: ${bbox.height.toFixed(2)} mm)`);
511
+ console.log(` Z: ${bbox.minZ.toFixed(2)} to ${bbox.maxZ.toFixed(2)} (depth: ${bbox.depth.toFixed(2)} mm)`);
512
+ ```
513
+
514
+ ### Topology Analysis
515
+
516
+ Examine the structure of a shape:
517
+
518
+ ```javascript
519
+ const edges = await kernel.getEdges(shape.id);
520
+ const faces = await kernel.getFaces(shape.id);
521
+
522
+ console.log(`Shape has ${edges.length} edges and ${faces.length} faces`);
523
+
524
+ // Iterate over edges
525
+ edges.forEach((edgeObj) => {
526
+ console.log(`Edge ${edgeObj.index}: ${edgeObj.name}`);
527
+ });
528
+
529
+ // Iterate over faces
530
+ faces.forEach((faceObj) => {
531
+ console.log(`Face ${faceObj.index}: ${faceObj.name}`);
532
+ });
533
+ ```
534
+
535
+ ---
536
+
537
+ ## Performance and Optimization
538
+
539
+ ### Mesh Fineness
540
+
541
+ The `linearDeflection` parameter controls how fine the tessellation is:
542
+
543
+ ```javascript
544
+ // Fine mesh (slower, more triangles)
545
+ const fineMesh = await kernel.shapeToMesh(shape.id, 0.01); // 0.01mm deflection
546
+
547
+ // Coarse mesh (faster, fewer triangles)
548
+ const coarseMesh = await kernel.shapeToMesh(shape.id, 1.0); // 1.0mm deflection
549
+ ```
550
+
551
+ For visualization, `0.1mm` is usually a good balance. For 3D printing, use `0.01mm`.
552
+
553
+ ### Memory Management
554
+
555
+ The kernel caches all shapes. For long sessions, clean up:
556
+
557
+ ```javascript
558
+ // Get cache statistics
559
+ const stats = kernel.getCacheStats();
560
+ console.log(`Cached shapes: ${stats.shapeCount}`);
561
+
562
+ // Delete a specific shape
563
+ kernel.deleteShape(shape.id);
564
+
565
+ // Clear all shapes
566
+ kernel.clearCache(); // WARNING: invalidates all shape IDs
567
+ ```
568
+
569
+ ### Async Best Practices
570
+
571
+ ```javascript
572
+ // DON'T wait for each operation sequentially
573
+ const box = await kernel.makeBox({width: 100, height: 50, depth: 30});
574
+ const cyl = await kernel.makeCylinder({radius: 15, height: 60});
575
+ const sphere = await kernel.makeSphere({radius: 25});
576
+
577
+ // DO create them in parallel
578
+ const [box, cyl, sphere] = await Promise.all([
579
+ kernel.makeBox({width: 100, height: 50, depth: 30}),
580
+ kernel.makeCylinder({radius: 15, height: 60}),
581
+ kernel.makeSphere({radius: 25})
582
+ ]);
583
+ ```
584
+
585
+ ---
586
+
587
+ ## Troubleshooting
588
+
589
+ ### WASM Loading Fails
590
+
591
+ **Problem**: "Failed to load OpenCascade.js from CDN"
592
+
593
+ **Solutions**:
594
+ 1. Check internet connection
595
+ 2. Verify CDN is not blocked by firewall/proxy
596
+ 3. Check browser console for 404 errors
597
+ 4. Try a different CDN or host locally
598
+ 5. Use browser DevTools to debug network issues
599
+
600
+ ### Boolean Operation Fails
601
+
602
+ **Problem**: "Cut failed at all recovery levels"
603
+
604
+ **Causes & Solutions**:
605
+ 1. **Shapes don't overlap** — Verify the shapes actually intersect with bounding box check
606
+ 2. **Self-intersecting faces** — Use shape healing: `kernel.shell()` or `kernel.fillet()` can clean up
607
+ 3. **Tolerance issues** — Try scaling the model larger (1000mm instead of 1mm)
608
+ 4. **Coplanar faces** — Ensure faces aren't exactly coplanar; translate one shape slightly
609
+
610
+ ### Mesh Rendering is Slow
611
+
612
+ **Problem**: Visualization stutters when displaying large tessellations
613
+
614
+ **Solutions**:
615
+ 1. Use coarser mesh deflection (0.5-1.0mm instead of 0.1mm)
616
+ 2. Use instanced rendering for many identical shapes
617
+ 3. Reduce lighting quality (fewer lights, lower shadow resolution)
618
+ 4. Use THREE.js LOD (Level of Detail) system
619
+ 5. Profile with Chrome DevTools to find bottleneck
620
+
621
+ ### Fillet/Chamfer Produces Empty Shape
622
+
623
+ **Problem**: Operation returns a valid shape ID but mesh is empty
624
+
625
+ **Causes & Solutions**:
626
+ 1. **Fillet radius too large** — Radius must be smaller than local edge curvature
627
+ 2. **Wrong edge indices** — Verify edge count with `kernel.getEdges()`
628
+ 3. **Invalid shape** — Check that input shape is valid solid (not a wire or shell)
629
+
630
+ ---
631
+
632
+ ## Complete API Reference
633
+
634
+ ### Initialization
635
+
636
+ | Method | Returns | Description |
637
+ |--------|---------|-------------|
638
+ | `init([onProgress])` | `Promise<Object>` | Load WASM from CDN. Call once per session. |
639
+
640
+ ### Primitives
641
+
642
+ | Method | Parameters | Returns | Description |
643
+ |--------|-----------|---------|-------------|
644
+ | `makeBox(options)` | `{width, height, depth, x?, y?, z?}` | `{id, shape}` | Create rectangular box |
645
+ | `makeCylinder(options)` | `{radius, height, angle?}` | `{id, shape}` | Create cylinder (360° or wedge) |
646
+ | `makeSphere(options)` | `{radius}` | `{id, shape}` | Create sphere |
647
+ | `makeCone(options)` | `{radius1, radius2, height}` | `{id, shape}` | Create cone or frustum |
648
+ | `makeTorus(options)` | `{majorRadius, minorRadius}` | `{id, shape}` | Create torus |
649
+
650
+ ### Transformations
651
+
652
+ | Method | Parameters | Returns | Description |
653
+ |--------|-----------|---------|-------------|
654
+ | `extrude(options)` | `{shapeId, dirX, dirY, dirZ, depth}` | `{id, shape}` | Extrude profile along vector |
655
+ | `revolve(options)` | `{shapeId, axisX, axisY, axisZ, dirX, dirY, dirZ, angle?}` | `{id, shape}` | Revolve profile around axis |
656
+ | `sweep(options)` | `{profileId, pathId}` | `{id, shape}` | Sweep profile along path |
657
+ | `loft(options)` | `{profileIds, isSolid?}` | `{id, shape}` | Loft between profiles |
658
+ | `mirror(options)` | `{shapeId, plane}` | `{id, shape}` | Mirror across plane |
659
+
660
+ ### Boolean Operations
661
+
662
+ | Method | Parameters | Returns | Description |
663
+ |--------|-----------|---------|-------------|
664
+ | `booleanUnion(options)` | `{shapeA, shapeB}` | `{id, shape}` | Union of two shapes (3-tier recovery) |
665
+ | `booleanCut(options)` | `{shapeA, shapeB}` | `{id, shape}` | Cut shapeB from shapeA (3-tier recovery) |
666
+ | `booleanIntersect(options)` | `{shapeA, shapeB}` | `{id, shape}` | Intersection of two shapes (3-tier recovery) |
667
+
668
+ ### Modifiers
669
+
670
+ | Method | Parameters | Returns | Description |
671
+ |--------|-----------|---------|-------------|
672
+ | `fillet(options)` | `{shapeId, edgeIndices, radius}` | `{id, shape}` | Round edges with real B-Rep |
673
+ | `chamfer(options)` | `{shapeId, edgeIndices, distance}` | `{id, shape}` | Bevel edges with sharp angle |
674
+ | `shell(options)` | `{shapeId, removeFaceIndices?, thickness}` | `{id, shape}` | Create hollow shell |
675
+
676
+ ### Topology
677
+
678
+ | Method | Parameters | Returns | Description |
679
+ |--------|-----------|---------|-------------|
680
+ | `getEdges(shapeId)` | `shapeId: string` | `Promise<Array>` | Get all edges with indices |
681
+ | `getFaces(shapeId)` | `shapeId: string` | `Promise<Array>` | Get all faces with indices |
682
+
683
+ ### Visualization & Analysis
684
+
685
+ | Method | Parameters | Returns | Description |
686
+ |--------|-----------|---------|-------------|
687
+ | `shapeToMesh(shapeId, deflection?)` | `shapeId, deflection?` | `THREE.BufferGeometry` | Tessellate to Three.js mesh |
688
+ | `getMassProperties(options)` | `{shapeId, density?}` | `{volume, area, mass, cog, moi}` | Compute mass properties |
689
+ | `getBoundingBox(shapeId)` | `shapeId: string` | `{minX, minY, minZ, maxX, maxY, maxZ, width, height, depth}` | Get bounds |
690
+
691
+ ### File I/O
692
+
693
+ | Method | Parameters | Returns | Description |
694
+ |--------|-----------|---------|-------------|
695
+ | `exportSTEP(shapeIds, options?)` | `[shapeIds], {fileName?, writeAscii?}` | `Uint8Array` | Export to STEP format |
696
+ | `importSTEP(stepData)` | `ArrayBuffer \| Uint8Array` | `{id, shape, count}` | Import from STEP |
697
+
698
+ ### Utility
699
+
700
+ | Method | Parameters | Returns | Description |
701
+ |--------|-----------|---------|-------------|
702
+ | `getShapeInfo(shapeId)` | `shapeId: string` | `Object \| null` | Get cached metadata |
703
+ | `deleteShape(shapeId)` | `shapeId: string` | `boolean` | Remove shape from cache |
704
+ | `clearCache()` | (none) | (none) | Clear all cached shapes |
705
+ | `getCacheStats()` | (none) | `{shapeCount, memoryEstimate}` | Get cache statistics |
706
+
707
+ ---
708
+
709
+ ## Examples
710
+
711
+ ### Example 1: Manufacturing a Bracket
712
+
713
+ ```javascript
714
+ // 1. Create the base plate
715
+ const plate = await kernel.makeBox({width: 100, height: 50, depth: 10});
716
+
717
+ // 2. Create mounting holes
718
+ const hole1 = await kernel.makeCylinder({radius: 5, height: 15});
719
+ const hole2 = await kernel.makeCylinder({radius: 5, height: 15});
720
+
721
+ // 3. Subtract holes from plate
722
+ let bracket = await kernel.booleanCut({shapeA: plate.id, shapeB: hole1.id});
723
+ bracket = await kernel.booleanCut({shapeA: bracket.id, shapeB: hole2.id});
724
+
725
+ // 4. Round edges for safety
726
+ const edges = await kernel.getEdges(bracket.id);
727
+ bracket = await kernel.fillet({
728
+ shapeId: bracket.id,
729
+ edgeIndices: [0, 1, 2, 3], // Top edges
730
+ radius: 2
731
+ });
732
+
733
+ // 5. Analyze
734
+ const props = await kernel.getMassProperties({
735
+ shapeId: bracket.id,
736
+ density: 2.7 // Aluminum
737
+ });
738
+ console.log(`Weight: ${(props.mass / 1000).toFixed(2)} kg`);
739
+
740
+ // 6. Export for machining
741
+ const stepData = await kernel.exportSTEP([bracket.id]);
742
+ ```
743
+
744
+ ### Example 2: Organic Shape via Loft
745
+
746
+ ```javascript
747
+ // 1. Create profiles at different Z heights
748
+ const base = await kernel.makeBox({width: 50, height: 50, depth: 1});
749
+ const mid = await kernel.makeBox({width: 40, height: 40, depth: 1});
750
+ const top = await kernel.makeBox({width: 20, height: 20, depth: 1});
751
+
752
+ // 2. Loft between them
753
+ const organic = await kernel.loft({
754
+ profileIds: [base.id, mid.id, top.id],
755
+ isSolid: true
756
+ });
757
+
758
+ // 3. Visualize with smooth shading
759
+ const geometry = await kernel.shapeToMesh(organic.id, 0.1);
760
+ const material = new THREE.MeshStandardMaterial({
761
+ color: 0x6fa3d0,
762
+ metalness: 0.2,
763
+ roughness: 0.8
764
+ });
765
+ const mesh = new THREE.Mesh(geometry, material);
766
+ scene.add(mesh);
767
+ ```
768
+
769
+ ---
770
+
771
+ ## Performance Tips
772
+
773
+ 1. **Lazy load** — WASM only loads when first geometry op happens
774
+ 2. **Batch operations** — Use `Promise.all()` for parallel shape creation
775
+ 3. **Cache meshes** — Don't regenerate THREE.js geometries; cache them
776
+ 4. **Reduce precision** — Use 0.5-1.0mm deflection for viewport, 0.1mm for export
777
+ 5. **Clean up** — Call `kernel.deleteShape()` or `kernel.clearCache()` in long sessions
778
+ 6. **Monitor memory** — Use DevTools to watch for memory leaks
779
+
780
+ ---
781
+
782
+ ## Next Steps
783
+
784
+ 1. **Try the test suite** — Run `/app/tests/brep-tests.html` to see all features
785
+ 2. **Integrate into cycleCAD** — Wire `BRepKernel` into the main CAD app
786
+ 3. **Build importers** — Parse Inventor .ipt files and convert to B-Rep shapes
787
+ 4. **Implement CAM export** — Generate G-code from B-Rep shapes for 3D printing/CNC
788
+ 5. **Add collaboration** — Share B-Rep models via WebSockets or IndexedDB
789
+
790
+ ---
791
+
792
+ ## Resources
793
+
794
+ - **OpenCascade Documentation**: https://dev.opencascade.org/cdoc
795
+ - **OpenCascade.js GitHub**: https://github.com/CadQuery/opencascade.js
796
+ - **STEP Format (ISO 10303)**: https://en.wikipedia.org/wiki/ISO_10303
797
+ - **B-Rep Theory**: *An Introduction to NURBS* by Rogers & Adams
798
+
799
+ ---
800
+
801
+ **Last Updated**: 2026-03-31
802
+ **B-Rep Kernel Version**: 3.0.0