Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'lemu/src/instructions/draw.rs')
| -rw-r--r-- | lemu/src/instructions/draw.rs | 601 |
1 files changed, 601 insertions, 0 deletions
diff --git a/lemu/src/instructions/draw.rs b/lemu/src/instructions/draw.rs new file mode 100644 index 0000000..1644da0 --- /dev/null +++ b/lemu/src/instructions/draw.rs @@ -0,0 +1,601 @@ +use super::{Flow, LInstruction, get_num}; +use crate::{ + debug::{info::DebugInfo, printable::Printable}, + executor::{Display, DisplayState, ExecutorContext}, + memory::{LAddress, LRegistry, LVar}, +}; +use enum_dispatch::enum_dispatch; +use fimg::Image; +use std::fmt::{self, Display as Disp}; +use vecto::Vec2; + +pub const INSTRS: &[&str] = &[ + "clear", "color", "col", "stroke", "line", "rect", "lineRect", "triangle", "poly", "linePoly", +]; + +#[enum_dispatch] +pub trait Apply: Disp { + fn apply(self, image: Image<&mut [u8], 4>, state: &mut DisplayState); +} + +#[derive(Debug)] +#[enum_dispatch(Apply)] +pub enum Drawn { + Line(LineD), + RectBordered(RectBorderedD), + RectFilled(RectFilledD), + Triangle(TriangleD), + Clear(ClearD), + SetColor(SetColorD), + SetStroke(SetStrokeD), + Poly(PolyD), + LinePoly(LinePolyD), +} + +impl std::fmt::Display for Drawn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Line(i) => write!(f, "{i}"), + Self::RectBordered(i) => write!(f, "{i}"), + Self::RectFilled(i) => write!(f, "{i}"), + Self::Triangle(i) => write!(f, "{i}"), + Self::Clear(i) => write!(f, "{i}"), + Self::SetColor(i) => write!(f, "{i}"), + Self::SetStroke(i) => write!(f, "{i}"), + Self::Poly(i) => write!(f, "{i}"), + Self::LinePoly(i) => write!(f, "{i}"), + } + } +} + +pub trait Frozen<A: Apply>: Printable { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<A>; +} + +macro_rules! dinstr { + [$($x:ident),+] => { + #[derive(Debug, Copy, Clone)] + pub enum DrawInstr { + $($x($x),)+ + } + + $(impl From<$x> for DrawInstr { + fn from(v: $x) -> Self { Self::$x(v) } + })+ + + impl Frozen<Drawn> for DrawInstr { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<Drawn> { + Some(match self { + $(Self::$x(i) => Drawn::from(i.freeze(mem)?),)+ + }) + } + } + impl Printable for DrawInstr { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + match self { + $(Self::$x(i) => i.print(info, f)),+ + } + } + } + } +} + +dinstr! { + Line, + RectBordered, + RectFilled, + Triangle, + Clear, + SetColor, + SetCol, + SetStroke, + Poly, + LinePoly +} + +#[derive(Debug, Copy, Clone)] + +pub struct Clear { + pub r: LAddress, + pub g: LAddress, + pub b: LAddress, +} + +#[derive(Debug, Copy, Clone)] +pub struct ClearD((u8, u8, u8)); + +impl Apply for ClearD { + fn apply(self, mut image: Image<&mut [u8], 4>, _: &mut DisplayState) { + let (r, g, b) = self.0; + for [r2, g2, b2, a2] in image.chunked_mut() { + (*r2, *b2, *g2, *a2) = (r, g, b, 255); + } + } +} + +macro_rules! u8 { + ($self:ident, $mem:expr, $($v:ident)+) => { + ($(match $mem.get($self.$v) { + LVar::Num(n) => n.round() as u8, + _ => return None, + },)+) + }; +} + +impl Frozen<ClearD> for Clear { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<ClearD> { + Some(ClearD(u8!(self, mem, r g b))) + } +} + +impl Printable for Clear { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!( + f, + "draw clear {} {} {}", + info[self.r], info[self.g], info[self.b] + ) + } +} + +impl Disp for ClearD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "draw clear {} {} {}", self.0.0, self.0.1, self.0.2) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct SetColorD((u8, u8, u8, u8)); + +impl Apply for SetColorD { + fn apply(self, _: Image<&mut [u8], 4>, state: &mut DisplayState) { + state.color = self.0; + } +} + +#[derive(Debug, Copy, Clone)] + +pub struct SetColor { + pub r: LAddress, + pub g: LAddress, + pub b: LAddress, + pub a: LAddress, +} + +impl Frozen<SetColorD> for SetColor { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<SetColorD> { + Some(SetColorD(u8!(self, mem, r g b a))) + } +} + +impl Printable for SetColor { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!( + f, + "draw color {} {} {} {}", + info[self.r], info[self.g], info[self.b], info[self.a] + ) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct SetCol { + pub col: LAddress, +} + +impl Printable for SetCol { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, "draw col ")?; + match &info[self.col] { + crate::debug::info::VarData::Variable(v) => write!(f, "{v}"), + crate::debug::info::VarData::Constant(c) => match c { + LVar::Num(n) => write!(f, "0x{:0<6x}", *n as u32), + LVar::String(s) => write!(f, r#""{s}""#), + }, + } + } +} + +impl Frozen<SetColorD> for SetCol { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<SetColorD> { + let [r, g, b, a] = fimg::Pack::unpack(mem.get(self.col).num()? as u32); + Some(SetColorD((r, g, b, a))) + } +} + +impl Disp for SetColorD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "draw color {} {} {} {}", + self.0.0, self.0.1, self.0.2, self.0.3 + ) + } +} + +#[derive(Debug, Copy, Clone)] + +pub struct SetStroke { + pub size: LAddress, +} + +#[derive(Debug, Copy, Clone)] +pub struct SetStrokeD(f64); + +impl Apply for SetStrokeD { + fn apply(self, _: Image<&mut [u8], 4>, state: &mut DisplayState) { + state.stroke = self.0; + } +} + +impl Frozen<SetStrokeD> for SetStroke { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<SetStrokeD> { + mem.get(self.size).num().map(SetStrokeD) + } +} + +impl Disp for SetStrokeD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "draw stroke {}", self.0) + } +} + +impl Printable for SetStroke { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, "draw stroke {}", info[self.size]) + } +} + +pub type Point = (LAddress, LAddress); +#[rustfmt::skip] +macro_rules! point { + ($mem:ident@$point:expr) => { + ($mem.get($point.0).num()?, $mem.get($point.1).num()?) + } +} + +macro_rules! map { + ($tup:expr, $fn:expr) => {{ + let (a, b) = $tup; + ($fn(a), $fn(b)) + }}; +} +#[derive(Debug, Copy, Clone)] + +pub struct Line { + pub point_a: Point, + pub point_b: Point, +} + +#[derive(Debug)] +pub struct LineD(Vec2, Vec2); + +impl Apply for LineD { + fn apply(self, mut image: Image<&mut [u8], 4>, state: &mut DisplayState) { + image.thick_line(self.0, self.1, state.stroke as f32, state.col()); + } +} + +impl Frozen<LineD> for Line { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<LineD> { + Some(LineD( + map!(point!([email protected]_a), |n| n as f32).into(), + map!(point!([email protected]_b), |n| n as f32).into(), + )) + } +} + +impl Disp for LineD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "draw line {} {} {} {}", + self.0.x, self.0.y, self.1.x, self.1.y + ) + } +} + +impl Printable for Line { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!( + f, + "draw line {} {} {} {}", + info[self.point_a.0], info[self.point_a.1], info[self.point_b.0], info[self.point_b.1] + ) + } +} + +#[derive(Debug, Copy, Clone)] + +pub struct RectFilled { + pub position: Point, + pub width: LAddress, + pub height: LAddress, +} + +#[derive(Debug)] +pub struct RectFilledD((u32, u32), (u32, u32)); + +impl Apply for RectFilledD { + fn apply(self, mut image: Image<&mut [u8], 4>, state: &mut DisplayState) { + image.filled_box(self.0, self.1.0, self.1.1, state.col()); + } +} + +impl Frozen<RectFilledD> for RectFilled { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<RectFilledD> { + Some(RectFilledD( + map!(point!([email protected]), |n| n as u32), + ( + get_num!(mem.get(self.width)) as u32, + get_num!(mem.get(self.height)) as u32, + ), + )) + } +} + +impl Disp for RectFilledD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "draw rect {} {} {} {}", + self.0.0, self.0.1, self.1.0, self.1.1 + ) + } +} + +impl Printable for RectFilled { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!( + f, + "draw rect {} {} {} {}", + info[self.position.0], info[self.position.1], info[self.width], info[self.height] + ) + } +} + +#[derive(Debug, Copy, Clone)] + +pub struct RectBordered { + pub position: Point, + pub width: LAddress, + pub height: LAddress, +} + +impl Disp for RectBorderedD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "draw lineRect {} {} {} {}", + self.0.0, self.0.1, self.1.0, self.1.1 + ) + } +} + +impl Printable for RectBordered { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!( + f, + "draw lineRect {} {} {} {}", + info[self.position.0], info[self.position.1], info[self.width], info[self.height] + ) + } +} + +#[derive(Debug)] +pub struct RectBorderedD((u32, u32), (u32, u32)); + +impl Apply for RectBorderedD { + fn apply(self, mut image: Image<&mut [u8], 4>, state: &mut DisplayState) { + image.stroked_box( + self.0, + self.1.0, + self.1.1, + state.stroke.round() as u32, + state.col(), + ); + } +} + +impl Frozen<RectBorderedD> for RectBordered { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<RectBorderedD> { + Some(RectBorderedD( + map!(point!([email protected]), |n| n as u32), + ( + get_num!(mem.get(self.width)) as u32, + get_num!(mem.get(self.height)) as u32, + ), + )) + } +} + +#[derive(Debug, Copy, Clone)] + +pub struct Triangle { + pub points: (Point, Point, Point), +} + +#[derive(Debug)] +pub struct TriangleD(Vec2, Vec2, Vec2); + +impl Apply for TriangleD { + fn apply(self, mut image: Image<&mut [u8], 4>, state: &mut DisplayState) { + image.tri::<f32>(self.0, self.1, self.2, state.col()); + } +} + +impl Frozen<TriangleD> for Triangle { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<TriangleD> { + Some(TriangleD( + map!(point!([email protected]), |n| n as f32).into(), + map!(point!([email protected]), |n| n as f32).into(), + map!(point!([email protected]), |n| n as f32).into(), + )) + } +} + +impl Disp for TriangleD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "draw triangle {} {} {} {} {} {}", + self.0.x, self.0.y, self.1.x, self.1.y, self.2.x, self.2.y + ) + } +} + +impl Printable for Triangle { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!( + f, + "draw triangle {} {} {} {} {} {}", + info[self.points.0.0], + info[self.points.0.1], + info[self.points.1.0], + info[self.points.1.1], + info[self.points.2.0], + info[self.points.2.1] + ) + } +} + +#[derive(Debug, Copy, Clone)] + +pub struct Poly { + pub(crate) pos: Point, + pub(crate) sides: LAddress, + pub(crate) radius: LAddress, + pub(crate) rot: LAddress, +} + +#[derive(Debug)] +pub enum PolyD { + Poly(Vec2, usize, f32, f32), + Circle((i32, i32), i32), +} + +impl Apply for PolyD { + fn apply(self, mut image: Image<&mut [u8], 4>, state: &mut DisplayState) { + match self { + PolyD::Poly(pos, sides, radius, rotation) => { + image.poly(pos, sides, radius, rotation, state.col()) + } + PolyD::Circle(pos, radius) => image.circle(pos, radius, state.col()), + } + } +} + +impl Frozen<PolyD> for Poly { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<PolyD> { + let sides = get_num!(mem.get(self.sides)).round() as usize; + Some(if sides < 90 { + PolyD::Poly( + map!(point!([email protected]), |n| n as f32).into(), + sides, + get_num!(mem.get(self.radius)) as f32, + get_num!(mem.get(self.rot)) as f32, + ) + } else { + PolyD::Circle( + map!(point!([email protected]), |n: f64| n.round() as i32), + get_num!(mem.get(self.radius)).round() as i32, + ) + }) + } +} + +impl Disp for PolyD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + PolyD::Poly(Vec2 { x, y }, sides, radius, rot) => { + write!(f, "draw poly {x} {y} {sides} {radius} {rot}") + } + PolyD::Circle((x, y), sides) => write!(f, "draw poly {x} {y} {sides}"), + } + } +} + +impl Printable for Poly { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!( + f, + "draw poly {} {} {} {} {}", + info[self.pos.0], info[self.pos.1], info[self.sides], info[self.radius], info[self.rot], + ) + } +} + +#[derive(Debug, Copy, Clone)] + +pub struct LinePoly { + pub(crate) pos: Point, + pub(crate) sides: LAddress, + pub(crate) radius: LAddress, + pub(crate) rot: LAddress, +} + +#[derive(Debug)] +/// border_Circle doesnt let you specify a stroke +pub struct LinePolyD(Vec2, usize, f32, f32); + +impl Apply for LinePolyD { + fn apply(self, mut image: Image<&mut [u8], 4>, state: &mut DisplayState) { + image.border_poly( + self.0, + self.1, + self.2, + self.3, + state.stroke as f32, + state.col(), + ) + } +} + +impl Frozen<LinePolyD> for LinePoly { + fn freeze(&self, mem: &LRegistry<'_>) -> Option<LinePolyD> { + Some(LinePolyD( + map!(point!([email protected]), |n| n as f32).into(), + get_num!(mem.get(self.sides)).round() as usize, + get_num!(mem.get(self.radius)) as f32, + get_num!(mem.get(self.rot)) as f32, + )) + } +} + +impl Disp for LinePolyD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "draw linePoly {} {} {} {} {}", + self.0.x, self.0.y, self.1, self.2, self.3 + ) + } +} + +impl Printable for LinePoly { + fn print(&self, info: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!( + f, + "draw linePoly {} {} {} {} {}", + info[self.pos.0], info[self.pos.1], info[self.sides], info[self.radius], info[self.rot], + ) + } +} + +#[derive(Debug, Copy, Clone, Default)] + +pub struct Flush { + pub(crate) display: Display, +} +impl LInstruction for Flush { + fn run<W: std::io::Write>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { + exec.flush(self.display); + Flow::Continue + } +} + +impl Printable for Flush { + fn print(&self, _: &DebugInfo<'_>, f: &mut impl fmt::Write) -> fmt::Result { + write!(f, "drawflush {}", self.display) + } +} |