/** * Position calculator for celestial bodies * Returns the scaled rendering position without artificial offsets */ import { scalePosition } from './scaleDistance'; import { getCelestialSize } from '../config/celestialSizes'; import type { CelestialBody } from '../types'; /** * Calculate rendering position using true scaled coordinates * For satellites and probes near planets, add a radial offset relative to PARENT PLANET */ export function calculateRenderPosition( body: CelestialBody, allBodies: CelestialBody[] ): { x: number; y: number; z: number; hasOffset: boolean } { const pos = body.positions[0]; if (!pos) { return { x: 0, y: 0, z: 0, hasOffset: false }; } // 1. Calculate base scaled position (fallback) const scaled = scalePosition(pos.x, pos.y, pos.z); // 2. Special handling for satellites (like Moon) and probes near planets if (body.type === 'satellite' || body.type === 'probe') { const parent = findParentPlanet(body, allBodies); if (parent) { const parentPos = parent.positions[0]; // Calculate parent's scaled position (where it is rendered) const parentScaled = scalePosition(parentPos.x, parentPos.y, parentPos.z); // Calculate vector from Parent to body (in True AU coordinates) const dx = pos.x - parentPos.x; const dy = pos.y - parentPos.y; const dz = pos.z - parentPos.z; const dist = Math.sqrt(dx*dx + dy*dy + dz*dz); if (dist > 0) { // Normalize the direction vector const nx = dx / dist; const ny = dy / dist; const nz = dz / dist; // Calculate dynamic offset based on parent planet's rendering size // Formula: planetRadius × 1.5 + 0.3 (fixed gap) // This ensures larger planets (Jupiter, Saturn) have larger offsets // while smaller planets (Earth, Mars) have smaller offsets const parentSize = getCelestialSize(parent.name, parent.type); const visualOffset = parentSize * 1.5 + 0.3; return { x: parentScaled.x + nx * visualOffset, y: parentScaled.y + ny * visualOffset, z: parentScaled.z + nz * visualOffset, hasOffset: true }; } } } return { x: scaled.x, y: scaled.y, z: scaled.z, hasOffset: false }; } /** * Find the parent planet for a celestial body (e.g., Moon orbits Earth) * Returns the planet that this body is closest to */ export function findParentPlanet( body: CelestialBody, allBodies: CelestialBody[] ): CelestialBody | null { const pos = body.positions[0]; if (!pos) return null; // Look for planets or dwarf planets const planets = allBodies.filter(b => b.type === 'planet' || b.type === 'dwarf_planet'); let closestPlanet: CelestialBody | null = null; let minDistance = Infinity; for (const planet of planets) { const planetPos = planet.positions[0]; if (!planetPos) continue; const distance = Math.sqrt( Math.pow(pos.x - planetPos.x, 2) + Math.pow(pos.y - planetPos.y, 2) + Math.pow(pos.z - planetPos.z, 2) ); // Consider as "parent" if within 0.1 AU // Moon is ~0.0026 AU from Earth // Io is ~0.0028 AU from Jupiter if (distance < 0.1 && distance < minDistance) { closestPlanet = planet; minDistance = distance; } } return closestPlanet; } /** * Get description for bodies that are close to planets */ export function getOffsetDescription(body: CelestialBody, allBodies: CelestialBody[]): string | null { if (body.type !== 'satellite' && body.type !== 'probe') { return null; } const parent = findParentPlanet(body, allBodies); if (!parent) { return null; } const pos = body.positions[0]; const parentPos = parent.positions[0]; if (!pos || !parentPos) { return null; } // Calculate actual distance const dx = pos.x - parentPos.x; const dy = pos.y - parentPos.y; const dz = pos.z - parentPos.z; const dist = Math.sqrt(dx*dx + dy*dy + dz*dz); // Return proximity description const distanceStr = dist < 0.01 ? `${(dist * 149597870.7).toFixed(0)} km` // Convert AU to km for very close objects : `${dist.toFixed(4)} AU`; return `近${parent.name_zh || parent.name} (${distanceStr})`; }