diff --git a/src/bomb.rs b/src/bomb.rs new file mode 100644 index 0000000..3fc6b79 --- /dev/null +++ b/src/bomb.rs @@ -0,0 +1,37 @@ +const BOMB_MAX_TIME: u64 = 40_000; +const BOMB_BLINK_DURATION: u64 = 300; +const BOMB_BLINK_SPEED: u64 = 700; + +pub struct Bomb { + last_up_planted: bool, + time_bomb_planted: u64, +} + +impl Bomb { + pub fn new() -> Bomb{ + Bomb { + last_up_planted: false, + time_bomb_planted: 0, + } + } + + pub fn tick(&mut self, upstream: bool, delta_t: u64) -> u8 { + if upstream != self.last_up_planted { + // A bomb has been planted or defused reset the timer + self.time_bomb_planted = 0; + self.last_up_planted = upstream; + } + + if upstream { + self.time_bomb_planted += delta_t; + + let should_blink = (self.time_bomb_planted) + % (BOMB_BLINK_SPEED * (BOMB_MAX_TIME / self.time_bomb_planted).max(1)) + < BOMB_BLINK_DURATION; + + return if should_blink { 255 } else { 0 }; + } + + return 0; + } +} \ No newline at end of file diff --git a/src/curves.rs b/src/curves.rs new file mode 100644 index 0000000..0c59df1 --- /dev/null +++ b/src/curves.rs @@ -0,0 +1,12 @@ + +pub fn qudratic_curve(f64:x, f64:x2, f64: c) -> T +where T: Fn(f64) -> f64 +{ + return |x| x2 * x.powi(2) + x * x + c; +} + +pub fn cubic_bezier(p1: (f64,f64),p2: (f64,f64)) -> T +where T: Fn(f64) -> f64 +{ + return |x| (1.0 - x).powi(3) * p1.0 + 3.0 * (1.0 - x).powi(2) * x * p1.1 + 3.0 * (1.0 - x) * x.powi(2) * p2.0 + x.powi(3) * p2.1; +} diff --git a/src/flash.rs b/src/flash.rs new file mode 100644 index 0000000..0525164 --- /dev/null +++ b/src/flash.rs @@ -0,0 +1,61 @@ +const FLASHED_X2: f64 = 0.00003113; +const FLASHED_X1: f64 = -0.17396053; +const FLASHED_X0: f64 = 244.51964267; + +fn interpolate_flashed(time: u64) -> u8 { + if time == 0 { + return 0; + } + + let y = FLASHED_X2 * (time as f64).powi(2) + FLASHED_X1 * (time as f64) + FLASHED_X0; + + return y.min(255.0).round() as u8; +} + +fn resolve_time_from_flashed(flashed: u8) -> u64 { + let discriminant = FLASHED_X1.powi(2) - 4.0 * FLASHED_X2 * (FLASHED_X0 - flashed as f64); + + let x = (-FLASHED_X1 - discriminant.sqrt()) / (2.0 * FLASHED_X2); + return x.min(5000.0) as u64; +} + +pub struct Flash { + last_up_flashed: u8, // Used to detect changes in upstream flashed + time_since_last_flashed: u64, +} + +impl Flash { + pub fn new() -> Flash { + Flash { + last_up_flashed: 0, + time_since_last_flashed: 0, + } + } + + pub fn tick(&mut self, upstream: u8, delta_t: u64) -> u8 { + // Count the time since the flash started to fade + if upstream > 0 && upstream < 255 { + self.time_since_last_flashed += delta_t; + } + + if upstream == self.last_up_flashed { + // Upstream value hasn't changed + + if upstream == 255 { + // Flash is at max + return 255; + } else if upstream == 0 { + // Flash is off + return 0; + } else { + // Flash is fading + return interpolate_flashed(self.time_since_last_flashed); + } + } + + // Upstream value has changed + self.time_since_last_flashed = resolve_time_from_flashed(upstream); + self.last_up_flashed = upstream; + return upstream; + } +} diff --git a/src/led.rs b/src/led.rs index 09b1330..dbe522a 100644 --- a/src/led.rs +++ b/src/led.rs @@ -1,4 +1,4 @@ -use super::state::State; +use super::{flash::Flash, bomb::Bomb,state::State}; use std::{sync::atomic::Ordering, thread}; fn build_led_package(len: u32, color: (u8, u8, u8)) -> Vec { @@ -15,109 +15,28 @@ fn build_led_package(len: u32, color: (u8, u8, u8)) -> Vec { return packet; } -const FLASHED_X2: f64 = 0.00003113; -const FLASHED_X1: f64 = -0.17396053; -const FLASHED_X0: f64 = 244.51964267; - -const BOMB_MAX_TIME: u64 = 40_000; -const BOMB_BLINK_DURATION: u64 = 300; -const BOMB_BLINK_SPEED: u64 = 700; - -fn interpolate_flashed(time: u64) -> u8 { - if time == 0 { - return 0; - } - - let y = FLASHED_X2 * (time as f64).powi(2) + FLASHED_X1 * (time as f64) + FLASHED_X0; - - return y.min(255.0).round() as u8; -} - -fn resolve_time_from_flashed(flashed: u8) -> u64 { - let discriminant = FLASHED_X1.powi(2) - 4.0 * FLASHED_X2 * (FLASHED_X0 - flashed as f64); - - let x = (-FLASHED_X1 - discriminant.sqrt()) / (2.0 * FLASHED_X2); - return x.min(5000.0) as u64; -} - -pub fn led_loop(fps: u32, addr: &str, len: u32, state: State) { +pub fn led_loop(fps: u32, addr: &str, len: u32, state: State) -> ! { let socket = std::net::UdpSocket::bind("0.0.0.0:0").unwrap(); let sleep_time: u64 = (1000 / fps).into(); - let mut last_up_flashed: u8 = 0; // Used to detect changes in upstream flashed - let mut interpolated_flashed: u8; - let mut time_since_last_flashed: u64 = 0; - - let mut last_up_planted: bool = false; - let mut time_bomb_planted: u64 = 0; + let mut flash = Flash::new(); + let mut bomb = Bomb::new(); loop { thread::sleep(std::time::Duration::from_millis(sleep_time)); let upstream_flashed = u8::try_from(state.flashed.load(Ordering::Relaxed)).unwrap_or(0); + let interpolated_flashed = flash.tick(upstream_flashed, sleep_time); - // Count the time since the flash started to fade - if upstream_flashed > 0 && upstream_flashed < 255 { - time_since_last_flashed += sleep_time; - } + let planted = state.bomb_planted.load(Ordering::Relaxed); + // let planted = true; + let bomb_blink = bomb.tick(planted, sleep_time); - if upstream_flashed == last_up_flashed { - // Upstream value hasn't changed + // Merge flashed and bomb blink + let color = (interpolated_flashed.max(bomb_blink), interpolated_flashed, interpolated_flashed); - if upstream_flashed == 255 { - // Flash is at max - interpolated_flashed = 255; - } else if upstream_flashed == 0 { - // Flash is off - interpolated_flashed = 0; - } else { - // Flash is fading - interpolated_flashed = interpolate_flashed(time_since_last_flashed); - } - } else { - // Upstream value has changed + let packet = build_led_package(len, color); - time_since_last_flashed = resolve_time_from_flashed(upstream_flashed); - interpolated_flashed = upstream_flashed; - last_up_flashed = upstream_flashed; - } - - let pack = build_led_package( - len, - ( - interpolated_flashed, - interpolated_flashed, - interpolated_flashed, - ), - ); - socket.send_to(&pack, addr).unwrap(); - - // let planted = state.bomb_planted.load(Ordering::Relaxed); - let planted = true; - - - if planted != last_up_planted { - // A bomb has been planted or defused reset the timer - time_bomb_planted = 0; - last_up_planted = planted; - } - - if planted { - time_bomb_planted += sleep_time; - - println!("Time: {}", time_bomb_planted); - - let should_blink = (time_bomb_planted) % (BOMB_BLINK_SPEED * (BOMB_MAX_TIME / time_bomb_planted).max(1)) < BOMB_BLINK_DURATION; - - let color = if should_blink { - (255, 0, 0) - } else { - (0, 0, 0) - }; - - let pack = build_led_package(len, color); - socket.send_to(&pack, addr).unwrap(); - - } + socket.send_to(&packet, addr).unwrap(); } } diff --git a/src/main.rs b/src/main.rs index 8ee9a76..f879369 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,8 @@ use std::thread; mod gamestate; mod led; mod state; +mod flash; +mod bomb; #[post("/gsi", data = "")] fn gsi(data: Json, gobal_state: &rocket::State) -> Value {