'use client'; /** * Data Stream Rings * Creates orbital rings around the brain with data particles flowing along them * Part of the Jarvis holographic aesthetic - particles flow around ring circumferences * Uses BufferGeometry with Float32Array for GPU efficiency */ import { useMemo, useRef } from 'react'; import { useFrame } from '@react-three/fiber'; import * as THREE from 'three'; interface RingConfig { radius: number; tiltX: number; tiltZ: number; particleCount: number; speed: number; colorIndex: number; // 0 = gold, 1 = warm gold, 2 = deep orange } // Ring configurations with different radii and tilts for visual variety const RING_CONFIGS: RingConfig[] = [ { radius: 4.0, tiltX: 0.3, tiltZ: 0.1, particleCount: 16, speed: 0.8, colorIndex: 0 }, { radius: 4.5, tiltX: -0.2, tiltZ: 0.4, particleCount: 18, speed: 0.6, colorIndex: 1 }, { radius: 5.0, tiltX: 0.5, tiltZ: -0.2, particleCount: 20, speed: 0.7, colorIndex: 2 }, { radius: 5.5, tiltX: -0.4, tiltZ: -0.3, particleCount: 14, speed: 0.9, colorIndex: 0 }, { radius: 6.0, tiltX: 0.1, tiltZ: 0.5, particleCount: 12, speed: 0.5, colorIndex: 1 }, ]; // Golden color palette matching the Jarvis aesthetic const COLORS = [ { r: 1.0, g: 0.843, b: 0.0 }, // #FFD700 - Bright gold { r: 1.0, g: 0.702, b: 0.278 }, // #FFB347 - Warm gold { r: 1.0, g: 0.549, b: 0.0 }, // #FF8C00 - Deep orange ]; interface DataStreamRingProps { config: RingConfig; } /** * Single orbital ring with flowing particles */ function DataStreamRing({ config }: DataStreamRingProps) { const pointsRef = useRef(null); const groupRef = useRef(null); // Initialize particle positions and phases along the ring circumference const { positions, phases, colors } = useMemo(() => { const { radius, particleCount, colorIndex } = config; const positions = new Float32Array(particleCount * 3); const phases = new Float32Array(particleCount); const colors = new Float32Array(particleCount * 3); const baseColor = COLORS[colorIndex]; for (let i = 0; i < particleCount; i++) { // Distribute particles evenly around the ring with some randomness const baseAngle = (i / particleCount) * Math.PI * 2; const angleOffset = (Math.random() - 0.5) * 0.2; // Small random offset const angle = baseAngle + angleOffset; phases[i] = angle; // Position on ring circumference (XZ plane, will be tilted by group rotation) positions[i * 3] = Math.cos(angle) * radius; positions[i * 3 + 1] = (Math.random() - 0.5) * 0.1; // Slight Y variation positions[i * 3 + 2] = Math.sin(angle) * radius; // Color with slight variation for visual interest const colorVariation = 0.9 + Math.random() * 0.2; colors[i * 3] = baseColor.r * colorVariation; colors[i * 3 + 1] = baseColor.g * colorVariation; colors[i * 3 + 2] = baseColor.b * colorVariation; } return { positions, phases, colors }; }, [config]); // Animate particles flowing around the ring useFrame((state, delta) => { if (!pointsRef.current) return; const { radius, particleCount, speed } = config; const positionAttr = pointsRef.current.geometry.attributes .position as THREE.BufferAttribute; const positions = positionAttr.array as Float32Array; const time = state.clock.elapsedTime; for (let i = 0; i < particleCount; i++) { // Update phase (angle) to create flow around the ring phases[i] += delta * speed; // Wrap angle if (phases[i] > Math.PI * 2) { phases[i] -= Math.PI * 2; } const angle = phases[i]; // Add subtle wave motion to the radius for organic feel const radiusWave = radius + Math.sin(time * 2 + angle * 3) * 0.05; // Update position on ring circumference positions[i * 3] = Math.cos(angle) * radiusWave; positions[i * 3 + 1] = Math.sin(time * 1.5 + angle) * 0.08; // Gentle vertical oscillation positions[i * 3 + 2] = Math.sin(angle) * radiusWave; } positionAttr.needsUpdate = true; }); return ( ); } interface DataStreamRingsProps { visible?: boolean; } /** * All orbital data stream rings around the brain */ export function DataStreamRings({ visible = true }: DataStreamRingsProps) { if (!visible) return null; return ( {RING_CONFIGS.map((config, index) => ( ))} ); } export default DataStreamRings;