#![deny(clippy::all)]
#![forbid(unsafe_code)]
mod color;
pub mod doc;
mod painter;
use crate::color::*;
use crate::doc::{MutSketch, OwnedSketch};
use crate::painter::Painter;
use serde::{Deserialize, Serialize};
use std::borrow::BorrowMut;
use std::collections::{HashMap, VecDeque};
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq, Hash)]
pub struct PenId(u32);
impl From<u32> for PenId {
fn from(x: u32) -> Self {
Self(x)
}
}
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub enum PenCommand {
ChangeColor(Color),
ChangeSize(PenSize),
MoveCursor(CursorPos),
Pressed,
Released,
}
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub enum Command {
ClearAll,
ResumeLastSnapshot,
TakeSnapshot,
PenCommand(PenId, PenCommand),
}
pub trait CommandSender {
fn send_command(&mut self, cmd: Command);
}
#[derive(Default)]
struct Pen {
cursor: Cursor,
prev_cursor: Cursor,
color: Color,
size: PenSize,
}
#[derive(Default)]
struct Pens(HashMap<PenId, Pen>);
impl Pens {
fn select(&mut self, id: PenId) -> &mut Pen {
self.0.entry(id).or_insert_with(Pen::default).borrow_mut()
}
}
pub struct App {
pen_id: PenId,
pens: Pens,
commands: VecDeque<Command>,
palette: ColorSelector,
snapshots: Vec<OwnedSketch>,
chained_command_sender: Option<Box<dyn FnMut(Command)>>,
}
#[derive(Default, Debug, Copy, Clone)]
struct Cursor {
pressed: bool,
pos: CursorPos,
}
#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)]
pub struct CursorPos {
pub x: isize,
pub y: isize,
}
impl App {
pub fn new(pen_id: PenId) -> Self {
App {
pens: Pens::default(),
commands: VecDeque::with_capacity(10),
palette: ColorSelector::new(&PALETTE),
snapshots: Vec::new(),
chained_command_sender: Default::default(),
pen_id,
}
}
pub fn update(&mut self, mut sketch: MutSketch) {
while let Some(command) = self.commands.pop_front() {
match command {
Command::ClearAll => {
self.snapshots.push(sketch.to_owned());
sketch.frame.fill(0x00);
}
Command::TakeSnapshot => {
self.snapshots.push(sketch.to_owned());
}
Command::ResumeLastSnapshot => {
if let Some(backup) = &self.snapshots.pop() {
sketch.copy_from(&backup.as_sketch());
}
}
Command::PenCommand(pen_id, cmd) => {
self.handle_pen_command(pen_id, cmd);
}
}
}
let mut painter = Painter::new(sketch);
painter.set_color(self.pens.select(self.pen_id).color);
draw_current_color_icon(&mut painter);
for (_, pen) in self.pens.0.iter_mut() {
painter.set_color(pen.color);
painter.set_size(pen.size);
if pen.cursor.pressed && pen.prev_cursor.pressed {
painter.draw_line(pen.prev_cursor.pos, pen.cursor.pos);
}
pen.prev_cursor = pen.cursor;
}
}
pub fn clear_all(&mut self) {
self.send_command_chained(Command::ClearAll);
}
pub fn resume_last_snapshot(&mut self) {
self.send_command_chained(Command::ResumeLastSnapshot);
}
pub fn take_snapshot(&mut self) {
self.send_command_chained(Command::TakeSnapshot);
}
pub fn move_cursor(&mut self, x: isize, y: isize) {
self.send_pen_command(PenCommand::MoveCursor(CursorPos { x, y }));
}
pub fn press(&mut self) {
self.send_pen_command(PenCommand::Pressed);
}
pub fn release(&mut self) {
self.send_pen_command(PenCommand::Released);
}
pub fn change_color(&mut self) {
if let Some(color) = self.palette.next() {
self.send_pen_command(PenCommand::ChangeColor(color));
}
}
pub fn grow_pen(&mut self) {
let mut size = self.my_pen().size;
size.grow();
self.send_pen_command(PenCommand::ChangeSize(size));
}
pub fn shrink_pen(&mut self) {
let mut size = self.my_pen().size;
size.shrink();
self.send_pen_command(PenCommand::ChangeSize(size));
}
pub fn needs_update(&self) -> bool {
!self.commands.is_empty()
}
pub fn connect_command_sender(&mut self, chained: Box<dyn FnMut(Command)>) {
self.chained_command_sender = Some(chained);
}
fn send_command_chained(&mut self, cmd: Command) {
self.commands.push_back(cmd);
if let Some(chained) = self.chained_command_sender.as_mut() {
chained(cmd);
}
}
fn send_pen_command(&mut self, cmd: PenCommand) {
let cmd = Command::PenCommand(self.pen_id, cmd);
self.send_command_chained(cmd);
}
pub fn force_release(&mut self) {
self.send_pen_command(PenCommand::Released);
}
fn handle_pen_command(&mut self, pen_id: PenId, cmd: PenCommand) {
let pen = self.pens.select(pen_id);
match cmd {
PenCommand::ChangeColor(color) => {
pen.color = color;
}
PenCommand::ChangeSize(size) => {
pen.size = size;
}
PenCommand::MoveCursor(pos) => {
pen.cursor.pos = pos;
}
PenCommand::Pressed => {
pen.cursor.pressed = true;
}
PenCommand::Released => {
pen.cursor.pressed = false;
}
}
}
fn my_pen(&mut self) -> &mut Pen {
self.pens.select(self.pen_id)
}
}
impl CommandSender for App {
fn send_command(&mut self, cmd: Command) {
self.commands.push_back(cmd);
}
}
fn draw_current_color_icon(painter: &mut Painter) {
const SQUARE_SIZE: isize = 10;
for x in 0..SQUARE_SIZE {
for y in 0..(SQUARE_SIZE - x) {
painter.draw_pixel(CursorPos { x, y });
}
}
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct PenSize(u32);
impl Default for PenSize {
fn default() -> Self {
Self(1)
}
}
impl PenSize {
fn grow(&mut self) {
self.0 = (self.0 * 2).clamp(1, 32);
}
fn shrink(&mut self) {
self.0 = (self.0 / 2).clamp(1, 32);
}
}