use std::fmt;
use crate::display::Display;
mod dither;
mod style;
pub use style::Style;
type Point = (usize, usize);
pub struct Canvas {
pub display: Display,
}
impl Canvas {
pub fn with_dot_size(width: usize, height: usize) -> Self {
let display = Display::with_dot_size(width, height);
Self { display }
}
pub fn with_output_size(width: usize, height: usize) -> Self {
let display = Display::with_output_size(width, height);
Self { display }
}
pub fn clear(&mut self) {
self.display.clear();
}
pub fn draw_line(&mut self, p0: Point, p1: Point, style: Style) {
let brightness = match style.outline {
Some(b) => b,
None => return,
};
let (x0, y0) = p0;
let (x1, y1) = p1;
match (x0 == x1, y0 == y1) {
(true, true) => self.set_dithered(x0, y0, brightness),
(false, true) => self.draw_hor_line(y0, x0, x1, brightness),
(true, false) => self.draw_ver_line(x0, y0, y1, brightness),
(false, false) => {
let (mut x0, x1) = (x0 as isize, x1 as isize);
let (mut y0, y1) = (y0 as isize, y1 as isize);
let dx = (x1 - x0).abs();
let sx = (x1 - x0).signum();
let dy = -(y1 - y0).abs();
let sy = (y1 - y0).signum();
let mut error = dx + dy;
loop {
self.set_dithered(x0 as usize, y0 as usize, brightness);
let e2 = 2 * error;
if e2 >= dy {
if x0 == x1 {
break;
}
error += dy;
x0 += sx;
}
if e2 <= dx {
if y0 == y1 {
break;
}
error += dx;
y0 += sy;
}
}
}
}
}
pub fn draw_rect(&mut self, p: Point, w: usize, h: usize, style: Style) {
if let Some(brightness) = style.fill {
for y in (p.1)..(p.1 + h) {
self.draw_line(
(p.0, y),
(p.0 + w - 1, y),
Style::outlined_with_brightness(brightness),
);
}
}
if let Some(brightness) = style.distinguishable_outline() {
if w == 0 || h == 0 {
return;
}
if w == 1 && h == 1 {
self.draw_line(p, p, Style::outlined_with_brightness(brightness));
return;
}
if w == 1 || h == 1 {
self.draw_line(
p,
if w == 1 {
(p.0, p.1 + h - 1)
} else {
(p.0 + w - 1, p.1)
},
Style::outlined_with_brightness(brightness),
);
}
self.draw_line(
p,
(p.0 + w - 1, p.1),
Style::outlined_with_brightness(brightness),
);
self.draw_line(
(p.0, p.1 + h - 1),
(p.0 + w - 1, p.1 + h - 1),
Style::outlined_with_brightness(brightness),
);
if h > 2 {
self.draw_line(
(p.0, p.1 + 1),
(p.0, p.1 + h - 2),
Style::outlined_with_brightness(brightness),
);
self.draw_line(
(p.0 + h - 1, p.1 + 1),
(p.0 + h - 1, p.1 + h - 2),
Style::outlined_with_brightness(brightness),
);
}
}
}
pub fn draw_tri(&mut self, p0: Point, p1: Point, p2: Point, style: Style) {
if let Some(brightness) = style.outline {
self.draw_line(p0, p1, Style::outlined_with_brightness(brightness));
self.draw_line(p1, p2, Style::outlined_with_brightness(brightness));
self.draw_line(p2, p0, Style::outlined_with_brightness(brightness));
}
}
pub fn draw_circle(&mut self, p: Point, r: usize, style: Style) {
let (cx, cy) = (p.0 as isize, p.1 as isize);
let mut t1 = (r / 16) as isize;
let mut x = r as isize;
let mut y = 0;
while x >= y {
for m0 in [-1, 1] {
for m1 in [-1, 1] {
let x_o1 = (cx + m0 * x) as usize;
let y_o1 = (cy + m1 * y) as usize;
let x_o2 = (cx + m0 * y) as usize;
let y_o2 = (cy + m1 * x) as usize;
if let Some(brightness) = style.fill {
let p = ((cx + m0 * y) as usize, (cy + m1 * y) as usize);
self.draw_line(
p,
(x_o1, y_o1),
Style::outlined_with_brightness(brightness),
);
self.draw_line(
p,
(x_o2, y_o2),
Style::outlined_with_brightness(brightness),
);
}
if let Some(brightness) = style.distinguishable_outline() {
self.set_dithered(x_o1, y_o1, brightness);
self.set_dithered(x_o2, y_o2, brightness);
}
}
}
y += 1;
t1 += y;
let t2 = t1 - x;
if t2 >= 0 {
t1 = t2;
x -= 1;
}
}
}
}
impl Canvas {
fn is_in_bounds(&self, x: usize, y: usize) -> bool {
let (w, h) = self.display.dot_size();
x < w && y < h
}
fn set_dithered(&mut self, x: usize, y: usize, brightness: usize) {
if self.is_in_bounds(x, y) {
const MAX_B: usize = dither::max_brightness();
match brightness {
0 => self.display.unset(x, y),
MAX_B.. => self.display.set(x, y),
b => {
if b > dither::threshold(x, y) {
self.display.set(x, y);
} else {
self.display.unset(x, y);
}
}
}
}
}
fn draw_ver_line(&mut self, x: usize, y0: usize, y1: usize, brightness: usize) {
let (y0, y1) = min_and_max(y0, y1);
for y in y0..=y1 {
self.set_dithered(x, y, brightness);
}
}
fn draw_hor_line(&mut self, y: usize, x0: usize, x1: usize, brightness: usize) {
let (x0, x1) = min_and_max(x0, x1);
for x in x0..=x1 {
self.set_dithered(x, y, brightness);
}
}
}
impl From<&Canvas> for String {
fn from(value: &Canvas) -> Self {
String::from(&value.display)
}
}
impl From<Canvas> for String {
fn from(value: Canvas) -> Self {
String::from(&value)
}
}
impl fmt::Display for Canvas {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", String::from(self))
}
}
fn min_and_max(a: usize, b: usize) -> (usize, usize) {
if a < b {
(a, b)
} else {
(b, a)
}
}