use crate::{
frame_map,
result::{RoarError, RoarResult},
};
use noise::{Fbm, MultiFractal, Perlin, RidgedMulti, Value};
use std::{collections::VecDeque, sync::Arc};
use crate::bc::Op;
use super::{value::RuntimeValue, FRAME_SIZE};
pub struct ExecutionFrame {
code_pos: usize,
code: Arc<[Op]>,
stack: VecDeque<RuntimeValue>,
input_pos: usize,
input_section: Arc<[RuntimeValue]>,
buf_pos: usize,
input_buffer: Arc<[RuntimeValue]>,
memory: Vec<RuntimeValue>,
ctx: ExecutionContext,
}
impl ExecutionFrame {
pub(super) fn new(
code: Arc<[Op]>,
input_section: Arc<[RuntimeValue]>,
input_buffer: Arc<[RuntimeValue]>,
ctx: ExecutionContext,
) -> Self {
Self {
code_pos: 0,
code,
stack: VecDeque::new(),
input_pos: 0,
input_section,
buf_pos: 0,
input_buffer,
memory: Vec::with_capacity(256),
ctx,
}
}
fn pull_op(&mut self) -> RoarResult<Op> {
if self.code_pos < self.code.len() {
self.code_pos += 1;
Ok(self.code[self.code_pos - 1])
} else {
Err(RoarError::RuntimeNoOpsLeft)
}
}
fn pull_const(&mut self) -> RoarResult<RuntimeValue> {
if self.input_pos < self.input_section.len() {
self.input_pos += 1;
Ok(self.input_section[self.input_pos - 1])
} else {
Err(RoarError::RuntimeNoConstsLeft)
}
}
fn pull_var(&mut self) -> RoarResult<RuntimeValue> {
if self.buf_pos < self.input_buffer.len() {
self.buf_pos += 1;
Ok(self.input_buffer[self.buf_pos - 1])
} else {
Err(RoarError::RuntimeNoVarsLeft)
}
}
fn pull_stack(&mut self) -> RoarResult<RuntimeValue> {
if let Some(pulled) = self.stack.pop_front() {
Ok(pulled)
} else {
Err(RoarError::RuntimeStackEmpty)
}
}
fn push_stack(&mut self, value: RuntimeValue) {
self.stack.push_front(value);
}
fn step(&mut self) -> RoarResult<()> {
match self.pull_op()? {
Op::Load => {
let value = self.pull_var()?;
self.push_stack(value);
}
Op::LoadConst => {
let value = self.pull_const()?;
self.push_stack(value);
}
Op::Set => {
let id = self.pull_stack()?.as_i64()?;
let val = self.pull_stack()?;
self.memory.insert(id[0] as usize, val);
}
Op::Get => {
let id = self.pull_stack()?.as_i64()?;
if let Some(val) = self.memory.get(id[0] as usize) {
self.push_stack(*val);
} else {
return Err(RoarError::IndexNotInMemory);
}
}
Op::Swap => {
self.stack.swap(0, 1);
}
Op::Perlin => {
let seed = self.pull_stack()?.as_i64()?.unanymous_val()?;
let noise = Perlin::new(seed as u32);
let noise_map = frame_map!(self.ctx, noise);
self.push_stack(RuntimeValue::f64_new(noise_map));
}
Op::Fbm => {
let persistence = self.pull_stack()?.as_f64()?.unanymous_val()?;
let lacunarity = self.pull_stack()?.as_f64()?.unanymous_val()?;
let freq = self.pull_stack()?.as_f64()?.unanymous_val()?;
let octaves = self.pull_stack()?.as_i64()?.unanymous_val()?;
let seed = self.pull_stack()?.as_i64()?.unanymous_val()?;
let noise = Fbm::<Perlin>::new(seed as u32)
.set_persistence(persistence)
.set_lacunarity(lacunarity)
.set_frequency(freq)
.set_octaves(octaves as usize);
let noise_map = frame_map!(self.ctx, noise);
self.push_stack(RuntimeValue::f64_new(noise_map));
}
Op::RidgedMulti => {
let persistence = self.pull_stack()?.as_f64()?.unanymous_val()?;
let lacunarity = self.pull_stack()?.as_f64()?.unanymous_val()?;
let freq = self.pull_stack()?.as_f64()?.unanymous_val()?;
let octaves = self.pull_stack()?.as_i64()?.unanymous_val()?;
let seed = self.pull_stack()?.as_i64()?.unanymous_val()?;
let noise = RidgedMulti::<Perlin>::new(seed as u32)
.set_persistence(persistence)
.set_lacunarity(lacunarity)
.set_frequency(freq)
.set_octaves(octaves as usize);
let noise_map = frame_map!(self.ctx, noise);
self.push_stack(RuntimeValue::f64_new(noise_map));
}
Op::Value => {
let seed = self.pull_stack()?.as_i64()?.unanymous_val()?;
let noise = Value::new(seed as u32);
let noise_map = frame_map!(self.ctx, noise);
self.push_stack(RuntimeValue::f64_new(noise_map));
}
Op::Add => {
let rhs = self.pull_stack()?;
let out = match rhs {
RuntimeValue::F64(rhs) => RuntimeValue::F64(rhs + self.pull_stack()?.as_f64()?),
RuntimeValue::I64(rhs) => RuntimeValue::I64(rhs + self.pull_stack()?.as_i64()?),
};
self.push_stack(out);
}
Op::Sub => {
let rhs = self.pull_stack()?;
let out = match rhs {
RuntimeValue::F64(rhs) => RuntimeValue::F64(rhs - self.pull_stack()?.as_f64()?),
RuntimeValue::I64(rhs) => RuntimeValue::I64(rhs - self.pull_stack()?.as_i64()?),
};
self.push_stack(out);
}
Op::Mul => {
let rhs = self.pull_stack()?;
let out = match rhs {
RuntimeValue::F64(rhs) => RuntimeValue::F64(rhs * self.pull_stack()?.as_f64()?),
RuntimeValue::I64(rhs) => RuntimeValue::I64(rhs * self.pull_stack()?.as_i64()?),
};
self.push_stack(out);
}
Op::Div => {
let rhs = self.pull_stack()?;
let out = match rhs {
RuntimeValue::F64(rhs) => RuntimeValue::F64(rhs / self.pull_stack()?.as_f64()?),
RuntimeValue::I64(rhs) => RuntimeValue::I64(rhs / self.pull_stack()?.as_i64()?),
};
self.push_stack(out);
}
Op::Neg => {
let out = match self.pull_stack()? {
RuntimeValue::I64(runtime_i64_value) => RuntimeValue::I64(-runtime_i64_value),
RuntimeValue::F64(runtime_f64_value) => RuntimeValue::F64(-runtime_f64_value),
};
self.push_stack(out);
}
Op::Max => {
let other = self.pull_stack()?;
let out = match other {
RuntimeValue::F64(other) => {
RuntimeValue::F64(other.max(self.pull_stack()?.as_f64()?))
}
RuntimeValue::I64(other) => {
RuntimeValue::I64(other.max(self.pull_stack()?.as_i64()?))
}
};
self.push_stack(out);
}
Op::Min => {
let other = self.pull_stack()?;
let out = match other {
RuntimeValue::F64(other) => {
RuntimeValue::F64(other.min(self.pull_stack()?.as_f64()?))
}
RuntimeValue::I64(other) => {
RuntimeValue::I64(other.min(self.pull_stack()?.as_i64()?))
}
};
self.push_stack(out);
}
Op::Pwr => {
let pow = self.pull_stack()?;
let out = match self.pull_stack()? {
RuntimeValue::I64(runtime_i64_value) => {
RuntimeValue::I64(runtime_i64_value.pow(pow.as_i64()?))
}
RuntimeValue::F64(runtime_f64_value) => match pow {
RuntimeValue::I64(pow_i64) => {
RuntimeValue::F64(runtime_f64_value.powi(pow_i64))
}
RuntimeValue::F64(pow_f64) => {
RuntimeValue::F64(runtime_f64_value.powf(pow_f64))
}
},
};
self.push_stack(out);
}
Op::LeftShift => {
let rhs = self.pull_stack()?.as_i64()?;
let lhs = self.pull_stack()?.as_i64()?;
self.push_stack(RuntimeValue::I64(lhs << rhs));
}
Op::RightShift => {
let rhs = self.pull_stack()?.as_i64()?;
let lhs = self.pull_stack()?.as_i64()?;
self.push_stack(RuntimeValue::I64(lhs >> rhs));
}
Op::GetCtx1 => self.push_stack(RuntimeValue::f64_splat(self.ctx.x)),
Op::GetCtx2 => self.push_stack(RuntimeValue::f64_splat(self.ctx.y)),
Op::SetCtx4 => self.ctx.scale_x = self.pull_stack()?.as_f64()?.unanymous_val()?,
Op::SetCtx5 => self.ctx.scale_y = self.pull_stack()?.as_f64()?.unanymous_val()?,
Op::PutOnCurve => {
let end_y = self.pull_stack()?.as_f64()?;
let end_x = self.pull_stack()?.as_f64()?;
let start_y = self.pull_stack()?.as_f64()?;
let start_x = self.pull_stack()?.as_f64()?;
let mem_idx = self.pull_stack()?.as_i64()?.unanymous_val()? as usize;
let value = self.pull_stack()?.as_f64()?;
if self.memory.get(mem_idx).is_none() {
self.memory.insert(mem_idx, RuntimeValue::f64_splat(0.0));
}
let mem = &mut self.memory[mem_idx];
match mem {
RuntimeValue::I64(_) => return Err(RoarError::UnmatchedTypeUnwrap),
RuntimeValue::F64(mem) => {
for i in 0..FRAME_SIZE {
let v = value[i];
let end_y = end_y[i];
let end_x = end_x[i];
let start_y = start_y[i];
let start_x = start_x[i];
if start_x <= v && v <= end_x {
let t = (v - start_x) / (end_x - start_x);
mem[i] = (1.0 - t) * start_y + t * end_y;
}
}
},
}
self.push_stack(RuntimeValue::F64(value));
},
}
Ok(())
}
pub fn run(&mut self) -> RoarResult<RuntimeValue> {
while self.code_pos < self.code.len() {
self.step()?;
}
if let Some(out) = self.stack.pop_front() {
Ok(out)
} else {
Err(RoarError::RuntimeFinishedWithoutReturnValue)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
pub struct ExecutionContext {
pub(super) x: f64,
pub(super) y: f64,
pub(super) scale_x: f64,
pub(super) scale_y: f64,
}
#[macro_export]
macro_rules! frame_map {
($ctx:expr, $noise: expr) => {{
use super::{FRAME_SIZE, FRAME_WIDTH};
use noise::NoiseFn;
let map = noise::utils::PlaneMapBuilder::new_fn(move |[x, y]| $noise.get([x, y]))
.set_size(FRAME_WIDTH, FRAME_WIDTH)
.set_x_bounds(
$ctx.x * 16.0 * $ctx.scale_x,
($ctx.x + 1.0) * 16.0 * $ctx.scale_x,
)
.set_y_bounds(
$ctx.y * 16.0 * $ctx.scale_y,
($ctx.y + 1.0) * 16.0 * $ctx.scale_y,
)
.build();
let mut arr = [0.0; FRAME_SIZE];
let mut i = 0;
for v in map.iter() {
arr[i] = *v;
i += 1;
}
arr
}};
}