Add 3D solar system view with Threlte and velocity tails in 2D

- 3D scene: Threlte/Three.js with starfield, orbit lines, planet meshes,
  sun glow, OrbitControls (orbit/zoom/pan), point lighting
- 2D velocity tails: gradient lines showing planet velocity direction/magnitude
- New WASM API: get_body_velocities_at_epoch(), get_orbit_points()
- Orbit ellipses computed in Rust, rendered in both 2D and 3D views
- Ecliptic-to-Three.js coordinate mapping (Y-up convention)
- View toggle now switches between working 2D and 3D renderers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-08 11:54:31 -07:00
parent 5efe0736ac
commit 067ef1f557
8 changed files with 258 additions and 10 deletions

View File

@@ -46,6 +46,15 @@ pub fn get_body_radii() -> Vec<f64> {
all.iter().map(|b| b.radius_km).collect()
}
/// Get velocities of all celestial bodies at a given Julian Date.
///
/// Returns a Float64Array of [vx0, vy0, vz0, vx1, vy1, vz1, ...] in AU/day.
#[wasm_bindgen]
pub fn get_body_velocities_at_epoch(jd: f64) -> Vec<f64> {
let all = bodies::all_bodies();
orbits::all_velocities_at_epoch(&all, jd)
}
/// Get orbit points for a given body, sampled around its full orbit.
/// Returns a Float64Array of [x0, y0, z0, x1, y1, z1, ...] in AU.
/// `samples` is the number of points around the orbit.

View File

@@ -75,6 +75,27 @@ pub fn position_at_epoch(bodies: &[CelestialBody], body_id: usize, jd: f64) -> V
}
}
/// Compute velocity of a body via finite differencing (AU/day).
pub fn velocity_at_epoch(bodies: &[CelestialBody], body_id: usize, jd: f64) -> Vector3<f64> {
let dt = 0.01; // days
let p1 = position_at_epoch(bodies, body_id, jd - dt);
let p2 = position_at_epoch(bodies, body_id, jd + dt);
(p2 - p1) / (2.0 * dt)
}
/// Compute velocities of all bodies at a given epoch.
/// Returns a flat Vec of [vx, vy, vz, ...] in AU/day.
pub fn all_velocities_at_epoch(bodies: &[CelestialBody], jd: f64) -> Vec<f64> {
let mut result = Vec::with_capacity(bodies.len() * 3);
for i in 0..bodies.len() {
let vel = velocity_at_epoch(bodies, i, jd);
result.push(vel.x);
result.push(vel.y);
result.push(vel.z);
}
result
}
/// Compute positions of all bodies at a given epoch.
/// Returns a flat Vec of [x, y, z, x, y, z, ...] in AU.
pub fn all_positions_at_epoch(bodies: &[CelestialBody], jd: f64) -> Vec<f64> {