use tiny_skia::Pixmap; use crate::{ render::{Renderer, RendererLimits}, system::System, value::{Ref, Reticle, Value, Vec2}, vm::{Exception, Vm}, }; #[derive(Debug, Clone, Copy)] pub struct Trampoline { pub value: Value, } // NOTE: This must be kept in sync with haku.js. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub enum Cont { Scribble, Dotter, } impl Trampoline { pub fn new(init: Value) -> Self { Self { value: init } } pub fn cont(&self, vm: &Vm) -> Cont { let Some((_, refv)) = vm.get_ref_value(self.value) else { return Cont::Scribble; }; match refv { Ref::Reticle(_) => Cont::Dotter, _ => Cont::Scribble, } } pub fn scribble( &mut self, vm: &Vm, pixmap: &mut Pixmap, translation: Vec2, limits: &RendererLimits, ) -> Result<(), Exception> { let mut renderer = Renderer::new(&mut *pixmap, limits); renderer.translate(translation.x, translation.y); renderer.render(vm, self.value) } pub fn dotter( &mut self, vm: &mut Vm, system: &System, from: Vec2, to: Vec2, num: f32, ) -> Result<(), Exception> { let (_, vref) = vm.get_ref_value(self.value).expect("value must be a ref"); let &Ref::Reticle(Reticle::Dotter { draw: Value::Ref(draw_id), }) = vref else { panic!("value must be a dotter reticle"); }; let dotter = vm.create_ref(Ref::Closure(system.create_dotter( from.into(), to.into(), num, )))?; let value = vm.run(system, draw_id, &[Value::Ref(dotter)])?; self.value = value; // Prevent event handling weirdness on the frontend by disallowing continuing dotters // with anything but scribbles. if self.cont(vm) != Cont::Scribble { return Err(vm.create_exception("a dotter must be continued with a scribble")); } Ok(()) } }