From 7b0e215008c94c4a7ddb5f47b12c341d807ea062 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Fri, 20 Jan 2023 16:03:25 +0100 Subject: tree-wide: properly set figure size --- cairo-renderer/src/lib.rs | 16 ++++++++++++++-- core/src/rendering.rs | 2 ++ core/src/runtime.rs | 13 +++++++++++-- lua-bindings/src/lib.rs | 42 ++++++++++++++++++++++++++++++++++++++---- 4 files changed, 65 insertions(+), 8 deletions(-) diff --git a/cairo-renderer/src/lib.rs b/cairo-renderer/src/lib.rs index 830e6bc..2a88379 100644 --- a/cairo-renderer/src/lib.rs +++ b/cairo-renderer/src/lib.rs @@ -10,15 +10,27 @@ pub struct CairoRenderer { impl CairoRenderer { pub fn new() -> Self { - // TODO: properly - let surface = cairo::SvgSurface::for_stream(1920., 1080., std::io::stdout()).unwrap(); + // HACK: a bit ugly, but text_extents requires a context, so we have to create a "dummy" + // surface + let surface = cairo::SvgSurface::for_stream(1920., 1080., std::io::sink()).unwrap(); let ctx = cairo::Context::new(&surface).unwrap(); Self { ctx } } } +impl Default for CairoRenderer { + fn default() -> Self { + Self::new() + } +} + impl Renderer for CairoRenderer { + fn set_size(&mut self, width: f64, height: f64) { + let surface = cairo::SvgSurface::for_stream(width, height, std::io::stdout()).unwrap(); + self.ctx = cairo::Context::new(&surface).unwrap(); + } + fn move_to(&mut self, x: f64, y: f64) { self.ctx.move_to(x, y) } diff --git a/core/src/rendering.rs b/core/src/rendering.rs index 3f2303b..73daa2a 100644 --- a/core/src/rendering.rs +++ b/core/src/rendering.rs @@ -4,6 +4,8 @@ use super::text::{DefinedFontDescription, FontDescription}; use super::types::DefinedCoreShapeContext; pub trait Renderer { + // Must be called once before any drawing happens + fn set_size(&mut self, width: f64, height: f64); fn move_to(&mut self, x: f64, y: f64); fn stroke(&mut self); fn fill(&mut self); diff --git a/core/src/runtime.rs b/core/src/runtime.rs index d70e4d0..6b1b5d1 100644 --- a/core/src/runtime.rs +++ b/core/src/runtime.rs @@ -1,5 +1,6 @@ use crate::core_shapes::CoreDrawable; use crate::rendering::Render; +use crate::types::Bounds; // use super::complex_shapes::{ComplexShape, Drawable}; use super::rendering::Renderer; @@ -40,14 +41,22 @@ impl<'a> Runtime<'a> { &mut *self.renderer } - pub fn render(mut self) { + pub fn render(mut self, bounds: Bounds) { let model = self.solver_ctx.solve(); + let bounds = bounds + .fixate(&*model) + .expect("Could not fixate figure bounds"); + + self.renderer.set_size(bounds.width, bounds.height); + for drawable in &self.drawables { let defined_drawable = drawable .fixate(&*model) .expect("Could not fixate core shape"); - defined_drawable.shape.render(defined_drawable.context, &mut *self.renderer); + defined_drawable + .shape + .render(defined_drawable.context, &mut *self.renderer); } } diff --git a/lua-bindings/src/lib.rs b/lua-bindings/src/lib.rs index 35cdaf8..53622de 100644 --- a/lua-bindings/src/lib.rs +++ b/lua-bindings/src/lib.rs @@ -16,7 +16,9 @@ use diaphragm_core::{ solving::VariableHandle, styles::Pattern, text::{FontDescription as CoreFontDescription, FontStyle, FontWeight, Text as CoreText}, - types::{Bool as CoreBool, CoreShapeContext, Float as CoreFloat, Point2D as CorePoint2D}, + types::{ + Bool as CoreBool, Bounds, CoreShapeContext, Float as CoreFloat, Point2D as CorePoint2D, + }, Runtime, }; use diaphragm_z3_solver::{z3, Z3Context}; @@ -656,6 +658,7 @@ thread_local! { enum Message { Do(Box), + SetBounds(Bounds), } fn runtime_thread_do(fun: Box T + Send>) -> T { @@ -698,7 +701,7 @@ fn constrain(_: &Lua, bool: Bool) -> LuaResult<()> { Ok(()) } -fn draw(_: &Lua, params: LuaTable) -> LuaResult<()> { +fn draw(lua: &Lua, params: LuaTable) -> LuaResult<()> { // So.... The Z3 stuff isn't Send and contains lifetimes, so we can't store them in global // variables or convert them to Lua. Solution: handle everything in a specific thread, and // communicate through a channel. @@ -728,23 +731,54 @@ fn draw(_: &Lua, params: LuaTable) -> LuaResult<()> { // TODO: we shouldn't need the renderer until the end let mut runtime = Runtime::new(Box::new(ctx), Box::new(cairo_renderer)); + let mut bounds = None; for message in message_receiver { match message { Message::Do(fun) => { fun(&mut runtime); } + Message::SetBounds(new_bounds) => bounds = Some(new_bounds), } } - runtime.render(); + let bounds = bounds.unwrap(); + + // TODO: it would be nicer to do in Lua + let bounds_top = bounds.top(runtime.solver_ctx()); + let top_constraint = runtime + .solver_ctx() + .float_eq(bounds_top, CoreFloat::Fixed(0.0)); + runtime.solver_ctx().constrain(top_constraint); + + let bounds_left = bounds.left(runtime.solver_ctx()); + let left_constraint = runtime + .solver_ctx() + .float_eq(bounds_left, CoreFloat::Fixed(0.0)); + runtime.solver_ctx().constrain(left_constraint); + + runtime.render(bounds); }); let content: LuaValue = params.get("content")?; let _output: LuaTable = params.get("output")?; + let figure = complex_shape(lua, ())?; + let figure_bounds = figure.0.bounds().clone(); + + // TODO: sending bounds before callin `draw` prevents doing things like + // figure = dia.rectangle.new(...) + SENDER.with(|sender| { + sender + .borrow_mut() + .as_mut() + .expect("Not currently drawing") + .send(Message::SetBounds(figure_bounds)) + .unwrap() + }); + match content { // TODO: this doesn't stop the runtime thread? - LuaValue::Table(table) => table.call_method("draw", ()).unwrap(), + LuaValue::Table(table) => table.call_method("draw", figure).unwrap(), // TODO: switch to enum // LuaValue::UserData(user_data) => { -- cgit v1.2.3