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.
- package/app/HELP-QUICK-START.md +207 -0
- package/app/HELP-SYSTEM-README.md +287 -0
- package/app/help-viewer.html +805 -0
- package/app/js/killer-features-help.json +310 -391
- package/app/js/modules/generative-design.js +159 -6
- package/app/js/modules/manufacturability.js +170 -3
- package/app/js/modules/multi-physics.js +167 -7
- package/app/js/modules/photo-to-cad.js +200 -10
- package/app/js/modules/smart-parts.js +179 -9
- package/app/js/modules/text-to-cad.js +242 -33
- package/app/tests/KILLER_FEATURES_TEST_GUIDE.md +324 -0
- package/app/tests/index.html +24 -7
- package/app/tests/killer-features-visual-test.html +1362 -0
- package/docs/KILLER-FEATURES-GUIDE.md +2728 -0
- package/docs/KILLER-FEATURES-TUTORIAL.md +1663 -5
- package/package.json +1 -1
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Photo-to-CAD Reverse Engineering Module
|
|
3
|
-
*
|
|
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
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* -
|
|
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
|
-
* @
|
|
12
|
-
*
|
|
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':
|