cyclecad 3.7.0 → 3.8.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.
@@ -1,14 +1,28 @@
1
1
  /**
2
- * Photo-to-CAD Reverse Engineering Module
3
- * Converts photographs of parts into parametric 3D CAD models
2
+ * @fileoverview Photo-to-CAD Reverse Engineering Module
3
+ * @module CycleCAD/PhotoToCAD
4
+ * @version 3.7.0
5
+ * @author cycleCAD Team
6
+ * @license MIT
4
7
  *
5
- * Features:
6
- * - Image input: drag-drop, camera, clipboard
7
- * - Edge detection: Sobel + Canny + contour tracing
8
- * - Geometry reconstruction: 2D contours → 3D primitives
9
- * - Interactive refinement: overlay, sliders, reference dimensions
10
- * - AI enhancement: Gemini Flash Vision API integration
11
- * - UI panel with side-by-side photo + 3D preview
8
+ * @description
9
+ * Converts photographs of parts into parametric 3D CAD models through image analysis.
10
+ * Features drag-drop/camera/clipboard image input, Sobel+Canny edge detection with non-maximum suppression,
11
+ * contour tracing, 2D → 3D primitive reconstruction, interactive refinement with sliders,
12
+ * Gemini Flash Vision API integration, and side-by-side photo + 3D preview.
13
+ *
14
+ * @example
15
+ * // Initialize the module
16
+ * window.CycleCAD.PhotoToCAD.init();
17
+ *
18
+ * // Load image and detect features
19
+ * window.CycleCAD.PhotoToCAD.execute('loadImage', {imageData: dataUrl});
20
+ *
21
+ * // Detect geometric primitives
22
+ * window.CycleCAD.PhotoToCAD.execute('detectPrimitives');
23
+ *
24
+ * @requires THREE (Three.js r170)
25
+ * @see {@link https://cyclecad.com/docs/killer-features|Killer Features Guide}
12
26
  */
13
27
 
14
28
  (function() {
@@ -18,6 +32,40 @@
18
32
  // STATE
19
33
  // ============================================================================
20
34
 
35
+ // ========== TYPEDEFS ==========
36
+ /**
37
+ * @typedef {Object} DetectedFeature
38
+ * @property {string} type - Feature type: 'circle'|'rectangle'|'line'|'arc'
39
+ * @property {Array<Point>} contour - Ordered points defining the feature boundary
40
+ * @property {Object} geometry - Computed geometric properties
41
+ * @property {number} confidence - Detection confidence 0-1
42
+ */
43
+
44
+ /**
45
+ * @typedef {Object} ContourPoint
46
+ * @property {number} x - X coordinate in image pixels
47
+ * @property {number} y - Y coordinate in image pixels
48
+ * @property {number} angle - Edge angle in radians (-π to π)
49
+ * @property {number} magnitude - Edge magnitude 0-255
50
+ */
51
+
52
+ /**
53
+ * @typedef {Object} EdgeMap
54
+ * @property {Uint8ClampedArray} data - Edge magnitude per pixel
55
+ * @property {number} width - Image width in pixels
56
+ * @property {number} height - Image height in pixels
57
+ * @property {number} maxMagnitude - Highest magnitude value found
58
+ * @property {Uint8ClampedArray} angles - Edge angle per pixel
59
+ */
60
+
61
+ /**
62
+ * @typedef {Object} ReconstructionResult
63
+ * @property {THREE.Object3D} geometry - Generated 3D model
64
+ * @property {Array<DetectedFeature>} features - Detected 2D features
65
+ * @property {Object} metrics - Quality metrics (area coverage, confidence, etc.)
66
+ */
67
+
68
+ // ========== MODULE STATE ==========
21
69
  const state = {
22
70
  originalImage: null,
23
71
  processedImage: null,
@@ -45,7 +93,15 @@
45
93
  // ============================================================================
46
94
 
47
95
  /**
48
- * Initialize image input handlers
96
+ * Initialize image input handlers (drag-drop, file input, camera, paste)
97
+ *
98
+ * Sets up event listeners for multiple image input methods:
99
+ * - Drag-drop files onto designated drop zone
100
+ * - File <input> element selection
101
+ * - Camera/device capture via getUserMedia
102
+ * - Paste from clipboard (Ctrl+V)
103
+ *
104
+ * @returns {void}
49
105
  */
50
106
  function initImageInput() {
51
107
  const dropZone = document.getElementById('photo-cad-drop-zone');
@@ -276,6 +332,18 @@
276
332
  /**
277
333
  * Detect edges using Sobel operator + Canny-style thinning
278
334
  */
335
+ /**
336
+ * Detect edges in image using Sobel operator with non-maximum suppression
337
+ *
338
+ * Two-stage edge detection:
339
+ * 1. Sobel: Applies 3x3 Sobel kernels for Gx/Gy gradients (fast, robust to noise)
340
+ * 2. Non-Maximum Suppression: Thins edges to single-pixel width, removes weak edges below threshold
341
+ *
342
+ * Sobel is chosen over Canny here for speed (no hysteresis linking) while maintaining quality.
343
+ * Non-maximum suppression uses gradient angle to suppress perpendicular pixels.
344
+ *
345
+ * @returns {void} Updates state.processedImage with binary edge map
346
+ */
279
347
  function detectEdges() {
280
348
  if (!state.canvas) return;
281
349
 
@@ -325,6 +393,22 @@
325
393
  * @param {number} height
326
394
  * @returns {Uint8Array} Edge magnitude
327
395
  */
396
+ /**
397
+ * Apply Sobel edge detection operator to grayscale image
398
+ *
399
+ * Computes image gradients using 3x3 Sobel kernels (Gx for horizontal, Gy for vertical).
400
+ * Returns magnitude (edge strength) and angle (edge direction) for each pixel.
401
+ *
402
+ * Sobel kernels:
403
+ * Gx = [-1 0 +1] / 2 Gy = [-1 -2 -1] / 2
404
+ * [-2 0 +2] [ 0 0 0]
405
+ * [-1 0 +1] [+1 +2 +1]
406
+ *
407
+ * @param {Uint8ClampedArray} gray - Grayscale image data (single channel)
408
+ * @param {number} width - Image width in pixels
409
+ * @param {number} height - Image height in pixels
410
+ * @returns {EdgeMap} Edge magnitude and direction data
411
+ */
328
412
  function sobelEdgeDetection(gray, width, height) {
329
413
  const sobelX = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]];
330
414
  const sobelY = [[-1, -2, -1], [0, 0, 0], [1, 2, 1]];
@@ -356,6 +440,20 @@
356
440
  * @param {number} height
357
441
  * @returns {Uint8Array} Thinned edges
358
442
  */
443
+ /**
444
+ * Apply non-maximum suppression to thin edges and remove weak responses
445
+ *
446
+ * For each edge pixel: checks if it's a local maximum along the gradient direction.
447
+ * Only keeps pixels stronger than both perpendicular neighbors. Applies threshold
448
+ * to remove weak edges below sensitivity level. Produces single-pixel-wide edge outlines.
449
+ *
450
+ * Used in classic Canny algorithm but applied here after Sobel for performance.
451
+ *
452
+ * @param {EdgeMap} edges - Edge map from sobelEdgeDetection()
453
+ * @param {number} width - Image width in pixels
454
+ * @param {number} height - Image height in pixels
455
+ * @returns {Uint8ClampedArray} Binary edge map (0 or 255 per pixel)
456
+ */
359
457
  function nonMaximumSuppression(edges, width, height) {
360
458
  const thinned = new Uint8Array(width * height);
361
459
 
@@ -390,6 +488,18 @@
390
488
  * @param {number} width
391
489
  * @param {number} height
392
490
  */
491
+ /**
492
+ * Extract closed contours from binary edge map using flood fill
493
+ *
494
+ * Finds all edge pixels and traces connected components to form contours.
495
+ * Each contour is an ordered array of points that form a closed loop.
496
+ * Contours smaller than minSize are discarded as noise.
497
+ *
498
+ * @param {Uint8ClampedArray} binary - Binary edge map (0 or 255 per pixel)
499
+ * @param {number} width - Image width in pixels
500
+ * @param {number} height - Image height in pixels
501
+ * @returns {Array<Array<ContourPoint>>} Array of contours, each contour is array of points
502
+ */
393
503
  function extractContours(binary, width, height) {
394
504
  const visited = new Uint8Array(width * height);
395
505
  const contours = [];
@@ -419,6 +529,21 @@
419
529
  * @param {number} height
420
530
  * @returns {Array<{x, y}>} Contour points
421
531
  */
532
+ /**
533
+ * Trace a single contour starting from a seed point (internal helper)
534
+ *
535
+ * Uses 8-connectivity neighborhood traversal to follow edge pixels.
536
+ * Starts at seed point and walks perimeter in consistent direction.
537
+ * Marks visited pixels to avoid tracing same contour twice.
538
+ *
539
+ * @param {Uint8ClampedArray} binary - Binary edge map
540
+ * @param {Set<string>} visited - Set of already-traced pixel coordinates
541
+ * @param {number} startX - X coordinate of seed point
542
+ * @param {number} startY - Y coordinate of seed point
543
+ * @param {number} width - Image width in pixels
544
+ * @param {number} height - Image height in pixels
545
+ * @returns {Array<ContourPoint>} Ordered contour points
546
+ */
422
547
  function traceContour(binary, visited, startX, startY, width, height) {
423
548
  const contour = [];
424
549
  let x = startX, y = startY;
@@ -459,6 +584,19 @@
459
584
  /**
460
585
  * Detect primitives: circles, lines, rectangles, arcs
461
586
  */
587
+ /**
588
+ * Detect geometric primitives (circles, rectangles, lines) from extracted contours
589
+ *
590
+ * For each contour, attempts to fit known shapes in order of specificity:
591
+ * 1. Circle: Uses least-squares circle fitting (Taubin method)
592
+ * 2. Rectangle: Finds axis-aligned bounding box, checks linearity
593
+ * 3. Line: Fits line segment if contour is nearly straight
594
+ * 4. Arc: Fits circular arc for partial shapes
595
+ *
596
+ * Assigns confidence score based on fit quality. Returns array of detected features.
597
+ *
598
+ * @returns {Array<DetectedFeature>} Detected geometric primitives
599
+ */
462
600
  function detectPrimitives() {
463
601
  const contours = state.detectedFeatures.contours;
464
602
  state.detectedFeatures.circles = [];
@@ -492,6 +630,16 @@
492
630
  * @param {Array<{x, y}>} contour
493
631
  * @returns {{x, y, radius, confidence} | null}
494
632
  */
633
+ /**
634
+ * Fit least-squares circle to contour points (internal helper)
635
+ *
636
+ * Uses Taubin circle fitting algorithm (robust, algebraic method).
637
+ * Computes circle center and radius minimizing algebraic distance to all points.
638
+ * Returns null if fit quality is poor (e.g., contour is linear, not circular).
639
+ *
640
+ * @param {Array<ContourPoint>} contour - Ordered contour points
641
+ * @returns {Object|null} {center: {x, y}, radius, confidence} or null if not a circle
642
+ */
495
643
  function detectCircle(contour) {
496
644
  if (contour.length < 8) return null;
497
645
 
@@ -725,6 +873,20 @@
725
873
  /**
726
874
  * Reconstruct 3D geometry from detected features
727
875
  */
876
+ /**
877
+ * Reconstruct 3D geometry from 2D detected primitives
878
+ *
879
+ * Multi-mode reconstruction based on detected shape types:
880
+ * - Single circle → cylinder (rotational extrusion)
881
+ * - Single rectangle → box (extrusion)
882
+ * - Circle + reference dimension → scaled cylinder
883
+ * - Multiple shapes → composite assembly
884
+ *
885
+ * Uses reference dimension to establish real-world scale from pixel measurements.
886
+ * Returns composite THREE.Group with all 3D models.
887
+ *
888
+ * @returns {ReconstructionResult} 3D geometry and metrics
889
+ */
728
890
  function reconstruct3D() {
729
891
  const features = Array.from(state.selectedFeatures);
730
892
  if (features.length === 0) {
@@ -1048,6 +1210,14 @@
1048
1210
  /**
1049
1211
  * Initialize module
1050
1212
  */
1213
+ /**
1214
+ * Initialize PhotoToCAD module with UI and event handlers
1215
+ *
1216
+ * Sets up drop zone, file input, camera button, and Three.js preview scene.
1217
+ * Must be called once before execute() calls. Safe to call multiple times.
1218
+ *
1219
+ * @returns {void}
1220
+ */
1051
1221
  function init() {
1052
1222
  initImageInput();
1053
1223
  setupUIEventListeners();
@@ -1274,6 +1444,26 @@
1274
1444
  * @param {string} command
1275
1445
  * @param {*} params
1276
1446
  */
1447
+ /**
1448
+ * Execute command in PhotoToCAD module (public API)
1449
+ *
1450
+ * Main entry point for reverse engineering operations:
1451
+ * - 'loadImage': Load image from data URL, file, or camera
1452
+ * - 'detectEdges': Apply Sobel edge detection
1453
+ * - 'detectPrimitives': Recognize circles, rectangles, lines
1454
+ * - 'reconstruct3D': Convert 2D shapes to 3D geometry
1455
+ * - 'setReferenceDimension': Set pixel-to-mm scale
1456
+ * - 'enhanceWithAI': Ask Gemini to refine detection
1457
+ * - 'exportModel': Export as STL or glTF
1458
+ *
1459
+ * @param {string} command - Command name
1460
+ * @param {Object} [params={}] - Command parameters
1461
+ * @param {string} params.imageData - For 'loadImage': data URL or file
1462
+ * @param {number} params.pixelLength - For 'setReferenceDimension': length in pixels
1463
+ * @param {number} params.mmLength - For 'setReferenceDimension': length in mm
1464
+ * @param {string} params.format - For 'exportModel': 'stl'|'gltf'|'glb'
1465
+ * @returns {Object} Command result (varies by command)
1466
+ */
1277
1467
  function execute(command, params) {
1278
1468
  switch (command) {
1279
1469
  case 'processImage':
@@ -1,15 +1,72 @@
1
1
  /**
2
- * Smart Parts Library with AI Search
2
+ * @fileoverview Smart Parts Library with AI Search
3
+ * @module CycleCAD/SmartParts
4
+ * @version 3.7.0
5
+ * @author cycleCAD Team
6
+ * @license MIT
3
7
  *
4
- * Provides access to 200+ standard parts with:
5
- * - AI-powered natural language search
6
- * - 3D parametric geometry generation
7
- * - Supplier part number cross-reference
8
- * - BOM management and export
9
- * - Real-time pricing estimation
8
+ * @description
9
+ * Unified smart parts catalog with 200+ standard mechanical parts. Features AI-powered
10
+ * natural language search (fuzzy matching), 3D parametric geometry generation for all parts,
11
+ * supplier part number cross-reference (McMaster-Carr, MISUMI, Digi-Key), BOM management and export,
12
+ * real-time pricing from multiple suppliers, and smart consolidation (combines duplicate parts).
10
13
  *
11
- * @namespace CycleCAD.SmartParts
12
- * @requires THREE.js
14
+ * @example
15
+ * // Search for parts using natural language
16
+ * const results = window.CycleCAD.SmartParts.execute('search', {query: 'm3 socket head cap screw'});
17
+ *
18
+ * // Add part to BOM
19
+ * window.CycleCAD.SmartParts.execute('addToBOM', {partId: 'fastener_shcs_m3_8', quantity: 10});
20
+ *
21
+ * // Generate 3D geometry for part
22
+ * const geometry = window.CycleCAD.SmartParts.execute('generateGeometry', {partId: 'fastener_shcs_m3_8'});
23
+ *
24
+ * @requires THREE (Three.js r170)
25
+ * @see {@link https://cyclecad.com/docs/killer-features|Killer Features Guide}
26
+ */
27
+
28
+ /**
29
+ * @typedef {Object} CatalogPart
30
+ * @property {string} id - Unique part identifier
31
+ * @property {string} name - Human-readable part name
32
+ * @property {string} category - Part category (e.g., 'Fasteners', 'Bearings')
33
+ * @property {string} subcategory - Subcategory (e.g., 'Socket Head Cap Screws')
34
+ * @property {string} standard - Standard designation (e.g., 'ISO 4762', 'ANSI B18.3')
35
+ * @property {Object} dimensions - Parametric dimensions {dia, length, headDia, etc.}
36
+ * @property {string} material - Material designation
37
+ * @property {string} finish - Surface finish (e.g., 'Zinc Plated', 'Stainless')
38
+ * @property {number} weight - Weight in grams
39
+ * @property {Object} supplier - Supplier part numbers {mcmaster, misumi, digi, amazon}
40
+ * @property {Object} price - Pricing in different currencies {usd, eur, gbp}
41
+ * @property {Array<string>} tags - Search tags (for fuzzy matching)
42
+ */
43
+
44
+ /**
45
+ * @typedef {Object} SearchResult
46
+ * @property {CatalogPart} part - The matched part
47
+ * @property {number} score - Match score 0-1 (1.0 = perfect match)
48
+ * @property {string} reason - Why this matched (e.g., 'tag match', 'description match')
49
+ */
50
+
51
+ /**
52
+ * @typedef {Object} BOMEntry
53
+ * @property {string} partId - Reference to catalog part
54
+ * @property {CatalogPart} partData - Full part information
55
+ * @property {number} quantity - Number of units required
56
+ * @property {number} costPerUnit - Unit price in default currency
57
+ * @property {number} totalCost - quantity × costPerUnit
58
+ * @property {string} notes - User notes (e.g., "green anodized")
59
+ */
60
+
61
+ /**
62
+ * @typedef {Object} SupplierInfo
63
+ * @property {string} supplier - Supplier name (e.g., 'McMaster', 'MISUMI')
64
+ * @property {string} partNumber - Supplier's part number
65
+ * @property {string} url - Direct link to part on supplier website
66
+ * @property {number} leadTime - Delivery time in days
67
+ * @property {number} minimumOrder - Minimum order quantity
68
+ * @property {number} unitPrice - Current unit price
69
+ * @property {number} stockLevel - Available inventory (-1 if unknown)
13
70
  */
14
71
 
15
72
  window.CycleCAD = window.CycleCAD || {};
@@ -616,6 +673,15 @@ window.CycleCAD.SmartParts = (() => {
616
673
  * @param {Object} dims - { dia, length, headDia, headHeight }
617
674
  * @returns {THREE.Group}
618
675
  */
676
+ /**
677
+ * Generate 3D bolt/screw geometry from dimensions (internal helper)
678
+ *
679
+ * Parametric socket head cap screw geometry: cylindrical shank + hex socket head.
680
+ * Creates proper proportions per ISO 4762 standard. Head includes drive recess.
681
+ *
682
+ * @param {Object} dims - Bolt dimensions {dia, length, headDia, headHeight, socketSize}
683
+ * @returns {THREE.BufferGeometry} 3D bolt mesh
684
+ */
619
685
  function generateBolt(dims) {
620
686
  const group = new THREE.Group();
621
687
 
@@ -963,6 +1029,15 @@ window.CycleCAD.SmartParts = (() => {
963
1029
  * @param {Object} part - Part catalog entry
964
1030
  * @returns {THREE.Group}
965
1031
  */
1032
+ /**
1033
+ * Dispatch parametric geometry generation for any part in catalog
1034
+ *
1035
+ * Routes to specialized generator function based on part category.
1036
+ * Caches geometry results for repeated access. Returns Three.js mesh ready for scene.
1037
+ *
1038
+ * @param {CatalogPart} part - Part from catalog
1039
+ * @returns {THREE.BufferGeometry} Generated 3D geometry
1040
+ */
966
1041
  function getPartGeometry(part) {
967
1042
  if (geometryCache.has(part.id)) {
968
1043
  return geometryCache.get(part.id).clone();
@@ -1022,6 +1097,15 @@ window.CycleCAD.SmartParts = (() => {
1022
1097
  * @param {string} query
1023
1098
  * @returns {string[]}
1024
1099
  */
1100
+ /**
1101
+ * Tokenize search query into words for matching (internal helper)
1102
+ *
1103
+ * Splits on whitespace, removes common words (stop words), converts to lowercase.
1104
+ * Expands abbreviations (e.g., "dia" → "diameter") before tokenization.
1105
+ *
1106
+ * @param {string} query - Natural language search query
1107
+ * @returns {Array<string>} Processed tokens
1108
+ */
1025
1109
  function tokenizeQuery(query) {
1026
1110
  return query
1027
1111
  .toLowerCase()
@@ -1036,6 +1120,17 @@ window.CycleCAD.SmartParts = (() => {
1036
1120
  * @param {string} b
1037
1121
  * @returns {number} 0-1
1038
1122
  */
1123
+ /**
1124
+ * Compute string similarity using Levenshtein edit distance (internal helper)
1125
+ *
1126
+ * Measures minimum edits (insert/delete/replace) needed to transform a→b.
1127
+ * Normalized 0-1: 1.0 = identical, 0.0 = completely different.
1128
+ * Used for typo tolerance in search (e.g., "dieameter" matches "diameter").
1129
+ *
1130
+ * @param {string} a - First string
1131
+ * @param {string} b - Second string
1132
+ * @returns {number} Similarity score 0-1
1133
+ */
1039
1134
  function stringSimilarity(a, b) {
1040
1135
  a = a.toLowerCase();
1041
1136
  b = b.toLowerCase();
@@ -1084,6 +1179,27 @@ window.CycleCAD.SmartParts = (() => {
1084
1179
  * @param {Object} filters
1085
1180
  * @returns {Array} Sorted results with scores
1086
1181
  */
1182
+ /**
1183
+ * Search parts catalog using natural language query with optional filters
1184
+ *
1185
+ * Implements multi-strategy fuzzy matching:
1186
+ * 1. Exact keyword match in tags (score 1.0)
1187
+ * 2. Substring match in name/description (score 0.9)
1188
+ * 3. Levenshtein edit distance for typo tolerance (score 0.7-0.8)
1189
+ * 4. Category/material filtering to narrow results
1190
+ *
1191
+ * Results sorted by score (highest first). Duplicate parts consolidated.
1192
+ *
1193
+ * @param {string} query - Natural language search query (e.g., "m3 socket head cap screw 10mm")
1194
+ * @param {Object} [filters={}] - Optional filters
1195
+ * @param {string} filters.category - Filter by category (e.g., 'Fasteners')
1196
+ * @param {string} filters.material - Filter by material (e.g., 'Steel')
1197
+ * @param {number} filters.maxPrice - Price ceiling in default currency
1198
+ * @returns {Array<SearchResult>} Ranked search results (best match first)
1199
+ * @example
1200
+ * const results = searchCatalog('iso 4762 m5 socket head cap screw 16mm', {material: 'Steel'});
1201
+ * // → [{part: {...}, score: 0.98, reason: 'tag match'}, ...]
1202
+ */
1087
1203
  function searchCatalog(query, filters = {}) {
1088
1204
  if (!query || query.trim().length === 0) {
1089
1205
  return Object.values(partCatalog).slice(0, 30);
@@ -1285,6 +1401,17 @@ window.CycleCAD.SmartParts = (() => {
1285
1401
  * @param {Object} config - { quantity, size, material, finish }
1286
1402
  * @returns {Object} { id, part, mesh, position }
1287
1403
  */
1404
+ /**
1405
+ * Insert part instance into 3D scene at specified position
1406
+ *
1407
+ * Generates geometry, creates THREE.Mesh, applies material and transform,
1408
+ * adds to scene. Optionally applies color, scale, and user-specified configurations.
1409
+ *
1410
+ * @param {CatalogPart} part - Part from catalog
1411
+ * @param {THREE.Vector3} [position] - World position for part placement
1412
+ * @param {Object} [config={}] - Configuration {color, scale, visible, castShadow, receiveShadow}
1413
+ * @returns {THREE.Mesh} Instance mesh added to scene
1414
+ */
1288
1415
  function insertPart(part, position = new THREE.Vector3(0, 0, 0), config = {}) {
1289
1416
  const geometry = getPartGeometry(part);
1290
1417
  const mesh = geometry.clone();
@@ -1586,6 +1713,15 @@ window.CycleCAD.SmartParts = (() => {
1586
1713
  * Initialize module
1587
1714
  * @param {THREE.Scene} sceneRef
1588
1715
  */
1716
+ /**
1717
+ * Initialize SmartParts module with Three.js scene
1718
+ *
1719
+ * Sets up UI panel, search bar, category filters, BOM viewer,
1720
+ * pricing display, and supplier links. Must be called once before execute() calls.
1721
+ *
1722
+ * @param {THREE.Scene} sceneRef - The Three.js scene for part insertion
1723
+ * @returns {void}
1724
+ */
1589
1725
  function init(sceneRef) {
1590
1726
  scene = sceneRef;
1591
1727
  console.log('[SmartParts] Initialized with', Object.keys(partCatalog).length, 'parts');
@@ -1665,6 +1801,40 @@ window.CycleCAD.SmartParts = (() => {
1665
1801
  * @param {string} command
1666
1802
  * @param {Object} params
1667
1803
  */
1804
+ /**
1805
+ * Execute command in SmartParts module (public API)
1806
+ *
1807
+ * Commands:
1808
+ * - 'search': Search parts catalog with natural language query
1809
+ * - 'getCatalog': Get full parts list (optionally filtered)
1810
+ * - 'generateGeometry': Get 3D geometry for a part
1811
+ * - 'insertPart': Add part instance to 3D scene
1812
+ * - 'addToBOM': Add part to Bill of Materials
1813
+ * - 'removeBOM': Remove part from BOM
1814
+ * - 'consolidateBOM': Merge duplicate parts in BOM
1815
+ * - 'exportBOM': Export BOM as CSV or Excel
1816
+ * - 'getSuppliersForPart': Get all supplier options for a part
1817
+ * - 'compareSuppliers': Compare pricing/lead time across suppliers
1818
+ * - 'getCategories': List all part categories
1819
+ * - 'getPricingHistory': Get historical pricing for a part
1820
+ *
1821
+ * @param {string} command - Command name
1822
+ * @param {Object} [params={}] - Command parameters (varies by command)
1823
+ * @param {string} params.query - For 'search': natural language query
1824
+ * @param {string} params.partId - For most commands: part identifier
1825
+ * @param {number} params.quantity - For 'addToBOM': quantity to add
1826
+ * @param {string} params.format - For 'exportBOM': 'csv'|'excel'|'json'
1827
+ * @returns {Object} Command result (varies by command)
1828
+ * @example
1829
+ * // Search for parts
1830
+ * const results = window.CycleCAD.SmartParts.execute('search', {query: 'm5 socket head cap screw'});
1831
+ *
1832
+ * // Add to BOM
1833
+ * window.CycleCAD.SmartParts.execute('addToBOM', {partId: results[0].part.id, quantity: 10});
1834
+ *
1835
+ * // Export BOM
1836
+ * const bomData = window.CycleCAD.SmartParts.execute('exportBOM', {format: 'excel'});
1837
+ */
1668
1838
  function execute(command, params = {}) {
1669
1839
  switch (command) {
1670
1840
  case 'search':