cosmo/VISUALIZATION_ISSUES.md

512 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 可视化问题分析与解决方案
## 问题 1: 探测器/月球的视觉偏移导致距离失真
### 🚨 问题描述
当前实现中,为了避免探测器和月球与行星重合,使用了 `renderPosition.ts` 中的智能偏移逻辑:
```typescript
// renderPosition.ts:116-127
if (realDistance < 0.01) {
// 月球或表面探测器 - 固定偏移
visualOffset = planetVisualRadius + 2.0; // 距离表面 2 个单位
} else if (realDistance < 0.05) {
visualOffset = planetVisualRadius + 3.0;
} else {
visualOffset = planetVisualRadius + 4.0;
}
```
**问题**
- ❌ 月球真实距离地球 0.0026 AU38万公里
- ❌ 但在视觉上被推到距离地球表面 2 个缩放单位
- ❌ 用户无法判断月球与太阳的真实距离关系
- ❌ 探测器的真实轨道距离完全失真
**影响范围**
- 月球Earth's Moon
- 火星探测器Perseverance, Curiosity
- 木星探测器Juno
- 土星探测器Cassini
---
## 问题 2: 矮行星轨道数据加载量过大
### 🚨 问题描述
当前 `DwarfPlanetOrbits.tsx` 的实现:
```typescript
// DwarfPlanetOrbits.tsx:61-72
const startDate = new Date('2020-01-01');
const endDate = new Date('2030-01-01');
const response = await fetch(
`http://localhost:8000/api/celestial/positions?` +
`body_ids=${bodyIds}&` +
`start_time=${startDate.toISOString()}&` +
`end_time=${endDate.toISOString()}&` +
`step=30d` // 10 年 = 120 个点/天体
);
```
**问题**
- ❌ 矮行星公转周期:冥王星 248 年,阋神星 557 年
- ❌ 10 年数据只能显示公转轨道的 4-18%
- ❌ 需要请求完整轨道周期数据248-557 年)
- ❌ 数据量巨大:冥王星完整轨道 = 248 年 × 12 月 = 2976 个点
- ❌ 首次加载会触发大量 NASA API 调用
**矮行星轨道周期**
| 天体 | 公转周期 | 完整轨道点数30天/点) | 数据量 |
|------|----------|----------------------|--------|
| 冥王星 | 248 年 | 2,976 点 | 71 KB |
| 阋神星 | 557 年 | 6,684 点 | 160 KB |
| 妊神星 | 285 年 | 3,420 点 | 82 KB |
| 鸟神星 | 309 年 | 3,708 点 | 89 KB |
| 谷神星 | 4.6 年 | 55 点 | 1.3 KB |
**总数据量**~403 KB单次请求
---
## 解决方案对比
### 方案 1: 视觉偏移 + 真实距离提示 ⭐⭐⭐
**策略**:保持当前视觉偏移,但在 UI 上明确标注真实距离。
**实现**
1. **在天体详情卡片中显示真实距离**
```typescript
// ProbeList.tsx 或 CelestialBody 详情
{hasOffset && (
<div className="text-yellow-400 text-xs">
<span>⚠️ 视觉位置已调整便于观察</span>
<span>真实距离: {realDistance.toFixed(4)} AU</span>
<span> {(realDistance * 149597870.7).toFixed(0)} 千米</span>
</div>
)}
```
2. **添加真实轨道线(虚线)**
```typescript
// 在 Probe.tsx 中添加真实轨道路径
{hasOffset && (
<Line
points={[
new Vector3(realPos.x, realPos.y, realPos.z), // 真实位置
new Vector3(visualPos.x, visualPos.y, visualPos.z) // 视觉位置
]}
color="yellow"
lineWidth={1}
dashed
dashSize={0.1}
gapSize={0.05}
/>
)}
```
**优点**
- ✅ 保持当前的视觉清晰度
- ✅ 通过文字和虚线提示真实位置
- ✅ 实现简单,改动小
**缺点**
- ⚠️ 用户仍然无法直观看到真实距离
- ⚠️ 需要额外的 UI 提示
---
### 方案 2: 移除视觉偏移,优化缩放算法 ⭐⭐⭐⭐⭐(推荐)
**策略**:改进距离缩放算法,让近距离天体也能清晰显示,无需偏移。
**核心思路**
-**极近距离**< 0.01 AU)使用**对数缩放**
- 让月球和探测器保持真实方向,但有足够的视觉间隔
**实现**
```typescript
// scaleDistance.ts - 新增超近距离缩放
export function scaleDistance(distanceInAU: number): number {
// Ultra-close region (< 0.001 AU): extreme expansion for moons/probes
if (distanceInAU < 0.001) {
// 对数缩放0.0001 AU → 0.1, 0.001 AU → 0.5
return 0.1 + Math.log10(distanceInAU + 0.0001) * 0.4;
}
// Very close region (0.001-0.01 AU): strong expansion
if (distanceInAU < 0.01) {
// 0.001 AU → 0.5, 0.01 AU → 1.5
return 0.5 + (distanceInAU - 0.001) * 100;
}
// Close region (0.01-0.1 AU): moderate expansion
if (distanceInAU < 0.1) {
return 1.5 + (distanceInAU - 0.01) * 20;
}
// Inner solar system (0.1-2 AU): expand by 3x
if (distanceInAU < 2) {
return 3.3 + (distanceInAU - 0.1) * 3;
}
// Middle region (2-10 AU): normal scale
if (distanceInAU < 10) {
return 9 + (distanceInAU - 2) * 1.5;
}
// Outer solar system (10-50 AU): compressed
if (distanceInAU < 50) {
return 21 + (distanceInAU - 10) * 0.5;
}
// Very far (> 50 AU): heavily compressed
return 41 + (distanceInAU - 50) * 0.2;
}
```
**修改 renderPosition.ts**
```typescript
// 移除视觉偏移逻辑,直接使用缩放位置
export function calculateRenderPosition(
body: CelestialBody,
allBodies: CelestialBody[]
): { x: number; y: number; z: number } {
const pos = body.positions[0];
if (!pos) {
return { x: 0, y: 0, z: 0 };
}
// 直接使用改进的缩放算法,无需偏移
const scaled = scalePosition(pos.x, pos.y, pos.z);
return { x: scaled.x, y: scaled.y, z: scaled.z };
}
```
**效果对比**
| 天体 | 真实距离 | 旧缩放 | 新缩放 | 改进 |
|------|----------|--------|--------|------|
| 月球 | 0.0026 AU | 0.0078 | 0.76 | **98倍** |
| 火星探测器 | ~1.5 AU | 4.5 | 7.5 | 更清晰 |
| 地球 | 1.0 AU | 3.0 | 5.7 | 更合理 |
**优点**
- 保持真实的空间关系
- 月球和探测器仍然可见(足够大)
- 用户可以直观理解距离
- 无需 UI 提示
**缺点**
- ⚠️ 需要调整缩放参数,可能需要多次调试
---
### 方案 3: 双模式切换(真实模式 vs 演示模式)⭐⭐⭐⭐
**策略**:提供两种显示模式,用户可以切换。
**实现**
```typescript
// App.tsx
const [visualMode, setVisualMode] = useState<'realistic' | 'demo'>('demo');
// Header.tsx 添加切换按钮
<button onClick={() => setVisualMode(mode === 'realistic' ? 'demo' : 'realistic')}>
{visualMode === 'realistic' ? '真实距离' : '演示模式'}
</button>
// renderPosition.ts
export function calculateRenderPosition(
body: CelestialBody,
allBodies: CelestialBody[],
mode: 'realistic' | 'demo'
): Position {
if (mode === 'realistic') {
// 使用改进的缩放,无偏移
return scalePosition(pos.x, pos.y, pos.z);
} else {
// 使用视觉偏移
return calculateDemoPosition(body, allBodies);
}
}
```
**优点**
- 灵活性最高
- 满足不同用户需求
- 教育价值高
**缺点**
- ⚠️ 实现复杂度较高
- ⚠️ 需要维护两套逻辑
---
## 矮行星轨道问题解决方案
### 方案 A: 预计算并存储完整轨道数据 ⭐⭐⭐⭐⭐(推荐)
**策略**:按照 `ORBIT_OPTIMIZATION.md` 的方案 1A 实现。
**实施步骤**
1. **创建 orbits 表**(已在 ORBIT_OPTIMIZATION.md 中定义)
2. **后端管理接口生成轨道**
```python
# app/api/routes.py
@router.post("/admin/orbits/generate")
async def generate_dwarf_planet_orbits(db: AsyncSession = Depends(get_db)):
"""为矮行星生成完整轨道数据"""
dwarf_planets = await celestial_body_service.get_bodies_by_type(db, "dwarf_planet")
orbital_periods = {
"999": 248, # 冥王星
"136199": 557, # 阋神星
"136108": 285, # 妊神星
"136472": 309, # 鸟神星
"1": 4.6, # 谷神星
}
for planet in dwarf_planets:
period_years = orbital_periods.get(planet.id, 250)
# 计算采样点数:完整周期,每 30 天一个点
num_points = min(int(period_years * 365 / 30), 1000) # 最多 1000 点
# 查询 NASA Horizons
start = datetime.utcnow()
end = start + timedelta(days=period_years * 365)
step_days = int(period_years * 365 / num_points)
positions = await horizons_service.get_body_positions(
planet.id,
start,
end,
f"{step_days}d"
)
# 保存到 orbits 表
await orbit_service.save_orbit(
planet.id,
[{"x": p.x, "y": p.y, "z": p.z} for p in positions],
num_points,
period_years * 365
)
return {"message": f"Generated {len(dwarf_planets)} orbits"}
```
3. **前端从 API 读取**
```typescript
// DwarfPlanetOrbits.tsx - 简化版
useEffect(() => {
const fetchOrbits = async () => {
// 直接从后端读取预存的轨道数据
const response = await fetch('http://localhost:8000/api/celestial/orbits?body_type=dwarf_planet');
const data = await response.json();
const orbitData = data.orbits.map((orbit: any) => ({
bodyId: orbit.body_id,
points: orbit.points.map((p: any) => {
const scaled = scalePosition(p.x, p.y, p.z);
return new THREE.Vector3(scaled.x, scaled.z, scaled.y);
}),
color: orbit.color || getDefaultColor(orbit.body_name)
}));
setOrbits(orbitData);
};
fetchOrbits();
}, []);
```
**优点**
- 完整准确的轨道
- 前端加载快(<1秒)
- 无需实时 NASA API 调用
- 数据量可接受(总共 ~400 KB
**缺点**
- ⚠️ 需要数据库迁移
- ⚠️ 首次生成需要时间(一次性)
---
### 方案 B: 使用数学模拟轨道 ⭐⭐⭐⭐
**策略**:基于轨道六要素(orbital elements)数学计算。
**实现**
```typescript
// EllipticalOrbit.tsx
interface OrbitalElements {
a: number; // 半长轴 (AU)
e: number; // 离心率
i: number; // 轨道倾角 (度)
omega: number; // 升交点黄经 (度)
w: number; // 近日点幅角 (度)
M0: number; // 平近点角 (度)
period: number; // 轨道周期 (天)
}
// 冥王星轨道要素(来自 NASA JPL
const PLUTO_ELEMENTS: OrbitalElements = {
a: 39.48,
e: 0.2488,
i: 17.16,
omega: 110.30,
w: 113.77,
M0: 14.53,
period: 90560
};
function generateOrbitPoints(elements: OrbitalElements, numPoints = 360): Vector3[] {
const points: Vector3[] = [];
for (let i = 0; i <= numPoints; i++) {
const M = (i / numPoints) * 2 * Math.PI; // 平近点角
// 求解开普勒方程得到偏近点角 E
let E = M;
for (let j = 0; j < 10; j++) {
E = M + elements.e * Math.sin(E);
}
// 计算真近点角
const v = 2 * Math.atan(
Math.sqrt((1 + elements.e) / (1 - elements.e)) * Math.tan(E / 2)
);
// 计算轨道平面坐标
const r = elements.a * (1 - elements.e * Math.cos(E));
const x_orb = r * Math.cos(v);
const y_orb = r * Math.sin(v);
// 旋转到黄道坐标系
const point = rotateToEcliptic(x_orb, y_orb, 0, elements);
const scaled = scalePosition(point.x, point.y, point.z);
points.push(new Vector3(scaled.x, scaled.z, scaled.y));
}
return points;
}
```
**轨道要素来源**
- NASA JPL Small-Body Database: https://ssd.jpl.nasa.gov/sbdb.cgi
- 可以从后端 `celestial_bodies` 表的 `orbital_elements` 字段读取
**优点**
- 不需要网络请求
- 瞬时生成
- 数学上准确
- 可以显示任意时间的轨道
**缺点**
- ⚠️ 实现复杂(轨道力学)
- ⚠️ 不考虑摄动(其他行星引力影响)
- ⚠️ 需要获取和验证轨道要素
---
### 方案 C: 混合方案 - 谷神星用真实数据,其他用模拟 ⭐⭐⭐
**策略**
- **谷神星**4.6 年周期):使用真实数据(55 点,1.3 KB
- **冥王星、阋神星等**>200 年周期):使用数学模拟
**理由**
- 谷神星周期短,数据量小
- 长周期矮行星用数学模拟足够准确
**实现**
```typescript
// DwarfPlanetOrbits.tsx
const fetchOrbits = async () => {
// 只请求谷神星Ceres的真实数据
const ceresOrbit = await fetchRealOrbit('1', 5); // 5 年数据
// 其他矮行星用数学模拟
const plutoOrbit = generateOrbitPoints(PLUTO_ELEMENTS);
const erisOrbit = generateOrbitPoints(ERIS_ELEMENTS);
// ...
};
```
**优点**
- ✅ 平衡准确性和性能
- ✅ 减少数据加载
- ✅ 关键天体(谷神星)使用真实数据
**缺点**
- ⚠️ 需要维护两套逻辑
---
## 推荐实施方案
### 问题 1探测器偏移**方案 2 - 优化缩放算法**
- 实施优先级:**高**
- 预计工时2-3 小时
- 风险:低(可以逐步调整参数)
### 问题 2矮行星轨道**方案 A - 预计算轨道数据**
- 实施优先级:**中**
- 预计工时4-6 小时(包括数据库迁移)
- 风险:低(数据生成是一次性的)
**备选方案**:如果时间紧张,可以先用 **方案 B数学模拟** 作为临时解决方案。
---
## 实施步骤(建议顺序)
### 第一阶段:修复探测器偏移问题
1. 修改 `scaleDistance.ts`,添加超近距离缩放
2. 简化 `renderPosition.ts`,移除偏移逻辑
3. 测试月球、火星探测器等的显示效果
4. 微调缩放参数
### 第二阶段:优化矮行星轨道
1. 创建 `orbits` 表(数据库迁移)
2. 实现后端轨道生成 API
3. 在管理后台添加"生成轨道"按钮
4. 修改 `DwarfPlanetOrbits.tsx` 从 API 读取
5. 首次生成所有矮行星轨道数据
---
## 后续优化建议
1. **添加缩放级别指示器**
- 显示当前视图的缩放比例
- "内太阳系视图0-2 AU放大 3x"
2. **添加距离标尺**
- 在场景中显示距离参考线
- "1 AU = X 屏幕单位"
3. **轨道数据自动更新**
- 定期(每月)重新生成轨道数据
- 保持数据时效性
---
**文档版本**: v1.0
**创建时间**: 2025-11-29
**相关文件**:
- `frontend/src/utils/scaleDistance.ts`
- `frontend/src/utils/renderPosition.ts`
- `frontend/src/components/DwarfPlanetOrbits.tsx`
- `ORBIT_OPTIMIZATION.md`