diff --git a/app/apps/page.tsx b/app/apps/page.tsx index a8c321b..1e31b7f 100644 --- a/app/apps/page.tsx +++ b/app/apps/page.tsx @@ -3,111 +3,234 @@ import Navbar from "../components/layout/Navbar"; import DNAHeroBackground from "../components/canvas/DNAHeroBackground"; import Footer from "../components/layout/Footer"; -import { motion } from "framer-motion"; -import { Lock, Globe, MessageSquare, Bell, HeartPulse, ShieldCheck, Fingerprint, Zap } from "lucide-react"; +import { motion, useScroll, useTransform } from "framer-motion"; +import { + Lock, Globe, MessageSquare, Bell, HeartPulse, ShieldCheck, + Fingerprint, Zap, ArrowRight, LayoutGrid +} from "lucide-react"; +import { useRef } from "react"; + +// Animation Variants +const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.15, + delayChildren: 0.2 + } + } +}; + +const itemVariants = { + hidden: { opacity: 0, y: 30 }, + visible: { + opacity: 1, + y: 0, + transition: { + duration: 0.8, + ease: [0.2, 0.65, 0.3, 0.9] as const + } + } +} satisfies import("framer-motion").Variants; export default function AppsPage() { - return ( -
- - + const targetRef = useRef(null); + const { scrollYProgress } = useScroll({ + target: targetRef, + offset: ["start end", "end start"] + }); + const y = useTransform(scrollYProgress, [0, 1], [100, -100]); -
-
-
- -
- - Neural Ecosystem v4.0 -
-

- Unified
Access -

-

- Institutional clinical command center. Real-time bio-sync and clinical assistance designed for absolute operational security. -

+ return ( +
+ {/* Background Layer */} +
+ +
+
+
-
- - -
- + - -
-
-
-
-
- -
-
-
+
+
+ + {/* --- HERO SECTION --- */} +
+ + {/* Left: Text Content */} + + + + Neural Ecosystem v4.0 + -
-

Live Clinical Sync

-
- - - -
-
-
-
-
-
-
-
-
+ + Unified
+ + Access + +
+ + + The institutional clinical command center. Real-time bio-sync and clinical assistance designed for absolute operational security. + + + + + + + + + {/* Right: Phone Mockup Animation */} + + {/* Decorative Background Blob */} +
+ + + {/* Glass Phone Frame */} +
+ + {/* Notch */} +
+
+
+ + {/* Screen Content */} +
+ + {/* Status Header */} +
+ +
-
- {[ - { icon: Lock, title: "Zero-Trust Protocol", desc: "Institutional-grade multi-layer security for clinical data transfers." }, - { icon: MessageSquare, title: "Clinical AI Bridge", desc: "Real-time verification and documentation via medical LLM layers." }, - { icon: Bell, title: "Predictive SOS Audit", desc: "Automated institutional alerts based on verified vital trend analysis." }, - { icon: Globe, title: "Universal Sync Hub", desc: "Global access to clinical data on certified HIPAA infrastructure." }, - { icon: Fingerprint, title: "Biometric Audit", desc: "Hardware-locked PHI access ensures zero-bias clinical security." }, - { icon: ShieldCheck, title: "Institutional Verified", desc: "Certified infrastructure for sovereign clinical data integrity." } - ].map((feature, i) => ( - -
-
- -
-

{feature.title}

-

{feature.desc}

- + {/* Dynamic Widget */} +
+
+
+ +
+

Bio-Sync Active

+

ENCRYPTED STREAM • 42ms

+
+ + {/* Dummy Chart Lines */} +
+ {[40, 70, 45, 90, 60, 80, 50, 75, 60, 95].map((h, i) => ( + ))} +
-
-
-
+ {/* Bottom Actions */} +
+ {[Bell, Globe, ShieldCheck].map((Icon, i) => ( + + ))} +
+
+ + {/* Reflection Gradient */} +
+
+ + {/* Shadow / Depth Layer */} +
+ + +
+ + {/* --- FEATURE GRID --- */} +
+ {/* Section Header */} +
+

System Architecture

+

Institutional-grade protocols built for the next generation of healthcare.

+
+ + + {[ + { icon: Lock, title: "Zero-Trust Protocol", desc: "Institutional-grade multi-layer security for clinical data transfers." }, + { icon: MessageSquare, title: "Clinical AI Bridge", desc: "Real-time verification and documentation via medical LLM layers." }, + { icon: Bell, title: "Predictive SOS Audit", desc: "Automated institutional alerts based on verified vital trend analysis." }, + { icon: Globe, title: "Universal Sync Hub", desc: "Global access to clinical data on certified HIPAA infrastructure." }, + { icon: Fingerprint, title: "Biometric Audit", desc: "Hardware-locked PHI access ensures zero-bias clinical security." }, + { icon: ShieldCheck, title: "Institutional Verified", desc: "Certified infrastructure for sovereign clinical data integrity." } + ].map((feature, i) => ( + + {/* Hover Glow */} +
+ +
+
+ +
+ +
+

{feature.title}

+

+ {feature.desc} +

+
+
+ + ))} + +
- ); -} +
+ +
+ ); +} \ No newline at end of file diff --git a/app/components/canvas/BioNeurons.tsx b/app/components/canvas/BioNeurons.tsx new file mode 100644 index 0000000..8592943 --- /dev/null +++ b/app/components/canvas/BioNeurons.tsx @@ -0,0 +1,192 @@ +"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 ( +
+ + + + + + +
+ ); +} \ No newline at end of file diff --git a/app/components/canvas/DNAHelix.tsx b/app/components/canvas/DNAHelix.tsx new file mode 100644 index 0000000..adf812b --- /dev/null +++ b/app/components/canvas/DNAHelix.tsx @@ -0,0 +1,110 @@ +"use client"; + +import React, { useEffect, useRef } from "react"; + +interface Props { + className?: string; +} + +const DNAHelix: React.FC = ({ className }) => { + const canvasRef = useRef(null); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + + const ctx = canvas.getContext("2d"); + if (!ctx) return; + + // Dimensions based on parent container + let width = canvas.width = canvas.parentElement?.clientWidth || window.innerWidth; + let height = canvas.height = canvas.parentElement?.clientHeight || window.innerHeight; + + let animationFrameId: number; + let time = 0; + + const handleResize = () => { + if (canvas.parentElement) { + width = canvas.width = canvas.parentElement.clientWidth; + height = canvas.height = canvas.parentElement.clientHeight; + } + }; + + window.addEventListener("resize", handleResize); + + const render = () => { + ctx.clearRect(0, 0, width, height); + time += 0.02; // Rotation speed + + const strandGap = 40; // Vertical distance between "base pairs" + const amplitude = 50; // Width of the helix + const frequency = 0.015; // Tightness of the loops + + // Position helix on the right side (85% of width) to keep content clear + const centerX = width * 0.85; + + ctx.lineWidth = 2; + + // Calculate number of points based on container height + const numPoints = Math.ceil(height / strandGap) + 5; + + for (let i = 0; i < numPoints; i++) { + // Scroll the helix slowly downward + const y = i * strandGap - (time * 10) % strandGap; + + // Calculate rotation phase + const phase = y * frequency + time; + + // Strand 1 (Cyan) + const x1 = centerX + Math.sin(phase) * amplitude; + // Strand 2 (Emerald) - Offset by PI + const x2 = centerX + Math.sin(phase + Math.PI) * amplitude; + + // Depth calculation for 3D effect (fade when "back") + const depth1 = Math.cos(phase); + const depth2 = Math.cos(phase + Math.PI); + + // Map depth (-1 to 1) to opacity (0.2 to 0.8) + const alpha1 = (depth1 + 1) / 2 * 0.6 + 0.2; + const alpha2 = (depth2 + 1) / 2 * 0.6 + 0.2; + + // Draw Connector Line (Base Pair) + ctx.beginPath(); + ctx.moveTo(x1, y); + ctx.lineTo(x2, y); + ctx.strokeStyle = `rgba(34, 211, 238, 0.05)`; // Very faint connector + ctx.stroke(); + + // Draw Strand 1 Particle + ctx.beginPath(); + ctx.arc(x1, y, 4 + depth1, 0, Math.PI * 2); + ctx.fillStyle = `rgba(34, 211, 238, ${alpha1})`; // Cyan + ctx.fill(); + + // Draw Strand 2 Particle + ctx.beginPath(); + ctx.arc(x2, y, 4 + depth2, 0, Math.PI * 2); + ctx.fillStyle = `rgba(52, 211, 153, ${alpha2})`; // Emerald + ctx.fill(); + } + + animationFrameId = requestAnimationFrame(render); + }; + + render(); + + return () => { + window.removeEventListener("resize", handleResize); + cancelAnimationFrame(animationFrameId); + }; + }, []); + + return ( + + ); +}; + +export default DNAHelix; \ No newline at end of file diff --git a/app/components/canvas/GravityWave.tsx b/app/components/canvas/GravityWave.tsx new file mode 100644 index 0000000..7e2c9e7 --- /dev/null +++ b/app/components/canvas/GravityWave.tsx @@ -0,0 +1,123 @@ +"use client"; + +import React, { useEffect, useRef } from "react"; + +interface Props { + className?: string; +} + +const GravityWave: React.FC = ({ className }) => { + const canvasRef = useRef(null); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + + const ctx = canvas.getContext("2d"); + if (!ctx) return; + + // Initialize dimensions based on parent container + let width = canvas.width = canvas.parentElement?.clientWidth || window.innerWidth; + let height = canvas.height = canvas.parentElement?.clientHeight || window.innerHeight; + + let animationFrameId: number; + let time = 0; + + // Configuration + const gap = 50; + const rows = Math.ceil(height / gap) + 4; + const cols = Math.ceil(width / gap) + 4; + + // Mouse state + const mouse = { x: -500, y: -500 }; + + const handleResize = () => { + // Resize based on parent element + if (canvas.parentElement) { + width = canvas.width = canvas.parentElement.clientWidth; + height = canvas.height = canvas.parentElement.clientHeight; + } + }; + + const handleMouseMove = (e: MouseEvent) => { + const rect = canvas.getBoundingClientRect(); + mouse.x = e.clientX - rect.left; + mouse.y = e.clientY - rect.top; + }; + + window.addEventListener("resize", handleResize); + // Attach mouse move to the specific canvas/container, not window, if you want localized interaction + // But keeping it on window usually feels smoother for "approaching" the section + window.addEventListener("mousemove", handleMouseMove); + + const render = () => { + ctx.clearRect(0, 0, width, height); + time += 0.02; + + ctx.shadowBlur = 4; + ctx.shadowColor = "rgba(34, 211, 238, 0.5)"; + + const gradient = ctx.createLinearGradient(0, 0, width, height); + gradient.addColorStop(0, "rgba(34, 211, 238, 0.4)"); + gradient.addColorStop(1, "rgba(52, 211, 153, 0.4)"); + + ctx.strokeStyle = gradient; + ctx.lineWidth = 1.5; + + // Re-calculate rows/cols in case of resize + const currentRows = Math.ceil(height / gap) + 4; + const currentCols = Math.ceil(width / gap) + 4; + + for (let iy = 0; iy < currentRows; iy++) { + const yBase = iy * gap; + ctx.beginPath(); + for (let ix = 0; ix < currentCols; ix++) { + const xBase = ix * gap; + + const waveOffset = Math.sin(ix * 0.2 + time) * 8 + Math.cos(iy * 0.3 + time) * 8; + const dx = xBase - mouse.x; + const dy = yBase - mouse.y; + const dist = Math.sqrt(dx * dx + dy * dy); + + const maxDist = 400; + let gravityX = 0; + let gravityY = 0; + + if (dist < maxDist) { + const force = (maxDist - dist) / maxDist; + const power = force * 60; + const angle = Math.atan2(dy, dx); + gravityX = Math.cos(angle) * power; + gravityY = Math.sin(angle) * power; + } + + const xFinal = xBase + gravityX; + const yFinal = yBase + waveOffset + gravityY; + + if (ix === 0) ctx.moveTo(xFinal, yFinal); + else ctx.lineTo(xFinal, yFinal); + } + ctx.stroke(); + } + animationFrameId = requestAnimationFrame(render); + }; + + render(); + + return () => { + window.removeEventListener("resize", handleResize); + window.removeEventListener("mousemove", handleMouseMove); + cancelAnimationFrame(animationFrameId); + }; + }, []); + + return ( + + ); +}; + +export default GravityWave; \ No newline at end of file diff --git a/app/components/canvas/NeuralNetwork.tsx b/app/components/canvas/NeuralNetwork.tsx new file mode 100644 index 0000000..b12ee82 --- /dev/null +++ b/app/components/canvas/NeuralNetwork.tsx @@ -0,0 +1,65 @@ +"use client"; + +import { useRef, useMemo } from "react"; +import { Canvas, useFrame } from "@react-three/fiber"; +import { Points, PointMaterial, Preload } from "@react-three/drei"; +import * as THREE from "three"; + +function Particles(props: any) { + const ref = useRef(null!); + + // Generate 5000 random points inside a sphere + const sphere = useMemo(() => { + const coords = new Float32Array(5000 * 3); + for (let i = 0; i < 5000; i++) { + const r = 1.2 * Math.cbrt(Math.random()); // Radius + const theta = Math.random() * 2 * Math.PI; // Theta + const phi = Math.acos(2 * Math.random() - 1); // Phi + + const x = r * Math.sin(phi) * Math.cos(theta); + const y = r * Math.sin(phi) * Math.sin(theta); + const z = r * Math.cos(phi); + + coords[i * 3] = x; + coords[i * 3 + 1] = y; + coords[i * 3 + 2] = z; + } + return coords; + }, []); + + useFrame((state, delta) => { + if (ref.current) { + // Rotate the entire cloud slowly + ref.current.rotation.x -= delta / 10; + ref.current.rotation.y -= delta / 15; + } + }); + + return ( + + + + + + ); +} + +const NeuralNetwork = () => { + return ( +
+ + + + +
+ ); +}; + +export default NeuralNetwork; \ No newline at end of file diff --git a/app/components/canvas/NeuronNetwork.tsx b/app/components/canvas/NeuronNetwork.tsx new file mode 100644 index 0000000..cc50741 --- /dev/null +++ b/app/components/canvas/NeuronNetwork.tsx @@ -0,0 +1,81 @@ +// src/components/canvas/NeuronNetwork.tsx +"use client"; + +import { useRef, useMemo } from "react"; +import { Canvas, useFrame } from "@react-three/fiber"; +import { Points, PointMaterial } from "@react-three/drei"; +import * as THREE from "three"; + +function NeuronParticles({ count = 200 }) { + // Create a ref for the points to animate them + const points = useRef(null!); + + // Generate random positions for the neurons + const positions = useMemo(() => { + const pos = new Float32Array(count * 3); + for (let i = 0; i < count; i++) { + // Spread particles across a wide area (x, y, z) + pos[i * 3] = (Math.random() - 0.5) * 25; // x + pos[i * 3 + 1] = (Math.random() - 0.5) * 25; // y + pos[i * 3 + 2] = (Math.random() - 0.5) * 10; // z + } + return pos; + }, [count]); + + useFrame((state, delta) => { + if (points.current) { + // subtle rotation of the entire system + points.current.rotation.x -= delta / 50; + points.current.rotation.y -= delta / 60; + + // Optional: Gentle wave motion could be added here + } + }); + + return ( + + + + + {/* For true "connections" (lines), calculating distance between 200 points + every frame is heavy. A visual trick is to render a second layer + of slightly larger, fainter particles or use a custom shader. + + However, for a clean "Neuron" look, we often just need the floating nodes + and a faint fog, or we can use the component from drei + if the count is low (<50). + */} + + ); +} + +// A wrapper to handle the Canvas settings +export default function NeuronNetwork() { + return ( +
+ + + + + {/* Fog creates depth, fading distant neurons */} + + +
+ ); +} \ No newline at end of file diff --git a/app/components/canvas/NeuronsGravity.tsx b/app/components/canvas/NeuronsGravity.tsx new file mode 100644 index 0000000..7f2efcc --- /dev/null +++ b/app/components/canvas/NeuronsGravity.tsx @@ -0,0 +1,183 @@ +"use client"; + +import { useMemo, useRef } from "react"; +import { Canvas, useFrame, useThree } from "@react-three/fiber"; +import * as THREE from "three"; +import { Preload } from "@react-three/drei"; + +// CONFIGURATION +const PARTICLE_COUNT = 80; +const CONNECT_DISTANCE = 3.5; +const MOUSE_INFLUENCE_RADIUS = 6; // Radius of the mouse wave +const WAVE_AMPLITUDE = 1.5; // How high the wave ripples (Z-axis) + +function NeuralMesh() { + const groupRef = useRef(null); + const particlesRef = useRef(null); + const linesRef = useRef(null); + + // Get viewport to map mouse coordinates correctly to world space + const { viewport } = useThree(); + + // 1. Generate Initial Random Positions & Velocities + // We store "original positions" to calculate the wave offset from + const [positions, velocities, originalPositions] = useMemo(() => { + const pos = new Float32Array(PARTICLE_COUNT * 3); + const origPos = new Float32Array(PARTICLE_COUNT * 3); + const vel = []; + + for (let i = 0; i < PARTICLE_COUNT; i++) { + const x = (Math.random() - 0.5) * 18; + const y = (Math.random() - 0.5) * 18; + const z = (Math.random() - 0.5) * 10; + + pos[i * 3] = x; + pos[i * 3 + 1] = y; + pos[i * 3 + 2] = z; + + origPos[i * 3] = x; + origPos[i * 3 + 1] = y; + origPos[i * 3 + 2] = z; + + vel.push({ + x: (Math.random() - 0.5) * 0.05, // Slower natural drift + y: (Math.random() - 0.5) * 0.05, + z: (Math.random() - 0.5) * 0.05, + }); + } + return [pos, vel, origPos]; + }, []); + + useFrame((state, delta) => { + // 2. Map Mouse to World Coordinates + // state.pointer is normalized (-1 to 1). Convert to world units using viewport. + const mouseX = (state.pointer.x * viewport.width) / 2; + const mouseY = (state.pointer.y * viewport.height) / 2; + + if (particlesRef.current && linesRef.current) { + const positionsAttr = particlesRef.current.geometry.attributes.position; + const currentPositions = positionsAttr.array as Float32Array; + + // 3. Update Particles + for (let i = 0; i < PARTICLE_COUNT; i++) { + const i3 = i * 3; + + // A. Natural Drift (Gravity) + // We use the "original" position as an anchor to prevent them from flying away too far + // but we add the velocity to keep them moving organically. + originalPositions[i3] += velocities[i].x * delta * 2; + originalPositions[i3 + 1] += velocities[i].y * delta * 2; + + // Bounce logic for the anchor points + if (Math.abs(originalPositions[i3]) > 10) velocities[i].x *= -1; + if (Math.abs(originalPositions[i3 + 1]) > 10) velocities[i].y *= -1; + + // B. Mouse Interaction (The Wave) + // Calculate distance from particle to mouse + const dx = mouseX - originalPositions[i3]; + const dy = mouseY - originalPositions[i3 + 1]; + const dist = Math.sqrt(dx * dx + dy * dy); + + // Apply Wave Effect + // If the mouse is close, we disturb the position + let xOffset = 0; + let yOffset = 0; + let zOffset = 0; + + if (dist < MOUSE_INFLUENCE_RADIUS) { + // 1. Repulsion force (XY Plane) - pushes them slightly aside + const force = (MOUSE_INFLUENCE_RADIUS - dist) / MOUSE_INFLUENCE_RADIUS; + const angle = Math.atan2(dy, dx); + + xOffset = -Math.cos(angle) * force * 2; // Push away X + yOffset = -Math.sin(angle) * force * 2; // Push away Y + + // 2. Wave Ripple (Z Plane) - Sine wave based on distance and time + // This creates the "water ripple" effect in 3D depth + zOffset = Math.sin(dist * 1.5 - state.clock.elapsedTime * 3) * WAVE_AMPLITUDE * force; + } + + // Apply calculated positions + currentPositions[i3] = originalPositions[i3] + xOffset; + currentPositions[i3 + 1] = originalPositions[i3 + 1] + yOffset; + currentPositions[i3 + 2] = originalPositions[i3 + 2] + zOffset; + } + positionsAttr.needsUpdate = true; + + // 4. Update Connections (Plexus) + // Re-calculate lines based on the NEW modified positions + const linePositions: number[] = []; + + for (let i = 0; i < PARTICLE_COUNT; i++) { + for (let j = i + 1; j < PARTICLE_COUNT; j++) { + const x1 = currentPositions[i * 3]; + const y1 = currentPositions[i * 3 + 1]; + const z1 = currentPositions[i * 3 + 2]; + + const x2 = currentPositions[j * 3]; + const y2 = currentPositions[j * 3 + 1]; + const z2 = currentPositions[j * 3 + 2]; + + const dist = Math.sqrt( + Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2) + Math.pow(z2 - z1, 2) + ); + + if (dist < CONNECT_DISTANCE) { + linePositions.push(x1, y1, z1, x2, y2, z2); + } + } + } + + linesRef.current.geometry.setAttribute( + "position", + new THREE.Float32BufferAttribute(linePositions, 3) + ); + } + }); + + return ( + + + + + + + + + + + + + + ); +} + +export default function NeuronsGravity() { + return ( +
+ + + + +
+ ); +} \ No newline at end of file diff --git a/app/components/canvas/RealisticNeeurons.tsx b/app/components/canvas/RealisticNeeurons.tsx new file mode 100644 index 0000000..30717d8 --- /dev/null +++ b/app/components/canvas/RealisticNeeurons.tsx @@ -0,0 +1,259 @@ +"use client"; + +import { useMemo, useRef } from "react"; +import { Canvas, useFrame, useThree } from "@react-three/fiber"; +import { MeshDistortMaterial, Sphere, Preload, Float } from "@react-three/drei"; +import { EffectComposer, Bloom, Vignette } from "@react-three/postprocessing"; +import * as THREE from "three"; + +// --- CONFIGURATION --- +const NEURON_COUNT = 15; // Keep low for high-quality geometry +const CONNECTION_DISTANCE = 6.5; +const PULSE_SPEED = 1.8; + +// 1. REALISTIC CELL (Soma + Nucleus) +function NeuronCell({ position }: { position: [number, number, number] }) { + const membraneRef = useRef(null!); + const nucleusRef = useRef(null!); + + // Randomize biology + const size = useMemo(() => 0.5 + Math.random() * 0.3, []); + + useFrame((state) => { + const t = state.clock.getElapsedTime(); + + // Biological breathing/pulsing + if (membraneRef.current) { + const scale = size + Math.sin(t * 2 + position[0]) * 0.05; + membraneRef.current.scale.setScalar(scale); + } + + // Nucleus gentle pulse (faster) + if (nucleusRef.current) { + const nScale = (size * 0.4) + Math.sin(t * 4) * 0.02; + nucleusRef.current.scale.setScalar(nScale); + } + }); + + return ( + + + + {/* A. The Membrane (Outer Shell) */} + + + + + {/* B. The Nucleus (Inner Core) */} + + + + + + + + ); +} + +// 2. SYNAPSE (Axon + Electric Pulse) +function Synapse({ start, end }: { start: THREE.Vector3; end: THREE.Vector3 }) { + const curve = useMemo(() => { + // Create organic curve + const mid = new THREE.Vector3().lerpVectors(start, end, 0.5); + // Randomize control point for "tendon" shape + mid.add(new THREE.Vector3( + (Math.random() - 0.5) * 3, + (Math.random() - 0.5) * 3, + (Math.random() - 0.5) * 3 + )); + return new THREE.QuadraticBezierCurve3(start, mid, end); + }, [start, end]); + + const points = useMemo(() => curve.getPoints(30), [curve]); + const impulseRef = useRef(null!); + + useFrame((state) => { + // Move electric pulse + const t = (state.clock.getElapsedTime() * PULSE_SPEED) % 1; + if (impulseRef.current) { + const pos = curve.getPointAt(t); + impulseRef.current.position.copy(pos); + + // Stretch effect for speed illusion + const tangent = curve.getTangent(t).normalize(); + impulseRef.current.lookAt(pos.clone().add(tangent)); + impulseRef.current.scale.set(0.6, 0.6, 2.5); // Stretch Z + } + }); + + return ( + + {/* The physical connection (Axon) */} + + + [p.x, p.y, p.z]))} + itemSize={3} + /> + + + + + {/* The Traveling Spark (Electricity) */} + + + + + + + ); +} + +// 3. BACKGROUND PARTICLES (Neurotransmitters) +function NeuroDust() { + const count = 200; + const mesh = useRef(null!); + + const particles = useMemo(() => { + const temp = []; + for(let i=0; i new THREE.Object3D(), []); + + useFrame((state) => { + particles.forEach((particle, i) => { + let { t, factor, speed, x, y, z } = particle; + t = particle.t += speed / 2; + const a = Math.cos(t) + Math.sin(t * 1) / 10; + const b = Math.sin(t) + Math.cos(t * 2) / 10; + const s = Math.cos(t); + + dummy.position.set( + x + Math.cos(t) + Math.sin(t) * 2, + y + Math.sin(t) + Math.cos(t) * 2, + z + Math.cos(t) + ); + dummy.scale.setScalar(s * 0.03); // Tiny dust + dummy.rotation.set(s * 5, s * 5, s * 5); + dummy.updateMatrix(); + + if (mesh.current) mesh.current.setMatrixAt(i, dummy.matrix); + }); + if (mesh.current) mesh.current.instanceMatrix.needsUpdate = true; + }); + + return ( + + + + + ); +} + +// 4. MAIN SCENE +function Scene() { + const { viewport } = useThree(); + + // Generate random positions for neurons + const neurons = useMemo(() => { + const temp = []; + for (let i = 0; i < NEURON_COUNT; i++) { + temp.push({ + position: new THREE.Vector3( + (Math.random() - 0.5) * 22, + (Math.random() - 0.5) * 22, + (Math.random() - 0.5) * 10 + ), + id: i + }); + } + return temp; + }, []); + + // Connect them + const connections = useMemo(() => { + const conns = []; + for(let i=0; i { + // Subtle Camera Parallax based on Mouse + const x = (state.pointer.x * viewport.width) / 12; + const y = (state.pointer.y * viewport.height) / 12; + state.camera.position.x = THREE.MathUtils.lerp(state.camera.position.x, x, 0.02); + state.camera.position.y = THREE.MathUtils.lerp(state.camera.position.y, y, 0.02); + state.camera.lookAt(0,0,0); + }); + + return ( + <> + {/* Match gray-950 */} + + + + {neurons.map(n => )} + {connections.map(c => )} + + + {/* POST PROCESSING - The Secret Sauce for Realism */} + + + + + + ); +} + +export default function RealisticNeurons() { + return ( +
+ + + + +
+ ); +} \ No newline at end of file diff --git a/app/components/layout/Footer.tsx b/app/components/layout/Footer.tsx index 8c35a2c..b4af7a3 100644 --- a/app/components/layout/Footer.tsx +++ b/app/components/layout/Footer.tsx @@ -5,7 +5,7 @@ import { motion } from "framer-motion"; export default function Footer() { return ( -