diff --git a/Cargo.lock b/Cargo.lock index 2147a08..e747ab6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -14,6 +23,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" +[[package]] +name = "bytemuck" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" + [[package]] name = "byteorder" version = "1.5.0" @@ -59,6 +74,7 @@ dependencies = [ "embedded-graphics", "embedded-graphics-core", "memmap2", + "nalgebra", ] [[package]] @@ -73,6 +89,16 @@ version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +[[package]] +name = "matrixmultiply" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +dependencies = [ + "autocfg", + "rawpointer", +] + [[package]] name = "memmap2" version = "0.9.9" @@ -125,6 +151,61 @@ dependencies = [ "syn", ] +[[package]] +name = "nalgebra" +version = "0.32.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5c17de023a86f59ed79891b2e5d5a94c705dbe904a5b5c9c952ea6221b03e4" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -151,6 +232,12 @@ dependencies = [ "oc2r-core", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "proc-macro2" version = "1.0.103" @@ -169,12 +256,40 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "safe_arch" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "simba" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + [[package]] name = "syn" version = "2.0.108" @@ -186,8 +301,24 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "wide" +version = "0.7.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" +dependencies = [ + "bytemuck", + "safe_arch", +] diff --git a/crates/graphics/Cargo.toml b/crates/graphics/Cargo.toml index cb4e0a3..06a1c4d 100644 --- a/crates/graphics/Cargo.toml +++ b/crates/graphics/Cargo.toml @@ -15,4 +15,4 @@ workspace = true memmap2 = "0.9" embedded-graphics = "0.8" embedded-graphics-core = "0.4" - +nalgebra = "0.32" diff --git a/crates/graphics/src/main.rs b/crates/graphics/src/main.rs index 46f3e64..3036bf3 100644 --- a/crates/graphics/src/main.rs +++ b/crates/graphics/src/main.rs @@ -3,16 +3,18 @@ clippy::cast_sign_loss, clippy::cast_possible_wrap )] -use std::{convert::Infallible, fs::OpenOptions, io, thread, time::Duration}; +use std::{fs::OpenOptions, io, thread, time::Duration}; use embedded_graphics::{ geometry::Size, - mono_font::{MonoTextStyle, ascii::FONT_6X10}, pixelcolor::Rgb565, - prelude::*, - text::{Baseline, Text}, + prelude::{ + DrawTarget, Drawable, IntoStorage, OriginDimensions, Pixel, Point, Primitive, RgbColor, + }, + primitives::{Line, PrimitiveStyle}, }; use memmap2::{MmapMut, MmapOptions}; +use nalgebra::{Point3, Rotation3, Vector3}; const FB_PATH: &str = "/dev/fb0"; const WIDTH: u32 = 640; @@ -25,39 +27,27 @@ struct FbDisplay { impl FbDisplay { fn new() -> io::Result { - println!("Opening framebuffer device at {FB_PATH}"); let file = OpenOptions::new().read(true).write(true).open(FB_PATH)?; let fb_size = (WIDTH as usize) * (HEIGHT as usize) * BYTES_PER_PIXEL; // Safety: map /dev/fb0 as writable memory let fb = unsafe { MmapOptions::new().len(fb_size).map_mut(&file)? }; - println!( - "Mapped framebuffer: {}x{} at {} bytes ({} bytes per pixel)", - WIDTH, - HEIGHT, - fb.len(), - BYTES_PER_PIXEL - ); Ok(Self { fb }) } fn clear(&mut self) { - println!("Clearing framebuffer to black"); - for chunk in self.fb.chunks_exact_mut(BYTES_PER_PIXEL) { - chunk.copy_from_slice(&[0x00, 0x00]); // RGB565 black - } + self.fb.fill(0); } - fn put_pixel(&mut self, x: u32, y: u32, color: Rgb565) { - if x >= WIDTH || y >= HEIGHT { + fn put_pixel(&mut self, x: i32, y: i32, color: Rgb565) { + if x < 0 || y < 0 || x as u32 >= WIDTH || y as u32 >= HEIGHT { return; } - let idx = ((y * WIDTH + x) as usize) * BYTES_PER_PIXEL; + let idx = ((y as u32 * WIDTH + x as u32) as usize) * BYTES_PER_PIXEL; if idx + 1 >= self.fb.len() { return; } - // Rgb565 stores 16-bit value; little endian in memory. let raw: u16 = color.into_storage(); let bytes = raw.to_le_bytes(); self.fb[idx] = bytes[0]; @@ -67,18 +57,14 @@ impl FbDisplay { impl DrawTarget for FbDisplay { type Color = Rgb565; - type Error = Infallible; + type Error = core::convert::Infallible; fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> where I: IntoIterator>, { for Pixel(coord, color) in pixels { - let x = coord.x; - let y = coord.y; - if x >= 0 && y >= 0 { - self.put_pixel(x as u32, y as u32, color); - } + self.put_pixel(coord.x, coord.y, color); } Ok(()) } @@ -94,26 +80,52 @@ pub fn main() -> Result<(), Box> { let mut fb = FbDisplay::new()?; fb.clear(); - let style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE); + let mut cube = [ + Point3::new(-1.0, -1.0, -1.0), + Point3::new(1.0, -1.0, -1.0), + Point3::new(1.0, 1.0, -1.0), + Point3::new(-1.0, 1.0, -1.0), + Point3::new(-1.0, -1.0, 1.0), + Point3::new(1.0, -1.0, 1.0), + Point3::new(1.0, 1.0, 1.0), + Point3::new(-1.0, 1.0, 1.0), + ]; - let text = "Hello embedded-graphics on RGB565"; + let scale = 60.0; + for edge in &mut cube { + *edge *= scale; + } - let text_width = (text.len() as u32) * 6; - let text_height = 10; + let angle_step = 0.06f32; + let rot_step = Rotation3::from_axis_angle(&Vector3::x_axis(), angle_step) + * Rotation3::from_axis_angle(&Vector3::y_axis(), angle_step * 0.8) + * Rotation3::from_axis_angle(&Vector3::z_axis(), angle_step * 0.6); - let x = (WIDTH - text_width) / 2; - let y = (HEIGHT / 2) + (text_height / 2); + let mut projected = [Point::new(0, 0); 8]; + let center_x = (WIDTH / 2) as i32; + let center_y = (HEIGHT / 2) as i32; + let style = PrimitiveStyle::with_stroke(Rgb565::RED, 1); - Text::with_baseline( - text, - Point::new(x as i32, y as i32), - style, - Baseline::Bottom, - ) - .draw(&mut fb)?; - - // Keep the framebuffer content visible loop { - thread::sleep(Duration::from_secs(1)); + fb.clear(); + + for (i, edge) in cube.iter_mut().enumerate() { + *edge = rot_step * *edge; + projected[i] = Point::new(edge.x as i32 + center_x, edge.y as i32 + center_y); + } + + for i in 0..4 { + Line::new(projected[i], projected[(i + 1) % 4]) + .into_styled(style) + .draw(&mut fb)?; + Line::new(projected[i + 4], projected[((i + 1) % 4) + 4]) + .into_styled(style) + .draw(&mut fb)?; + Line::new(projected[i], projected[i + 4]) + .into_styled(style) + .draw(&mut fb)?; + } + + thread::sleep(Duration::from_millis(40)); } }