Skip to content

Instantly share code, notes, and snippets.

@agrif
Created November 18, 2021 00:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save agrif/3d0450a92924c8367736733450d58c2d to your computer and use it in GitHub Desktop.
Save agrif/3d0450a92924c8367736733450d58c2d to your computer and use it in GitHub Desktop.
use angle::Rad;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
macro_rules! log {
( $( $t:tt )* ) => {
web_sys::console::log_1(&format!( $( $t )* ).into());
}
}
type JsResult<T> = Result<T, wasm_bindgen::JsValue>;
#[derive(Clone, Debug)]
struct Orbit<F = f64> {
// eccentricity: 0 to infinity, unitless
e: F,
// semimajor axis: 0 to infinity, distance
a: F,
// argument of periapsis
w: Rad<F>,
// inclination
i: Rad<F>,
// longitude of the ascending node
o: Rad<F>,
// mean anomaly
m: Rad<F>,
// standard gravitational parameter, 0 to infinity, distance^3/time^2
u: F,
}
impl<F> Orbit<F>
where
F: num_traits::Float + num_traits::FloatConst,
{
// constructors
pub fn elliptical(r1: F, r2: F) -> Self {
let ratio = if r1 > r2 { r1 / r2 } else { r2 / r1 };
let two = F::from(2.0).unwrap();
let e = F::one() - two / (ratio + F::one());
Orbit {
e: e,
a: (r1 + r2) / two,
w: Rad(F::zero()),
i: Rad(F::zero()),
o: Rad(F::zero()),
m: Rad(F::zero()),
u: F::zero(),
}
}
pub fn circular(r: F) -> Self {
Self::elliptical(r, r)
}
// properties
pub fn apoapsis(&self) -> F {
self.a * (F::one() + self.e)
}
pub fn argument_of_periapsis(&self) -> Rad<F> {
self.w
}
pub fn eccentricity(&self) -> F {
self.e
}
pub fn gravitational_parameter(&self) -> F {
self.u
}
pub fn inclination(&self) -> Rad<F> {
self.i
}
pub fn linear_eccentricity(&self) -> F {
self.a * self.e
}
pub fn longitude_of_ascending_node(&self) -> Rad<F> {
self.o
}
pub fn mean_anomaly(&self) -> Rad<F> {
self.m
}
pub fn mean_motion(&self) -> F {
(self.u * self.a.powi(3)).sqrt()
}
pub fn periapsis(&self) -> F {
self.a * (F::one() - self.e)
}
pub fn period(&self) -> F {
F::from(2.0).unwrap() * F::PI() / self.mean_motion()
}
pub fn semilatus_rectum(&self) -> F {
self.a * (F::one() - self.e.powi(2))
}
pub fn semimajor_axis(&self) -> F {
self.a
}
pub fn semiminor_axis(&self) -> F {
self.a * (F::one() - self.e.powi(2)).sqrt()
}
}
struct Orrery {
ctx: web_sys::CanvasRenderingContext2d,
scale: f64,
scale_orbit: f64,
}
impl Orrery {
pub fn new(canvas: web_sys::HtmlCanvasElement) -> JsResult<Self> {
let width = canvas.width();
let height = canvas.height();
let scale = (width.min(height) as f64) / 2.0;
let ctx = canvas
.get_context("2d")
.unwrap()
.unwrap()
.dyn_into::<web_sys::CanvasRenderingContext2d>()?;
ctx.scale(scale, scale)?;
ctx.translate(1.0, 1.0)?;
ctx.set_line_width(1.0 / scale);
Ok(Orrery { ctx, scale, scale_orbit: 1.0 })
}
pub fn set_scale(&mut self, scale: f64) {
self.scale_orbit = scale;
}
pub fn draw_orbit(&mut self, orbit: &Orbit) -> JsResult<()> {
self.ctx.save();
self.ctx.scale(0.8 / self.scale_orbit, 0.8 / self.scale_orbit)?;
self.ctx.begin_path();
self.ctx.ellipse(
orbit.linear_eccentricity(),
0.0,
orbit.semimajor_axis(),
orbit.semiminor_axis(),
0.0,
0.0,
2.0 * std::f64::consts::PI,
)?;
self.ctx.restore();
self.ctx.stroke();
Ok(())
}
pub fn draw_planet(&mut self) -> JsResult<()> {
self.ctx.begin_path();
self.ctx.arc(0.0, 0.0,
10.0 / self.scale,
0.0, 2.0 * std::f64::consts::PI)?;
self.ctx.fill();
Ok(())
}
}
#[wasm_bindgen(start)]
pub fn start() -> JsResult<()> {
let document = web_sys::window().unwrap().document()
.ok_or("could not get window")?;
let canvas = document.get_element_by_id("canvas")
.ok_or("could not get canvas")?;
let canvas = canvas.dyn_into::<web_sys::HtmlCanvasElement>()?;
let mut orr = Orrery::new(canvas)?;
let orb = Orbit::elliptical(100.0, 1000.0);
let orb2 = Orbit::circular(orb.apoapsis());
log!("orbit: {:?}", orb);
log!("peri: {:?} apo: {:?}", orb.periapsis(), orb.apoapsis());
log!("orbit2: {:?}", orb2);
log!("peri: {:?} apo: {:?}", orb2.periapsis(), orb2.apoapsis());
orr.set_scale(orb.apoapsis());
orr.draw_orbit(&orb)?;
orr.draw_orbit(&orb2)?;
orr.draw_planet()?;
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment