mindustry logic execution, map- and schematic- parsing and rendering
-rw-r--r--lemu/Cargo.toml1
-rw-r--r--lemu/src/executor/builder.rs11
-rw-r--r--lemu/src/executor/mod.rs41
-rw-r--r--lemu/src/instructions/draw.rs471
-rw-r--r--lemu/src/instructions/mod.rs2
-rw-r--r--lemu/src/main.rs2
-rw-r--r--lemu/src/memory.rs7
7 files changed, 371 insertions, 164 deletions
diff --git a/lemu/Cargo.toml b/lemu/Cargo.toml
index 11bc0a9..714ecfe 100644
--- a/lemu/Cargo.toml
+++ b/lemu/Cargo.toml
@@ -18,6 +18,7 @@ rust-fuzzy-search = { version = "0.1.1", optional = true }
beef = "0.5"
lerr = { version = "0.1.5", optional = true }
comat = { version = "0.1.2", optional = true }
+vecto = "0.1.1"
[features]
bin = ["fimg/save", "diagnose"]
diff --git a/lemu/src/executor/builder.rs b/lemu/src/executor/builder.rs
index 3f83159..fbc7284 100644
--- a/lemu/src/executor/builder.rs
+++ b/lemu/src/executor/builder.rs
@@ -2,8 +2,8 @@ use fimg::Image;
use std::{collections::VecDeque, io::Write as Wr};
use super::{
- Display, Drawing, Executor, ExecutorContext, Instruction, Limit, Memory, PInstr, UPInstr,
- BANK_SIZE, CELL_SIZE,
+ Display, DisplayState, Drawing, Executor, ExecutorContext, Instruction, Limit, Memory, PInstr,
+ UPInstr, BANK_SIZE, CELL_SIZE,
};
use crate::{
code::Code,
@@ -15,7 +15,7 @@ use crate::{
/// for internal use by [parser](crate::parser) only
pub struct ExecutorBuilderInternal<'v, W: Wr> {
- displays: Vec<Image<Vec<u8>, 4>>,
+ displays: Box<[(Image<Vec<u8>, 4>, DisplayState)]>,
pub(crate) program: Vec<UPInstr<'v>>,
output: Option<W>,
banks: Vec<f64>,
@@ -30,7 +30,10 @@ impl<'s, W: Wr> ExecutorBuilderInternal<'s, W> {
pub(crate) fn new(w: Option<W>, d: Vec<Image<Vec<u8>, 4>>) -> Self {
Self {
output: w,
- displays: d,
+ displays: d
+ .into_iter()
+ .map(|d| (d, DisplayState::default()))
+ .collect(),
program: vec![],
banks: vec![],
cells: vec![],
diff --git a/lemu/src/executor/mod.rs b/lemu/src/executor/mod.rs
index 94b9c57..fb33c91 100644
--- a/lemu/src/executor/mod.rs
+++ b/lemu/src/executor/mod.rs
@@ -1,10 +1,13 @@
mod builder;
-use crate::debug::{info::DebugInfo, printable::Printable};
+use crate::{
+ debug::{info::DebugInfo, printable::Printable},
+ instructions::draw::Drawn,
+};
use super::{
code::{Code, PInstr},
- instructions::{DrawInstr, DrawInstruction, Flow, Instr, LInstruction},
+ instructions::{DrawInstr, Flow, Frozen, Instr, LInstruction},
lexer::Token,
memory::{LAddress, LRegistry, LVar},
};
@@ -132,13 +135,12 @@ pub enum UPInstr<'s> {
}
pub struct Drawing {
- pub displays: Box<[fimg::Image<Vec<u8>, 4>]>,
- /// points to `Executor.program`
- pub buffer: VecDeque<*const DrawInstr>,
+ pub displays: Box<[(fimg::Image<Vec<u8>, 4>, DisplayState)]>,
+ pub buffer: VecDeque<Drawn>,
}
impl Drawing {
- fn buffer(&mut self, i: &DrawInstr) {
+ fn buffer(&mut self, i: Drawn) {
self.buffer.push_back(i);
}
}
@@ -155,8 +157,11 @@ pub struct ExecutorContext<'strings, W: Write> {
pub iterations: usize,
}
+/// State of a display.
pub struct DisplayState {
+ /// Color to draw
pub color: (u8, u8, u8, u8),
+ /// Stroke to draw
pub stroke: f64,
}
@@ -176,15 +181,17 @@ impl Default for DisplayState {
}
impl<'s, W: Write> ExecutorContext<'s, W> {
+ pub fn buffer(&mut self, instr: &DrawInstr) {
+ if let Some(i) = instr.freeze(&self.memory) {
+ self.display.buffer(i)
+ }
+ }
+
pub fn flush(&mut self, to: Display) {
- let mut state = DisplayState::default();
- // SAFETY: safe as long as the instruction isnt held too long
- while let Some(d) = unsafe { self.display.buffer.pop_front().map(|v| &*v) } {
- d.draw(
- &mut self.memory,
- &mut self.display.displays[to.0].as_mut(),
- &mut state,
- );
+ let (ref mut img, ref mut state) = &mut self.display.displays[to.0];
+ while let Some(d) = self.display.buffer.pop_front() {
+ use crate::instructions::draw::Apply;
+ d.apply(img.as_mut(), state);
}
}
@@ -217,7 +224,7 @@ pub struct Output<W: Write> {
/// Everything created by a `print` instruction.
pub output: Option<W>,
/// Logic displays that were drawn with `draw` instructions.
- pub displays: Box<[Image<Vec<u8>, 4>]>,
+ pub displays: Box<[(Image<Vec<u8>, 4>, DisplayState)]>,
/// Memory banks, written to with the `write`/`read` instructions
pub cells: Box<[[f64; CELL_SIZE]]>,
/// Memory cells, written to with the `write`/`read` instructions
@@ -227,7 +234,7 @@ pub struct Output<W: Write> {
impl<'s, W: Write> Executor<'s, W> {
/// Consume this executor, returning all output.
pub fn output(mut self) -> Output<W> {
- for display in &mut *self.inner.display.displays {
+ for (display, _) in &mut *self.inner.display.displays {
// TODO make the instructions draw flipped-ly
display.flip_v();
}
@@ -256,7 +263,7 @@ impl<'s, W: Write> Executor<'s, W> {
i.run(&mut self.inner)
}
PInstr::Draw(i) => {
- self.inner.display.buffer(i);
+ self.inner.buffer(i);
Flow::Continue
}
_ => Flow::Continue,
diff --git a/lemu/src/instructions/draw.rs b/lemu/src/instructions/draw.rs
index bc22d4c..7bcca94 100644
--- a/lemu/src/instructions/draw.rs
+++ b/lemu/src/instructions/draw.rs
@@ -6,34 +6,91 @@ use crate::{
};
use enum_dispatch::enum_dispatch;
use fimg::Image;
-use std::fmt;
+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 DrawInstruction: Printable {
- fn draw(
- &self,
- mem: &mut LRegistry<'_>,
- image: &mut Image<&mut [u8], 4>,
- state: &mut DisplayState,
- );
+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}"),
+ }
+ }
}
-#[derive(Debug, Copy, Clone)]
-#[enum_dispatch(DrawInstruction)]
-pub enum DrawInstr {
- Line(Line),
- RectBordered(RectBordered),
- RectFilled(RectFilled),
- Triangle(Triangle),
- Clear(Clear),
- SetColor(SetColor),
- SetStroke(SetStroke),
- Poly(Poly),
- LinePoly(LinePoly),
+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) }
+ })+
+ }
+}
+
+dinstr! {
+ Line,
+ RectBordered,
+ RectFilled,
+ Triangle,
+ Clear,
+ SetColor,
+ SetStroke,
+ Poly,
+ LinePoly
+}
+
+impl Frozen<Drawn> for DrawInstr {
+ fn freeze(&self, mem: &LRegistry<'_>) -> Option<Drawn> {
+ Some(match self {
+ Self::Line(i) => Drawn::from(i.freeze(mem)?),
+ Self::RectBordered(i) => Drawn::from(i.freeze(mem)?),
+ Self::RectFilled(i) => Drawn::from(i.freeze(mem)?),
+ Self::Triangle(i) => Drawn::from(i.freeze(mem)?),
+ Self::Clear(i) => Drawn::from(i.freeze(mem)?),
+ Self::SetColor(i) => Drawn::from(i.freeze(mem)?),
+ Self::SetStroke(i) => Drawn::from(i.freeze(mem)?),
+ Self::Poly(i) => Drawn::from(i.freeze(mem)?),
+ Self::LinePoly(i) => Drawn::from(i.freeze(mem)?),
+ })
+ }
}
impl Printable for DrawInstr {
@@ -60,25 +117,28 @@ pub struct Clear {
pub b: LAddress,
}
-impl DrawInstruction for Clear {
- fn draw(
- &self,
- mem: &mut LRegistry<'_>,
- image: &mut Image<&mut [u8], 4>,
- _: &mut DisplayState,
- ) {
+#[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) {
+ for [r2, g2, b2, a2] in image.chunked_mut() {
+ (*r2, *b2, *g2, *a2) = (self.0, self.1, self.2, 255);
+ }
+ }
+}
+
+impl Frozen<ClearD> for Clear {
+ fn freeze(&self, mem: &LRegistry<'_>) -> Option<ClearD> {
macro_rules! u8 {
($v:ident) => {
match mem.get(self.$v) {
LVar::Num(n) => n.round() as u8,
- _ => return,
+ _ => return None,
}
};
}
- let (r, g, b) = (u8!(r), u8!(g), u8!(b));
- for [r2, g2, b2, a2] in image.chunked_mut() {
- (*r2, *b2, *g2, *a2) = (r, g, b, 255);
- }
+ Some(ClearD(u8!(r), u8!(g), u8!(b)))
}
}
@@ -92,6 +152,21 @@ impl Printable for Clear {
}
}
+impl Disp for ClearD {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "draw clear {} {} {}", self.0, self.1, self.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 {
@@ -100,22 +175,18 @@ pub struct SetColor {
pub b: LAddress,
pub a: LAddress,
}
-impl DrawInstruction for SetColor {
- fn draw(
- &self,
- mem: &mut LRegistry<'_>,
- _: &mut Image<&mut [u8], 4>,
- state: &mut DisplayState,
- ) {
+
+impl Frozen<SetColorD> for SetColor {
+ fn freeze(&self, mem: &LRegistry<'_>) -> Option<SetColorD> {
macro_rules! u8 {
($v:ident) => {
match mem.get(self.$v) {
LVar::Num(n) => n.round() as u8,
- _ => return,
+ _ => return None,
}
};
}
- state.color = (u8!(r), u8!(g), u8!(b), u8!(a));
+ Some(SetColorD((u8!(r), u8!(g), u8!(b), u8!(a))))
}
}
@@ -129,21 +200,40 @@ impl Printable for SetColor {
}
}
+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,
}
-impl DrawInstruction for SetStroke {
- fn draw(
- &self,
- mem: &mut LRegistry<'_>,
- _: &mut Image<&mut [u8], 4>,
- state: &mut DisplayState,
- ) {
- if let &LVar::Num(n) = mem.get(self.size) {
- state.stroke = n;
- }
+
+#[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)
}
}
@@ -156,11 +246,9 @@ impl Printable for SetStroke {
pub type Point = (LAddress, LAddress);
#[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)
- }}
+ ($mem:ident@$point:expr) => {
+ ($mem.get($point.0).num()?, $mem.get($point.1).num()?)
+ }
}
macro_rules! map {
@@ -176,16 +264,31 @@ pub struct Line {
pub point_b: Point,
}
-impl DrawInstruction for Line {
- fn draw(
- &self,
- mem: &mut LRegistry<'_>,
- image: &mut Image<&mut [u8], 4>,
- state: &mut DisplayState,
- ) {
- let a = map!(point!([email protected]_a), |n| n as f32);
- let b = map!(point!([email protected]_b), |n| n as f32);
- image.thick_line(a, b, state.stroke as f32, state.col());
+#[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
+ )
}
}
@@ -207,17 +310,34 @@ pub struct RectFilled {
pub height: LAddress,
}
-impl DrawInstruction for RectFilled {
- fn draw(
- &self,
- mem: &mut LRegistry<'_>,
- 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)) as u32;
- let height = get_num!(mem.get(self.height)) as u32;
- image.filled_box(pos, width, height, state.col());
+#[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
+ )
}
}
@@ -239,6 +359,16 @@ pub struct RectBordered {
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!(
@@ -249,17 +379,30 @@ impl Printable for RectBordered {
}
}
-impl DrawInstruction for RectBordered {
- fn draw(
- &self,
- mem: &mut LRegistry<'_>,
- 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)) as u32;
- let height = get_num!(mem.get(self.height)) as u32;
- image.stroked_box(pos, width, height, state.stroke.round() as u32, state.col());
+#[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,
+ ),
+ ))
}
}
@@ -268,22 +411,36 @@ impl DrawInstruction for RectBordered {
pub struct Triangle {
pub points: (Point, Point, Point),
}
-impl DrawInstruction for Triangle {
- fn draw(
- &self,
- mem: &mut LRegistry<'_>,
- 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),
- );
- i.tri::<f32>(a, b, c, state.col());
+
+#[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!(
@@ -308,28 +465,49 @@ pub struct Poly {
pub(crate) rot: LAddress,
}
-impl DrawInstruction for Poly {
- fn draw(
- &self,
- mem: &mut LRegistry<'_>,
- image: &mut Image<&mut [u8], 4>,
- state: &mut DisplayState,
- ) {
+#[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;
- if sides < 90 {
- image.poly(
- map!(point!([email protected]), |n| n as f32),
+ 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,
- state.col(),
- );
+ )
} else {
- image.circle(
+ PolyD::Circle(
map!(point!([email protected]), |n: f64| n.round() as i32),
get_num!(mem.get(self.radius)).round() as i32,
- state.col(),
- );
+ )
+ })
+ }
+}
+
+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}"),
}
}
}
@@ -353,30 +531,41 @@ pub struct LinePoly {
pub(crate) rot: LAddress,
}
-impl DrawInstruction for LinePoly {
- fn draw(
- &self,
- mem: &mut LRegistry<'_>,
- image: &mut Image<&mut [u8], 4>,
- state: &mut DisplayState,
- ) {
- let sides = get_num!(mem.get(self.sides)).round() as usize;
- if sides < 90 {
- image.border_poly(
- map!(point!([email protected]), |n| n as f32),
- sides,
- get_num!(mem.get(self.radius)) as f32,
- get_num!(mem.get(self.rot)) as f32,
- state.stroke as f32,
- state.col(),
- );
- } else {
- image.border_circle(
- map!(point!([email protected]), |n: f64| n.round() as i32),
- get_num!(mem.get(self.radius)).round() as i32,
- state.col(),
- );
- }
+#[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
+ )
}
}
diff --git a/lemu/src/instructions/mod.rs b/lemu/src/instructions/mod.rs
index f4d6565..e072a24 100644
--- a/lemu/src/instructions/mod.rs
+++ b/lemu/src/instructions/mod.rs
@@ -19,7 +19,7 @@ mod mop;
mod mop2;
pub use cop::ConditionOp;
-pub use draw::{DrawInstr, DrawInstruction};
+pub use draw::{DrawInstr, Frozen};
use enum_dispatch::enum_dispatch;
pub use mop::MathOp1;
pub use mop2::MathOp2;
diff --git a/lemu/src/main.rs b/lemu/src/main.rs
index 7231eb9..bbb45b4 100644
--- a/lemu/src/main.rs
+++ b/lemu/src/main.rs
@@ -23,7 +23,7 @@ fn main() -> ExitCode {
lex.run();
dbg!(lex.instructions_ran);
let Output { displays, .. } = lex.output();
- for (d, i) in displays.iter().zip(1..=displays.len()) {
+ for ((d, _), i) in displays.iter().zip(1..=displays.len()) {
d.save(format!("image{i}.png"));
}
}
diff --git a/lemu/src/memory.rs b/lemu/src/memory.rs
index ddb0e8a..86024c3 100644
--- a/lemu/src/memory.rs
+++ b/lemu/src/memory.rs
@@ -28,6 +28,13 @@ impl LVar<'_> {
pub const fn null() -> LVar<'static> {
LVar::Num(0.0)
}
+
+ pub fn num(&self) -> Option<f64> {
+ match *self {
+ Self::Num(n) => Some(n),
+ Self::String(_) => None,
+ }
+ }
}
#[derive(Clone, Copy)]