roar 0.1.0

A toolkit for procedural world generation (with a focus on voxel games)
Documentation
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
    }};
}