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:
2025-01-12 11:21:18 +01:00
parent ba09e87c1e
commit 0ee30764fe
7 changed files with 238 additions and 205 deletions

View File

@@ -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;

View File

@@ -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!(

View File

@@ -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>,

View File

@@ -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);
} }

View File

@@ -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;

View File

@@ -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 }
} }
} }

View File

@@ -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);
} }
} }
} }