From 66fceb03de6cadfd7b8810602c03d94ce5b6e39e Mon Sep 17 00:00:00 2001 From: "mula.liu" Date: Thu, 4 Dec 2025 21:43:43 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=A7=E5=B9=85=E8=B0=83=E6=95=B4=E7=9A=84?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E7=89=88=E6=9C=AC=EF=BC=8C=E5=BE=85=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/index.html | 2 +- frontend/public/favicon.svg | 44 ++++++++++ frontend/src/App.tsx | 2 +- frontend/src/components/BodyDetailOverlay.tsx | 31 +++---- frontend/src/components/BodyViewer.tsx | 85 ++++++++++++++++--- frontend/src/components/CelestialBody.tsx | 60 +++++++++++-- frontend/src/components/ProbeList.tsx | 22 ++++- frontend/src/pages/admin/CelestialBodies.tsx | 2 +- frontend/src/pages/admin/SystemSettings.tsx | 18 +--- frontend/src/utils/labelTexture.ts | 22 ++--- 10 files changed, 219 insertions(+), 69 deletions(-) create mode 100644 frontend/public/favicon.svg diff --git a/frontend/index.html b/frontend/index.html index d3cdd1c..5fb065d 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,7 +2,7 @@ - + Cosmo - 深空探测器可视化 diff --git a/frontend/public/favicon.svg b/frontend/public/favicon.svg new file mode 100644 index 0000000..a4d1283 --- /dev/null +++ b/frontend/public/favicon.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 7e634be..e3d9788 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -117,7 +117,7 @@ function App() { // Filter probes and planets from all bodies const probes = bodies.filter((b) => b.type === 'probe'); const planets = bodies.filter((b) => - b.type === 'planet' || b.type === 'dwarf_planet' || b.type === 'satellite' || b.type === 'comet' + b.type === 'planet' || b.type === 'dwarf_planet' || b.type === 'satellite' || b.type === 'comet' || b.type === 'star' ); const handleBodySelect = (body: CelestialBody | null) => { diff --git a/frontend/src/components/BodyDetailOverlay.tsx b/frontend/src/components/BodyDetailOverlay.tsx index fe86cef..e6ae149 100644 --- a/frontend/src/components/BodyDetailOverlay.tsx +++ b/frontend/src/components/BodyDetailOverlay.tsx @@ -16,15 +16,8 @@ interface BodyDetailOverlayProps { onClose: () => void; } -// Custom camera control for automatic rotation -function AutoRotateCamera() { - useFrame((state) => { - state.camera.position.x = Math.sin(state.clock.elapsedTime * 0.1) * 3; - state.camera.position.z = Math.cos(state.clock.elapsedTime * 0.1) * 3; - state.camera.lookAt(0, 0, 0); - }); - return null; -} +// No auto-rotation - user controls the view +// Removed AutoRotateCamera to allow user to control the view angle export function BodyDetailOverlay({ bodyId, onClose }: BodyDetailOverlayProps) { @@ -74,21 +67,21 @@ export function BodyDetailOverlay({ bodyId, onClose }: BodyDetailOverlayProps) { {loading ? (
加载中...
) : ( - - - - - {/* Frontal light */} - {/* Back light */} - - {/* Auto rotate for presentation */} + + + + + + diff --git a/frontend/src/components/BodyViewer.tsx b/frontend/src/components/BodyViewer.tsx index c3689bb..912537b 100644 --- a/frontend/src/components/BodyViewer.tsx +++ b/frontend/src/components/BodyViewer.tsx @@ -20,18 +20,39 @@ export function BodyViewer({ body }: BodyViewerProps) { const [modelScale, setModelScale] = useState(1.0); const [loadError, setLoadError] = useState(false); - // Determine size and appearance + // Determine size and appearance - use larger sizes for detail view with a cap const appearance = useMemo(() => { + const baseSize = getCelestialSize(body.name, body.type); + + // Detail view scaling strategy: + // - Small bodies (< 0.15): scale up 3x for visibility + // - Medium bodies (0.15-0.3): scale up 2x + // - Large bodies (> 0.3): use base size with max cap of 1.2 + let finalSize: number; + if (body.type === 'star') { - return { size: 0.4, emissive: '#FDB813', emissiveIntensity: 1.5 }; + finalSize = 1.0; // Fixed size for stars + } else if (baseSize < 0.15) { + // Small bodies: scale up for visibility + finalSize = baseSize * 3.0; + } else if (baseSize < 0.3) { + // Medium bodies: moderate scaling + finalSize = baseSize * 2.0; + } else { + // Large bodies: minimal or no scaling with a cap + finalSize = Math.min(baseSize * 1.2, 1.2); + } + + if (body.type === 'star') { + return { size: finalSize, emissive: '#FDB813', emissiveIntensity: 1.5 }; } if (body.type === 'comet') { - return { size: getCelestialSize(body.name, body.type), emissive: '#000000', emissiveIntensity: 0 }; + return { size: finalSize, emissive: '#000000', emissiveIntensity: 0 }; } if (body.type === 'satellite') { - return { size: getCelestialSize(body.name, body.type), emissive: '#888888', emissiveIntensity: 0.4 }; + return { size: finalSize, emissive: '#888888', emissiveIntensity: 0.4 }; } - return { size: getCelestialSize(body.name, body.type), emissive: '#000000', emissiveIntensity: 0 }; + return { size: finalSize, emissive: '#000000', emissiveIntensity: 0 }; }, [body.name, body.type]); // Fetch resources (texture or model) @@ -113,9 +134,9 @@ function ProbeModelViewer({ modelPath, modelScale }: { modelPath: string; modelS const size = new THREE.Vector3(); box.getSize(size); const maxDimension = Math.max(size.x, size.y, size.z); - const targetSize = 0.35; // Standardize view - const calculatedScale = maxDimension > 0 ? targetSize / maxDimension : 0.3; - const finalScale = Math.max(0.2, Math.min(1.0, calculatedScale)); + const targetSize = 1.2; // Increased from 0.35 to 1.2 for detail view - larger probes + const calculatedScale = maxDimension > 0 ? targetSize / maxDimension : 0.8; + const finalScale = Math.max(0.5, Math.min(3.0, calculatedScale)); // Increased range for larger display return finalScale * modelScale; }, [scene, modelScale]); @@ -373,13 +394,57 @@ function PlanetModelViewer({ body, size, emissive, emissiveIntensity, texturePat {/* Saturn Rings */} {body.id === '699' && } - {/* Sun glow effect */} + {/* Sun glow effect - multi-layer scattered light */} {body.type === 'star' && ( <> + + {/* Inner bright corona */} + + + + + + {/* Middle glow layer */} - + + + + {/* Outer diffuse halo */} + + + + + + {/* Far scattered light */} + + + )} diff --git a/frontend/src/components/CelestialBody.tsx b/frontend/src/components/CelestialBody.tsx index dca5753..bde531c 100644 --- a/frontend/src/components/CelestialBody.tsx +++ b/frontend/src/components/CelestialBody.tsx @@ -355,13 +355,57 @@ function PlanetMesh({ body, size, emissive, emissiveIntensity, scaledPos, textur {/* Saturn Rings */} {body.id === '699' && } - {/* Sun glow effect */} + {/* Sun glow effect - multi-layer scattered light */} {body.type === 'star' && ( <> + + {/* Inner bright corona */} + + + + + + {/* Middle glow layer */} - + + + + {/* Outer diffuse halo */} + + + + + + {/* Far scattered light */} + + + )} @@ -369,18 +413,18 @@ function PlanetMesh({ body, size, emissive, emissiveIntensity, scaledPos, textur {/* Name label using CanvasTexture */} {labelTexture && ( - + - diff --git a/frontend/src/components/ProbeList.tsx b/frontend/src/components/ProbeList.tsx index 58adf94..28d484e 100644 --- a/frontend/src/components/ProbeList.tsx +++ b/frontend/src/components/ProbeList.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react'; -import { ChevronLeft, ChevronRight, ChevronDown, ChevronUp, Search, Globe, Rocket, Moon, Asterisk, Sparkles } from 'lucide-react'; +import { ChevronLeft, ChevronRight, ChevronDown, ChevronUp, Search, Globe, Rocket, Moon, Asterisk, Sparkles, Star } from 'lucide-react'; import type { CelestialBody } from '../types'; interface ProbeListProps { @@ -38,9 +38,8 @@ export function ProbeList({ probes, planets, onBodySelect, selectedBody, onReset if (!b.positions || b.positions.length === 0) { return false; } - // Filter by search term and type - return (b.name_zh || b.name).toLowerCase().includes(searchTerm.toLowerCase()) && - b.type !== 'star'; // Exclude Sun from list + // Filter by search term - include all types including stars + return (b.name_zh || b.name).toLowerCase().includes(searchTerm.toLowerCase()); }) .map(body => ({ body, @@ -53,6 +52,7 @@ export function ProbeList({ probes, planets, onBodySelect, selectedBody, onReset const allBodies = [...planets, ...probes]; const processedBodies = processBodies(allBodies); + const starList = processedBodies.filter(({ body }) => body.type === 'star'); const planetList = processedBodies.filter(({ body }) => body.type === 'planet'); const dwarfPlanetList = processedBodies.filter(({ body }) => body.type === 'dwarf_planet'); const satelliteList = processedBodies.filter(({ body }) => body.type === 'satellite'); @@ -107,6 +107,20 @@ export function ProbeList({ probes, planets, onBodySelect, selectedBody, onReset {/* List Content */}
+ {/* Stars Group */} + {starList.length > 0 && ( + } + count={starList.length} + bodies={starList} + isExpanded={expandedGroup === 'star'} + onToggle={() => toggleGroup('star')} + selectedBody={selectedBody} + onBodySelect={onBodySelect} + /> + )} + {/* Planets Group */} {planetList.length > 0 && ( )} - + -

- 清除缓存会清空所有内存缓存和 Redis 缓存,包括: -

    -
  • 位置数据缓存(当前位置和历史位置)
  • -
  • NASA API 响应缓存
  • -
  • 所有其他临时缓存数据
  • +
  • * 位置数据缓存(当前位置和历史位置)
  • +
  • * NASA API 响应缓存
  • +
  • * 所有其他临时缓存数据
-

- 清除后下次查询会重新从数据库或 NASA API 获取数据,可能会较慢 -

} type="warning" @@ -279,10 +273,6 @@ export function SystemSettings() { 清除所有缓存 - - diff --git a/frontend/src/utils/labelTexture.ts b/frontend/src/utils/labelTexture.ts index df86a20..d951a44 100644 --- a/frontend/src/utils/labelTexture.ts +++ b/frontend/src/utils/labelTexture.ts @@ -27,37 +27,37 @@ export function createLabelTexture(name: string, subtext: string | null, distanc // Center the single name vertically ctx.textBaseline = 'middle'; // Use a slightly larger font for simple labels (like stars/constellations) - ctx.font = 'bold 72px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Microsoft YaHei", sans-serif'; + ctx.font = 'bold 48px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Microsoft YaHei", sans-serif'; ctx.fillStyle = color; ctx.fillText(name, 256, 128); } else { // Complex Label Mode (Name + Subtext + Distance) ctx.textBaseline = 'top'; - + // 1. Name (Large, Top) - ctx.font = 'bold 64px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Microsoft YaHei", sans-serif'; + ctx.font = 'bold 42px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Microsoft YaHei", sans-serif'; ctx.fillStyle = color; ctx.fillText(name, 256, 10); - - let nextY = 90; + + let nextY = 65; // 2. Subtext/Offset (Medium, Middle) if (subtext) { - ctx.font = 'bold 42px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Microsoft YaHei", sans-serif'; + ctx.font = 'bold 32px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Microsoft YaHei", sans-serif'; ctx.fillStyle = '#ffaa00'; // Orange for warning/info ctx.fillText(subtext, 256, nextY); - nextY += 60; + nextY += 48; } else { // Add some spacing if no subtext - nextY += 20; + nextY += 15; } - + // 3. Distance (Small, Bottom) if (distance) { - ctx.font = '36px "SF Mono", "Roboto Mono", Menlo, monospace'; + ctx.font = '28px "SF Mono", "Roboto Mono", Menlo, monospace'; ctx.fillStyle = color; // Reduce opacity for distance to make it less distracting - ctx.globalAlpha = 0.7; + ctx.globalAlpha = 0.7; ctx.fillText(distance, 256, nextY); } }