From e42feadb22c1e383f8ebe5083042b72445d0218b Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Tue, 12 Mar 2024 21:55:31 -0700 Subject: [PATCH 1/9] feat: simple embedded graphics driver for the V5 display --- packages/pros-graphics/Cargo.toml | 19 +++++++ packages/pros-graphics/src/lib.rs | 82 +++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 packages/pros-graphics/Cargo.toml create mode 100644 packages/pros-graphics/src/lib.rs diff --git a/packages/pros-graphics/Cargo.toml b/packages/pros-graphics/Cargo.toml new file mode 100644 index 00000000..580dbeda --- /dev/null +++ b/packages/pros-graphics/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "pros-graphics" +version = "0.1.0" +edition = "2021" +license = "MIT" +description = "Core functionality for pros-rs" +keywords = ["PROS", "Robotics", "graphics", "vex", "v5"] +categories = [ + "api-bindings", + "no-std", + "science::robotics", +] + +[dependencies] +embedded-graphics-core = "0.4.0" +pros-devices = { version = "0.1.0", path = "../pros-devices" } + +[lints] +workspace = true diff --git a/packages/pros-graphics/src/lib.rs b/packages/pros-graphics/src/lib.rs new file mode 100644 index 00000000..7e5d2d14 --- /dev/null +++ b/packages/pros-graphics/src/lib.rs @@ -0,0 +1,82 @@ +//! An embedded_graphics driver for VEX V5 Brain displays. +//! Implemented for the [`pros-rs`](https://crates.io/crates/pros) ecosystem and implemented using [pros-devices](https://crates.io/crates/pros-devices). +#![no_std] + +use embedded_graphics_core::{ + draw_target::DrawTarget, + geometry::{Dimensions, Point, Size}, + pixelcolor::{Rgb888, RgbColor}, + primitives::Rectangle, + Pixel, +}; +use pros_devices::{ + color::Rgb, + Screen, +}; + +/// An embedded_graphics driver for the Brain display +pub struct VexDisplay { + screen: Screen, + pixel_buffer: [[Rgb; Screen::HORIZONTAL_RESOLUTION as _]; Screen::VERTICAL_RESOLUTION as _], +} + +impl VexDisplay { + /// Creates a new VexDisplay from a Screen + pub const fn new(screen: Screen) -> Self { + Self { + screen, + pixel_buffer: [[Rgb::new(0, 0, 0); Screen::HORIZONTAL_RESOLUTION as _]; + Screen::VERTICAL_RESOLUTION as _], + } + } +} + +impl From for VexDisplay { + fn from(value: Screen) -> Self { + Self::new(value) + } +} + +impl Dimensions for VexDisplay { + fn bounding_box(&self) -> Rectangle { + Rectangle::new( + Point::new(0, 0), + Size::new( + Screen::HORIZONTAL_RESOLUTION as _, + Screen::VERTICAL_RESOLUTION as _, + ), + ) + } +} + +impl DrawTarget for VexDisplay { + type Color = Rgb888; + type Error = pros_devices::screen::ScreenError; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + pixels + .into_iter() + .map(|pixel| (pixel.0, Rgb::new(pixel.1.r(), pixel.1.g(), pixel.1.b()))) + .for_each(|(pos, color)| { + // Make sure that the coordinate is valid to index with. + if !(pos.x > Screen::HORIZONTAL_RESOLUTION as _ || pos.x < 0) + && !(pos.y > Screen::VERTICAL_RESOLUTION as _ || pos.y < 0) + { + self.pixel_buffer[pos.y as usize][pos.x as usize] = color + } + }); + + self.screen.draw_buffer( + 0, + 0, + Screen::HORIZONTAL_RESOLUTION, + Screen::VERTICAL_RESOLUTION, + self.pixel_buffer.clone().into_iter().flatten(), + Screen::HORIZONTAL_RESOLUTION as _, + )?; + Ok(()) + } +} From 528b6a9d932c191f3c85cf45ec00d954a50d7405 Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Tue, 12 Mar 2024 21:56:43 -0700 Subject: [PATCH 2/9] chore: fmt --- packages/pros-graphics/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/pros-graphics/src/lib.rs b/packages/pros-graphics/src/lib.rs index 7e5d2d14..7c83a0a2 100644 --- a/packages/pros-graphics/src/lib.rs +++ b/packages/pros-graphics/src/lib.rs @@ -9,10 +9,7 @@ use embedded_graphics_core::{ primitives::Rectangle, Pixel, }; -use pros_devices::{ - color::Rgb, - Screen, -}; +use pros_devices::{color::Rgb, Screen}; /// An embedded_graphics driver for the Brain display pub struct VexDisplay { From 12ca5467b15cbf170107e1a048ad8e6942b54a54 Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Wed, 13 Mar 2024 01:15:20 -0700 Subject: [PATCH 3/9] fix: put pixel buffer on heap to prevent stack overflows --- flake.lock | 115 ++++++++++++++++++++++++------ packages/pros-graphics/Cargo.toml | 1 + packages/pros-graphics/src/lib.rs | 44 ++++++++---- 3 files changed, 124 insertions(+), 36 deletions(-) diff --git a/flake.lock b/flake.lock index 6fded360..46ebd0b4 100644 --- a/flake.lock +++ b/flake.lock @@ -3,14 +3,15 @@ "cargo-pros": { "inputs": { "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "pros-cli-nix": "pros-cli-nix" }, "locked": { - "lastModified": 1703362525, - "narHash": "sha256-QPFRaWCDlHKyFu4P4HCsw8pKkJcznup0NIwOjGkkI7g=", + "lastModified": 1709775910, + "narHash": "sha256-PF60KNzSX+Z2zrk1KLAPtrzsnoW7A9k8r60zmZbsyQ4=", "owner": "pros-rs", "repo": "cargo-pros", - "rev": "479331022e4a74a62eaa1f776325c690e449f5dd", + "rev": "fc8d1f336cd09314b69a7810a59e0c0e4d65a252", "type": "github" }, "original": { @@ -36,16 +37,33 @@ "type": "indirect" } }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib_2" + }, + "locked": { + "lastModified": 1688254665, + "narHash": "sha256-8FHEgBrr7gYNiS/NzCxIO3m4hvtLRW9YY1nYo1ivm3o=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "267149c58a14d15f7f81b4d737308421de9d7152", + "type": "github" + }, + "original": { + "id": "flake-parts", + "type": "indirect" + } + }, "flake-utils": { "inputs": { "systems": "systems" }, "locked": { - "lastModified": 1692799911, - "narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=", + "lastModified": 1709126324, + "narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=", "owner": "numtide", "repo": "flake-utils", - "rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44", + "rev": "d465f4819400de7c8d874d50b982301f28a84605", "type": "github" }, "original": { @@ -59,11 +77,11 @@ "systems": "systems_2" }, "locked": { - "lastModified": 1694529238, - "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -74,11 +92,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1694062546, - "narHash": "sha256-PiGI4f2BGnZcedP6slLjCLGLRLXPa9+ogGGgVPfGxys=", + "lastModified": 1709675310, + "narHash": "sha256-w61tqFEmuJ+/1rAwU7nkYZ+dN6sLwyobfLwX2Yn42FE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b200e0df08f80c32974a6108ce431d8a8a5e6547", + "rev": "43d259f8d726113fac056e8bb17d5ac2dea3e0a8", "type": "github" }, "original": { @@ -104,21 +122,53 @@ "type": "github" } }, + "nixpkgs-lib_2": { + "locked": { + "dir": "lib", + "lastModified": 1688049487, + "narHash": "sha256-100g4iaKC9MalDjUW9iN6Jl/OocTDtXdeAj7pEGIRh4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "4bc72cae107788bf3f24f30db2e2f685c9298dc9", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs_2": { "locked": { - "lastModified": 1681358109, - "narHash": "sha256-eKyxW4OohHQx9Urxi7TQlFBTDWII+F+x2hklDOQPB50=", + "lastModified": 1688231357, + "narHash": "sha256-ZOn16X5jZ6X5ror58gOJAxPfFLAQhZJ6nOUeS4tfFwo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "96ba1c52e54e74c3197f4d43026b3f3d92e83ff9", + "rev": "645ff62e09d294a30de823cb568e9c6d68e92606", "type": "github" }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 0, + "narHash": "sha256-n6F0n8UV6lnTZbYPl1A9q1BS0p4hduAv1mGAP17CVd0=", + "path": "/nix/store/bjvqq8c79dbi59g7xzcc6lhl0f19m3d7-source", + "type": "path" + }, "original": { "id": "nixpkgs", "type": "indirect" } }, - "nixpkgs_3": { + "nixpkgs_4": { "locked": { "lastModified": 1688231357, "narHash": "sha256-ZOn16X5jZ6X5ror58gOJAxPfFLAQhZJ6nOUeS4tfFwo=", @@ -137,14 +187,33 @@ "pros-cli-nix": { "inputs": { "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs_3" + "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1691848327, - "narHash": "sha256-bGijqyZb+orLxmY541hUdVJKa44VB6iV2Aviwn5Ji5k=", + "lastModified": 1706024690, + "narHash": "sha256-SVzHiRW6edEhN/etZvG6T6h54z+LySxNWOi179ly8/4=", "owner": "BattleCh1cken", "repo": "pros-cli-nix", - "rev": "4c00d8e3c6caa377e67ce0cf84923feba5533f23", + "rev": "3c2c47944735013c6252b16e086534b76d55bf36", + "type": "github" + }, + "original": { + "owner": "BattleCh1cken", + "repo": "pros-cli-nix", + "type": "github" + } + }, + "pros-cli-nix_2": { + "inputs": { + "flake-parts": "flake-parts_2", + "nixpkgs": "nixpkgs_4" + }, + "locked": { + "lastModified": 1706024690, + "narHash": "sha256-SVzHiRW6edEhN/etZvG6T6h54z+LySxNWOi179ly8/4=", + "owner": "BattleCh1cken", + "repo": "pros-cli-nix", + "rev": "3c2c47944735013c6252b16e086534b76d55bf36", "type": "github" }, "original": { @@ -157,8 +226,8 @@ "inputs": { "cargo-pros": "cargo-pros", "flake-utils": "flake-utils_2", - "nixpkgs": "nixpkgs_2", - "pros-cli-nix": "pros-cli-nix" + "nixpkgs": "nixpkgs_3", + "pros-cli-nix": "pros-cli-nix_2" } }, "systems": { diff --git a/packages/pros-graphics/Cargo.toml b/packages/pros-graphics/Cargo.toml index 580dbeda..a965a99f 100644 --- a/packages/pros-graphics/Cargo.toml +++ b/packages/pros-graphics/Cargo.toml @@ -14,6 +14,7 @@ categories = [ [dependencies] embedded-graphics-core = "0.4.0" pros-devices = { version = "0.1.0", path = "../pros-devices" } +pros-sys = { version = "0.7.0", path = "../pros-sys" } [lints] workspace = true diff --git a/packages/pros-graphics/src/lib.rs b/packages/pros-graphics/src/lib.rs index 7c83a0a2..33d3ed53 100644 --- a/packages/pros-graphics/src/lib.rs +++ b/packages/pros-graphics/src/lib.rs @@ -1,6 +1,11 @@ //! An embedded_graphics driver for VEX V5 Brain displays. //! Implemented for the [`pros-rs`](https://crates.io/crates/pros) ecosystem and implemented using [pros-devices](https://crates.io/crates/pros-devices). #![no_std] +#![feature(new_uninit)] + +extern crate alloc; + +use alloc::boxed::Box; use embedded_graphics_core::{ draw_target::DrawTarget, @@ -14,16 +19,32 @@ use pros_devices::{color::Rgb, Screen}; /// An embedded_graphics driver for the Brain display pub struct VexDisplay { screen: Screen, - pixel_buffer: [[Rgb; Screen::HORIZONTAL_RESOLUTION as _]; Screen::VERTICAL_RESOLUTION as _], + pixel_buffer: + Box<[u32; Screen::HORIZONTAL_RESOLUTION as usize * Screen::VERTICAL_RESOLUTION as usize]>, } impl VexDisplay { /// Creates a new VexDisplay from a Screen - pub const fn new(screen: Screen) -> Self { + pub fn new(screen: Screen) -> Self { + let pixel_buffer = Box::new_zeroed(); + let pixel_buffer = unsafe { pixel_buffer.assume_init() }; + Self { screen, - pixel_buffer: [[Rgb::new(0, 0, 0); Screen::HORIZONTAL_RESOLUTION as _]; - Screen::VERTICAL_RESOLUTION as _], + pixel_buffer, + } + } + + unsafe fn draw_buffer(&self) { + unsafe { + pros_sys::screen_copy_area( + 0, + 0, + Screen::HORIZONTAL_RESOLUTION, + Screen::VERTICAL_RESOLUTION, + self.pixel_buffer.as_ptr(), + Screen::HORIZONTAL_RESOLUTION as _, + ); } } } @@ -62,18 +83,15 @@ impl DrawTarget for VexDisplay { if !(pos.x > Screen::HORIZONTAL_RESOLUTION as _ || pos.x < 0) && !(pos.y > Screen::VERTICAL_RESOLUTION as _ || pos.y < 0) { - self.pixel_buffer[pos.y as usize][pos.x as usize] = color + // SAFETY: We initialize the buffer with zeroes, so it's safe to assume it's initialized. + self.pixel_buffer[(pos.y as usize * Screen::HORIZONTAL_RESOLUTION as usize) + + pos.x as usize] = color.into(); } }); - self.screen.draw_buffer( - 0, - 0, - Screen::HORIZONTAL_RESOLUTION, - Screen::VERTICAL_RESOLUTION, - self.pixel_buffer.clone().into_iter().flatten(), - Screen::HORIZONTAL_RESOLUTION as _, - )?; + unsafe { + self.draw_buffer(); + } Ok(()) } } From d987f4eeee3a9de5274665b2007d67d3fa256ea4 Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Wed, 13 Mar 2024 11:34:23 -0700 Subject: [PATCH 4/9] refactor: improve driver code quality --- packages/pros-graphics/src/lib.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/pros-graphics/src/lib.rs b/packages/pros-graphics/src/lib.rs index 33d3ed53..770404a1 100644 --- a/packages/pros-graphics/src/lib.rs +++ b/packages/pros-graphics/src/lib.rs @@ -18,24 +18,27 @@ use pros_devices::{color::Rgb, Screen}; /// An embedded_graphics driver for the Brain display pub struct VexDisplay { - screen: Screen, pixel_buffer: Box<[u32; Screen::HORIZONTAL_RESOLUTION as usize * Screen::VERTICAL_RESOLUTION as usize]>, } impl VexDisplay { /// Creates a new VexDisplay from a Screen - pub fn new(screen: Screen) -> Self { + pub fn new(_screen: Screen) -> Self { let pixel_buffer = Box::new_zeroed(); let pixel_buffer = unsafe { pixel_buffer.assume_init() }; - Self { - screen, - pixel_buffer, - } + Self { pixel_buffer } } - unsafe fn draw_buffer(&self) { + /// Draws the pixel buffer to the screen + /// + /// # Note + /// + /// I would use the [`Screen::draw_buffer`](pros_devices::screen::Screen::draw_buffer) API, + /// but unfortunately it stack overflows with a buffer this big and is more complicated. + fn draw_buffer(&self) { + // SAFETY: The pixel buffer is guarenteed to be large enough and live long enough and we take ownership of the screen when created. unsafe { pros_sys::screen_copy_area( 0, @@ -84,14 +87,13 @@ impl DrawTarget for VexDisplay { && !(pos.y > Screen::VERTICAL_RESOLUTION as _ || pos.y < 0) { // SAFETY: We initialize the buffer with zeroes, so it's safe to assume it's initialized. - self.pixel_buffer[(pos.y as usize * Screen::HORIZONTAL_RESOLUTION as usize) + self.pixel_buffer[pos.y as usize * Screen::HORIZONTAL_RESOLUTION as usize + pos.x as usize] = color.into(); } }); - unsafe { - self.draw_buffer(); - } + self.draw_buffer(); + Ok(()) } } From 172d070fe2b1a72f522ec58e9091ceed8cf22d48 Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Wed, 13 Mar 2024 13:07:56 -0700 Subject: [PATCH 5/9] feat: add graphics feature to pros --- packages/pros/Cargo.toml | 2 ++ packages/pros/src/lib.rs | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/pros/Cargo.toml b/packages/pros/Cargo.toml index 97e368fc..f751e8d8 100644 --- a/packages/pros/Cargo.toml +++ b/packages/pros/Cargo.toml @@ -21,6 +21,7 @@ rust-version = "1.75.0" pros-sync = { version = "0.1.0", path = "../pros-sync", optional = true } pros-async = { version = "0.1.0", path = "../pros-async", optional = true } pros-devices = { version = "0.1.0", path = "../pros-devices", optional = true } +pros-graphics = { version = "0.1.0", path = "../pros-graphics", optional = true } pros-panic = { version = "0.1.0", path = "../pros-panic", optional = true } pros-core = { version = "0.1.0", path = "../pros-core", optional = true } pros-math = { version = "0.1.0", path = "../pros-math", optional = true } @@ -35,6 +36,7 @@ async = ["dep:pros-async"] sync = ["dep:pros-sync"] devices = ["dep:pros-devices"] +graphics = ["dep:pros-graphics"] math = ["dep:pros-math"] diff --git a/packages/pros/src/lib.rs b/packages/pros/src/lib.rs index e14accd7..d9e9e801 100644 --- a/packages/pros/src/lib.rs +++ b/packages/pros/src/lib.rs @@ -61,6 +61,8 @@ pub use pros_async as async_runtime; pub use pros_core as core; #[cfg(feature = "devices")] pub use pros_devices as devices; +#[cfg(feature = "graphics")] +pub use pros_graphics as graphics; #[cfg(feature = "math")] pub use pros_math as math; #[cfg(feature = "panic")] @@ -82,6 +84,10 @@ pub mod prelude { print, println, task::delay, }; + #[cfg(all(not(feature = "graphics"), feature = "devices"))] + pub use pros_devices::screen::{ + Circle, Line, Rect, Text, TextFormat, TextPosition, TouchState, + }; #[cfg(feature = "devices")] pub use pros_devices::{ adi::{ @@ -99,7 +105,7 @@ pub mod prelude { color::Rgb, peripherals::{DynamicPeripherals, Peripherals}, position::Position, - screen::{Circle, Line, Rect, Screen, Text, TextFormat, TextPosition, TouchState}, + screen::Screen, smart::{ distance::DistanceSensor, expander::AdiExpander, @@ -113,6 +119,8 @@ pub mod prelude { SmartDevice, SmartPort, }, }; + #[cfg(feature = "graphics")] + pub use pros_graphics::VexDisplay; #[cfg(feature = "math")] pub use pros_math::{feedforward::MotorFeedforwardController, pid::PidController}; #[cfg(feature = "sync")] From 567f0c14aef4c049f40cc46a904279d4a58f9e73 Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Wed, 13 Mar 2024 13:44:03 -0700 Subject: [PATCH 6/9] refactor: rename VexDisplay to V5BrainDisplay --- packages/pros-graphics/src/lib.rs | 12 ++++++------ packages/pros/src/lib.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/pros-graphics/src/lib.rs b/packages/pros-graphics/src/lib.rs index 770404a1..9b226b52 100644 --- a/packages/pros-graphics/src/lib.rs +++ b/packages/pros-graphics/src/lib.rs @@ -16,13 +16,13 @@ use embedded_graphics_core::{ }; use pros_devices::{color::Rgb, Screen}; -/// An embedded_graphics driver for the Brain display -pub struct VexDisplay { +/// An embedded_graphics driver for the V5 Brain display +pub struct V5BrainDisplay { pixel_buffer: Box<[u32; Screen::HORIZONTAL_RESOLUTION as usize * Screen::VERTICAL_RESOLUTION as usize]>, } -impl VexDisplay { +impl V5BrainDisplay { /// Creates a new VexDisplay from a Screen pub fn new(_screen: Screen) -> Self { let pixel_buffer = Box::new_zeroed(); @@ -52,13 +52,13 @@ impl VexDisplay { } } -impl From for VexDisplay { +impl From for V5BrainDisplay { fn from(value: Screen) -> Self { Self::new(value) } } -impl Dimensions for VexDisplay { +impl Dimensions for V5BrainDisplay { fn bounding_box(&self) -> Rectangle { Rectangle::new( Point::new(0, 0), @@ -70,7 +70,7 @@ impl Dimensions for VexDisplay { } } -impl DrawTarget for VexDisplay { +impl DrawTarget for V5BrainDisplay { type Color = Rgb888; type Error = pros_devices::screen::ScreenError; diff --git a/packages/pros/src/lib.rs b/packages/pros/src/lib.rs index d9e9e801..5164c80b 100644 --- a/packages/pros/src/lib.rs +++ b/packages/pros/src/lib.rs @@ -120,7 +120,7 @@ pub mod prelude { }, }; #[cfg(feature = "graphics")] - pub use pros_graphics::VexDisplay; + pub use pros_graphics::V5BrainDisplay; #[cfg(feature = "math")] pub use pros_math::{feedforward::MotorFeedforwardController, pid::PidController}; #[cfg(feature = "sync")] From 9ccac1fca1496433b1cfe2473f406758a4955802 Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Wed, 13 Mar 2024 13:46:35 -0700 Subject: [PATCH 7/9] docs: update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8942371..71c7e7f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,8 @@ Before releasing: - Added support for getting motor fault flags (e.g. over-temperature, over-current, H-bridge faults). - Added support for internal motor PID tuning. Feature gated behind `dangerous_motor_tuning`, as this can cause hardware damage and is not recommended. - Added various constants for convenience around `Motor` and `Gearset`. +- An ``embedded_graphics`` driver crate (pros_graphics). +- New `graphics` feature in the `pros` crate. ### Fixed From 4f2c77897ce75de47152fd68912c6e3c7b48403e Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Wed, 13 Mar 2024 19:52:43 -0700 Subject: [PATCH 8/9] refactor: allow for multiple graphics drivers in the future --- packages/pros-graphics/Cargo.toml | 6 +- .../pros-graphics/src/embedded_graphics.rs | 94 ++++++++++++++++ packages/pros-graphics/src/lib.rs | 102 ++---------------- packages/pros/Cargo.toml | 2 + 4 files changed, 109 insertions(+), 95 deletions(-) create mode 100644 packages/pros-graphics/src/embedded_graphics.rs diff --git a/packages/pros-graphics/Cargo.toml b/packages/pros-graphics/Cargo.toml index a965a99f..9a8935fb 100644 --- a/packages/pros-graphics/Cargo.toml +++ b/packages/pros-graphics/Cargo.toml @@ -12,9 +12,13 @@ categories = [ ] [dependencies] -embedded-graphics-core = "0.4.0" +embedded-graphics-core = { version = "0.4.0", optional = true } pros-devices = { version = "0.1.0", path = "../pros-devices" } pros-sys = { version = "0.7.0", path = "../pros-sys" } [lints] workspace = true + +[features] +default = [] +embedded-graphics = ["dep:embedded-graphics-core"] diff --git a/packages/pros-graphics/src/embedded_graphics.rs b/packages/pros-graphics/src/embedded_graphics.rs new file mode 100644 index 00000000..0e74a0b3 --- /dev/null +++ b/packages/pros-graphics/src/embedded_graphics.rs @@ -0,0 +1,94 @@ +//! An embedded_graphics driver for VEX V5 Brain displays. + +use alloc::boxed::Box; + +use embedded_graphics_core::{ + draw_target::DrawTarget, + geometry::{Dimensions, Point, Size}, + pixelcolor::{Rgb888, RgbColor}, + primitives::Rectangle, + Pixel, +}; +use pros_devices::{color::Rgb, Screen}; + +/// An embedded_graphics driver for the V5 Brain display +pub struct V5BrainDisplay { + pixel_buffer: + Box<[u32; Screen::HORIZONTAL_RESOLUTION as usize * Screen::VERTICAL_RESOLUTION as usize]>, +} + +impl V5BrainDisplay { + /// Creates a new VexDisplay from a Screen + pub fn new(_screen: Screen) -> Self { + let pixel_buffer = Box::new_zeroed(); + let pixel_buffer = unsafe { pixel_buffer.assume_init() }; + + Self { pixel_buffer } + } + + /// Draws the pixel buffer to the screen + /// + /// # Note + /// + /// I would use the [`Screen::draw_buffer`](pros_devices::screen::Screen::draw_buffer) API, + /// but unfortunately it stack overflows with a buffer this big and is more complicated. + fn draw_buffer(&self) { + // SAFETY: The pixel buffer is guarenteed to be large enough and live long enough and we take ownership of the screen when created. + unsafe { + pros_sys::screen_copy_area( + 0, + 0, + Screen::HORIZONTAL_RESOLUTION, + Screen::VERTICAL_RESOLUTION, + self.pixel_buffer.as_ptr(), + Screen::HORIZONTAL_RESOLUTION as _, + ); + } + } +} + +impl From for V5BrainDisplay { + fn from(value: Screen) -> Self { + Self::new(value) + } +} + +impl Dimensions for V5BrainDisplay { + fn bounding_box(&self) -> Rectangle { + Rectangle::new( + Point::new(0, 0), + Size::new( + Screen::HORIZONTAL_RESOLUTION as _, + Screen::VERTICAL_RESOLUTION as _, + ), + ) + } +} + +impl DrawTarget for V5BrainDisplay { + type Color = Rgb888; + type Error = pros_devices::screen::ScreenError; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + pixels + .into_iter() + .map(|pixel| (pixel.0, Rgb::new(pixel.1.r(), pixel.1.g(), pixel.1.b()))) + .for_each(|(pos, color)| { + // Make sure that the coordinate is valid to index with. + if !(pos.x > Screen::HORIZONTAL_RESOLUTION as _ || pos.x < 0) + && !(pos.y > Screen::VERTICAL_RESOLUTION as _ || pos.y < 0) + { + // SAFETY: We initialize the buffer with zeroes, so it's safe to assume it's initialized. + self.pixel_buffer[pos.y as usize * Screen::HORIZONTAL_RESOLUTION as usize + + pos.x as usize] = color.into(); + } + }); + + self.draw_buffer(); + + Ok(()) + } +} diff --git a/packages/pros-graphics/src/lib.rs b/packages/pros-graphics/src/lib.rs index 9b226b52..aaec18cf 100644 --- a/packages/pros-graphics/src/lib.rs +++ b/packages/pros-graphics/src/lib.rs @@ -1,99 +1,13 @@ -//! An embedded_graphics driver for VEX V5 Brain displays. +//! Graphics driver implementations for the V5 Brain display. +//! +//! Currently supports: +//! - [embedded-graphics](https://crates.io/crates/embedded-graphics) //! Implemented for the [`pros-rs`](https://crates.io/crates/pros) ecosystem and implemented using [pros-devices](https://crates.io/crates/pros-devices). #![no_std] -#![feature(new_uninit)] +#![cfg_attr(feature = "embedded-graphics", feature(new_uninit))] +#[cfg(feature = "embedded-graphics")] extern crate alloc; -use alloc::boxed::Box; - -use embedded_graphics_core::{ - draw_target::DrawTarget, - geometry::{Dimensions, Point, Size}, - pixelcolor::{Rgb888, RgbColor}, - primitives::Rectangle, - Pixel, -}; -use pros_devices::{color::Rgb, Screen}; - -/// An embedded_graphics driver for the V5 Brain display -pub struct V5BrainDisplay { - pixel_buffer: - Box<[u32; Screen::HORIZONTAL_RESOLUTION as usize * Screen::VERTICAL_RESOLUTION as usize]>, -} - -impl V5BrainDisplay { - /// Creates a new VexDisplay from a Screen - pub fn new(_screen: Screen) -> Self { - let pixel_buffer = Box::new_zeroed(); - let pixel_buffer = unsafe { pixel_buffer.assume_init() }; - - Self { pixel_buffer } - } - - /// Draws the pixel buffer to the screen - /// - /// # Note - /// - /// I would use the [`Screen::draw_buffer`](pros_devices::screen::Screen::draw_buffer) API, - /// but unfortunately it stack overflows with a buffer this big and is more complicated. - fn draw_buffer(&self) { - // SAFETY: The pixel buffer is guarenteed to be large enough and live long enough and we take ownership of the screen when created. - unsafe { - pros_sys::screen_copy_area( - 0, - 0, - Screen::HORIZONTAL_RESOLUTION, - Screen::VERTICAL_RESOLUTION, - self.pixel_buffer.as_ptr(), - Screen::HORIZONTAL_RESOLUTION as _, - ); - } - } -} - -impl From for V5BrainDisplay { - fn from(value: Screen) -> Self { - Self::new(value) - } -} - -impl Dimensions for V5BrainDisplay { - fn bounding_box(&self) -> Rectangle { - Rectangle::new( - Point::new(0, 0), - Size::new( - Screen::HORIZONTAL_RESOLUTION as _, - Screen::VERTICAL_RESOLUTION as _, - ), - ) - } -} - -impl DrawTarget for V5BrainDisplay { - type Color = Rgb888; - type Error = pros_devices::screen::ScreenError; - - fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> - where - I: IntoIterator>, - { - pixels - .into_iter() - .map(|pixel| (pixel.0, Rgb::new(pixel.1.r(), pixel.1.g(), pixel.1.b()))) - .for_each(|(pos, color)| { - // Make sure that the coordinate is valid to index with. - if !(pos.x > Screen::HORIZONTAL_RESOLUTION as _ || pos.x < 0) - && !(pos.y > Screen::VERTICAL_RESOLUTION as _ || pos.y < 0) - { - // SAFETY: We initialize the buffer with zeroes, so it's safe to assume it's initialized. - self.pixel_buffer[pos.y as usize * Screen::HORIZONTAL_RESOLUTION as usize - + pos.x as usize] = color.into(); - } - }); - - self.draw_buffer(); - - Ok(()) - } -} +#[cfg(feature = "embedded-graphics")] +pub mod embedded_graphics; diff --git a/packages/pros/Cargo.toml b/packages/pros/Cargo.toml index f751e8d8..306848c1 100644 --- a/packages/pros/Cargo.toml +++ b/packages/pros/Cargo.toml @@ -36,7 +36,9 @@ async = ["dep:pros-async"] sync = ["dep:pros-sync"] devices = ["dep:pros-devices"] + graphics = ["dep:pros-graphics"] +embedded-graphics = ["pros-graphics/embedded-graphics"] math = ["dep:pros-math"] From 68abf89f67ecd3977426e77f20b9040475a038a6 Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Wed, 13 Mar 2024 20:22:55 -0700 Subject: [PATCH 9/9] fix: make prelude work with new features --- CHANGELOG.md | 4 ++-- packages/pros/src/lib.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71c7e7f8..af93c6e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,8 +31,8 @@ Before releasing: - Added support for getting motor fault flags (e.g. over-temperature, over-current, H-bridge faults). - Added support for internal motor PID tuning. Feature gated behind `dangerous_motor_tuning`, as this can cause hardware damage and is not recommended. - Added various constants for convenience around `Motor` and `Gearset`. -- An ``embedded_graphics`` driver crate (pros_graphics). -- New `graphics` feature in the `pros` crate. +- An ``embedded_graphics`` driver in the new `pros_graphics` crate. +- New `graphics` and `embedded-graphics` features in the `pros` crate. ### Fixed diff --git a/packages/pros/src/lib.rs b/packages/pros/src/lib.rs index 5164c80b..014569b2 100644 --- a/packages/pros/src/lib.rs +++ b/packages/pros/src/lib.rs @@ -119,8 +119,8 @@ pub mod prelude { SmartDevice, SmartPort, }, }; - #[cfg(feature = "graphics")] - pub use pros_graphics::V5BrainDisplay; + #[cfg(all(feature = "graphics", feature = "embedded-graphics"))] + pub use pros_graphics::embedded_graphics::V5BrainDisplay; #[cfg(feature = "math")] pub use pros_math::{feedforward::MotorFeedforwardController, pid::PidController}; #[cfg(feature = "sync")]