1mod physics_objects;
2
3use pg_sdl::prelude::*;
4use pg_sdl::vector2::Vec2;
5use pg_sdl::widgets::Widgets;
6use physics_objects::{apply_gravity, Mass, Motor, Rod, Spring};
7use std::collections::HashMap;
8
9pub struct PhysicsApp {
11 masses: Vec<Mass>,
12 rods: Vec<Rod>,
13 springs: Vec<Spring>,
14 motors: Vec<Motor>,
15}
16
17impl App for PhysicsApp {
18 fn update(&mut self, delta: f32, input: &Input, widgets: &mut Widgets) -> bool {
19 if widgets
20 .get_mut::<Button>("play")
21 .unwrap()
22 .state
23 .is_pressed()
24 {
25 if widgets.get::<Slider>("speed").unwrap().get_value() == 0.0 {
26 widgets.get_mut::<Slider>("speed").unwrap().set_value(1.0);
27 widgets
28 .get_mut::<Button>("play")
29 .unwrap()
30 .set_text("Stop".to_string());
31 } else {
32 widgets.get_mut::<Slider>("speed").unwrap().set_value(0.0);
33 widgets
34 .get_mut::<Button>("play")
35 .unwrap()
36 .set_text("Start".to_string());
37 }
38 }
39
40 if input.mouse.left_button.is_pressed() {
41 let mouse_position = Vec2::from(input.mouse.position);
42 self.masses.iter().enumerate().for_each(|(index, mass)| {
43 if mass.collide_point(mouse_position) {
45 self.springs[0].change_end(index);
46 }
47 });
48 } else if input.mouse.left_button.is_released() {
49 self.springs[0].change_end(0);
50 }
51
52 apply_gravity(&mut self.masses);
53 self.springs.iter_mut().for_each(|spring| {
54 spring.apply_force(&mut self.masses);
55 });
56
57 let delta = delta * widgets.get_mut::<Slider>("speed").unwrap().get_value() * 10.0;
58
59 self.masses.iter_mut().for_each(|mass| mass.update(delta));
60 self.motors
61 .iter()
62 .for_each(|motor| motor.update(delta, &mut self.masses));
63
64 self.masses[0].position = Vec2::from(input.mouse.position);
65
66 let delta_length = self.masses[3].position - self.masses[4].position;
68 self.masses[3].position =
69 self.masses[4].position + delta_length.normalized() * self.rods[0].length;
70
71 let delta_velocity = self.masses[3].velocity - self.masses[4].velocity;
72 let v1 = delta_velocity.dot(delta_length.perpendicular());
73 self.masses[3].velocity = self.masses[4].velocity + delta_length.perpendicular() * v1;
74
75 true
76 }
77
78 fn draw(&mut self, canvas: &mut Canvas<Window>, _text_drawer_: &mut TextDrawer) {
79 self.motors
80 .iter()
81 .for_each(|motor| motor.draw(canvas, &self.masses));
82 self.masses.iter().for_each(|mass| mass.draw(canvas));
83 self.rods
84 .iter()
85 .for_each(|rod| rod.draw(canvas, &self.masses));
86 self.springs
87 .iter()
88 .for_each(|spring| spring.draw(canvas, &self.masses));
89 }
90}
91
92fn main() {
93 let my_app = &mut PhysicsApp {
94 masses: Vec::from([
95 Mass::new(Vec2::new(0.0, 0.0), 0.0, 0.0, Colors::BLACK, true),
96 Mass::new(Vec2::new(600.0, 400.0), 1.0, 20.0, Colors::ORANGE, true),
97 Mass::new(Vec2::new(600.0, 450.0), 1.0, 15.0, Colors::ORANGE, true),
98 Mass::new(Vec2::new(600.0, 550.0), 5.0, 25.0, Colors::RED, false),
99 Mass::new(Vec2::new(800.0, 200.0), 1.0, 20.0, Colors::GREEN, true),
100 Mass::new(Vec2::new(800.0, 650.0), 1.0, 20.0, Colors::MAGENTA, false),
101 ]),
102 rods: Vec::from([
103 Rod::new(3, 4, 250.0, 10.0, Colors::BROWN),
104 Rod::new(3, 5, 50.0, 10.0, Colors::BROWN),
105 ]),
106 springs: Vec::from([
107 Spring::new(0, 0, 1.0, 0.5, 0.0, 20.0, Colors::WHITE),
108 Spring::new(2, 3, 0.5, 0.2, 150.0, 40.0, Colors::BEIGE),
109 ]),
110 motors: Vec::from([Motor::new(1, 2, 0.4, Colors::LIGHT_GREY)]),
111 };
112
113 let mut app: PgSdl = PgSdl::init("Spring test", 1200, 720, Some(60), true, Colors::SKY_BLUE);
114 app.add_widgets(HashMap::from([
115 (
116 "play",
117 Box::new(Button::new(
118 Colors::ORANGE,
119 rect!(300, 35, 120, 50),
120 Some(9),
121 Some(Text::new("Start".to_string(), 18, None)),
122 )) as Box<dyn Widget>,
123 ),
124 (
125 "speed",
126 Box::new(Slider::new(
127 Colors::ORANGE,
128 rect!(500, 50, 200, 30),
129 Some(20),
130 SliderType::Continuous {
131 default_value: 0.0,
132 display: Some(Box::new(|value| format!("{:.2}", value))),
133 },
134 )),
135 ),
136 ]));
137 app.run(my_app);
138}