From 2c457ae91f220f931fb3e5664492738e4723eda2 Mon Sep 17 00:00:00 2001 From: Raptorox <70806316+Raptorox@users.noreply.github.com> Date: Mon, 19 May 2025 12:43:45 +0200 Subject: [PATCH 01/10] finish functioning --- src/link.rs | 4 +-- src/main.rs | 88 ++++++++++++++++++++++++++++++----------------------- 2 files changed, 52 insertions(+), 40 deletions(-) diff --git a/src/link.rs b/src/link.rs index 342e6ee..6e4a79c 100644 --- a/src/link.rs +++ b/src/link.rs @@ -21,9 +21,9 @@ impl Link { let vec = self.p2.borrow().pos - self.p1.borrow().pos; let vec_len = (vec.x*vec.x + vec.y*vec.y).sqrt(); let vec_norm = vec/vec_len; - let vec_scaled = vec_norm * (self.length-vec_len) * 9.; + let vec_scaled = vec_norm * (self.length-vec_len) * 99.; self.p1.borrow_mut().apply_force(-vec_scaled); self.p2.borrow_mut().apply_force(vec_scaled); } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 9675dae..6119ff2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,14 +13,30 @@ use link::Link; //const GRAVITY: f32 = 100.; -fn populate_particles(mut particles: Vec>>, num: u32) { - for i in 0..=num { - particles.push(Rc::new(RefCell::new(Particle::new(Vector2f::new(100. * i as f32, 300.))))); +fn populate_particles(particles: &mut Vec>>, num: u32) { + for i in 0..num { + particles.push(Rc::new(RefCell::new(Particle::new(Vector2f::new(300. + 100. * i as f32, 300.))))); } } -fn populate_links(links: Vec, particles: Vec>>) { +fn populate_circles(circles: &mut Vec, num: u32, rad: f32, pts: usize) { + for _ in 0..num { + let mut circle = CircleShape::new(rad, pts); + circle.set_origin((rad, rad)); + circles.push(circle); + } +} +fn populate_links(links: &mut Vec, particles: &Vec>>, num: u32) { + for i in 0..(num-1) { + let p1 = &particles[i as usize]; + let p2 = &particles[(i as usize)+1]; + let vec = p2.borrow().pos - p1.borrow().pos; + let dist = (vec.x*vec.x + vec.y*vec.y).sqrt(); + let link = Link::new(Rc::clone(p1), Rc::clone(p2), dist); + + links.push(link); + } } fn apply_forces(particles: Vec>>) { @@ -32,16 +48,24 @@ fn apply_forces(particles: Vec>>) { } } -fn update_particles(particles: Vec>>, dt: f32) { +fn update_particles(particles: &Vec>>, dt: f32) { for particle in particles { particle.borrow_mut().update(dt); } } -fn solve_links(links: Vec) { - for mut link in links { link.solve(); } +fn solve_links(links: &mut Vec) { + for link in links { link.solve(); } } -fn update_positions(circles: Vec) { +fn update_positions(circles: &mut Vec, particles: &Vec>>) { + for (index, circle) in circles.iter_mut().enumerate() { + circle.set_position(particles[index].borrow().pos); + } +} +fn draw_all(window: &mut RenderWindow, circles: &Vec, /* links: Vec */ ) { + for circle in circles { + window.draw(circle); + } } fn main() -> SfResult<()> { @@ -51,27 +75,23 @@ fn main() -> SfResult<()> { Style::CLOSE, &Default::default() )?; - window.set_framerate_limit(60); + window.set_framerate_limit(144); let mut clock = Clock::start()?; + let particle_count: u32 = 6; + let mut particles: Vec>> = vec![]; - populate_particles(particles, 3); - let particle1 = Rc::new(RefCell::new(Particle::new(Vector2f::new(200., 300.)))); - let particle2 = Rc::new(RefCell::new(Particle::new(Vector2f::new(300., 300.)))); - let particle3 = Rc::new(RefCell::new(Particle::new(Vector2f::new(400., 300.)))); + populate_particles(&mut particles, particle_count); - let mut link1 = Link::new(Rc::clone(&particle1), Rc::clone(&particle2), 100.); - let mut link2 = Link::new(Rc::clone(&particle2), Rc::clone(&particle3), 100.); - let mut link3 = Link::new(Rc::clone(&particle1), Rc::clone(&particle3), 200.); + let mut links: Vec = vec![]; + populate_links(&mut links, &particles, particle_count); + // let mut link3 = Link::new(Rc::clone(&(particles[0])), Rc::clone(&(particles[2])), 200.); - let radius = 32.; - let mut circle1 = CircleShape::new(radius, 100); - let mut circle2 = CircleShape::new(radius, 100); - let mut circle3 = CircleShape::new(radius, 100); - circle1.set_origin((radius, radius)); - circle2.set_origin((radius, radius)); - circle3.set_origin((radius, radius)); + let radius: f32 = 32.; + let point_count: usize = 100; + let mut circles: Vec = vec![]; + populate_circles(&mut circles, particle_count, radius, point_count); while window.is_open() { while let Some(event) = window.poll_event() { @@ -84,32 +104,24 @@ fn main() -> SfResult<()> { if mouse::Button::is_pressed(mouse::Button::Left) { let mouse_pos = window.mouse_position(); let mouse_coords = window.map_pixel_to_coords_current_view(mouse_pos); - let p_pos = particle1.borrow().pos; - particle2.borrow_mut().apply_force(mouse_coords - p_pos); + let p_pos = particles[0].borrow().pos; + particles[0].borrow_mut().apply_force((mouse_coords - p_pos)*8.); } let dt = clock.restart().as_seconds(); //particle.apply_force(Vector2f::new(0., GRAVITY)); - particle1.borrow_mut().update(dt); - particle2.borrow_mut().update(dt); - particle3.borrow_mut().update(dt); + update_particles(&particles, dt); - link1.solve(); - link2.solve(); - link3.solve(); + solve_links(&mut links); - circle1.set_position(particle1.borrow().pos); - circle2.set_position(particle2.borrow().pos); - circle3.set_position(particle3.borrow().pos); + update_positions(&mut circles, &particles); window.clear(Color::BLACK); - window.draw(&circle1); - window.draw(&circle2); - window.draw(&circle3); + draw_all(&mut window, &circles); window.display(); } Ok(()) -} \ No newline at end of file +} From 8c713df698c3faa444036f0d47a90fd72beffdbe Mon Sep 17 00:00:00 2001 From: Raptorox <70806316+Raptorox@users.noreply.github.com> Date: Thu, 22 May 2025 10:41:38 +0200 Subject: [PATCH 02/10] make cloth shape and cry --- src/link.rs | 13 ++++++++++--- src/main.rs | 44 +++++++++++++++++++++++++++++++------------- src/particle.rs | 14 ++++++++++---- 3 files changed, 51 insertions(+), 20 deletions(-) diff --git a/src/link.rs b/src/link.rs index 6e4a79c..41c9962 100644 --- a/src/link.rs +++ b/src/link.rs @@ -21,9 +21,16 @@ impl Link { let vec = self.p2.borrow().pos - self.p1.borrow().pos; let vec_len = (vec.x*vec.x + vec.y*vec.y).sqrt(); let vec_norm = vec/vec_len; - let vec_scaled = vec_norm * (self.length-vec_len) * 99.; - self.p1.borrow_mut().apply_force(-vec_scaled); - self.p2.borrow_mut().apply_force(vec_scaled); + let max_stretch = 50.; + let clamped_len = vec_len.min(self.length + max_stretch); + + let displacement = clamped_len - vec_len; + let stiffness = 99.; + let vec_scaled = vec_norm * displacement * stiffness; + + let damping = 0.98; + self.p1.borrow_mut().apply_force(-vec_scaled * damping); + self.p2.borrow_mut().apply_force(vec_scaled * damping); } } diff --git a/src/main.rs b/src/main.rs index 6119ff2..d4d1d57 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,11 +11,15 @@ use particle::Particle; mod link; use link::Link; -//const GRAVITY: f32 = 100.; +const GRAVITY: f32 = 100.; -fn populate_particles(particles: &mut Vec>>, num: u32) { +fn populate_particles(particles: &mut Vec>>, num: u32, cols: u32) { for i in 0..num { - particles.push(Rc::new(RefCell::new(Particle::new(Vector2f::new(300. + 100. * i as f32, 300.))))); + let x_pos = 200. + 50. * (i % cols) as f32; + let y_pos = 100. + 50. * (i / cols) as f32; + let immovable = i < cols; + let particle = Particle::new(Vector2f::new(x_pos, y_pos), immovable); + particles.push(Rc::new(RefCell::new(particle))); } } @@ -39,11 +43,16 @@ fn populate_links(links: &mut Vec, particles: &Vec>>, } } -fn apply_forces(particles: Vec>>) { +/*fn populate_lines(lines: &mut Vec, circles: &Vec, num: u32) { + for i in 0..num { + let rect = RectangleShape::with_size(Vector2f::new(100., 100.)); + } +}*/ + +fn apply_forces(particles: &Vec>>) { for particle in particles { let mut borrowed = particle.borrow_mut(); - // borrowed.apply_force(Vector2f::new(0., GRAVITY)); - borrowed.apply_force(Vector2f::new(1., 1.)); + borrowed.apply_force(Vector2f::new(0., GRAVITY)); } } @@ -62,10 +71,14 @@ fn update_positions(circles: &mut Vec, particles: &Vec, /* links: Vec */ ) { +fn draw_all(window: &mut RenderWindow, circles: &Vec, /* lines: &Vec */) { for circle in circles { window.draw(circle); } + + // for line in lines { + // window.draw(line); + // } } fn main() -> SfResult<()> { @@ -75,14 +88,15 @@ fn main() -> SfResult<()> { Style::CLOSE, &Default::default() )?; - window.set_framerate_limit(144); + window.set_framerate_limit(60); let mut clock = Clock::start()?; - let particle_count: u32 = 6; + let particle_count: u32 = 60; + let column_count: u32 = 10; let mut particles: Vec>> = vec![]; - populate_particles(&mut particles, particle_count); + populate_particles(&mut particles, particle_count, column_count); let mut links: Vec = vec![]; populate_links(&mut links, &particles, particle_count); @@ -93,6 +107,9 @@ fn main() -> SfResult<()> { let mut circles: Vec = vec![]; populate_circles(&mut circles, particle_count, radius, point_count); + // let mut lines: Vec = vec![]; + // populate_lines(&mut lines, &circles, particle_count); + while window.is_open() { while let Some(event) = window.poll_event() { match event { @@ -104,14 +121,15 @@ fn main() -> SfResult<()> { if mouse::Button::is_pressed(mouse::Button::Left) { let mouse_pos = window.mouse_position(); let mouse_coords = window.map_pixel_to_coords_current_view(mouse_pos); - let p_pos = particles[0].borrow().pos; - particles[0].borrow_mut().apply_force((mouse_coords - p_pos)*8.); + let p_pos = particles[10].borrow().pos; + particles[10].borrow_mut().apply_force((mouse_coords - p_pos)*4.); } let dt = clock.restart().as_seconds(); //particle.apply_force(Vector2f::new(0., GRAVITY)); + apply_forces(&particles); update_particles(&particles, dt); solve_links(&mut links); @@ -119,7 +137,7 @@ fn main() -> SfResult<()> { update_positions(&mut circles, &particles); window.clear(Color::BLACK); - draw_all(&mut window, &circles); + draw_all(&mut window, &circles, /* &lines */); window.display(); } diff --git a/src/particle.rs b/src/particle.rs index 0998294..6ad602f 100644 --- a/src/particle.rs +++ b/src/particle.rs @@ -3,15 +3,19 @@ use sfml::system::Vector2f; pub struct Particle { pub pos: Vector2f, prev_pos: Vector2f, - accel: Vector2f + accel: Vector2f, + + immovable: bool } impl Particle { - pub fn new(pos: Vector2f) -> Self { + pub fn new(pos: Vector2f, immovable: bool) -> Self { Particle { pos: pos, prev_pos: pos, - accel: Vector2f::default() + accel: Vector2f::default(), + + immovable: immovable } } @@ -20,7 +24,9 @@ impl Particle { } pub fn update(&mut self, dt: f32) { - let vel = (self.pos - self.prev_pos) * 0.99; + if self.immovable { return; } + let damping = 0.99; + let vel = (self.pos - self.prev_pos) * damping; let new_pos = self.pos + vel + self.accel * (dt * dt); self.prev_pos = self.pos; From 57e28360a0d4dc29f0ceebc9be76c4af6289290f Mon Sep 17 00:00:00 2001 From: Raptorox <70806316+Raptorox@users.noreply.github.com> Date: Thu, 22 May 2025 12:39:21 +0200 Subject: [PATCH 03/10] change link length to static size --- src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index d4d1d57..c4ac27c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,9 +35,9 @@ fn populate_links(links: &mut Vec, particles: &Vec>>, for i in 0..(num-1) { let p1 = &particles[i as usize]; let p2 = &particles[(i as usize)+1]; - let vec = p2.borrow().pos - p1.borrow().pos; - let dist = (vec.x*vec.x + vec.y*vec.y).sqrt(); - let link = Link::new(Rc::clone(p1), Rc::clone(p2), dist); + //let vec = p2.borrow().pos - p1.borrow().pos; + //let dist = (vec.x*vec.x + vec.y*vec.y).sqrt(); + let link = Link::new(Rc::clone(p1), Rc::clone(p2), 100.); links.push(link); } From de5f28b4f706879d7cee1a29da3c29c99910b846 Mon Sep 17 00:00:00 2001 From: Raptorox <70806316+Raptorox@users.noreply.github.com> Date: Thu, 5 Jun 2025 17:55:35 +0200 Subject: [PATCH 04/10] update deps; tweak constants --- Cargo.lock | 8 ++-- src/link.rs | 33 ++++++++-------- src/main.rs | 103 ++++++++++++++++++++++++++++++++++-------------- src/particle.rs | 12 +++--- 4 files changed, 101 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9a6ed36..df125b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,15 +10,15 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "cc" -version = "1.2.20" +version = "1.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" +checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7" dependencies = [ "shlex", ] diff --git a/src/link.rs b/src/link.rs index 41c9962..fe34657 100644 --- a/src/link.rs +++ b/src/link.rs @@ -1,36 +1,35 @@ use crate::particle::Particle; -use std::rc::Rc; use std::cell::RefCell; +use std::rc::Rc; pub struct Link { p1: Rc>, p2: Rc>, - length: f32 + length: f32, } impl Link { pub fn new(p1: Rc>, p2: Rc>, length: f32) -> Self { - Link { - p1: p1, - p2: p2, - length: length - } + Link { p1, p2, length } } pub fn solve(&mut self) { let vec = self.p2.borrow().pos - self.p1.borrow().pos; - let vec_len = (vec.x*vec.x + vec.y*vec.y).sqrt(); - let vec_norm = vec/vec_len; + let vec_len = (vec.x * vec.x + vec.y * vec.y).sqrt(); - let max_stretch = 50.; - let clamped_len = vec_len.min(self.length + max_stretch); + if vec_len > self.length { + let vec_norm = vec / vec_len; - let displacement = clamped_len - vec_len; - let stiffness = 99.; - let vec_scaled = vec_norm * displacement * stiffness; + let max_stretch = 0.5; + let clamped_len = vec_len.min(self.length + max_stretch); - let damping = 0.98; - self.p1.borrow_mut().apply_force(-vec_scaled * damping); - self.p2.borrow_mut().apply_force(vec_scaled * damping); + let displacement = clamped_len - vec_len; + let stiffness = 999.; + let vec_scaled = vec_norm * displacement * stiffness; + + let damping = 0.99; + self.p1.borrow_mut().apply_force(-vec_scaled * damping); + self.p2.borrow_mut().apply_force(vec_scaled * damping); + } } } diff --git a/src/main.rs b/src/main.rs index c4ac27c..afee280 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ -use sfml::graphics::*; -use sfml::window::*; use sfml::SfResult; -use sfml::system::{Vector2f, Clock}; -use std::rc::Rc; +use sfml::graphics::*; +use sfml::system::{Clock, Vector2f}; +use sfml::window::*; use std::cell::RefCell; +use std::rc::Rc; mod particle; use particle::Particle; @@ -15,8 +15,8 @@ const GRAVITY: f32 = 100.; fn populate_particles(particles: &mut Vec>>, num: u32, cols: u32) { for i in 0..num { - let x_pos = 200. + 50. * (i % cols) as f32; - let y_pos = 100. + 50. * (i / cols) as f32; + let x_pos = 150. + 20. * (i % cols) as f32; + let y_pos = 80. + 20. * (i / cols) as f32; let immovable = i < cols; let particle = Particle::new(Vector2f::new(x_pos, y_pos), immovable); particles.push(Rc::new(RefCell::new(particle))); @@ -31,16 +31,41 @@ fn populate_circles(circles: &mut Vec, num: u32, rad: f32, pts: usi } } -fn populate_links(links: &mut Vec, particles: &Vec>>, num: u32) { - for i in 0..(num-1) { +/*fn populate_links(links: &mut Vec, particles: &[Rc>], num: u32) { + for i in 0..(num - 1) { let p1 = &particles[i as usize]; - let p2 = &particles[(i as usize)+1]; + let p2 = &particles[(i as usize) + 1]; //let vec = p2.borrow().pos - p1.borrow().pos; //let dist = (vec.x*vec.x + vec.y*vec.y).sqrt(); let link = Link::new(Rc::clone(p1), Rc::clone(p2), 100.); links.push(link); } +}*/ + +fn populate_links( + links: &mut Vec, + particles: &[Rc>], + width: usize, + height: usize, +) { + for y in 0..height { + for x in 0..width { + let i = y * width + x; + + if x < width - 1 { + let right = i + 1; + let link = Link::new(Rc::clone(&particles[i]), Rc::clone(&particles[right]), 20.); + links.push(link); + } + + if y < height - 1 { + let below = i + width; + let link = Link::new(Rc::clone(&particles[i]), Rc::clone(&particles[below]), 20.); + links.push(link); + } + } + } } /*fn populate_lines(lines: &mut Vec, circles: &Vec, num: u32) { @@ -53,25 +78,31 @@ fn apply_forces(particles: &Vec>>) { for particle in particles { let mut borrowed = particle.borrow_mut(); borrowed.apply_force(Vector2f::new(0., GRAVITY)); - } } fn update_particles(particles: &Vec>>, dt: f32) { - for particle in particles { particle.borrow_mut().update(dt); } + for particle in particles { + particle.borrow_mut().update(dt); + } } fn solve_links(links: &mut Vec) { - for link in links { link.solve(); } + for link in links { + link.solve(); + } } -fn update_positions(circles: &mut Vec, particles: &Vec>>) { +fn update_positions(circles: &mut Vec, particles: &[Rc>]) { for (index, circle) in circles.iter_mut().enumerate() { circle.set_position(particles[index].borrow().pos); } } -fn draw_all(window: &mut RenderWindow, circles: &Vec, /* lines: &Vec */) { +fn draw_all( + window: &mut RenderWindow, + circles: &Vec, /* lines: &Vec */ +) { for circle in circles { window.draw(circle); } @@ -82,27 +113,31 @@ fn draw_all(window: &mut RenderWindow, circles: &Vec, /* lines: &Ve } fn main() -> SfResult<()> { - let mut window = RenderWindow::new( - (800, 600), - "Verlet", - Style::CLOSE, - &Default::default() - )?; + let mut window = RenderWindow::new((800, 600), "Verlet", Style::CLOSE, &Default::default())?; window.set_framerate_limit(60); let mut clock = Clock::start()?; - let particle_count: u32 = 60; - let column_count: u32 = 10; + let mouse_pos_prev = window.mouse_position(); + let mut mouse_coords_prev = window.map_pixel_to_coords_current_view(mouse_pos_prev); + + let particle_count: u32 = 600; + let column_count: u32 = 30; + let row_count: u32 = particle_count / column_count; let mut particles: Vec>> = vec![]; populate_particles(&mut particles, particle_count, column_count); let mut links: Vec = vec![]; - populate_links(&mut links, &particles, particle_count); + populate_links( + &mut links, + &particles, + column_count as usize, + row_count as usize, + ); // let mut link3 = Link::new(Rc::clone(&(particles[0])), Rc::clone(&(particles[2])), 200.); - let radius: f32 = 32.; + let radius: f32 = 8.; let point_count: usize = 100; let mut circles: Vec = vec![]; populate_circles(&mut circles, particle_count, radius, point_count); @@ -112,17 +147,27 @@ fn main() -> SfResult<()> { while window.is_open() { while let Some(event) = window.poll_event() { + #[allow(clippy::single_match)] match event { Event::Closed => window.close(), _ => {} } } + let mouse_pos = window.mouse_position(); + let mouse_coords = window.map_pixel_to_coords_current_view(mouse_pos); + let mouse_vel = mouse_coords - mouse_coords_prev; + mouse_coords_prev = mouse_coords; + if mouse::Button::is_pressed(mouse::Button::Left) { - let mouse_pos = window.mouse_position(); - let mouse_coords = window.map_pixel_to_coords_current_view(mouse_pos); - let p_pos = particles[10].borrow().pos; - particles[10].borrow_mut().apply_force((mouse_coords - p_pos)*4.); + for particle in &particles { + let p_pos = particle.borrow().pos; + let dist_vec = mouse_coords - p_pos; + let dist = (dist_vec.x * dist_vec.x + dist_vec.y * dist_vec.y).sqrt(); + if dist < 20. { + particle.borrow_mut().apply_force(mouse_vel * 128.); + } + } } let dt = clock.restart().as_seconds(); @@ -137,7 +182,7 @@ fn main() -> SfResult<()> { update_positions(&mut circles, &particles); window.clear(Color::BLACK); - draw_all(&mut window, &circles, /* &lines */); + draw_all(&mut window, &circles /* &lines */); window.display(); } diff --git a/src/particle.rs b/src/particle.rs index 6ad602f..f82c886 100644 --- a/src/particle.rs +++ b/src/particle.rs @@ -5,26 +5,28 @@ pub struct Particle { prev_pos: Vector2f, accel: Vector2f, - immovable: bool + immovable: bool, } impl Particle { pub fn new(pos: Vector2f, immovable: bool) -> Self { Particle { - pos: pos, + pos, prev_pos: pos, accel: Vector2f::default(), - immovable: immovable + immovable, } } pub fn apply_force(&mut self, force: Vector2f) { + if self.immovable { + return; + } self.accel += force; } pub fn update(&mut self, dt: f32) { - if self.immovable { return; } let damping = 0.99; let vel = (self.pos - self.prev_pos) * damping; let new_pos = self.pos + vel + self.accel * (dt * dt); @@ -33,4 +35,4 @@ impl Particle { self.pos = new_pos; self.accel = Vector2f::default(); } -} \ No newline at end of file +} From 2feaed6fd525c0f595b8b2d2ea3559e768652d18 Mon Sep 17 00:00:00 2001 From: Raptorox <70806316+Raptorox@users.noreply.github.com> Date: Fri, 1 Aug 2025 18:08:52 +0200 Subject: [PATCH 05/10] update deps --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df125b1..96d42ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" @@ -16,9 +16,9 @@ checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "cc" -version = "1.2.24" +version = "1.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7" +checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" dependencies = [ "shlex", ] @@ -34,9 +34,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.172" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libflac-sys" From 1eaa48d511d7fdf0c48bfa7595effbaee849057a Mon Sep 17 00:00:00 2001 From: Raptorox <70806316+Raptorox@users.noreply.github.com> Date: Fri, 1 Aug 2025 18:09:08 +0200 Subject: [PATCH 06/10] add constants, delegate vector length calculation --- src/link.rs | 8 +++----- src/main.rs | 34 +++++++++++++++------------------- src/math.rs | 5 +++++ 3 files changed, 23 insertions(+), 24 deletions(-) create mode 100644 src/math.rs diff --git a/src/link.rs b/src/link.rs index fe34657..fefbd2b 100644 --- a/src/link.rs +++ b/src/link.rs @@ -1,3 +1,4 @@ +use crate::math; use crate::particle::Particle; use std::cell::RefCell; use std::rc::Rc; @@ -15,15 +16,12 @@ impl Link { pub fn solve(&mut self) { let vec = self.p2.borrow().pos - self.p1.borrow().pos; - let vec_len = (vec.x * vec.x + vec.y * vec.y).sqrt(); + let vec_len = math::vec_len(vec); if vec_len > self.length { let vec_norm = vec / vec_len; - let max_stretch = 0.5; - let clamped_len = vec_len.min(self.length + max_stretch); - - let displacement = clamped_len - vec_len; + let displacement = self.length - vec_len; let stiffness = 999.; let vec_scaled = vec_norm * displacement * stiffness; diff --git a/src/main.rs b/src/main.rs index afee280..bbc996f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,18 +5,24 @@ use sfml::window::*; use std::cell::RefCell; use std::rc::Rc; +mod math; + mod particle; use particle::Particle; mod link; use link::Link; +const WIN_WIDTH: u32 = 800; +const WIN_HEIGHT: u32 = 600; + const GRAVITY: f32 = 100.; fn populate_particles(particles: &mut Vec>>, num: u32, cols: u32) { for i in 0..num { - let x_pos = 150. + 20. * (i % cols) as f32; - let y_pos = 80. + 20. * (i / cols) as f32; + const OFFSET: u32 = 20; + let x_pos = (WIN_WIDTH/2 - cols/2*OFFSET + OFFSET * (i % cols)) as f32; + let y_pos = (WIN_HEIGHT/2 - (num/cols)/2*OFFSET + OFFSET * (i / cols)) as f32; let immovable = i < cols; let particle = Particle::new(Vector2f::new(x_pos, y_pos), immovable); particles.push(Rc::new(RefCell::new(particle))); @@ -31,18 +37,6 @@ fn populate_circles(circles: &mut Vec, num: u32, rad: f32, pts: usi } } -/*fn populate_links(links: &mut Vec, particles: &[Rc>], num: u32) { - for i in 0..(num - 1) { - let p1 = &particles[i as usize]; - let p2 = &particles[(i as usize) + 1]; - //let vec = p2.borrow().pos - p1.borrow().pos; - //let dist = (vec.x*vec.x + vec.y*vec.y).sqrt(); - let link = Link::new(Rc::clone(p1), Rc::clone(p2), 100.); - - links.push(link); - } -}*/ - fn populate_links( links: &mut Vec, particles: &[Rc>], @@ -55,13 +49,15 @@ fn populate_links( if x < width - 1 { let right = i + 1; - let link = Link::new(Rc::clone(&particles[i]), Rc::clone(&particles[right]), 20.); + let dist = math::vec_len(particles[i].borrow().pos - particles[right].borrow().pos); + let link = Link::new(Rc::clone(&particles[i]), Rc::clone(&particles[right]), dist); links.push(link); } if y < height - 1 { let below = i + width; - let link = Link::new(Rc::clone(&particles[i]), Rc::clone(&particles[below]), 20.); + let dist = math::vec_len(particles[i].borrow().pos - particles[below].borrow().pos); + let link = Link::new(Rc::clone(&particles[i]), Rc::clone(&particles[below]), dist); links.push(link); } } @@ -113,7 +109,7 @@ fn draw_all( } fn main() -> SfResult<()> { - let mut window = RenderWindow::new((800, 600), "Verlet", Style::CLOSE, &Default::default())?; + let mut window = RenderWindow::new((WIN_WIDTH, WIN_HEIGHT), "Verlet", Style::CLOSE, &Default::default())?; window.set_framerate_limit(60); let mut clock = Clock::start()?; @@ -147,7 +143,6 @@ fn main() -> SfResult<()> { while window.is_open() { while let Some(event) = window.poll_event() { - #[allow(clippy::single_match)] match event { Event::Closed => window.close(), _ => {} @@ -163,7 +158,7 @@ fn main() -> SfResult<()> { for particle in &particles { let p_pos = particle.borrow().pos; let dist_vec = mouse_coords - p_pos; - let dist = (dist_vec.x * dist_vec.x + dist_vec.y * dist_vec.y).sqrt(); + let dist = math::vec_len(dist_vec); if dist < 20. { particle.borrow_mut().apply_force(mouse_vel * 128.); } @@ -174,6 +169,7 @@ fn main() -> SfResult<()> { //particle.apply_force(Vector2f::new(0., GRAVITY)); + apply_forces(&particles); update_particles(&particles, dt); diff --git a/src/math.rs b/src/math.rs new file mode 100644 index 0000000..9150b0d --- /dev/null +++ b/src/math.rs @@ -0,0 +1,5 @@ +use sfml::system::Vector2f; + +pub fn vec_len(vec: Vector2f) -> f32 { + (vec.x * vec.x + vec.y * vec.y).sqrt() +} \ No newline at end of file From 493af1e71cd27a026e0a18f82e7862c86b15ee74 Mon Sep 17 00:00:00 2001 From: Raptorox <70806316+Raptorox@users.noreply.github.com> Date: Fri, 1 Aug 2025 18:18:31 +0200 Subject: [PATCH 07/10] simplify particle vectors --- src/main.rs | 8 +++----- src/particle.rs | 7 ++++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index bbc996f..89472c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ use link::Link; const WIN_WIDTH: u32 = 800; const WIN_HEIGHT: u32 = 600; -const GRAVITY: f32 = 100.; +const GRAVITY: Vector2f = Vector2f::new(0., 100.); fn populate_particles(particles: &mut Vec>>, num: u32, cols: u32) { for i in 0..num { @@ -24,7 +24,7 @@ fn populate_particles(particles: &mut Vec>>, num: u32, cols let x_pos = (WIN_WIDTH/2 - cols/2*OFFSET + OFFSET * (i % cols)) as f32; let y_pos = (WIN_HEIGHT/2 - (num/cols)/2*OFFSET + OFFSET * (i / cols)) as f32; let immovable = i < cols; - let particle = Particle::new(Vector2f::new(x_pos, y_pos), immovable); + let particle = Particle::new((x_pos, y_pos), immovable); particles.push(Rc::new(RefCell::new(particle))); } } @@ -73,7 +73,7 @@ fn populate_links( fn apply_forces(particles: &Vec>>) { for particle in particles { let mut borrowed = particle.borrow_mut(); - borrowed.apply_force(Vector2f::new(0., GRAVITY)); + borrowed.apply_force(GRAVITY); } } @@ -167,8 +167,6 @@ fn main() -> SfResult<()> { let dt = clock.restart().as_seconds(); - //particle.apply_force(Vector2f::new(0., GRAVITY)); - apply_forces(&particles); update_particles(&particles, dt); diff --git a/src/particle.rs b/src/particle.rs index f82c886..e7faa0c 100644 --- a/src/particle.rs +++ b/src/particle.rs @@ -9,7 +9,8 @@ pub struct Particle { } impl Particle { - pub fn new(pos: Vector2f, immovable: bool) -> Self { + pub fn new>(pos: P, immovable: bool) -> Self { + let pos = pos.into(); Particle { pos, prev_pos: pos, @@ -19,11 +20,11 @@ impl Particle { } } - pub fn apply_force(&mut self, force: Vector2f) { + pub fn apply_force>(&mut self, force: F) { if self.immovable { return; } - self.accel += force; + self.accel += force.into(); } pub fn update(&mut self, dt: f32) { From 05abba5afe70876e67b4f86c124fe4098f9db3b0 Mon Sep 17 00:00:00 2001 From: Raptorox <70806316+Raptorox@users.noreply.github.com> Date: Fri, 1 Aug 2025 19:47:42 +0200 Subject: [PATCH 08/10] add substepping, update solver --- src/link.rs | 8 +++----- src/main.rs | 29 ++++++++++++++++++++--------- src/particle.rs | 22 ++++++++++++++-------- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/link.rs b/src/link.rs index fefbd2b..75bf156 100644 --- a/src/link.rs +++ b/src/link.rs @@ -22,12 +22,10 @@ impl Link { let vec_norm = vec / vec_len; let displacement = self.length - vec_len; - let stiffness = 999.; - let vec_scaled = vec_norm * displacement * stiffness; + let vec_scaled = vec_norm * displacement / 2.; - let damping = 0.99; - self.p1.borrow_mut().apply_force(-vec_scaled * damping); - self.p2.borrow_mut().apply_force(vec_scaled * damping); + self.p1.borrow_mut().apply_vel(-vec_scaled); + self.p2.borrow_mut().apply_vel(vec_scaled); } } } diff --git a/src/main.rs b/src/main.rs index 89472c5..1f2673d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,9 @@ use link::Link; const WIN_WIDTH: u32 = 800; const WIN_HEIGHT: u32 = 600; -const GRAVITY: Vector2f = Vector2f::new(0., 100.); +const GRAVITY: Vector2f = Vector2f::new(0., 1500.); + +const SUBSTEP_COUNT: u32 = 16; fn populate_particles(particles: &mut Vec>>, num: u32, cols: u32) { for i in 0..num { @@ -72,8 +74,10 @@ fn populate_links( fn apply_forces(particles: &Vec>>) { for particle in particles { + let velocity = particle.borrow().vel; let mut borrowed = particle.borrow_mut(); borrowed.apply_force(GRAVITY); + borrowed.apply_force(-(velocity * 0.5)); } } @@ -89,6 +93,12 @@ fn solve_links(links: &mut Vec) { } } +fn update_particle_derivatives(particles: &Vec>>, dt: f32) { + for particle in particles { + particle.borrow_mut().update_derivatives(dt); + } +} + fn update_positions(circles: &mut Vec, particles: &[Rc>]) { for (index, circle) in circles.iter_mut().enumerate() { circle.set_position(particles[index].borrow().pos); @@ -160,20 +170,21 @@ fn main() -> SfResult<()> { let dist_vec = mouse_coords - p_pos; let dist = math::vec_len(dist_vec); if dist < 20. { - particle.borrow_mut().apply_force(mouse_vel * 128.); + particle.borrow_mut().apply_force(mouse_vel * 8000.); } } } let dt = clock.restart().as_seconds(); - + let substep_dt = dt/SUBSTEP_COUNT as f32; - apply_forces(&particles); - update_particles(&particles, dt); - - solve_links(&mut links); - - update_positions(&mut circles, &particles); + for _ in 0..SUBSTEP_COUNT { + apply_forces(&particles); + update_particles(&particles, substep_dt); + solve_links(&mut links); + update_particle_derivatives(&particles, substep_dt); + update_positions(&mut circles, &particles); + } window.clear(Color::BLACK); draw_all(&mut window, &circles /* &lines */); diff --git a/src/particle.rs b/src/particle.rs index e7faa0c..5b39883 100644 --- a/src/particle.rs +++ b/src/particle.rs @@ -3,6 +3,7 @@ use sfml::system::Vector2f; pub struct Particle { pub pos: Vector2f, prev_pos: Vector2f, + pub vel: Vector2f, accel: Vector2f, immovable: bool, @@ -14,6 +15,7 @@ impl Particle { Particle { pos, prev_pos: pos, + vel: Vector2f::default(), accel: Vector2f::default(), immovable, @@ -21,19 +23,23 @@ impl Particle { } pub fn apply_force>(&mut self, force: F) { - if self.immovable { - return; - } self.accel += force.into(); } pub fn update(&mut self, dt: f32) { - let damping = 0.99; - let vel = (self.pos - self.prev_pos) * damping; - let new_pos = self.pos + vel + self.accel * (dt * dt); - + if self.immovable {return} self.prev_pos = self.pos; - self.pos = new_pos; + self.vel += self.accel * dt; + self.pos += self.vel * dt; + } + + pub fn update_derivatives(&mut self, dt: f32) { + self.vel = (self.pos - self.prev_pos) / dt; self.accel = Vector2f::default(); } + + pub fn apply_vel(&mut self, vel: Vector2f) { + if self.immovable {return} + self.pos += vel; + } } From fb06e9cb9ea609d932169311d1408ba877b6f3be Mon Sep 17 00:00:00 2001 From: Raptorox <70806316+Raptorox@users.noreply.github.com> Date: Fri, 1 Aug 2025 21:20:38 +0200 Subject: [PATCH 09/10] add more particles --- src/main.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1f2673d..239f85c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,9 +22,9 @@ const SUBSTEP_COUNT: u32 = 16; fn populate_particles(particles: &mut Vec>>, num: u32, cols: u32) { for i in 0..num { - const OFFSET: u32 = 20; - let x_pos = (WIN_WIDTH/2 - cols/2*OFFSET + OFFSET * (i % cols)) as f32; - let y_pos = (WIN_HEIGHT/2 - (num/cols)/2*OFFSET + OFFSET * (i / cols)) as f32; + const OFFSET: u32 = 10; + let x_pos = (/* WIN_WIDTH/2 - cols/2*OFFSET */ 40 + OFFSET * (i % cols)) as f32; + let y_pos = (/* WIN_HEIGHT/2 - (num/cols)/2*OFFSET */ 40 + OFFSET * (i / cols)) as f32; let immovable = i < cols; let particle = Particle::new((x_pos, y_pos), immovable); particles.push(Rc::new(RefCell::new(particle))); @@ -127,8 +127,8 @@ fn main() -> SfResult<()> { let mouse_pos_prev = window.mouse_position(); let mut mouse_coords_prev = window.map_pixel_to_coords_current_view(mouse_pos_prev); - let particle_count: u32 = 600; - let column_count: u32 = 30; + let particle_count: u32 = 2000; + let column_count: u32 = 50; let row_count: u32 = particle_count / column_count; let mut particles: Vec>> = vec![]; @@ -143,7 +143,7 @@ fn main() -> SfResult<()> { ); // let mut link3 = Link::new(Rc::clone(&(particles[0])), Rc::clone(&(particles[2])), 200.); - let radius: f32 = 8.; + let radius: f32 = 1.; let point_count: usize = 100; let mut circles: Vec = vec![]; populate_circles(&mut circles, particle_count, radius, point_count); @@ -169,7 +169,7 @@ fn main() -> SfResult<()> { let p_pos = particle.borrow().pos; let dist_vec = mouse_coords - p_pos; let dist = math::vec_len(dist_vec); - if dist < 20. { + if dist < 40. { particle.borrow_mut().apply_force(mouse_vel * 8000.); } } From 4b1933f3f8462387f340211afd8c91a56b8e0524 Mon Sep 17 00:00:00 2001 From: Raptorox <70806316+Raptorox@users.noreply.github.com> Date: Fri, 3 Apr 2026 14:20:29 +0200 Subject: [PATCH 10/10] only substep solving; set only 3 attachment points --- src/main.rs | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/main.rs b/src/main.rs index 239f85c..80c3d40 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,9 +23,9 @@ const SUBSTEP_COUNT: u32 = 16; fn populate_particles(particles: &mut Vec>>, num: u32, cols: u32) { for i in 0..num { const OFFSET: u32 = 10; - let x_pos = (/* WIN_WIDTH/2 - cols/2*OFFSET */ 40 + OFFSET * (i % cols)) as f32; - let y_pos = (/* WIN_HEIGHT/2 - (num/cols)/2*OFFSET */ 40 + OFFSET * (i / cols)) as f32; - let immovable = i < cols; + let x_pos = (/* WIN_WIDTH/2 - cols/2*OFFSET */120 + OFFSET * (i % cols)) as f32; + let y_pos = (/* WIN_HEIGHT/2 - (num/cols)/2*OFFSET */40 + OFFSET * (i / cols)) as f32; + let immovable = i == 0 || i == cols - 1 || i == (cols / 2); let particle = Particle::new((x_pos, y_pos), immovable); particles.push(Rc::new(RefCell::new(particle))); } @@ -119,7 +119,12 @@ fn draw_all( } fn main() -> SfResult<()> { - let mut window = RenderWindow::new((WIN_WIDTH, WIN_HEIGHT), "Verlet", Style::CLOSE, &Default::default())?; + let mut window = RenderWindow::new( + (WIN_WIDTH, WIN_HEIGHT), + "Verlet", + Style::CLOSE, + &Default::default(), + )?; window.set_framerate_limit(60); let mut clock = Clock::start()?; @@ -170,21 +175,23 @@ fn main() -> SfResult<()> { let dist_vec = mouse_coords - p_pos; let dist = math::vec_len(dist_vec); if dist < 40. { - particle.borrow_mut().apply_force(mouse_vel * 8000.); + particle.borrow_mut().apply_force(mouse_vel * 800.); } } } let dt = clock.restart().as_seconds(); - let substep_dt = dt/SUBSTEP_COUNT as f32; - + let substep_dt = dt / SUBSTEP_COUNT as f32; + + //for _ in 0..SUBSTEP_COUNT { + apply_forces(&particles); + update_particles(&particles, dt); for _ in 0..SUBSTEP_COUNT { - apply_forces(&particles); - update_particles(&particles, substep_dt); solve_links(&mut links); - update_particle_derivatives(&particles, substep_dt); - update_positions(&mut circles, &particles); } + update_particle_derivatives(&particles, dt); + update_positions(&mut circles, &particles); + //} window.clear(Color::BLACK); draw_all(&mut window, &circles /* &lines */);