summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMinijackson <minijackson@riseup.net>2022-12-29 01:40:08 +0100
committerMinijackson <minijackson@riseup.net>2022-12-29 01:40:08 +0100
commit9c15b76c5a6355902b2a105a7c6ee93f6b5016dc (patch)
tree44cacfe756169f7b3d3cc23f875864683ba2af74
parent30f7d39ca2ed4590b5d356b1a4c024d11156a383 (diff)
downloaddiaphragm-9c15b76c5a6355902b2a105a7c6ee93f6b5016dc.tar.gz
diaphragm-9c15b76c5a6355902b2a105a7c6ee93f6b5016dc.zip
WIP v2: text works, primitives works in Lua
-rw-r--r--cairo-renderer/src/lib.rs45
-rw-r--r--core/src/core_shapes.rs73
-rw-r--r--core/src/lib.rs8
-rw-r--r--core/src/rendering.rs53
-rw-r--r--core/src/runtime.rs154
-rw-r--r--core/src/solving.rs55
-rw-r--r--core/src/types.rs110
-rw-r--r--lua-bindings/src/lib.rs482
-rw-r--r--z3-solver/src/lib.rs21
9 files changed, 761 insertions, 240 deletions
diff --git a/cairo-renderer/src/lib.rs b/cairo-renderer/src/lib.rs
index d912afc..df6f8fd 100644
--- a/cairo-renderer/src/lib.rs
+++ b/cairo-renderer/src/lib.rs
@@ -1,5 +1,5 @@
1use diaphragm_core::{ 1use diaphragm_core::{
2 styles::{Pattern, DefinedDashStyle}, 2 styles::{DefinedDashStyle, Pattern},
3 text::{DefinedFontDescription, FontDescription}, 3 text::{DefinedFontDescription, FontDescription},
4 Renderer, 4 Renderer,
5}; 5};
@@ -67,28 +67,29 @@ impl Renderer for CairoRenderer {
67 } 67 }
68 68
69 fn text_extents(&self, text: &str, font: &FontDescription) -> (f64, f64) { 69 fn text_extents(&self, text: &str, font: &FontDescription) -> (f64, f64) {
70 // Pango gives us integer back, so we lose some precision. We use this for that.
71 const TEST_ABSOLUTE_SIZE: f64 = 1_000_000.;
72
70 let layout = pangocairo::create_layout(&self.ctx); 73 let layout = pangocairo::create_layout(&self.ctx);
71 let font_desc = pango::FontDescription::from_string(&font.family); 74 let mut font_desc = pango::FontDescription::from_string(&font.family);
75 font_desc.set_absolute_size(TEST_ABSOLUTE_SIZE);
72 layout.set_font_description(Some(&font_desc)); 76 layout.set_font_description(Some(&font_desc));
73 layout.set_markup(text); 77 layout.set_markup(text);
74 78
75 //let (extents, _) = layout.get_pixel_extents();
76 //(extents.width as f64, extents.height as f64)
77
78 // TODO: get height from the baseline 79 // TODO: get height from the baseline
79 let mut layout_iter = layout.iter(); 80 // let mut layout_iter = layout.iter();
80 let _height = loop { 81 // let _height = loop {
81 if layout_iter.at_last_line() { 82 // if layout_iter.at_last_line() {
82 break layout_iter.baseline(); 83 // break layout_iter.baseline();
83 } 84 // }
84 85 // layout_iter.next_line();
85 layout_iter.next_line(); 86 // };
86 }; 87
87 88 let (_, extents) = dbg!(layout.pixel_extents());
88 // TODO: Probably should use the logical extents, but it has weird width 89 (
89 let (_, extents) = layout.pixel_extents(); 90 extents.width() as f64 / TEST_ABSOLUTE_SIZE,
90 //let (extents, _) = layout.get_pixel_extents(); 91 extents.height() as f64 / TEST_ABSOLUTE_SIZE,
91 (extents.width() as f64, extents.height() as f64) 92 )
92 93
93 //let (width, height) = layout.get_pixel_size(); 94 //let (width, height) = layout.get_pixel_size();
94 //(width as f64, height as f64) 95 //(width as f64, height as f64)
@@ -98,14 +99,14 @@ impl Renderer for CairoRenderer {
98 let layout = pangocairo::create_layout(&self.ctx); 99 let layout = pangocairo::create_layout(&self.ctx);
99 let mut font_desc = pango::FontDescription::from_string(&font.family); 100 let mut font_desc = pango::FontDescription::from_string(&font.family);
100 101
101 // TODO: I have no fucking idea why 102 font_desc.set_absolute_size(dbg!(font.size) as _);
102 font_desc.set_size((font.size * 600.) as _);
103 //font_desc.set_size((font.size * 700.) as _);
104 //font_desc.set_absolute_size(font.size * 800.);
105 layout.set_font_description(Some(&font_desc)); 103 layout.set_font_description(Some(&font_desc));
106 layout.set_markup(text); 104 layout.set_markup(text);
107 105
108 //self.ctx.set_font_size(dbg!(font.size)); 106 //self.ctx.set_font_size(dbg!(font.size));
109 pangocairo::show_layout(&self.ctx, &layout); 107 pangocairo::show_layout(&self.ctx, &layout);
108
109 dbg!(pangocairo::context_get_resolution(&layout.context()));
110 dbg!(layout.pixel_extents());
110 } 111 }
111} 112}
diff --git a/core/src/core_shapes.rs b/core/src/core_shapes.rs
index 805d82e..d8017ab 100644
--- a/core/src/core_shapes.rs
+++ b/core/src/core_shapes.rs
@@ -1,8 +1,44 @@
1use super::complex_shapes::{ComplexShape, DrawResult}; 1pub struct CoreDrawable {
2use super::rendering::{Render, Renderer}; 2 pub(crate) shape: CoreShape,
3use super::solving::{Constrainable, SolverContext, SolverModel}; 3 pub(crate) context: CoreShapeContext,
4use super::types::*; 4}
5
6impl CoreDrawable {
7 pub fn new(shape: CoreShape, context: CoreShapeContext) -> Self {
8 Self { shape, context }
9 }
10}
11
12pub enum CoreShape {
13 Rectangle(Rectangle),
14 Text(Text),
15 // StraightPath(StraightPath),
16}
5 17
18impl From<Rectangle> for CoreShape {
19 fn from(rectangle: Rectangle) -> Self {
20 CoreShape::Rectangle(rectangle)
21 }
22}
23
24impl From<Text> for CoreShape {
25 fn from(text: Text) -> Self {
26 CoreShape::Text(text)
27 }
28}
29
30pub struct DefinedCoreDrawable {
31 pub(crate) shape: DefinedCoreShape,
32 pub(crate) context: DefinedCoreShapeContext,
33}
34
35pub enum DefinedCoreShape {
36 Rectangle(Rectangle),
37 Text(DefinedText),
38 // StraightPath(StraightPath),
39}
40
41/*
6pub trait CoreShape { 42pub trait CoreShape {
7 fn constrain( 43 fn constrain(
8 &self, 44 &self,
@@ -23,19 +59,25 @@ impl<T: CoreShape + Clone + 'static> ComplexShape for T {
23 panic!("Tried to decompose core shape") 59 panic!("Tried to decompose core shape")
24 } 60 }
25} 61}
62*/
26 63
27// TODO: add default 64// TODO: add default
28#[derive(Copy, Clone, Debug, Default)] 65#[derive(Copy, Clone, Debug, Default)]
29pub struct Rectangle {} 66pub struct Rectangle {}
30 67
68/*
31impl CoreShape for Rectangle { 69impl CoreShape for Rectangle {
32 fn to_render(&self, _model: &dyn SolverModel) -> Option<Box<dyn Render>> { 70 fn to_render(&self, _model: &dyn SolverModel) -> Option<Box<dyn Render>> {
33 Some(Box::new(*self)) 71 Some(Box::new(*self))
34 } 72 }
35} 73}
74*/
75
76use crate::types::{CoreShapeContext, DefinedCoreShapeContext};
36 77
37pub use super::text::{DefinedText, Text}; 78pub use super::text::{DefinedText, Text};
38 79
80/*
39impl CoreShape for Text { 81impl CoreShape for Text {
40 fn constrain( 82 fn constrain(
41 &self, 83 &self,
@@ -62,18 +104,20 @@ impl CoreShape for Text {
62 .map(|path| -> Box<dyn Render> { Box::new(path) }) 104 .map(|path| -> Box<dyn Render> { Box::new(path) })
63 } 105 }
64} 106}
107*/
65 108
66#[derive(Clone, Debug, Default)] 109// #[derive(Clone, Debug, Default)]
67pub struct StraightPath { 110// pub struct StraightPath {
68 pub(crate) points: Vec<Point2D>, 111// pub(crate) points: Vec<Point2D>,
69} 112// }
70 113//
71impl StraightPath { 114// impl StraightPath {
72 pub fn new(points: Vec<Point2D>) -> Self { 115// pub fn new(points: Vec<Point2D>) -> Self {
73 Self { points } 116// Self { points }
74 } 117// }
75} 118// }
76 119
120/*
77pub struct DefinedStraightPath { 121pub struct DefinedStraightPath {
78 pub(crate) points: Vec<DefinedPoint2D>, 122 pub(crate) points: Vec<DefinedPoint2D>,
79} 123}
@@ -84,3 +128,4 @@ impl CoreShape for StraightPath {
84 .map(|path| -> Box<dyn Render> { Box::new(path) }) 128 .map(|path| -> Box<dyn Render> { Box::new(path) })
85 } 129 }
86} 130}
131*/
diff --git a/core/src/lib.rs b/core/src/lib.rs
index 57e45df..f949cd9 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -2,7 +2,7 @@
2#![deny(unsafe_code)] 2#![deny(unsafe_code)]
3 3
4pub mod colors; 4pub mod colors;
5mod complex_shapes; 5// mod complex_shapes;
6pub mod core_shapes; 6pub mod core_shapes;
7mod rendering; 7mod rendering;
8mod runtime; 8mod runtime;
@@ -11,9 +11,9 @@ pub mod styles;
11pub mod text; 11pub mod text;
12pub mod types; 12pub mod types;
13 13
14pub use complex_shapes::{ 14// pub use complex_shapes::{
15 ComplexShape, DrawResult, Drawable, DrawableBuilder, DynClone, DynDrawable, 15// ComplexShape, DrawResult, Drawable, DrawableBuilder, DynClone, DynDrawable,
16}; 16// };
17pub use rendering::Renderer; 17pub use rendering::Renderer;
18pub use runtime::Runtime; 18pub use runtime::Runtime;
19pub use solving::{SolverContext, SolverModel}; 19pub use solving::{SolverContext, SolverModel};
diff --git a/core/src/rendering.rs b/core/src/rendering.rs
index f7a0189..9c9f9e4 100644
--- a/core/src/rendering.rs
+++ b/core/src/rendering.rs
@@ -1,7 +1,7 @@
1use super::core_shapes::*; 1use super::core_shapes::*;
2use super::styles::{DefinedDashStyle, DefinedStrokeStyle, FillStyle, Pattern}; 2use super::styles::{DefinedDashStyle, DefinedStrokeStyle, FillStyle, Pattern};
3use super::text::{DefinedFontDescription, FontDescription}; 3use super::text::{DefinedFontDescription, FontDescription};
4use super::types::DefinedShapeContext; 4use super::types::DefinedCoreShapeContext;
5 5
6pub trait Renderer { 6pub trait Renderer {
7 fn move_to(&mut self, x: f64, y: f64); 7 fn move_to(&mut self, x: f64, y: f64);
@@ -20,7 +20,16 @@ pub trait Renderer {
20} 20}
21 21
22pub trait Render { 22pub trait Render {
23 fn render(&self, context: DefinedShapeContext, renderer: &mut dyn Renderer); 23 fn render(&self, context: DefinedCoreShapeContext, renderer: &mut dyn Renderer);
24}
25
26impl Render for DefinedCoreShape {
27 fn render(&self, context: DefinedCoreShapeContext, renderer: &mut dyn Renderer) {
28 match self {
29 Self::Rectangle(r) => r.render(context, renderer),
30 Self::Text(t) => t.render(context, renderer),
31 }
32 }
24} 33}
25 34
26fn draw(fill: &FillStyle, stroke: &DefinedStrokeStyle, renderer: &mut dyn Renderer) { 35fn draw(fill: &FillStyle, stroke: &DefinedStrokeStyle, renderer: &mut dyn Renderer) {
@@ -47,7 +56,7 @@ fn draw(fill: &FillStyle, stroke: &DefinedStrokeStyle, renderer: &mut dyn Render
47} 56}
48 57
49impl Render for Rectangle { 58impl Render for Rectangle {
50 fn render(&self, context: DefinedShapeContext, renderer: &mut dyn Renderer) { 59 fn render(&self, context: DefinedCoreShapeContext, renderer: &mut dyn Renderer) {
51 let bounds = &context.bounds; 60 let bounds = &context.bounds;
52 renderer.rectangle(bounds.left, bounds.top, bounds.width, bounds.height); 61 renderer.rectangle(bounds.left, bounds.top, bounds.width, bounds.height);
53 draw(&context.fill, &context.stroke, renderer); 62 draw(&context.fill, &context.stroke, renderer);
@@ -55,7 +64,7 @@ impl Render for Rectangle {
55} 64}
56 65
57impl Render for DefinedText { 66impl Render for DefinedText {
58 fn render(&self, context: DefinedShapeContext, renderer: &mut dyn Renderer) { 67 fn render(&self, context: DefinedCoreShapeContext, renderer: &mut dyn Renderer) {
59 // TODO: select font, style, text shaping (renderer specific), etc. 68 // TODO: select font, style, text shaping (renderer specific), etc.
60 let bounds = &context.bounds; 69 let bounds = &context.bounds;
61 //renderer.move_to(bounds.left, bounds.top + self.font.size); 70 //renderer.move_to(bounds.left, bounds.top + self.font.size);
@@ -66,21 +75,21 @@ impl Render for DefinedText {
66 } 75 }
67} 76}
68 77
69impl Render for DefinedStraightPath { 78// impl Render for DefinedStraightPath {
70 fn render(&self, context: DefinedShapeContext, renderer: &mut dyn Renderer) { 79// fn render(&self, context: DefinedShapeContext, renderer: &mut dyn Renderer) {
71 let mut iter = self.points.iter(); 80// let mut iter = self.points.iter();
72 81//
73 let first_point = match iter.next() { 82// let first_point = match iter.next() {
74 Some(point) => point, 83// Some(point) => point,
75 None => return, 84// None => return,
76 }; 85// };
77 86//
78 renderer.move_to(first_point.x, first_point.y); 87// renderer.move_to(first_point.x, first_point.y);
79 88//
80 for point in iter { 89// for point in iter {
81 renderer.line_to(point.x, point.y); 90// renderer.line_to(point.x, point.y);
82 } 91// }
83 92//
84 draw(&context.fill, &context.stroke, renderer); 93// draw(&context.fill, &context.stroke, renderer);
85 } 94// }
86} 95// }
diff --git a/core/src/runtime.rs b/core/src/runtime.rs
index bfb31fa..d70e4d0 100644
--- a/core/src/runtime.rs
+++ b/core/src/runtime.rs
@@ -1,13 +1,17 @@
1use super::complex_shapes::{ComplexShape, Drawable, DynDrawable}; 1use crate::core_shapes::CoreDrawable;
2use crate::rendering::Render;
3
4// use super::complex_shapes::{ComplexShape, Drawable};
2use super::rendering::Renderer; 5use super::rendering::Renderer;
3use super::solving::{Constrainable, SolverContext}; 6use super::solving::{Constrainable, SolverContext};
4 7
5const RECURSION_LIMIT: u64 = 10_000; 8// const RECURSION_LIMIT: u64 = 10_000;
6 9
7pub struct Runtime<'a> { 10pub struct Runtime<'a> {
8 solver_ctx: Box<dyn SolverContext + 'a>, 11 solver_ctx: Box<dyn SolverContext + 'a>,
9 renderer: Box<dyn Renderer>, 12 renderer: Box<dyn Renderer>,
10 drawables: Vec<DynDrawable>, 13 // drawables: Vec<DynDrawable>,
14 drawables: Vec<CoreDrawable>,
11} 15}
12 16
13impl<'a> Runtime<'a> { 17impl<'a> Runtime<'a> {
@@ -15,81 +19,101 @@ impl<'a> Runtime<'a> {
15 Self { 19 Self {
16 solver_ctx, 20 solver_ctx,
17 renderer, 21 renderer,
22 // drawables: Vec::new(),
18 drawables: Vec::new(), 23 drawables: Vec::new(),
19 } 24 }
20 } 25 }
21 26
22 pub fn add_drawable<T: ComplexShape + 'static>(&mut self, drawable: Drawable<T>) { 27 // pub fn add_drawable<T: ComplexShape + 'static>(&mut self, drawable: Drawable<T>) {
23 self.drawables.push(drawable.into()) 28 // self.drawables.push(drawable.into())
29 // }
30
31 pub fn add_drawable(&mut self, drawable: CoreDrawable) {
32 self.drawables.push(drawable)
24 } 33 }
25 34
26 pub fn solver_ctx(&mut self) -> &mut (dyn SolverContext + 'a) { 35 pub fn solver_ctx(&mut self) -> &mut (dyn SolverContext + 'a) {
27 &mut *self.solver_ctx 36 &mut *self.solver_ctx
28 } 37 }
29 38
30 // TODO: preserve ordering of shapes 39 pub fn renderer(&mut self) -> &mut dyn Renderer {
31 pub fn render(mut self) { 40 &mut *self.renderer
32 let mut drawables = self.drawables; 41 }
33 let mut waited_on_variables = Vec::new();
34 let mut core_shapes = Vec::new();
35
36 /*
37 for drawable in &self.shapes {
38 let bounds = &drawable.bounds;
39 let shape = &drawable.shape;
40
41 if let Some(core_shape) = shape.to_render() {
42 drawables.push((*drawable).clone());
43 continue;
44 }
45
46 let mut result = shape.draw(bounds);
47 drawables.append(&mut result.subshapes);
48 waited_on_variables.append(&mut result.waiting_on);
49 }
50 */
51
52 let mut recursion_count = 0;
53
54 while !drawables.is_empty() {
55 recursion_count += 1;
56
57 if recursion_count > RECURSION_LIMIT {
58 panic!("Recursion limit reached");
59 }
60
61 let mut tmp_drawables = Vec::new();
62
63 for drawable in drawables.drain(..) {
64 let shape_ctx = &drawable.context;
65 let shape = &drawable.shape;
66
67 if let Some(core_shape) = shape.as_core_shape() {
68 core_shape.constrain(shape_ctx, &mut *self.solver_ctx, &*self.renderer);
69 core_shapes.push((shape.dyn_clone(), shape_ctx.clone())); // Better to Arc? Cow?
70 continue;
71 }
72
73 let mut result = shape.draw(shape_ctx, &mut *self.solver_ctx);
74 tmp_drawables.append(&mut result.subshapes);
75 waited_on_variables.append(&mut result.waiting_on);
76 }
77
78 drawables = tmp_drawables;
79 }
80 42
43 pub fn render(mut self) {
81 let model = self.solver_ctx.solve(); 44 let model = self.solver_ctx.solve();
82 45
83 // Delay rendering core shapes until later to have all the constraints 46 for drawable in &self.drawables {
84 for (core_shape, shape_ctx) in core_shapes { 47 let defined_drawable = drawable
85 let core_shape = core_shape.as_core_shape().unwrap(); 48 .fixate(&*model)
86 49 .expect("Could not fixate core shape");
87 match (core_shape.to_render(&*model), shape_ctx.fixate(&*model)) { 50 defined_drawable.shape.render(defined_drawable.context, &mut *self.renderer);
88 (Some(defined_shape), Some(shape_ctx)) => {
89 defined_shape.render(shape_ctx, &mut *self.renderer)
90 }
91 _ => panic!("Failed to fixate core shape"),
92 }
93 } 51 }
94 } 52 }
53
54 // TODO: preserve ordering of shapes
55 // pub fn render(mut self) {
56 // let mut drawables = self.drawables;
57 // let mut waited_on_variables = Vec::new();
58 // let mut core_shapes = Vec::new();
59 //
60 // /*
61 // for drawable in &self.shapes {
62 // let bounds = &drawable.bounds;
63 // let shape = &drawable.shape;
64 //
65 // if let Some(core_shape) = shape.to_render() {
66 // drawables.push((*drawable).clone());
67 // continue;
68 // }
69 //
70 // let mut result = shape.draw(bounds);
71 // drawables.append(&mut result.subshapes);
72 // waited_on_variables.append(&mut result.waiting_on);
73 // }
74 // */
75 //
76 // let mut recursion_count = 0;
77 //
78 // while !drawables.is_empty() {
79 // recursion_count += 1;
80 //
81 // if recursion_count > RECURSION_LIMIT {
82 // panic!("Recursion limit reached");
83 // }
84 //
85 // let mut tmp_drawables = Vec::new();
86 //
87 // for drawable in drawables.drain(..) {
88 // let shape_ctx = &drawable.context;
89 // let shape = &drawable.shape;
90 //
91 // if let Some(core_shape) = shape.as_core_shape() {
92 // core_shape.constrain(shape_ctx, &mut *self.solver_ctx, &*self.renderer);
93 // core_shapes.push((shape.dyn_clone(), shape_ctx.clone())); // Better to Arc? Cow?
94 // continue;
95 // }
96 //
97 // let mut result = shape.draw(shape_ctx, &mut *self.solver_ctx);
98 // tmp_drawables.append(&mut result.subshapes);
99 // waited_on_variables.append(&mut result.waiting_on);
100 // }
101 //
102 // drawables = tmp_drawables;
103 // }
104 //
105 // let model = self.solver_ctx.solve();
106 //
107 // // Delay rendering core shapes until later to have all the constraints
108 // for (core_shape, shape_ctx) in core_shapes {
109 // let core_shape = core_shape.as_core_shape().unwrap();
110 //
111 // match (core_shape.to_render(&*model), shape_ctx.fixate(&*model)) {
112 // (Some(defined_shape), Some(shape_ctx)) => {
113 // defined_shape.render(shape_ctx, &mut *self.renderer)
114 // }
115 // _ => panic!("Failed to fixate core shape"),
116 // }
117 // }
118 // }
95} 119}
diff --git a/core/src/solving.rs b/core/src/solving.rs
index 4760611..261b4d4 100644
--- a/core/src/solving.rs
+++ b/core/src/solving.rs
@@ -1,4 +1,5 @@
1use super::core_shapes::*; 1use crate::core_shapes::{CoreDrawable, CoreShape, DefinedCoreDrawable, DefinedCoreShape};
2
2use super::styles::*; 3use super::styles::*;
3use super::text::*; 4use super::text::*;
4use super::types::{Bool, Float}; 5use super::types::{Bool, Float};
@@ -147,6 +148,17 @@ pub trait Constrainable {
147 fn fixate(&self, model: &dyn SolverModel) -> Option<Self::Fixated>; 148 fn fixate(&self, model: &dyn SolverModel) -> Option<Self::Fixated>;
148} 149}
149 150
151impl Constrainable for CoreShape {
152 type Fixated = DefinedCoreShape;
153
154 fn fixate(&self, model: &dyn SolverModel) -> Option<Self::Fixated> {
155 match self {
156 CoreShape::Rectangle(r) => Some(DefinedCoreShape::Rectangle(*r)),
157 CoreShape::Text(t) => t.fixate(model).map(DefinedCoreShape::Text),
158 }
159 }
160}
161
150impl Constrainable for Float { 162impl Constrainable for Float {
151 type Fixated = f64; 163 type Fixated = f64;
152 164
@@ -218,11 +230,22 @@ impl Constrainable for DashStyle {
218 } 230 }
219} 231}
220 232
221impl Constrainable for ShapeContext { 233impl Constrainable for CoreDrawable {
222 type Fixated = DefinedShapeContext; 234 type Fixated = DefinedCoreDrawable;
235
236 fn fixate(&self, model: &dyn SolverModel) -> Option<Self::Fixated> {
237 Some(DefinedCoreDrawable {
238 shape: self.shape.fixate(model)?,
239 context: self.context.fixate(model)?,
240 })
241 }
242}
243
244impl Constrainable for CoreShapeContext {
245 type Fixated = DefinedCoreShapeContext;
223 246
224 fn fixate(&self, model: &dyn SolverModel) -> Option<Self::Fixated> { 247 fn fixate(&self, model: &dyn SolverModel) -> Option<Self::Fixated> {
225 Some(DefinedShapeContext { 248 Some(DefinedCoreShapeContext {
226 bounds: self.bounds.fixate(model)?, 249 bounds: self.bounds.fixate(model)?,
227 fill: self.fill.clone(), 250 fill: self.fill.clone(),
228 stroke: self.stroke.fixate(model)?, 251 stroke: self.stroke.fixate(model)?,
@@ -265,15 +288,15 @@ impl Constrainable for Text {
265 } 288 }
266} 289}
267 290
268impl Constrainable for StraightPath { 291// impl Constrainable for StraightPath {
269 type Fixated = DefinedStraightPath; 292// type Fixated = DefinedStraightPath;
270 293//
271 fn fixate(&self, model: &dyn SolverModel) -> Option<Self::Fixated> { 294// fn fixate(&self, model: &dyn SolverModel) -> Option<Self::Fixated> {
272 let points: Option<_> = self 295// let points: Option<_> = self
273 .points 296// .points
274 .iter() 297// .iter()
275 .map(|point| point.fixate(model)) 298// .map(|point| point.fixate(model))
276 .collect(); 299// .collect();
277 Some(DefinedStraightPath { points: points? }) 300// Some(DefinedStraightPath { points: points? })
278 } 301// }
279} 302// }
diff --git a/core/src/types.rs b/core/src/types.rs
index 1b7623a..96e059a 100644
--- a/core/src/types.rs
+++ b/core/src/types.rs
@@ -99,19 +99,19 @@ impl Bounds {
99 BoundsBuilder::default() 99 BoundsBuilder::default()
100 } 100 }
101 101
102 pub fn top(&self, _ctx: &mut dyn SolverContext) -> Float { 102 pub fn top(&self, _solver: &mut dyn SolverContext) -> Float {
103 self.top 103 self.top
104 } 104 }
105 105
106 pub fn left(&self, _ctx: &mut dyn SolverContext) -> Float { 106 pub fn left(&self, _solver: &mut dyn SolverContext) -> Float {
107 self.left 107 self.left
108 } 108 }
109 109
110 pub fn width(&self, _ctx: &mut dyn SolverContext) -> Float { 110 pub fn width(&self, _solver: &mut dyn SolverContext) -> Float {
111 self.width 111 self.width
112 } 112 }
113 113
114 pub fn height(&self, _ctx: &mut dyn SolverContext) -> Float { 114 pub fn height(&self, _solver: &mut dyn SolverContext) -> Float {
115 self.height 115 self.height
116 } 116 }
117 117
@@ -133,7 +133,7 @@ impl Bounds {
133 solver.float_add(&[self.left, half_width]) 133 solver.float_add(&[self.left, half_width])
134 } 134 }
135 135
136 pub fn top_left(&self, _ctx: &mut dyn SolverContext) -> Point2D { 136 pub fn top_left(&self, _solver: &mut dyn SolverContext) -> Point2D {
137 Point2D::new(self.left, self.top) 137 Point2D::new(self.left, self.top)
138 } 138 }
139 139
@@ -206,11 +206,11 @@ impl BoundsBuilder {
206 } 206 }
207} 207}
208 208
209#[derive(Clone, Debug)] 209// #[derive(Clone, Debug)]
210pub struct Bounded<Shape> { 210// pub struct Bounded<Shape> {
211 pub bounds: Bounds, 211// pub bounds: Bounds,
212 pub shape: Shape, 212// pub shape: Shape,
213} 213// }
214 214
215#[derive(Clone, PartialEq, PartialOrd, Debug)] 215#[derive(Clone, PartialEq, PartialOrd, Debug)]
216pub struct DefinedBounds { 216pub struct DefinedBounds {
@@ -220,20 +220,20 @@ pub struct DefinedBounds {
220 pub height: f64, 220 pub height: f64,
221} 221}
222 222
223#[derive(Clone, PartialEq, PartialOrd, Debug)] 223// #[derive(Clone, PartialEq, PartialOrd, Debug)]
224pub struct DefinitelyBounded<Shape> { 224// pub struct DefinitelyBounded<Shape> {
225 pub bounds: DefinedBounds, 225// pub bounds: DefinedBounds,
226 pub shape: Shape, 226// pub shape: Shape,
227} 227// }
228 228
229#[derive(Clone, Debug)] 229#[derive(Clone, Debug)]
230pub struct ShapeContext { 230pub struct CoreShapeContext {
231 pub(crate) bounds: Bounds, 231 pub(crate) bounds: Bounds,
232 pub(crate) fill: FillStyle, 232 pub(crate) fill: FillStyle,
233 pub(crate) stroke: StrokeStyle, 233 pub(crate) stroke: StrokeStyle,
234} 234}
235 235
236impl ShapeContext { 236impl CoreShapeContext {
237 pub fn new(solver: &mut dyn SolverContext) -> Self { 237 pub fn new(solver: &mut dyn SolverContext) -> Self {
238 Self { 238 Self {
239 bounds: Bounds { 239 bounds: Bounds {
@@ -247,9 +247,9 @@ impl ShapeContext {
247 } 247 }
248 } 248 }
249 249
250 pub(crate) fn builder() -> ShapeContextBuilder { 250 // pub(crate) fn builder() -> ShapeContextBuilder {
251 ShapeContextBuilder::default() 251 // ShapeContextBuilder::default()
252 } 252 // }
253 253
254 pub fn bounds(&self) -> &Bounds { 254 pub fn bounds(&self) -> &Bounds {
255 &self.bounds 255 &self.bounds
@@ -276,41 +276,41 @@ impl ShapeContext {
276 } 276 }
277} 277}
278 278
279#[derive(Clone, Default)] 279// #[derive(Clone, Default)]
280pub(crate) struct ShapeContextBuilder { 280// pub(crate) struct ShapeContextBuilder {
281 bounds: Option<Bounds>, 281// bounds: Option<Bounds>,
282 fill: Option<FillStyle>, 282// fill: Option<FillStyle>,
283 stroke: Option<StrokeStyle>, 283// stroke: Option<StrokeStyle>,
284} 284// }
285 285//
286impl ShapeContextBuilder { 286// impl ShapeContextBuilder {
287 pub fn bounds(&mut self, bounds: Bounds) -> &mut Self { 287// pub fn bounds(&mut self, bounds: Bounds) -> &mut Self {
288 self.bounds = Some(bounds); 288// self.bounds = Some(bounds);
289 self 289// self
290 } 290// }
291 291//
292 pub fn fill(&mut self, fill: FillStyle) -> &mut Self { 292// pub fn fill(&mut self, fill: FillStyle) -> &mut Self {
293 self.fill = Some(fill); 293// self.fill = Some(fill);
294 self 294// self
295 } 295// }
296 296//
297 pub fn stroke(&mut self, stroke: StrokeStyle) -> &mut Self { 297// pub fn stroke(&mut self, stroke: StrokeStyle) -> &mut Self {
298 self.stroke = Some(stroke); 298// self.stroke = Some(stroke);
299 self 299// self
300 } 300// }
301 301//
302 pub fn build(&self, solver: &mut dyn SolverContext) -> ShapeContext { 302// pub fn build(&self, solver: &mut dyn SolverContext) -> ShapeContext {
303 ShapeContext { 303// ShapeContext {
304 bounds: self.bounds.clone().unwrap_or_else(|| Bounds::builder().build(solver)), 304// bounds: self.bounds.clone().unwrap_or_else(|| Bounds::builder().build(solver)),
305 fill: self.fill.clone().unwrap_or_default(), 305// fill: self.fill.clone().unwrap_or_default(),
306 stroke: self.stroke.clone().unwrap_or_default(), 306// stroke: self.stroke.clone().unwrap_or_default(),
307 } 307// }
308 } 308// }
309} 309// }
310 310
311#[derive(Clone, Debug)] 311#[derive(Clone, Debug)]
312pub struct DefinedShapeContext { 312pub struct DefinedCoreShapeContext {
313 pub bounds: DefinedBounds, 313 pub(crate) bounds: DefinedBounds,
314 pub fill: FillStyle, 314 pub(crate) fill: FillStyle,
315 pub stroke: DefinedStrokeStyle, 315 pub(crate) stroke: DefinedStrokeStyle,
316} 316}
diff --git a/lua-bindings/src/lib.rs b/lua-bindings/src/lib.rs
index fcf307b..daf4b48 100644
--- a/lua-bindings/src/lib.rs
+++ b/lua-bindings/src/lib.rs
@@ -1,40 +1,149 @@
1use std::{sync::atomic::{AtomicUsize, Ordering}, cell::RefCell}; 1use std::{
2 any::Any,
3 cell::RefCell,
4 sync::mpsc::{sync_channel, Receiver, SyncSender},
5 thread,
6};
2 7
3use diaphragm_cairo_renderer::CairoRenderer; 8use diaphragm_cairo_renderer::CairoRenderer;
4use diaphragm_core::{ 9use diaphragm_core::{
10 core_shapes::{CoreDrawable, CoreShape, Rectangle as CoreRectangle},
5 solving::VariableHandle, 11 solving::VariableHandle,
6 text::{FontDescription as CoreFontDescription, FontStyle, FontWeight, Text as CoreText}, 12 text::{FontDescription as CoreFontDescription, FontStyle, FontWeight, Text as CoreText},
7 types::Float as CoreFloat, 13 types::{Bool as CoreBool, CoreShapeContext, Float as CoreFloat},
8 Runtime, 14 Runtime,
9}; 15};
10use diaphragm_z3_solver::{z3, Z3Context}; 16use diaphragm_z3_solver::{z3, Z3Context};
11use mlua::prelude::*; 17use mlua::prelude::*;
12 18
13static MAX_ID: AtomicUsize = AtomicUsize::new(0);
14
15thread_local! {
16 static SENDER: RefCell<Option<Runtime<'static>>> = RefCell::new(None);
17}
18
19#[derive(Clone, Copy, Debug)] 19#[derive(Clone, Copy, Debug)]
20struct Float(CoreFloat); 20struct Float(CoreFloat);
21 21
22impl Float { 22impl Float {
23 fn new() -> Float { 23 fn new() -> Float {
24 Float(CoreFloat::Variable(VariableHandle::new( 24 Float(runtime_thread_do(Box::new(|r| {
25 MAX_ID.fetch_add(1, Ordering::SeqCst), 25 r.solver_ctx().new_free_float()
26 ))) 26 })))
27 }
28}
29impl LuaUserData for Float {
30 fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
31 macro_rules! float_method {
32 ($name: expr, $op: ident) => {
33 methods.add_method($name, |_lua, lhs: &Float, rhs: LuaValue| {
34 let lhs = *lhs;
35 let rhs = Float::try_from(rhs).unwrap();
36 Ok(Bool(runtime_thread_do(Box::new(move |r| {
37 r.solver_ctx().$op(lhs.0, rhs.0)
38 }))))
39 });
40 };
41 }
42
43 float_method!("eq", float_eq);
44 float_method!("ne", float_ne);
45 float_method!("gt", float_gt);
46 float_method!("ge", float_ge);
47 float_method!("lt", float_lt);
48 float_method!("le", float_le);
49 }
50}
51
52impl TryFrom<LuaValue<'_>> for Float {
53 type Error = LuaError;
54
55 fn try_from(value: LuaValue) -> Result<Self, Self::Error> {
56 match value {
57 LuaValue::Integer(i) => Ok(Float(CoreFloat::Fixed(i as _))),
58 LuaValue::Number(f) => Ok(Float(CoreFloat::Fixed(f))),
59 // Create a new float from the borrow, since it might already be borrowed, with for ex.
60 // f:eq(f)
61 LuaValue::UserData(u) => Ok(Float(u.borrow::<Float>().unwrap().0)),
62 _ => Err(LuaError::FromLuaConversionError {
63 from: "Value",
64 to: "Float",
65 message: Some("Only int, float, or float() values are allowed".to_string()),
66 }),
67 }
27 } 68 }
28} 69}
29impl LuaUserData for Float {}
30 70
31fn float(_: &Lua, _: ()) -> LuaResult<Float> { 71fn float(_: &Lua, _: ()) -> LuaResult<Float> {
32 Ok(Float::new()) 72 Ok(Float::new())
33} 73}
34 74
75#[derive(Clone, Copy, Debug)]
76struct Bool(CoreBool);
77
78impl LuaUserData for Bool {
79 fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
80 macro_rules! bool_method {
81 ($name: expr, $op: ident) => {
82 methods.add_method($name, |_lua, lhs: &Bool, rhs: LuaValue| {
83 let lhs = *lhs;
84 let rhs = Bool::try_from(rhs).unwrap();
85 Ok(Bool(runtime_thread_do(Box::new(move |r| {
86 r.solver_ctx().$op(lhs.0, rhs.0)
87 }))))
88 });
89 };
90 }
91
92 bool_method!("eq", bool_eq);
93 bool_method!("implies", bool_implies);
94
95 methods.add_method("and", |_lua, lhs: &Bool, rhs: LuaValue| {
96 let lhs = *lhs;
97 let rhs = Bool::try_from(rhs).unwrap();
98 Ok(Bool(runtime_thread_do(Box::new(move |r| {
99 r.solver_ctx().bool_and(&[lhs.0, rhs.0])
100 }))))
101 });
102
103 methods.add_method("or", |_lua, lhs: &Bool, rhs: LuaValue| {
104 let lhs = *lhs;
105 let rhs = Bool::try_from(rhs).unwrap();
106 Ok(Bool(runtime_thread_do(Box::new(move |r| {
107 r.solver_ctx().bool_or(&[lhs.0, rhs.0])
108 }))))
109 });
110
111 methods.add_method("no", |_lua, b: &Bool, _: ()| {
112 let b = *b;
113 Ok(Bool(runtime_thread_do(Box::new(move |r| {
114 r.solver_ctx().bool_not(b.0)
115 }))))
116 });
117 }
118}
119
120impl TryFrom<LuaValue<'_>> for Bool {
121 type Error = LuaError;
122
123 fn try_from(value: LuaValue) -> Result<Self, Self::Error> {
124 match value {
125 LuaValue::Boolean(b) => Ok(Bool(runtime_thread_do(Box::new(move |r| {
126 r.solver_ctx().new_fixed_bool(b)
127 })))),
128 // Create a new bool from the borrow, since it might already be borrowed, with for ex.
129 // b:eq(b)
130 LuaValue::UserData(u) => Ok(Bool(u.borrow::<Bool>().unwrap().0)),
131 _ => Err(LuaError::FromLuaConversionError {
132 from: "Value",
133 to: "Bool",
134 message: Some("Only bool, or bool() values are allowed".to_string()),
135 }),
136 }
137 }
138}
139
35#[derive(Clone, Debug)] 140#[derive(Clone, Debug)]
36struct FontDescription(CoreFontDescription); 141struct FontDescription(CoreFontDescription);
37impl LuaUserData for FontDescription {} 142impl LuaUserData for FontDescription {
143 fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
144 fields.add_field_method_get("size", |_, this| Ok(Float(this.0.size)))
145 }
146}
38 147
39const DEFAULT_FONT_FAMILY: &str = "serif"; 148const DEFAULT_FONT_FAMILY: &str = "serif";
40 149
@@ -68,9 +177,10 @@ fn font(_: &Lua, params: LuaTable) -> LuaResult<FontDescription> {
68 Some(_) => return Err(LuaError::RuntimeError("Unknown weight".to_string())), 177 Some(_) => return Err(LuaError::RuntimeError("Unknown weight".to_string())),
69 }; 178 };
70 179
71 let size = params 180 let size = match params.get::<_, Option<LuaValue>>("size")? {
72 .get::<_, Option<_>>("size")? 181 Some(f) => Float::try_from(f)?,
73 .unwrap_or_else(Float::new); 182 None => Float::new(),
183 };
74 184
75 Ok(FontDescription(CoreFontDescription { 185 Ok(FontDescription(CoreFontDescription {
76 family, 186 family,
@@ -80,46 +190,272 @@ fn font(_: &Lua, params: LuaTable) -> LuaResult<FontDescription> {
80 })) 190 }))
81} 191}
82 192
193fn new_shape_context() -> CoreShapeContext {
194 runtime_thread_do(Box::new(|r| CoreShapeContext::new(r.solver_ctx())))
195}
196
197#[derive(Debug, Clone)]
198struct Drawable<T>
199where
200 T: Into<CoreShape>,
201{
202 shape: T,
203 context: CoreShapeContext,
204}
205
206trait HasContext {
207 fn get_context(&self) -> &CoreShapeContext;
208}
209
210fn add_bound_fields<'lua, T, F>(fields: &mut F)
211where
212 T: LuaUserData,
213 T: HasContext,
214 F: LuaUserDataFields<'lua, T>,
215{
216 macro_rules! bound_field {
217 ($name: expr, $method: ident) => {
218 fields.add_field_method_get($name, |_, this| {
219 let bounds = this.get_context().bounds().clone();
220 Ok(Float(runtime_thread_do(Box::new(move |r| {
221 bounds.$method(r.solver_ctx())
222 }))))
223 })
224 };
225 }
226
227 bound_field!("top", top);
228 bound_field!("left", left);
229 bound_field!("width", width);
230 bound_field!("height", height);
231 bound_field!("right", right);
232 bound_field!("bottom", bottom);
233 bound_field!("vert_center", vert_center);
234 bound_field!("horiz_center", horiz_center);
235}
236
237impl<T: Into<CoreShape>> From<Drawable<T>> for CoreDrawable {
238 fn from(value: Drawable<T>) -> Self {
239 CoreDrawable::new(value.shape.into(), value.context)
240 }
241}
242
83#[derive(Clone, Debug)] 243#[derive(Clone, Debug)]
84struct Text(CoreText); 244struct Text(Drawable<CoreText>);
85impl LuaUserData for Text { 245impl LuaUserData for Text {
86 fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { 246 fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
87 methods.add_method("draw", |_, this, _params: ()| { 247 methods.add_method("draw", |_, this, _params: ()| {
88 println!("I'm drawing: {}", this.0.content); 248 let drawable = this.0.clone().into();
249 runtime_thread_do(Box::new(|r| {
250 r.add_drawable(drawable);
251 }));
89 Ok(()) 252 Ok(())
90 }) 253 })
91 } 254 }
255
256 fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
257 add_bound_fields(fields);
258 }
259}
260
261impl HasContext for Text {
262 fn get_context(&self) -> &CoreShapeContext {
263 &self.0.context
264 }
92} 265}
93 266
94fn text(_: &Lua, params: LuaTable) -> LuaResult<Text> { 267fn text(_: &Lua, params: LuaTable) -> LuaResult<Text> {
95 let content = params.get("content")?; 268 let content: String = params.get("content")?;
96 269
97 let font = params 270 let font = params
98 .get::<_, Option<FontDescription>>("font")? 271 .get::<_, Option<FontDescription>>("font")?
99 .unwrap_or_default(); 272 .unwrap_or_default();
100 273
101 Ok(Text(CoreText { 274 let context = new_shape_context();
102 content, 275
103 font: font.0, 276 let content_2 = content.clone();
277 let font_2 = font.0.clone();
278 let context_2 = context.clone();
279
280 runtime_thread_do(Box::new(move |r| {
281 let (text_width, text_height) = r.renderer().text_extents(&content_2, &font_2);
282 let solver = r.solver_ctx();
283 // let scale = solver.float_div(font.0.size, CoreFloat::Fixed(height));
284
285 let calculated_width = solver.float_mul(&[CoreFloat::Fixed(text_width), font.0.size]);
286 let bounds_width = context_2.bounds().width(solver);
287 let width_constraint = solver.float_eq(bounds_width, calculated_width);
288 solver.constrain(width_constraint);
289
290 let calculated_height = solver.float_mul(&[CoreFloat::Fixed(text_height), font.0.size]);
291 let bounds_height = context_2.bounds().height(solver);
292 let height_constraint = solver.float_eq(bounds_height, calculated_height);
293 solver.constrain(height_constraint);
294 }));
295
296 Ok(Text(Drawable {
297 shape: CoreText {
298 content,
299 font: font.0,
300 },
301 context,
302 }))
303}
304
305#[derive(Clone, Debug)]
306struct Rectangle(Drawable<CoreRectangle>);
307impl LuaUserData for Rectangle {
308 fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
309 methods.add_method("draw", |_, this, _params: ()| {
310 let drawable = this.0.clone().into();
311 runtime_thread_do(Box::new(|r| {
312 r.add_drawable(drawable);
313 }));
314 Ok(())
315 })
316 }
317
318 fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
319 add_bound_fields(fields);
320 }
321}
322
323impl HasContext for Rectangle {
324 fn get_context(&self) -> &CoreShapeContext {
325 &self.0.context
326 }
327}
328
329fn rectangle(_: &Lua, _params: ()) -> LuaResult<Rectangle> {
330 Ok(Rectangle(Drawable {
331 shape: CoreRectangle {},
332 context: new_shape_context(),
104 })) 333 }))
105} 334}
106 335
336thread_local! {
337 static SENDER: RefCell<Option<SyncSender<Message>>> = RefCell::new(None);
338 static REPLY: RefCell<Option<Receiver<Reply>>> = RefCell::new(None);
339 static REPLIER: RefCell<Option<SyncSender<Reply>>> = RefCell::new(None);
340}
341
342enum Message {
343 Do(Box<dyn FnOnce(&mut Runtime) + Send>),
344}
345
346fn runtime_thread_do<T: Any + Send>(fun: Box<dyn FnOnce(&mut Runtime) -> T + Send>) -> T {
347 SENDER.with(|sender| {
348 sender
349 .borrow_mut()
350 .as_mut()
351 .expect("Not currently drawing")
352 .send(Message::Do(Box::new(|r| {
353 let ret = fun(r);
354 REPLIER.with(|replier| {
355 replier
356 .borrow_mut()
357 .as_mut()
358 .unwrap()
359 .send(Reply::Any(Box::new(ret)))
360 .unwrap();
361 });
362 })))
363 .unwrap();
364 });
365
366 REPLY.with(|reply| {
367 let Reply::Any(any) = reply
368 .borrow_mut()
369 .as_mut()
370 .expect("Not currently drawing")
371 .recv()
372 .unwrap();
373 *any.downcast().unwrap()
374 })
375}
376
377enum Reply {
378 Any(Box<dyn Any + Send>),
379}
380
381fn constrain(_: &Lua, bool: Bool) -> LuaResult<()> {
382 runtime_thread_do(Box::new(move |r| r.solver_ctx().constrain(bool.0)));
383 Ok(())
384}
385
107fn draw(_: &Lua, params: LuaTable) -> LuaResult<()> { 386fn draw(_: &Lua, params: LuaTable) -> LuaResult<()> {
108 let content: LuaTable = params.get("content")?; 387 // So.... The Z3 stuff isn't Send and contains lifetimes, so we can't store them in global
109 let output: LuaTable = params.get("output")?; 388 // variables or convert them to Lua. Solution: handle everything in a specific thread, and
389 // communicate through a channel.
390 thread::scope(|s| {
391 let (message_sender, message_receiver) = sync_channel(1);
392 let (reply_sender, reply_receiver) = sync_channel(1);
393
394 SENDER.with(|f| {
395 *f.borrow_mut() = Some(message_sender);
396 });
397
398 REPLY.with(|f| {
399 *f.borrow_mut() = Some(reply_receiver);
400 });
110 401
111 let z3_cfg = z3::Config::new(); 402 s.spawn(move || {
112 let z3_ctx = z3::Context::new(&z3_cfg); 403 REPLIER.with(|f| {
113 let ctx = Z3Context::new(&z3_ctx); 404 *f.borrow_mut() = Some(reply_sender.clone());
405 });
114 406
115 let cairo_renderer = CairoRenderer::new(); 407 let z3_cfg = z3::Config::new();
408 let z3_ctx = z3::Context::new(&z3_cfg);
409 let ctx = Z3Context::new(&z3_ctx);
116 410
117 // TODO: we shouldn't need the renderer until the end 411 let cairo_renderer = CairoRenderer::new();
118 let mut runtime = Runtime::new(Box::new(ctx), Box::new(cairo_renderer));
119 412
120 let _solver = runtime.solver_ctx(); 413 // TODO: we shouldn't need the renderer until the end
414 let mut runtime = Runtime::new(Box::new(ctx), Box::new(cairo_renderer));
121 415
122 dbg!(content, output); 416 for message in message_receiver {
417 match message {
418 Message::Do(fun) => {
419 fun(&mut runtime);
420 }
421 }
422 }
423
424 runtime.render();
425 });
426
427 let content: LuaValue = params.get("content").unwrap();
428 let _output: LuaTable = params.get("output").unwrap();
429
430 match content {
431 LuaValue::Table(table) => {
432 let () = table.call_method("draw", ()).unwrap();
433 }
434 // TODO: switch to enum
435 // LuaValue::UserData(user_data) => {
436 // if user_data.is::<Text>() {
437 // let text = user_data
438 // .borrow::<Text>()
439 // .expect("Couldn't borrow Text user data");
440 //
441 // SENDER.with(|f| {
442 // f.borrow_mut()
443 // .as_ref()
444 // .expect("Not currently drawing")
445 // .send(Message::AddDrawable(CoreShape::Text(text.0.clone())))
446 // .expect("Could not send shape");
447 // })
448 // } else {
449 // panic!("Non-drawable passed to draw");
450 // }
451 // }
452 _ => panic!("Non-drawable passed to draw"),
453 }
454
455 SENDER.with(|s| {
456 *s.borrow_mut() = None;
457 });
458 });
123 459
124 Ok(()) 460 Ok(())
125} 461}
@@ -128,11 +464,91 @@ fn draw(_: &Lua, params: LuaTable) -> LuaResult<()> {
128fn libdiaphragm(lua: &Lua) -> LuaResult<LuaTable> { 464fn libdiaphragm(lua: &Lua) -> LuaResult<LuaTable> {
129 // TODO: the solver as a mutable global solves so much problem (pun not intended) 465 // TODO: the solver as a mutable global solves so much problem (pun not intended)
130 let exports = lua.create_table()?; 466 let exports = lua.create_table()?;
467 exports.set("float", lua.create_function(float)?)?;
468
131 exports.set("text", lua.create_function(text)?)?; 469 exports.set("text", lua.create_function(text)?)?;
132 exports.set("font", lua.create_function(font)?)?; 470 exports.set("font", lua.create_function(font)?)?;
133 exports.set("float", lua.create_function(float)?)?;
134 471
472 exports.set("rectangle", lua.create_function(rectangle)?)?;
473
474 exports.set("constrain", lua.create_function(constrain)?)?;
135 exports.set("draw", lua.create_function(draw)?)?; 475 exports.set("draw", lua.create_function(draw)?)?;
136 476
477 // Setting up metatables
478 // ---
479
480 let float_metatable = lua
481 .create_userdata(Float(CoreFloat::Fixed(0.)))
482 .unwrap()
483 .get_metatable()
484 .unwrap();
485
486 macro_rules! float_metamethod {
487 ($method: ident, $op: ident) => {
488 float_metatable
489 .set(
490 LuaMetaMethod::$method,
491 lua.create_function(|_lua, (lhs, rhs): (Float, LuaValue)| {
492 let rhs = Float::try_from(rhs).unwrap();
493 Ok(Float(runtime_thread_do(Box::new(move |r| {
494 r.solver_ctx().$op(&[lhs.0, rhs.0])
495 }))))
496 })
497 .unwrap(),
498 )
499 .unwrap();
500 };
501 }
502
503 float_metamethod!(Add, float_add);
504 float_metamethod!(Sub, float_sub);
505 float_metamethod!(Mul, float_mul);
506
507 float_metatable
508 .set(
509 LuaMetaMethod::Div,
510 lua.create_function(|_lua, (lhs, rhs): (Float, LuaValue)| {
511 let rhs = Float::try_from(rhs).unwrap();
512 Ok(Float(runtime_thread_do(Box::new(move |r| {
513 r.solver_ctx().float_div(lhs.0, rhs.0)
514 }))))
515 })
516 .unwrap(),
517 )
518 .unwrap();
519
520 float_metatable
521 .set(
522 LuaMetaMethod::Unm,
523 lua.create_function(|_lua, f: Float| {
524 Ok(Float(runtime_thread_do(Box::new(move |r| {
525 r.solver_ctx().float_neg(f.0)
526 }))))
527 })
528 .unwrap(),
529 )
530 .unwrap();
531
532 // Not the operators `==`, `!=`, `<`, `<=`, `>`, and `>=` because Lua converts result to bool
533
534 let bool_metatable = lua
535 .create_userdata(Bool(CoreBool::new(VariableHandle::new(0))))
536 .unwrap()
537 .get_metatable()
538 .unwrap();
539
540 bool_metatable
541 .set(
542 LuaMetaMethod::Shl,
543 lua.create_function(|_lua, (lhs, rhs): (Bool, LuaValue)| {
544 let rhs = Bool::try_from(rhs).unwrap();
545 Ok(Bool(runtime_thread_do(Box::new(move |r| {
546 r.solver_ctx().bool_implies(lhs.0, rhs.0)
547 }))))
548 })
549 .unwrap(),
550 )
551 .unwrap();
552
137 Ok(exports) 553 Ok(exports)
138} 554}
diff --git a/z3-solver/src/lib.rs b/z3-solver/src/lib.rs
index 35a17a8..1c90a6f 100644
--- a/z3-solver/src/lib.rs
+++ b/z3-solver/src/lib.rs
@@ -30,15 +30,18 @@ impl Drop for Z3Context<'_> {
30} 30}
31 31
32fn value_to_num_den(value: f64) -> (i32, i32) { 32fn value_to_num_den(value: f64) -> (i32, i32) {
33 let fract = value.fract(); 33 // TODO: FIXME: so hacky, because I'm so lazy...
34 let number_of_fract_digits = -fract.log10().floor(); 34 ((value * 1_000_000.) as _, 1_000_000)
35 35
36 if number_of_fract_digits >= 1. && !number_of_fract_digits.is_infinite() { 36 // let fract = value.fract();
37 let den = 10f64.powf(number_of_fract_digits); 37 // let number_of_fract_digits = -fract.log10().floor();
38 ((value * den) as i32, den as i32) 38 //
39 } else { 39 // if number_of_fract_digits >= 1. && !number_of_fract_digits.is_infinite() {
40 (value as i32, 1) 40 // let den = 10f64.powf(number_of_fract_digits);
41 } 41 // ((value * den) as i32, den as i32)
42 // } else {
43 // (value as i32, 1)
44 // }
42} 45}
43 46
44impl<'z3> Z3Context<'z3> { 47impl<'z3> Z3Context<'z3> {