diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main.rs | 191 |
1 files changed, 184 insertions, 7 deletions
diff --git a/src/main.rs b/src/main.rs index c1bb344..fdf3e37 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use std::{ + cmp::Ordering, num::NonZeroU32, ops::DerefMut, rc::Rc, @@ -13,8 +14,9 @@ use tracing_subscriber::EnvFilter; use winit::{ application::ApplicationHandler, dpi::LogicalSize, - event::WindowEvent, + event::{DeviceEvent, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, + keyboard::{Key, NamedKey}, window::Window, }; @@ -40,6 +42,10 @@ fn main() { left_angle: 0.0, right_angle: 0.0, next_check: Instant::now(), + + gallop_x: Gallop::default(), + gallop_y: Gallop::default(), + next_gallop_check: Instant::now(), }; el.run_app(&mut etch).unwrap(); @@ -52,6 +58,106 @@ fn setup_logging() { tracing_subscriber::fmt().with_env_filter(env_filter).init(); } +#[derive(Clone, Debug, Default)] +struct Gallop { + keys: [Option<Instant>; 4], +} + +impl Gallop { + pub fn push(&mut self, idx: usize) { + if idx >= self.keys.len() { + panic!("gallop index out of bound"); + } + + self.keys[idx] = Some(Instant::now()); + } + + pub fn event(&mut self) -> Option<GallopEvent> { + let mut sorted: Vec<(usize, Option<Instant>)> = self.keys.into_iter().enumerate().collect(); + sorted.sort_by(|(_, a_inst), (_, b_inst)| { + if a_inst.is_none() { + Ordering::Greater + } else if b_inst.is_none() { + Ordering::Less + } else { + a_inst.cmp(&b_inst) + } + }); + + let (early2_idx, Some(early2)) = sorted[0] else { + return None; + }; + + let (early_idx, Some(early)) = sorted[1] else { + return None; + }; + + tracing::info!( + "early = [{}] {}us // early2 = [{}] {}us", + early_idx, + early.elapsed().as_micros(), + early2_idx, + early2.elapsed().as_micros() + ); + + let high = self.keys.len() - 1; + if early_idx == 0 && early2_idx == high { + tracing::trace!("gallop (+) wrap special case"); + self.keys[early2_idx] = None; + return Some(GallopEvent::Positive(early.duration_since(early2))); + } else if early_idx == high && early2_idx == 0 { + tracing::trace!("gallop (-) wrap special case"); + } + + if early_idx > early2_idx { + let delta = early_idx - early2_idx; + self.keys[early2_idx] = None; + + if delta > 1 { + tracing::info!("gallop (+) delta>1"); + None + } else { + Some(GallopEvent::Positive(early.duration_since(early2))) + } + } else if early_idx < early2_idx { + let delta = early2_idx - early_idx; + self.keys[early2_idx] = None; + + if delta > 1 { + tracing::info!("gallop (-) delta>1"); + None + } else { + Some(GallopEvent::Negative(early.duration_since(early2))) + } + } else { + None + } + } +} + +#[derive(Clone, Debug)] +enum GallopEvent { + Positive(Duration), + Negative(Duration), +} + +impl GallopEvent { + pub fn value(&self) -> f32 { + match self { + Self::Positive(gdur) => { + let delta = GALLOP_TOLERANCE - *gdur; + let units = delta.as_millis() as f32 / GALLOP_SENSETIVITY.as_millis() as f32; + units + } + Self::Negative(gdur) => { + let delta = GALLOP_TOLERANCE - *gdur; + let units = delta.as_millis() as f32 / GALLOP_SENSETIVITY.as_millis() as f32; + -units + } + } + } +} + #[derive(Copy, Clone, Debug, Default)] struct DialState { left: Vec2<f32>, @@ -73,6 +179,10 @@ struct Etch { left_angle: f32, right_angle: f32, next_check: Instant, + + gallop_x: Gallop, + gallop_y: Gallop, + next_gallop_check: Instant, } impl Etch { @@ -115,6 +225,8 @@ impl Etch { // Why are my consts HERE of all places const DIAL_SENSITIVITY: f32 = 10.0; +const GALLOP_SENSETIVITY: Duration = Duration::from_millis(25); +const GALLOP_TOLERANCE: Duration = Duration::from_millis(250); const WIDTH: f32 = 640.0; const HEIGHT: f32 = 480.0; @@ -146,9 +258,40 @@ impl ApplicationHandler for Etch { tracing::info!("close requested! shutting down."); event_loop.exit(); } + WindowEvent::KeyboardInput { + device_id, + event, + is_synthetic, + } => { + let KeyEvent { + logical_key, + state, + repeat, + .. + } = event; + + // We only track keys down and non-repeat + if repeat || !state.is_pressed() { + return; + } + + match logical_key.as_ref() { + Key::Character("a") => self.gallop_x.push(0), + Key::Character("s") => self.gallop_x.push(1), + Key::Character("d") => self.gallop_x.push(2), + Key::Character("f") => self.gallop_x.push(3), + Key::Character("j") => self.gallop_y.push(0), + Key::Character("k") => self.gallop_y.push(1), + Key::Character("l") => self.gallop_y.push(2), + Key::Character(";") => self.gallop_y.push(3), + _ => (), + } + } WindowEvent::RedrawRequested => { self.process_gamepad_events(); + let stylus_prev = self.stylus; + // We check the state of the joystick at 40fps if self.next_check.elapsed() > Duration::from_millis(25) { let left_angle = xy_to_deg(self.dial.left.x, self.dial.left.y); @@ -177,12 +320,11 @@ impl ApplicationHandler for Etch { }; self.right_angle = right_angle; - tracing::info!( + tracing::trace!( "ANGLE ({}) {left_angle} // {left_delta}v -=- ({}) {right_angle} // {right_delta}v", self.dial.left.mag(), self.dial.right.mag() ); - let stylus_prev = self.stylus; let movement_x = left_delta / DIAL_SENSITIVITY; let movement_y = right_delta / DIAL_SENSITIVITY; self.stylus.x = @@ -190,6 +332,43 @@ impl ApplicationHandler for Etch { self.stylus.y = (self.stylus.y - movement_y).clamp(0.0, self.img.height() as f32); + self.next_check = Instant::now(); + } + + if self.next_gallop_check.elapsed() > Duration::from_millis(100) { + while let Some(ge) = self.gallop_x.event() { + match ge { + GallopEvent::Positive(gdur) => { + if gdur <= GALLOP_TOLERANCE { + self.stylus.x += ge.value(); + } + } + GallopEvent::Negative(gdur) => { + if gdur <= GALLOP_TOLERANCE { + self.stylus.x += ge.value(); + } + } + } + } + + while let Some(ge) = self.gallop_y.event() { + match ge { + GallopEvent::Positive(gdur) => { + if gdur <= GALLOP_TOLERANCE { + self.stylus.y += ge.value(); + } + } + GallopEvent::Negative(gdur) => { + if gdur <= GALLOP_TOLERANCE { + self.stylus.y += ge.value(); + } + } + } + } + } + + // If the stylus moved, we should draw + if stylus_prev != self.stylus { self.img.line( self.stylus.as_u32(), stylus_prev.as_u32(), @@ -205,9 +384,7 @@ impl ApplicationHandler for Etch { // And then draw the stylus self.img.rect(self.stylus.as_u32(), Vec2::new(2, 2), STYLUS_COLOUR.into()); - tracing::info!("STYLUS: ({},{})", self.stylus.x, self.stylus.y); - - self.next_check = Instant::now(); + tracing::trace!("STYLUS: ({},{})", self.stylus.x, self.stylus.y); } let Some(surfaced) = self.window.as_mut() else { @@ -289,7 +466,7 @@ fn angle_delta(lhs: f32, rhs: f32) -> f32 { } } -#[derive(Copy, Clone, Debug, Default)] +#[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct Vec2<T> { pub x: T, pub y: T, |