@amg555/frameforge-mcp 2.4.8 → 2.4.9
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/package.json +3 -3
- package/plugins/video-pipeline/skills/html-renderer/references/motion-craft.md +91 -0
- package/plugins/video-pipeline/skills/html-renderer/references/render-contract.md +65 -0
- package/plugins/video-pipeline/skills/video-pipeline/references/motion-craft.md +91 -0
- package/plugins/video-pipeline/skills/video-pipeline/references/render-contract.md +65 -0
- package/scripts/deep-validate.js +8 -2
- package/src/harness/bin/harness.mjs +32 -2
- package/src/harness/lib/verify-quality.mjs +251 -0
- package/src/mcp-server/server.js +222 -2
- package/src/video-renderer/scene-animator.js +873 -9
|
@@ -326,7 +326,7 @@ function detectComponentTheme({ visualDescription = '', visual_description = '',
|
|
|
326
326
|
}
|
|
327
327
|
|
|
328
328
|
// 7. Shame / Mirror / Reflection / Water / Self Awareness / Theory of Mind (Scene 3, 9)
|
|
329
|
-
if (/(?:^|[^\w])(shame|blush|embarrass|mirror|reflection|water pool|theory of mind|how others see|self-aware|identity|looking at me|psychology|mind-shift)(?:[^\w]|$)/i.test(text)
|
|
329
|
+
if (/(?:^|[^\w])(shame|blush|embarrass|mirror|reflection|water pool|theory of mind|how others see|self-aware|identity|looking at me|shame psychology|mind-shift)(?:[^\w]|$)/i.test(text)
|
|
330
330
|
|| /കണ്ണാടി|പ്രതിഫലനം|മറ്റുള്ളവർ എന്നെ|തിയറി ഓഫ് മൈൻഡ്|സ്വന്തം ശരീരം മറച്ചു|വ്യക്തിത്വം|ശ്രദ്ധിക്കുന്നുണ്ട്|യഥാർത്ഥ രഹസ്യം|നാണം മറയ്ക്കാൻ/.test(text)) {
|
|
331
331
|
return 'doodle_shame_reflection';
|
|
332
332
|
}
|
|
@@ -392,7 +392,7 @@ function detectComponentTheme({ visualDescription = '', visual_description = '',
|
|
|
392
392
|
}
|
|
393
393
|
|
|
394
394
|
// High Velocity Speed Action
|
|
395
|
-
if (/(?:^|[^\w])(sports|workout|gym|athlete|speed|streaks|race|championship|extreme|
|
|
395
|
+
if (/(?:^|[^\w])(sports|workout|gym|athlete|speed|streaks|race|championship|extreme|hype|adrenaline|velocity|sprint|fast motion)(?:[^\w]|$)/.test(text)
|
|
396
396
|
|| /കായിക|മത്സര|വേഗത|വ്യായാമം|खेल|दौड़/.test(text)) {
|
|
397
397
|
return 'speed_action';
|
|
398
398
|
}
|
|
@@ -421,9 +421,9 @@ function detectComponentTheme({ visualDescription = '', visual_description = '',
|
|
|
421
421
|
return 'chart';
|
|
422
422
|
}
|
|
423
423
|
|
|
424
|
-
// Neural Network Nodes
|
|
425
|
-
if (/(?:^|[^\w])(network|
|
|
426
|
-
||
|
|
424
|
+
// Neural Network Nodes / AI Topology
|
|
425
|
+
if (/(?:^|[^\w])(network|nodes|neural network|neural net|distributed system|server mesh|deep learning)(?:[^\w]|$)/.test(text)
|
|
426
|
+
|| /നെറ്റ്വർക്ക്|ശൃംഖല|नेटवर्क/.test(text)) {
|
|
427
427
|
return 'nodes';
|
|
428
428
|
}
|
|
429
429
|
|
|
@@ -433,16 +433,59 @@ function detectComponentTheme({ visualDescription = '', visual_description = '',
|
|
|
433
433
|
return 'isometric_geometry';
|
|
434
434
|
}
|
|
435
435
|
|
|
436
|
-
//
|
|
436
|
+
// ── Universal Semantic Visual Archetypes (General Content & Education) ──
|
|
437
|
+
// 1. Versus / Comparison / Split Screen
|
|
438
|
+
if (/(?:^|[^\w])(versus|vs|comparison|compare|pros and cons|pros vs cons|difference between|dichotomy|contrast|side-by-side|before and after|before vs after)(?:[^\w]|$)/i.test(text)
|
|
439
|
+
|| /വ്യത്യാസം|താരതമ്യം|എതിര്|പോരാട്ടം|തമ്മിൽ|തുലനം|താരതമ്യപ്പെടുത്തൽ/.test(text)) {
|
|
440
|
+
return 'versus_comparison';
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// 2. Mechanism / Physics / Machinery / Engine / Aerodynamics
|
|
444
|
+
if (/(?:^|[^\w])(engine|gear|gears|motor|physics|aerodynamics|thrust|turbine|propulsion|piston|hydraulic|kinetic|machine|machinery|circuit|mechanics|thermodynamics|levers|pulley|internal combustion|mechanical|cutaway|airflow|lift|rotor|dynamo)(?:[^\w]|$)/i.test(text)
|
|
445
|
+
|| /യന്ത്രം|എഞ്ചിൻ|ഭൗതികശാസ്ത്രം|പ്രവർത്തനം|ചലനം|മെക്കാനിസം|ഭൗതികം/.test(text)) {
|
|
446
|
+
return 'mechanism_physics';
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// 3. Process / Pipeline / Workflow / Stages / Assembly / Lifecycle
|
|
450
|
+
if (/(?:^|[^\w])(pipeline|workflow|step by step|steps|stages|phase|phases|production line|assembly|manufacturing|lifecycle|funnel|conveyor|sequence|how it works|how it is made|procedure)(?:[^\w]|$)/i.test(text)
|
|
451
|
+
|| /ഘട്ടങ്ങൾ|പ്രക്രിയ|ഘട്ടം|ഉൽപ്പാദനം|വഴികൾ|പ്രവർത്തനരീതി/.test(text)) {
|
|
452
|
+
return 'process_pipeline';
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// 4. Cognitive Insight / Psychology / Brain / Eureka / Revelation / Mind
|
|
456
|
+
if (/(?:^|[^\w])(mind|psychology|cognitive|brain|insight|revelation|eureka|intuition|subconscious|thinking|thought|contemplation|intelligence|mental|awareness|neuroscience|concept|idea|epiphany)(?:[^\w]|$)/i.test(text)
|
|
457
|
+
|| /മനസ്സ്|ബുദ്ധി|ചിന്ത|മനഃശാസ്ത്രം|ജ്ഞാനം|മസ്തിഷ്കം|ബോധം/.test(text)) {
|
|
458
|
+
return 'cognitive_insight';
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// 5. Magnification / Loupe / Macro Zoom / Scrutiny / Detailed Inspection
|
|
462
|
+
if (/(?:^|[^\w])(magnify|magnification|zoom|microscopic|macro|close up|closeup|inspection|loupe|examine|scrutiny|detail|focal point|focus in|specimen)(?:[^\w]|$)/i.test(text)
|
|
463
|
+
|| /സൂക്ഷ്മ|പരിശോധന|വലുതാക്കി കാണുക|സൂക്ഷ്മമായി|നിരീക്ഷണം/.test(text)) {
|
|
464
|
+
return 'magnification_focus';
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// 6. Explainer / Presentation / Tutorial / Masterclass Whiteboard / Breakdown
|
|
468
|
+
if (/(?:^|[^\w])(explain|explainer|presentation|lecture|teach|tutorial|whiteboard|blackboard|breakdown|guide|overview|theory|principles|fundamentals|introduction|summary|lesson|masterclass)(?:[^\w]|$)/i.test(text)
|
|
469
|
+
|| /വിവരണം|പഠിപ്പിക്കുക|ക്ലാസ്|പാഠം|തത്വം|വിവരിക്കുക/.test(text)) {
|
|
470
|
+
return 'explainer_presentation';
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// 7. Waveform / Audio / Sound / Acoustics / Equalizer
|
|
474
|
+
if (/(?:^|[^\w])(waveform|audio|sound|equalizer|frequency|acoustics|hz|decibel|music spectrum|voice waveform)(?:[^\w]|$)/i.test(text)
|
|
475
|
+
|| /ശബ്ദതരംഗം|തരംഗം|സംഗീതം|ധ്വനി/.test(text)) {
|
|
476
|
+
return 'waveform';
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Fallbacks matched to aesthetic (rich semantic defaults, never sterile)
|
|
437
480
|
if (aesthetic === 'cosmic_space') return 'celestial_orbit';
|
|
438
481
|
if (aesthetic === 'cinematic_documentary') return 'doodle_general_human';
|
|
439
482
|
if (aesthetic === 'finance_executive') return 'chart';
|
|
440
483
|
if (aesthetic === 'nature_organic') return 'dna_helix';
|
|
441
484
|
if (aesthetic === 'breaking_news') return 'radar_globe';
|
|
442
485
|
if (aesthetic === 'cyber_tech') return 'nodes';
|
|
443
|
-
if (aesthetic === 'minimal_luxury') return '
|
|
486
|
+
if (aesthetic === 'minimal_luxury') return 'explainer_presentation';
|
|
444
487
|
if (aesthetic === 'hype_sports') return 'speed_action';
|
|
445
|
-
return '
|
|
488
|
+
return 'explainer_presentation';
|
|
446
489
|
}
|
|
447
490
|
|
|
448
491
|
// ── Harness HTML Wrapper for User / Agent Code ────────────────────────────────
|
|
@@ -712,6 +755,8 @@ const IS_LIGHT = ${Boolean(pal.isLight)};
|
|
|
712
755
|
const STROKE_COLOR = ${JSON.stringify(pal.isLight ? (pal.stroke || '#1c1917') : '#f8fafc')};
|
|
713
756
|
const STROKE_ALT = ${JSON.stringify(pal.isLight ? (pal.strokeAlt || '#44403c') : 'rgba(255, 255, 255, 0.7)')};
|
|
714
757
|
const AMBIENT_LINE = ${JSON.stringify(pal.isLight ? 'rgba(28, 25, 23, 0.22)' : 'rgba(255, 255, 255, 0.25)')};
|
|
758
|
+
const TEXT_MAIN = ${JSON.stringify(pal.isLight ? '#1c1917' : '#f8fafc')};
|
|
759
|
+
const TEXT_MUTED = ${JSON.stringify(pal.isLight ? '#78716c' : '#94a3b8')};
|
|
715
760
|
|
|
716
761
|
let cachedImage = null;
|
|
717
762
|
if (IMAGE_SRC) {
|
|
@@ -3313,7 +3358,679 @@ function drawMainScene(t) {
|
|
|
3313
3358
|
ctx.drawImage(cachedImage, cx - imgW / 2, cy - imgH / 2, imgW, imgH);
|
|
3314
3359
|
ctx.restore();
|
|
3315
3360
|
|
|
3316
|
-
} else {
|
|
3361
|
+
} else if (COMP_THEME === 'versus_comparison') {
|
|
3362
|
+
// Universal Archetype: Split-Screen Versus / Comparison
|
|
3363
|
+
const splitSpring = spring(t, 0.05, 180, 14);
|
|
3364
|
+
const cardLeftSpring = spring(t, 0.15, 190, 15);
|
|
3365
|
+
const cardRightSpring = spring(t, 0.28, 190, 15);
|
|
3366
|
+
const badgeSpring = spring(t, 0.42, 220, 13);
|
|
3367
|
+
|
|
3368
|
+
// Center divider beam
|
|
3369
|
+
const divAlpha = Math.min(1, Math.max(0, splitSpring));
|
|
3370
|
+
ctx.save();
|
|
3371
|
+
ctx.globalAlpha = divAlpha;
|
|
3372
|
+
const seamGrad = ctx.createLinearGradient(0, cy - H * 0.42, 0, cy + H * 0.42);
|
|
3373
|
+
seamGrad.addColorStop(0, 'rgba(255,255,255,0)');
|
|
3374
|
+
seamGrad.addColorStop(0.5, ACCENT);
|
|
3375
|
+
seamGrad.addColorStop(1, 'rgba(255,255,255,0)');
|
|
3376
|
+
ctx.strokeStyle = seamGrad;
|
|
3377
|
+
ctx.lineWidth = 3;
|
|
3378
|
+
ctx.beginPath();
|
|
3379
|
+
ctx.moveTo(cx, cy - H * 0.42);
|
|
3380
|
+
ctx.lineTo(cx, cy + H * 0.42);
|
|
3381
|
+
ctx.stroke();
|
|
3382
|
+
|
|
3383
|
+
// Seam energy sparks
|
|
3384
|
+
for (let sp = 0; sp < 8; sp++) {
|
|
3385
|
+
const spY = cy - H * 0.35 + ((sp * 95 + t * 240) % (H * 0.7));
|
|
3386
|
+
const spR = 3 + Math.sin(t * 8 + sp) * 2;
|
|
3387
|
+
ctx.fillStyle = ACCENT;
|
|
3388
|
+
ctx.beginPath();
|
|
3389
|
+
ctx.arc(cx, spY, spR, 0, Math.PI * 2);
|
|
3390
|
+
ctx.fill();
|
|
3391
|
+
}
|
|
3392
|
+
ctx.restore();
|
|
3393
|
+
|
|
3394
|
+
// Left Card (Side A)
|
|
3395
|
+
const cardW = Math.min(W * 0.38, 520);
|
|
3396
|
+
const cardH = Math.min(H * 0.62, 540);
|
|
3397
|
+
const leftX = cx - cardW - 35;
|
|
3398
|
+
const cardY = cy - cardH / 2;
|
|
3399
|
+
|
|
3400
|
+
ctx.save();
|
|
3401
|
+
ctx.globalAlpha = Math.min(1, Math.max(0, cardLeftSpring));
|
|
3402
|
+
ctx.translate(leftX + (1 - cardLeftSpring) * -60, cardY);
|
|
3403
|
+
ctx.fillStyle = CARD_BG;
|
|
3404
|
+
ctx.strokeStyle = CARD_BORDER;
|
|
3405
|
+
ctx.lineWidth = 2;
|
|
3406
|
+
ctx.beginPath();
|
|
3407
|
+
ctx.roundRect(0, 0, cardW, cardH, 20);
|
|
3408
|
+
ctx.fill();
|
|
3409
|
+
ctx.stroke();
|
|
3410
|
+
|
|
3411
|
+
// Left Card Header & Accent Pill
|
|
3412
|
+
ctx.fillStyle = 'rgba(56, 189, 248, 0.15)';
|
|
3413
|
+
ctx.beginPath();
|
|
3414
|
+
ctx.roundRect(24, 24, 110, 32, 16);
|
|
3415
|
+
ctx.fill();
|
|
3416
|
+
ctx.fillStyle = '#38bdf8';
|
|
3417
|
+
ctx.font = 'bold 16px ' + FONT_DISPLAY;
|
|
3418
|
+
ctx.fillText('OPTION A', 42, 45);
|
|
3419
|
+
|
|
3420
|
+
// Left Metrics / Feature Bars
|
|
3421
|
+
for (let b = 0; b < 4; b++) {
|
|
3422
|
+
const barY = 90 + b * 75;
|
|
3423
|
+
const bProgress = Math.min(1, Math.max(0, spring(t, 0.3 + b * 0.1, 160, 14)));
|
|
3424
|
+
ctx.fillStyle = IS_LIGHT ? 'rgba(0,0,0,0.06)' : 'rgba(255,255,255,0.06)';
|
|
3425
|
+
ctx.beginPath();
|
|
3426
|
+
ctx.roundRect(24, barY + 22, cardW - 48, 14, 7);
|
|
3427
|
+
ctx.fill();
|
|
3428
|
+
|
|
3429
|
+
ctx.fillStyle = '#38bdf8';
|
|
3430
|
+
ctx.beginPath();
|
|
3431
|
+
const fillW = (cardW - 48) * (0.45 + 0.15 * Math.sin(b * 1.5 + 2)) * bProgress;
|
|
3432
|
+
ctx.roundRect(24, barY + 22, fillW, 14, 7);
|
|
3433
|
+
ctx.fill();
|
|
3434
|
+
|
|
3435
|
+
// Feature icon / indicator
|
|
3436
|
+
sCircle(36, barY + 8, 6, 100 + b, '#38bdf8', 2, '#38bdf8');
|
|
3437
|
+
ctx.fillStyle = TEXT_MAIN;
|
|
3438
|
+
ctx.font = '600 18px ' + FONT_DISPLAY;
|
|
3439
|
+
ctx.fillText('ATTRIBUTE 0' + (b + 1), 54, barY + 14);
|
|
3440
|
+
}
|
|
3441
|
+
ctx.restore();
|
|
3442
|
+
|
|
3443
|
+
// Right Card (Side B)
|
|
3444
|
+
const rightX = cx + 35;
|
|
3445
|
+
ctx.save();
|
|
3446
|
+
ctx.globalAlpha = Math.min(1, Math.max(0, cardRightSpring));
|
|
3447
|
+
ctx.translate(rightX + (1 - cardRightSpring) * 60, cardY);
|
|
3448
|
+
ctx.fillStyle = CARD_BG;
|
|
3449
|
+
ctx.strokeStyle = CARD_BORDER;
|
|
3450
|
+
ctx.lineWidth = 2;
|
|
3451
|
+
ctx.beginPath();
|
|
3452
|
+
ctx.roundRect(0, 0, cardW, cardH, 20);
|
|
3453
|
+
ctx.fill();
|
|
3454
|
+
ctx.stroke();
|
|
3455
|
+
|
|
3456
|
+
// Right Card Header & Accent Pill
|
|
3457
|
+
ctx.fillStyle = 'rgba(244, 63, 94, 0.15)';
|
|
3458
|
+
ctx.beginPath();
|
|
3459
|
+
ctx.roundRect(24, 24, 110, 32, 16);
|
|
3460
|
+
ctx.fill();
|
|
3461
|
+
ctx.fillStyle = '#f43f5e';
|
|
3462
|
+
ctx.font = 'bold 16px ' + FONT_DISPLAY;
|
|
3463
|
+
ctx.fillText('OPTION B', 42, 45);
|
|
3464
|
+
|
|
3465
|
+
// Right Metrics / Feature Bars
|
|
3466
|
+
for (let b = 0; b < 4; b++) {
|
|
3467
|
+
const barY = 90 + b * 75;
|
|
3468
|
+
const bProgress = Math.min(1, Math.max(0, spring(t, 0.35 + b * 0.1, 160, 14)));
|
|
3469
|
+
ctx.fillStyle = IS_LIGHT ? 'rgba(0,0,0,0.06)' : 'rgba(255,255,255,0.06)';
|
|
3470
|
+
ctx.beginPath();
|
|
3471
|
+
ctx.roundRect(24, barY + 22, cardW - 48, 14, 7);
|
|
3472
|
+
ctx.fill();
|
|
3473
|
+
|
|
3474
|
+
ctx.fillStyle = '#f43f5e';
|
|
3475
|
+
ctx.beginPath();
|
|
3476
|
+
const fillW = (cardW - 48) * (0.55 + 0.12 * Math.cos(b * 1.8 + 1)) * bProgress;
|
|
3477
|
+
ctx.roundRect(24, barY + 22, fillW, 14, 7);
|
|
3478
|
+
ctx.fill();
|
|
3479
|
+
|
|
3480
|
+
sCircle(36, barY + 8, 6, 200 + b, '#f43f5e', 2, '#f43f5e');
|
|
3481
|
+
ctx.fillStyle = TEXT_MAIN;
|
|
3482
|
+
ctx.font = '600 18px ' + FONT_DISPLAY;
|
|
3483
|
+
ctx.fillText('ATTRIBUTE 0' + (b + 1), 54, barY + 14);
|
|
3484
|
+
}
|
|
3485
|
+
ctx.restore();
|
|
3486
|
+
|
|
3487
|
+
// Center VS Emblem Badge
|
|
3488
|
+
ctx.save();
|
|
3489
|
+
ctx.translate(cx, cy);
|
|
3490
|
+
const bScale = Math.max(0, badgeSpring);
|
|
3491
|
+
ctx.scale(bScale, bScale);
|
|
3492
|
+
ctx.fillStyle = IS_LIGHT ? '#ffffff' : '#0f172a';
|
|
3493
|
+
ctx.strokeStyle = ACCENT;
|
|
3494
|
+
ctx.lineWidth = 4;
|
|
3495
|
+
ctx.beginPath();
|
|
3496
|
+
ctx.arc(0, 0, 48, 0, Math.PI * 2);
|
|
3497
|
+
ctx.fill();
|
|
3498
|
+
ctx.stroke();
|
|
3499
|
+
|
|
3500
|
+
ctx.strokeStyle = 'rgba(255, 255, 255, 0.4)';
|
|
3501
|
+
ctx.lineWidth = 1.5;
|
|
3502
|
+
ctx.beginPath();
|
|
3503
|
+
ctx.arc(0, 0, 42, 0, Math.PI * 2);
|
|
3504
|
+
ctx.stroke();
|
|
3505
|
+
|
|
3506
|
+
ctx.font = '900 28px ' + FONT_DISPLAY;
|
|
3507
|
+
ctx.fillStyle = ACCENT;
|
|
3508
|
+
ctx.textAlign = 'center';
|
|
3509
|
+
ctx.textBaseline = 'middle';
|
|
3510
|
+
ctx.fillText('VS', 0, 2);
|
|
3511
|
+
ctx.restore();
|
|
3512
|
+
|
|
3513
|
+
} else if (COMP_THEME === 'mechanism_physics') {
|
|
3514
|
+
// Universal Archetype: Mechanical Engineering, Cutaway Gears, Forces & Kinematics
|
|
3515
|
+
const engSpring = spring(t, 0.05, 170, 14);
|
|
3516
|
+
const vectorSpring = spring(t, 0.45, 180, 15);
|
|
3517
|
+
|
|
3518
|
+
// Background schematic blueprint grid lines
|
|
3519
|
+
ctx.save();
|
|
3520
|
+
ctx.strokeStyle = IS_LIGHT ? 'rgba(0,0,0,0.07)' : 'rgba(56, 189, 248, 0.12)';
|
|
3521
|
+
ctx.lineWidth = 1;
|
|
3522
|
+
for (let gx = cx - 450; gx <= cx + 450; gx += 60) {
|
|
3523
|
+
ctx.beginPath(); ctx.moveTo(gx, cy - 320); ctx.lineTo(gx, cy + 320); ctx.stroke();
|
|
3524
|
+
}
|
|
3525
|
+
for (let gy = cy - 300; gy <= cy + 300; gy += 60) {
|
|
3526
|
+
ctx.beginPath(); ctx.moveTo(cx - 480, gy); ctx.lineTo(cx + 480, gy); ctx.stroke();
|
|
3527
|
+
}
|
|
3528
|
+
ctx.strokeStyle = IS_LIGHT ? 'rgba(0,0,0,0.1)' : 'rgba(56, 189, 248, 0.2)';
|
|
3529
|
+
ctx.lineWidth = 1.5;
|
|
3530
|
+
ctx.beginPath(); ctx.arc(cx, cy, 340, 0, Math.PI * 2); ctx.stroke();
|
|
3531
|
+
ctx.restore();
|
|
3532
|
+
|
|
3533
|
+
// Helper: Draw precision technical gear with teeth
|
|
3534
|
+
function drawGear(gx, gy, radius, teeth, rotAngle, color, axleColor) {
|
|
3535
|
+
ctx.save();
|
|
3536
|
+
ctx.translate(gx, gy);
|
|
3537
|
+
ctx.rotate(rotAngle);
|
|
3538
|
+
ctx.fillStyle = color;
|
|
3539
|
+
ctx.strokeStyle = STROKE_COLOR;
|
|
3540
|
+
ctx.lineWidth = 2.5;
|
|
3541
|
+
|
|
3542
|
+
ctx.beginPath();
|
|
3543
|
+
const toothDepth = radius * 0.14;
|
|
3544
|
+
for (let i = 0; i < teeth; i++) {
|
|
3545
|
+
const a0 = (i * 2 * Math.PI) / teeth;
|
|
3546
|
+
const a1 = a0 + (Math.PI / teeth) * 0.35;
|
|
3547
|
+
const a2 = a0 + (Math.PI / teeth) * 0.65;
|
|
3548
|
+
const a3 = a0 + (Math.PI / teeth);
|
|
3549
|
+
|
|
3550
|
+
const rIn = radius - toothDepth;
|
|
3551
|
+
const rOut = radius + toothDepth;
|
|
3552
|
+
|
|
3553
|
+
if (i === 0) ctx.moveTo(rIn * Math.cos(a0), rIn * Math.sin(a0));
|
|
3554
|
+
else ctx.lineTo(rIn * Math.cos(a0), rIn * Math.sin(a0));
|
|
3555
|
+
ctx.lineTo(rOut * Math.cos(a1), rOut * Math.sin(a1));
|
|
3556
|
+
ctx.lineTo(rOut * Math.cos(a2), rOut * Math.sin(a2));
|
|
3557
|
+
ctx.lineTo(rIn * Math.cos(a3), rIn * Math.sin(a3));
|
|
3558
|
+
}
|
|
3559
|
+
ctx.closePath();
|
|
3560
|
+
ctx.fill();
|
|
3561
|
+
ctx.stroke();
|
|
3562
|
+
|
|
3563
|
+
// Hollow interior & spokes
|
|
3564
|
+
ctx.beginPath();
|
|
3565
|
+
ctx.arc(0, 0, radius * 0.62, 0, Math.PI * 2);
|
|
3566
|
+
ctx.fillStyle = IS_LIGHT ? '#ffffff' : '#080d1a';
|
|
3567
|
+
ctx.fill();
|
|
3568
|
+
ctx.stroke();
|
|
3569
|
+
|
|
3570
|
+
// 4 Precision Spokes
|
|
3571
|
+
for (let sp = 0; sp < 4; sp++) {
|
|
3572
|
+
ctx.save();
|
|
3573
|
+
ctx.rotate((sp * Math.PI) / 2);
|
|
3574
|
+
ctx.fillStyle = color;
|
|
3575
|
+
ctx.fillRect(-6, -radius * 0.58, 12, radius * 0.58 * 2);
|
|
3576
|
+
ctx.restore();
|
|
3577
|
+
}
|
|
3578
|
+
|
|
3579
|
+
// Center Axle Hub
|
|
3580
|
+
ctx.beginPath();
|
|
3581
|
+
ctx.arc(0, 0, radius * 0.25, 0, Math.PI * 2);
|
|
3582
|
+
ctx.fillStyle = axleColor;
|
|
3583
|
+
ctx.fill();
|
|
3584
|
+
ctx.stroke();
|
|
3585
|
+
sCircle(0, 0, radius * 0.1, 50, '#ffffff', 2, '#ffffff');
|
|
3586
|
+
|
|
3587
|
+
ctx.restore();
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3590
|
+
ctx.save();
|
|
3591
|
+
const gScale = Math.min(1, Math.max(0, engSpring));
|
|
3592
|
+
ctx.translate(cx, cy);
|
|
3593
|
+
ctx.scale(gScale, gScale);
|
|
3594
|
+
ctx.translate(-cx, -cy);
|
|
3595
|
+
|
|
3596
|
+
// Primary Gear Train (Interlocking 3 gears)
|
|
3597
|
+
const gearSpeed = t * 1.4;
|
|
3598
|
+
drawGear(cx - 150, cy, 140, 24, gearSpeed, IS_LIGHT ? '#e2e8f0' : '#1e293b', ACCENT);
|
|
3599
|
+
drawGear(cx + 90, cy - 80, 105, 18, -gearSpeed * (24 / 18) + 0.17, IS_LIGHT ? '#cbd5e1' : '#334155', ACCENT2);
|
|
3600
|
+
drawGear(cx + 175, cy + 95, 70, 12, gearSpeed * (24 / 12) + 0.25, IS_LIGHT ? '#94a3b8' : '#475569', '#10b981');
|
|
3601
|
+
|
|
3602
|
+
// Dynamic Kinematic Linkage / Piston
|
|
3603
|
+
const pistonCycle = t * 3.5;
|
|
3604
|
+
const crankR = 60;
|
|
3605
|
+
const crankX = cx - 150 + Math.cos(gearSpeed) * crankR;
|
|
3606
|
+
const crankY = cy + Math.sin(gearSpeed) * crankR;
|
|
3607
|
+
const pistonY = cy + 180 + Math.sin(pistonCycle) * 35;
|
|
3608
|
+
const pistonX = cx - 150;
|
|
3609
|
+
|
|
3610
|
+
sLine(crankX, crankY, pistonX, pistonY, 301, ACCENT, 5);
|
|
3611
|
+
sCircle(crankX, crankY, 8, 302, '#ffffff', 2, ACCENT);
|
|
3612
|
+
sCircle(pistonX, pistonY, 8, 303, '#ffffff', 2, ACCENT);
|
|
3613
|
+
|
|
3614
|
+
// Piston Head in Cylinder
|
|
3615
|
+
ctx.fillStyle = IS_LIGHT ? '#64748b' : '#38bdf8';
|
|
3616
|
+
ctx.fillRect(pistonX - 35, pistonY - 15, 70, 30);
|
|
3617
|
+
ctx.strokeStyle = STROKE_COLOR;
|
|
3618
|
+
ctx.lineWidth = 2.5;
|
|
3619
|
+
ctx.strokeRect(pistonX - 35, pistonY - 15, 70, 30);
|
|
3620
|
+
|
|
3621
|
+
// Dynamic Force Vectors & Aerodynamic Flow Arrows
|
|
3622
|
+
if (vectorSpring > 0.05) {
|
|
3623
|
+
ctx.save();
|
|
3624
|
+
ctx.globalAlpha = Math.min(1, Math.max(0, vectorSpring));
|
|
3625
|
+
|
|
3626
|
+
const vX = cx + 290;
|
|
3627
|
+
const vY = cy - 140;
|
|
3628
|
+
sLine(vX, vY, vX + 110, vY, 401, '#10b981', 4);
|
|
3629
|
+
ctx.fillStyle = '#10b981';
|
|
3630
|
+
ctx.beginPath();
|
|
3631
|
+
ctx.moveTo(vX + 125, vY);
|
|
3632
|
+
ctx.lineTo(vX + 105, vY - 8);
|
|
3633
|
+
ctx.lineTo(vX + 105, vY + 8);
|
|
3634
|
+
ctx.closePath();
|
|
3635
|
+
ctx.fill();
|
|
3636
|
+
ctx.font = 'bold 15px ' + FONT_DISPLAY;
|
|
3637
|
+
ctx.fillText('F_THRUST = 4.8 kN', vX, vY - 16);
|
|
3638
|
+
|
|
3639
|
+
ctx.strokeStyle = '#f59e0b';
|
|
3640
|
+
ctx.lineWidth = 3;
|
|
3641
|
+
ctx.beginPath();
|
|
3642
|
+
ctx.arc(cx - 150, cy, 185, -0.6, 0.6);
|
|
3643
|
+
ctx.stroke();
|
|
3644
|
+
ctx.fillStyle = '#f59e0b';
|
|
3645
|
+
ctx.font = 'bold 15px ' + FONT_DISPLAY;
|
|
3646
|
+
ctx.fillText('TORQUE = 320 Nm', cx - 210, cy - 195);
|
|
3647
|
+
ctx.restore();
|
|
3648
|
+
}
|
|
3649
|
+
|
|
3650
|
+
ctx.restore();
|
|
3651
|
+
|
|
3652
|
+
} else if (COMP_THEME === 'process_pipeline') {
|
|
3653
|
+
// Universal Archetype: Multi-Stage Process / Pipeline / Supply Chain / Lifecycle
|
|
3654
|
+
const pipeSpring = spring(t, 0.05, 170, 14);
|
|
3655
|
+
const stages = [
|
|
3656
|
+
{ name: 'INGEST', label: '01 / SOURCE', desc: 'Raw Signals' },
|
|
3657
|
+
{ name: 'SYNTHESIS', label: '02 / TRANSFORM', desc: 'Active Process' },
|
|
3658
|
+
{ name: 'VALIDATION', label: '03 / VERIFY', desc: 'Quality Gate' },
|
|
3659
|
+
{ name: 'DEPLOYMENT', label: '04 / OUTPUT', desc: 'Master Release' },
|
|
3660
|
+
];
|
|
3661
|
+
|
|
3662
|
+
const pipeY = cy;
|
|
3663
|
+
const startX = cx - 450;
|
|
3664
|
+
const totalW = 900;
|
|
3665
|
+
const segW = totalW / (stages.length - 1);
|
|
3666
|
+
|
|
3667
|
+
ctx.save();
|
|
3668
|
+
const pScale = Math.min(1, Math.max(0, pipeSpring));
|
|
3669
|
+
ctx.translate(cx, cy);
|
|
3670
|
+
ctx.scale(pScale, pScale);
|
|
3671
|
+
ctx.translate(-cx, -cy);
|
|
3672
|
+
|
|
3673
|
+
// Main Conduit Pipeline Track
|
|
3674
|
+
const conduitG = ctx.createLinearGradient(startX, 0, startX + totalW, 0);
|
|
3675
|
+
conduitG.addColorStop(0, ACCENT);
|
|
3676
|
+
conduitG.addColorStop(0.5, '#38bdf8');
|
|
3677
|
+
conduitG.addColorStop(1, '#10b981');
|
|
3678
|
+
|
|
3679
|
+
ctx.strokeStyle = IS_LIGHT ? 'rgba(0,0,0,0.1)' : 'rgba(255,255,255,0.15)';
|
|
3680
|
+
ctx.lineWidth = 14;
|
|
3681
|
+
ctx.lineCap = 'round';
|
|
3682
|
+
ctx.beginPath();
|
|
3683
|
+
ctx.moveTo(startX, pipeY);
|
|
3684
|
+
ctx.lineTo(startX + totalW, pipeY);
|
|
3685
|
+
ctx.stroke();
|
|
3686
|
+
|
|
3687
|
+
ctx.strokeStyle = conduitG;
|
|
3688
|
+
ctx.lineWidth = 6;
|
|
3689
|
+
ctx.stroke();
|
|
3690
|
+
|
|
3691
|
+
// Flowing Data Packet Orbs along the conduit
|
|
3692
|
+
for (let pk = 0; pk < 5; pk++) {
|
|
3693
|
+
const pkProgress = ((t * 0.45 + pk * 0.22) % 1.0);
|
|
3694
|
+
const pkX = startX + pkProgress * totalW;
|
|
3695
|
+
ctx.fillStyle = '#ffffff';
|
|
3696
|
+
ctx.beginPath();
|
|
3697
|
+
ctx.arc(pkX, pipeY, 6, 0, Math.PI * 2);
|
|
3698
|
+
ctx.fill();
|
|
3699
|
+
|
|
3700
|
+
ctx.fillStyle = ACCENT;
|
|
3701
|
+
ctx.globalAlpha = 0.4;
|
|
3702
|
+
ctx.beginPath();
|
|
3703
|
+
ctx.arc(pkX, pipeY, 14, 0, Math.PI * 2);
|
|
3704
|
+
ctx.fill();
|
|
3705
|
+
ctx.globalAlpha = 1.0;
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3708
|
+
// Stage Nodes & Info Cards
|
|
3709
|
+
for (let s = 0; s < stages.length; s++) {
|
|
3710
|
+
const st = stages[s];
|
|
3711
|
+
const nodeX = startX + s * segW;
|
|
3712
|
+
const nodeSpring = spring(t, 0.15 + s * 0.16, 180, 14);
|
|
3713
|
+
const nodeAlpha = Math.min(1, Math.max(0, nodeSpring));
|
|
3714
|
+
|
|
3715
|
+
const activeStage = Math.min(stages.length - 1, Math.floor((t / window.DURATION) * stages.length));
|
|
3716
|
+
const isActive = s === activeStage;
|
|
3717
|
+
|
|
3718
|
+
ctx.save();
|
|
3719
|
+
ctx.globalAlpha = nodeAlpha;
|
|
3720
|
+
ctx.translate(nodeX, pipeY);
|
|
3721
|
+
|
|
3722
|
+
const isTop = s % 2 === 0;
|
|
3723
|
+
const cardOffsetY = isTop ? -180 : 70;
|
|
3724
|
+
const cardW = 190;
|
|
3725
|
+
const cardH = 95;
|
|
3726
|
+
|
|
3727
|
+
sLine(0, 0, 0, isTop ? -50 : 50, 500 + s, isActive ? ACCENT : STROKE_ALT, 2.5);
|
|
3728
|
+
|
|
3729
|
+
ctx.fillStyle = CARD_BG;
|
|
3730
|
+
ctx.strokeStyle = isActive ? ACCENT : CARD_BORDER;
|
|
3731
|
+
ctx.lineWidth = isActive ? 3 : 1.5;
|
|
3732
|
+
ctx.beginPath();
|
|
3733
|
+
ctx.roundRect(-cardW / 2, cardOffsetY, cardW, cardH, 14);
|
|
3734
|
+
ctx.fill();
|
|
3735
|
+
ctx.stroke();
|
|
3736
|
+
|
|
3737
|
+
if (isActive) {
|
|
3738
|
+
ctx.strokeStyle = 'rgba(255, 255, 255, 0.3)';
|
|
3739
|
+
ctx.lineWidth = 1;
|
|
3740
|
+
ctx.beginPath();
|
|
3741
|
+
ctx.roundRect(-cardW / 2 + 4, cardOffsetY + 4, cardW - 8, cardH - 8, 10);
|
|
3742
|
+
ctx.stroke();
|
|
3743
|
+
}
|
|
3744
|
+
|
|
3745
|
+
ctx.textAlign = 'center';
|
|
3746
|
+
ctx.font = 'bold 12px ' + FONT_DISPLAY;
|
|
3747
|
+
ctx.fillStyle = isActive ? ACCENT : '#94a3b8';
|
|
3748
|
+
ctx.fillText(st.label, 0, cardOffsetY + 26);
|
|
3749
|
+
|
|
3750
|
+
ctx.font = '800 16px ' + FONT_DISPLAY;
|
|
3751
|
+
ctx.fillStyle = TEXT_MAIN;
|
|
3752
|
+
ctx.fillText(st.name, 0, cardOffsetY + 52);
|
|
3753
|
+
|
|
3754
|
+
ctx.font = '500 13px ' + FONT_BODY;
|
|
3755
|
+
ctx.fillStyle = TEXT_MUTED;
|
|
3756
|
+
ctx.fillText(st.desc, 0, cardOffsetY + 76);
|
|
3757
|
+
|
|
3758
|
+
const nodeR = isActive ? 22 : 16;
|
|
3759
|
+
ctx.fillStyle = IS_LIGHT ? '#ffffff' : '#0f172a';
|
|
3760
|
+
ctx.beginPath();
|
|
3761
|
+
ctx.arc(0, 0, nodeR, 0, Math.PI * 2);
|
|
3762
|
+
ctx.fill();
|
|
3763
|
+
|
|
3764
|
+
ctx.strokeStyle = isActive ? ACCENT : '#64748b';
|
|
3765
|
+
ctx.lineWidth = isActive ? 4 : 3;
|
|
3766
|
+
ctx.beginPath();
|
|
3767
|
+
ctx.arc(0, 0, nodeR, 0, Math.PI * 2);
|
|
3768
|
+
ctx.stroke();
|
|
3769
|
+
|
|
3770
|
+
sCircle(0, 0, isActive ? 8 : 5, 520 + s, isActive ? '#ffffff' : '#94a3b8', 2, isActive ? ACCENT : '#64748b');
|
|
3771
|
+
|
|
3772
|
+
ctx.restore();
|
|
3773
|
+
}
|
|
3774
|
+
|
|
3775
|
+
ctx.restore();
|
|
3776
|
+
|
|
3777
|
+
} else if (COMP_THEME === 'cognitive_insight') {
|
|
3778
|
+
// Universal Archetype: Cognitive Psychology, Neural Eureka, Theory of Mind & Idea Spark
|
|
3779
|
+
const brainSpring = spring(t, 0.05, 170, 14);
|
|
3780
|
+
const eurekaSpring = spring(t, window.DURATION * 0.35, 160, 12);
|
|
3781
|
+
const orbitSpring = spring(t, 0.40, 180, 15);
|
|
3782
|
+
|
|
3783
|
+
ctx.save();
|
|
3784
|
+
const cScale = Math.min(1, Math.max(0, brainSpring));
|
|
3785
|
+
ctx.translate(cx, cy);
|
|
3786
|
+
ctx.scale(cScale, cScale);
|
|
3787
|
+
|
|
3788
|
+
// Concentric Thought Wave Ripples
|
|
3789
|
+
for (let r = 0; r < 4; r++) {
|
|
3790
|
+
const ripPhase = (t * 0.8 + r * 0.25) % 1.0;
|
|
3791
|
+
const ripR = 90 + ripPhase * 240;
|
|
3792
|
+
ctx.strokeStyle = ACCENT;
|
|
3793
|
+
ctx.globalAlpha = Math.max(0, (1 - ripPhase) * 0.35);
|
|
3794
|
+
ctx.lineWidth = 2.5;
|
|
3795
|
+
ctx.beginPath();
|
|
3796
|
+
ctx.arc(0, 0, ripR, 0, Math.PI * 2);
|
|
3797
|
+
ctx.stroke();
|
|
3798
|
+
}
|
|
3799
|
+
ctx.globalAlpha = 1.0;
|
|
3800
|
+
|
|
3801
|
+
// Cerebral Neural Core / Brain Silhouette
|
|
3802
|
+
const brainR = 120;
|
|
3803
|
+
const coreGrad = ctx.createRadialGradient(0, -20, 20, 0, 0, brainR * 1.5);
|
|
3804
|
+
coreGrad.addColorStop(0, '#fef08a');
|
|
3805
|
+
coreGrad.addColorStop(0.3, ACCENT);
|
|
3806
|
+
coreGrad.addColorStop(0.8, '#6366f1');
|
|
3807
|
+
coreGrad.addColorStop(1, 'rgba(15, 23, 42, 0)');
|
|
3808
|
+
|
|
3809
|
+
ctx.fillStyle = coreGrad;
|
|
3810
|
+
ctx.beginPath();
|
|
3811
|
+
ctx.arc(0, 0, brainR * 1.4, 0, Math.PI * 2);
|
|
3812
|
+
ctx.fill();
|
|
3813
|
+
|
|
3814
|
+
// Neural Synapses
|
|
3815
|
+
const synNodes = [
|
|
3816
|
+
{ x: -50, y: -60 }, { x: 0, y: -80 }, { x: 50, y: -60 },
|
|
3817
|
+
{ x: -80, y: -20 }, { x: -30, y: -20 }, { x: 30, y: -20 }, { x: 80, y: -20 },
|
|
3818
|
+
{ x: -60, y: 30 }, { x: 0, y: 25 }, { x: 60, y: 30 },
|
|
3819
|
+
{ x: -25, y: 70 }, { x: 25, y: 70 },
|
|
3820
|
+
];
|
|
3821
|
+
|
|
3822
|
+
ctx.strokeStyle = 'rgba(255, 255, 255, 0.35)';
|
|
3823
|
+
ctx.lineWidth = 2;
|
|
3824
|
+
for (let i = 0; i < synNodes.length; i++) {
|
|
3825
|
+
for (let j = i + 1; j < synNodes.length; j++) {
|
|
3826
|
+
const dx = synNodes[i].x - synNodes[j].x;
|
|
3827
|
+
const dy = synNodes[i].y - synNodes[j].y;
|
|
3828
|
+
const dist = Math.hypot(dx, dy);
|
|
3829
|
+
if (dist < 75) {
|
|
3830
|
+
ctx.beginPath();
|
|
3831
|
+
ctx.moveTo(synNodes[i].x, synNodes[i].y);
|
|
3832
|
+
ctx.lineTo(synNodes[j].x, synNodes[j].y);
|
|
3833
|
+
ctx.stroke();
|
|
3834
|
+
|
|
3835
|
+
const pulse = (t * 2.5 + i * 0.3 + j * 0.2) % 1.0;
|
|
3836
|
+
const px = synNodes[i].x + (synNodes[j].x - synNodes[i].x) * pulse;
|
|
3837
|
+
const py = synNodes[i].y + (synNodes[j].y - synNodes[i].y) * pulse;
|
|
3838
|
+
ctx.fillStyle = '#ffffff';
|
|
3839
|
+
ctx.beginPath();
|
|
3840
|
+
ctx.arc(px, py, 2.5, 0, Math.PI * 2);
|
|
3841
|
+
ctx.fill();
|
|
3842
|
+
}
|
|
3843
|
+
}
|
|
3844
|
+
}
|
|
3845
|
+
|
|
3846
|
+
for (let n = 0; n < synNodes.length; n++) {
|
|
3847
|
+
const sn = synNodes[n];
|
|
3848
|
+
const fire = Math.sin(t * 8 + n * 1.3);
|
|
3849
|
+
const dotR = 5 + (fire > 0.5 ? 2.5 : 0);
|
|
3850
|
+
sCircle(sn.x, sn.y, dotR, 600 + n, '#ffffff', 2, fire > 0.3 ? '#fef08a' : ACCENT);
|
|
3851
|
+
}
|
|
3852
|
+
|
|
3853
|
+
// Eureka Idea Spark Burst
|
|
3854
|
+
if (eurekaSpring > 0.05) {
|
|
3855
|
+
const eProgress = Math.min(1, Math.max(0, eurekaSpring));
|
|
3856
|
+
ctx.save();
|
|
3857
|
+
ctx.scale(eProgress, eProgress);
|
|
3858
|
+
|
|
3859
|
+
const rays = 12;
|
|
3860
|
+
for (let ry = 0; ry < rays; ry++) {
|
|
3861
|
+
const ang = (ry * Math.PI * 2) / rays + t * 0.4;
|
|
3862
|
+
const len = 140 + Math.sin(t * 6 + ry) * 25;
|
|
3863
|
+
ctx.strokeStyle = ry % 2 === 0 ? '#fef08a' : '#ffffff';
|
|
3864
|
+
ctx.lineWidth = ry % 2 === 0 ? 3 : 1.5;
|
|
3865
|
+
ctx.beginPath();
|
|
3866
|
+
ctx.moveTo(Math.cos(ang) * 50, Math.sin(ang) * 50);
|
|
3867
|
+
ctx.lineTo(Math.cos(ang) * len, Math.sin(ang) * len);
|
|
3868
|
+
ctx.stroke();
|
|
3869
|
+
}
|
|
3870
|
+
|
|
3871
|
+
sCircle(0, 0, 18, 650, '#ffffff', 3, '#f59e0b');
|
|
3872
|
+
sCircle(0, 0, 8, 651, '#ffffff', 2, '#ffffff');
|
|
3873
|
+
ctx.restore();
|
|
3874
|
+
}
|
|
3875
|
+
|
|
3876
|
+
// Orbiting Insight Badges
|
|
3877
|
+
if (orbitSpring > 0.05) {
|
|
3878
|
+
const badges = ['PERCEPTION', 'COGNITION', 'INTUITION', 'INSIGHT'];
|
|
3879
|
+
ctx.save();
|
|
3880
|
+
ctx.globalAlpha = Math.min(1, Math.max(0, orbitSpring));
|
|
3881
|
+
|
|
3882
|
+
for (let bg = 0; bg < badges.length; bg++) {
|
|
3883
|
+
const bgAngle = (bg * Math.PI * 2) / badges.length + t * 0.35;
|
|
3884
|
+
const bgX = Math.cos(bgAngle) * 310;
|
|
3885
|
+
const bgY = Math.sin(bgAngle) * 190;
|
|
3886
|
+
|
|
3887
|
+
ctx.fillStyle = CARD_BG;
|
|
3888
|
+
ctx.strokeStyle = CARD_BORDER;
|
|
3889
|
+
ctx.lineWidth = 1.5;
|
|
3890
|
+
ctx.beginPath();
|
|
3891
|
+
ctx.roundRect(bgX - 65, bgY - 18, 130, 36, 18);
|
|
3892
|
+
ctx.fill();
|
|
3893
|
+
ctx.stroke();
|
|
3894
|
+
|
|
3895
|
+
ctx.font = 'bold 13px ' + FONT_DISPLAY;
|
|
3896
|
+
ctx.fillStyle = TEXT_MAIN;
|
|
3897
|
+
ctx.textAlign = 'center';
|
|
3898
|
+
ctx.fillText(badges[bg], bgX, bgY + 5);
|
|
3899
|
+
|
|
3900
|
+
ctx.strokeStyle = 'rgba(255, 255, 255, 0.12)';
|
|
3901
|
+
ctx.beginPath();
|
|
3902
|
+
ctx.moveTo(0, 0);
|
|
3903
|
+
ctx.lineTo(bgX, bgY);
|
|
3904
|
+
ctx.stroke();
|
|
3905
|
+
}
|
|
3906
|
+
ctx.restore();
|
|
3907
|
+
}
|
|
3908
|
+
|
|
3909
|
+
ctx.restore();
|
|
3910
|
+
|
|
3911
|
+
} else if (COMP_THEME === 'magnification_focus') {
|
|
3912
|
+
// Universal Archetype: Optical Loupe, Precision Macro Zoom & Scrutiny Reticle
|
|
3913
|
+
const focusSpring = spring(t, 0.05, 170, 14);
|
|
3914
|
+
const loupeSpring = spring(t, 0.25, 180, 14);
|
|
3915
|
+
|
|
3916
|
+
// Deep research background grid & underlying specimen traces
|
|
3917
|
+
ctx.save();
|
|
3918
|
+
ctx.strokeStyle = IS_LIGHT ? 'rgba(0,0,0,0.06)' : 'rgba(255,255,255,0.08)';
|
|
3919
|
+
ctx.lineWidth = 1;
|
|
3920
|
+
for (let gx = 0; gx < W; gx += 40) {
|
|
3921
|
+
ctx.beginPath(); ctx.moveTo(gx, 0); ctx.lineTo(gx, H); ctx.stroke();
|
|
3922
|
+
}
|
|
3923
|
+
for (let gy = 0; gy < H; gy += 40) {
|
|
3924
|
+
ctx.beginPath(); ctx.moveTo(0, gy); ctx.lineTo(W, gy); ctx.stroke();
|
|
3925
|
+
}
|
|
3926
|
+
|
|
3927
|
+
for (let tr = 0; tr < 16; tr++) {
|
|
3928
|
+
const trX = cx - 350 + tr * 48;
|
|
3929
|
+
const trY = cy - 200 + ((tr * 73) % 400);
|
|
3930
|
+
sLine(trX, trY, trX + 80, trY + 40, 700 + tr, IS_LIGHT ? 'rgba(0,0,0,0.15)' : 'rgba(56, 189, 248, 0.2)', 2);
|
|
3931
|
+
}
|
|
3932
|
+
ctx.restore();
|
|
3933
|
+
|
|
3934
|
+
// Floating Optical Loupe (Lissajous gentle inspection movement)
|
|
3935
|
+
const loupeR = Math.min(W, H) * 0.26;
|
|
3936
|
+
const lissX = cx + Math.sin(t * 1.1) * 70;
|
|
3937
|
+
const lissY = cy + Math.cos(t * 0.85) * 45;
|
|
3938
|
+
|
|
3939
|
+
ctx.save();
|
|
3940
|
+
const lScale = Math.min(1, Math.max(0, loupeSpring));
|
|
3941
|
+
ctx.translate(lissX, lissY);
|
|
3942
|
+
ctx.scale(lScale, lScale);
|
|
3943
|
+
|
|
3944
|
+
ctx.fillStyle = 'rgba(0, 0, 0, 0.45)';
|
|
3945
|
+
ctx.beginPath();
|
|
3946
|
+
ctx.arc(15, 20, loupeR, 0, Math.PI * 2);
|
|
3947
|
+
ctx.fill();
|
|
3948
|
+
|
|
3949
|
+
// Inside Magnified Specimen (Clipped Circle)
|
|
3950
|
+
ctx.save();
|
|
3951
|
+
ctx.beginPath();
|
|
3952
|
+
ctx.arc(0, 0, loupeR - 8, 0, Math.PI * 2);
|
|
3953
|
+
ctx.clip();
|
|
3954
|
+
|
|
3955
|
+
ctx.fillStyle = IS_LIGHT ? '#ffffff' : '#070f1e';
|
|
3956
|
+
ctx.fillRect(-loupeR, -loupeR, loupeR * 2, loupeR * 2);
|
|
3957
|
+
|
|
3958
|
+
ctx.save();
|
|
3959
|
+
ctx.scale(2.4, 2.4);
|
|
3960
|
+
for (let m = -8; m <= 8; m++) {
|
|
3961
|
+
sLine(m * 22, -loupeR, m * 22, loupeR, 720 + m, 'rgba(56, 189, 248, 0.3)', 1.5);
|
|
3962
|
+
sLine(-loupeR, m * 22, loupeR, m * 22, 740 + m, 'rgba(56, 189, 248, 0.3)', 1.5);
|
|
3963
|
+
}
|
|
3964
|
+
sCircle(0, 0, 36, 760, '#f59e0b', 3, 'rgba(245, 158, 11, 0.2)');
|
|
3965
|
+
sCircle(0, 0, 16, 761, '#ef4444', 2.5, '#ef4444');
|
|
3966
|
+
for (let br = 0; br < 6; br++) {
|
|
3967
|
+
const brA = (br * Math.PI * 2) / 6 + t * 0.5;
|
|
3968
|
+
sLine(0, 0, Math.cos(brA) * 55, Math.sin(brA) * 55, 770 + br, ACCENT, 2.5);
|
|
3969
|
+
sCircle(Math.cos(brA) * 55, Math.sin(brA) * 55, 5, 780 + br, '#ffffff', 1.5, ACCENT);
|
|
3970
|
+
}
|
|
3971
|
+
ctx.restore();
|
|
3972
|
+
|
|
3973
|
+
// Precision Crosshairs & Calibrated Reticle
|
|
3974
|
+
ctx.strokeStyle = '#38bdf8';
|
|
3975
|
+
ctx.lineWidth = 1.5;
|
|
3976
|
+
ctx.beginPath();
|
|
3977
|
+
ctx.moveTo(-loupeR + 15, 0); ctx.lineTo(-25, 0);
|
|
3978
|
+
ctx.moveTo(25, 0); ctx.lineTo(loupeR - 15, 0);
|
|
3979
|
+
ctx.moveTo(0, -loupeR + 15); ctx.lineTo(0, -25);
|
|
3980
|
+
ctx.moveTo(0, 25); ctx.lineTo(0, loupeR - 15);
|
|
3981
|
+
ctx.stroke();
|
|
3982
|
+
|
|
3983
|
+
for (let deg = 0; deg < 36; deg++) {
|
|
3984
|
+
const dRad = (deg * Math.PI * 2) / 36;
|
|
3985
|
+
const isMajor = deg % 9 === 0;
|
|
3986
|
+
const tLen = isMajor ? 16 : 8;
|
|
3987
|
+
const rOut = loupeR - 12;
|
|
3988
|
+
const rIn = rOut - tLen;
|
|
3989
|
+
ctx.strokeStyle = isMajor ? ACCENT : 'rgba(255,255,255,0.4)';
|
|
3990
|
+
ctx.lineWidth = isMajor ? 2 : 1;
|
|
3991
|
+
ctx.beginPath();
|
|
3992
|
+
ctx.moveTo(Math.cos(dRad) * rIn, Math.sin(dRad) * rIn);
|
|
3993
|
+
ctx.lineTo(Math.cos(dRad) * rOut, Math.sin(dRad) * rOut);
|
|
3994
|
+
ctx.stroke();
|
|
3995
|
+
}
|
|
3996
|
+
ctx.restore(); // End clipping
|
|
3997
|
+
|
|
3998
|
+
// Bezel
|
|
3999
|
+
ctx.strokeStyle = '#d4af37';
|
|
4000
|
+
ctx.lineWidth = 14;
|
|
4001
|
+
ctx.beginPath();
|
|
4002
|
+
ctx.arc(0, 0, loupeR, 0, Math.PI * 2);
|
|
4003
|
+
ctx.stroke();
|
|
4004
|
+
|
|
4005
|
+
ctx.strokeStyle = '#fef08a';
|
|
4006
|
+
ctx.lineWidth = 2.5;
|
|
4007
|
+
ctx.beginPath();
|
|
4008
|
+
ctx.arc(0, 0, loupeR - 6, 0, Math.PI * 2);
|
|
4009
|
+
ctx.stroke();
|
|
4010
|
+
|
|
4011
|
+
ctx.strokeStyle = 'rgba(255, 255, 255, 0.4)';
|
|
4012
|
+
ctx.lineWidth = 4;
|
|
4013
|
+
ctx.beginPath();
|
|
4014
|
+
ctx.arc(0, 0, loupeR - 24, -1.8, -0.6);
|
|
4015
|
+
ctx.stroke();
|
|
4016
|
+
|
|
4017
|
+
ctx.fillStyle = CARD_BG;
|
|
4018
|
+
ctx.strokeStyle = ACCENT;
|
|
4019
|
+
ctx.lineWidth = 1.5;
|
|
4020
|
+
ctx.beginPath();
|
|
4021
|
+
ctx.roundRect(loupeR - 40, -loupeR - 10, 160, 48, 10);
|
|
4022
|
+
ctx.fill();
|
|
4023
|
+
ctx.stroke();
|
|
4024
|
+
|
|
4025
|
+
ctx.font = 'bold 12px monospace';
|
|
4026
|
+
ctx.fillStyle = ACCENT;
|
|
4027
|
+
ctx.fillText('MAGNIFY: 250%', loupeR - 26, -loupeR + 12);
|
|
4028
|
+
ctx.fillStyle = TEXT_MAIN;
|
|
4029
|
+
ctx.fillText('POS: ' + Math.round(lissX) + ', ' + Math.round(lissY), loupeR - 26, -loupeR + 28);
|
|
4030
|
+
|
|
4031
|
+
ctx.restore();
|
|
4032
|
+
|
|
4033
|
+
} else if (COMP_THEME === 'waveform') {
|
|
3317
4034
|
// Kinetic Frequency Equalizer Waveform
|
|
3318
4035
|
const bars = 36;
|
|
3319
4036
|
const barW = (W * 0.7) / bars;
|
|
@@ -3330,6 +4047,153 @@ function drawMainScene(t) {
|
|
|
3330
4047
|
ctx.fillStyle = barGrad;
|
|
3331
4048
|
ctx.beginPath(); ctx.roundRect(bx, by, barW - 6, hVal, 4); ctx.fill();
|
|
3332
4049
|
}
|
|
4050
|
+
|
|
4051
|
+
} else {
|
|
4052
|
+
// Universal Semantic Default & Explainer Presentation (Whiteboard Masterclass & Concept Pillars)
|
|
4053
|
+
const boardSpring = spring(t, 0.05, 170, 14);
|
|
4054
|
+
const p1Spring = spring(t, 0.18, 190, 15);
|
|
4055
|
+
const p2Spring = spring(t, 0.32, 190, 15);
|
|
4056
|
+
const p3Spring = spring(t, 0.46, 190, 15);
|
|
4057
|
+
|
|
4058
|
+
const boardW = Math.min(W * 0.88, 1100);
|
|
4059
|
+
const boardH = Math.min(H * 0.68, 620);
|
|
4060
|
+
const boardX = cx - boardW / 2;
|
|
4061
|
+
const boardY = cy - boardH / 2;
|
|
4062
|
+
|
|
4063
|
+
ctx.save();
|
|
4064
|
+
const bScale = Math.min(1, Math.max(0, boardSpring));
|
|
4065
|
+
ctx.translate(cx, cy);
|
|
4066
|
+
ctx.scale(bScale, bScale);
|
|
4067
|
+
ctx.translate(-cx, -cy);
|
|
4068
|
+
|
|
4069
|
+
// Architectural Frosted Glass Presentation Board
|
|
4070
|
+
ctx.fillStyle = CARD_BG;
|
|
4071
|
+
ctx.strokeStyle = CARD_BORDER;
|
|
4072
|
+
ctx.lineWidth = 2.5;
|
|
4073
|
+
ctx.beginPath();
|
|
4074
|
+
ctx.roundRect(boardX, boardY, boardW, boardH, 24);
|
|
4075
|
+
ctx.fill();
|
|
4076
|
+
ctx.stroke();
|
|
4077
|
+
|
|
4078
|
+
// Board Header Accent Bar
|
|
4079
|
+
ctx.fillStyle = ACCENT;
|
|
4080
|
+
ctx.beginPath();
|
|
4081
|
+
ctx.roundRect(boardX + 32, boardY + 28, 6, 32, 3);
|
|
4082
|
+
ctx.fill();
|
|
4083
|
+
|
|
4084
|
+
ctx.font = '800 22px ' + FONT_DISPLAY;
|
|
4085
|
+
ctx.fillStyle = TEXT_MAIN;
|
|
4086
|
+
ctx.textAlign = 'left';
|
|
4087
|
+
ctx.fillText('CORE PRINCIPLES & ARCHITECTURE', boardX + 48, boardY + 50);
|
|
4088
|
+
|
|
4089
|
+
// Live Status Badge
|
|
4090
|
+
ctx.fillStyle = 'rgba(16, 185, 129, 0.15)';
|
|
4091
|
+
ctx.beginPath();
|
|
4092
|
+
ctx.roundRect(boardX + boardW - 160, boardY + 28, 128, 30, 15);
|
|
4093
|
+
ctx.fill();
|
|
4094
|
+
ctx.fillStyle = '#10b981';
|
|
4095
|
+
ctx.font = 'bold 13px ' + FONT_DISPLAY;
|
|
4096
|
+
ctx.fillText('● SYSTEM ACTIVE', boardX + boardW - 146, boardY + 48);
|
|
4097
|
+
|
|
4098
|
+
// 3 Structured Concept Pillars
|
|
4099
|
+
const pillarData = [
|
|
4100
|
+
{ num: '01', title: 'FOUNDATION', desc: 'Core axioms & structure', icon: 'diamond', spr: p1Spring },
|
|
4101
|
+
{ num: '02', title: 'DYNAMICS', desc: 'Kinetic transformation', icon: 'gear', spr: p2Spring },
|
|
4102
|
+
{ num: '03', title: 'IMPACT', desc: 'Synthesized outcome', icon: 'star', spr: p3Spring },
|
|
4103
|
+
];
|
|
4104
|
+
|
|
4105
|
+
const pillarW = (boardW - 128) / 3;
|
|
4106
|
+
const pillarH = boardH - 140;
|
|
4107
|
+
const pillarY = boardY + 95;
|
|
4108
|
+
|
|
4109
|
+
for (let p = 0; p < 3; p++) {
|
|
4110
|
+
const pd = pillarData[p];
|
|
4111
|
+
const pilX = boardX + 32 + p * (pillarW + 32);
|
|
4112
|
+
const pAlpha = Math.min(1, Math.max(0, pd.spr));
|
|
4113
|
+
|
|
4114
|
+
ctx.save();
|
|
4115
|
+
ctx.globalAlpha = pAlpha;
|
|
4116
|
+
ctx.translate(pilX, pillarY + (1 - pd.spr) * 40);
|
|
4117
|
+
|
|
4118
|
+
// Pillar Card
|
|
4119
|
+
ctx.fillStyle = IS_LIGHT ? 'rgba(255,255,255,0.7)' : 'rgba(255,255,255,0.04)';
|
|
4120
|
+
ctx.strokeStyle = p === 1 ? ACCENT : 'rgba(255,255,255,0.1)';
|
|
4121
|
+
ctx.lineWidth = p === 1 ? 2.5 : 1.5;
|
|
4122
|
+
ctx.beginPath();
|
|
4123
|
+
ctx.roundRect(0, 0, pillarW, pillarH, 16);
|
|
4124
|
+
ctx.fill();
|
|
4125
|
+
ctx.stroke();
|
|
4126
|
+
|
|
4127
|
+
// Number Tag
|
|
4128
|
+
ctx.font = '900 48px ' + FONT_DISPLAY;
|
|
4129
|
+
ctx.fillStyle = p === 1 ? ACCENT : (IS_LIGHT ? 'rgba(0,0,0,0.1)' : 'rgba(255,255,255,0.12)');
|
|
4130
|
+
ctx.fillText(pd.num, 24, 60);
|
|
4131
|
+
|
|
4132
|
+
// Pillar Title
|
|
4133
|
+
ctx.font = '700 20px ' + FONT_DISPLAY;
|
|
4134
|
+
ctx.fillStyle = TEXT_MAIN;
|
|
4135
|
+
ctx.fillText(pd.title, 24, 110);
|
|
4136
|
+
|
|
4137
|
+
// Pillar Description
|
|
4138
|
+
ctx.font = '500 15px ' + FONT_BODY;
|
|
4139
|
+
ctx.fillStyle = TEXT_MUTED;
|
|
4140
|
+
ctx.fillText(pd.desc, 24, 138);
|
|
4141
|
+
|
|
4142
|
+
// Dynamic Animated Diagram Graphic inside Pillar
|
|
4143
|
+
const gfxY = pillarH * 0.65;
|
|
4144
|
+
const gfxX = pillarW / 2;
|
|
4145
|
+
|
|
4146
|
+
if (pd.icon === 'diamond') {
|
|
4147
|
+
ctx.save();
|
|
4148
|
+
ctx.translate(gfxX, gfxY);
|
|
4149
|
+
ctx.rotate(t * 1.2);
|
|
4150
|
+
ctx.strokeStyle = ACCENT;
|
|
4151
|
+
ctx.lineWidth = 3;
|
|
4152
|
+
ctx.strokeRect(-28, -28, 56, 56);
|
|
4153
|
+
sCircle(0, 0, 10, 800, '#ffffff', 2, ACCENT);
|
|
4154
|
+
ctx.restore();
|
|
4155
|
+
} else if (pd.icon === 'gear') {
|
|
4156
|
+
sCircle(gfxX, gfxY, 36, 810, ACCENT, 3);
|
|
4157
|
+
const pulseR = 20 + Math.sin(t * 5) * 8;
|
|
4158
|
+
sCircle(gfxX, gfxY, pulseR, 811, '#ffffff', 2, '#38bdf8');
|
|
4159
|
+
} else {
|
|
4160
|
+
for (let st = 0; st < 4; st++) {
|
|
4161
|
+
const ang = (st * Math.PI) / 2 + t * 0.8;
|
|
4162
|
+
sLine(gfxX, gfxY, gfxX + Math.cos(ang) * 36, gfxY + Math.sin(ang) * 36, 820 + st, '#f59e0b', 3);
|
|
4163
|
+
}
|
|
4164
|
+
sCircle(gfxX, gfxY, 8, 825, '#ffffff', 2, '#f59e0b');
|
|
4165
|
+
}
|
|
4166
|
+
|
|
4167
|
+
// Feature Check items
|
|
4168
|
+
for (let ch = 0; ch < 3; ch++) {
|
|
4169
|
+
const itemY = pillarH - 85 + ch * 24;
|
|
4170
|
+
sCircle(28, itemY, 4, 830 + ch, ACCENT, 2, ACCENT);
|
|
4171
|
+
ctx.fillStyle = TEXT_MAIN;
|
|
4172
|
+
ctx.font = '500 13px ' + FONT_BODY;
|
|
4173
|
+
ctx.fillText('Verification Key 0' + (ch + 1), 42, itemY + 4);
|
|
4174
|
+
}
|
|
4175
|
+
|
|
4176
|
+
ctx.restore();
|
|
4177
|
+
}
|
|
4178
|
+
|
|
4179
|
+
// Inter-Pillar Energy Connectors
|
|
4180
|
+
ctx.strokeStyle = ACCENT;
|
|
4181
|
+
ctx.lineWidth = 2.5;
|
|
4182
|
+
for (let c = 0; c < 2; c++) {
|
|
4183
|
+
const connX1 = boardX + 32 + (c + 1) * pillarW + c * 32 - 16;
|
|
4184
|
+
const connX2 = connX1 + 32;
|
|
4185
|
+
const connY = pillarY + 80;
|
|
4186
|
+
sLine(connX1, connY, connX2, connY, 850 + c, ACCENT, 3);
|
|
4187
|
+
ctx.fillStyle = ACCENT;
|
|
4188
|
+
ctx.beginPath();
|
|
4189
|
+
ctx.moveTo(connX2 + 4, connY);
|
|
4190
|
+
ctx.lineTo(connX2 - 4, connY - 5);
|
|
4191
|
+
ctx.lineTo(connX2 - 4, connY + 5);
|
|
4192
|
+
ctx.closePath();
|
|
4193
|
+
ctx.fill();
|
|
4194
|
+
}
|
|
4195
|
+
|
|
4196
|
+
ctx.restore();
|
|
3333
4197
|
}
|
|
3334
4198
|
|
|
3335
4199
|
ctx.restore();
|