cyclecad 3.6.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/index.html +48 -0
- package/app/js/killer-features-help.json +310 -391
- package/app/js/modules/generative-design.js +1102 -0
- package/app/js/modules/manufacturability.js +170 -3
- package/app/js/modules/multi-physics.js +1404 -0
- package/app/js/modules/photo-to-cad.js +200 -10
- package/app/js/modules/smart-parts.js +1925 -0
- package/app/js/modules/text-to-cad.js +242 -33
- package/app/tests/KILLER_FEATURES_BATCH2_README.md +214 -0
- package/app/tests/KILLER_FEATURES_TEST_GUIDE.md +324 -0
- package/app/tests/index.html +24 -7
- package/app/tests/killer-features-batch2-tests.html +849 -0
- 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':
|