// === DUAL-BRAIN HOLOGRAPHIC PANEL === import * as THREE from 'three'; import { NEXUS } from './constants.js'; import { scene } from './scene-setup.js'; const DUAL_BRAIN_ORIGIN = new THREE.Vector3(10, 3, -8); export const dualBrainGroup = new THREE.Group(); dualBrainGroup.position.copy(DUAL_BRAIN_ORIGIN); dualBrainGroup.lookAt(0, 3, 0); scene.add(dualBrainGroup); function createDualBrainTexture() { const W = 512, H = 512; const canvas = document.createElement('canvas'); canvas.width = W; canvas.height = H; const ctx = canvas.getContext('2d'); ctx.fillStyle = 'rgba(0, 6, 20, 0.90)'; ctx.fillRect(0, 0, W, H); ctx.strokeStyle = '#4488ff'; ctx.lineWidth = 2; ctx.strokeRect(1, 1, W - 2, H - 2); ctx.strokeStyle = '#223366'; ctx.lineWidth = 1; ctx.strokeRect(5, 5, W - 10, H - 10); ctx.font = 'bold 22px "Courier New", monospace'; ctx.fillStyle = '#88ccff'; ctx.textAlign = 'center'; ctx.fillText('\u25C8 DUAL-BRAIN STATUS', W / 2, 40); ctx.strokeStyle = '#1a3a6a'; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(20, 52); ctx.lineTo(W - 20, 52); ctx.stroke(); ctx.font = '11px "Courier New", monospace'; ctx.fillStyle = '#556688'; ctx.textAlign = 'left'; ctx.fillText('BRAIN GAP SCORECARD', 20, 74); const categories = [ { name: 'Triage' }, { name: 'Tool Use' }, { name: 'Code Gen' }, { name: 'Planning' }, { name: 'Communication' }, { name: 'Reasoning' }, ]; const barX = 20; const barW = W - 130; const barH = 20; let y = 90; for (const cat of categories) { ctx.font = '13px "Courier New", monospace'; ctx.fillStyle = '#445566'; ctx.textAlign = 'left'; ctx.fillText(cat.name, barX, y + 14); ctx.font = 'bold 13px "Courier New", monospace'; ctx.fillStyle = '#334466'; ctx.textAlign = 'right'; ctx.fillText('\u2014', W - 20, y + 14); y += 22; ctx.fillStyle = 'rgba(255, 255, 255, 0.06)'; ctx.fillRect(barX, y, barW, barH); y += barH + 12; } ctx.strokeStyle = '#1a3a6a'; ctx.beginPath(); ctx.moveTo(20, y + 4); ctx.lineTo(W - 20, y + 4); ctx.stroke(); y += 22; ctx.font = 'bold 18px "Courier New", monospace'; ctx.fillStyle = '#334466'; ctx.textAlign = 'center'; ctx.fillText('AWAITING DEPLOYMENT', W / 2, y + 10); ctx.font = '11px "Courier New", monospace'; ctx.fillStyle = '#223344'; ctx.fillText('Dual-brain system not yet connected', W / 2, y + 32); y += 52; ctx.beginPath(); ctx.arc(W / 2 - 60, y + 8, 6, 0, Math.PI * 2); ctx.fillStyle = '#334466'; ctx.fill(); ctx.font = '11px "Courier New", monospace'; ctx.fillStyle = '#334466'; ctx.textAlign = 'left'; ctx.fillText('CLOUD', W / 2 - 48, y + 12); ctx.beginPath(); ctx.arc(W / 2 + 30, y + 8, 6, 0, Math.PI * 2); ctx.fillStyle = '#334466'; ctx.fill(); ctx.fillStyle = '#334466'; ctx.fillText('LOCAL', W / 2 + 42, y + 12); return new THREE.CanvasTexture(canvas); } const dualBrainTexture = createDualBrainTexture(); const dualBrainMaterial = new THREE.SpriteMaterial({ map: dualBrainTexture, transparent: true, opacity: 0.92, depthWrite: false, }); export const dualBrainSprite = new THREE.Sprite(dualBrainMaterial); dualBrainSprite.scale.set(5.0, 5.0, 1); dualBrainSprite.position.set(0, 0, 0); dualBrainSprite.userData = { baseY: 0, floatPhase: 0, floatSpeed: 0.22, zoomLabel: 'Dual-Brain Status', }; dualBrainGroup.add(dualBrainSprite); export const dualBrainLight = new THREE.PointLight(0x4488ff, 0.6, 10); dualBrainLight.position.set(0, 0.5, 1); dualBrainGroup.add(dualBrainLight); // Brain Orbs const CLOUD_ORB_COLOR = 0x334466; const cloudOrbGeo = new THREE.SphereGeometry(0.35, 32, 32); export const cloudOrbMat = new THREE.MeshStandardMaterial({ color: CLOUD_ORB_COLOR, emissive: new THREE.Color(CLOUD_ORB_COLOR), emissiveIntensity: 0.1, metalness: 0.3, roughness: 0.2, transparent: true, opacity: 0.85, }); export const cloudOrb = new THREE.Mesh(cloudOrbGeo, cloudOrbMat); cloudOrb.position.set(-2.0, 3.0, 0); cloudOrb.userData.zoomLabel = 'Cloud Brain'; dualBrainGroup.add(cloudOrb); export const cloudOrbLight = new THREE.PointLight(CLOUD_ORB_COLOR, 0.15, 5); cloudOrbLight.position.copy(cloudOrb.position); dualBrainGroup.add(cloudOrbLight); const LOCAL_ORB_COLOR = 0x334466; const localOrbGeo = new THREE.SphereGeometry(0.35, 32, 32); export const localOrbMat = new THREE.MeshStandardMaterial({ color: LOCAL_ORB_COLOR, emissive: new THREE.Color(LOCAL_ORB_COLOR), emissiveIntensity: 0.1, metalness: 0.3, roughness: 0.2, transparent: true, opacity: 0.85, }); export const localOrb = new THREE.Mesh(localOrbGeo, localOrbMat); localOrb.position.set(2.0, 3.0, 0); localOrb.userData.zoomLabel = 'Local Brain'; dualBrainGroup.add(localOrb); export const localOrbLight = new THREE.PointLight(LOCAL_ORB_COLOR, 0.15, 5); localOrbLight.position.copy(localOrb.position); dualBrainGroup.add(localOrbLight); // Brain Pulse Particle Stream export const BRAIN_PARTICLE_COUNT = 0; const brainParticlePositions = new Float32Array(BRAIN_PARTICLE_COUNT * 3); export const brainParticlePhases = new Float32Array(BRAIN_PARTICLE_COUNT); export const brainParticleSpeeds = new Float32Array(BRAIN_PARTICLE_COUNT); for (let i = 0; i < BRAIN_PARTICLE_COUNT; i++) { brainParticlePhases[i] = Math.random(); brainParticleSpeeds[i] = 0.15 + Math.random() * 0.2; brainParticlePositions[i * 3] = 0; brainParticlePositions[i * 3 + 1] = 0; brainParticlePositions[i * 3 + 2] = 0; } export const brainParticleGeo = new THREE.BufferGeometry(); brainParticleGeo.setAttribute('position', new THREE.BufferAttribute(brainParticlePositions, 3)); export const brainParticleMat = new THREE.PointsMaterial({ color: 0x44ddff, size: 0.08, sizeAttenuation: true, transparent: true, opacity: 0.8, depthWrite: false, }); const brainParticles = new THREE.Points(brainParticleGeo, brainParticleMat); dualBrainGroup.add(brainParticles); // Scanning line overlay const _scanCanvas = document.createElement('canvas'); _scanCanvas.width = 512; _scanCanvas.height = 512; export const _scanCtx = _scanCanvas.getContext('2d'); export const dualBrainScanTexture = new THREE.CanvasTexture(_scanCanvas); const dualBrainScanMat = new THREE.SpriteMaterial({ map: dualBrainScanTexture, transparent: true, opacity: 0.18, depthWrite: false, }); export const dualBrainScanSprite = new THREE.Sprite(dualBrainScanMat); dualBrainScanSprite.scale.set(5.0, 5.0, 1); dualBrainScanSprite.position.set(0, 0, 0.01); dualBrainGroup.add(dualBrainScanSprite);