use gnuplot::Caption; use gnuplot::Graph; use gnuplot::AxesCommon; use gnuplot::Figure; use parsys::{FnThatComputesForceFromNode, integration::IntegrationMethod, point2d::Point2D}; use parsys::ParSys; use clap::Clap; #[derive(Clap)] #[clap(name = "parsys-example-attraction", version = "0.1", author = "Han Kruiger")] struct Opts { renderer: String, output: String, } fn main() { let opts = Opts::parse(); let (times_ee, distances_ee) = simulate_with(0.01, IntegrationMethod::ExplicitEuler, 60.0); let (times_rk4, distances_rk4) = simulate_with(0.01, IntegrationMethod::RungeKutta4, 60.0); let mut fg = Figure::new(); fg.set_terminal(&opts.renderer, &opts.output); fg.axes2d() .set_title("Distance vs time", &[]) .set_legend(Graph(0.5), Graph(0.9), &[], &[]) .set_x_label("t", &[]) .set_y_label("distance", &[]) .lines( ×_ee, &distances_ee, &[Caption("EE")], ) .lines( ×_rk4, &distances_rk4, &[Caption("RK4")], ); fg.show().unwrap(); } fn simulate_with(timestep: f32, method: IntegrationMethod, until: f32) -> (Vec, Vec) { let mut system = ParSys::new(timestep, method); let n1_id = system.add_node(Point2D(1.0, 0.0), 1.0); let n2_id = system.add_node(Point2D(0.0, 0.0), 1.0); let spring_force: FnThatComputesForceFromNode = |(pos1, _v1), (pos2, _v2), n, m| { // Don't try to apply this force to oneself. if n.id == m.id { return Point2D(0.0, 0.0); } // Vector from n to m let delta = *pos2 - *pos1; // Return that same vector (spring constant 1) delta }; system.add_force(|_, _, _| true, spring_force); let mut times = vec![]; let mut distances = vec![]; let mut t = 0.0; while t < until { times.push(t); distances.push(system.get_dist(n1_id, n2_id).unwrap()); system.step(); t += system.timestep; } times.push(t); distances.push(system.get_dist(n1_id, n2_id).unwrap()); (times, distances) }