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:
@@ -15,6 +15,7 @@ export class SolarSystem2DRenderer {
|
||||
private camera: Camera2D = { x: 0, y: 0, zoom: 1 };
|
||||
private bodyInfos: BodyInfo[] = [];
|
||||
private positions: Float64Array = new Float64Array(0);
|
||||
private velocities: Float64Array = new Float64Array(0);
|
||||
private orbitPoints: Map<number, Float64Array> = new Map();
|
||||
private isDragging = false;
|
||||
private lastMouse = { x: 0, y: 0 };
|
||||
@@ -66,9 +67,10 @@ export class SolarSystem2DRenderer {
|
||||
];
|
||||
}
|
||||
|
||||
updateBodies(infos: BodyInfo[], positions: Float64Array) {
|
||||
updateBodies(infos: BodyInfo[], positions: Float64Array, velocities?: Float64Array) {
|
||||
this.bodyInfos = infos;
|
||||
this.positions = positions;
|
||||
if (velocities) this.velocities = velocities;
|
||||
}
|
||||
|
||||
updateOrbit(bodyId: number, points: Float64Array) {
|
||||
@@ -108,7 +110,7 @@ export class SolarSystem2DRenderer {
|
||||
if (i === 0) {
|
||||
this.drawSun(x, y, info);
|
||||
} else {
|
||||
this.drawBody(x, y, info);
|
||||
this.drawBody(i, x, y, info);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,7 +215,7 @@ export class SolarSystem2DRenderer {
|
||||
ctx.fillText(info.name, sx + 10, sy + 4);
|
||||
}
|
||||
|
||||
private drawBody(xAU: number, yAU: number, info: BodyInfo) {
|
||||
private drawBody(bodyIndex: number, xAU: number, yAU: number, info: BodyInfo) {
|
||||
const ctx = this.ctx;
|
||||
const [sx, sy] = this.auToScreen(xAU, yAU);
|
||||
const [r, g, b] = info.color;
|
||||
@@ -223,6 +225,32 @@ export class SolarSystem2DRenderer {
|
||||
// Moons are smaller
|
||||
if (info.radius_km < 3000) size = 1.5;
|
||||
|
||||
// Velocity tail
|
||||
if (this.velocities.length > bodyIndex * 3 + 2) {
|
||||
const vx = this.velocities[bodyIndex * 3];
|
||||
const vy = this.velocities[bodyIndex * 3 + 1];
|
||||
const speed = Math.sqrt(vx * vx + vy * vy);
|
||||
if (speed > 1e-10) {
|
||||
// Scale tail length: normalize velocity and multiply by a visual factor
|
||||
const tailScale = this.getScale() * 0.15; // pixels per (AU/day)
|
||||
const tx = sx + vx * tailScale;
|
||||
const ty = sy - vy * tailScale; // flip Y
|
||||
|
||||
// Gradient tail from body color to transparent
|
||||
const gradient = ctx.createLinearGradient(sx, sy, tx, ty);
|
||||
gradient.addColorStop(0, `rgba(${r}, ${g}, ${b}, 0.6)`);
|
||||
gradient.addColorStop(1, `rgba(${r}, ${g}, ${b}, 0)`);
|
||||
|
||||
ctx.strokeStyle = gradient;
|
||||
ctx.lineWidth = size * 0.8;
|
||||
ctx.lineCap = 'round';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(sx, sy);
|
||||
ctx.lineTo(tx, ty);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
// Body dot
|
||||
ctx.fillStyle = rgbToCss(r, g, b);
|
||||
ctx.beginPath();
|
||||
|
||||
Reference in New Issue
Block a user