From 319c812a616921217f48d65c6d65f2ec82b6f03c Mon Sep 17 00:00:00 2001 From: Victoria Fischer Date: Sat, 28 Feb 2026 16:47:25 +0100 Subject: [PATCH] graphics: update to use figments Fract8 type --- src/graphics/display.rs | 12 ++++++------ src/graphics/oled_ui.rs | 4 ++-- src/graphics/shaders.rs | 37 +++++++++++++++++++------------------ src/graphics/ssd1306.rs | 10 +++++----- src/tasks/oled.rs | 8 ++++---- 5 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/graphics/display.rs b/src/graphics/display.rs index 88dd885..6c1e364 100644 --- a/src/graphics/display.rs +++ b/src/graphics/display.rs @@ -1,6 +1,6 @@ use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal, watch::{Receiver, Watch}}; -use figments::prelude::*; -use core::{fmt::Debug, sync::atomic::{AtomicBool, AtomicU8}}; +use figments::{liber8tion::interpolate::Fract8, prelude::*}; +use core::{fmt::Debug, ops::Mul, sync::atomic::{AtomicBool, AtomicU8}}; use alloc::sync::Arc; //use super::{Output}; @@ -201,8 +201,8 @@ impl DisplayControls { self.data.on.load(core::sync::atomic::Ordering::Relaxed) } - pub fn brightness(&self) -> u8 { - self.data.brightness.load(core::sync::atomic::Ordering::Relaxed) + pub fn brightness(&self) -> Fract8 { + Fract8::from_raw(self.data.brightness.load(core::sync::atomic::Ordering::Relaxed)) } pub async fn wait_until_display_is_on(&self) { @@ -228,8 +228,8 @@ impl GammaCorrected for DisplayControls { } impl Brightness for DisplayControls { - fn set_brightness(&mut self, brightness: u8) { - self.data.brightness.store(brightness, core::sync::atomic::Ordering::Relaxed); + fn set_brightness(&mut self, brightness: Fract8) { + self.data.brightness.store(brightness.to_raw(), core::sync::atomic::Ordering::Relaxed); } fn set_on(&mut self, is_on: bool) { diff --git a/src/graphics/oled_ui.rs b/src/graphics/oled_ui.rs index 1c3285f..160c178 100644 --- a/src/graphics/oled_ui.rs +++ b/src/graphics/oled_ui.rs @@ -145,9 +145,9 @@ impl Screen { Image::new(&images::BOOT_LOGO, Point::zero()).draw(sampler).unwrap(); const SPARKLE_COUNT: i32 = 8; for n in 0..SPARKLE_COUNT { - let sparkle_center = Point::new(128u8.scale8(cos8(state.frame.wrapping_mul(n as usize))) as i32, 64u8.scale8(sin8(state.frame.wrapping_mul(n as usize) as u8)) as i32); + let sparkle_center = Point::new((128u8 * state.frame.wrapping_mul(n as usize).cos8()) as i32, (64u8 * state.frame.wrapping_mul(n as usize).sin8()) as i32); let offset = (state.frame / 2 % 32) as i32 - 16; - let rotation = PI * 2.0 * (sin8(state.frame) as f32 / 255.0); + let rotation = PI * 2.0 * state.frame.sin8(); let normal = Point::new((rotation.cos() * offset as f32) as i32, (rotation.sin() * offset as f32) as i32); let cross_normal = Point::new((rotation.sin() * offset as f32) as i32, (rotation.cos() * offset as f32) as i32); // Draw horizontal diff --git a/src/graphics/shaders.rs b/src/graphics/shaders.rs index 09188c4..b461155 100644 --- a/src/graphics/shaders.rs +++ b/src/graphics/shaders.rs @@ -1,6 +1,7 @@ use core::cmp::max; -use figments::{liber8tion::{interpolate::{ease_in_out_quad}, noise::inoise8, trig::{cos8, sin8}}, prelude::*}; +use figments::{liber8tion::{interpolate::{Fract8, ease_in_out_quad}, noise::inoise8, trig::Trig8}, prelude::*}; +use num_traits::WrappingAdd; use rgb::Rgba; use crate::graphics::display::{SegmentSpace, Uniforms}; @@ -25,8 +26,8 @@ impl Shader> for Movement { } else { uniforms.frame.wrapping_sub(surface_coords.x) }; - let idx = sin8(offset).wrapping_add(uniforms.primary_color.hue); - Rgba::new(idx, idx.wrapping_mul(2), idx.wrapping_div(2), 128) + let idx = offset.sin8().wrapping_add(&Fract8::from_raw(uniforms.primary_color.hue)); + Rgba::new(idx.to_raw(), idx.to_raw().wrapping_mul(2), idx.to_raw() / 2, 128) } } @@ -45,12 +46,12 @@ impl Background { impl Shader> for Background { fn draw(&self, coords: &Coordinates, uniforms: &Uniforms) -> Rgba { - let noise_x = sin8(uniforms.frame % 255) as i16; - let noise_y = cos8(uniforms.frame % 255) as i16; + let noise_x = (uniforms.frame % 255).sin8().to_raw() as i16; + let noise_y = (uniforms.frame % 255).cos8().to_raw() as i16; let brightness = inoise8(noise_x.wrapping_add(coords.x as i16), noise_y.wrapping_add(coords.y as i16)); let saturation = inoise8(noise_y.wrapping_add(coords.y as i16), noise_x.wrapping_add(coords.x as i16)); let rgb: Rgb = match self.color { - None => Hsv::new(uniforms.primary_color.hue, max(128, saturation), brightness).into(), + None => Hsv::new(uniforms.primary_color.hue, max(128, saturation.to_raw()), brightness.to_raw()).into(), Some(c) => c }; @@ -62,9 +63,9 @@ impl Shader> for Background { pub struct Tail {} impl Shader> for Tail { fn draw(&self, coords: &Coordinates, uniforms: &Uniforms) -> Rgba { - let hue_offset: u8 = 32.scale8(sin8(uniforms.frame.wrapping_sub(coords.x))); - let value = max(30, inoise8(coords.x.wrapping_add(uniforms.frame) as i16, coords.y.wrapping_add(uniforms.frame) as i16)); - Hsv::new(uniforms.primary_color.hue.wrapping_sub(hue_offset), max(210, sin8(uniforms.frame.wrapping_add(coords.x))), value).into() + let hue_offset: u8 = 32u8 * Fract8::from_raw(uniforms.frame.wrapping_sub(coords.x) as u8); + let value = max(30, inoise8(coords.x.wrapping_add(uniforms.frame) as i16, coords.y.wrapping_add(uniforms.frame) as i16).to_raw()); + Hsv::new(uniforms.primary_color.hue.wrapping_sub(hue_offset), max(210, uniforms.frame.wrapping_add(coords.x).sin8().to_raw()), value).into() } } @@ -96,10 +97,10 @@ impl Shader> for Brakelight { let distance_from_end = Self::end() - coords.x; if distance_from_end < Self::safety_length() { - Rgba::new(max(128, sin8(uniforms.frame.wrapping_sub(coords.x))), 0, 0, 255) + Rgba::new(max(128, uniforms.frame.wrapping_sub(coords.x).sin8().to_raw()), 0, 0, 255) } else { let pct = (distance_from_end as f32 / Self::length() as f32) * 255f32; - Rgba::new(max(100, ease_in_out_quad((pct as u8).wrapping_add(uniforms.frame as u8))), 0, 0, ease_in_out_quad(255 - pct as u8)) + Rgba::new(max(100, ease_in_out_quad(Fract8::from_raw(pct as u8).wrapping_add(&Fract8::from_raw(uniforms.frame as u8))).to_raw()), 0, 0, ease_in_out_quad(Fract8::from_raw(255 - pct as u8)).to_raw()) } } } @@ -108,7 +109,7 @@ impl Shader> for Brakelight { pub struct Headlight {} impl Shader> for Headlight { fn draw(&self, coords: &Coordinates, uniforms: &Uniforms) -> Rgba { - Hsv::new(0, 0, max(130, ease_in_out_quad(sin8(uniforms.frame.wrapping_sub(coords.x))))).into() + Hsv::new(0, 0, max(130, ease_in_out_quad(uniforms.frame.wrapping_sub(coords.x).sin8()).to_raw())).into() } } @@ -116,14 +117,14 @@ impl Shader> for Headlight { pub struct Panel {} impl Shader> for Panel { fn draw(&self, coords: &Coordinates, uniforms: &Uniforms) -> Rgba { - let noise_offset = max(180, inoise8(coords.x.wrapping_add(uniforms.frame) as i16, coords.y.wrapping_add(uniforms.frame) as i16)); + let noise_offset = max(180, inoise8(coords.x.wrapping_add(uniforms.frame) as i16, coords.y.wrapping_add(uniforms.frame) as i16).to_raw()); let pct = (coords.x as f32 / 18f32) * 255f32; let shift = match coords.y { 1..=2 => 106, // 150 degrees 3..=4 => 148, // 210 degrees _ => 0 }; - Hsv::new(uniforms.primary_color.hue.wrapping_add(shift), noise_offset, max(100, sin8(pct as u8).wrapping_add(uniforms.frame as u8))).into() + Hsv::new(uniforms.primary_color.hue.wrapping_add(shift), noise_offset, max(100, pct.sin8().wrapping_add(&Fract8::from_raw(uniforms.frame as u8)).to_raw())).into() } } @@ -134,15 +135,15 @@ impl Shader> for Thinking { fn draw(&self, coords: &Coordinates, uniforms: &Uniforms) -> Rgba { //let noise_x = sin8(sin8((frame % 255) as u8).wrapping_add(coords.x)); //let noise_y = cos8(cos8((frame % 255) as u8).wrapping_add(coords.y)); - let offset_x = sin8(uniforms.frame.wrapping_add(coords.x)); - let offset_y = cos8(uniforms.frame.wrapping_add(coords.y)); + let offset_x = uniforms.frame.wrapping_add(coords.x).sin8(); + let offset_y = uniforms.frame.wrapping_add(coords.y).cos8(); let noise_x = offset_x / 2; let noise_y = offset_y / 2; //let noise_x = coords.x.wrapping_add(offset_x); //let noise_y = coords.y.wrapping_add(offset_y); Hsv::new( - inoise8(offset_x as i16, offset_y as i16), - 128_u8.saturating_add(inoise8(noise_y.into(), noise_x.into())), + inoise8(offset_x.to_raw() as i16, offset_y.to_raw() as i16).to_raw(), + 128_u8.saturating_add(inoise8(noise_y.to_raw().into(), noise_x.to_raw().into()).to_raw()), 255 ).into() } diff --git a/src/graphics/ssd1306.rs b/src/graphics/ssd1306.rs index dd42b10..75873bf 100644 --- a/src/graphics/ssd1306.rs +++ b/src/graphics/ssd1306.rs @@ -4,7 +4,7 @@ use core::{cmp::min, ops::{BitAnd, Shr}}; use display_interface::DisplayError; use embedded_graphics::prelude::*; use esp_hal::{gpio::Output, i2c::master::I2c, Async}; -use figments::{liber8tion::{interpolate::Fract8, noise}, mappings::embedded_graphics::Matrix2DSpace, prelude::*}; +use figments::{liber8tion::{interpolate::Fract8}, mappings::embedded_graphics::Matrix2DSpace, prelude::*}; use embedded_graphics::pixelcolor::BinaryColor; use figments::pixels::AdditivePixelSink; use figments_render::output::OutputAsync; @@ -59,8 +59,8 @@ const DITHER_MAP: [u16;15] = [ impl AdditivePixelSink for SsdPixel { fn add(&mut self, pixel: BinaryColor, opacity: Fract8) { match opacity { - 0 => (), - 255 => self.set_pixel(pixel), + Fract8::MIN => (), + Fract8::MAX => self.set_pixel(pixel), _ => { let dither_value = DITHER_MAP[opacity as usize / 17]; let dither_x = self.coords.x % 4; @@ -108,7 +108,7 @@ pub struct SsdOutput { target: ssd1306::Ssd1306Async>, ssd1306::prelude::DisplaySize128x64, ssd1306::mode::BasicMode>, controls: DisplayControls, is_on: bool, - last_brightness: u8, + last_brightness: Fract8, reset_pin: Output<'static> } @@ -132,7 +132,7 @@ impl SsdOutput { pixbuf: [0; 128 * 64 / 8], target, controls, - last_brightness: 255, + last_brightness: Fract8::MAX, is_on: true, reset_pin } diff --git a/src/tasks/oled.rs b/src/tasks/oled.rs index cd97987..5e4aabb 100644 --- a/src/tasks/oled.rs +++ b/src/tasks/oled.rs @@ -2,7 +2,7 @@ use alloc::sync::Arc; use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex, pubsub::DynSubscriber}; use embassy_time::{Duration, Timer}; use embedded_graphics::pixelcolor::BinaryColor; -use figments::{mappings::embedded_graphics::Matrix2DSpace, prelude::{Coordinates, Rectangle}, render::Shader, surface::{BufferedSurfacePool, NullBufferPool, Surface, SurfaceBuilder, Surfaces}}; +use figments::{liber8tion::interpolate::Fract8, mappings::embedded_graphics::Matrix2DSpace, prelude::{Coordinates, Rectangle}, render::Shader, surface::{Surface, SurfaceBuilder, Surfaces}}; use figments_render::output::Brightness; use log::*; @@ -37,7 +37,7 @@ impl = Animation::new().from(Fract8::MIN).to(Fract8::MAX).duration(Duration::from_millis(300)); + const FADE_OUT: Animation = Animation::new().from(Fract8::MAX).to(Fract8::MIN).duration(Duration::from_millis(300)); info!("Fading in to screen {next_screen:?}"); FADE_IN.apply(&mut self.overlay).await; {