import React, { useRef, useMemo, useEffect } from 'react'; import { Canvas, useFrame } from '@react-three/fiber'; import { OrbitControls, PerspectiveCamera } from '@react-three/drei'; import * as THREE from 'three'; // Add global declaration to fix TypeScript errors with React Three Fiber elements declare module 'react' { namespace JSX { interface IntrinsicElements { points: any; bufferGeometry: any; bufferAttribute: any; pointsMaterial: any; ambientLight: any; } } } declare global { namespace JSX { interface IntrinsicElements { points: any; bufferGeometry: any; bufferAttribute: any; pointsMaterial: any; ambientLight: any; } } } interface TechVisualizerProps { mode: 'server' | 'monitor' | 'hidden'; } const ParticleMorph = ({ mode }: { mode: 'server' | 'monitor' | 'hidden' }) => { const meshRef = useRef(null); const COUNT = 3000; // Define geometries const data = useMemo(() => { const pos = new Float32Array(COUNT * 3); const col = new Float32Array(COUNT * 3); const serverPos = new Float32Array(COUNT * 3); const monitorPos = new Float32Array(COUNT * 3); // SERVER SHAPE (Rectangular Tower) for (let i = 0; i < COUNT; i++) { const x = (Math.random() - 0.5) * 2; // Thin width const y = (Math.random() - 0.5) * 6; // Tall const z = (Math.random() - 0.5) * 2; // Depth serverPos[i * 3] = x; serverPos[i * 3 + 1] = y; serverPos[i * 3 + 2] = z; // Add some "layers" to make it look like a rack if (Math.random() > 0.8) { serverPos[i * 3] *= 1.2; serverPos[i * 3 + 2] *= 1.2; } } // MONITOR SHAPE (Curved Plane) for (let i = 0; i < COUNT; i++) { const x = (Math.random() - 0.5) * 8; // Wide const y = (Math.random() - 0.5) * 3.5; // Aspect ratio // Curve the Z based on X (parabolic) const z = Math.pow(x * 0.3, 2) - 2; monitorPos[i * 3] = x; monitorPos[i * 3 + 1] = y + 0.5; // Lift up slightly monitorPos[i * 3 + 2] = z; // Bezel/Frame particles if (Math.random() > 0.95) { monitorPos[i * 3 + 2] += 0.1; } } // Initial Positions (start at server) for (let i = 0; i < COUNT * 3; i++) { pos[i] = serverPos[i]; col[i] = 1; // Initial white/cyan mix logic in shader or simple color } return { positions: pos, colors: col, serverPos, monitorPos }; }, []); // Buffer attributes const bufferRef = useRef(null); useFrame((state, delta) => { if (!meshRef.current || !bufferRef.current) return; const target = mode === 'monitor' ? data.monitorPos : data.serverPos; const current = bufferRef.current.array as Float32Array; // Morph speed const speed = 4 * delta; // Visibility transition const isHidden = mode === 'hidden'; for (let i = 0; i < COUNT; i++) { const ix = i * 3; const iy = i * 3 + 1; const iz = i * 3 + 2; if (isHidden) { // Explode/Hide current[ix] = THREE.MathUtils.lerp(current[ix], current[ix] * 1.01, speed); current[iy] = THREE.MathUtils.lerp(current[iy], current[iy] + 10, speed); } else { // Standard Morph current[ix] = THREE.MathUtils.lerp(current[ix], target[ix], speed); current[iy] = THREE.MathUtils.lerp(current[iy], target[iy], speed); current[iz] = THREE.MathUtils.lerp(current[iz], target[iz], speed); // Add subtle noise/floating current[iy] += Math.sin(state.clock.elapsedTime + current[ix]) * 0.002; } } bufferRef.current.needsUpdate = true; // Rotate entire mesh slowly meshRef.current.rotation.y += delta * 0.1; }); return ( ); }; const TechVisualizer: React.FC = ({ mode }) => { return (
); }; export default TechVisualizer;