use std::{sync::atomic::{AtomicUsize, Ordering}, cell::RefCell}; use diaphragm_cairo_renderer::CairoRenderer; use diaphragm_core::{ solving::VariableHandle, text::{FontDescription as CoreFontDescription, FontStyle, FontWeight, Text as CoreText}, types::Float as CoreFloat, Runtime, }; use diaphragm_z3_solver::{z3, Z3Context}; use mlua::prelude::*; static MAX_ID: AtomicUsize = AtomicUsize::new(0); thread_local! { static SENDER: RefCell>> = RefCell::new(None); } #[derive(Clone, Copy, Debug)] struct Float(CoreFloat); impl Float { fn new() -> Float { Float(CoreFloat::Variable(VariableHandle::new( MAX_ID.fetch_add(1, Ordering::SeqCst), ))) } } impl LuaUserData for Float {} fn float(_: &Lua, _: ()) -> LuaResult { Ok(Float::new()) } #[derive(Clone, Debug)] struct FontDescription(CoreFontDescription); impl LuaUserData for FontDescription {} const DEFAULT_FONT_FAMILY: &str = "serif"; impl Default for FontDescription { fn default() -> Self { Self(CoreFontDescription { family: DEFAULT_FONT_FAMILY.to_string(), style: FontStyle::Normal, weight: FontWeight::Normal, size: Float::new().0, }) } } fn font(_: &Lua, params: LuaTable) -> LuaResult { // TODO: better validation of the table // What happens when I mistype a param? // TODO: better error handling let family = params .get::<_, Option<_>>("family")? .unwrap_or_else(|| DEFAULT_FONT_FAMILY.to_string()); let style = match params.get::<_, Option>("style")?.as_deref() { Some("normal") | None => FontStyle::Normal, Some(_) => return Err(LuaError::RuntimeError("Unknown style".to_string())), }; let weight = match params.get::<_, Option>("weight")?.as_deref() { Some("normal") | None => FontWeight::Normal, Some(_) => return Err(LuaError::RuntimeError("Unknown weight".to_string())), }; let size = params .get::<_, Option<_>>("size")? .unwrap_or_else(Float::new); Ok(FontDescription(CoreFontDescription { family, style, weight, size: size.0, })) } #[derive(Clone, Debug)] struct Text(CoreText); impl LuaUserData for Text { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_method("draw", |_, this, _params: ()| { println!("I'm drawing: {}", this.0.content); Ok(()) }) } } fn text(_: &Lua, params: LuaTable) -> LuaResult { let content = params.get("content")?; let font = params .get::<_, Option>("font")? .unwrap_or_default(); Ok(Text(CoreText { content, font: font.0, })) } fn draw(_: &Lua, params: LuaTable) -> LuaResult<()> { let content: LuaTable = params.get("content")?; let output: LuaTable = params.get("output")?; let z3_cfg = z3::Config::new(); let z3_ctx = z3::Context::new(&z3_cfg); let ctx = Z3Context::new(&z3_ctx); let cairo_renderer = CairoRenderer::new(); // TODO: we shouldn't need the renderer until the end let mut runtime = Runtime::new(Box::new(ctx), Box::new(cairo_renderer)); let _solver = runtime.solver_ctx(); dbg!(content, output); Ok(()) } #[mlua::lua_module] fn libdiaphragm(lua: &Lua) -> LuaResult { // TODO: the solver as a mutable global solves so much problem (pun not intended) let exports = lua.create_table()?; exports.set("text", lua.create_function(text)?)?; exports.set("font", lua.create_function(font)?)?; exports.set("float", lua.create_function(float)?)?; exports.set("draw", lua.create_function(draw)?)?; Ok(exports) }