cosmo/frontend/src/utils/renderPosition.ts

138 lines
4.1 KiB
TypeScript
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.

/**
* 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})`;
}