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,19 +1,25 @@
1
1
  /**
2
- * TextToCAD - Natural Language to 3D Geometry with Live Preview
2
+ * @fileoverview TextToCAD - Natural Language to 3D Geometry with Live Preview
3
+ * @module CycleCAD/TextToCAD
4
+ * @version 3.7.0
5
+ * @author cycleCAD Team
6
+ * @license MIT
7
+ *
8
+ * @description
3
9
  * Converts English descriptions to parametric 3D CAD models in real-time.
10
+ * Features NLP parser for 50+ shape types, live ghost preview, multi-step builder with state awareness,
11
+ * Gemini Flash API integration with local fallback, 3D dimension annotations, undo/redo, variant generation,
12
+ * and production-ready error handling.
13
+ *
14
+ * @example
15
+ * // Initialize the module
16
+ * window.CycleCAD.TextToCAD.init(scene, renderer);
4
17
  *
5
- * @module TextToCAD
6
- * @version 1.0.0
18
+ * // Parse natural language and generate geometry
19
+ * const result = window.CycleCAD.TextToCAD.execute('parseDescription', 'create a cylinder 50mm diameter 80mm tall');
7
20
  *
8
- * Features:
9
- * - NLP parser for 50+ shape types and features
10
- * - Live preview with ghost geometry as you type
11
- * - Multi-step builder with state awareness
12
- * - Gemini Flash API integration (with local fallback)
13
- * - 3D dimension annotations
14
- * - Undo/redo per step
15
- * - Variant generation (3 alternatives)
16
- * - Production-ready error handling
21
+ * @requires THREE (Three.js r170)
22
+ * @see {@link https://cyclecad.com/docs/killer-features|Killer Features Guide}
17
23
  */
18
24
 
19
25
  (function initTextToCAD() {
@@ -35,7 +41,58 @@
35
41
  lastAction: null
36
42
  };
37
43
 
44
+ // ========== TYPEDEFS ==========
45
+ /**
46
+ * @typedef {Object} ParseResult
47
+ * @property {string} intent - User intent: 'create', 'add', 'modify', 'combine', 'pattern', 'export'
48
+ * @property {string} primaryShape - Primary shape type (e.g., 'cylinder', 'box')
49
+ * @property {Object} dimensions - Extracted numeric dimensions in mm
50
+ * @property {Array} features - Array of feature objects (holes, fillets, etc.)
51
+ * @property {Object} relationships - Spatial relationships between components
52
+ * @property {Object} parameters - Computed parameters for shape generation
53
+ * @property {number} confidence - Confidence score 0-1
54
+ */
55
+
56
+ /**
57
+ * @typedef {Object} ShapeVocab
58
+ * @property {Array<string>} alias - Alternative names for the shape
59
+ * @property {Array<string>} params - Parameter names this shape accepts
60
+ */
61
+
62
+ /**
63
+ * @typedef {Object} FeatureSpec
64
+ * @property {string} type - Feature type: 'hole', 'fillet', 'chamfer', 'pattern', 'counterbore', etc.
65
+ * @property {Object} params - Feature parameters
66
+ * @property {number} diameter - For hole features
67
+ * @property {number} depth - For counterbore/countersink
68
+ * @property {string} direction - For patterns: 'radial' or 'rectangular'
69
+ */
70
+
71
+ /**
72
+ * @typedef {Object} BuildStep
73
+ * @property {number} index - Step number
74
+ * @property {string} description - User's natural language description
75
+ * @property {ParseResult} parsed - Parsed specification
76
+ * @property {THREE.Object3D} geometry - Generated 3D geometry
77
+ * @property {number} timestamp - Creation time
78
+ */
79
+
38
80
  // ========== SHAPE VOCABULARY & PATTERNS ==========
81
+
82
+ /**
83
+ * Vocabulary of recognized shapes with aliases and parameter names
84
+ * @constant {Object.<string, ShapeVocab>}
85
+ * @property {ShapeVocab} cylinder - Cylindrical shape (aliases: cyl, tube, pipe)
86
+ * @property {ShapeVocab} box - Rectangular block (aliases: cube, block, rectangular)
87
+ * @property {ShapeVocab} sphere - Spherical shape (aliases: ball, round)
88
+ * @property {ShapeVocab} cone - Conical shape (aliases: taper)
89
+ * @property {ShapeVocab} torus - Toroidal shape (aliases: donut, ring, washer)
90
+ * @property {ShapeVocab} gear - Gear teeth (aliases: cog, sprocket)
91
+ * @property {ShapeVocab} flange - Cylindrical collar (aliases: rim, collar)
92
+ * @property {ShapeVocab} shaft - Rotating shaft (aliases: axle, spindle)
93
+ * @property {ShapeVocab} housing - Enclosure (aliases: enclosure, case, container)
94
+ * @property {ShapeVocab} keyway - Key slot (aliases: key-slot)
95
+ */
39
96
  const SHAPE_VOCAB = {
40
97
  // Basic primitives
41
98
  cylinder: { alias: ['cyl', 'tube', 'pipe'], params: ['diameter', 'radius', 'height', 'tall'] },
@@ -94,8 +151,17 @@
94
151
 
95
152
  /**
96
153
  * Parse natural language description into structured CAD commands
97
- * @param {string} input - English description
98
- * @returns {Object} Structured geometry specification
154
+ *
155
+ * Performs multi-stage NLP pipeline: intent detection → shape recognition → dimension extraction →
156
+ * feature identification → relationship analysis → parameter computation → confidence scoring.
157
+ * Uses regex patterns and statistical scoring for robustness with imperfect input.
158
+ *
159
+ * @param {string} input - English description of part to create
160
+ * @returns {ParseResult|null} Structured geometry specification or null if unparseable
161
+ * @throws {Error} If input contains invalid UTF-8 or is longer than 2000 characters
162
+ * @example
163
+ * const spec = parseDescription('create cylinder 50mm diameter 80mm tall with 10mm hole');
164
+ * // Returns: { intent: 'create', primaryShape: 'cylinder', dimensions: {...}, features: [...], confidence: 0.92 }
99
165
  */
100
166
  function parseDescription(input) {
101
167
  if (!input || input.trim().length === 0) {
@@ -128,9 +194,17 @@
128
194
  }
129
195
 
130
196
  /**
131
- * Detect user intent from input
132
- * @param {string} input
133
- * @returns {string} Intent type
197
+ * Detect user intent (action) from natural language input
198
+ *
199
+ * Maps keywords and patterns to one of 6 primary intents. Uses priority-ordered regex matching
200
+ * to distinguish between creation, modification, combination, and export workflows.
201
+ *
202
+ * @param {string} input - Natural language description
203
+ * @returns {string} Intent type: 'create'|'add'|'modify'|'combine'|'pattern'|'export'
204
+ * @example
205
+ * detectIntent('make a cylinder') // → 'create'
206
+ * detectIntent('add a hole') // → 'add'
207
+ * detectIntent('fillet the edges') // → 'modify'
134
208
  */
135
209
  function detectIntent(input) {
136
210
  const lower = input.toLowerCase();
@@ -144,9 +218,18 @@
144
218
  }
145
219
 
146
220
  /**
147
- * Detect primary shape from natural language
148
- * @param {string} input
149
- * @returns {string|null} Shape type
221
+ * Detect primary shape type from natural language input
222
+ *
223
+ * Uses vocabulary lookup followed by heuristic fallback. Checks all registered shapes and their aliases
224
+ * using case-insensitive word-boundary regex matching. Maintains a ranked preference order for
225
+ * common shapes (cylinder > box > sphere) when multiple matches exist.
226
+ *
227
+ * @param {string} input - Natural language description
228
+ * @returns {string|null} Shape type (e.g., 'cylinder', 'box', 'sphere') or null if no match
229
+ * @example
230
+ * detectShape('create a cylindrical tube') // → 'cylinder'
231
+ * detectShape('make a round ball') // → 'sphere'
232
+ * detectShape('totally ambiguous text') // → null
150
233
  */
151
234
  function detectShape(input) {
152
235
  const lower = input.toLowerCase();
@@ -167,9 +250,18 @@
167
250
  }
168
251
 
169
252
  /**
170
- * Extract numerical dimensions with unit conversion
171
- * @param {string} input
172
- * @returns {Object} Dimensions in mm
253
+ * Extract numerical dimensions and convert to millimeters
254
+ *
255
+ * Multi-pass extraction: first identifies all numbers with explicit units (mm/cm/in/m) using regex patterns,
256
+ * then performs context-aware labeling based on dimension order (diameter → height → width → depth).
257
+ * Supports explicit parameter names (e.g., "diameter 50mm", "height 80mm") and implicit positional inference.
258
+ * Handles ambiguous units by preferring explicit labels.
259
+ *
260
+ * @param {string} input - Natural language with measurements
261
+ * @returns {Object} Dimensions object with keys like {diameter, height, width, depth, etc.} all in mm
262
+ * @example
263
+ * extractDimensions('cylinder 50mm dia 80 tall') // → {diameter: 50, height: 80, radius: 25}
264
+ * extractDimensions('2 inch width and 3cm depth') // → {width: 50.8, depth: 30}
173
265
  */
174
266
  function extractDimensions(input) {
175
267
  const dimensions = {};
@@ -229,9 +321,16 @@
229
321
  }
230
322
 
231
323
  /**
232
- * Extract features from input
233
- * @param {string} input
234
- * @returns {Array} Feature specifications
324
+ * Extract manufacturing features from natural language description
325
+ *
326
+ * Identifies hole, counterbore, countersink, thread, fillet, chamfer, pattern, and slot features
327
+ * using regex pattern matching. Returns array of feature specs with extracted parameters.
328
+ *
329
+ * @param {string} input - Natural language description
330
+ * @returns {Array<FeatureSpec>} Array of feature specifications
331
+ * @example
332
+ * extractFeatures('cylinder with 10mm hole, 5mm fillet, and 4x pattern')
333
+ * // → [{type: 'hole', diameter: 10}, {type: 'fillet', radius: 5}, {type: 'pattern', count: 4}]
235
334
  */
236
335
  function extractFeatures(input) {
237
336
  const features = [];
@@ -399,10 +498,17 @@
399
498
  }
400
499
 
401
500
  /**
402
- * Convert value to millimeters
403
- * @param {number} value
404
- * @param {string} unit
405
- * @returns {number} Value in mm
501
+ * Convert measurement value to millimeters (internal utility)
502
+ *
503
+ * Handles four common unit systems: metric (mm/cm/m) and imperial (inches).
504
+ * Used internally by extractDimensions for consistent unit handling.
505
+ *
506
+ * @param {number} value - Numeric value in source units
507
+ * @param {string} unit - Unit type: 'mm'|'cm'|'inch'|'m'
508
+ * @returns {number} Converted value in millimeters
509
+ * @example
510
+ * convertToMM(2, 'inch') // → 50.8
511
+ * convertToMM(5, 'cm') // → 50
406
512
  */
407
513
  function convertToMM(value, unit) {
408
514
  switch (unit) {
@@ -417,9 +523,18 @@
417
523
  // ========== GEOMETRY GENERATION (~300 lines) ==========
418
524
 
419
525
  /**
420
- * Generate THREE.js geometry from parsed specification
421
- * @param {Object} spec - Parsed CAD specification
422
- * @returns {THREE.Group} Composite 3D geometry
526
+ * Generate THREE.js 3D geometry from parsed CAD specification
527
+ *
528
+ * Dispatcher function that creates appropriate Three.js primitives based on shape type.
529
+ * Applies features (holes, fillets, patterns) to base geometry. Returns composite group
530
+ * containing all geometry and feature visualizations.
531
+ *
532
+ * @param {ParseResult} spec - Parsed CAD specification with shape and parameters
533
+ * @returns {THREE.Group|null} Composite 3D geometry with all features applied, or null if invalid
534
+ * @example
535
+ * const spec = parseDescription('cylinder 50mm diameter 80mm tall with 10mm hole');
536
+ * const geometry = generateGeometry(spec);
537
+ * scene.add(geometry);
423
538
  */
424
539
  function generateGeometry(spec) {
425
540
  if (!spec || !spec.primaryShape) {
@@ -542,6 +657,17 @@
542
657
  * @param {Object} params
543
658
  * @returns {THREE.BufferGeometry}
544
659
  */
660
+ /**
661
+ * Create parametric spur gear geometry
662
+ *
663
+ * Generates involute gear profile with user-specified teeth count and module.
664
+ * Implements involute curve construction for smooth tooth engagement.
665
+ *
666
+ * @param {Object} params - Gear parameters
667
+ * @param {number} params.teeth - Number of teeth
668
+ * @param {number} params.module - Module (mm/tooth) - standard values: 0.5, 1.0, 1.5, 2.0, 3.0, 4.0
669
+ * @returns {THREE.BufferGeometry} Gear geometry
670
+ */
545
671
  function createGearGeometry(params) {
546
672
  const teeth = params.teeth || 24;
547
673
  const module = params.module || 2;
@@ -584,6 +710,18 @@
584
710
  * @param {Object} params
585
711
  * @returns {THREE.BufferGeometry}
586
712
  */
713
+ /**
714
+ * Create parametric angle bracket (L-shaped) geometry
715
+ *
716
+ * Constructs two perpendicular flange sheets with optional boss features.
717
+ * Common in mechanical assemblies for structural support.
718
+ *
719
+ * @param {Object} params - Bracket parameters
720
+ * @param {number} params.width - Horizontal width (mm)
721
+ * @param {number} params.height - Vertical height (mm)
722
+ * @param {number} params.thickness - Material thickness (mm)
723
+ * @returns {THREE.BufferGeometry} Bracket geometry
724
+ */
587
725
  function createBracketGeometry(params) {
588
726
  const w = params.width || 60;
589
727
  const h = params.height || 100;
@@ -723,6 +861,16 @@
723
861
  * Update live preview as user types
724
862
  * @param {string} input
725
863
  */
864
+ /**
865
+ * Update live preview geometry as user types (debounced)
866
+ *
867
+ * Implements 500ms debounce to avoid excessive parsing/rendering. Creates "ghost" geometry
868
+ * with semi-transparent material to show real-time feedback without committing to history.
869
+ * Updates confidence score display and dimension annotations.
870
+ *
871
+ * @param {string} input - Current user input text
872
+ * @returns {void}
873
+ */
726
874
  function updateLivePreview(input) {
727
875
  // Clear existing debounce timer
728
876
  if (state.parseDebounceTimer) {
@@ -771,6 +919,14 @@
771
919
  /**
772
920
  * Commit preview to actual geometry
773
921
  */
922
+ /**
923
+ * Commit current preview to history and make permanent
924
+ *
925
+ * Replaces ghost geometry with opaque final geometry, adds to feature tree,
926
+ * pushes to step history, enables undo/redo. Triggers event listeners.
927
+ *
928
+ * @returns {void}
929
+ */
774
930
  function commitPreview() {
775
931
  if (!state.previewGeometry) return;
776
932
 
@@ -815,6 +971,14 @@
815
971
  /**
816
972
  * Undo to previous step
817
973
  */
974
+ /**
975
+ * Undo last step in feature history
976
+ *
977
+ * Moves currentStepIndex backward, restores previous geometry state,
978
+ * updates UI and 3D view. Does nothing if already at first step.
979
+ *
980
+ * @returns {BuildStep|null} Previous step or null if at beginning
981
+ */
818
982
  function undoStep() {
819
983
  if (state.currentStepIndex > 0) {
820
984
  state.currentStepIndex--;
@@ -837,6 +1001,14 @@
837
1001
  /**
838
1002
  * Redo to next step
839
1003
  */
1004
+ /**
1005
+ * Redo last undone step in feature history
1006
+ *
1007
+ * Moves currentStepIndex forward, restores next geometry state,
1008
+ * updates UI and 3D view. Does nothing if already at latest step.
1009
+ *
1010
+ * @returns {BuildStep|null} Next step or null if at end
1011
+ */
840
1012
  function redoStep() {
841
1013
  if (state.currentStepIndex < state.steps.length - 1) {
842
1014
  state.currentStepIndex++;
@@ -1273,6 +1445,17 @@
1273
1445
  * @param {THREE.Scene} scene
1274
1446
  * @param {Object} renderer
1275
1447
  */
1448
+ /**
1449
+ * Initialize TextToCAD module with Three.js scene and renderer
1450
+ *
1451
+ * Sets up event listeners, UI panel, material definitions, and camera controls.
1452
+ * Must be called once before any execute() calls. Safe to call multiple times.
1453
+ *
1454
+ * @param {THREE.Scene} scene - The Three.js scene object
1455
+ * @param {THREE.WebGLRenderer} renderer - The Three.js renderer for viewport updates
1456
+ * @returns {void}
1457
+ * @throws {Error} If scene is null or not a THREE.Scene instance
1458
+ */
1276
1459
  function init(scene, renderer) {
1277
1460
  state.scene = scene;
1278
1461
  state.renderer = renderer;
@@ -1367,6 +1550,32 @@
1367
1550
  * @param {Object} params
1368
1551
  * @returns {any}
1369
1552
  */
1553
+ /**
1554
+ * Execute command in TextToCAD module (public API)
1555
+ *
1556
+ * Main entry point for all text-to-CAD operations. Commands include:
1557
+ * - 'parse': Parse natural language and return structured spec
1558
+ * - 'generate': Generate and display geometry
1559
+ * - 'commit': Add to history
1560
+ * - 'undo'/'redo': Navigate history
1561
+ * - 'clear': Reset everything
1562
+ * - 'setVariant': Select one of 3 generated alternatives
1563
+ *
1564
+ * @param {string} command - Command name: 'parse'|'generate'|'commit'|'undo'|'redo'|'clear'|'setVariant'
1565
+ * @param {Object} [params={}] - Command parameters (varies by command)
1566
+ * @param {string} params.input - For 'parse' and 'generate': natural language text
1567
+ * @param {number} params.variantIndex - For 'setVariant': index 0-2
1568
+ * @returns {Object} Command result (structure varies by command)
1569
+ * @example
1570
+ * // Parse natural language
1571
+ * const spec = window.CycleCAD.TextToCAD.execute('parse', {input: 'cylinder 50mm dia 80mm tall'});
1572
+ *
1573
+ * // Generate geometry with preview
1574
+ * window.CycleCAD.TextToCAD.execute('generate', {input: 'cylinder 50mm dia 80mm tall'});
1575
+ *
1576
+ * // Commit to history
1577
+ * window.CycleCAD.TextToCAD.execute('commit');
1578
+ */
1370
1579
  function execute(command, params) {
1371
1580
  switch (command) {
1372
1581
  case 'parse':