Mercurial > lbo > hg > collisions
changeset 0:3de93b2549e8
Initial commit
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Sun, 15 Nov 2020 15:16:43 +0100 |
parents | |
children | aaff243d027c |
files | .hgignore Cargo.lock Cargo.toml src/main.rs |
diffstat | 4 files changed, 389 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Sun Nov 15 15:16:43 2020 +0100 @@ -0,0 +1,1 @@ +^target/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Cargo.lock Sun Nov 15 15:16:43 2020 +0100 @@ -0,0 +1,84 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "collisions" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "getrandom" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Cargo.toml Sun Nov 15 15:16:43 2020 +0100 @@ -0,0 +1,10 @@ +[package] +name = "collisions" +version = "0.1.0" +authors = ["Lewin Bormann <lewin@lewin-bormann.info>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rand = "~0.7"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main.rs Sun Nov 15 15:16:43 2020 +0100 @@ -0,0 +1,294 @@ +use std::ops; + +use std::cmp::Reverse; +use std::collections::BinaryHeap; + +use rand::{thread_rng, Rng}; + +type BaseNum = f32; + +const TIME_UNIT: BaseNum = 1e-6; + +#[derive(Clone, Copy, Debug, PartialEq)] +struct Vector(BaseNum, BaseNum); + +impl ops::Add for Vector { + type Output = Vector; + fn add(self, other: Vector) -> Self::Output { + Vector(self.0 + other.0, self.1 + other.1) + } +} + +impl ops::Sub for Vector { + type Output = Vector; + fn sub(self, other: Vector) -> Self::Output { + Vector(self.0 - other.0, self.1 - other.1) + } +} + +impl ops::Mul<BaseNum> for Vector { + type Output = Vector; + fn mul(self, f: BaseNum) -> Self::Output { + Vector(self.0 * f, self.1 * f) + } +} + +impl Vector { + fn normsq(&self) -> BaseNum { + self.dot(&self) + } + fn dot(self, other: &Vector) -> BaseNum { + self.0 * other.0 + self.1 * other.1 + } + fn unit(&self) -> Vector { + let norm = self.normsq().sqrt(); + *self * (1. / norm) + } + fn orth(&self) -> Vector { + Vector(-self.1, self.0) + } +} + +#[derive(PartialEq, Eq, Debug, Clone)] +struct Event { + typ: EventType, + when: usize, +} + +impl PartialOrd for Event { + fn partial_cmp(&self, other: &Event) -> Option<std::cmp::Ordering> { + self.when.partial_cmp(&other.when) + } +} +impl Ord for Event { + fn cmp(&self, other: &Event) -> std::cmp::Ordering { + self.when.cmp(&other.when) + } +} + +#[derive(PartialEq, Eq, Debug, Clone)] +enum EventType { + PP(ParticleHandle, ParticleHandle), + HorizWall(ParticleHandle), + VertWall(ParticleHandle), + Redraw, +} + +#[derive(Eq, PartialEq, Debug, Clone, Copy)] +struct ParticleHandle(usize); + +#[derive(Debug, Clone, PartialEq)] +struct Particle { + pos: Vector, + vel: Vector, + rad: BaseNum, + mass: BaseNum, + coll: usize, +} + +impl Particle { + fn draw(&self) { + unimplemented!() + } + fn mov(&mut self, dt: usize) { + self.pos = self.pos + self.vel * dt as f32 * TIME_UNIT; + } + fn timetohit(&self, dt: usize, other: &Particle, limit: usize) -> Option<usize> { + let mut mypos = self.pos; + let mut otherpos = other.pos; + let mindistsq = self.rad * self.rad + other.rad * other.rad; + let mut i = 0; + + while (otherpos - mypos).normsq() < mindistsq && i * dt < limit { + mypos = mypos + self.vel * dt as f32 * TIME_UNIT; + otherpos = otherpos + other.vel * dt as f32 * TIME_UNIT; + i += 1; + } + + if (otherpos - mypos).normsq() < mindistsq { + Some(i * dt) + } else { + None + } + } + + fn timetohit_horiz_wall(&self, y: BaseNum) -> Option<usize> { + if (self.pos.1 > y && self.vel.1 > 0.) || (self.pos.1 < y && self.vel.1 < 0.) { + None + } else { + Some((((y - self.pos.1).abs() / self.vel.1) / TIME_UNIT as BaseNum) as usize) + } + } + fn timetohit_vert_wall(&self, x: BaseNum) -> Option<usize> { + if (self.pos.0 > x && self.vel.0 > 0.) || (self.pos.0 < x && self.vel.0 < 0.) { + None + } else { + Some((((x - self.pos.0).abs() / self.vel.0) / TIME_UNIT as BaseNum) as usize) + } + } + fn bounce_off(&mut self, other: &mut Particle) { + let conn = self.pos - other.pos; + assert!(conn.normsq() <= (self.rad * self.rad + other.rad * other.rad)); + + // Determine orthogonal bounce plane. + let connu = conn.unit(); + let bounceplane = connu.orth(); + // Relevant speeds are parallel to connection line between positions. + let v1_bounce = self.vel.dot(&connu); + let v2_bounce = self.vel.dot(&connu); + + let v1_orth = self.vel.dot(&bounceplane); + let v2_orth = self.vel.dot(&bounceplane); + + let v1_bounce_after = 2. * (self.mass * v1_bounce + other.mass * v2_bounce) + / (self.mass + other.mass) + - v1_bounce; + let v2_bounce_after = 2. * (self.mass * v1_bounce + other.mass * v2_bounce) + / (self.mass + other.mass) + - v2_bounce; + + self.vel = connu * v1_bounce_after + bounceplane * v1_orth; + other.vel = connu * v2_bounce_after + bounceplane * v2_orth; + } + fn bounce_horiz_wall(&mut self) { + self.vel.1 = -self.vel.1 + } + fn bounce_vert_wall(&mut self) { + self.vel.0 = -self.vel.0 + } +} + +fn test_generate_data() { + let dt = 0.01; + // 2x2 box. + let mut p1 = Particle { + pos: Vector(0., 0.), + vel: Vector(1., 1.), + rad: 0.1, + mass: 1., + coll: 0, + }; + let mut p2 = Particle { + pos: Vector(1., 1.), + vel: Vector(-0.9, -0.9), + ..p1 + }; + + for i in 0..1000 {} +} + +struct System { + parts: Vec<Particle>, + pq: BinaryHeap<Reverse<Event>>, + t: usize, + dt: usize, + dim: BaseNum, +} + +impl System { + fn new(dim: BaseNum, dt: usize, particles: usize) -> System { + let mut rng = thread_rng(); + let particles = (0..particles) + .map(|_| Particle { + pos: Vector(rng.gen_range(0., dim), rng.gen_range(0., dim)), + vel: Vector(rng.gen_range(0., dim), rng.gen_range(0., dim)), + mass: rng.gen_range(0., 1.), + rad: rng.gen_range(0., dim / 100.), + coll: 0, + }) + .collect::<Vec<Particle>>(); + + System { + parts: particles, + pq: BinaryHeap::new(), + t: 0, + dt: dt, + dim: dim, + } + } + + fn handle_for(&self, i: usize) -> ParticleHandle { + ParticleHandle(i) + } + fn particle_for(&self, ph: ParticleHandle) -> &Particle { + &self.parts[ph.0] + } + fn particle_for_mut(&mut self, ph: ParticleHandle) -> &mut Particle { + &mut self.parts[ph.0] + } + fn for_all_particles<F>(&mut self, f: F) where F: std::ops::Fn(&mut Particle) { + for p in self.parts.iter_mut() { + f(p); + } + } + + fn predict_collisions(&mut self, p: ParticleHandle, limit: usize) { + for (i, other) in self.parts.iter().enumerate() { + if let Some(tth) = self.particle_for(p).timetohit(self.dt, other, limit) { + self.pq.push(Reverse(Event { + typ: EventType::PP(p, self.handle_for(i)), + when: (tth / self.dt) as usize, + })); + } + } + let particle = self.particle_for(p); + let (dtx, dty) = ( + particle.timetohit_vert_wall(self.dim), + particle.timetohit_horiz_wall(self.dim), + ); + if let Some(dtx) = dtx { + if dtx <= limit { + self.pq.push(Reverse(Event { + typ: EventType::VertWall(p), + when: self.t + dtx, + })); + } + } + if let Some(dty) = dty { + if dty <= limit { + self.pq.push(Reverse(Event { + typ: EventType::HorizWall(p), + when: self.t + dty, + })); + } + } + } + + fn simulate(&mut self, limit: usize, redrawHz: usize) { + for i in 0..self.parts.len() { + self.predict_collisions(self.handle_for(i), limit); + } + self.pq.push(Reverse(Event { typ: EventType::Redraw, when: (1./(redrawHz*self.dt) as f32) as usize })); + + while !self.pq.is_empty() { + if let Some(next) = self.pq.pop() { + let next = next.0; + let dt = next.when; + let t = self.t + dt; + self.for_all_particles(|p| p.mov(dt)); + match next.typ { + EventType::Redraw => {}, + EventType::HorizWall(p) => { + self.particle_for_mut(p).bounce_horiz_wall(); + self.predict_collisions(p, limit); + }, + EventType::VertWall(p) => { + self.particle_for_mut(p).bounce_vert_wall(); + self.predict_collisions(p, limit); + }, + EventType::PP(p1, p2) => { + let mut pm1 = self.parts[p1.0].clone(); + let mut pm2 = self.parts[p2.0].clone(); + pm1.bounce_off(&mut pm2); + self.parts[p1.0] = pm1; + self.parts[p2.0] = pm2; + }, + } + } + } + } +} + +fn main() { + println!("Hello, world!"); +}