diff options
-rw-r--r-- | cairo-renderer/src/lib.rs | 45 | ||||
-rw-r--r-- | core/src/core_shapes.rs | 73 | ||||
-rw-r--r-- | core/src/lib.rs | 8 | ||||
-rw-r--r-- | core/src/rendering.rs | 53 | ||||
-rw-r--r-- | core/src/runtime.rs | 154 | ||||
-rw-r--r-- | core/src/solving.rs | 55 | ||||
-rw-r--r-- | core/src/types.rs | 110 | ||||
-rw-r--r-- | lua-bindings/src/lib.rs | 482 | ||||
-rw-r--r-- | z3-solver/src/lib.rs | 21 |
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 @@ | |||
1 | use diaphragm_core::{ | 1 | use 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 @@ | |||
1 | use super::complex_shapes::{ComplexShape, DrawResult}; | 1 | pub struct CoreDrawable { |
2 | use super::rendering::{Render, Renderer}; | 2 | pub(crate) shape: CoreShape, |
3 | use super::solving::{Constrainable, SolverContext, SolverModel}; | 3 | pub(crate) context: CoreShapeContext, |
4 | use super::types::*; | 4 | } |
5 | |||
6 | impl CoreDrawable { | ||
7 | pub fn new(shape: CoreShape, context: CoreShapeContext) -> Self { | ||
8 | Self { shape, context } | ||
9 | } | ||
10 | } | ||
11 | |||
12 | pub enum CoreShape { | ||
13 | Rectangle(Rectangle), | ||
14 | Text(Text), | ||
15 | // StraightPath(StraightPath), | ||
16 | } | ||
5 | 17 | ||
18 | impl From<Rectangle> for CoreShape { | ||
19 | fn from(rectangle: Rectangle) -> Self { | ||
20 | CoreShape::Rectangle(rectangle) | ||
21 | } | ||
22 | } | ||
23 | |||
24 | impl From<Text> for CoreShape { | ||
25 | fn from(text: Text) -> Self { | ||
26 | CoreShape::Text(text) | ||
27 | } | ||
28 | } | ||
29 | |||
30 | pub struct DefinedCoreDrawable { | ||
31 | pub(crate) shape: DefinedCoreShape, | ||
32 | pub(crate) context: DefinedCoreShapeContext, | ||
33 | } | ||
34 | |||
35 | pub enum DefinedCoreShape { | ||
36 | Rectangle(Rectangle), | ||
37 | Text(DefinedText), | ||
38 | // StraightPath(StraightPath), | ||
39 | } | ||
40 | |||
41 | /* | ||
6 | pub trait CoreShape { | 42 | pub 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)] |
29 | pub struct Rectangle {} | 66 | pub struct Rectangle {} |
30 | 67 | ||
68 | /* | ||
31 | impl CoreShape for Rectangle { | 69 | impl 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 | |||
76 | use crate::types::{CoreShapeContext, DefinedCoreShapeContext}; | ||
36 | 77 | ||
37 | pub use super::text::{DefinedText, Text}; | 78 | pub use super::text::{DefinedText, Text}; |
38 | 79 | ||
80 | /* | ||
39 | impl CoreShape for Text { | 81 | impl 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)] |
67 | pub struct StraightPath { | 110 | // pub struct StraightPath { |
68 | pub(crate) points: Vec<Point2D>, | 111 | // pub(crate) points: Vec<Point2D>, |
69 | } | 112 | // } |
70 | 113 | // | |
71 | impl 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 | /* | ||
77 | pub struct DefinedStraightPath { | 121 | pub 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 | ||
4 | pub mod colors; | 4 | pub mod colors; |
5 | mod complex_shapes; | 5 | // mod complex_shapes; |
6 | pub mod core_shapes; | 6 | pub mod core_shapes; |
7 | mod rendering; | 7 | mod rendering; |
8 | mod runtime; | 8 | mod runtime; |
@@ -11,9 +11,9 @@ pub mod styles; | |||
11 | pub mod text; | 11 | pub mod text; |
12 | pub mod types; | 12 | pub mod types; |
13 | 13 | ||
14 | pub use complex_shapes::{ | 14 | // pub use complex_shapes::{ |
15 | ComplexShape, DrawResult, Drawable, DrawableBuilder, DynClone, DynDrawable, | 15 | // ComplexShape, DrawResult, Drawable, DrawableBuilder, DynClone, DynDrawable, |
16 | }; | 16 | // }; |
17 | pub use rendering::Renderer; | 17 | pub use rendering::Renderer; |
18 | pub use runtime::Runtime; | 18 | pub use runtime::Runtime; |
19 | pub use solving::{SolverContext, SolverModel}; | 19 | pub 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 @@ | |||
1 | use super::core_shapes::*; | 1 | use super::core_shapes::*; |
2 | use super::styles::{DefinedDashStyle, DefinedStrokeStyle, FillStyle, Pattern}; | 2 | use super::styles::{DefinedDashStyle, DefinedStrokeStyle, FillStyle, Pattern}; |
3 | use super::text::{DefinedFontDescription, FontDescription}; | 3 | use super::text::{DefinedFontDescription, FontDescription}; |
4 | use super::types::DefinedShapeContext; | 4 | use super::types::DefinedCoreShapeContext; |
5 | 5 | ||
6 | pub trait Renderer { | 6 | pub 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 | ||
22 | pub trait Render { | 22 | pub trait Render { |
23 | fn render(&self, context: DefinedShapeContext, renderer: &mut dyn Renderer); | 23 | fn render(&self, context: DefinedCoreShapeContext, renderer: &mut dyn Renderer); |
24 | } | ||
25 | |||
26 | impl 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 | ||
26 | fn draw(fill: &FillStyle, stroke: &DefinedStrokeStyle, renderer: &mut dyn Renderer) { | 35 | fn 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 | ||
49 | impl Render for Rectangle { | 58 | impl 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 | ||
57 | impl Render for DefinedText { | 66 | impl 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 | ||
69 | impl 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 @@ | |||
1 | use super::complex_shapes::{ComplexShape, Drawable, DynDrawable}; | 1 | use crate::core_shapes::CoreDrawable; |
2 | use crate::rendering::Render; | ||
3 | |||
4 | // use super::complex_shapes::{ComplexShape, Drawable}; | ||
2 | use super::rendering::Renderer; | 5 | use super::rendering::Renderer; |
3 | use super::solving::{Constrainable, SolverContext}; | 6 | use super::solving::{Constrainable, SolverContext}; |
4 | 7 | ||
5 | const RECURSION_LIMIT: u64 = 10_000; | 8 | // const RECURSION_LIMIT: u64 = 10_000; |
6 | 9 | ||
7 | pub struct Runtime<'a> { | 10 | pub 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 | ||
13 | impl<'a> Runtime<'a> { | 17 | impl<'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 @@ | |||
1 | use super::core_shapes::*; | 1 | use crate::core_shapes::{CoreDrawable, CoreShape, DefinedCoreDrawable, DefinedCoreShape}; |
2 | |||
2 | use super::styles::*; | 3 | use super::styles::*; |
3 | use super::text::*; | 4 | use super::text::*; |
4 | use super::types::{Bool, Float}; | 5 | use 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 | ||
151 | impl 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 | |||
150 | impl Constrainable for Float { | 162 | impl 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 | ||
221 | impl Constrainable for ShapeContext { | 233 | impl 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 | |||
244 | impl 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 | ||
268 | impl 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)] |
210 | pub 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)] |
216 | pub struct DefinedBounds { | 216 | pub 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)] |
224 | pub 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)] |
230 | pub struct ShapeContext { | 230 | pub 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 | ||
236 | impl ShapeContext { | 236 | impl 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)] |
280 | pub(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 | // | |
286 | impl 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)] |
312 | pub struct DefinedShapeContext { | 312 | pub 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 @@ | |||
1 | use std::{sync::atomic::{AtomicUsize, Ordering}, cell::RefCell}; | 1 | use std::{ |
2 | any::Any, | ||
3 | cell::RefCell, | ||
4 | sync::mpsc::{sync_channel, Receiver, SyncSender}, | ||
5 | thread, | ||
6 | }; | ||
2 | 7 | ||
3 | use diaphragm_cairo_renderer::CairoRenderer; | 8 | use diaphragm_cairo_renderer::CairoRenderer; |
4 | use diaphragm_core::{ | 9 | use 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 | }; |
10 | use diaphragm_z3_solver::{z3, Z3Context}; | 16 | use diaphragm_z3_solver::{z3, Z3Context}; |
11 | use mlua::prelude::*; | 17 | use mlua::prelude::*; |
12 | 18 | ||
13 | static MAX_ID: AtomicUsize = AtomicUsize::new(0); | ||
14 | |||
15 | thread_local! { | ||
16 | static SENDER: RefCell<Option<Runtime<'static>>> = RefCell::new(None); | ||
17 | } | ||
18 | |||
19 | #[derive(Clone, Copy, Debug)] | 19 | #[derive(Clone, Copy, Debug)] |
20 | struct Float(CoreFloat); | 20 | struct Float(CoreFloat); |
21 | 21 | ||
22 | impl Float { | 22 | impl 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 | } | ||
29 | impl 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 | |||
52 | impl 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 | } |
29 | impl LuaUserData for Float {} | ||
30 | 70 | ||
31 | fn float(_: &Lua, _: ()) -> LuaResult<Float> { | 71 | fn float(_: &Lua, _: ()) -> LuaResult<Float> { |
32 | Ok(Float::new()) | 72 | Ok(Float::new()) |
33 | } | 73 | } |
34 | 74 | ||
75 | #[derive(Clone, Copy, Debug)] | ||
76 | struct Bool(CoreBool); | ||
77 | |||
78 | impl 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 | |||
120 | impl 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)] |
36 | struct FontDescription(CoreFontDescription); | 141 | struct FontDescription(CoreFontDescription); |
37 | impl LuaUserData for FontDescription {} | 142 | impl 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 | ||
39 | const DEFAULT_FONT_FAMILY: &str = "serif"; | 148 | const 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 | ||
193 | fn new_shape_context() -> CoreShapeContext { | ||
194 | runtime_thread_do(Box::new(|r| CoreShapeContext::new(r.solver_ctx()))) | ||
195 | } | ||
196 | |||
197 | #[derive(Debug, Clone)] | ||
198 | struct Drawable<T> | ||
199 | where | ||
200 | T: Into<CoreShape>, | ||
201 | { | ||
202 | shape: T, | ||
203 | context: CoreShapeContext, | ||
204 | } | ||
205 | |||
206 | trait HasContext { | ||
207 | fn get_context(&self) -> &CoreShapeContext; | ||
208 | } | ||
209 | |||
210 | fn add_bound_fields<'lua, T, F>(fields: &mut F) | ||
211 | where | ||
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 | |||
237 | impl<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)] |
84 | struct Text(CoreText); | 244 | struct Text(Drawable<CoreText>); |
85 | impl LuaUserData for Text { | 245 | impl 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 | |||
261 | impl HasContext for Text { | ||
262 | fn get_context(&self) -> &CoreShapeContext { | ||
263 | &self.0.context | ||
264 | } | ||
92 | } | 265 | } |
93 | 266 | ||
94 | fn text(_: &Lua, params: LuaTable) -> LuaResult<Text> { | 267 | fn 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)] | ||
306 | struct Rectangle(Drawable<CoreRectangle>); | ||
307 | impl 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 | |||
323 | impl HasContext for Rectangle { | ||
324 | fn get_context(&self) -> &CoreShapeContext { | ||
325 | &self.0.context | ||
326 | } | ||
327 | } | ||
328 | |||
329 | fn rectangle(_: &Lua, _params: ()) -> LuaResult<Rectangle> { | ||
330 | Ok(Rectangle(Drawable { | ||
331 | shape: CoreRectangle {}, | ||
332 | context: new_shape_context(), | ||
104 | })) | 333 | })) |
105 | } | 334 | } |
106 | 335 | ||
336 | thread_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 | |||
342 | enum Message { | ||
343 | Do(Box<dyn FnOnce(&mut Runtime) + Send>), | ||
344 | } | ||
345 | |||
346 | fn 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 | |||
377 | enum Reply { | ||
378 | Any(Box<dyn Any + Send>), | ||
379 | } | ||
380 | |||
381 | fn constrain(_: &Lua, bool: Bool) -> LuaResult<()> { | ||
382 | runtime_thread_do(Box::new(move |r| r.solver_ctx().constrain(bool.0))); | ||
383 | Ok(()) | ||
384 | } | ||
385 | |||
107 | fn draw(_: &Lua, params: LuaTable) -> LuaResult<()> { | 386 | fn 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<()> { | |||
128 | fn libdiaphragm(lua: &Lua) -> LuaResult<LuaTable> { | 464 | fn 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 | ||
32 | fn value_to_num_den(value: f64) -> (i32, i32) { | 32 | fn 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 | ||
44 | impl<'z3> Z3Context<'z3> { | 47 | impl<'z3> Z3Context<'z3> { |