"use client"; import { useMemo, useRef, useState } from "react"; import { Canvas, useFrame, useThree } from "@react-three/fiber"; import { MeshDistortMaterial, Sphere, Preload, Float } from "@react-three/drei"; import * as THREE from "three"; // --- CONFIGURATION --- const NEURON_COUNT = 18; // Fewer, but more detailed neurons const CONNECTION_DISTANCE = 5.5; // Distance to form a synapse const PULSE_SPEED = 2.5; // Speed of the electric signal // 1. SINGLE NEURON CELL (The Soma) // We use a distorted sphere to make it look like organic tissue function NeuronCell({ position }: { position: [number, number, number] }) { const meshRef = useRef(null!); const [hovered, setHover] = useState(false); // Randomize size slightly for variety const scale = useMemo(() => 0.6 + Math.random() * 0.4, []); useFrame((state) => { // Gentle heartbeat pulsation const t = state.clock.getElapsedTime(); const pulse = Math.sin(t * 3) * 0.05 + 1; if (meshRef.current) { meshRef.current.scale.set(scale * pulse, scale * pulse, scale * pulse); } }); return ( setHover(true)} onPointerOut={() => setHover(false)} > ); } // 2. SYNAPSE (The Connection & The Impulse) // Draws a curved organic line and a traveling light pulse function Synapse({ start, end }: { start: THREE.Vector3; end: THREE.Vector3 }) { const curve = useMemo(() => { // Create a quadratic bezier curve for a natural "tendon" look // Control point is midway but offset randomly to create the curve const mid = new THREE.Vector3().lerpVectors(start, end, 0.5); mid.x += (Math.random() - 0.5) * 2; mid.y += (Math.random() - 0.5) * 2; mid.z += (Math.random() - 0.5) * 2; return new THREE.QuadraticBezierCurve3(start, mid, end); }, [start, end]); // Create points for the line geometry const points = useMemo(() => curve.getPoints(20), [curve]); // The traveling impulse ref const impulseRef = useRef(null!); useFrame((state) => { // Move the impulse along the curve const t = (state.clock.getElapsedTime() * PULSE_SPEED) % 1; // 0 to 1 loop if (impulseRef.current) { const pos = curve.getPointAt(t); impulseRef.current.position.copy(pos); // Scale impulse based on position (fade in/out at ends) const scale = Math.sin(t * Math.PI); impulseRef.current.scale.setScalar(scale * 0.15); } }); return ( {/* The Axon (Line) */} [p.x, p.y, p.z]))} itemSize={3} /> {/* The Electric Impulse (Glowing Dot) */} ); } // 3. MAIN SCENE CONTROLLER function NeuralNetworkScene() { const { viewport } = useThree(); const mouse = useRef(new THREE.Vector2()); // Generate Neurons const neurons = useMemo(() => { const temp = []; for (let i = 0; i < NEURON_COUNT; i++) { temp.push({ position: new THREE.Vector3( (Math.random() - 0.5) * 20, (Math.random() - 0.5) * 20, (Math.random() - 0.5) * 10 ), id: i, }); } return temp; }, []); // Generate Connections (Proximity based) const connections = useMemo(() => { const conns = []; for (let i = 0; i < neurons.length; i++) { for (let j = i + 1; j < neurons.length; j++) { const dist = neurons[i].position.distanceTo(neurons[j].position); if (dist < CONNECTION_DISTANCE) { conns.push({ start: neurons[i].position, end: neurons[j].position, key: `${i}-${j}` }); } } } return conns; }, [neurons]); useFrame((state) => { // Soft camera movement based on mouse const x = (state.pointer.x * viewport.width) / 10; const y = (state.pointer.y * viewport.height) / 10; state.camera.position.x = THREE.MathUtils.lerp(state.camera.position.x, x, 0.05); state.camera.position.y = THREE.MathUtils.lerp(state.camera.position.y, y, 0.05); state.camera.lookAt(0, 0, 0); }); return ( {/* Draw Neurons */} {neurons.map((n) => ( ))} {/* Draw Synapses */} {connections.map((c) => ( ))} ); } export default function BioNeurons() { return (
); }