feat: enhance James Webb simulation with 100 trillion zoom and target focus

This commit is contained in:
Flatlogic Bot 2026-02-26 06:03:28 +00:00
parent f02a8b21d0
commit c8a5aca21b

View File

@ -3,18 +3,74 @@ import React, { useEffect, useRef, useState } from 'react';
import type { ReactElement } from 'react';
import Head from 'next/head';
import { useRouter } from 'next/router';
import { mdiClose, mdiCamera, mdiTarget, mdiInformationOutline, mdiTelescope } from '@mdi/js';
import {
mdiClose,
mdiCamera,
mdiTarget,
mdiInformationOutline,
mdiTelescope,
mdiMagnifyPlusOutline,
mdiMagnifyMinusOutline,
mdiOrbitVariant,
mdiFlare,
mdiWeatherNight,
mdiCrosshairsGps
} from '@mdi/js';
import BaseIcon from '../components/BaseIcon';
import { useAppDispatch, useAppSelector } from '../stores/hooks';
import { fetch as fetchSkyObjects } from '../stores/sky_objects/sky_objectsSlice';
import LayoutAuthenticated from '../layouts/Authenticated';
const PRESET_TARGETS = [
{
id: 'mars',
name: 'Mars',
type: 'Planet',
img: 'https://images-assets.nasa.gov/image/PIA04591/PIA04591~medium.jpg',
dist: '225M km',
temp: '210K'
},
{
id: 'jupiter',
name: 'Jupiter',
type: 'Planet',
img: 'https://images-assets.nasa.gov/image/PIA04866/PIA04866~medium.jpg',
dist: '778M km',
temp: '110K'
},
{
id: 'orion',
name: 'Orion Nebula',
type: 'Nebula',
img: 'https://images-assets.nasa.gov/image/PIA08653/PIA08653~medium.jpg',
dist: '1,344 ly',
temp: '10,000K'
},
{
id: 'andromeda',
name: 'Andromeda',
type: 'Galaxy',
img: 'https://images-assets.nasa.gov/image/PIA15416/PIA15416~medium.jpg',
dist: '2.5M ly',
temp: '2.7K'
},
{
id: 'pillars',
name: 'Pillars of Creation',
type: 'Nebula',
img: 'https://images-assets.nasa.gov/image/as11-40-5874/as11-40-5874~medium.jpg', // Using a generic high-res space image placeholder
dist: '6,500 ly',
temp: '15K'
}
];
const ObservationPage = () => {
const videoRef = useRef<HTMLVideoElement>(null);
const [isCameraActive, setIsCameraActive] = useState(false);
const [mode, setMode] = useState<'normal' | 'ir' | 'deep'>('normal');
const [scanning, setScanning] = useState(false);
const [identifiedObject, setIdentifiedObject] = useState<any>(null);
const [zoom, setZoom] = useState(1);
const [selectedTarget, setSelectedTarget] = useState<any>(null);
const [isFocusing, setIsFocusing] = useState(false);
const [telemetry, setTelemetry] = useState({
temp: -233,
dist: 1.5,
@ -22,8 +78,6 @@ const ObservationPage = () => {
});
const dispatch = useAppDispatch();
const { sky_objects } = useAppSelector((state) => state.sky_objects);
const router = useRouter();
useEffect(() => {
dispatch(fetchSkyObjects({}));
@ -56,18 +110,34 @@ const ObservationPage = () => {
return () => stopCamera();
}, []);
useEffect(() => {
if (scanning) {
const timeout = setTimeout(() => {
if (sky_objects.length > 0) {
const randomObj = sky_objects[Math.floor(Math.random() * sky_objects.length)];
setIdentifiedObject(randomObj);
}
setScanning(false);
}, 3000);
return () => clearTimeout(timeout);
}
}, [scanning, sky_objects]);
const handleZoom = (direction: 'in' | 'out') => {
setZoom(prev => {
if (direction === 'in') {
return Math.min(prev * 2.5, 100000000000000);
} else {
return Math.max(prev / 2.5, 1);
}
});
};
const selectTarget = (target: any) => {
setSelectedTarget(target);
setIsFocusing(true);
// Animate zoom in
let currentZoom = zoom;
const targetZoom = 1000000;
const interval = setInterval(() => {
currentZoom *= 1.5;
if (currentZoom >= targetZoom) {
setZoom(targetZoom);
setIsFocusing(false);
clearInterval(interval);
} else {
setZoom(currentZoom);
}
}, 100);
};
// Simulate telemetry fluctuations
useEffect(() => {
@ -89,45 +159,83 @@ const ObservationPage = () => {
}
};
const formatZoom = (z: number) => {
if (z >= 1000000000000) return `${(z / 1000000000000).toFixed(1)}T`;
if (z >= 1000000000) return `${(z / 1000000000).toFixed(1)}B`;
if (z >= 1000000) return `${(z / 1000000).toFixed(1)}M`;
if (z >= 1000) return `${(z / 1000).toFixed(1)}K`;
return `${z.toFixed(0)}x`;
};
const isDeepZoom = zoom > 5000;
return (
<div className="relative h-screen w-full bg-black overflow-hidden flex flex-col font-mono text-[#00F2FF]">
<Head>
<title>JWST | Live Observation</title>
<title>JWST | Deep Space Observation</title>
</Head>
{/* Camera View */}
<div className="absolute inset-0 z-0 bg-gray-900">
{/* Main Viewport */}
<div className="absolute inset-0 z-0 bg-gray-950 overflow-hidden">
{!isCameraActive && (
<div className="flex flex-col items-center justify-center h-full space-y-6">
<div className="w-24 h-24 border-2 border-[#E3B341] rounded-full flex items-center justify-center animate-pulse">
<BaseIcon path={mdiTelescope} size={48} className="text-[#E3B341]" />
<div className="flex flex-col items-center justify-center h-full space-y-6 z-10 relative">
<div className="w-32 h-32 border-2 border-[#E3B341] rounded-full flex items-center justify-center animate-pulse">
<BaseIcon path={mdiTelescope} size={64} className="text-[#E3B341]" />
</div>
<p className="text-[#E3B341] uppercase tracking-[0.3em] font-bold">Systems Offline</p>
<p className="text-[#E3B341] uppercase tracking-[0.4em] font-bold text-xl">Systems Offline</p>
<button
onClick={startCamera}
className="bg-[#E3B341] text-black px-8 py-3 font-bold uppercase tracking-widest hover:bg-white transition-colors"
className="bg-[#E3B341] text-black px-12 py-4 font-bold uppercase tracking-widest hover:bg-white transition-all transform hover:scale-105"
>
Initialize Deployment
</button>
</div>
)}
{/* Live Camera Feed */}
<video
ref={videoRef}
autoPlay
playsInline
muted
className={`w-full h-full object-cover transition-all duration-1000 ${isCameraActive ? 'opacity-100' : 'opacity-0'}`}
style={{ filter: getFilter() }}
className={`absolute inset-0 w-full h-full object-cover transition-all duration-1000 ${isCameraActive && (!isDeepZoom || !selectedTarget) ? 'opacity-100' : 'opacity-0'}`}
style={{
filter: getFilter(),
transform: `scale(${1 + Math.log10(zoom)})`,
transition: 'transform 0.5s ease-out, opacity 1.5s ease-in-out'
}}
/>
{/* Deep Space High-Res Layer */}
{selectedTarget && (
<div
className={`absolute inset-0 w-full h-full bg-cover bg-center transition-opacity duration-1000 ${isDeepZoom ? 'opacity-100' : 'opacity-0'}`}
style={{
backgroundImage: `url(${selectedTarget.img})`,
transform: `scale(${1 + (Math.log10(zoom) - 3) / 20})`,
filter: getFilter()
}}
/>
)}
{/* Loading Overlay when Focusing */}
{isFocusing && (
<div className="absolute inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm">
<div className="flex flex-col items-center space-y-4">
<div className="w-16 h-16 border-4 border-[#00F2FF] border-t-transparent rounded-full animate-spin"></div>
<div className="text-[#00F2FF] uppercase tracking-[0.3em] font-bold animate-pulse">Adjusting Focal Plane...</div>
</div>
</div>
)}
</div>
{/* JWST Hexagonal Overlay */}
{isCameraActive && (
<div className="absolute inset-0 z-10 pointer-events-none opacity-40">
<div className="absolute inset-0 z-10 pointer-events-none opacity-20">
<svg className="w-full h-full" viewBox="0 0 100 100" preserveAspectRatio="none">
<defs>
<pattern id="hexagons" width="10" height="10" patternUnits="userSpaceOnUse" patternTransform="scale(1.5)">
<path d="M5 0 L10 2.5 L10 7.5 L5 10 L0 7.5 L0 2.5 Z" fill="none" stroke="#E3B341" strokeWidth="0.1" />
<pattern id="hexagons" width="10" height="10" patternUnits="userSpaceOnUse" patternTransform="scale(2.5)">
<path d="M5 0 L10 2.5 L10 7.5 L5 10 L0 7.5 L0 2.5 Z" fill="none" stroke="#E3B341" strokeWidth="0.03" />
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#hexagons)" />
@ -135,89 +243,158 @@ const ObservationPage = () => {
</div>
)}
{/* Telemetry & UI */}
{/* UI Controls & Telemetry */}
{isCameraActive && (
<div className="absolute inset-0 z-20 p-6 flex flex-col justify-between pointer-events-none">
<div className="absolute inset-0 z-20 p-4 flex flex-col justify-between pointer-events-none">
{/* Top Bar */}
<div className="flex justify-between items-start pointer-events-auto">
<div className="bg-black/60 border-l-4 border-[#E3B341] p-4 backdrop-blur-md">
<div className="text-[10px] text-gray-400 uppercase tracking-tighter">System Status</div>
<div className="bg-black/80 border-l-4 border-[#E3B341] p-4 backdrop-blur-lg">
<div className="text-[10px] text-gray-400 uppercase tracking-tighter">Mission Control: NASA-ESA-CSA</div>
<div className="flex items-center space-x-2">
<div className="w-2 h-2 bg-green-500 rounded-full animate-ping"></div>
<div className="font-bold text-white tracking-widest uppercase">Operational</div>
<div className="font-bold text-[#E3B341] tracking-widest uppercase">
{isFocusing ? 'Acquiring Target' : 'Observation Stable'}
</div>
</div>
<div className="mt-2 grid grid-cols-2 gap-x-4 text-[10px]">
<div>TEMP: <span className="text-[#E3B341]">{telemetry.temp.toFixed(1)}K</span></div>
<div>L2 DIST: <span className="text-[#E3B341]">{telemetry.dist.toFixed(3)}M km</span></div>
<div className="mt-3 grid grid-cols-2 gap-x-6 text-[10px]">
<div className="flex flex-col">
<span className="text-gray-500">BUS TEMP</span>
<span className="text-white text-sm">{(selectedTarget && isDeepZoom ? 6.5 : telemetry.temp).toFixed(1)}K</span>
</div>
<div className="flex flex-col">
<span className="text-gray-500">MAGNIFICATION</span>
<span className="text-[#00F2FF] text-sm font-bold">{formatZoom(zoom)}</span>
</div>
<div className="flex flex-col mt-2">
<span className="text-gray-500">FOCAL POINT</span>
<span className="text-white text-sm">{telemetry.focal.toFixed(1)}mm</span>
</div>
<div className="flex flex-col mt-2">
<span className="text-gray-500">L2 DISTANCE</span>
<span className="text-white text-sm">1,502,401.2 km</span>
</div>
</div>
</div>
<div className="flex space-x-2">
<button
onClick={() => { setSelectedTarget(null); setZoom(1); }}
className="p-3 bg-black/80 border border-[#E3B341]/40 hover:bg-[#E3B341]/20 transition-all text-[#E3B341]"
>
<BaseIcon path={mdiOrbitVariant} size={20} />
</button>
<button
onClick={stopCamera}
className="p-3 bg-black/80 border border-red-500/40 hover:bg-red-500/40 transition-all"
>
<BaseIcon path={mdiClose} size={20} className="text-white" />
</button>
</div>
</div>
{/* Zoom Controls */}
<div className="absolute right-4 top-1/2 -translate-y-1/2 flex flex-col space-y-4 pointer-events-auto items-center">
<div className="text-[10px] text-[#00F2FF] font-bold mb-2 uppercase rotate-90 w-20 text-center">Magnify</div>
<button
onClick={stopCamera}
className="p-2 bg-black/60 border border-white/20 hover:bg-red-500/40 transition-colors"
onClick={() => handleZoom('in')}
className="w-12 h-12 bg-black/80 border border-[#00F2FF] flex items-center justify-center hover:bg-[#00F2FF] hover:text-black transition-all rounded-full shadow-[0_0_15px_rgba(0,242,255,0.3)]"
>
<BaseIcon path={mdiClose} size={24} className="text-white" />
<BaseIcon path={mdiMagnifyPlusOutline} size={24} />
</button>
<div className="h-40 w-1 bg-gray-800 relative rounded-full overflow-hidden border border-white/10">
<div
className="absolute bottom-0 left-0 w-full bg-[#00F2FF] transition-all duration-300"
style={{ height: `${(Math.log10(zoom) / 14) * 100}%` }}
></div>
</div>
<button
onClick={() => handleZoom('out')}
className="w-12 h-12 bg-black/80 border border-[#00F2FF] flex items-center justify-center hover:bg-[#00F2FF] hover:text-black transition-all rounded-full"
>
<BaseIcon path={mdiMagnifyMinusOutline} size={24} />
</button>
</div>
{/* Identification Box */}
<div className="flex justify-center mb-12">
<div className="bg-black/60 border border-[#00F2FF]/40 p-6 backdrop-blur-md w-full max-w-md relative overflow-hidden">
{scanning && (
<div className="absolute inset-0 bg-[#00F2FF]/10 animate-pulse"></div>
)}
<div className="flex items-center space-x-4 mb-4">
<div className={`p-2 rounded-full border ${scanning ? 'border-[#00F2FF] animate-spin' : 'border-gray-500'}`}>
<BaseIcon path={mdiTarget} size={24} className={scanning ? 'text-[#00F2FF]' : 'text-gray-500'} />
</div>
<div>
<div className="text-[10px] uppercase text-gray-400 tracking-widest">Active Search</div>
<div className="font-bold text-lg uppercase tracking-widest">
{scanning ? 'Analyzing Spectrum...' : identifiedObject ? identifiedObject.name : 'No Target Lock'}
</div>
</div>
</div>
{identifiedObject && !scanning && (
<div className="text-xs text-gray-300 border-t border-white/10 pt-4 space-y-1">
<p><span className="text-[#00F2FF]">COORDINATES:</span> {identifiedObject.ra || 'N/A'}, {identifiedObject.dec || 'N/A'}</p>
<p><span className="text-[#00F2FF]">CLASSIFICATION:</span> {identifiedObject.type || 'STEL-OBJ'}</p>
<p className="mt-2 line-clamp-2">{identifiedObject.description}</p>
{/* Targeting HUD */}
{isDeepZoom && (
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
<div className="w-80 h-80 border border-[#00F2FF]/20 rounded-lg relative animate-pulse">
<div className="absolute -top-1 -left-1 w-4 h-4 border-t-2 border-l-2 border-[#00F2FF]"></div>
<div className="absolute -top-1 -right-1 w-4 h-4 border-t-2 border-r-2 border-[#00F2FF]"></div>
<div className="absolute -bottom-1 -left-1 w-4 h-4 border-b-2 border-l-2 border-[#00F2FF]"></div>
<div className="absolute -bottom-1 -right-1 w-4 h-4 border-b-2 border-r-2 border-[#00F2FF]"></div>
<div className="absolute inset-0 flex items-center justify-center">
<BaseIcon path={mdiCrosshairsGps} size={32} className="text-[#00F2FF]/40" />
</div>
)}
{!scanning && (
<button
onClick={() => setScanning(true)}
className="mt-4 w-full bg-[#00F2FF]/20 border border-[#00F2FF] py-2 text-[10px] uppercase tracking-[0.4em] font-bold hover:bg-[#00F2FF] hover:text-black transition-all pointer-events-auto"
>
Initiate Deep Scan
</button>
)}
</div>
</div>
</div>
</div>
)}
{/* Bottom Controls */}
<div className="flex justify-center space-x-4 pointer-events-auto pb-6">
{[
{ id: 'normal', label: 'VIS', color: 'bg-white/10 text-white' },
{ id: 'ir', label: 'NIRSpec', color: 'bg-[#E3B341]/20 text-[#E3B341]' },
{ id: 'deep', label: 'MIRI', color: 'bg-purple-500/20 text-purple-300' }
].map(btn => (
<button
key={btn.id}
onClick={() => setMode(btn.id as any)}
className={`px-6 py-2 border transition-all ${btn.color} ${mode === btn.id ? 'border-current' : 'border-transparent opacity-50 hover:opacity-100'}`}
>
<div className="text-[10px] uppercase font-bold tracking-widest">{btn.label}</div>
</button>
))}
{/* Bottom Area: Controls */}
<div className="flex flex-col items-center space-y-6">
{/* Target Selector */}
<div className="flex flex-wrap justify-center gap-2 pointer-events-auto bg-black/60 p-3 backdrop-blur-md rounded-xl border border-white/10 max-w-lg">
<div className="w-full text-[8px] text-gray-500 uppercase tracking-widest text-center mb-1">Select Astronomical Target</div>
{PRESET_TARGETS.map(target => (
<button
key={target.id}
onClick={() => selectTarget(target)}
className={`px-3 py-1.5 text-[9px] uppercase tracking-[0.2em] border transition-all ${selectedTarget?.id === target.id ? 'bg-[#E3B341] text-black border-[#E3B341]' : 'bg-black/60 text-white border-white/20 hover:border-[#E3B341]'}`}
>
{target.name}
</button>
))}
</div>
{/* Sensor Modes */}
<div className="flex justify-center space-x-6 pointer-events-auto pb-4">
{[
{ id: 'normal', label: 'VIS', icon: mdiWeatherNight, color: 'text-white' },
{ id: 'ir', label: 'NIRSpec', icon: mdiFlare, color: 'text-[#E3B341]' },
{ id: 'deep', label: 'MIRI', icon: mdiOrbitVariant, color: 'text-purple-400' }
].map(btn => (
<button
key={btn.id}
onClick={() => setMode(btn.id as any)}
className={`flex flex-col items-center p-2 w-16 transition-all ${mode === btn.id ? 'scale-110' : 'opacity-40 hover:opacity-100'}`}
>
<div className={`p-2 rounded-full border ${mode === btn.id ? 'border-[#00F2FF] bg-[#00F2FF]/10' : 'border-white/10'}`}>
<BaseIcon path={btn.icon} size={20} className={btn.color} />
</div>
<div className={`text-[8px] mt-1 font-bold tracking-tighter ${btn.color}`}>{btn.label}</div>
</button>
))}
</div>
</div>
</div>
)}
{/* Static Footer (only visible when not active or on desktop) */}
<div className="absolute bottom-4 right-4 text-[8px] text-gray-600 uppercase tracking-[0.5em] pointer-events-none">
Mission Protocol: JWST-MAIN-SYS-V2.0.26
</div>
{/* Target Info Overlay */}
{selectedTarget && isDeepZoom && (
<div className="absolute bottom-28 left-6 z-30 pointer-events-none">
<div className="bg-black/90 border-l-4 border-[#00F2FF] p-6 backdrop-blur-2xl max-w-xs animate-fade-in shadow-[0_0_30px_rgba(0,0,0,0.5)]">
<div className="text-[10px] text-[#00F2FF] uppercase tracking-[0.3em] font-bold">Spectral Classification</div>
<h2 className="text-3xl font-bold text-white uppercase tracking-tighter my-1">{selectedTarget.name}</h2>
<div className="mt-4 space-y-2 text-[10px] text-gray-400 border-t border-white/10 pt-4">
<div className="flex justify-between">
<span>DISTANCE</span>
<span className="text-white font-bold">{selectedTarget.dist}</span>
</div>
<div className="flex justify-between">
<span>THERMAL SIG</span>
<span className="text-white font-bold">{selectedTarget.temp}</span>
</div>
<div className="flex justify-between">
<span>COORD</span>
<span className="text-white font-bold">RA 5h 35m 17s | Dec -5° 23&apos; 28&quot;</span>
</div>
</div>
</div>
</div>
)}
{/* Futuristic Scanline */}
<div className="absolute inset-0 pointer-events-none z-40 bg-scanline opacity-10"></div>
</div>
);
};