use core::fmt;
use embedded_graphics::{
mono_font::{MonoFont, MonoTextStyle},
pixelcolor::PixelColor,
prelude::*,
primitives::{Circle, Line, PrimitiveStyle, PrimitiveStyleBuilder, Rectangle, StrokeAlignment},
text::{Alignment, Text},
};
#[derive(Copy, Clone, Debug)]
pub struct RenderError;
impl fmt::Display for RenderError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("render error")
}
}
pub trait Renderer<C: PixelColor> {
fn fill_rect(&mut self, rect: Rectangle, color: C) -> Result<(), RenderError>;
fn stroke_rect(&mut self, rect: Rectangle, color: C) -> Result<(), RenderError>;
fn fill_circle(&mut self, center: Point, radius: u32, color: C) -> Result<(), RenderError>;
fn stroke_line(
&mut self,
start: Point,
end: Point,
color: C,
width: u32,
) -> Result<(), RenderError>;
fn draw_text(
&mut self,
text: &str,
position: Point,
font: &MonoFont<'_>,
color: C,
alignment: Alignment,
) -> Result<(), RenderError>;
fn draw_image(
&mut self,
_top_left: Point,
_size: Size,
_pixels: &[C],
) -> Result<(), RenderError> {
Ok(())
}
fn stroke_arc(
&mut self,
center: Point,
radius: u32,
start_deg: i32,
sweep_deg: i32,
width: u32,
color: C,
) -> Result<(), RenderError> {
if radius == 0 || width == 0 || sweep_deg == 0 {
return Ok(());
}
let total = sweep_deg.unsigned_abs().min(360);
let step: i32 = if sweep_deg >= 0 { 1 } else { -1 };
let r = radius as f32;
let point_at = |deg: i32| -> Point {
let (s, c) = arc_sin_cos(deg);
Point::new(center.x + (c * r) as i32, center.y - (s * r) as i32)
};
let mut prev = point_at(start_deg);
for i in 1..=total as i32 {
let next = point_at(start_deg + i * step);
self.stroke_line(prev, next, color, width)?;
prev = next;
}
Ok(())
}
fn fill_arc(
&mut self,
center: Point,
radius: u32,
start_deg: i32,
sweep_deg: i32,
color: C,
) -> Result<(), RenderError> {
if radius == 0 || sweep_deg == 0 {
return Ok(());
}
let total = sweep_deg.unsigned_abs().min(360);
let step: i32 = if sweep_deg >= 0 { 1 } else { -1 };
let r = radius as f32;
for i in 0..=total as i32 {
let (s, c) = arc_sin_cos(start_deg + i * step);
let end = Point::new(center.x + (c * r) as i32, center.y - (s * r) as i32);
self.stroke_line(center, end, color, 2)?;
}
Ok(())
}
fn push_clip(&mut self, _rect: Rectangle) {}
fn pop_clip(&mut self) {}
}
pub struct DrawTargetRenderer<'d, D> {
target: &'d mut D,
}
impl<'d, D> DrawTargetRenderer<'d, D> {
pub fn new(target: &'d mut D) -> Self {
Self { target }
}
}
impl<'d, C, D> Renderer<C> for DrawTargetRenderer<'d, D>
where
C: PixelColor,
D: DrawTarget<Color = C>,
{
fn fill_rect(&mut self, rect: Rectangle, color: C) -> Result<(), RenderError> {
rect.into_styled(PrimitiveStyle::with_fill(color))
.draw(self.target)
.map_err(|_| RenderError)
}
fn stroke_rect(&mut self, rect: Rectangle, color: C) -> Result<(), RenderError> {
let style = PrimitiveStyleBuilder::new()
.stroke_color(color)
.stroke_width(1)
.stroke_alignment(StrokeAlignment::Inside)
.build();
rect.into_styled(style)
.draw(self.target)
.map_err(|_| RenderError)
}
fn fill_circle(&mut self, center: Point, radius: u32, color: C) -> Result<(), RenderError> {
Circle::with_center(center, radius * 2)
.into_styled(PrimitiveStyle::with_fill(color))
.draw(self.target)
.map_err(|_| RenderError)
}
fn stroke_line(
&mut self,
start: Point,
end: Point,
color: C,
width: u32,
) -> Result<(), RenderError> {
Line::new(start, end)
.into_styled(PrimitiveStyle::with_stroke(color, width))
.draw(self.target)
.map_err(|_| RenderError)
}
fn draw_text(
&mut self,
text: &str,
position: Point,
font: &MonoFont<'_>,
color: C,
alignment: Alignment,
) -> Result<(), RenderError> {
let style = MonoTextStyle::new(font, color);
Text::with_alignment(text, position, style, alignment)
.draw(self.target)
.map(|_| ())
.map_err(|_| RenderError)
}
fn draw_image(&mut self, top_left: Point, size: Size, pixels: &[C]) -> Result<(), RenderError> {
let area = Rectangle::new(top_left, size);
self.target
.fill_contiguous(&area, pixels.iter().copied())
.map_err(|_| RenderError)
}
}
static SIN_TABLE: [f32; 360] = [
0.0,
0.01745241,
0.0348995,
0.05233596,
0.06975647,
0.08715574,
0.1045285,
0.1218693,
0.1391731,
0.1564345,
0.1736482,
0.190809,
0.2079117,
0.2249511,
0.2419219,
0.258819,
0.2756374,
0.2923717,
0.309017,
0.3255682,
0.3420201,
0.3583679,
0.3746066,
0.3907311,
0.4067366,
0.4226183,
0.4383711,
0.4539905,
0.4694716,
0.4848096,
0.5,
0.5150381,
0.5299193,
0.544639,
0.5591929,
0.5735764,
0.5877853,
0.601815,
0.6156615,
0.6293204,
0.6427876,
0.656059,
0.6691306,
0.6819984,
0.6946584,
0.7071068,
0.7193398,
0.7313537,
0.7431448,
0.7547096,
0.7660444,
0.777146,
0.7880108,
0.7986355,
0.809017,
0.819152,
0.8290376,
0.8386706,
0.8480481,
0.8571673,
0.8660254,
0.8746197,
0.8829476,
0.8910065,
0.898794,
0.9063078,
0.9135455,
0.9205049,
0.9271839,
0.9335804,
0.9396926,
0.9455186,
0.9510565,
0.9563048,
0.9612617,
0.9659258,
0.9702957,
0.9743701,
0.9781476,
0.9816272,
0.9848078,
0.9876883,
0.9902681,
0.9925462,
0.9945219,
0.9961947,
0.9975641,
0.9986295,
0.9993908,
0.9998477,
1.0,
0.9998477,
0.9993908,
0.9986295,
0.9975641,
0.9961947,
0.9945219,
0.9925462,
0.9902681,
0.9876883,
0.9848078,
0.9816272,
0.9781476,
0.9743701,
0.9702957,
0.9659258,
0.9612617,
0.9563048,
0.9510565,
0.9455186,
0.9396926,
0.9335804,
0.9271839,
0.9205049,
0.9135455,
0.9063078,
0.898794,
0.8910065,
0.8829476,
0.8746197,
0.8660254,
0.8571673,
0.8480481,
0.8386706,
0.8290376,
0.819152,
0.809017,
0.7986355,
0.7880108,
0.777146,
0.7660444,
0.7547096,
0.7431448,
0.7313537,
0.7193398,
0.7071068,
0.6946584,
0.6819984,
0.6691306,
0.656059,
0.6427876,
0.6293204,
0.6156615,
0.601815,
0.5877853,
0.5735764,
0.5591929,
0.544639,
0.5299193,
0.5150381,
0.5,
0.4848096,
0.4694716,
0.4539905,
0.4383711,
0.4226183,
0.4067366,
0.3907311,
0.3746066,
0.3583679,
0.3420201,
0.3255682,
0.309017,
0.2923717,
0.2756374,
0.258819,
0.2419219,
0.2249511,
0.2079117,
0.190809,
0.1736482,
0.1564345,
0.1391731,
0.1218693,
0.1045285,
0.08715574,
0.06975647,
0.05233596,
0.0348995,
0.01745241,
0.0,
-0.01745241,
-0.0348995,
-0.05233596,
-0.06975647,
-0.08715574,
-0.1045285,
-0.1218693,
-0.1391731,
-0.1564345,
-0.1736482,
-0.190809,
-0.2079117,
-0.2249511,
-0.2419219,
-0.258819,
-0.2756374,
-0.2923717,
-0.309017,
-0.3255682,
-0.3420201,
-0.3583679,
-0.3746066,
-0.3907311,
-0.4067366,
-0.4226183,
-0.4383711,
-0.4539905,
-0.4694716,
-0.4848096,
-0.5,
-0.5150381,
-0.5299193,
-0.544639,
-0.5591929,
-0.5735764,
-0.5877853,
-0.601815,
-0.6156615,
-0.6293204,
-0.6427876,
-0.656059,
-0.6691306,
-0.6819984,
-0.6946584,
-0.7071068,
-0.7193398,
-0.7313537,
-0.7431448,
-0.7547096,
-0.7660444,
-0.777146,
-0.7880108,
-0.7986355,
-0.809017,
-0.819152,
-0.8290376,
-0.8386706,
-0.8480481,
-0.8571673,
-0.8660254,
-0.8746197,
-0.8829476,
-0.8910065,
-0.898794,
-0.9063078,
-0.9135455,
-0.9205049,
-0.9271839,
-0.9335804,
-0.9396926,
-0.9455186,
-0.9510565,
-0.9563048,
-0.9612617,
-0.9659258,
-0.9702957,
-0.9743701,
-0.9781476,
-0.9816272,
-0.9848078,
-0.9876883,
-0.9902681,
-0.9925462,
-0.9945219,
-0.9961947,
-0.9975641,
-0.9986295,
-0.9993908,
-0.9998477,
-1.0,
-0.9998477,
-0.9993908,
-0.9986295,
-0.9975641,
-0.9961947,
-0.9945219,
-0.9925462,
-0.9902681,
-0.9876883,
-0.9848078,
-0.9816272,
-0.9781476,
-0.9743701,
-0.9702957,
-0.9659258,
-0.9612617,
-0.9563048,
-0.9510565,
-0.9455186,
-0.9396926,
-0.9335804,
-0.9271839,
-0.9205049,
-0.9135455,
-0.9063078,
-0.898794,
-0.8910065,
-0.8829476,
-0.8746197,
-0.8660254,
-0.8571673,
-0.8480481,
-0.8386706,
-0.8290376,
-0.819152,
-0.809017,
-0.7986355,
-0.7880108,
-0.777146,
-0.7660444,
-0.7547096,
-0.7431448,
-0.7313537,
-0.7193398,
-0.7071068,
-0.6946584,
-0.6819984,
-0.6691306,
-0.656059,
-0.6427876,
-0.6293204,
-0.6156615,
-0.601815,
-0.5877853,
-0.5735764,
-0.5591929,
-0.544639,
-0.5299193,
-0.5150381,
-0.5,
-0.4848096,
-0.4694716,
-0.4539905,
-0.4383711,
-0.4226183,
-0.4067366,
-0.3907311,
-0.3746066,
-0.3583679,
-0.3420201,
-0.3255682,
-0.309017,
-0.2923717,
-0.2756374,
-0.258819,
-0.2419219,
-0.2249511,
-0.2079117,
-0.190809,
-0.1736482,
-0.1564345,
-0.1391731,
-0.1218693,
-0.1045285,
-0.08715574,
-0.06975647,
-0.05233596,
-0.0348995,
-0.01745241,
];
#[must_use]
pub fn arc_sin_cos(deg: i32) -> (f32, f32) {
let d = deg.rem_euclid(360) as usize;
let c = (deg + 90).rem_euclid(360) as usize;
(SIN_TABLE[d], SIN_TABLE[c])
}