Rotating cube
This commit is contained in:
131
Cargo.lock
generated
131
Cargo.lock
generated
@@ -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",
|
||||
]
|
||||
|
||||
@@ -15,4 +15,4 @@ workspace = true
|
||||
memmap2 = "0.9"
|
||||
embedded-graphics = "0.8"
|
||||
embedded-graphics-core = "0.4"
|
||||
|
||||
nalgebra = "0.32"
|
||||
|
||||
@@ -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<Self> {
|
||||
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<I>(&mut self, pixels: I) -> Result<(), Self::Error>
|
||||
where
|
||||
I: IntoIterator<Item = Pixel<Self::Color>>,
|
||||
{
|
||||
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<dyn std::error::Error>> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user