mindustry logic execution, map- and schematic- parsing and rendering
Diffstat (limited to 'lemu/src/instructions/draw.rs')
| -rw-r--r-- | lemu/src/instructions/draw.rs | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/lemu/src/instructions/draw.rs b/lemu/src/instructions/draw.rs new file mode 100644 index 0000000..701fc55 --- /dev/null +++ b/lemu/src/instructions/draw.rs @@ -0,0 +1,232 @@ +use super::{get_num, Flow, LInstruction}; +use crate::{ + executor::{Display, DisplayState, ExecutorContext}, + memory::{LAddress, LRegistry, LVar}, +}; +use enum_dispatch::enum_dispatch; +use fimg::Image; + +pub const INSTRS: &[&str] = &[ + "clear", "color", "col", "stroke", "line", "rect", "lineRect", "triangle", +]; + +#[enum_dispatch] +pub trait DrawInstruction<'v> { + #[allow(unused_variables)] + fn draw( + &self, + mem: &mut LRegistry<'v>, + image: &mut Image<&mut [u8], 4>, + state: &mut DisplayState, + ); +} + +#[derive(Debug)] +#[enum_dispatch(DrawInstruction)] +pub enum DrawInstr<'v> { + DrawLine(Line<'v>), + DrawRectBordered(RectBordered<'v>), + DrawRectFilled(RectFilled<'v>), + DrawTriangle(Triangle<'v>), + Clear(Clear<'v>), + SetColorDyn(SetColorDyn<'v>), + SetColorConst(SetColorConst), + SetStroke(SetStroke<'v>), +} + +#[derive(Debug)] +pub struct Clear<'v> { + pub r: LAddress<'v>, + pub g: LAddress<'v>, + pub b: LAddress<'v>, + pub a: LAddress<'v>, +} + +impl<'v> DrawInstruction<'v> for Clear<'v> { + fn draw(&self, mem: &mut LRegistry<'v>, image: &mut Image<&mut [u8], 4>, _: &mut DisplayState) { + macro_rules! u8 { + ($v:ident) => { + match mem.get(self.$v) { + LVar::Num(n) => n.round() as u8, + _ => return, + } + }; + } + let (r, g, b, a) = (u8!(r), u8!(g), u8!(b), u8!(a)); + for [r2, g2, b2, a2] in image.chunked_mut() { + (*r2, *b2, *g2, *a2) = (r, g, b, a); + } + } +} + +#[derive(Debug)] +pub struct SetColorDyn<'v> { + pub r: LAddress<'v>, + pub g: LAddress<'v>, + pub b: LAddress<'v>, + pub a: LAddress<'v>, +} +impl<'v> DrawInstruction<'v> for SetColorDyn<'v> { + fn draw(&self, mem: &mut LRegistry<'v>, _: &mut Image<&mut [u8], 4>, state: &mut DisplayState) { + macro_rules! u8 { + ($v:ident) => { + match mem.get(self.$v) { + LVar::Num(n) => n.round() as u8, + _ => return, + } + }; + } + state.color = (u8!(r), u8!(g), u8!(b), u8!(a)); + } +} + +#[derive(Debug)] +pub struct SetColorConst { + pub r: u8, + pub g: u8, + pub b: u8, + pub a: u8, +} +impl DrawInstruction<'_> for SetColorConst { + fn draw(&self, _: &mut LRegistry<'_>, _: &mut Image<&mut [u8], 4>, state: &mut DisplayState) { + state.color = (self.r, self.g, self.b, self.a); + } +} + +#[derive(Debug)] +pub struct SetStroke<'v> { + pub size: LAddress<'v>, +} +impl<'v> DrawInstruction<'v> for SetStroke<'v> { + fn draw(&self, mem: &mut LRegistry<'v>, _: &mut Image<&mut [u8], 4>, state: &mut DisplayState) { + if let LVar::Num(n) = mem.get(self.size) { + state.stroke = n; + } + } +} + +pub type Point<'v> = (LAddress<'v>, LAddress<'v>); +#[rustfmt::skip] +macro_rules! point { + ($mem:ident@$point:expr) => {{ + let LVar::Num(a) = $mem.get($point.0) else { return; }; + let LVar::Num(b) = $mem.get($point.1) else { return; }; + (a,b) + }} +} + +macro_rules! map { + ($tup:expr, $fn:expr) => {{ + let (a, b) = $tup; + ($fn(a), $fn(b)) + }}; +} +#[derive(Debug)] +pub struct Line<'v> { + pub point_a: Point<'v>, + pub point_b: Point<'v>, +} +impl<'v> DrawInstruction<'v> for Line<'v> { + #[allow(unused_variables)] + fn draw( + &self, + mem: &mut LRegistry<'v>, + image: &mut Image<&mut [u8], 4>, + state: &mut DisplayState, + ) { + // i will happily ignore that stroke specifys the stroke of lines + let a = map!(point!([email protected]_a), |n| n as i32); + let b = map!(point!([email protected]_b), |n| n as i32); + image.line(a, b, state.col()); + } +} + +macro_rules! unbounded { + ($img:ident @ $x:expr => $y:expr) => { + $img.width() < $x || $img.height() < $y + }; +} + +#[derive(Debug)] +pub struct RectFilled<'v> { + pub position: Point<'v>, + pub width: LAddress<'v>, + pub height: LAddress<'v>, +} +impl<'v> DrawInstruction<'v> for RectFilled<'v> { + fn draw( + &self, + mem: &mut LRegistry<'v>, + image: &mut Image<&mut [u8], 4>, + state: &mut DisplayState, + ) { + let pos = map!(point!([email protected]), |n| n as u32); + let width = get_num!(mem.get(self.width), or ret) as u32; + let height = get_num!(mem.get(self.height), or ret) as u32; + if unbounded!(image @ pos.0 + width => pos.1 + height) { + return; + } + // SAFETY: bounds checked above + unsafe { image.filled_box(pos, width, height, state.col()) }; + } +} + +#[derive(Debug)] +pub struct RectBordered<'v> { + pub position: Point<'v>, + pub width: LAddress<'v>, + pub height: LAddress<'v>, +} + +impl<'v> DrawInstruction<'v> for RectBordered<'v> { + fn draw( + &self, + mem: &mut LRegistry<'v>, + image: &mut Image<&mut [u8], 4>, + state: &mut DisplayState, + ) { + // happily ignoring that state specifies box stroke width + let pos = map!(point!([email protected]), |n| n as u32); + let width = get_num!(mem.get(self.width), or ret) as u32; + let height = get_num!(mem.get(self.height), or ret) as u32; + if unbounded!(image @ pos.0 + width => pos.1 + height) { + return; + } + // SAFETY: bounds checked above + unsafe { image.r#box(pos, width, height, state.col()) }; + } +} + +#[derive(Debug)] +pub struct Triangle<'v> { + pub points: (Point<'v>, Point<'v>, Point<'v>), +} +impl<'v> DrawInstruction<'v> for Triangle<'v> { + fn draw(&self, mem: &mut LRegistry<'v>, i: &mut Image<&mut [u8], 4>, state: &mut DisplayState) { + let to32 = |n| n as f32; + let (a, b, c) = ( + map!(point!([email protected]), to32), + map!(point!([email protected]), to32), + map!(point!([email protected]), to32), + ); + if unbounded!(i @ a.0 as u32 => a.1 as u32) + || unbounded!(i @ b.0 as u32 => b.1 as u32) + || unbounded!(i @ c.0 as u32 => c.1 as u32) + { + return; + } + // SAFETY: bounds are checked + unsafe { i.tri(a, b, c, state.col()) }; + } +} + +#[derive(Debug)] +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 + } +} |