diff options
author | gennyble <gen@nyble.dev> | 2024-11-30 04:20:28 -0600 |
---|---|---|
committer | gennyble <gen@nyble.dev> | 2024-11-30 04:20:28 -0600 |
commit | 7a720b63d1fe4e2f50813d5c1a1d89579258d453 (patch) | |
tree | ca5f9aa7786c4f00e02184d4bc8111be6e5e85bb /src | |
parent | 4c9c479ddc80d245d1ae7424cc3dd775f9ee3eef (diff) | |
download | really-etches-7a720b63d1fe4e2f50813d5c1a1d89579258d453.tar.gz really-etches-7a720b63d1fe4e2f50813d5c1a1d89579258d453.zip |
Add 50% deadzone
Also: - fixes my a mispelling - lets you erase with the South button - tunes thumbstick angle movement
Diffstat (limited to 'src')
-rw-r--r-- | src/image.rs | 27 | ||||
-rw-r--r-- | src/main.rs | 120 |
2 files changed, 99 insertions, 48 deletions
diff --git a/src/image.rs b/src/image.rs index 0ce41d9..6f03db5 100644 --- a/src/image.rs +++ b/src/image.rs @@ -1,3 +1,5 @@ +use crate::Vec2; + #[allow(dead_code)] pub struct Image { width: u32, @@ -36,12 +38,16 @@ impl Image { self.height } - pub fn rect(&mut self, x: u32, y: u32, width: u32, height: u32, clr: Color) { - let x_start = x as usize; - let x_end = (x + width) as usize; + pub fn fill(&mut self, clr: Color) { + self.data.fill(clr.into()); + } + + pub fn rect(&mut self, pos: Vec2<u32>, dim: Vec2<u32>, clr: Color) { + let x_start = pos.x as usize; + let x_end = (pos.x + dim.x) as usize; for idx in x_start..x_end { - for idy in y as usize..y as usize + height as usize { + for idy in pos.y as usize..pos.y as usize + dim.y as usize { let data_idx = idx + idy * self.width as usize; self.data[data_idx] = clr.into(); } @@ -50,11 +56,11 @@ impl Image { /// Primitive, naive line drawing funciton. lerps the points together and /// draws a rect of the specified width at that location. - pub fn line(&mut self, x1: u32, y1: u32, x2: u32, y2: u32, width: u32, clr: Color) { - let start_x = x1.min(x2) as f32; - let start_y = y1.min(y2) as f32; - let end_x = x1.max(x2) as f32; - let end_y = y1.max(y2) as f32; + pub fn line(&mut self, p1: Vec2<u32>, p2: Vec2<u32>, width: u32, clr: Color) { + let start_x = p1.x.min(p2.x) as f32; + let start_y = p1.y.min(p2.y) as f32; + let end_x = p1.x.max(p2.x) as f32; + let end_y = p1.y.max(p2.y) as f32; tracing::trace!("start_x = {start_x} / end_x = {end_x}"); tracing::trace!("start_y = {start_y} / end_y = {end_y}"); @@ -63,11 +69,12 @@ impl Image { let dy = end_y - start_y; let long = dx.max(dy); + let dim = Vec2::new(width, width); for idx in 0..long as usize { let x = start_x + dx * (idx as f32 / long); let y = start_y + dy * (idx as f32 / long); - self.rect(x as u32, y as u32, width, width, clr); + self.rect(Vec2::new(x, y).as_u32(), dim, clr); } } } diff --git a/src/main.rs b/src/main.rs index 6d07411..c6563f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use std::{ time::{Duration, Instant}, }; -use gilrs::{Axis, Gilrs}; +use gilrs::{Axis, Button, Gilrs}; use image::Image; use softbuffer::{Context, Surface}; use tracing::level_filters::LevelFilter; @@ -36,18 +36,13 @@ fn main() { y: HEIGHT / 2.0, }, - dial: DialState { - left_x: 0.0, - left_y: 0.0, - right_x: 0.0, - right_y: 0.0, - }, + dial: DialState::default(), left_angle: 0.0, right_angle: 0.0, next_check: Instant::now(), }; - el.run_app(&mut etch); + el.run_app(&mut etch).unwrap(); } fn setup_logging() { @@ -59,11 +54,8 @@ fn setup_logging() { #[derive(Copy, Clone, Debug, Default)] struct DialState { - left_x: f32, - left_y: f32, - - right_x: f32, - right_y: f32, + left: Vec2<f32>, + right: Vec2<f32>, } struct SurfacedWindow { @@ -75,7 +67,7 @@ struct Etch { window: Option<SurfacedWindow>, gilrs: Gilrs, img: Image, - stylus: Vec2, + stylus: Vec2<f32>, dial: DialState, left_angle: f32, @@ -98,10 +90,20 @@ impl Etch { } match axis { - Axis::LeftStickX => self.dial.left_x = value * 100.0, - Axis::LeftStickY => self.dial.left_y = value * 100.0, - Axis::RightStickX => self.dial.right_x = value * 100.0, - Axis::RightStickY => self.dial.right_y = value * 100.0, + Axis::LeftStickX => self.dial.left.x = value * 100.0, + Axis::LeftStickY => self.dial.left.y = value * 100.0, + Axis::RightStickX => self.dial.right.x = value * 100.0, + Axis::RightStickY => self.dial.right.y = value * 100.0, + _ => (), + } + } + gilrs::EventType::ButtonPressed(btn, code) => { + tracing::trace!("Button press! {btn:?}"); + + match btn { + Button::South => { + self.img.fill(BACKGROUND_COLOUR.into()); + } _ => (), } } @@ -112,13 +114,14 @@ impl Etch { } // Why are my consts HERE of all places -const DIAL_SENSETIVITY: f32 = 2.0; +const DIAL_SENSITIVITY: f32 = 10.0; const WIDTH: f32 = 640.0; const HEIGHT: f32 = 480.0; // a very sublte gentle, dark-and-dull green const BACKGROUND_COLOUR: u32 = 0x00868886; const LINE_COLOUR: u32 = 0x00303230; +const STYLUS_COLOUR: u32 = 0x00a0a0a0; impl ApplicationHandler for Etch { fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { @@ -147,45 +150,60 @@ impl ApplicationHandler for Etch { // 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); - let left_delta = if !left_angle.is_nan() { + let left_angle = xy_to_deg(self.dial.left.x, self.dial.left.y); + + let left_is_large = self.dial.left.mag() > 50.0; + let left_neither_nan = !left_angle.is_nan() && !self.left_angle.is_nan(); + + let left_delta = if left_is_large && left_neither_nan { let delta = angle_delta(left_angle, self.left_angle); - self.left_angle = left_angle; delta } else { 0.0 }; + self.left_angle = left_angle; + + let right_angle = xy_to_deg(self.dial.right.x, self.dial.right.y); - let right_angle = xy_to_deg(self.dial.right_x, self.dial.right_y); - let right_delta = if !right_angle.is_nan() { + let right_is_large = self.dial.right.mag() > 50.0; + let right_neither_nan = !right_angle.is_nan() && !self.right_angle.is_nan(); + + let right_delta = if right_is_large && right_neither_nan { let delta = angle_delta(right_angle, self.right_angle); - self.right_angle = right_angle; delta } else { 0.0 }; + self.right_angle = right_angle; tracing::info!( - "ANGLE {left_angle} // {left_delta}v -=- {right_angle} // {right_delta}v" + "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 / 10.0; - let movement_y = right_delta / 10.0; + let movement_x = left_delta / DIAL_SENSITIVITY; + let movement_y = right_delta / DIAL_SENSITIVITY; self.stylus.x = (self.stylus.x + movement_x).clamp(0.0, self.img.width() as f32); self.stylus.y = (self.stylus.y - movement_y).clamp(0.0, self.img.height() as f32); self.img.line( - self.stylus.x as u32, - self.stylus.y as u32, - stylus_prev.x as u32, - stylus_prev.y as u32, + self.stylus.as_u32(), + stylus_prev.as_u32(), 2, LINE_COLOUR.into(), ); + // Letting the line-draw take care of overdrawing the stylus + // worked well except going up for some reason, so we + // explicitly overdraw here. + self.img.rect(stylus_prev.as_u32(), Vec2::new(2, 2), LINE_COLOUR.into()); + + // 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(); @@ -252,10 +270,17 @@ fn xy_to_deg(x: f32, y: f32) -> f32 { /// and the left-hand side. Intelligently handles the zero-crossing. /// lhs should be the newer value, rhs the older. fn angle_delta(lhs: f32, rhs: f32) -> f32 { - if rhs >= 270.0 && lhs < 90.0 { + // If we move too fast, or flick the stick just right, these values can be + // bypassed. Perhaps we can check on either side of 180, but this is working + // for now. It used to be 270 and 90 for high/low, respectively, but it was + // getting a touch jumpy. + const HIGH: f32 = 225.0; + const LOW: f32 = 135.0; + + if rhs >= HIGH && lhs < LOW { // It is likely we crossed zero in the clockwise direction (lhs + 360.0) - rhs - } else if rhs < 90.0 && lhs > 270.0 { + } else if rhs < LOW && lhs > HIGH { // It is likely we crossed zero in the anti-clockwise direction lhs - (rhs + 360.0) } else { @@ -263,8 +288,27 @@ fn angle_delta(lhs: f32, rhs: f32) -> f32 { } } -#[derive(Copy, Clone, Debug)] -pub struct Vec2 { - x: f32, - y: f32, +#[derive(Copy, Clone, Debug, Default)] +pub struct Vec2<T> { + pub x: T, + pub y: T, +} + +impl<T> Vec2<T> { + pub fn new(x: T, y: T) -> Self { + Self { x, y } + } +} + +impl Vec2<f32> { + pub fn as_u32(&self) -> Vec2<u32> { + Vec2 { + x: self.x as u32, + y: self.y as u32, + } + } + + pub fn mag(&self) -> f32 { + (self.x * self.x + self.y * self.y).sqrt() + } } |