Refactor: improve naming & types
* renamed display traits to Draw* * renamed Send to SendBits to differentiate from the standard trait * introduced the Paint trait * changed type of coordinate to u8 instead of usize * use generic constant when performing moodulo operation based on screen size
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
mod thumbv6m;
|
mod thumbv6m;
|
||||||
|
|
||||||
pub trait Sender {
|
pub trait SendBits {
|
||||||
fn send(&self, value: u32);
|
fn send_bits(&self, value: u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use thumbv6m::Msb2LsbSender;
|
pub use thumbv6m::Msb2LsbSender;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::Sender;
|
use super::SendBits;
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -52,7 +52,7 @@ impl<
|
|||||||
const T0B: u32,
|
const T0B: u32,
|
||||||
const T1A: u32,
|
const T1A: u32,
|
||||||
const T1B: u32,
|
const T1B: u32,
|
||||||
> Sender
|
> SendBits
|
||||||
for Msb2LsbSender<
|
for Msb2LsbSender<
|
||||||
BITS,
|
BITS,
|
||||||
SHIFT,
|
SHIFT,
|
||||||
@@ -72,7 +72,7 @@ impl<
|
|||||||
// T0A, T0B, T1A, T1B are the number of cycles to wait after writing to A START, and B STOP address.
|
// T0A, T0B, T1A, T1B are the number of cycles to wait after writing to A START, and B STOP address.
|
||||||
// At a minimum there is always a 3cycle-delay, and due to rounding there might be a +/- 1 cycle
|
// At a minimum there is always a 3cycle-delay, and due to rounding there might be a +/- 1 cycle
|
||||||
// difference for non-multiple of 3.
|
// difference for non-multiple of 3.
|
||||||
fn send(&self, value: u32) {
|
fn send_bits(&self, value: u32) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut primask: u32;
|
let mut primask: u32;
|
||||||
asm!(
|
asm!(
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
pub trait DisplayRectangles<Position, Color> {
|
pub trait DrawRectangles<Position, Color> {
|
||||||
fn fill_rectangle(&mut self, a: Position, b: Position, color: Color);
|
fn draw_filled_rectangle(&mut self, a: Position, b: Position, color: Color);
|
||||||
|
|
||||||
fn rectangle(&mut self, bottom_left: Position, upper_right: Position, color: Color);
|
fn draw_rectangle(&mut self, bottom_left: Position, upper_right: Position, color: Color);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DisplayCircles<Position, Color> {
|
pub trait DrawCircles<Position, Color> {
|
||||||
fn fill_circle(&mut self, origin: Position, radius: usize, color: Color);
|
fn draw_filled_circle(&mut self, origin: Position, radius: usize, color: Color);
|
||||||
|
|
||||||
fn circle(&mut self, origin: Position, radius: usize, color: Color);
|
fn draw_circle(&mut self, origin: Position, radius: usize, color: Color);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Sprite<Color, const X: usize, const Y: usize> = [[Color; X]; Y];
|
pub type Sprite<Color, const X: usize, const Y: usize> = [[Color; X]; Y];
|
||||||
|
|
||||||
pub trait DisplaySprites<Position, Color> {
|
pub trait DrawSprites<Position, Color> {
|
||||||
fn copy<const SPRITE_X: usize, const SPRITE_Y: usize>(
|
fn draw_sprite<const SPRITE_X: usize, const SPRITE_Y: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: Position,
|
origin: Position,
|
||||||
data: &Sprite<Color, SPRITE_X, SPRITE_Y>,
|
data: &Sprite<Color, SPRITE_X, SPRITE_Y>,
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ mod draw;
|
|||||||
|
|
||||||
pub use draw::*;
|
pub use draw::*;
|
||||||
|
|
||||||
pub trait Screen<Position, Color> {
|
pub trait Display<Position, Color> {
|
||||||
fn show(&self);
|
fn show(&self);
|
||||||
|
|
||||||
fn clear(&mut self);
|
fn clear(&mut self);
|
||||||
|
|
||||||
fn fill(&mut self, color: Color);
|
fn fill(&mut self, color: Color);
|
||||||
|
|
||||||
fn point(&mut self, position: Position, color: Color);
|
fn draw_dot(&mut self, position: Position, color: Color);
|
||||||
}
|
}
|
||||||
|
|||||||
123
src/lib.rs
123
src/lib.rs
@@ -5,14 +5,14 @@ pub mod bit_string;
|
|||||||
pub mod led_display;
|
pub mod led_display;
|
||||||
pub mod neo_pixel;
|
pub mod neo_pixel;
|
||||||
|
|
||||||
use crate::led_display::{DisplaySprites, Screen, Sprite};
|
use crate::led_display::{Display, DrawSprites, Sprite};
|
||||||
use crate::neo_pixel::{Color, CompositeNeoPixelDisplay, DisplayCircles, DisplayRectangles};
|
use crate::neo_pixel::{Color, DrawCircles, DrawRectangles, NeoPixelScreen};
|
||||||
use cortex_m::asm::delay;
|
use cortex_m::asm::delay;
|
||||||
use rand::rngs::SmallRng;
|
use rand::rngs::SmallRng;
|
||||||
use rand::{RngCore, SeedableRng};
|
use rand::{RngCore, SeedableRng};
|
||||||
|
|
||||||
mod hardware {
|
mod hardware {
|
||||||
use super::neo_pixel::{CompositeNeoPixelDisplay, NeoPixelSender};
|
use super::neo_pixel::{NeoPixelScreen, NeoPixelSender};
|
||||||
use cortex_m::asm::delay;
|
use cortex_m::asm::delay;
|
||||||
use microbit::hal::gpio::Level::Low;
|
use microbit::hal::gpio::Level::Low;
|
||||||
use microbit::Board;
|
use microbit::Board;
|
||||||
@@ -21,13 +21,12 @@ mod hardware {
|
|||||||
const T0H_NS: u32 = 250; // 350+/-150 [ns]
|
const T0H_NS: u32 = 250; // 350+/-150 [ns]
|
||||||
const T1H_NS: u32 = 900; // 700+/-150 [ns]
|
const T1H_NS: u32 = 900; // 700+/-150 [ns]
|
||||||
|
|
||||||
// Latch reset delay
|
|
||||||
const TLL_NS: u32 = 250_000; // 6_000 [ns] min, newer pixels require ~250_000 [ns]
|
|
||||||
|
|
||||||
const T0L_NS: u32 = 900; // 800+/-150 [ns]
|
const T0L_NS: u32 = 900; // 800+/-150 [ns]
|
||||||
const T1L_NS: u32 = 600; // 600+/-150 [ns]
|
const T1L_NS: u32 = 600; // 600+/-150 [ns]
|
||||||
|
|
||||||
// Rounded down, from 20.8333
|
// Latch reset delay
|
||||||
|
const TLL_NS: u32 = 250_000; // 6_000 [ns] min, newer pixels require ~250_000 [ns]
|
||||||
|
|
||||||
pub const NS_PER_CYCLE: u32 = 114; // empirically measured to be about 114ns
|
pub const NS_PER_CYCLE: u32 = 114; // empirically measured to be about 114ns
|
||||||
|
|
||||||
const T0H: u32 = T0H_NS / NS_PER_CYCLE;
|
const T0H: u32 = T0H_NS / NS_PER_CYCLE;
|
||||||
@@ -41,7 +40,7 @@ mod hardware {
|
|||||||
const EDGE_PAD0_SET_ADDRESS: u32 = 0x5000_0508; // Address of the SET register
|
const EDGE_PAD0_SET_ADDRESS: u32 = 0x5000_0508; // Address of the SET register
|
||||||
const EDGE_PAD0_CLEAR_ADDRESS: u32 = 0x5000_050c; // Address of the toggle register
|
const EDGE_PAD0_CLEAR_ADDRESS: u32 = 0x5000_050c; // Address of the toggle register
|
||||||
|
|
||||||
pub fn setup() -> CompositeNeoPixelDisplay<
|
pub fn setup() -> NeoPixelScreen<
|
||||||
NeoPixelSender<
|
NeoPixelSender<
|
||||||
EDGE_PAD0_MASK,
|
EDGE_PAD0_MASK,
|
||||||
EDGE_PAD0_SET_ADDRESS,
|
EDGE_PAD0_SET_ADDRESS,
|
||||||
@@ -71,26 +70,47 @@ mod hardware {
|
|||||||
T1L,
|
T1L,
|
||||||
>::new();
|
>::new();
|
||||||
|
|
||||||
CompositeNeoPixelDisplay::<_, TLL, 8, 8, 4 /*NX*/, 1 /*NY*/>::new(sender)
|
NeoPixelScreen::<_, TLL, 8, 8, 4 /*NX*/, 1 /*NY*/>::new(sender)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// const BLANK: Option<Color> = None;
|
trait Paint {
|
||||||
|
fn dx(frame: i32, fps: i32, speed: i32) -> i32;
|
||||||
|
|
||||||
|
fn dy(frame: i32, fps: i32, speed: i32) -> i32;
|
||||||
|
|
||||||
|
fn paint_with_color<const SPRITE_X: usize, const SPRITE_Y: usize>(
|
||||||
|
&mut self,
|
||||||
|
x0: usize,
|
||||||
|
y0: usize,
|
||||||
|
sprite: &[[bool; SPRITE_Y]; SPRITE_X],
|
||||||
|
color: u32,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn paint_list<const S: usize, const ELEM_X: usize, const ELEM_Y: usize>(
|
||||||
|
&mut self,
|
||||||
|
list: &[Option<(u8, u8, Color)>; S],
|
||||||
|
dx: u8,
|
||||||
|
dy: u8,
|
||||||
|
tree: &[[bool; ELEM_X]; ELEM_Y],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
Sender: neo_pixel::Sender,
|
Sender: neo_pixel::SendBits,
|
||||||
const DELAY_RESET: u32,
|
const DELAY_RESET: u32,
|
||||||
const X: usize,
|
const X: usize,
|
||||||
const Y: usize,
|
const Y: usize,
|
||||||
const NX: usize,
|
const NX: usize,
|
||||||
const NY: usize,
|
const NY: usize,
|
||||||
> CompositeNeoPixelDisplay<Sender, DELAY_RESET, X, Y, NX, NY>
|
> Paint for NeoPixelScreen<Sender, DELAY_RESET, X, Y, NX, NY>
|
||||||
{
|
{
|
||||||
fn dx(frame: i32, fps: i32, speed: i32) -> i32 {
|
fn dx(frame: i32, fps: i32, speed: i32) -> i32 {
|
||||||
((speed * frame / fps) / 512) % 32
|
((speed * frame / fps) / 512) % { (X * NX) as i32 }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dy(frame: i32, fps: i32, speed: i32) -> i32 {
|
fn dy(frame: i32, fps: i32, speed: i32) -> i32 {
|
||||||
((speed * frame / fps) / 512) % 8
|
((speed * frame / fps) / 512) % { (Y * NY) as i32 }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint_with_color<const SPRITE_X: usize, const SPRITE_Y: usize>(
|
fn paint_with_color<const SPRITE_X: usize, const SPRITE_Y: usize>(
|
||||||
@@ -103,7 +123,10 @@ impl<
|
|||||||
for (x, line) in sprite.iter().enumerate() {
|
for (x, line) in sprite.iter().enumerate() {
|
||||||
for (y, paint) in line.iter().enumerate() {
|
for (y, paint) in line.iter().enumerate() {
|
||||||
if *paint {
|
if *paint {
|
||||||
self.point(self.position((x0 + x) % 32, (y0 + y) % 8), color);
|
self.draw_dot(
|
||||||
|
self.position(((x0 + x) % { X * NX }) as u8, ((y0 + y) % Y) as u8),
|
||||||
|
color,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,7 +143,17 @@ impl<
|
|||||||
self.paint_with_color((*x0 + dx) as usize, (*y0 + dy) as usize, tree, *color);
|
self.paint_with_color((*x0 + dx) as usize, (*y0 + dy) as usize, tree, *color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
Sender: neo_pixel::SendBits,
|
||||||
|
const DELAY_RESET: u32,
|
||||||
|
const X: usize,
|
||||||
|
const Y: usize,
|
||||||
|
const NX: usize,
|
||||||
|
const NY: usize,
|
||||||
|
> NeoPixelScreen<Sender, DELAY_RESET, X, Y, NX, NY>
|
||||||
|
{
|
||||||
fn stars_far(&mut self, frame: i32, fps: i32, rng: &mut SmallRng) {
|
fn stars_far(&mut self, frame: i32, fps: i32, rng: &mut SmallRng) {
|
||||||
// We repeat the same 2-screen pseudo-random pattern. We have an area of (3 * 8 * 2 =) 48
|
// We repeat the same 2-screen pseudo-random pattern. We have an area of (3 * 8 * 2 =) 48
|
||||||
// pixels we want to cover with some stars. so we arbitrarily decided 6 stars was enough.
|
// pixels we want to cover with some stars. so we arbitrarily decided 6 stars was enough.
|
||||||
@@ -149,24 +182,21 @@ impl<
|
|||||||
STAR_COLOR[i + NB_STARS] = COLORS[(rng.next_u32() % 10) as usize];
|
STAR_COLOR[i + NB_STARS] = COLORS[(rng.next_u32() % 10) as usize];
|
||||||
}
|
}
|
||||||
// Screens 1 & 2
|
// Screens 1 & 2
|
||||||
self.point(self.position(*x as usize, *y as usize), STAR_COLOR[i]);
|
self.draw_dot(self.position(*x, *y), STAR_COLOR[i]);
|
||||||
|
|
||||||
// Screens 3 & 4
|
// Screens 3 & 4
|
||||||
self.point(
|
self.draw_dot(self.position(*x + 16, *y), STAR_COLOR[i + NB_STARS]);
|
||||||
self.position((*x + 16) as usize, *y as usize),
|
|
||||||
STAR_COLOR[i + NB_STARS],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn moon(&mut self, frame: i32, fps: i32) {
|
fn moon(&mut self, frame: i32, fps: i32) {
|
||||||
const COLOR: Option<u32> = Some(0x1b1a1a);
|
const COLOR: Option<u32> = Some(0x1b1a1a);
|
||||||
let x = Self::dx(frame, fps, 256);
|
let x = Self::dx(frame, fps, 256) as u8;
|
||||||
let dy = Self::dy(frame, fps, 128);
|
let dy = Self::dy(frame, fps, 128) as u8;
|
||||||
let y = if dy < 4 { dy + 2 } else { 11 - dy };
|
let y = if dy < 4 { dy + 2 } else { 11 - dy };
|
||||||
let origin = self.position(x as usize, y as usize);
|
let origin = self.position(x, y);
|
||||||
self.fill_circle(origin, 2, COLOR);
|
self.draw_filled_circle(origin, 2, COLOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stars_close(&mut self, frame: i32, fps: i32) {
|
fn stars_close(&mut self, frame: i32, fps: i32) {
|
||||||
@@ -191,14 +221,14 @@ impl<
|
|||||||
];
|
];
|
||||||
|
|
||||||
for (i, (x, y)) in STAR_POSITIONS.iter().enumerate() {
|
for (i, (x, y)) in STAR_POSITIONS.iter().enumerate() {
|
||||||
let x = ((*x as i32 + Self::dx(frame, fps, 64)) % 32) as usize;
|
let x = ((*x as i32 + Self::dx(frame, fps, 64)) % { (X * NX) as i32 }) as usize;
|
||||||
let y = *y as usize;
|
let y = *y as usize;
|
||||||
self.paint_with_color(x, y, &STAR, COLORS[i]);
|
self.paint_with_color(x, y, &STAR, COLORS[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn snow(&mut self) {
|
fn snow(&mut self) {
|
||||||
self.fill_rectangle(self.position(0, 0), self.position(31, 2), Some(0x0a0a0a));
|
self.draw_filled_rectangle(self.position(0, 0), self.position(31, 2), Some(0x0a0a0a));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trees(&mut self, frame: i32, fps: i32) {
|
fn trees(&mut self, frame: i32, fps: i32) {
|
||||||
@@ -244,13 +274,16 @@ impl<
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn houses(&mut self, frame: i32, fps: i32) {
|
fn houses(&mut self, frame: i32, fps: i32) {
|
||||||
|
let max_x: i32 = { (X * NX) as i32 };
|
||||||
|
|
||||||
const BLANK_: Option<Color> = None;
|
const BLANK_: Option<Color> = None;
|
||||||
const RED___: Option<Color> = Some(0x003300);
|
const RED___: Option<Color> = Some(0x003300);
|
||||||
const BLUE__: Option<Color> = Some(0x000033);
|
const BLUE__: Option<Color> = Some(0x000033);
|
||||||
const PURPLE: Option<Color> = Some(0x004433);
|
const PURPLE: Option<Color> = Some(0x004433);
|
||||||
const YELLOW: Option<Color> = Some(0x444400);
|
const YELLOW: Option<Color> = Some(0x444400);
|
||||||
const ORANGE: Option<Color> = Some(0x448800);
|
const ORANGE: Option<Color> = Some(0x448800);
|
||||||
const BROWN_: Option<Color> = Some(0x8b4513);
|
//const BROWN_: Option<Color> = Some(0x8b4513);
|
||||||
|
|
||||||
const HOUSE1: Sprite<Option<Color>, 5, 5> = [
|
const HOUSE1: Sprite<Option<Color>, 5, 5> = [
|
||||||
[BLANK_, BLANK_, RED___, BLANK_, BLANK_],
|
[BLANK_, BLANK_, RED___, BLANK_, BLANK_],
|
||||||
[PURPLE, PURPLE, PURPLE, RED___, BLANK_],
|
[PURPLE, PURPLE, PURPLE, RED___, BLANK_],
|
||||||
@@ -267,30 +300,27 @@ impl<
|
|||||||
[BLANK_, BLANK_, RED___, BLANK_, BLANK_],
|
[BLANK_, BLANK_, RED___, BLANK_, BLANK_],
|
||||||
];
|
];
|
||||||
|
|
||||||
self.copy(
|
self.draw_sprite(
|
||||||
self.position((3 + Self::dx(frame, fps, 1024)) as usize % 32, 0),
|
self.position(((3 + Self::dx(frame, fps, 1024)) % max_x) as u8, 0),
|
||||||
&HOUSE2,
|
&HOUSE2,
|
||||||
);
|
);
|
||||||
self.copy(
|
self.draw_sprite(
|
||||||
self.position((7 + Self::dx(frame, fps, 1024)) as usize % 32, 2),
|
self.position(((7 + Self::dx(frame, fps, 1024)) % max_x) as u8, 2),
|
||||||
&HOUSE1,
|
&HOUSE1,
|
||||||
);
|
);
|
||||||
self.copy(
|
self.draw_sprite(
|
||||||
self.position((14 + Self::dx(frame, fps, 1024)) as usize % 32, 1),
|
self.position(((14 + Self::dx(frame, fps, 1024)) % max_x) as u8, 1),
|
||||||
&HOUSE2,
|
&HOUSE2,
|
||||||
);
|
);
|
||||||
self.copy(
|
self.draw_sprite(
|
||||||
self.position((20 + Self::dx(frame, fps, 1024)) as usize % 32, 0),
|
self.position(((20 + Self::dx(frame, fps, 1024)) % max_x) as u8, 0),
|
||||||
&HOUSE2,
|
&HOUSE2,
|
||||||
);
|
);
|
||||||
self.copy(
|
self.draw_sprite(
|
||||||
self.position((27 + Self::dx(frame, fps, 1024)) as usize % 32, 1),
|
self.position(((27 + Self::dx(frame, fps, 1024)) % max_x) as u8, 1),
|
||||||
&HOUSE2,
|
&HOUSE2,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
fn sled(&self, _frame: usize) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
@@ -304,16 +334,13 @@ pub fn main() {
|
|||||||
loop {
|
loop {
|
||||||
display.clear();
|
display.clear();
|
||||||
|
|
||||||
// 10 Background
|
display.stars_far(frame, FRAME_PER_S, &mut rng);
|
||||||
display.stars_far(frame, FRAME_PER_S, &mut rng); // OK
|
display.stars_close(frame, FRAME_PER_S);
|
||||||
display.stars_close(frame, FRAME_PER_S); // OK
|
display.moon(frame, FRAME_PER_S);
|
||||||
display.moon(frame, FRAME_PER_S); // OK
|
display.snow();
|
||||||
display.snow(); // OK
|
|
||||||
|
|
||||||
display.trees(frame, FRAME_PER_S); // OK
|
display.trees(frame, FRAME_PER_S);
|
||||||
display.houses(frame, FRAME_PER_S);
|
display.houses(frame, FRAME_PER_S);
|
||||||
//
|
|
||||||
// display.sled(frame);
|
|
||||||
|
|
||||||
display.show();
|
display.show();
|
||||||
frame += 1;
|
frame += 1;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
mod screen;
|
mod screen;
|
||||||
|
|
||||||
pub use crate::bit_string::{self, Sender};
|
pub use crate::bit_string::{self, SendBits};
|
||||||
pub use crate::led_display::{DisplayCircles, DisplayRectangles, DisplaySprites, Screen, Sprite};
|
pub use crate::led_display::{Display, DrawCircles, DrawRectangles, DrawSprites, Sprite};
|
||||||
use defmt::Format;
|
use defmt::Format;
|
||||||
pub use screen::CompositeNeoPixelDisplay;
|
pub use screen::CompositeNeoPixelScreen as NeoPixelScreen;
|
||||||
|
|
||||||
pub type Color = u32;
|
pub type Color = u32;
|
||||||
|
|
||||||
@@ -29,12 +29,12 @@ pub type NeoPixelSender<
|
|||||||
|
|
||||||
#[derive(Eq, Format, Ord, PartialEq, PartialOrd)]
|
#[derive(Eq, Format, Ord, PartialEq, PartialOrd)]
|
||||||
pub struct Position {
|
pub struct Position {
|
||||||
x: usize,
|
x: u8,
|
||||||
y: usize,
|
y: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Position {
|
impl Position {
|
||||||
fn new(x: usize, y: usize) -> Self {
|
pub(crate) fn new(x: u8, y: u8) -> Self {
|
||||||
Self { x, y }
|
Self { x, y }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use cortex_m::asm::delay;
|
use cortex_m::asm::delay;
|
||||||
use defmt::warn;
|
use defmt::warn;
|
||||||
|
|
||||||
use super::{Color, DisplayCircles, DisplayRectangles, DisplaySprites, Position, Screen, Sprite};
|
use super::{Color, Display, DrawCircles, DrawRectangles, DrawSprites, Position, Sprite};
|
||||||
|
|
||||||
struct BufferPosition<const X: usize, const Y: usize, const NX: usize, const NY: usize> {
|
struct BufferPosition<const X: usize, const Y: usize, const NX: usize, const NY: usize> {
|
||||||
row: usize,
|
row: usize,
|
||||||
@@ -30,27 +30,24 @@ impl<const X: usize, const Y: usize, const NX: usize, const NY: usize> TryFrom<P
|
|||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
fn try_from(position: Position) -> Result<Self, Self::Error> {
|
fn try_from(position: Position) -> Result<Self, Self::Error> {
|
||||||
let col = position.x / X;
|
let px = position.x as usize;
|
||||||
let row = position.y / Y;
|
let py = position.y as usize;
|
||||||
|
|
||||||
if col > NX && row > NY {
|
let col = px / X;
|
||||||
return Err(());
|
let row = py / Y;
|
||||||
}
|
let x = px % X;
|
||||||
if col > NX {
|
let y = py % Y;
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
if row > NY {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let x = position.x % X;
|
if col > NX || row > NY {
|
||||||
let y = position.y % Y;
|
Err(())
|
||||||
Ok(BufferPosition { row, col, y, x })
|
} else {
|
||||||
|
Ok(BufferPosition { row, col, y, x })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CompositeNeoPixelDisplay<
|
pub struct CompositeNeoPixelScreen<
|
||||||
Sender: super::Sender,
|
Sender: super::SendBits,
|
||||||
const DELAY_RESET: u32,
|
const DELAY_RESET: u32,
|
||||||
const X: usize,
|
const X: usize,
|
||||||
const Y: usize,
|
const Y: usize,
|
||||||
@@ -62,13 +59,13 @@ pub struct CompositeNeoPixelDisplay<
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
Sender: super::Sender,
|
Sender: super::SendBits,
|
||||||
const DELAY_RESET: u32,
|
const DELAY_RESET: u32,
|
||||||
const X: usize,
|
const X: usize,
|
||||||
const Y: usize,
|
const Y: usize,
|
||||||
const NX: usize,
|
const NX: usize,
|
||||||
const NY: usize,
|
const NY: usize,
|
||||||
> CompositeNeoPixelDisplay<Sender, DELAY_RESET, X, Y, NX, NY>
|
> CompositeNeoPixelScreen<Sender, DELAY_RESET, X, Y, NX, NY>
|
||||||
{
|
{
|
||||||
pub fn new(sender: Sender) -> Self {
|
pub fn new(sender: Sender) -> Self {
|
||||||
let display = Self {
|
let display = Self {
|
||||||
@@ -80,8 +77,8 @@ impl<
|
|||||||
display
|
display
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn position(&self, x: usize, y: usize) -> Option<Position> {
|
pub fn position(&self, x: u8, y: u8) -> Option<Position> {
|
||||||
if (x < { X * NX }) && (y < { Y * NY }) {
|
if ((x as usize) < { X * NX }) && ((y as usize) < { Y * NY }) {
|
||||||
Some(Position::new(x, y))
|
Some(Position::new(x, y))
|
||||||
} else {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
@@ -94,24 +91,54 @@ impl<
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_rectangle_parameters(
|
||||||
|
a: Option<Position>,
|
||||||
|
b: Option<Position>,
|
||||||
|
color: Option<Color>,
|
||||||
|
) -> Option<(Position, Position, Color)> {
|
||||||
|
let color = color?;
|
||||||
|
let a = a?;
|
||||||
|
let b = b?;
|
||||||
|
|
||||||
|
if b > a {
|
||||||
|
Some((a, b, color))
|
||||||
|
} else {
|
||||||
|
Some((b, a, color))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_circle_parameters(
|
||||||
|
origin: Option<Position>,
|
||||||
|
radius: u8,
|
||||||
|
color: Option<Color>,
|
||||||
|
) -> Option<(u8, u8, u8, u8, Color)> {
|
||||||
|
let color = color?;
|
||||||
|
let Position { x: cx, y: cy } = origin?;
|
||||||
|
|
||||||
|
let y0 = if cy < radius { 0 } else { cy - radius };
|
||||||
|
let x0 = if cx < radius { 0 } else { cx - radius };
|
||||||
|
|
||||||
|
Some((cx, cy, x0, y0, color))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
Sender: super::Sender,
|
Sender: super::SendBits,
|
||||||
const DELAY_RESET: u32,
|
const DELAY_RESET: u32,
|
||||||
const X: usize,
|
const X: usize,
|
||||||
const Y: usize,
|
const Y: usize,
|
||||||
const NX: usize,
|
const NX: usize,
|
||||||
const NY: usize,
|
const NY: usize,
|
||||||
> Screen<Option<Position>, Color>
|
> Display<Option<Position>, Color>
|
||||||
for CompositeNeoPixelDisplay<Sender, DELAY_RESET, X, Y, NX, NY>
|
for CompositeNeoPixelScreen<Sender, DELAY_RESET, X, Y, NX, NY>
|
||||||
{
|
{
|
||||||
fn show(&self) {
|
fn show(&self) {
|
||||||
for r in 0..NY {
|
for r in 0..NY {
|
||||||
for c in 0..NX {
|
for c in 0..NX {
|
||||||
for y in 0..Y {
|
for y in 0..Y {
|
||||||
for x in 0..X {
|
for x in 0..X {
|
||||||
self.sender.send(self.buffer[r][c][y][x]);
|
self.sender.send_bits(self.buffer[r][c][y][x]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,14 +151,14 @@ impl<
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn fill(&mut self, color: Color) {
|
fn fill(&mut self, color: Color) {
|
||||||
self.fill_rectangle(
|
self.draw_filled_rectangle(
|
||||||
self.position(0, 0),
|
self.position(0, 0),
|
||||||
self.position(X * NX - 1, Y * NY - 1),
|
self.position((X * NX - 1) as u8, (Y * NY - 1) as u8),
|
||||||
Some(color),
|
Some(color),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn point(&mut self, position: Option<Position>, color: Color) {
|
fn draw_dot(&mut self, position: Option<Position>, color: Color) {
|
||||||
if let Ok(BufferPosition::<X, Y, NX, NY> { row, col, y, x }) = position.try_into() {
|
if let Ok(BufferPosition::<X, Y, NX, NY> { row, col, y, x }) = position.try_into() {
|
||||||
self.buffer[row][col][y][x] = color;
|
self.buffer[row][col][y][x] = color;
|
||||||
}
|
}
|
||||||
@@ -139,67 +166,104 @@ impl<
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
Sender: super::Sender,
|
Sender: super::SendBits,
|
||||||
const DELAY_RESET: u32,
|
const DELAY_RESET: u32,
|
||||||
const X: usize,
|
const X: usize,
|
||||||
const Y: usize,
|
const Y: usize,
|
||||||
const NX: usize,
|
const NX: usize,
|
||||||
const NY: usize,
|
const NY: usize,
|
||||||
> DisplayRectangles<Option<Position>, Option<Color>>
|
> DrawRectangles<Option<Position>, Option<Color>>
|
||||||
for CompositeNeoPixelDisplay<Sender, DELAY_RESET, X, Y, NX, NY>
|
for CompositeNeoPixelScreen<Sender, DELAY_RESET, X, Y, NX, NY>
|
||||||
{
|
{
|
||||||
fn fill_rectangle(&mut self, a: Option<Position>, b: Option<Position>, color: Option<Color>) {
|
fn draw_filled_rectangle(
|
||||||
let color = if let Some(c) = color { c } else { return };
|
&mut self,
|
||||||
|
a: Option<Position>,
|
||||||
|
b: Option<Position>,
|
||||||
|
color: Option<Color>,
|
||||||
|
) {
|
||||||
|
if let Some((bottom_left, upper_right, color)) = Self::get_rectangle_parameters(a, b, color)
|
||||||
|
{
|
||||||
|
let Position { x: x0, y: y0 } = bottom_left;
|
||||||
|
let Position { x: x1, y: y1 } = upper_right;
|
||||||
|
|
||||||
if let Some(a) = a {
|
for y in y0..=y1 {
|
||||||
if let Some(b) = b {
|
for x in x0..=x1 {
|
||||||
let bottom_left;
|
self.draw_dot(self.position(x, y), color)
|
||||||
let upper_right;
|
|
||||||
if a < b {
|
|
||||||
bottom_left = a;
|
|
||||||
upper_right = b;
|
|
||||||
} else {
|
|
||||||
bottom_left = b;
|
|
||||||
upper_right = a;
|
|
||||||
}
|
}
|
||||||
let Position { x: x0, y: y0 } = bottom_left;
|
}
|
||||||
let Position { x: x1, y: y1 } = upper_right;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for y in y0..=y1 {
|
fn draw_rectangle(&mut self, a: Option<Position>, b: Option<Position>, color: Option<Color>) {
|
||||||
for x in x0..=x1 {
|
if let Some((bottom_left, upper_right, color)) = Self::get_rectangle_parameters(a, b, color)
|
||||||
self.point(self.position(x, y), color)
|
{
|
||||||
|
let Position { x: x0, y: y0 } = bottom_left;
|
||||||
|
let Position { x: x1, y: y1 } = upper_right;
|
||||||
|
|
||||||
|
for y in y0..=y1 {
|
||||||
|
if (y != y0) && (y != y1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for x in x0..=x1 {
|
||||||
|
if (x != x0) && (x != x1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
self.draw_dot(self.position(x, y), color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
Sender: super::SendBits,
|
||||||
|
const DELAY_RESET: u32,
|
||||||
|
const X: usize,
|
||||||
|
const Y: usize,
|
||||||
|
const NX: usize,
|
||||||
|
const NY: usize,
|
||||||
|
> DrawCircles<Option<Position>, Option<Color>>
|
||||||
|
for CompositeNeoPixelScreen<Sender, DELAY_RESET, X, Y, NX, NY>
|
||||||
|
{
|
||||||
|
fn draw_filled_circle(
|
||||||
|
&mut self,
|
||||||
|
origin: Option<Position>,
|
||||||
|
radius: usize,
|
||||||
|
color: Option<Color>,
|
||||||
|
) {
|
||||||
|
let radius = radius as u8;
|
||||||
|
|
||||||
|
if let Some((cx, cy, x0, y0, color)) = Self::get_circle_parameters(origin, radius, color) {
|
||||||
|
for y in y0..=(cy + radius) {
|
||||||
|
for x in x0..=(cx + radius) {
|
||||||
|
let dx = if x < cx { cx - x } else { x - cx };
|
||||||
|
let dy = if y < cy { cy - y } else { y - cy };
|
||||||
|
let h = dx * dx + dy * dy;
|
||||||
|
let epsilon = (radius * 2) + 1 / radius + 1; // todo simplify or tune some more
|
||||||
|
if h <= radius + epsilon {
|
||||||
|
self.draw_dot(self.position(x, y), color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rectangle(&mut self, a: Option<Position>, b: Option<Position>, color: Option<Color>) {
|
fn draw_circle(&mut self, origin: Option<Position>, radius: usize, color: Option<Color>) {
|
||||||
if let Some(color) = color {
|
let radius = radius as u8;
|
||||||
if let Some(a) = a {
|
|
||||||
if let Some(b) = b {
|
|
||||||
let bottom_left;
|
|
||||||
let upper_right;
|
|
||||||
if a < b {
|
|
||||||
bottom_left = a;
|
|
||||||
upper_right = b;
|
|
||||||
} else {
|
|
||||||
bottom_left = b;
|
|
||||||
upper_right = a;
|
|
||||||
}
|
|
||||||
let Position { x: x0, y: y0 } = bottom_left;
|
|
||||||
let Position { x: x1, y: y1 } = upper_right;
|
|
||||||
|
|
||||||
for y in y0..=y1 {
|
if let Some((cx, cy, x0, y0, color)) = Self::get_circle_parameters(origin, radius, color) {
|
||||||
if (y != y0) && (y != y1) {
|
for y in y0..=(cy + radius) {
|
||||||
continue;
|
for x in x0..=(cx + radius) {
|
||||||
}
|
let dx = if x < cx { cx - x } else { x - cx };
|
||||||
for x in x0..=x1 {
|
let dy = if y < cy { cy - y } else { y - cy };
|
||||||
if (x != x0) && (x != x1) {
|
let r2 = radius * radius;
|
||||||
continue;
|
let h = dx * dx + dy * dy;
|
||||||
}
|
let d = if r2 < h { h - r2 } else { r2 - h };
|
||||||
self.point(self.position(x, y), color);
|
let epsilon = (radius * 2 + 1) / radius + 1;
|
||||||
}
|
|
||||||
|
if d < epsilon {
|
||||||
|
// we use a larger epsilon to have a full line
|
||||||
|
self.draw_dot(self.position(x, y), color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,74 +272,16 @@ impl<
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
Sender: super::Sender,
|
Sender: super::SendBits,
|
||||||
const DELAY_RESET: u32,
|
const DELAY_RESET: u32,
|
||||||
const X: usize,
|
const X: usize,
|
||||||
const Y: usize,
|
const Y: usize,
|
||||||
const NX: usize,
|
const NX: usize,
|
||||||
const NY: usize,
|
const NY: usize,
|
||||||
> DisplayCircles<Option<Position>, Option<Color>>
|
> DrawSprites<Option<Position>, Option<Color>>
|
||||||
for CompositeNeoPixelDisplay<Sender, DELAY_RESET, X, Y, NX, NY>
|
for CompositeNeoPixelScreen<Sender, DELAY_RESET, X, Y, NX, NY>
|
||||||
{
|
{
|
||||||
fn fill_circle(&mut self, origin: Option<Position>, radius: usize, color: Option<Color>) {
|
fn draw_sprite<const SW: usize, const SH: usize>(
|
||||||
if let Some(color) = color {
|
|
||||||
if let Some(origin) = origin {
|
|
||||||
let Position { x: cx, y: cy } = origin;
|
|
||||||
let y0 = if cy < radius { 0 } else { cy - radius };
|
|
||||||
let x0 = if cx < radius { 0 } else { cx - radius };
|
|
||||||
|
|
||||||
for y in y0..=(cy + radius) {
|
|
||||||
for x in x0..=(cx + radius) {
|
|
||||||
let dx = if x < cx { cx - x } else { x - cx };
|
|
||||||
let dy = if y < cy { cy - y } else { y - cy };
|
|
||||||
let h = dx * dx + dy * dy;
|
|
||||||
let epsilon = (radius * 2) + 1 / radius + 1; // todo simplify or tune some more
|
|
||||||
if h <= radius + epsilon {
|
|
||||||
self.point(self.position(x, y), color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn circle(&mut self, origin: Option<Position>, radius: usize, color: Option<Color>) {
|
|
||||||
if let Some(color) = color {
|
|
||||||
if let Some(origin) = origin {
|
|
||||||
let Position { x: cx, y: cy } = origin;
|
|
||||||
let y0 = if cy < radius { 0 } else { cy - radius };
|
|
||||||
let x0 = if cx < radius { 0 } else { cx - radius };
|
|
||||||
|
|
||||||
for y in y0..=(cy + radius) {
|
|
||||||
for x in x0..=(cx + radius) {
|
|
||||||
let dx = if x < cx { cx - x } else { x - cx };
|
|
||||||
let dy = if y < cy { cy - y } else { y - cy };
|
|
||||||
let r2 = radius * radius;
|
|
||||||
let h = dx * dx + dy * dy;
|
|
||||||
let d = if r2 < h { h - r2 } else { r2 - h };
|
|
||||||
let epsilon = (radius * 2 + 1) / radius + 1;
|
|
||||||
if d < epsilon {
|
|
||||||
// we use a larger epsilon to have a full line
|
|
||||||
self.point(self.position(x, y), color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<
|
|
||||||
Sender: super::Sender,
|
|
||||||
const DELAY_RESET: u32,
|
|
||||||
const X: usize,
|
|
||||||
const Y: usize,
|
|
||||||
const NX: usize,
|
|
||||||
const NY: usize,
|
|
||||||
> DisplaySprites<Option<Position>, Option<Color>>
|
|
||||||
for CompositeNeoPixelDisplay<Sender, DELAY_RESET, X, Y, NX, NY>
|
|
||||||
{
|
|
||||||
fn copy<const SW: usize, const SH: usize>(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: Option<Position>,
|
origin: Option<Position>,
|
||||||
sprite: &Sprite<Option<Color>, SW, SH>,
|
sprite: &Sprite<Option<Color>, SW, SH>,
|
||||||
@@ -284,7 +290,7 @@ impl<
|
|||||||
for (x, line) in sprite.iter().enumerate() {
|
for (x, line) in sprite.iter().enumerate() {
|
||||||
for (y, color) in line.iter().enumerate() {
|
for (y, color) in line.iter().enumerate() {
|
||||||
if let &Some(color) = color {
|
if let &Some(color) = color {
|
||||||
self.point(self.position(x0 + x, y0 + y), color);
|
self.draw_dot(self.position(x0 + x as u8, y0 + y as u8), color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user