#![allow(dead_code)]
use rand::random;
use serde::{Serialize, Deserialize};
use winit::{event_loop::EventLoop, window::{WindowBuilder, Window}, event::{WindowEvent, Event}, dpi::LogicalSize};
use std::{ops::*, collections::HashMap, time::{Duration, Instant}, env::{Args, args}, path::Path, fs::{read_to_string, write}, process::exit};
use common_macros::*;
use pixels::{Pixels, SurfaceTexture};
#[derive(Debug, Default, Copy, Clone, PartialEq)]
struct Vector2 {
x: f64,
y: f64
}
impl Add for Vector2 {
type Output = Self;
fn add(self, other: Self) -> Self {
Self{x: self.x + other.x, y: self.y + other.y}
}
}
impl Sub for Vector2 {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self{x: self.x - other.x, y: self.y - other.y}
}
}
impl Mul for Vector2 {
type Output = Self;
fn mul(self, other: Self) -> Self {
Self{x: self.x * other.x, y: self.y * other.y}
}
}
impl Div for Vector2 {
type Output = Self;
fn div(self, other: Self) -> Self {
Self{x: self.x / other.x, y: self.y / other.y}
}
}
impl Vector2 {
fn len(&self) -> f64 {
(self.x.powf(2.0) + self.y.powf(2.0)).powf(0.5)
}
}
#[derive(Debug, Hash, Serialize, Deserialize, Eq, PartialEq,Clone,Copy)]
enum Color {
Red,
Orange,
Yellow,
Green,
Teal,
Blue,
Purple,
Pink,
White
}
impl Color {
fn get_quad(self) -> [u8;4] {
match self {
Color::Red => [252,73,60,255],
Color::Orange => [237,151,80,255],
Color::Yellow => [242,233,111,255],
Color::Green => [111,242,113,255],
Color::Teal => [21,234,202,255],
Color::Blue => [58,131,234,255],
Color::Purple => [161,38,255,255],
Color::Pink => [255,38,186,255],
Color::White => [255,255,255,255]
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
struct Ball {
color: Color,
pos: Vector2,
vel: Vector2
}
impl Ball {
fn new(color: Color, minx:f64, maxx:f64, miny:f64, maxy:f64) -> Self {
let rangex = maxx - minx;
let rangey = maxy - miny;
let valx = random::<f64>() * rangex + minx;
let valy = random::<f64>() * rangey + miny;
Ball {color, vel: Vector2::default(), pos:Vector2{x:valx, y: valy}}
}
}
#[derive(Debug, Serialize, Deserialize)]
struct Config {
#[serde(default)]
counts: HashMap<Color, u32>,
#[serde(default)]
forces: HashMap<(Color, Color), f64>,
#[serde(default)]
speed: f64,
#[serde(default)]
damp: f64,
#[serde(default)]
radius: f64,
}
fn main() {
let defaultconf = Config{
counts: hash_map!{
Color::Green => 1500,
Color::Red => 200,
},
forces: hash_map!{
(Color::Red,Color::Red) => 0.5,
(Color::Red,Color::Green) => 0.1,
(Color::Green,Color::Red) => -0.5,
(Color::Green,Color::Green) => -0.1
},
speed: 1.0,
damp: 4.0,
radius: 80.0
};
let conf:Config = if args().len() > 1 {
let pathstr = args().nth(1).unwrap();
let path = Path::new(&pathstr);
if path.exists() {
ron::from_str(&read_to_string(path).unwrap()).unwrap()
}
else {
write(path, ron::to_string(&defaultconf).unwrap());
defaultconf
}
}
else {defaultconf};
let events = EventLoop::new();
let win = WindowBuilder::new()
.with_maximized(false)
.with_resizable(false)
.with_inner_size::<LogicalSize<u32>>(LogicalSize::new(640, 480))
.build(&events).unwrap();
let mut pixels = {
let ws = win.inner_size();
let surface_texture = SurfaceTexture::new(ws.width, ws.height, &win);
Pixels::new(ws.width, ws.height, surface_texture).unwrap()
};
let mut balls = {
let dims = {
let size = win.inner_size();
Vector2{x: size.width as f64, y: size.height as f64}
};
conf.counts.iter()
.map(|(color, count):(&Color,&u32)| vec![color; *count as usize])
.flatten()
.map(|color:&Color| Ball::new(*color, 0.0, dims.x, 0.0, dims.y))
.collect::<Vec<Ball>>()
};
let mut last = Instant::now();
events.run(move |e, _, cf| {
match e {
Event::WindowEvent { window_id: _, event: WindowEvent::CloseRequested } => cf.set_exit(),
Event::MainEventsCleared => {
if Instant::now() - last < Duration::from_millis(20) {return};
last = Instant::now();
let dims = {
let size = win.inner_size();
Vector2{x: size.width as f64, y: size.height as f64}
};
update(&conf, &mut balls, dims);
redraw(&balls, &mut pixels, win.inner_size().width, win.inner_size().height);
pixels.render()
.map_err(|e| panic!("pixels.render() failed: {}", e));
},
Event::LoopDestroyed => {
exit(0);
}
_ => ()
}
});
}
fn apply(conf:&Config, a:&mut Ball,b:Ball) -> Vector2 {
if !conf.forces.contains_key(&(a.color, b.color)) {return Vector2::default()}
let atob = b.pos - a.pos;
let dist = atob.len();
if dist > 0.0 && dist < conf.radius {
let factor = conf.forces[&(a.color, b.color)] / dist;
atob * Vector2{x:factor, y:factor}
} else {Vector2::default()}
}
fn update(conf: &Config, balls: &mut Vec<Ball>, dims: Vector2) {
for a in 0..balls.len() {
let mut newvel = balls[a].vel;
for b in 0..balls.len() {
if a == b {continue;}
let ALRIGHT_RUST_I_MADE_IT_IMMUTABLE_FIRST_ARE_YOU_HAPPY = balls[b];
newvel = newvel + apply(conf, &mut balls[a], ALRIGHT_RUST_I_MADE_IT_IMMUTABLE_FIRST_ARE_YOU_HAPPY);
}
balls[a].vel = newvel/Vector2{x:conf.damp,y:conf.damp};
}
for ball in balls.iter_mut() {
if ball.pos.x <= 0.0 {ball.vel.x = -ball.vel.x * conf.damp}
if ball.pos.y <= 0.0 {ball.vel.y = -ball.vel.y * conf.damp}
if ball.pos.x >= dims.x {ball.vel.x = -ball.vel.x * conf.damp}
if ball.pos.y >= dims.y {ball.vel.y = -ball.vel.y * conf.damp}
ball.pos = ball.pos + ball.vel;
}
}
fn redraw(balls: &Vec<Ball>, pix: &mut Pixels, width: u32, height: u32) {
let frame = pix.get_frame();
{
let mut count: u8 = 0;
frame.fill_with(move || {
if count == 3 {count = 0; 1}
else {count += 1; 0}
});
}
for ball in balls {
let px = ball.pos.x as i32;
let py = ball.pos.y as i32;
for (x,y) in [(px,py), (px+1,py), (px-1,py), (px,py+1), (px,py-1)] {
if x <= 0 ||
x >= width as i32 ||
y <= 0 ||
y >= height as i32
{continue;}
let start = y * width as i32 + x;
frame.chunks_exact_mut(4).nth(start as usize).unwrap().copy_from_slice(&ball.color.get_quad());
}
}
}