Add Lambert solver, transfer matrix, Dijkstra routing, and route planner UI

- Lambert's problem solver using universal variable method with bisection
  (handles elliptic, parabolic, hyperbolic transfers + anti-podal cases)
- Transfer matrix: precompute pairwise station transfers over time window
  using Lambert solver with configurable launch velocity
- Dijkstra routing on time-expanded graph (station × week nodes, transfer
  + wait edges) to find minimum-time routes
- Route Planner UI: from/to station dropdowns, search window selector
  (1-10 years), "Find Optimal Route" button with results card
- Route visualization: orange dashed trajectory lines with arrow heads
  and leg numbers on the 2D canvas
- Tested: Mercury L1 → Jupiter L1 computes 6-month direct transfer at
  30 km/s — physically reasonable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-08 12:14:37 -07:00
parent a2daa2d617
commit 22dcc5b6ec
12 changed files with 977 additions and 2 deletions

View File

@@ -1,5 +1,8 @@
use mass_driver_core::router;
use mass_driver_core::station;
use mass_driver_core::transfer_matrix;
use orbital_mechanics::bodies;
use orbital_mechanics::orbits;
use wasm_bindgen::prelude::*;
@@ -98,3 +101,59 @@ pub fn get_station_names(stations_json: &str) -> String {
let names: Vec<&str> = stations.iter().map(|s| s.name.as_str()).collect();
serde_json::to_string(&names).unwrap()
}
/// Compute transfer matrix and find optimal route between two stations.
///
/// This is the main computation function. It:
/// 1. Computes the transfer matrix for the given stations over a time window
/// 2. Runs Dijkstra to find the optimal route
///
/// Returns JSON-serialized RouteResult, or empty string if no route found.
///
/// # Arguments (all as JSON)
/// * `stations_json` - Station configuration
/// * `from_station` - Source station index
/// * `to_station` - Destination station index
/// * `launch_velocity_kms` - Max launch velocity in km/s
/// * `start_jd` - Start of search window (Julian Date)
/// * `week_window` - Number of weeks to search
#[wasm_bindgen]
pub fn compute_route(
stations_json: &str,
from_station: usize,
to_station: usize,
launch_velocity_kms: f64,
start_jd: f64,
week_window: usize,
) -> String {
let stations: Vec<station::Station> = match serde_json::from_str(stations_json) {
Ok(s) => s,
Err(_) => return String::new(),
};
// Compute transfer matrix (the heavy part)
let matrix = transfer_matrix::compute_transfer_matrix(
&stations,
launch_velocity_kms,
start_jd,
week_window,
|_completed, _total| {
// Progress callback — in WASM we can't easily report progress
// to the main thread without Web Workers. For now, just run.
},
);
// Find optimal route
let route = router::find_optimal_route(
&matrix,
from_station,
to_station,
0,
week_window.saturating_sub(1),
);
match route {
Some(r) => serde_json::to_string(&r).unwrap_or_default(),
None => String::new(),
}
}