aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock84
-rw-r--r--Cargo.toml1
-rw-r--r--src/boid.rs82
-rw-r--r--src/main.rs34
-rw-r--r--src/vector2.rs55
5 files changed, 223 insertions, 33 deletions
diff --git a/Cargo.lock b/Cargo.lock
index ba5e857..4e06051 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -10,6 +10,7 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
name = "boids"
version = "0.1.0"
dependencies = [
+ "rand",
"sdl2",
]
@@ -20,6 +21,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
+name = "getrandom"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -27,15 +39,62 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.71"
+version = "0.2.76"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
+checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
+
+[[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 = "sdl2"
-version = "0.34.1"
+version = "0.34.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45c68e7a45838d6dd8723347a5d233f2c989f0b291f18a46158d5dfbb275ada1"
+checksum = "fcbb85f4211627a7291c83434d6bbfa723e28dcaa53c7606087e3c61929e4b9c"
dependencies = [
"bitflags",
"lazy_static",
@@ -45,10 +104,23 @@ dependencies = [
[[package]]
name = "sdl2-sys"
-version = "0.34.0"
+version = "0.34.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "575db20c45cce6565d4ece46509288ded53f5571dc89e4af165a562eb0eeec26"
+checksum = "3ed17d6d46b62b7df12134513bcc4f071268963e8c9bc8bf7ad983fbfb2bc3cc"
dependencies = [
"cfg-if",
"libc",
+ "version-compare",
]
+
+[[package]]
+name = "version-compare"
+version = "0.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1"
+
+[[package]]
+name = "wasi"
+version = "0.9.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
diff --git a/Cargo.toml b/Cargo.toml
index a31ad4c..41db10b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,3 +8,4 @@ edition = "2018"
[dependencies]
sdl2 = "0.34.0"
+rand = "*"
diff --git a/src/boid.rs b/src/boid.rs
index dfc0755..5c66272 100644
--- a/src/boid.rs
+++ b/src/boid.rs
@@ -2,26 +2,71 @@ use std::f32::consts::{FRAC_PI_2};
use sdl2::rect::Point;
use sdl2::render::Canvas;
+use sdl2::video::Window;
+
+use crate::vector2::Vector2;
+
+const NEIGHBOURS_RADIUS: i32 = 50;
+#[derive(PartialEq, Clone)]
pub struct Boid {
- dir_x: f32,
- dir_y: f32,
- position: Point,
+ dir: Vector2,
+ pos: Point,
}
const TRIANGLE_SIZE: i32 = 20;
-const SPEED: f32 = 10.0;
+const SPEED: f32 = 15.0;
-use sdl2::video::Window;
impl Boid {
- pub fn new(position: Point) -> Boid {
- Boid { dir_x: 0.1, dir_y: 0.2, position }
+ pub fn new(x: i32, y: i32, dir_x: f32, dir_y: f32) -> Boid {
+ let d = Vector2::new(dir_x, dir_y);
+ Boid { dir: d / d.norm(), pos: Point::new(x, y) }
+ }
+
+ pub fn step(&mut self, boids: &Vec<Boid>, width: i32, height: i32) {
+ let ns = self.neighbours(boids);
+
+ let ns_len = if ns.len() != 0 { ns.len() } else { 1 };
+
+ let center = ns.iter()
+ .fold(Point::new(0, 0), |acc, x| acc + x.pos) / ns_len as i32;
+ let mut center_dir = Vector2::from_point(center - self.pos);
+ center_dir.normalize();
+
+ let mut align_dir = ns.iter()
+ .fold(Vector2::new(0.0, 0.0), |acc, x| acc + x.dir) / ns_len as f32;
+ align_dir.normalize();
+
+ let sep_dir = Vector2::from_point(ns.iter()
+ .fold(Point::new(0, 0), |acc, x| acc + (x.pos - self.pos)) / ns_len as i32) * -1.0;
+
+
+ if ns.len() != 0 {
+ self.dir = (self.dir + align_dir + center_dir + sep_dir) / 4.0;
+ }
+ self.dir.normalize();
+
+ self.pos = self.pos.offset(
+ (self.dir.x * SPEED as f32) as i32,
+ (self.dir.y * SPEED as f32) as i32
+ );
+ self.pos.x %= width;
+ self.pos.y %= height;
+ }
+
+ fn neighbours<'a>(&self, boids: &'a Vec<Boid>) -> Vec<&'a Boid> {
+ boids.iter().filter(|n| self.dist(n) <= NEIGHBOURS_RADIUS && *n != self).collect()
+ }
+
+ fn dist(&self, other: &Boid) -> i32 {
+ let p = self.pos - other.pos;
+ ((p.x * p.x + p.y * p.y) as f32).sqrt() as i32
}
pub fn draw(&self, canvas: &mut Canvas<Window>) {
- let top = self.position.offset(0, -TRIANGLE_SIZE);
- let bot_left = self.position.offset(-TRIANGLE_SIZE / 3, TRIANGLE_SIZE / 2);
- let bot_right = self.position.offset(TRIANGLE_SIZE / 3, TRIANGLE_SIZE / 2);
+ let top = self.pos.offset(0, -TRIANGLE_SIZE);
+ let bot_left = self.pos.offset(-TRIANGLE_SIZE / 3, TRIANGLE_SIZE / 2);
+ let bot_right = self.pos.offset(TRIANGLE_SIZE / 3, TRIANGLE_SIZE / 2);
// direction angle = t
// tan t = y / x
@@ -29,24 +74,17 @@ impl Boid {
// | x cos t -y sin t |
// | x sin t y cos t |
- let angle = (self.dir_y / self.dir_x).atan() + FRAC_PI_2;
+ let angle = (self.dir.y / self.dir.x).atan() + FRAC_PI_2;
let s = angle.sin();
let c = angle.cos();
let ps: Vec<Point> = [top, bot_left, bot_right, top].iter().map(|p| {
- let x = (p.x() - self.position.x()) as f32;
- let y = (p.y() - self.position.y()) as f32;
- Point::new((x * c - y * s) as i32 + self.position.x(),
- (x * s + y * c) as i32 + self.position.y())
+ let x = (p.x() - self.pos.x()) as f32;
+ let y = (p.y() - self.pos.y()) as f32;
+ Point::new((x * c - y * s) as i32 + self.pos.x(),
+ (x * s + y * c) as i32 + self.pos.y())
}).collect();
canvas.draw_lines(&ps[..]).unwrap();
}
-
- pub fn step(&mut self) {
- self.position = self.position.offset(
- (self.dir_x * SPEED) as i32,
- (self.dir_y * SPEED) as i32
- );
- }
}
diff --git a/src/main.rs b/src/main.rs
index 4c6ce8f..2c633cc 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,21 +1,39 @@
extern crate sdl2;
+extern crate rand;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::pixels::Color;
-use sdl2::rect::Point;
+use rand::Rng;
+
+pub mod vector2;
pub mod boid;
use boid::Boid;
+const HEIGHT: i32 = 480;
+const WIDTH: i32 = 640;
+const BOIDS_NUM: usize = 100;
+// const NEIGHBOURS_RADIUS: i32 = ((HEIGHT + WIDTH) / 2) / 10;
+
fn main() {
let sdl = sdl2::init().unwrap();
let video_subsys = sdl.video().unwrap();
- let window = video_subsys.window("boids", 640, 480).build().unwrap();
+ let window = video_subsys.window("boids", WIDTH as u32, HEIGHT as u32).build().unwrap();
let mut canvas = window.into_canvas().build().unwrap();
- let mut b = Boid::new(Point::new(50, 50));
+ let mut boids: Vec<Boid> = Vec::with_capacity(BOIDS_NUM);
+
+ let mut rng = rand::thread_rng();
+ for _ in 0..BOIDS_NUM {
+ boids.push(Boid::new(
+ rng.gen_range(0, WIDTH as i32),
+ rng.gen_range(0, HEIGHT as i32),
+ rng.gen_range(-2.0, 2.0),
+ rng.gen_range(-2.0, 2.0),
+ ));
+ }
let mut event_pump = sdl.event_pump().unwrap();
'main: loop {
@@ -31,11 +49,17 @@ fn main() {
canvas.set_draw_color(Color::RGB(0, 0, 0));
canvas.clear();
canvas.set_draw_color(Color::RGB(200, 200, 200));
- b.draw(&mut canvas);
+ for b in boids.iter() {
+ b.draw(&mut canvas);
+ }
canvas.present();
- b.step();
+ let prev_boids = boids.clone();
+
+ for b in boids.iter_mut() {
+ b.step(&prev_boids, WIDTH, HEIGHT);
+ }
std::thread::sleep(std::time::Duration::new(0, 50_000_000));
}
diff --git a/src/vector2.rs b/src/vector2.rs
new file mode 100644
index 0000000..e991a2a
--- /dev/null
+++ b/src/vector2.rs
@@ -0,0 +1,55 @@
+use sdl2::rect::Point;
+
+#[derive(PartialEq, Clone, Copy)]
+pub struct Vector2 {
+ pub y: f32,
+ pub x: f32,
+}
+
+impl Vector2 {
+ pub fn new(x: f32, y: f32) -> Vector2 {
+ Vector2 { x, y }
+ }
+
+ pub fn from_point(point: Point) -> Vector2 {
+ Vector2::new(point.x as f32, point.y as f32)
+ }
+
+ pub fn norm(&self) -> f32 {
+ (self.x * self.x + self.y * self.y).sqrt()
+ }
+
+ pub fn normalize(&mut self) {
+ *self = *self / self.norm();
+ }
+}
+
+use std::ops::{Add, Mul, MulAssign, Div};
+
+impl Add for Vector2 {
+ type Output = Self;
+ fn add(self, other: Self) -> Self::Output {
+ Vector2::new(self.x + other.x, self.y + other.y)
+ }
+}
+
+impl Mul<f32> for Vector2 {
+ type Output = Vector2;
+ fn mul(self, scalar: f32) -> Self::Output {
+ Vector2::new(self.x * scalar, self.y * scalar)
+ }
+}
+
+impl MulAssign<f32> for Vector2 {
+ fn mul_assign(&mut self, scalar: f32) {
+ self.x *= scalar;
+ self.y *= scalar;
+ }
+}
+
+impl Div<f32> for Vector2 {
+ type Output = Vector2;
+ fn div(self, scalar: f32) -> Self::Output {
+ Vector2::new(self.x / scalar, self.y / scalar)
+ }
+}