summaryrefslogtreecommitdiff
path: root/playground/src
diff options
context:
space:
mode:
Diffstat (limited to 'playground/src')
-rw-r--r--playground/src/main.rs185
1 files changed, 185 insertions, 0 deletions
diff --git a/playground/src/main.rs b/playground/src/main.rs
new file mode 100644
index 0000000..1cb8198
--- /dev/null
+++ b/playground/src/main.rs
@@ -0,0 +1,185 @@
+use raylib::prelude::*;
+
+use physics::force::{drag::Drag, gravity::Gravity, spring::Spring};
+use physics::nalgebra::Point as PointBase;
+use physics::particle_system::{Particle, ParticleSystem};
+use physics::algebra::{Point, Scalar, Vector, N};
+use physics::renderer::Camera;
+use physics::solver::Solver;
+
+const SCALE: i32 = 5;
+fn screen_space_to_raylib(p: PointBase<Scalar, 2>, d: &RaylibDrawHandle) -> PointBase<i32, 2> {
+ PointBase::<i32, 2>::new(
+ d.get_screen_width() / 2 + p.x as i32 * SCALE,
+ d.get_screen_height() / 2 - p.y as i32 * SCALE,
+ )
+}
+fn raylib_to_screen_space(p: PointBase<i32, 2>, d: &RaylibDrawHandle) -> PointBase<Scalar, 2> {
+ PointBase::<Scalar, 2>::new(
+ ((p.x - d.get_screen_width() / 2) / SCALE) as Scalar,
+ ((p.y - d.get_screen_height() / 2) / -SCALE) as Scalar,
+ )
+}
+
+fn main() {
+ let dt = 0.0001;
+ let mut system = ParticleSystem {
+ particles: vec![
+ Particle::new(Point::origin(), 4.0),
+ Particle::new(Point::new(-30.0, 0.0), 15.0),
+ Particle::new(Point::new(20.0, 0.0), 30.0),
+ Particle::new(Point::new(5.0, 20.0), 50.0),
+ Particle::new(Point::origin(), 25.0),
+ Particle::new(Point::new(50.0, 0.0), 10.0),
+ Particle::new(Point::new(-100.0, -100.0), 100.0),
+ ],
+ constraints: vec![],
+ forces: vec![
+ Box::new(Gravity {
+ vector: Vector::y() * -9.8,
+ }),
+ Box::new(Drag { coefficient: 0.2 }),
+ Box::new(Spring {
+ particle_ids: [4, 2],
+ spring_constant: 0.75,
+ damping_constant: 0.1,
+ rest_length: 20.0,
+ }),
+ ],
+ t: 0.0,
+ };
+
+ system.add_anchor_constraint(0);
+ system.add_beam_constraint([0, 2]);
+ system.add_beam_constraint([1, 2]);
+ system.add_beam_constraint([1, 3]);
+ system.add_beam_constraint([2, 3]);
+ system.add_slider_constraint(5, Vector::x());
+ system.add_beam_constraint([5, 4]);
+
+ let mut selected_particle_id = None;
+
+ let (mut rl, thread) = raylib::init()
+ .size(640, 480)
+ .title("Physics simulation")
+ .resizable()
+ .build();
+
+ while !rl.window_should_close() {
+ let mut d = rl.begin_drawing(&thread);
+
+ d.clear_background(Color::WHITE);
+ d.draw_text(
+ format!(
+ "Time: {:03.3}\n\nFPS: {}\n\nKinetic energy: {:03.0}",
+ system.t,
+ d.get_fps(),
+ system.get_kinetic_energy()
+ )
+ .as_str(),
+ 12,
+ 12,
+ 20,
+ Color::BLACK,
+ );
+
+ let camera = Camera::new(Point::origin(), Vector::y(), Vector::x());
+
+ match selected_particle_id {
+ Some(particle_id) => {
+ // let p: &Particle = &system.particles[particle_id];
+
+ let mouse_point = PointBase::<i32, 2>::new(d.get_mouse_x(), d.get_mouse_y());
+ let screen_space = raylib_to_screen_space(mouse_point, &d);
+ let world_mouse = camera.screen_space_to_world(screen_space);
+
+ let mouse_particle_id = system.particles.len() - 1;
+ system.particles[mouse_particle_id].position = world_mouse;
+
+ system.forces.push(Box::new(Spring {
+ particle_ids: [mouse_particle_id, particle_id],
+ spring_constant: 0.99,
+ damping_constant: 0.99,
+ rest_length: 1.0,
+ }));
+ }
+ None => (),
+ }
+
+ for _ in 0..10 {
+ system.apply_forces();
+ system.enforce_constraints(dt);
+ system.step(dt);
+ }
+
+ match selected_particle_id {
+ Some(_) => {
+ system.forces.pop();
+ }
+ None => (),
+ }
+
+ if d.is_mouse_button_released(MouseButton::MOUSE_BUTTON_LEFT) {
+ selected_particle_id = None
+ }
+
+ for (particle_id, particle) in system.particles.iter().enumerate() {
+ let position =
+ screen_space_to_raylib(camera.world_to_screen_space(particle.position), &d);
+ let radius = particle.mass.powf(1.0 / (N as f64)) as f32 * SCALE as f32;
+
+ if d.is_mouse_button_down(MouseButton::MOUSE_BUTTON_LEFT)
+ && selected_particle_id.is_none()
+ {
+ let mouse_point =
+ PointBase::<f32, 2>::new(d.get_mouse_x() as f32, d.get_mouse_y() as f32);
+ let position = PointBase::<f32, 2>::new(position.x as f32, position.y as f32);
+ if (position - mouse_point).norm() < radius {
+ selected_particle_id = Some(particle_id)
+ }
+ }
+
+ d.draw_circle(
+ position.x,
+ position.y,
+ radius,
+ if selected_particle_id.is_some_and(|v| v == particle_id) {
+ Color::RED
+ } else {
+ Color::BLACK
+ },
+ );
+ }
+ for c in &system.constraints {
+ let particle_ids = c.get_particles();
+ if particle_ids.len() == 2 {
+ let a = screen_space_to_raylib(system.particles[particle_ids[0]].position, &d);
+ let b = screen_space_to_raylib(system.particles[particle_ids[1]].position, &d);
+
+ d.draw_line(a.x, a.y, b.x, b.y, Color::GRAY);
+ }
+ }
+
+ {
+ // Hard-coded spring
+ let particle_ids = [4, 2];
+ let a = screen_space_to_raylib(system.particles[particle_ids[0]].position, &d);
+ let b = screen_space_to_raylib(system.particles[particle_ids[1]].position, &d);
+ d.draw_line(a.x, a.y, b.x, b.y, Color::GREEN);
+ }
+ {
+ // Hard-coded spring
+ match selected_particle_id {
+ Some(id) => {
+ let mouse_particle_id = system.particles.len() - 1;
+ let a = screen_space_to_raylib(system.particles[id].position, &d);
+ let b =
+ screen_space_to_raylib(system.particles[mouse_particle_id].position, &d);
+
+ d.draw_line(a.x, a.y, b.x, b.y, Color::GREEN);
+ }
+ None => (),
+ }
+ }
+ }
+}