cyclecad 0.2.2 → 0.2.3

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 (69) hide show
  1. package/API-BUILD-MANIFEST.txt +339 -0
  2. package/API-SERVER.md +535 -0
  3. package/Architecture-Deck.pptx +0 -0
  4. package/CLAUDE.md +172 -11
  5. package/CLI-BUILD-SUMMARY.md +504 -0
  6. package/CLI-INDEX.md +356 -0
  7. package/CLI-README.md +466 -0
  8. package/COLLABORATION-INTEGRATION-GUIDE.md +325 -0
  9. package/CONNECTED_FABS_GUIDE.md +612 -0
  10. package/CONNECTED_FABS_README.md +310 -0
  11. package/DELIVERABLES.md +343 -0
  12. package/DFM-ANALYZER-INTEGRATION.md +368 -0
  13. package/DFM-QUICK-START.js +253 -0
  14. package/Dockerfile +69 -0
  15. package/IMPLEMENTATION.md +327 -0
  16. package/LICENSE +31 -0
  17. package/MARKETPLACE_QUICK_REFERENCE.txt +294 -0
  18. package/MCP-INDEX.md +264 -0
  19. package/QUICKSTART-API.md +388 -0
  20. package/QUICKSTART-CLI.md +211 -0
  21. package/QUICKSTART-MCP.md +196 -0
  22. package/README-MCP.md +208 -0
  23. package/TEST-TOKEN-ENGINE.md +319 -0
  24. package/TOKEN-ENGINE-SUMMARY.md +266 -0
  25. package/TOKENS-README.md +263 -0
  26. package/TOOLS-REFERENCE.md +254 -0
  27. package/app/index.html +168 -3
  28. package/app/js/TOKEN-INTEGRATION.md +391 -0
  29. package/app/js/agent-api.js +3 -3
  30. package/app/js/ai-copilot.js +1435 -0
  31. package/app/js/cam-pipeline.js +840 -0
  32. package/app/js/collaboration-ui.js +995 -0
  33. package/app/js/collaboration.js +1116 -0
  34. package/app/js/connected-fabs-example.js +404 -0
  35. package/app/js/connected-fabs.js +1449 -0
  36. package/app/js/dfm-analyzer.js +1760 -0
  37. package/app/js/marketplace.js +1994 -0
  38. package/app/js/material-library.js +2115 -0
  39. package/app/js/token-dashboard.js +563 -0
  40. package/app/js/token-engine.js +743 -0
  41. package/app/test-agent.html +1801 -0
  42. package/bin/cyclecad-cli.js +662 -0
  43. package/bin/cyclecad-mcp +2 -0
  44. package/bin/server.js +242 -0
  45. package/cycleCAD-Architecture.pptx +0 -0
  46. package/cycleCAD-Investor-Deck.pptx +0 -0
  47. package/demo-mcp.sh +60 -0
  48. package/docs/API-SERVER-SUMMARY.md +375 -0
  49. package/docs/API-SERVER.md +667 -0
  50. package/docs/CAM-EXAMPLES.md +344 -0
  51. package/docs/CAM-INTEGRATION.md +612 -0
  52. package/docs/CAM-QUICK-REFERENCE.md +199 -0
  53. package/docs/CLI-INTEGRATION.md +510 -0
  54. package/docs/CLI.md +872 -0
  55. package/docs/MARKETPLACE-API-SCHEMA.json +564 -0
  56. package/docs/MARKETPLACE-INTEGRATION.md +467 -0
  57. package/docs/MARKETPLACE-SETUP.html +439 -0
  58. package/docs/MCP-SERVER.md +403 -0
  59. package/examples/api-client-example.js +488 -0
  60. package/examples/api-client-example.py +359 -0
  61. package/examples/batch-manufacturing.txt +28 -0
  62. package/examples/batch-simple.txt +26 -0
  63. package/model-marketplace.html +1273 -0
  64. package/package.json +14 -3
  65. package/server/api-server.js +1120 -0
  66. package/server/mcp-server.js +1161 -0
  67. package/test-api-server.js +432 -0
  68. package/test-mcp.js +198 -0
  69. package/~$cycleCAD-Investor-Deck.pptx +0 -0
@@ -0,0 +1,2115 @@
1
+ /**
2
+ * material-library.js - PBR Material System + Physical Properties Database
3
+ *
4
+ * Comprehensive material library for cycleCAD with:
5
+ * - 50+ materials organized by category
6
+ * - Full physical properties (density, strength, thermal, electrical)
7
+ * - Manufacturing properties (machinability, weldability, processes)
8
+ * - Cost estimation and lead time
9
+ * - PBR material application (Three.js MeshStandardMaterial)
10
+ * - Surface finish variants with visual properties
11
+ * - Material comparison, search, and calculators
12
+ * - Integration with DFM analyzer and cost estimator
13
+ */
14
+
15
+ import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js';
16
+
17
+ // ============================================================================
18
+ // Material Database (50+ materials)
19
+ // ============================================================================
20
+
21
+ const MATERIAL_DATABASE = {
22
+ // METALS - Steels (5)
23
+ steel_1018: {
24
+ id: 'steel_1018',
25
+ name: 'Steel 1018 (Mild)',
26
+ category: 'metal',
27
+ subcategory: 'steel_carbon',
28
+ description: 'Low carbon steel, most common for CNC machining',
29
+
30
+ // Physical properties (kg/m³, MPa, %)
31
+ density: 7850,
32
+ tensileStrength: 440,
33
+ yieldStrength: 370,
34
+ elongation: 20,
35
+ hardness: { value: 95, scale: 'HB' },
36
+ youngsModulus: 205,
37
+ poissonsRatio: 0.285,
38
+ thermalConductivity: 51.9,
39
+ thermalExpansion: 12.2,
40
+ meltingPoint: 1475,
41
+ specificHeat: 486,
42
+ electricalResistivity: 155,
43
+
44
+ // Manufacturing
45
+ machinability: 1.0, // Reference material
46
+ weldability: 'excellent',
47
+ formability: 'good',
48
+ processes: ['cnc_mill', 'cnc_lathe', 'laser_cut', 'waterjet', 'sheet_metal', 'casting'],
49
+
50
+ // Cost and availability
51
+ costPerKg: 0.95,
52
+ availability: 'high',
53
+ leadTime: 'stock',
54
+
55
+ // PBR appearance
56
+ pbr: {
57
+ color: 0x8899aa,
58
+ metalness: 0.95,
59
+ roughness: 0.4,
60
+ envMapIntensity: 0.8,
61
+ clearcoat: 0.05,
62
+ clearcoatRoughness: 0.15
63
+ },
64
+
65
+ // Finish variants
66
+ finishes: [
67
+ { name: 'As-Machined', roughness: 0.45, metalness: 0.92 },
68
+ { name: 'Ground', roughness: 0.25, metalness: 0.95 },
69
+ { name: 'Polished', roughness: 0.1, metalness: 0.98 },
70
+ { name: 'Blackened', color: 0x1a1a1a, roughness: 0.6, metalness: 0.85 },
71
+ { name: 'Galvanized', color: 0xc8ccd0, roughness: 0.35, metalness: 0.93 }
72
+ ]
73
+ },
74
+
75
+ steel_4140: {
76
+ id: 'steel_4140',
77
+ name: 'Steel 4140 (Alloy)',
78
+ category: 'metal',
79
+ subcategory: 'steel_alloy',
80
+ description: 'Medium carbon alloy steel, high strength after heat treat',
81
+
82
+ density: 7850,
83
+ tensileStrength: 1000, // After heat treat
84
+ yieldStrength: 830,
85
+ elongation: 12,
86
+ hardness: { value: 32, scale: 'HRC' },
87
+ youngsModulus: 210,
88
+ poissonsRatio: 0.29,
89
+ thermalConductivity: 42.6,
90
+ thermalExpansion: 12.3,
91
+ meltingPoint: 1450,
92
+ specificHeat: 475,
93
+ electricalResistivity: 420,
94
+
95
+ machinability: 0.65,
96
+ weldability: 'good',
97
+ formability: 'moderate',
98
+ processes: ['cnc_mill', 'cnc_lathe', 'heat_treat', 'casting'],
99
+
100
+ costPerKg: 3.50,
101
+ availability: 'high',
102
+ leadTime: 'stock',
103
+
104
+ pbr: {
105
+ color: 0x7a8a9a,
106
+ metalness: 0.96,
107
+ roughness: 0.35,
108
+ envMapIntensity: 0.85,
109
+ clearcoat: 0.08,
110
+ clearcoatRoughness: 0.12
111
+ },
112
+
113
+ finishes: [
114
+ { name: 'As-Machined', roughness: 0.4, metalness: 0.94 },
115
+ { name: 'Ground', roughness: 0.2, metalness: 0.97 },
116
+ { name: 'Polished', roughness: 0.08, metalness: 0.99 },
117
+ { name: 'Oil-Hardened', color: 0x6a7a8a, roughness: 0.5, metalness: 0.90 }
118
+ ]
119
+ },
120
+
121
+ steel_316ss: {
122
+ id: 'steel_316ss',
123
+ name: 'Stainless Steel 316',
124
+ category: 'metal',
125
+ subcategory: 'steel_stainless',
126
+ description: 'Superior corrosion resistance, medical/marine grade',
127
+
128
+ density: 8000,
129
+ tensileStrength: 515,
130
+ yieldStrength: 205,
131
+ elongation: 40,
132
+ hardness: { value: 79, scale: 'HRB' },
133
+ youngsModulus: 193,
134
+ poissonsRatio: 0.27,
135
+ thermalConductivity: 16.3,
136
+ thermalExpansion: 16.0,
137
+ meltingPoint: 1400,
138
+ specificHeat: 500,
139
+ electricalResistivity: 740,
140
+
141
+ machinability: 0.36,
142
+ weldability: 'good',
143
+ formability: 'good',
144
+ processes: ['cnc_mill', 'cnc_lathe', 'laser_cut', 'waterjet', 'welding'],
145
+
146
+ costPerKg: 4.50,
147
+ availability: 'high',
148
+ leadTime: 'stock',
149
+
150
+ pbr: {
151
+ color: 0xc0c0c0,
152
+ metalness: 0.98,
153
+ roughness: 0.25,
154
+ envMapIntensity: 0.9,
155
+ clearcoat: 0.12,
156
+ clearcoatRoughness: 0.08
157
+ },
158
+
159
+ finishes: [
160
+ { name: 'Brushed', roughness: 0.3, metalness: 0.97 },
161
+ { name: 'Polished', roughness: 0.08, metalness: 0.99 },
162
+ { name: 'Passivated', roughness: 0.2, metalness: 0.98 }
163
+ ]
164
+ },
165
+
166
+ steel_a36: {
167
+ id: 'steel_a36',
168
+ name: 'Steel A36 (Structural)',
169
+ category: 'metal',
170
+ subcategory: 'steel_structural',
171
+ description: 'Structural steel, beam and plate stock',
172
+
173
+ density: 7750,
174
+ tensileStrength: 400,
175
+ yieldStrength: 250,
176
+ elongation: 23,
177
+ hardness: { value: 119, scale: 'HB' },
178
+ youngsModulus: 200,
179
+ poissonsRatio: 0.26,
180
+ thermalConductivity: 50,
181
+ thermalExpansion: 12,
182
+ meltingPoint: 1480,
183
+ specificHeat: 490,
184
+ electricalResistivity: 160,
185
+
186
+ machinability: 0.92,
187
+ weldability: 'excellent',
188
+ formability: 'good',
189
+ processes: ['cnc_mill', 'cnc_lathe', 'welding', 'rolling', 'casting'],
190
+
191
+ costPerKg: 0.85,
192
+ availability: 'high',
193
+ leadTime: 'stock',
194
+
195
+ pbr: {
196
+ color: 0x8a8a8a,
197
+ metalness: 0.92,
198
+ roughness: 0.5,
199
+ envMapIntensity: 0.7,
200
+ clearcoat: 0.0,
201
+ clearcoatRoughness: 0.2
202
+ },
203
+
204
+ finishes: [
205
+ { name: 'Hot-Rolled', roughness: 0.55, metalness: 0.90 },
206
+ { name: 'Painted', color: 0x4a4a4a, roughness: 0.7, metalness: 0.1 }
207
+ ]
208
+ },
209
+
210
+ steel_ductile_iron: {
211
+ id: 'steel_ductile_iron',
212
+ name: 'Ductile Iron (Gray Cast)',
213
+ category: 'metal',
214
+ subcategory: 'cast_iron',
215
+ description: 'Cast iron with spheroidized graphite, strong and damping',
216
+
217
+ density: 7100,
218
+ tensileStrength: 450,
219
+ yieldStrength: 310,
220
+ elongation: 6,
221
+ hardness: { value: 150, scale: 'HB' },
222
+ youngsModulus: 170,
223
+ poissonsRatio: 0.27,
224
+ thermalConductivity: 38,
225
+ thermalExpansion: 11,
226
+ meltingPoint: 1150,
227
+ specificHeat: 460,
228
+ electricalResistivity: 1000,
229
+
230
+ machinability: 0.75,
231
+ weldability: 'difficult',
232
+ formability: 'none',
233
+ processes: ['casting', 'cnc_mill', 'cnc_lathe'],
234
+
235
+ costPerKg: 1.20,
236
+ availability: 'high',
237
+ leadTime: '4-6 weeks',
238
+
239
+ pbr: {
240
+ color: 0x5a5a5a,
241
+ metalness: 0.88,
242
+ roughness: 0.55,
243
+ envMapIntensity: 0.6,
244
+ clearcoat: 0.0,
245
+ clearcoatRoughness: 0.2
246
+ },
247
+
248
+ finishes: [
249
+ { name: 'As-Cast', roughness: 0.65, metalness: 0.85 },
250
+ { name: 'Machined', roughness: 0.45, metalness: 0.90 }
251
+ ]
252
+ },
253
+
254
+ // METALS - Aluminum (3)
255
+ aluminum_6061: {
256
+ id: 'aluminum_6061',
257
+ name: 'Aluminum 6061-T6',
258
+ category: 'metal',
259
+ subcategory: 'aluminum',
260
+ description: 'Most versatile aluminum alloy, good corrosion resistance',
261
+
262
+ density: 2700,
263
+ tensileStrength: 310,
264
+ yieldStrength: 275,
265
+ elongation: 12,
266
+ hardness: { value: 95, scale: 'HB' },
267
+ youngsModulus: 69,
268
+ poissonsRatio: 0.33,
269
+ thermalConductivity: 167,
270
+ thermalExpansion: 23.6,
271
+ meltingPoint: 650,
272
+ specificHeat: 897,
273
+ electricalResistivity: 42,
274
+
275
+ machinability: 0.90,
276
+ weldability: 'good',
277
+ formability: 'excellent',
278
+ processes: ['cnc_mill', 'cnc_lathe', 'extrusion', 'anodizing', 'sheet_metal'],
279
+
280
+ costPerKg: 2.50,
281
+ availability: 'high',
282
+ leadTime: 'stock',
283
+
284
+ pbr: {
285
+ color: 0xb0b8c4,
286
+ metalness: 0.92,
287
+ roughness: 0.35,
288
+ envMapIntensity: 0.85,
289
+ clearcoat: 0.06,
290
+ clearcoatRoughness: 0.12
291
+ },
292
+
293
+ finishes: [
294
+ { name: 'As-Machined', roughness: 0.40, metalness: 0.90 },
295
+ { name: 'Anodized Clear', roughness: 0.3, metalness: 0.91 },
296
+ { name: 'Anodized Black', color: 0x2a2a2a, roughness: 0.5, metalness: 0.85 },
297
+ { name: 'Anodized Red', color: 0xaa3333, roughness: 0.35, metalness: 0.88 },
298
+ { name: 'Polished', roughness: 0.1, metalness: 0.95 }
299
+ ]
300
+ },
301
+
302
+ aluminum_7075: {
303
+ id: 'aluminum_7075',
304
+ name: 'Aluminum 7075-T6',
305
+ category: 'metal',
306
+ subcategory: 'aluminum',
307
+ description: 'High-strength aluminum, aerospace grade',
308
+
309
+ density: 2810,
310
+ tensileStrength: 570,
311
+ yieldStrength: 505,
312
+ elongation: 11,
313
+ hardness: { value: 150, scale: 'HB' },
314
+ youngsModulus: 72,
315
+ poissonsRatio: 0.33,
316
+ thermalConductivity: 130,
317
+ thermalExpansion: 23.4,
318
+ meltingPoint: 630,
319
+ specificHeat: 960,
320
+ electricalResistivity: 52,
321
+
322
+ machinability: 0.70,
323
+ weldability: 'poor',
324
+ formability: 'moderate',
325
+ processes: ['cnc_mill', 'cnc_lathe', 'extrusion'],
326
+
327
+ costPerKg: 8.00,
328
+ availability: 'medium',
329
+ leadTime: '2-3 weeks',
330
+
331
+ pbr: {
332
+ color: 0xa8b0c0,
333
+ metalness: 0.93,
334
+ roughness: 0.32,
335
+ envMapIntensity: 0.88,
336
+ clearcoat: 0.08,
337
+ clearcoatRoughness: 0.10
338
+ },
339
+
340
+ finishes: [
341
+ { name: 'As-Machined', roughness: 0.38, metalness: 0.91 },
342
+ { name: 'Polished', roughness: 0.12, metalness: 0.96 }
343
+ ]
344
+ },
345
+
346
+ aluminum_2024: {
347
+ id: 'aluminum_2024',
348
+ name: 'Aluminum 2024-T3',
349
+ category: 'metal',
350
+ subcategory: 'aluminum',
351
+ description: 'High-strength, fatigue-resistant, aerospace/aircraft',
352
+
353
+ density: 2780,
354
+ tensileStrength: 470,
355
+ yieldStrength: 325,
356
+ elongation: 19,
357
+ hardness: { value: 120, scale: 'HB' },
358
+ youngsModulus: 73,
359
+ poissonsRatio: 0.33,
360
+ thermalConductivity: 121,
361
+ thermalExpansion: 23.6,
362
+ meltingPoint: 640,
363
+ specificHeat: 875,
364
+ electricalResistivity: 64,
365
+
366
+ machinability: 0.75,
367
+ weldability: 'poor',
368
+ formability: 'good',
369
+ processes: ['cnc_mill', 'cnc_lathe', 'extrusion', 'sheet_metal'],
370
+
371
+ costPerKg: 6.50,
372
+ availability: 'medium',
373
+ leadTime: '2-3 weeks',
374
+
375
+ pbr: {
376
+ color: 0xacb4c0,
377
+ metalness: 0.92,
378
+ roughness: 0.34,
379
+ envMapIntensity: 0.86,
380
+ clearcoat: 0.07,
381
+ clearcoatRoughness: 0.11
382
+ },
383
+
384
+ finishes: [
385
+ { name: 'As-Machined', roughness: 0.39, metalness: 0.90 },
386
+ { name: 'Clad (alclad)', roughness: 0.36, metalness: 0.91 }
387
+ ]
388
+ },
389
+
390
+ // METALS - Copper/Brass (2)
391
+ brass_c360: {
392
+ id: 'brass_c360',
393
+ name: 'Brass C360 (Free-Cutting)',
394
+ category: 'metal',
395
+ subcategory: 'brass_copper',
396
+ description: 'Excellent machinability, decorative and electrical',
397
+
398
+ density: 8500,
399
+ tensileStrength: 310,
400
+ yieldStrength: 115,
401
+ elongation: 57,
402
+ hardness: { value: 65, scale: 'HRB' },
403
+ youngsModulus: 97,
404
+ poissonsRatio: 0.34,
405
+ thermalConductivity: 121,
406
+ thermalExpansion: 20.3,
407
+ meltingPoint: 930,
408
+ specificHeat: 380,
409
+ electricalResistivity: 61,
410
+
411
+ machinability: 1.60, // Excellent
412
+ weldability: 'difficult',
413
+ formability: 'excellent',
414
+ processes: ['cnc_mill', 'cnc_lathe', 'stamping', 'drawing'],
415
+
416
+ costPerKg: 5.50,
417
+ availability: 'high',
418
+ leadTime: 'stock',
419
+
420
+ pbr: {
421
+ color: 0xc4a54a,
422
+ metalness: 0.96,
423
+ roughness: 0.28,
424
+ envMapIntensity: 0.90,
425
+ clearcoat: 0.10,
426
+ clearcoatRoughness: 0.10
427
+ },
428
+
429
+ finishes: [
430
+ { name: 'As-Machined', roughness: 0.32, metalness: 0.95 },
431
+ { name: 'Polished', roughness: 0.08, metalness: 0.98 },
432
+ { name: 'Lacquered', roughness: 0.25, metalness: 0.92 }
433
+ ]
434
+ },
435
+
436
+ copper_c110: {
437
+ id: 'copper_c110',
438
+ name: 'Copper C110 (Annealed)',
439
+ category: 'metal',
440
+ subcategory: 'brass_copper',
441
+ description: 'Pure copper, highest electrical/thermal conductivity',
442
+
443
+ density: 8960,
444
+ tensileStrength: 220,
445
+ yieldStrength: 70,
446
+ elongation: 45,
447
+ hardness: { value: 40, scale: 'HRB' },
448
+ youngsModulus: 110,
449
+ poissonsRatio: 0.34,
450
+ thermalConductivity: 401,
451
+ thermalExpansion: 16.5,
452
+ meltingPoint: 1083,
453
+ specificHeat: 385,
454
+ electricalResistivity: 16,
455
+
456
+ machinability: 1.40,
457
+ weldability: 'difficult',
458
+ formability: 'excellent',
459
+ processes: ['cnc_mill', 'cnc_lathe', 'deep_drawing', 'stamping'],
460
+
461
+ costPerKg: 7.50,
462
+ availability: 'high',
463
+ leadTime: 'stock',
464
+
465
+ pbr: {
466
+ color: 0xb87333,
467
+ metalness: 0.98,
468
+ roughness: 0.22,
469
+ envMapIntensity: 0.92,
470
+ clearcoat: 0.12,
471
+ clearcoatRoughness: 0.08
472
+ },
473
+
474
+ finishes: [
475
+ { name: 'As-Machined', roughness: 0.26, metalness: 0.97 },
476
+ { name: 'Polished', roughness: 0.06, metalness: 0.99 },
477
+ { name: 'Oxidized', color: 0x6b4423, roughness: 0.6, metalness: 0.85 }
478
+ ]
479
+ },
480
+
481
+ // METALS - Special (3)
482
+ titanium_ti6al4v: {
483
+ id: 'titanium_ti6al4v',
484
+ name: 'Titanium Ti-6Al-4V',
485
+ category: 'metal',
486
+ subcategory: 'titanium',
487
+ description: 'Aerospace/medical, high strength-to-weight, corrosion resistant',
488
+
489
+ density: 4430,
490
+ tensileStrength: 1160,
491
+ yieldStrength: 1100,
492
+ elongation: 10,
493
+ hardness: { value: 34, scale: 'HRC' },
494
+ youngsModulus: 103,
495
+ poissonsRatio: 0.342,
496
+ thermalConductivity: 7.4,
497
+ thermalExpansion: 8.6,
498
+ meltingPoint: 1655,
499
+ specificHeat: 560,
500
+ electricalResistivity: 1540,
501
+
502
+ machinability: 0.16,
503
+ weldability: 'good',
504
+ formability: 'moderate',
505
+ processes: ['cnc_mill', 'cnc_lathe', 'welding', 'casting'],
506
+
507
+ costPerKg: 18.00,
508
+ availability: 'low',
509
+ leadTime: '4-8 weeks',
510
+
511
+ pbr: {
512
+ color: 0x8a8a90,
513
+ metalness: 0.94,
514
+ roughness: 0.38,
515
+ envMapIntensity: 0.82,
516
+ clearcoat: 0.05,
517
+ clearcoatRoughness: 0.14
518
+ },
519
+
520
+ finishes: [
521
+ { name: 'As-Machined', roughness: 0.42, metalness: 0.92 },
522
+ { name: 'Polished', roughness: 0.15, metalness: 0.96 },
523
+ { name: 'Anodized Blue', color: 0x1565c0, roughness: 0.35, metalness: 0.88 }
524
+ ]
525
+ },
526
+
527
+ inconel_718: {
528
+ id: 'inconel_718',
529
+ name: 'Inconel 718 (Superalloy)',
530
+ category: 'metal',
531
+ subcategory: 'superalloy',
532
+ description: 'High-temperature, turbine blades, extreme environments',
533
+
534
+ density: 8190,
535
+ tensileStrength: 1275,
536
+ yieldStrength: 1050,
537
+ elongation: 12,
538
+ hardness: { value: 39, scale: 'HRC' },
539
+ youngsModulus: 200,
540
+ poissonsRatio: 0.31,
541
+ thermalConductivity: 11.4,
542
+ thermalExpansion: 13.3,
543
+ meltingPoint: 1260,
544
+ specificHeat: 435,
545
+ electricalResistivity: 1300,
546
+
547
+ machinability: 0.06,
548
+ weldability: 'difficult',
549
+ formability: 'poor',
550
+ processes: ['cnc_mill', 'cnc_lathe', 'casting', 'welding'],
551
+
552
+ costPerKg: 35.00,
553
+ availability: 'low',
554
+ leadTime: '8-12 weeks',
555
+
556
+ pbr: {
557
+ color: 0x7a7a7a,
558
+ metalness: 0.93,
559
+ roughness: 0.42,
560
+ envMapIntensity: 0.80,
561
+ clearcoat: 0.03,
562
+ clearcoatRoughness: 0.16
563
+ },
564
+
565
+ finishes: [
566
+ { name: 'As-Machined', roughness: 0.48, metalness: 0.90 },
567
+ { name: 'Polish', roughness: 0.18, metalness: 0.95 }
568
+ ]
569
+ },
570
+
571
+ magnesium_az31: {
572
+ id: 'magnesium_az31',
573
+ name: 'Magnesium AZ31B',
574
+ category: 'metal',
575
+ subcategory: 'magnesium',
576
+ description: 'Lightest structural metal, good strength-to-weight ratio',
577
+
578
+ density: 1810,
579
+ tensileStrength: 230,
580
+ yieldStrength: 160,
581
+ elongation: 15,
582
+ hardness: { value: 75, scale: 'HB' },
583
+ youngsModulus: 45,
584
+ poissonsRatio: 0.35,
585
+ thermalConductivity: 156,
586
+ thermalExpansion: 26.0,
587
+ meltingPoint: 600,
588
+ specificHeat: 1025,
589
+ electricalResistivity: 45,
590
+
591
+ machinability: 0.80,
592
+ weldability: 'good',
593
+ formability: 'excellent',
594
+ processes: ['cnc_mill', 'cnc_lathe', 'casting', 'extrusion'],
595
+
596
+ costPerKg: 4.50,
597
+ availability: 'medium',
598
+ leadTime: '2-4 weeks',
599
+
600
+ pbr: {
601
+ color: 0xc0c0c0,
602
+ metalness: 0.90,
603
+ roughness: 0.40,
604
+ envMapIntensity: 0.82,
605
+ clearcoat: 0.04,
606
+ clearcoatRoughness: 0.15
607
+ },
608
+
609
+ finishes: [
610
+ { name: 'As-Machined', roughness: 0.44, metalness: 0.88 },
611
+ { name: 'Anodized', roughness: 0.35, metalness: 0.87 }
612
+ ]
613
+ },
614
+
615
+ // PLASTICS - Engineering (8)
616
+ abs: {
617
+ id: 'abs',
618
+ name: 'ABS (Acrylonitrile Butadiene Styrene)',
619
+ category: 'plastic',
620
+ subcategory: 'engineering_plastic',
621
+ description: 'Impact resistant, good machinability, common 3D print filament',
622
+
623
+ density: 1050,
624
+ tensileStrength: 40,
625
+ yieldStrength: 35,
626
+ elongation: 20,
627
+ hardness: { value: 75, scale: 'Shore-D' },
628
+ youngsModulus: 2.3,
629
+ poissonsRatio: 0.35,
630
+ thermalConductivity: 0.20,
631
+ thermalExpansion: 80,
632
+ meltingPoint: 220,
633
+ specificHeat: 1400,
634
+ electricalResistivity: 1e16,
635
+
636
+ machinability: 0.85,
637
+ weldability: 'none',
638
+ formability: 'excellent',
639
+ processes: ['injection_mold', '3d_print_fdm', 'cnc_mill', 'laser_cut'],
640
+
641
+ costPerKg: 2.20,
642
+ availability: 'high',
643
+ leadTime: 'stock',
644
+
645
+ pbr: {
646
+ color: 0x2a2a2e,
647
+ metalness: 0.0,
648
+ roughness: 0.7,
649
+ envMapIntensity: 0.3,
650
+ clearcoat: 0.0,
651
+ clearcoatRoughness: 0.2
652
+ },
653
+
654
+ finishes: [
655
+ { name: 'Natural', roughness: 0.7, metalness: 0.0 },
656
+ { name: 'Polished', roughness: 0.3, metalness: 0.05 },
657
+ { name: 'Glossy Black', color: 0x1a1a1a, roughness: 0.5, metalness: 0.0 },
658
+ { name: 'Matte', roughness: 0.85, metalness: 0.0 }
659
+ ]
660
+ },
661
+
662
+ pla: {
663
+ id: 'pla',
664
+ name: 'PLA (Polylactic Acid)',
665
+ category: 'plastic',
666
+ subcategory: 'engineering_plastic',
667
+ description: 'Biodegradable, easy to print, lower strength than ABS',
668
+
669
+ density: 1250,
670
+ tensileStrength: 50,
671
+ yieldStrength: 45,
672
+ elongation: 3,
673
+ hardness: { value: 70, scale: 'Shore-D' },
674
+ youngsModulus: 2.7,
675
+ poissonsRatio: 0.36,
676
+ thermalConductivity: 0.13,
677
+ thermalExpansion: 70,
678
+ meltingPoint: 160,
679
+ specificHeat: 1800,
680
+ electricalResistivity: 1e17,
681
+
682
+ machinability: 0.75,
683
+ weldability: 'none',
684
+ formability: 'good',
685
+ processes: ['injection_mold', '3d_print_fdm', 'cnc_mill'],
686
+
687
+ costPerKg: 1.80,
688
+ availability: 'high',
689
+ leadTime: 'stock',
690
+
691
+ pbr: {
692
+ color: 0xf0f0f0,
693
+ metalness: 0.0,
694
+ roughness: 0.6,
695
+ envMapIntensity: 0.4,
696
+ clearcoat: 0.0,
697
+ clearcoatRoughness: 0.2
698
+ },
699
+
700
+ finishes: [
701
+ { name: 'Natural White', roughness: 0.65, metalness: 0.0 },
702
+ { name: 'Glossy', roughness: 0.25, metalness: 0.02 },
703
+ { name: 'Matte', roughness: 0.80, metalness: 0.0 }
704
+ ]
705
+ },
706
+
707
+ nylon_66: {
708
+ id: 'nylon_66',
709
+ name: 'Nylon 6/6 (Polyamide)',
710
+ category: 'plastic',
711
+ subcategory: 'engineering_plastic',
712
+ description: 'High strength, low friction, bearing material',
713
+
714
+ density: 1140,
715
+ tensileStrength: 80,
716
+ yieldStrength: 70,
717
+ elongation: 30,
718
+ hardness: { value: 80, scale: 'Shore-D' },
719
+ youngsModulus: 3.5,
720
+ poissonsRatio: 0.37,
721
+ thermalConductivity: 0.24,
722
+ thermalExpansion: 80,
723
+ meltingPoint: 265,
724
+ specificHeat: 1700,
725
+ electricalResistivity: 1e16,
726
+
727
+ machinability: 0.70,
728
+ weldability: 'none',
729
+ formability: 'excellent',
730
+ processes: ['injection_mold', '3d_print_fdm', 'cnc_mill', 'machining'],
731
+
732
+ costPerKg: 2.80,
733
+ availability: 'high',
734
+ leadTime: 'stock',
735
+
736
+ pbr: {
737
+ color: 0xe8e0d0,
738
+ metalness: 0.0,
739
+ roughness: 0.55,
740
+ envMapIntensity: 0.35,
741
+ clearcoat: 0.0,
742
+ clearcoatRoughness: 0.2
743
+ },
744
+
745
+ finishes: [
746
+ { name: 'Natural', roughness: 0.60, metalness: 0.0 },
747
+ { name: 'Black', color: 0x1a1a1a, roughness: 0.65, metalness: 0.0 },
748
+ { name: 'Polished', roughness: 0.25, metalness: 0.03 }
749
+ ]
750
+ },
751
+
752
+ petg: {
753
+ id: 'petg',
754
+ name: 'PETG (Polyethylene Terephthalate Glycol)',
755
+ category: 'plastic',
756
+ subcategory: 'engineering_plastic',
757
+ description: 'Better strength than PLA, easier to print than ABS',
758
+
759
+ density: 1270,
760
+ tensileStrength: 55,
761
+ yieldStrength: 50,
762
+ elongation: 50,
763
+ hardness: { value: 76, scale: 'Shore-D' },
764
+ youngsModulus: 2.8,
765
+ poissonsRatio: 0.36,
766
+ thermalConductivity: 0.19,
767
+ thermalExpansion: 75,
768
+ meltingPoint: 245,
769
+ specificHeat: 1400,
770
+ electricalResistivity: 1e14,
771
+
772
+ machinability: 0.80,
773
+ weldability: 'none',
774
+ formability: 'excellent',
775
+ processes: ['injection_mold', '3d_print_fdm', 'cnc_mill'],
776
+
777
+ costPerKg: 2.50,
778
+ availability: 'high',
779
+ leadTime: 'stock',
780
+
781
+ pbr: {
782
+ color: 0xd0d0d0,
783
+ metalness: 0.0,
784
+ roughness: 0.58,
785
+ envMapIntensity: 0.38,
786
+ clearcoat: 0.0,
787
+ clearcoatRoughness: 0.2
788
+ },
789
+
790
+ finishes: [
791
+ { name: 'Natural', roughness: 0.60, metalness: 0.0 },
792
+ { name: 'Transparent', color: 0xffffff, roughness: 0.3, metalness: 0.0 }
793
+ ]
794
+ },
795
+
796
+ delrin_acetal: {
797
+ id: 'delrin_acetal',
798
+ name: 'Acetal (Delrin) - Copolymer',
799
+ category: 'plastic',
800
+ subcategory: 'engineering_plastic',
801
+ description: 'Excellent machinability, dimensional stability, bearing material',
802
+
803
+ density: 1410,
804
+ tensileStrength: 70,
805
+ yieldStrength: 65,
806
+ elongation: 25,
807
+ hardness: { value: 86, scale: 'Shore-D' },
808
+ youngsModulus: 3.1,
809
+ poissonsRatio: 0.35,
810
+ thermalConductivity: 0.25,
811
+ thermalExpansion: 100,
812
+ meltingPoint: 180,
813
+ specificHeat: 1500,
814
+ electricalResistivity: 1e17,
815
+
816
+ machinability: 0.95,
817
+ weldability: 'none',
818
+ formability: 'moderate',
819
+ processes: ['cnc_mill', 'cnc_lathe', 'injection_mold', 'machining'],
820
+
821
+ costPerKg: 3.50,
822
+ availability: 'high',
823
+ leadTime: 'stock',
824
+
825
+ pbr: {
826
+ color: 0xf5f5f5,
827
+ metalness: 0.0,
828
+ roughness: 0.50,
829
+ envMapIntensity: 0.40,
830
+ clearcoat: 0.0,
831
+ clearcoatRoughness: 0.2
832
+ },
833
+
834
+ finishes: [
835
+ { name: 'Natural White', roughness: 0.52, metalness: 0.0 },
836
+ { name: 'Black', color: 0x1a1a1a, roughness: 0.55, metalness: 0.0 },
837
+ { name: 'Polished', roughness: 0.20, metalness: 0.05 }
838
+ ]
839
+ },
840
+
841
+ polycarbonate: {
842
+ id: 'polycarbonate',
843
+ name: 'Polycarbonate (PC)',
844
+ category: 'plastic',
845
+ subcategory: 'engineering_plastic',
846
+ description: 'Transparent, impact-resistant, optical applications',
847
+
848
+ density: 1200,
849
+ tensileStrength: 65,
850
+ yieldStrength: 62,
851
+ elongation: 110,
852
+ hardness: { value: 80, scale: 'Shore-D' },
853
+ youngsModulus: 2.3,
854
+ poissonsRatio: 0.37,
855
+ thermalConductivity: 0.20,
856
+ thermalExpansion: 65,
857
+ meltingPoint: 225,
858
+ specificHeat: 1200,
859
+ electricalResistivity: 1e16,
860
+
861
+ machinability: 0.60,
862
+ weldability: 'none',
863
+ formability: 'good',
864
+ processes: ['injection_mold', 'cnc_mill', 'thermoform'],
865
+
866
+ costPerKg: 4.00,
867
+ availability: 'high',
868
+ leadTime: 'stock',
869
+
870
+ pbr: {
871
+ color: 0xffffff,
872
+ metalness: 0.0,
873
+ roughness: 0.2,
874
+ envMapIntensity: 0.6,
875
+ clearcoat: 0.8,
876
+ clearcoatRoughness: 0.1
877
+ },
878
+
879
+ finishes: [
880
+ { name: 'Clear', roughness: 0.15, metalness: 0.0 },
881
+ { name: 'Frosted', roughness: 0.65, metalness: 0.0 }
882
+ ]
883
+ },
884
+
885
+ peek: {
886
+ id: 'peek',
887
+ name: 'PEEK (Polyetheretherketone)',
888
+ category: 'plastic',
889
+ subcategory: 'engineering_plastic',
890
+ description: 'High-temperature, chemical resistant, aerospace/medical',
891
+
892
+ density: 1320,
893
+ tensileStrength: 100,
894
+ yieldStrength: 90,
895
+ elongation: 50,
896
+ hardness: { value: 85, scale: 'Shore-D' },
897
+ youngsModulus: 3.6,
898
+ poissonsRatio: 0.36,
899
+ thermalConductivity: 0.25,
900
+ thermalExpansion: 47,
901
+ meltingPoint: 330,
902
+ specificHeat: 1600,
903
+ electricalResistivity: 1e18,
904
+
905
+ machinability: 0.75,
906
+ weldability: 'none',
907
+ formability: 'moderate',
908
+ processes: ['cnc_mill', 'cnc_lathe', 'injection_mold'],
909
+
910
+ costPerKg: 15.00,
911
+ availability: 'medium',
912
+ leadTime: '2-3 weeks',
913
+
914
+ pbr: {
915
+ color: 0xf0f0f0,
916
+ metalness: 0.0,
917
+ roughness: 0.48,
918
+ envMapIntensity: 0.42,
919
+ clearcoat: 0.0,
920
+ clearcoatRoughness: 0.2
921
+ },
922
+
923
+ finishes: [
924
+ { name: 'Natural', roughness: 0.50, metalness: 0.0 },
925
+ { name: 'Polished', roughness: 0.18, metalness: 0.04 }
926
+ ]
927
+ },
928
+
929
+ tpu_flexible: {
930
+ id: 'tpu_flexible',
931
+ name: 'TPU (Thermoplastic Polyurethane)',
932
+ category: 'plastic',
933
+ subcategory: 'engineering_plastic',
934
+ description: 'Flexible, high elongation, impact resistant',
935
+
936
+ density: 1200,
937
+ tensileStrength: 25,
938
+ yieldStrength: 20,
939
+ elongation: 500,
940
+ hardness: { value: 80, scale: 'Shore-A' },
941
+ youngsModulus: 0.015,
942
+ poissonsRatio: 0.49,
943
+ thermalConductivity: 0.18,
944
+ thermalExpansion: 150,
945
+ meltingPoint: 190,
946
+ specificHeat: 1800,
947
+ electricalResistivity: 1e14,
948
+
949
+ machinability: 0.50,
950
+ weldability: 'none',
951
+ formability: 'excellent',
952
+ processes: ['injection_mold', '3d_print_fdm', 'casting'],
953
+
954
+ costPerKg: 3.50,
955
+ availability: 'high',
956
+ leadTime: 'stock',
957
+
958
+ pbr: {
959
+ color: 0x333333,
960
+ metalness: 0.0,
961
+ roughness: 0.75,
962
+ envMapIntensity: 0.25,
963
+ clearcoat: 0.0,
964
+ clearcoatRoughness: 0.2
965
+ },
966
+
967
+ finishes: [
968
+ { name: 'Natural', roughness: 0.75, metalness: 0.0 },
969
+ { name: 'Glossy', roughness: 0.4, metalness: 0.0 }
970
+ ]
971
+ },
972
+
973
+ // COMPOSITES (3)
974
+ carbon_fiber_cfrp: {
975
+ id: 'carbon_fiber_cfrp',
976
+ name: 'Carbon Fiber (CFRP) - Epoxy',
977
+ category: 'composite',
978
+ subcategory: 'fiber_reinforced',
979
+ description: 'High strength-to-weight, aerospace, stiff',
980
+
981
+ density: 1600,
982
+ tensileStrength: 750,
983
+ yieldStrength: 750,
984
+ elongation: 1.5,
985
+ hardness: { value: 130, scale: 'HB' },
986
+ youngsModulus: 130,
987
+ poissonsRatio: 0.3,
988
+ thermalConductivity: 7,
989
+ thermalExpansion: -1.0, // Negative thermal expansion
990
+ meltingPoint: 200,
991
+ specificHeat: 1500,
992
+ electricalResistivity: 1e4,
993
+
994
+ machinability: 0.40,
995
+ weldability: 'none',
996
+ formability: 'moderate',
997
+ processes: ['layup', 'vacuum_bag', 'cnc_mill'],
998
+
999
+ costPerKg: 12.00,
1000
+ availability: 'medium',
1001
+ leadTime: '3-4 weeks',
1002
+
1003
+ pbr: {
1004
+ color: 0x1a1a1a,
1005
+ metalness: 0.15,
1006
+ roughness: 0.45,
1007
+ envMapIntensity: 0.35,
1008
+ clearcoat: 0.0,
1009
+ clearcoatRoughness: 0.2
1010
+ },
1011
+
1012
+ finishes: [
1013
+ { name: 'Weave', roughness: 0.50, metalness: 0.10 },
1014
+ { name: 'Glossy', roughness: 0.25, metalness: 0.20 },
1015
+ { name: 'Matte', roughness: 0.70, metalness: 0.05 }
1016
+ ]
1017
+ },
1018
+
1019
+ fiberglass_gfrp: {
1020
+ id: 'fiberglass_gfrp',
1021
+ name: 'Fiberglass (GFRP) - Polyester',
1022
+ category: 'composite',
1023
+ subcategory: 'fiber_reinforced',
1024
+ description: 'Cost-effective, good corrosion resistance, marine',
1025
+
1026
+ density: 1850,
1027
+ tensileStrength: 250,
1028
+ yieldStrength: 250,
1029
+ elongation: 2,
1030
+ hardness: { value: 90, scale: 'HB' },
1031
+ youngsModulus: 45,
1032
+ poissonsRatio: 0.25,
1033
+ thermalConductivity: 0.25,
1034
+ thermalExpansion: 25,
1035
+ meltingPoint: 200,
1036
+ specificHeat: 1200,
1037
+ electricalResistivity: 1e11,
1038
+
1039
+ machinability: 0.30,
1040
+ weldability: 'none',
1041
+ formability: 'good',
1042
+ processes: ['hand_layup', 'vacuum_bag', 'injection_mold'],
1043
+
1044
+ costPerKg: 2.50,
1045
+ availability: 'high',
1046
+ leadTime: 'stock',
1047
+
1048
+ pbr: {
1049
+ color: 0x999999,
1050
+ metalness: 0.0,
1051
+ roughness: 0.60,
1052
+ envMapIntensity: 0.35,
1053
+ clearcoat: 0.0,
1054
+ clearcoatRoughness: 0.2
1055
+ },
1056
+
1057
+ finishes: [
1058
+ { name: 'As-laid', roughness: 0.65, metalness: 0.0 },
1059
+ { name: 'Gelcoat White', color: 0xf5f5f5, roughness: 0.4, metalness: 0.0 },
1060
+ { name: 'Gelcoat Colors', roughness: 0.45, metalness: 0.0 }
1061
+ ]
1062
+ },
1063
+
1064
+ kevlar_fiber: {
1065
+ id: 'kevlar_fiber',
1066
+ name: 'Kevlar (Aramid Fiber)',
1067
+ category: 'composite',
1068
+ subcategory: 'fiber_reinforced',
1069
+ description: 'Impact resistant, ballistic applications, lightweight',
1070
+
1071
+ density: 1450,
1072
+ tensileStrength: 620,
1073
+ yieldStrength: 620,
1074
+ elongation: 3.3,
1075
+ hardness: { value: 110, scale: 'HB' },
1076
+ youngsModulus: 112,
1077
+ poissonsRatio: 0.29,
1078
+ thermalConductivity: 5.5,
1079
+ thermalExpansion: -2.0,
1080
+ meltingPoint: 350,
1081
+ specificHeat: 1200,
1082
+ electricalResistivity: 1e17,
1083
+
1084
+ machinability: 0.25,
1085
+ weldability: 'none',
1086
+ formability: 'moderate',
1087
+ processes: ['layup', 'vacuum_bag'],
1088
+
1089
+ costPerKg: 18.00,
1090
+ availability: 'low',
1091
+ leadTime: '4-8 weeks',
1092
+
1093
+ pbr: {
1094
+ color: 0xffff00,
1095
+ metalness: 0.0,
1096
+ roughness: 0.55,
1097
+ envMapIntensity: 0.40,
1098
+ clearcoat: 0.0,
1099
+ clearcoatRoughness: 0.2
1100
+ },
1101
+
1102
+ finishes: [
1103
+ { name: 'Woven', roughness: 0.60, metalness: 0.0 },
1104
+ { name: 'Glossy', roughness: 0.3, metalness: 0.05 }
1105
+ ]
1106
+ },
1107
+
1108
+ // OTHER (3)
1109
+ wood_oak: {
1110
+ id: 'wood_oak',
1111
+ name: 'Wood (Oak)',
1112
+ category: 'other',
1113
+ subcategory: 'natural',
1114
+ description: 'Hardwood, strong, good machinability',
1115
+
1116
+ density: 750,
1117
+ tensileStrength: 60,
1118
+ yieldStrength: 45,
1119
+ elongation: 0,
1120
+ hardness: { value: 4.6, scale: 'Janka' },
1121
+ youngsModulus: 11,
1122
+ poissonsRatio: 0.4,
1123
+ thermalConductivity: 0.16,
1124
+ thermalExpansion: 4.5,
1125
+ meltingPoint: 450, // Charring temp
1126
+ specificHeat: 1700,
1127
+ electricalResistivity: 1e15,
1128
+
1129
+ machinability: 0.88,
1130
+ weldability: 'none',
1131
+ formability: 'moderate',
1132
+ processes: ['cnc_mill', 'cnc_lathe', 'routing', 'hand_tools'],
1133
+
1134
+ costPerKg: 1.50,
1135
+ availability: 'high',
1136
+ leadTime: 'stock',
1137
+
1138
+ pbr: {
1139
+ color: 0x8b6914,
1140
+ metalness: 0.0,
1141
+ roughness: 0.75,
1142
+ envMapIntensity: 0.30,
1143
+ clearcoat: 0.0,
1144
+ clearcoatRoughness: 0.2
1145
+ },
1146
+
1147
+ finishes: [
1148
+ { name: 'Natural', roughness: 0.80, metalness: 0.0 },
1149
+ { name: 'Sanded', roughness: 0.55, metalness: 0.0 },
1150
+ { name: 'Stained Dark', color: 0x3d2817, roughness: 0.65, metalness: 0.0 },
1151
+ { name: 'Varnished', roughness: 0.25, metalness: 0.10 }
1152
+ ]
1153
+ },
1154
+
1155
+ rubber_neoprene: {
1156
+ id: 'rubber_neoprene',
1157
+ name: 'Rubber (Neoprene)',
1158
+ category: 'other',
1159
+ subcategory: 'elastomer',
1160
+ description: 'Oil resistant, flexible, sealing applications',
1161
+
1162
+ density: 1250,
1163
+ tensileStrength: 25,
1164
+ yieldStrength: 20,
1165
+ elongation: 800,
1166
+ hardness: { value: 70, scale: 'Shore-A' },
1167
+ youngsModulus: 0.003,
1168
+ poissonsRatio: 0.49,
1169
+ thermalConductivity: 0.20,
1170
+ thermalExpansion: 200,
1171
+ meltingPoint: 120,
1172
+ specificHeat: 2000,
1173
+ electricalResistivity: 1e12,
1174
+
1175
+ machinability: 0.70,
1176
+ weldability: 'none',
1177
+ formability: 'excellent',
1178
+ processes: ['injection_mold', 'compression_mold', 'cnc_mill'],
1179
+
1180
+ costPerKg: 2.50,
1181
+ availability: 'high',
1182
+ leadTime: 'stock',
1183
+
1184
+ pbr: {
1185
+ color: 0x1a1a1a,
1186
+ metalness: 0.0,
1187
+ roughness: 0.85,
1188
+ envMapIntensity: 0.20,
1189
+ clearcoat: 0.0,
1190
+ clearcoatRoughness: 0.2
1191
+ },
1192
+
1193
+ finishes: [
1194
+ { name: 'Natural Black', roughness: 0.90, metalness: 0.0 },
1195
+ { name: 'Polished', roughness: 0.50, metalness: 0.05 }
1196
+ ]
1197
+ },
1198
+
1199
+ ceramic_alumina: {
1200
+ id: 'ceramic_alumina',
1201
+ name: 'Ceramic (Alumina - Al2O3)',
1202
+ category: 'other',
1203
+ subcategory: 'ceramic',
1204
+ description: 'High hardness, wear resistant, insulating',
1205
+
1206
+ density: 3900,
1207
+ tensileStrength: 300,
1208
+ yieldStrength: 300,
1209
+ elongation: 0,
1210
+ hardness: { value: 1900, scale: 'HV' },
1211
+ youngsModulus: 345,
1212
+ poissonsRatio: 0.22,
1213
+ thermalConductivity: 30,
1214
+ thermalExpansion: 5.3,
1215
+ meltingPoint: 2072,
1216
+ specificHeat: 880,
1217
+ electricalResistivity: 1e18,
1218
+
1219
+ machinability: 0.15,
1220
+ weldability: 'none',
1221
+ formability: 'none',
1222
+ processes: ['sintering', 'grinding'],
1223
+
1224
+ costPerKg: 8.00,
1225
+ availability: 'medium',
1226
+ leadTime: '3-4 weeks',
1227
+
1228
+ pbr: {
1229
+ color: 0xf5f5f5,
1230
+ metalness: 0.0,
1231
+ roughness: 0.65,
1232
+ envMapIntensity: 0.40,
1233
+ clearcoat: 0.0,
1234
+ clearcoatRoughness: 0.2
1235
+ },
1236
+
1237
+ finishes: [
1238
+ { name: 'Sintered', roughness: 0.70, metalness: 0.0 },
1239
+ { name: 'Glazed', roughness: 0.3, metalness: 0.0 }
1240
+ ]
1241
+ },
1242
+
1243
+ glass_borosilicate: {
1244
+ id: 'glass_borosilicate',
1245
+ name: 'Glass (Borosilicate)',
1246
+ category: 'other',
1247
+ subcategory: 'ceramic',
1248
+ description: 'Thermal resistant, transparent, laboratory grade',
1249
+
1250
+ density: 2230,
1251
+ tensileStrength: 70,
1252
+ yieldStrength: 70,
1253
+ elongation: 0,
1254
+ hardness: { value: 6, scale: 'Mohs' },
1255
+ youngsModulus: 64,
1256
+ poissonsRatio: 0.20,
1257
+ thermalConductivity: 1.4,
1258
+ thermalExpansion: 3.25,
1259
+ meltingPoint: 1650,
1260
+ specificHeat: 840,
1261
+ electricalResistivity: 1e17,
1262
+
1263
+ machinability: 0.20,
1264
+ weldability: 'none',
1265
+ formability: 'none',
1266
+ processes: ['grinding', 'laser_cut'],
1267
+
1268
+ costPerKg: 5.00,
1269
+ availability: 'medium',
1270
+ leadTime: 'stock',
1271
+
1272
+ pbr: {
1273
+ color: 0xffffff,
1274
+ metalness: 0.0,
1275
+ roughness: 0.1,
1276
+ envMapIntensity: 0.8,
1277
+ clearcoat: 0.95,
1278
+ clearcoatRoughness: 0.05
1279
+ },
1280
+
1281
+ finishes: [
1282
+ { name: 'Clear', roughness: 0.08, metalness: 0.0 },
1283
+ { name: 'Frosted', roughness: 0.70, metalness: 0.0 }
1284
+ ]
1285
+ }
1286
+ };
1287
+
1288
+ // ============================================================================
1289
+ // Surface Finish Database (30 finishes)
1290
+ // ============================================================================
1291
+
1292
+ const SURFACE_FINISH_DATABASE = {
1293
+ machined_063: {
1294
+ id: 'machined_063',
1295
+ name: 'As-Machined (6.3 µm Ra)',
1296
+ category: 'machined',
1297
+ roughness: 0.50,
1298
+ metalness: 0.92,
1299
+ cost_multiplier: 1.0,
1300
+ lead_days: 0
1301
+ },
1302
+ machined_32: {
1303
+ id: 'machined_32',
1304
+ name: 'As-Machined (3.2 µm Ra)',
1305
+ category: 'machined',
1306
+ roughness: 0.35,
1307
+ metalness: 0.94,
1308
+ cost_multiplier: 1.0,
1309
+ lead_days: 0
1310
+ },
1311
+ machined_16: {
1312
+ id: 'machined_16',
1313
+ name: 'As-Machined (1.6 µm Ra)',
1314
+ category: 'machined',
1315
+ roughness: 0.20,
1316
+ metalness: 0.96,
1317
+ cost_multiplier: 1.1,
1318
+ lead_days: 1
1319
+ },
1320
+ ground: {
1321
+ id: 'ground',
1322
+ name: 'Ground (0.4 µm Ra)',
1323
+ category: 'ground',
1324
+ roughness: 0.15,
1325
+ metalness: 0.97,
1326
+ cost_multiplier: 1.3,
1327
+ lead_days: 2
1328
+ },
1329
+ polished: {
1330
+ id: 'polished',
1331
+ name: 'Polished (0.05 µm Ra)',
1332
+ category: 'polished',
1333
+ roughness: 0.08,
1334
+ metalness: 0.99,
1335
+ cost_multiplier: 1.8,
1336
+ lead_days: 3
1337
+ },
1338
+ mirror: {
1339
+ id: 'mirror',
1340
+ name: 'Mirror Polished (0.01 µm Ra)',
1341
+ category: 'polished',
1342
+ roughness: 0.03,
1343
+ metalness: 1.0,
1344
+ cost_multiplier: 2.5,
1345
+ lead_days: 5
1346
+ },
1347
+ anodize_ii: {
1348
+ id: 'anodize_ii',
1349
+ name: 'Anodize Type II (Clear)',
1350
+ category: 'coated',
1351
+ roughness: 0.30,
1352
+ metalness: 0.92,
1353
+ cost_multiplier: 1.4,
1354
+ lead_days: 3
1355
+ },
1356
+ anodize_iii: {
1357
+ id: 'anodize_iii',
1358
+ name: 'Anodize Type III (Hard)',
1359
+ category: 'coated',
1360
+ roughness: 0.25,
1361
+ metalness: 0.88,
1362
+ cost_multiplier: 1.6,
1363
+ lead_days: 4
1364
+ },
1365
+ electroplate_chrome: {
1366
+ id: 'electroplate_chrome',
1367
+ name: 'Electroplated Chrome',
1368
+ category: 'plated',
1369
+ roughness: 0.10,
1370
+ metalness: 0.99,
1371
+ cost_multiplier: 2.0,
1372
+ lead_days: 4
1373
+ },
1374
+ electroplate_nickel: {
1375
+ id: 'electroplate_nickel',
1376
+ name: 'Electroplated Nickel',
1377
+ category: 'plated',
1378
+ roughness: 0.12,
1379
+ metalness: 0.97,
1380
+ cost_multiplier: 1.8,
1381
+ lead_days: 3
1382
+ },
1383
+ powder_coat_matte: {
1384
+ id: 'powder_coat_matte',
1385
+ name: 'Powder Coat (Matte)',
1386
+ category: 'coated',
1387
+ roughness: 0.75,
1388
+ metalness: 0.05,
1389
+ cost_multiplier: 1.5,
1390
+ lead_days: 5
1391
+ },
1392
+ powder_coat_gloss: {
1393
+ id: 'powder_coat_gloss',
1394
+ name: 'Powder Coat (Gloss)',
1395
+ category: 'coated',
1396
+ roughness: 0.35,
1397
+ metalness: 0.15,
1398
+ cost_multiplier: 1.5,
1399
+ lead_days: 5
1400
+ },
1401
+ paint_matte: {
1402
+ id: 'paint_matte',
1403
+ name: 'Paint (Matte)',
1404
+ category: 'painted',
1405
+ roughness: 0.80,
1406
+ metalness: 0.0,
1407
+ cost_multiplier: 1.2,
1408
+ lead_days: 2
1409
+ },
1410
+ paint_satin: {
1411
+ id: 'paint_satin',
1412
+ name: 'Paint (Satin)',
1413
+ category: 'painted',
1414
+ roughness: 0.50,
1415
+ metalness: 0.02,
1416
+ cost_multiplier: 1.3,
1417
+ lead_days: 2
1418
+ },
1419
+ paint_gloss: {
1420
+ id: 'paint_gloss',
1421
+ name: 'Paint (Gloss)',
1422
+ category: 'painted',
1423
+ roughness: 0.25,
1424
+ metalness: 0.05,
1425
+ cost_multiplier: 1.3,
1426
+ lead_days: 2
1427
+ },
1428
+ fdm_layer: {
1429
+ id: 'fdm_layer',
1430
+ name: '3D Print FDM (Layer Lines)',
1431
+ category: 'additive',
1432
+ roughness: 0.85,
1433
+ metalness: 0.0,
1434
+ cost_multiplier: 0.8,
1435
+ lead_days: 1
1436
+ },
1437
+ sla_smooth: {
1438
+ id: 'sla_smooth',
1439
+ name: '3D Print SLA (Smooth)',
1440
+ category: 'additive',
1441
+ roughness: 0.35,
1442
+ metalness: 0.0,
1443
+ cost_multiplier: 1.2,
1444
+ lead_days: 1
1445
+ },
1446
+ sls_powdery: {
1447
+ id: 'sls_powdery',
1448
+ name: '3D Print SLS (Powdery)',
1449
+ category: 'additive',
1450
+ roughness: 0.65,
1451
+ metalness: 0.0,
1452
+ cost_multiplier: 1.5,
1453
+ lead_days: 2
1454
+ },
1455
+ brushed: {
1456
+ id: 'brushed',
1457
+ name: 'Brushed Finish',
1458
+ category: 'brushed',
1459
+ roughness: 0.32,
1460
+ metalness: 0.93,
1461
+ cost_multiplier: 1.2,
1462
+ lead_days: 1
1463
+ },
1464
+ satin: {
1465
+ id: 'satin',
1466
+ name: 'Satin Finish',
1467
+ category: 'brushed',
1468
+ roughness: 0.28,
1469
+ metalness: 0.94,
1470
+ cost_multiplier: 1.25,
1471
+ lead_days: 1
1472
+ }
1473
+ };
1474
+
1475
+ // ============================================================================
1476
+ // Material Library State
1477
+ // ============================================================================
1478
+
1479
+ let materialState = {
1480
+ scene: null,
1481
+ camera: null,
1482
+ renderer: null,
1483
+ appliedMaterials: {}, // Map of mesh uuid → { materialId, finishId, material }
1484
+ previewMesh: null,
1485
+ previewScene: null,
1486
+ previewRenderer: null
1487
+ };
1488
+
1489
+ // ============================================================================
1490
+ // Core API - Material Application
1491
+ // ============================================================================
1492
+
1493
+ /**
1494
+ * Initialize material library with Three.js context
1495
+ * @param {THREE.Scene} scene - Three.js scene
1496
+ * @param {THREE.Camera} camera - Three.js camera
1497
+ * @param {THREE.WebGLRenderer} renderer - Three.js renderer
1498
+ */
1499
+ export function initMaterialLibrary(scene, camera, renderer) {
1500
+ materialState.scene = scene;
1501
+ materialState.camera = camera;
1502
+ materialState.renderer = renderer;
1503
+
1504
+ window.cycleCAD = window.cycleCAD || {};
1505
+ window.cycleCAD.materials = {
1506
+ database: MATERIAL_DATABASE,
1507
+ finishes: SURFACE_FINISH_DATABASE,
1508
+ apply: applyMaterial,
1509
+ remove: removeMaterial,
1510
+ applyToAll: applyToAll,
1511
+ preview: previewMaterial,
1512
+ compare: compareMaterials,
1513
+ findAlternatives: findAlternatives,
1514
+ findByRequirements: findByRequirements,
1515
+ calculateWeight: calculateWeight,
1516
+ calculateCost: calculateCost,
1517
+ calculateDeflection: calculateDeflection,
1518
+ calculateThermalExpansion: calculateThermalExpansion,
1519
+ calculateStress: calculateStress,
1520
+ getProperties: getMaterialProperties,
1521
+ getAllMaterials: () => ({ ...MATERIAL_DATABASE }),
1522
+ getFinishes: () => ({ ...SURFACE_FINISH_DATABASE })
1523
+ };
1524
+
1525
+ dispatchEvent(new CustomEvent('materials-initialized', { detail: { count: Object.keys(MATERIAL_DATABASE).length } }));
1526
+ }
1527
+
1528
+ /**
1529
+ * Apply a material and optional finish to a mesh or array of meshes
1530
+ * @param {THREE.Mesh|THREE.Mesh[]|number} target - Mesh(es) or part index
1531
+ * @param {string} materialId - Material ID from database
1532
+ * @param {string} finishId - Optional finish ID
1533
+ * @returns {THREE.MeshStandardMaterial} Applied material
1534
+ */
1535
+ export function applyMaterial(target, materialId, finishId) {
1536
+ const matDef = MATERIAL_DATABASE[materialId];
1537
+ if (!matDef) {
1538
+ console.warn(`Material "${materialId}" not found in database`);
1539
+ return null;
1540
+ }
1541
+
1542
+ const meshes = normalizeMeshArray(target);
1543
+ if (meshes.length === 0) return null;
1544
+
1545
+ const pbrProps = { ...matDef.pbr };
1546
+
1547
+ // Apply finish if specified
1548
+ if (finishId) {
1549
+ const finishDef = SURFACE_FINISH_DATABASE[finishId];
1550
+ if (finishDef) {
1551
+ pbrProps.roughness = finishDef.roughness;
1552
+ pbrProps.metalness = finishDef.metalness;
1553
+ } else {
1554
+ // Check if finish is in material's finishes array
1555
+ const materialFinish = matDef.finishes?.find(f => f.name.toLowerCase().replace(/\s+/g, '_') === finishId);
1556
+ if (materialFinish) {
1557
+ pbrProps.roughness = materialFinish.roughness;
1558
+ pbrProps.metalness = materialFinish.metalness;
1559
+ if (materialFinish.color) pbrProps.color = materialFinish.color;
1560
+ }
1561
+ }
1562
+ }
1563
+
1564
+ // Create MeshStandardMaterial
1565
+ const threeMaterial = new THREE.MeshStandardMaterial({
1566
+ color: new THREE.Color(pbrProps.color),
1567
+ metalness: pbrProps.metalness,
1568
+ roughness: pbrProps.roughness,
1569
+ envMapIntensity: pbrProps.envMapIntensity || 1.0,
1570
+ clearcoat: pbrProps.clearcoat || 0.0,
1571
+ clearcoatRoughness: pbrProps.clearcoatRoughness || 0.0,
1572
+ side: THREE.FrontSide
1573
+ });
1574
+
1575
+ // Apply to all meshes
1576
+ meshes.forEach(mesh => {
1577
+ mesh.material = threeMaterial;
1578
+ mesh.userData.material = {
1579
+ id: materialId,
1580
+ name: matDef.name,
1581
+ finish: finishId || 'default',
1582
+ appliedAt: Date.now()
1583
+ };
1584
+ materialState.appliedMaterials[mesh.uuid] = {
1585
+ materialId,
1586
+ finishId: finishId || 'default',
1587
+ material: threeMaterial,
1588
+ meshUuid: mesh.uuid
1589
+ };
1590
+ });
1591
+
1592
+ dispatchEvent(new CustomEvent('material-applied', {
1593
+ detail: { materialId, finishId, meshCount: meshes.length }
1594
+ }));
1595
+
1596
+ return threeMaterial;
1597
+ }
1598
+
1599
+ /**
1600
+ * Remove material from mesh(es), reset to default gray
1601
+ * @param {THREE.Mesh|THREE.Mesh[]|number} target - Mesh(es) or part index
1602
+ */
1603
+ export function removeMaterial(target) {
1604
+ const meshes = normalizeMeshArray(target);
1605
+
1606
+ const defaultMaterial = new THREE.MeshStandardMaterial({
1607
+ color: 0x888888,
1608
+ metalness: 0.6,
1609
+ roughness: 0.5
1610
+ });
1611
+
1612
+ meshes.forEach(mesh => {
1613
+ mesh.material = defaultMaterial;
1614
+ delete mesh.userData.material;
1615
+ delete materialState.appliedMaterials[mesh.uuid];
1616
+ });
1617
+
1618
+ dispatchEvent(new CustomEvent('material-removed', { detail: { meshCount: meshes.length } }));
1619
+ }
1620
+
1621
+ /**
1622
+ * Apply material to all objects in scene
1623
+ * @param {string} materialId - Material ID
1624
+ * @param {string} finishId - Optional finish ID
1625
+ */
1626
+ export function applyToAll(materialId, finishId) {
1627
+ const allMeshes = [];
1628
+ materialState.scene.traverse(obj => {
1629
+ if (obj.isMesh) allMeshes.push(obj);
1630
+ });
1631
+
1632
+ applyMaterial(allMeshes, materialId, finishId);
1633
+ }
1634
+
1635
+ /**
1636
+ * Preview material on a small sphere
1637
+ * @param {string} materialId - Material ID
1638
+ * @param {string} finishId - Optional finish ID
1639
+ * @returns {THREE.Mesh} Preview mesh
1640
+ */
1641
+ export function previewMaterial(materialId, finishId) {
1642
+ // Create small preview geometry
1643
+ const geometry = new THREE.SphereGeometry(1, 32, 32);
1644
+ const meshes = [{ isMesh: true }]; // Mock mesh for applyMaterial
1645
+
1646
+ const matDef = MATERIAL_DATABASE[materialId];
1647
+ if (!matDef) return null;
1648
+
1649
+ const pbrProps = { ...matDef.pbr };
1650
+ if (finishId) {
1651
+ const finishDef = SURFACE_FINISH_DATABASE[finishId];
1652
+ if (finishDef) {
1653
+ pbrProps.roughness = finishDef.roughness;
1654
+ pbrProps.metalness = finishDef.metalness;
1655
+ }
1656
+ }
1657
+
1658
+ const material = new THREE.MeshStandardMaterial({
1659
+ color: new THREE.Color(pbrProps.color),
1660
+ metalness: pbrProps.metalness,
1661
+ roughness: pbrProps.roughness,
1662
+ envMapIntensity: pbrProps.envMapIntensity || 1.0
1663
+ });
1664
+
1665
+ const mesh = new THREE.Mesh(geometry, material);
1666
+ return mesh;
1667
+ }
1668
+
1669
+ // ============================================================================
1670
+ // Comparison & Search
1671
+ // ============================================================================
1672
+
1673
+ /**
1674
+ * Compare materials side-by-side
1675
+ * @param {string[]} materialIds - Array of material IDs to compare (max 4)
1676
+ * @returns {Object} Comparison table data
1677
+ */
1678
+ export function compareMaterials(materialIds) {
1679
+ const materials = materialIds
1680
+ .slice(0, 4)
1681
+ .map(id => MATERIAL_DATABASE[id])
1682
+ .filter(m => m);
1683
+
1684
+ if (materials.length === 0) return null;
1685
+
1686
+ const properties = [
1687
+ 'density',
1688
+ 'tensileStrength',
1689
+ 'yieldStrength',
1690
+ 'youngsModulus',
1691
+ 'hardness',
1692
+ 'thermalConductivity',
1693
+ 'machinability',
1694
+ 'costPerKg',
1695
+ 'weldability',
1696
+ 'processes'
1697
+ ];
1698
+
1699
+ const comparison = {
1700
+ materials: materials.map(m => ({ id: m.id, name: m.name, category: m.category })),
1701
+ properties: {}
1702
+ };
1703
+
1704
+ properties.forEach(prop => {
1705
+ comparison.properties[prop] = materials.map(m => {
1706
+ const value = m[prop];
1707
+ if (typeof value === 'object') {
1708
+ return JSON.stringify(value);
1709
+ }
1710
+ return value;
1711
+ });
1712
+ });
1713
+
1714
+ return comparison;
1715
+ }
1716
+
1717
+ /**
1718
+ * Find alternative materials based on priorities
1719
+ * @param {string} materialId - Reference material ID
1720
+ * @param {string[]} priorities - ['cost', 'weight', 'strength', 'temperature', 'corrosion']
1721
+ * @returns {Array} Sorted alternatives with trade-off explanations
1722
+ */
1723
+ export function findAlternatives(materialId, priorities = ['cost']) {
1724
+ const ref = MATERIAL_DATABASE[materialId];
1725
+ if (!ref) return [];
1726
+
1727
+ const candidates = Object.values(MATERIAL_DATABASE).filter(m => m.id !== materialId && m.category === ref.category);
1728
+
1729
+ const scored = candidates.map(mat => {
1730
+ let score = 0;
1731
+ let explanation = [];
1732
+
1733
+ if (priorities.includes('cost')) {
1734
+ const costDiff = ref.costPerKg - mat.costPerKg;
1735
+ score += (costDiff / ref.costPerKg) * 100; // % cheaper
1736
+ if (costDiff > 0) explanation.push(`${((costDiff / ref.costPerKg) * 100).toFixed(0)}% cheaper`);
1737
+ }
1738
+
1739
+ if (priorities.includes('weight')) {
1740
+ const weightDiff = (ref.density - mat.density) / ref.density;
1741
+ score += weightDiff * 100; // % lighter
1742
+ if (weightDiff > 0) explanation.push(`${(weightDiff * 100).toFixed(0)}% lighter`);
1743
+ }
1744
+
1745
+ if (priorities.includes('strength')) {
1746
+ const strengthDiff = (mat.tensileStrength - ref.tensileStrength) / ref.tensileStrength;
1747
+ score += strengthDiff * 50;
1748
+ if (strengthDiff > 0) explanation.push(`${(strengthDiff * 100).toFixed(0)}% stronger`);
1749
+ }
1750
+
1751
+ if (priorities.includes('temperature')) {
1752
+ const tempDiff = mat.meltingPoint - ref.meltingPoint;
1753
+ if (tempDiff > 100) explanation.push(`+${tempDiff}°C melting point`);
1754
+ }
1755
+
1756
+ return {
1757
+ id: mat.id,
1758
+ name: mat.name,
1759
+ score,
1760
+ explanation: explanation.join(', '),
1761
+ costPerKg: mat.costPerKg,
1762
+ density: mat.density,
1763
+ tensileStrength: mat.tensileStrength
1764
+ };
1765
+ });
1766
+
1767
+ return scored.sort((a, b) => b.score - a.score).slice(0, 5);
1768
+ }
1769
+
1770
+ /**
1771
+ * Find materials matching requirements
1772
+ * @param {Object} requirements - { minTensileStrength, maxDensity, maxCostPerKg, etc. }
1773
+ * @returns {Array} Matching materials
1774
+ */
1775
+ export function findByRequirements(requirements) {
1776
+ return Object.values(MATERIAL_DATABASE).filter(mat => {
1777
+ if (requirements.minTensileStrength && mat.tensileStrength < requirements.minTensileStrength) return false;
1778
+ if (requirements.maxDensity && mat.density > requirements.maxDensity) return false;
1779
+ if (requirements.maxCostPerKg && mat.costPerKg > requirements.maxCostPerKg) return false;
1780
+ if (requirements.minMachinability && mat.machinability < requirements.minMachinability) return false;
1781
+ if (requirements.category && mat.category !== requirements.category) return false;
1782
+ if (requirements.processes) {
1783
+ const hasProcess = requirements.processes.some(p => mat.processes.includes(p));
1784
+ if (!hasProcess) return false;
1785
+ }
1786
+ return true;
1787
+ });
1788
+ }
1789
+
1790
+ // ============================================================================
1791
+ // Calculators
1792
+ // ============================================================================
1793
+
1794
+ /**
1795
+ * Calculate part weight from volume
1796
+ * @param {number} volume_mm3 - Volume in cubic millimeters
1797
+ * @param {string} materialId - Material ID
1798
+ * @returns {number} Weight in grams
1799
+ */
1800
+ export function calculateWeight(volume_mm3, materialId) {
1801
+ const mat = MATERIAL_DATABASE[materialId];
1802
+ if (!mat) return 0;
1803
+
1804
+ const volume_cm3 = volume_mm3 / 1000;
1805
+ return (mat.density * volume_cm3) / 1000; // to grams
1806
+ }
1807
+
1808
+ /**
1809
+ * Calculate raw material cost
1810
+ * @param {number} volume_mm3 - Volume in cubic millimeters
1811
+ * @param {string} materialId - Material ID
1812
+ * @param {number} finishCostMult - Finish cost multiplier (default 1.0)
1813
+ * @returns {number} Cost in EUR
1814
+ */
1815
+ export function calculateCost(volume_mm3, materialId, finishCostMult = 1.0) {
1816
+ const weight_kg = calculateWeight(volume_mm3, materialId) / 1000;
1817
+ const mat = MATERIAL_DATABASE[materialId];
1818
+ if (!mat) return 0;
1819
+
1820
+ return mat.costPerKg * weight_kg * finishCostMult;
1821
+ }
1822
+
1823
+ /**
1824
+ * Calculate deflection of a cantilever beam
1825
+ * @param {number} length_mm - Beam length
1826
+ * @param {number} load_n - Force applied (Newtons)
1827
+ * @param {number} momentOfInertia_mm4 - Second moment of inertia
1828
+ * @param {string} materialId - Material ID
1829
+ * @returns {number} Deflection in mm
1830
+ */
1831
+ export function calculateDeflection(length_mm, load_n, momentOfInertia_mm4, materialId) {
1832
+ const mat = MATERIAL_DATABASE[materialId];
1833
+ if (!mat) return 0;
1834
+
1835
+ // δ = (F * L³) / (3 * E * I)
1836
+ // E in MPa, convert to correct units
1837
+ const E_mpa = mat.youngsModulus * 1000; // Convert GPa to MPa
1838
+ const length_m = length_mm / 1000;
1839
+ const I_m4 = momentOfInertia_mm4 * 1e-12;
1840
+
1841
+ const deflection_m = (load_n * Math.pow(length_m, 3)) / (3 * E_mpa * 1e6 * I_m4);
1842
+ return deflection_m * 1000; // Convert to mm
1843
+ }
1844
+
1845
+ /**
1846
+ * Calculate thermal expansion
1847
+ * @param {number} length_mm - Original length
1848
+ * @param {number} temp_change_c - Temperature change in Celsius
1849
+ * @param {string} materialId - Material ID
1850
+ * @returns {number} Length change in mm
1851
+ */
1852
+ export function calculateThermalExpansion(length_mm, temp_change_c, materialId) {
1853
+ const mat = MATERIAL_DATABASE[materialId];
1854
+ if (!mat) return 0;
1855
+
1856
+ // ΔL = L0 * α * ΔT
1857
+ // α in µm/m·°C, convert to mm/mm·°C
1858
+ const alpha = mat.thermalExpansion / 1e6;
1859
+ return length_mm * alpha * temp_change_c;
1860
+ }
1861
+
1862
+ /**
1863
+ * Calculate stress and safety factor
1864
+ * @param {number} force_n - Applied force (Newtons)
1865
+ * @param {number} area_mm2 - Cross-sectional area
1866
+ * @param {string} materialId - Material ID
1867
+ * @param {boolean} includeYieldCheck - Check against yield strength
1868
+ * @returns {Object} { stress_mpa, yield_stress_mpa, tensile_stress_mpa, safety_factor, safe: boolean }
1869
+ */
1870
+ export function calculateStress(force_n, area_mm2, materialId, includeYieldCheck = true) {
1871
+ const mat = MATERIAL_DATABASE[materialId];
1872
+ if (!mat) return null;
1873
+
1874
+ // σ = F / A (convert N to MPa)
1875
+ const stress_mpa = force_n / area_mm2;
1876
+ const yieldStress_mpa = mat.yieldStrength;
1877
+ const tensileStress_mpa = mat.tensileStrength;
1878
+
1879
+ const safetyFactorYield = yieldStress_mpa / stress_mpa;
1880
+ const safetyFactorTensile = tensileStress_mpa / stress_mpa;
1881
+ const safetyFactor = Math.min(safetyFactorYield, safetyFactorTensile);
1882
+
1883
+ return {
1884
+ stress_mpa: stress_mpa.toFixed(2),
1885
+ yield_stress_mpa: yieldStress_mpa,
1886
+ tensile_stress_mpa: tensileStress_mpa,
1887
+ safety_factor: safetyFactor.toFixed(2),
1888
+ safe: safetyFactor >= 2.0 // Typical safety factor is 2+
1889
+ };
1890
+ }
1891
+
1892
+ // ============================================================================
1893
+ // Utility Functions
1894
+ // ============================================================================
1895
+
1896
+ /**
1897
+ * Get full material properties
1898
+ * @param {string} materialId - Material ID
1899
+ * @returns {Object} Full material definition
1900
+ */
1901
+ export function getMaterialProperties(materialId) {
1902
+ return MATERIAL_DATABASE[materialId] || null;
1903
+ }
1904
+
1905
+ /**
1906
+ * Normalize mesh target to array of meshes
1907
+ * @private
1908
+ */
1909
+ function normalizeMeshArray(target) {
1910
+ if (!target) return [];
1911
+
1912
+ if (Array.isArray(target)) {
1913
+ return target.filter(t => t && t.isMesh);
1914
+ }
1915
+
1916
+ if (target.isMesh) {
1917
+ return [target];
1918
+ }
1919
+
1920
+ if (typeof target === 'number' && materialState.scene) {
1921
+ const allMeshes = [];
1922
+ materialState.scene.traverse(obj => {
1923
+ if (obj.isMesh && obj.userData.partIndex === target) {
1924
+ allMeshes.push(obj);
1925
+ }
1926
+ });
1927
+ return allMeshes;
1928
+ }
1929
+
1930
+ return [];
1931
+ }
1932
+
1933
+ /**
1934
+ * Dispatch custom events (material-applied, material-removed, etc.)
1935
+ * @private
1936
+ */
1937
+ function dispatchEvent(event) {
1938
+ window.dispatchEvent(event);
1939
+ if (typeof console !== 'undefined') {
1940
+ console.log(`[Materials] ${event.type}:`, event.detail);
1941
+ }
1942
+ }
1943
+
1944
+ // ============================================================================
1945
+ // UI Panel (Optional - if integrated into app/index.html)
1946
+ // ============================================================================
1947
+
1948
+ /**
1949
+ * Generate material library HTML panel
1950
+ * @returns {string} HTML markup
1951
+ */
1952
+ export function generateMaterialPanelHTML() {
1953
+ let html = `
1954
+ <div id="material-library-panel" class="material-panel">
1955
+ <div class="material-header">
1956
+ <h3>Material Library</h3>
1957
+ <button id="material-close-btn" class="close-btn">×</button>
1958
+ </div>
1959
+ <div class="material-tabs">
1960
+ <button class="material-tab-btn active" data-tab="browser">Browser</button>
1961
+ <button class="material-tab-btn" data-tab="compare">Compare</button>
1962
+ <button class="material-tab-btn" data-tab="calculator">Calculator</button>
1963
+ <button class="material-tab-btn" data-tab="search">Search</button>
1964
+ </div>
1965
+ <div id="material-tabs-content">
1966
+ `;
1967
+
1968
+ // Browser tab
1969
+ html += '<div id="material-browser-tab" class="material-tab-content active">';
1970
+ html += '<div class="material-category-grid">';
1971
+
1972
+ const categories = {};
1973
+ Object.values(MATERIAL_DATABASE).forEach(mat => {
1974
+ if (!categories[mat.category]) categories[mat.category] = [];
1975
+ categories[mat.category].push(mat);
1976
+ });
1977
+
1978
+ Object.entries(categories).forEach(([cat, mats]) => {
1979
+ html += `<div class="material-category"><h4>${cat.toUpperCase()}</h4>`;
1980
+ mats.forEach(mat => {
1981
+ const colorHex = mat.pbr.color.toString(16).padStart(6, '0');
1982
+ html += `
1983
+ <div class="material-card" data-material="${mat.id}">
1984
+ <div class="material-color" style="background-color: #${colorHex};"></div>
1985
+ <div class="material-info">
1986
+ <strong>${mat.name}</strong>
1987
+ <small>${mat.density} kg/m³ | €${mat.costPerKg.toFixed(2)}/kg</small>
1988
+ </div>
1989
+ </div>
1990
+ `;
1991
+ });
1992
+ html += '</div>';
1993
+ });
1994
+
1995
+ html += '</div></div>';
1996
+
1997
+ // Compare tab
1998
+ html += '<div id="material-compare-tab" class="material-tab-content">';
1999
+ html += '<p>Select up to 4 materials to compare properties</p>';
2000
+ html += '<div id="compare-table"></div>';
2001
+ html += '</div>';
2002
+
2003
+ // Calculator tab
2004
+ html += '<div id="material-calc-tab" class="material-tab-content">';
2005
+ html += `
2006
+ <div class="calc-section">
2007
+ <label>Volume (mm³)</label>
2008
+ <input type="number" id="calc-volume" placeholder="1000">
2009
+ <label>Material</label>
2010
+ <select id="calc-material">
2011
+ `;
2012
+ Object.values(MATERIAL_DATABASE).forEach(mat => {
2013
+ html += `<option value="${mat.id}">${mat.name}</option>`;
2014
+ });
2015
+ html += `
2016
+ </select>
2017
+ <button id="calc-weight-btn">Calculate Weight</button>
2018
+ <div id="calc-result"></div>
2019
+ </div>
2020
+ `;
2021
+ html += '</div>';
2022
+
2023
+ // Search tab
2024
+ html += '<div id="material-search-tab" class="material-tab-content">';
2025
+ html += '<input type="text" id="material-search" placeholder="Search materials...">';
2026
+ html += '<div id="search-results"></div>';
2027
+ html += '</div>';
2028
+
2029
+ html += '</div></div>';
2030
+
2031
+ return html;
2032
+ }
2033
+
2034
+ /**
2035
+ * Initialize material panel UI
2036
+ * @param {HTMLElement} containerEl - Container for the panel
2037
+ */
2038
+ export function initMaterialPanel(containerEl) {
2039
+ if (!containerEl) return;
2040
+
2041
+ containerEl.innerHTML = generateMaterialPanelHTML();
2042
+
2043
+ // Tab switching
2044
+ const tabBtns = containerEl.querySelectorAll('.material-tab-btn');
2045
+ tabBtns.forEach(btn => {
2046
+ btn.addEventListener('click', () => {
2047
+ const tabName = btn.dataset.tab;
2048
+ containerEl.querySelectorAll('.material-tab-btn').forEach(b => b.classList.remove('active'));
2049
+ containerEl.querySelectorAll('.material-tab-content').forEach(c => c.classList.remove('active'));
2050
+ btn.classList.add('active');
2051
+ document.getElementById(`material-${tabName}-tab`)?.classList.add('active');
2052
+ });
2053
+ });
2054
+
2055
+ // Material card clicks
2056
+ containerEl.querySelectorAll('.material-card').forEach(card => {
2057
+ card.addEventListener('click', () => {
2058
+ const materialId = card.dataset.material;
2059
+ applyMaterial(allMeshes, materialId);
2060
+ });
2061
+ });
2062
+
2063
+ // Calculator
2064
+ document.getElementById('calc-weight-btn')?.addEventListener('click', () => {
2065
+ const volume = parseFloat(document.getElementById('calc-volume').value);
2066
+ const materialId = document.getElementById('calc-material').value;
2067
+ const weight = calculateWeight(volume, materialId);
2068
+ const cost = calculateCost(volume, materialId);
2069
+ document.getElementById('calc-result').innerHTML = `
2070
+ <strong>Weight:</strong> ${weight.toFixed(2)}g<br>
2071
+ <strong>Cost:</strong> €${cost.toFixed(2)}
2072
+ `;
2073
+ });
2074
+
2075
+ // Search
2076
+ document.getElementById('material-search')?.addEventListener('input', (e) => {
2077
+ const query = e.target.value.toLowerCase();
2078
+ const results = Object.values(MATERIAL_DATABASE).filter(m =>
2079
+ m.name.toLowerCase().includes(query) ||
2080
+ m.description.toLowerCase().includes(query)
2081
+ );
2082
+ const html = results.map(m => `<div>${m.name} - ${m.category}</div>`).join('');
2083
+ document.getElementById('search-results').innerHTML = html;
2084
+ });
2085
+
2086
+ // Close button
2087
+ document.getElementById('material-close-btn')?.addEventListener('click', () => {
2088
+ containerEl.style.display = 'none';
2089
+ });
2090
+ }
2091
+
2092
+ // ============================================================================
2093
+ // Export
2094
+ // ============================================================================
2095
+
2096
+ export default {
2097
+ initMaterialLibrary,
2098
+ applyMaterial,
2099
+ removeMaterial,
2100
+ applyToAll,
2101
+ previewMaterial,
2102
+ compareMaterials,
2103
+ findAlternatives,
2104
+ findByRequirements,
2105
+ calculateWeight,
2106
+ calculateCost,
2107
+ calculateDeflection,
2108
+ calculateThermalExpansion,
2109
+ calculateStress,
2110
+ getMaterialProperties,
2111
+ generateMaterialPanelHTML,
2112
+ initMaterialPanel,
2113
+ MATERIAL_DATABASE,
2114
+ SURFACE_FINISH_DATABASE
2115
+ };