diff --git a/.gitignore b/.gitignore index ea8c4bf..ec4afbb 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ /target + +# ffmpeg -framerate 60 -i recordings/%05d.png -pix_fmt yuv420p recording.mp4 +/recordings +/recording.mp4 diff --git a/Cargo.lock b/Cargo.lock index 0037c13..f97b8ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2240,6 +2240,7 @@ name = "sketch-gravity" version = "0.1.0" dependencies = [ "nannou", + "once_cell", "rayon", ] diff --git a/Cargo.toml b/Cargo.toml index 94da397..d317109 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,5 @@ edition = "2018" [dependencies] nannou = "0.17.1" +once_cell = "1.8.0" rayon = "1.5.1" diff --git a/src/main.rs b/src/main.rs index b79d80c..7ad59ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,21 @@ use nannou::prelude::*; use rayon::prelude::*; +mod recording; + fn main() { - nannou::app(model).update(update).simple_window(view).run(); + nannou::app(model) + .size(1440, 1440) + .update(update) + .simple_window(view) + .run(); } #[derive(Clone)] struct Body { pos: DVec2, vel: DVec2, + mass: i64, } struct Model { @@ -18,17 +25,33 @@ struct Model { fn model(_app: &App) -> Model { let mut bodies = Vec::new(); - let mut theta = 0.0; - for r in 0..512 { - let r = r as f64; - let r = 10.0 * r.sqrt(); + const MASS_SUN: i64 = 150000000000000000; - bodies.push(Body { - pos: (r * theta.cos(), r * theta.sin()).into(), - vel: (0.0, 0.0).into(), - }); + bodies.push(Body { + pos: (0.0, 0.0).into(), + vel: (0.0, 0.0).into(), + mass: MASS_SUN, + }); - theta += 1.94161103873; + const G: f64 = 6.67408e-11; + + const COPIES: i32 = 1; + + let d_theta: f64 = (360.0 / COPIES as f64).to_radians(); + + for i in 0..COPIES { + let theta = i as f64 * d_theta; + + for i in 10..250 { + let r = 5.0 * i as f64; + let m = 10000000 / i; + + bodies.push(Body { + pos: (r * theta.cos(), r * theta.sin()).into(), + vel: (0.0, (G * MASS_SUN as f64 / r).sqrt()).into(), + mass: m, + }); + } } Model { bodies } @@ -38,7 +61,7 @@ fn update(app: &App, model: &mut Model, _update: Update) { const G: f64 = 6.67408e-11; let bodies = model.bodies.clone(); - let delta_t = app.duration.since_prev_update.as_secs_f64(); + let delta_t = recording::delta_t(&app); model .bodies @@ -59,10 +82,9 @@ fn update(app: &App, model: &mut Model, _update: Update) { return; } - let force = G * /* × mass_a × mass_b = */ 1e14 / dist_sq; + let acceleration = G * other_body.mass as f64 / dist_sq; - acc += delta * force / dist_sq.sqrt(); - acc -= 0.5 * delta / (dist_sq * 0.5); + acc += delta * acceleration / dist_sq.sqrt(); }); body.vel += acc * delta_t; @@ -73,12 +95,14 @@ fn update(app: &App, model: &mut Model, _update: Update) { fn view(app: &App, model: &Model, frame: Frame) { // let t = app.duration.since_start.as_secs_f64(); - let draw = app.draw(); + let draw = app.draw().scale(2.0); draw.background().color(BLACK); for body in model.bodies.iter() { - draw.ellipse().w_h(10.0, 10.0).xy(body.pos.as_f32()); + let diameter = (body.mass as f32).log10(); // should be proportional to cuberoot but this is so we can actually see the small ones + draw.ellipse().w_h(diameter, diameter).xy(body.pos.as_f32()); } draw.to_frame(app, &frame).unwrap(); + recording::record(app, &frame); } diff --git a/src/recording.rs b/src/recording.rs new file mode 100644 index 0000000..3d3a100 --- /dev/null +++ b/src/recording.rs @@ -0,0 +1,33 @@ +use nannou::prelude::*; +use once_cell::sync::Lazy; + +static RECORDING: Lazy = Lazy::new(|| { + let args: Vec = std::env::args().collect(); + args.iter().any(|s| s == "--record") +}); + +pub fn record(app: &App, frame: &Frame) { + if !*RECORDING { + return; + } + // save frame + let path = app + .project_path() + .expect("failed to locate `project_path`") + // Capture all frames to a directory called `//nannou/simple_capture`. + .join("recordings") + // Name each file after the number of the frame. + .join(format!("{:05}", frame.nth())) + // The extension will be PNG. We also support tiff, bmp, gif, jpeg, webp and some others. + .with_extension("png"); + println!("frame: {} {:.05}", frame.nth(), frame.nth() as f32 / 60.0); + app.main_window().capture_frame(path); +} + +pub fn delta_t(app: &App) -> f64 { + if *RECORDING { + 1.0 / 60.0 + } else { + app.duration.since_prev_update.as_secs_f64() + } +}