summaryrefslogtreecommitdiffstats
path: root/lua-bindings
diff options
context:
space:
mode:
authorMinijackson <minijackson@riseup.net>2023-01-04 09:38:29 +0100
committerMinijackson <minijackson@riseup.net>2023-01-04 09:38:29 +0100
commitefc83b6fec3f38c45a6b2f51d5e791c30f59eb42 (patch)
tree993b8f3a13cf05de8afa40bfe8f5726faa52f231 /lua-bindings
parentc1ac90def08fa7a16c120e57c21f50abc743fd70 (diff)
downloaddiaphragm-efc83b6fec3f38c45a6b2f51d5e791c30f59eb42.tar.gz
diaphragm-efc83b6fec3f38c45a6b2f51d5e791c30f59eb42.zip
lua-bindings: update with straightpath, better error handling, etc.
Diffstat (limited to 'lua-bindings')
-rw-r--r--lua-bindings/src/lib.rs505
1 files changed, 419 insertions, 86 deletions
diff --git a/lua-bindings/src/lib.rs b/lua-bindings/src/lib.rs
index daf4b48..35cdaf8 100644
--- a/lua-bindings/src/lib.rs
+++ b/lua-bindings/src/lib.rs
@@ -1,3 +1,5 @@
1// TODO: split file
2
1use std::{ 3use std::{
2 any::Any, 4 any::Any,
3 cell::RefCell, 5 cell::RefCell,
@@ -7,10 +9,14 @@ use std::{
7 9
8use diaphragm_cairo_renderer::CairoRenderer; 10use diaphragm_cairo_renderer::CairoRenderer;
9use diaphragm_core::{ 11use diaphragm_core::{
10 core_shapes::{CoreDrawable, CoreShape, Rectangle as CoreRectangle}, 12 colors::Color,
13 core_shapes::{
14 CoreDrawable, CoreShape, Rectangle as CoreRectangle, StraightPath as CoreStraightPath,
15 },
11 solving::VariableHandle, 16 solving::VariableHandle,
17 styles::Pattern,
12 text::{FontDescription as CoreFontDescription, FontStyle, FontWeight, Text as CoreText}, 18 text::{FontDescription as CoreFontDescription, FontStyle, FontWeight, Text as CoreText},
13 types::{Bool as CoreBool, CoreShapeContext, Float as CoreFloat}, 19 types::{Bool as CoreBool, CoreShapeContext, Float as CoreFloat, Point2D as CorePoint2D},
14 Runtime, 20 Runtime,
15}; 21};
16use diaphragm_z3_solver::{z3, Z3Context}; 22use diaphragm_z3_solver::{z3, Z3Context};
@@ -32,7 +38,7 @@ impl LuaUserData for Float {
32 ($name: expr, $op: ident) => { 38 ($name: expr, $op: ident) => {
33 methods.add_method($name, |_lua, lhs: &Float, rhs: LuaValue| { 39 methods.add_method($name, |_lua, lhs: &Float, rhs: LuaValue| {
34 let lhs = *lhs; 40 let lhs = *lhs;
35 let rhs = Float::try_from(rhs).unwrap(); 41 let rhs = Float::try_from(rhs)?;
36 Ok(Bool(runtime_thread_do(Box::new(move |r| { 42 Ok(Bool(runtime_thread_do(Box::new(move |r| {
37 r.solver_ctx().$op(lhs.0, rhs.0) 43 r.solver_ctx().$op(lhs.0, rhs.0)
38 })))) 44 }))))
@@ -46,6 +52,13 @@ impl LuaUserData for Float {
46 float_method!("ge", float_ge); 52 float_method!("ge", float_ge);
47 float_method!("lt", float_lt); 53 float_method!("lt", float_lt);
48 float_method!("le", float_le); 54 float_method!("le", float_le);
55
56 methods.add_method("abs", |_lua, f: &Float, _: ()| {
57 let f = *f;
58 Ok(Float(runtime_thread_do(Box::new(move |r| {
59 r.solver_ctx().float_abs(f.0)
60 }))))
61 });
49 } 62 }
50} 63}
51 64
@@ -58,9 +71,9 @@ impl TryFrom<LuaValue<'_>> for Float {
58 LuaValue::Number(f) => Ok(Float(CoreFloat::Fixed(f))), 71 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. 72 // Create a new float from the borrow, since it might already be borrowed, with for ex.
60 // f:eq(f) 73 // f:eq(f)
61 LuaValue::UserData(u) => Ok(Float(u.borrow::<Float>().unwrap().0)), 74 LuaValue::UserData(u) => Ok(Float(u.borrow::<Float>()?.0)),
62 _ => Err(LuaError::FromLuaConversionError { 75 _ => Err(LuaError::FromLuaConversionError {
63 from: "Value", 76 from: value.type_name(),
64 to: "Float", 77 to: "Float",
65 message: Some("Only int, float, or float() values are allowed".to_string()), 78 message: Some("Only int, float, or float() values are allowed".to_string()),
66 }), 79 }),
@@ -72,6 +85,20 @@ fn float(_: &Lua, _: ()) -> LuaResult<Float> {
72 Ok(Float::new()) 85 Ok(Float::new())
73} 86}
74 87
88fn float_max(_: &Lua, elems: Vec<Float>) -> LuaResult<Float> {
89 let elems = elems.into_iter().map(|f| f.0).collect::<Vec<_>>();
90 Ok(Float(runtime_thread_do(Box::new(move |r| {
91 r.solver_ctx().float_max(&elems)
92 }))))
93}
94
95fn float_min(_: &Lua, elems: Vec<Float>) -> LuaResult<Float> {
96 let elems = elems.into_iter().map(|f| f.0).collect::<Vec<_>>();
97 Ok(Float(runtime_thread_do(Box::new(move |r| {
98 r.solver_ctx().float_min(&elems)
99 }))))
100}
101
75#[derive(Clone, Copy, Debug)] 102#[derive(Clone, Copy, Debug)]
76struct Bool(CoreBool); 103struct Bool(CoreBool);
77 104
@@ -81,7 +108,7 @@ impl LuaUserData for Bool {
81 ($name: expr, $op: ident) => { 108 ($name: expr, $op: ident) => {
82 methods.add_method($name, |_lua, lhs: &Bool, rhs: LuaValue| { 109 methods.add_method($name, |_lua, lhs: &Bool, rhs: LuaValue| {
83 let lhs = *lhs; 110 let lhs = *lhs;
84 let rhs = Bool::try_from(rhs).unwrap(); 111 let rhs = Bool::try_from(rhs)?;
85 Ok(Bool(runtime_thread_do(Box::new(move |r| { 112 Ok(Bool(runtime_thread_do(Box::new(move |r| {
86 r.solver_ctx().$op(lhs.0, rhs.0) 113 r.solver_ctx().$op(lhs.0, rhs.0)
87 })))) 114 }))))
@@ -94,7 +121,7 @@ impl LuaUserData for Bool {
94 121
95 methods.add_method("and", |_lua, lhs: &Bool, rhs: LuaValue| { 122 methods.add_method("and", |_lua, lhs: &Bool, rhs: LuaValue| {
96 let lhs = *lhs; 123 let lhs = *lhs;
97 let rhs = Bool::try_from(rhs).unwrap(); 124 let rhs = Bool::try_from(rhs)?;
98 Ok(Bool(runtime_thread_do(Box::new(move |r| { 125 Ok(Bool(runtime_thread_do(Box::new(move |r| {
99 r.solver_ctx().bool_and(&[lhs.0, rhs.0]) 126 r.solver_ctx().bool_and(&[lhs.0, rhs.0])
100 })))) 127 }))))
@@ -102,7 +129,7 @@ impl LuaUserData for Bool {
102 129
103 methods.add_method("or", |_lua, lhs: &Bool, rhs: LuaValue| { 130 methods.add_method("or", |_lua, lhs: &Bool, rhs: LuaValue| {
104 let lhs = *lhs; 131 let lhs = *lhs;
105 let rhs = Bool::try_from(rhs).unwrap(); 132 let rhs = Bool::try_from(rhs)?;
106 Ok(Bool(runtime_thread_do(Box::new(move |r| { 133 Ok(Bool(runtime_thread_do(Box::new(move |r| {
107 r.solver_ctx().bool_or(&[lhs.0, rhs.0]) 134 r.solver_ctx().bool_or(&[lhs.0, rhs.0])
108 })))) 135 }))))
@@ -127,7 +154,7 @@ impl TryFrom<LuaValue<'_>> for Bool {
127 })))), 154 })))),
128 // Create a new bool from the borrow, since it might already be borrowed, with for ex. 155 // Create a new bool from the borrow, since it might already be borrowed, with for ex.
129 // b:eq(b) 156 // b:eq(b)
130 LuaValue::UserData(u) => Ok(Bool(u.borrow::<Bool>().unwrap().0)), 157 LuaValue::UserData(u) => Ok(Bool(u.borrow::<Bool>()?.0)),
131 _ => Err(LuaError::FromLuaConversionError { 158 _ => Err(LuaError::FromLuaConversionError {
132 from: "Value", 159 from: "Value",
133 to: "Bool", 160 to: "Bool",
@@ -137,11 +164,87 @@ impl TryFrom<LuaValue<'_>> for Bool {
137 } 164 }
138} 165}
139 166
167#[derive(Debug, Clone)]
168struct Point2D(CorePoint2D);
169
170impl LuaUserData for Point2D {
171 fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
172 fields.add_field_method_get("x", |_, this| Ok(Float(this.0.x())));
173 fields.add_field_method_get("y", |_, this| Ok(Float(this.0.y())));
174 }
175
176 fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
177 methods.add_method("eq", |_, this, other: LuaValue| {
178 let x = this.0.x();
179 let y = this.0.y();
180 let other: Point2D = other.try_into()?;
181 Ok(Bool(runtime_thread_do(Box::new(move |r| {
182 let solver = r.solver_ctx();
183 let x_constraint = solver.float_eq(x, other.0.x());
184 let y_constraint = solver.float_eq(y, other.0.y());
185 solver.bool_and(&[x_constraint, y_constraint])
186 }))))
187 });
188
189 methods.add_method("ne", |_, this, other: LuaValue| {
190 let x = this.0.x();
191 let y = this.0.y();
192 let other: Point2D = other.try_into()?;
193 Ok(Bool(runtime_thread_do(Box::new(move |r| {
194 let solver = r.solver_ctx();
195 let x_constraint = solver.float_ne(x, other.0.x());
196 let y_constraint = solver.float_ne(y, other.0.y());
197 solver.bool_or(&[x_constraint, y_constraint])
198 }))))
199 });
200
201 methods.add_method("man_dist", |_, this, other: LuaValue| {
202 let x = this.0.x();
203 let y = this.0.y();
204 let other: Point2D = other.try_into()?;
205 Ok(Float(runtime_thread_do(Box::new(move |r| {
206 let solver = r.solver_ctx();
207
208 let signed_x_dist = solver.float_sub(&[x, other.0.x()]);
209 let signed_y_dist = solver.float_sub(&[y, other.0.y()]);
210
211 let x_dist = solver.float_abs(signed_x_dist);
212 let y_dist = solver.float_abs(signed_y_dist);
213
214 solver.float_add(&[x_dist, y_dist])
215 }))))
216 });
217 }
218}
219
220impl TryFrom<LuaValue<'_>> for Point2D {
221 type Error = LuaError;
222
223 fn try_from(value: LuaValue<'_>) -> Result<Self, Self::Error> {
224 match value {
225 LuaValue::Table(table) => {
226 let x: Float = table.get::<_, LuaValue>("x")?.try_into()?;
227 let y: Float = table.get::<_, LuaValue>("y")?.try_into()?;
228 Ok(Point2D(CorePoint2D::new(x.0, y.0)))
229 }
230 // Create a new float from the borrow, since it might already be borrowed, with for ex.
231 // f:eq(f)
232 LuaValue::UserData(u) => Ok(Point2D(u.borrow::<Point2D>()?.0.clone())),
233 _ => Err(LuaError::FromLuaConversionError {
234 from: value.type_name(),
235 to: "Point2D",
236 message: Some("Only { x, y } tables are allowed".to_string()),
237 }),
238 }
239 }
240}
241
140#[derive(Clone, Debug)] 242#[derive(Clone, Debug)]
141struct FontDescription(CoreFontDescription); 243struct FontDescription(CoreFontDescription);
142impl LuaUserData for FontDescription { 244impl LuaUserData for FontDescription {
143 fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { 245 fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
144 fields.add_field_method_get("size", |_, this| Ok(Float(this.0.size))) 246 // fields.add_field_method_get("size", |_, this| Ok(Float(this.0.size)))
247 fields.add_field_method_get("size", |_, this| Ok(Float(dbg!(this.0.size))))
145 } 248 }
146} 249}
147 250
@@ -205,6 +308,7 @@ where
205 308
206trait HasContext { 309trait HasContext {
207 fn get_context(&self) -> &CoreShapeContext; 310 fn get_context(&self) -> &CoreShapeContext;
311 fn get_context_mut(&mut self) -> &mut CoreShapeContext;
208} 312}
209 313
210fn add_bound_fields<'lua, T, F>(fields: &mut F) 314fn add_bound_fields<'lua, T, F>(fields: &mut F)
@@ -214,24 +318,139 @@ where
214 F: LuaUserDataFields<'lua, T>, 318 F: LuaUserDataFields<'lua, T>,
215{ 319{
216 macro_rules! bound_field { 320 macro_rules! bound_field {
217 ($name: expr, $method: ident) => { 321 ($name: expr, $method: ident, $type: tt) => {
218 fields.add_field_method_get($name, |_, this| { 322 fields.add_field_method_get($name, |_, this| {
219 let bounds = this.get_context().bounds().clone(); 323 let bounds = this.get_context().bounds().clone();
220 Ok(Float(runtime_thread_do(Box::new(move |r| { 324 Ok($type(runtime_thread_do(Box::new(move |r| {
221 bounds.$method(r.solver_ctx()) 325 bounds.$method(r.solver_ctx())
222 })))) 326 }))))
223 }) 327 });
328
329 fields.add_field_method_set($name, |_, this, other: LuaValue| {
330 let bounds = this.get_context().bounds().clone();
331 let other: $type = other.try_into()?;
332 runtime_thread_do(Box::new(move |r| {
333 let solver = r.solver_ctx();
334 let lhs = bounds.$method(solver);
335 bound_field!(@check, solver, lhs, other, $type);
336 }));
337 Ok(())
338 });
339 };
340 (@check, $solver: ident, $lhs: ident, $rhs: ident, Float) => {
341 let constraint = $solver.float_eq($lhs, $rhs.0);
342 $solver.constrain(constraint);
343 };
344 (@check, $solver: ident, $lhs: ident, $rhs: ident, Point2D) => {
345 let x_constraint = $solver.float_eq($lhs.x(), $rhs.0.x());
346 let y_constraint = $solver.float_eq($lhs.y(), $rhs.0.y());
347 let constraint = $solver.bool_and(&[x_constraint, y_constraint]);
348 $solver.constrain(constraint);
224 }; 349 };
225 } 350 }
226 351
227 bound_field!("top", top); 352 bound_field!("top", top, Float);
228 bound_field!("left", left); 353 bound_field!("left", left, Float);
229 bound_field!("width", width); 354 bound_field!("width", width, Float);
230 bound_field!("height", height); 355 bound_field!("height", height, Float);
231 bound_field!("right", right); 356 bound_field!("right", right, Float);
232 bound_field!("bottom", bottom); 357 bound_field!("bottom", bottom, Float);
233 bound_field!("vert_center", vert_center); 358 bound_field!("vert_center", vert_center, Float);
234 bound_field!("horiz_center", horiz_center); 359 bound_field!("horiz_center", horiz_center, Float);
360
361 // TODO: two names: middle or center?
362 bound_field!("top_left", top_left, Point2D);
363 bound_field!("top_right", top_right, Point2D);
364 bound_field!("bottom_left", bottom_left, Point2D);
365 bound_field!("bottom_right", bottom_right, Point2D);
366 bound_field!("middle_left", middle_left, Point2D);
367 bound_field!("middle_right", middle_right, Point2D);
368 bound_field!("top_middle", top_middle, Point2D);
369 bound_field!("bottom_middle", bottom_middle, Point2D);
370 bound_field!("center", center, Point2D);
371}
372
373fn add_context_fields<'lua, T, F>(fields: &mut F)
374where
375 T: LuaUserData,
376 T: HasContext,
377 F: LuaUserDataFields<'lua, T>,
378{
379 // Everything in top-level namespace, else we need struct with interior mutability?
380 fields.add_field_method_get("stroke_color", |lua, this| {
381 Ok(match this.get_context().stroke().pattern() {
382 diaphragm_core::styles::Pattern::Solid(color) => {
383 LuaValue::String(lua.create_string(&color.to_hex())?)
384 }
385 diaphragm_core::styles::Pattern::None => LuaValue::Nil,
386 _ => todo!(),
387 })
388 });
389
390 fields.add_field_method_set("stroke_color", |_, this, value: Option<String>| {
391 let context = this.get_context_mut();
392 if let Some(color) = value {
393 context
394 .stroke_mut()
395 .set_pattern(Pattern::Solid(Color::from_hex(&color)));
396 } else {
397 context.stroke_mut().set_pattern(Pattern::None);
398 }
399 Ok(())
400 });
401
402 fields.add_field_method_get("stroke_width", |_, this| {
403 Ok(this.get_context().stroke().line_width().map(Float))
404 });
405
406 fields.add_field_method_set("stroke_width", |_, this, value: LuaValue| {
407 let other: Float = match value {
408 LuaNil => {
409 this.get_context_mut().stroke_mut().set_line_width(None);
410 return Ok(());
411 }
412 other => other.try_into()?,
413 };
414
415 let stroke_width = match this.get_context().stroke().line_width() {
416 Some(f) => f,
417 None => {
418 this.get_context_mut()
419 .stroke_mut()
420 .set_line_width(Some(other.0));
421 return Ok(());
422 }
423 };
424
425 runtime_thread_do(Box::new(move |r| {
426 let solver = r.solver_ctx();
427 let constraint = solver.float_eq(stroke_width, other.0);
428 solver.constrain(constraint);
429 }));
430 Ok(())
431 });
432
433 fields.add_field_method_get("fill_color", |lua, this| {
434 Ok(match this.get_context().fill().pattern() {
435 diaphragm_core::styles::Pattern::Solid(color) => {
436 LuaValue::String(lua.create_string(&color.to_hex())?)
437 }
438 diaphragm_core::styles::Pattern::None => LuaValue::Nil,
439 _ => todo!(),
440 })
441 });
442
443 fields.add_field_method_set("fill_color", |_, this, value: Option<String>| {
444 let context = this.get_context_mut();
445 if let Some(color) = value {
446 context
447 .fill_mut()
448 .set_pattern(Pattern::Solid(Color::from_hex(&color)));
449 } else {
450 context.fill_mut().set_pattern(Pattern::None);
451 }
452 Ok(())
453 });
235} 454}
236 455
237impl<T: Into<CoreShape>> From<Drawable<T>> for CoreDrawable { 456impl<T: Into<CoreShape>> From<Drawable<T>> for CoreDrawable {
@@ -262,6 +481,10 @@ impl HasContext for Text {
262 fn get_context(&self) -> &CoreShapeContext { 481 fn get_context(&self) -> &CoreShapeContext {
263 &self.0.context 482 &self.0.context
264 } 483 }
484
485 fn get_context_mut(&mut self) -> &mut CoreShapeContext {
486 &mut self.0.context
487 }
265} 488}
266 489
267fn text(_: &Lua, params: LuaTable) -> LuaResult<Text> { 490fn text(_: &Lua, params: LuaTable) -> LuaResult<Text> {
@@ -277,6 +500,8 @@ fn text(_: &Lua, params: LuaTable) -> LuaResult<Text> {
277 let font_2 = font.0.clone(); 500 let font_2 = font.0.clone();
278 let context_2 = context.clone(); 501 let context_2 = context.clone();
279 502
503 // TODO: this shouldn't be here, this should be an innate property of Text. Move the
504 // Drawable<T> struct in core?
280 runtime_thread_do(Box::new(move |r| { 505 runtime_thread_do(Box::new(move |r| {
281 let (text_width, text_height) = r.renderer().text_extents(&content_2, &font_2); 506 let (text_width, text_height) = r.renderer().text_extents(&content_2, &font_2);
282 let solver = r.solver_ctx(); 507 let solver = r.solver_ctx();
@@ -317,6 +542,7 @@ impl LuaUserData for Rectangle {
317 542
318 fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { 543 fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
319 add_bound_fields(fields); 544 add_bound_fields(fields);
545 add_context_fields(fields);
320 } 546 }
321} 547}
322 548
@@ -324,6 +550,10 @@ impl HasContext for Rectangle {
324 fn get_context(&self) -> &CoreShapeContext { 550 fn get_context(&self) -> &CoreShapeContext {
325 &self.0.context 551 &self.0.context
326 } 552 }
553
554 fn get_context_mut(&mut self) -> &mut CoreShapeContext {
555 &mut self.0.context
556 }
327} 557}
328 558
329fn rectangle(_: &Lua, _params: ()) -> LuaResult<Rectangle> { 559fn rectangle(_: &Lua, _params: ()) -> LuaResult<Rectangle> {
@@ -333,6 +563,91 @@ fn rectangle(_: &Lua, _params: ()) -> LuaResult<Rectangle> {
333 })) 563 }))
334} 564}
335 565
566#[derive(Debug, Clone)]
567struct StraightPath(Drawable<CoreStraightPath>);
568impl LuaUserData for StraightPath {
569 fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
570 methods.add_method("draw", |_, this, _params: ()| {
571 let drawable = this.0.clone().into();
572 runtime_thread_do(Box::new(|r| {
573 r.add_drawable(drawable);
574 }));
575 Ok(())
576 })
577 }
578
579 fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
580 add_bound_fields(fields);
581 add_context_fields(fields);
582 }
583}
584
585impl HasContext for StraightPath {
586 fn get_context(&self) -> &CoreShapeContext {
587 &self.0.context
588 }
589
590 fn get_context_mut(&mut self) -> &mut CoreShapeContext {
591 &mut self.0.context
592 }
593}
594
595impl TryFrom<LuaValue<'_>> for StraightPath {
596 type Error = LuaError;
597
598 fn try_from(value: LuaValue) -> Result<Self, Self::Error> {
599 match value {
600 LuaValue::Table(t) => {
601 let points = t
602 .sequence_values::<LuaValue>()
603 .map(|el| Ok(Point2D::try_from(el?)?.0))
604 .collect::<LuaResult<Vec<_>>>()?;
605 Ok(StraightPath(Drawable {
606 shape: CoreStraightPath::new(points),
607 context: new_shape_context(),
608 }))
609 }
610 // Create a new float from the borrow, since it might already be borrowed, with for ex.
611 // f:eq(f)
612 LuaValue::UserData(u) => Ok(StraightPath(u.borrow::<StraightPath>()?.0.clone())),
613 _ => Err(LuaError::FromLuaConversionError {
614 from: value.type_name(),
615 to: "StraightPath",
616 message: Some("Only a list of points are allowed".to_string()),
617 }),
618 }
619 }
620}
621
622// TODO: add bounds constraints before drawing
623fn straight_path(_: &Lua, params: LuaTable) -> LuaResult<StraightPath> {
624 let points: LuaValue = params.get("points")?;
625 points.try_into()
626}
627
628// It just has a context for bounds, the rest is handled manually in Lua
629#[derive(Debug, Clone)]
630struct ComplexShape(CoreShapeContext);
631impl LuaUserData for ComplexShape {
632 fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
633 add_bound_fields(fields);
634 }
635}
636
637impl HasContext for ComplexShape {
638 fn get_context(&self) -> &CoreShapeContext {
639 &self.0
640 }
641
642 fn get_context_mut(&mut self) -> &mut CoreShapeContext {
643 &mut self.0
644 }
645}
646
647fn complex_shape(_: &Lua, _params: ()) -> LuaResult<ComplexShape> {
648 Ok(ComplexShape(new_shape_context()))
649}
650
336thread_local! { 651thread_local! {
337 static SENDER: RefCell<Option<SyncSender<Message>>> = RefCell::new(None); 652 static SENDER: RefCell<Option<SyncSender<Message>>> = RefCell::new(None);
338 static REPLY: RefCell<Option<Receiver<Reply>>> = RefCell::new(None); 653 static REPLY: RefCell<Option<Receiver<Reply>>> = RefCell::new(None);
@@ -387,7 +702,7 @@ fn draw(_: &Lua, params: LuaTable) -> LuaResult<()> {
387 // So.... The Z3 stuff isn't Send and contains lifetimes, so we can't store them in global 702 // So.... The Z3 stuff isn't Send and contains lifetimes, so we can't store them in global
388 // variables or convert them to Lua. Solution: handle everything in a specific thread, and 703 // variables or convert them to Lua. Solution: handle everything in a specific thread, and
389 // communicate through a channel. 704 // communicate through a channel.
390 thread::scope(|s| { 705 thread::scope(|s| -> LuaResult<()> {
391 let (message_sender, message_receiver) = sync_channel(1); 706 let (message_sender, message_receiver) = sync_channel(1);
392 let (reply_sender, reply_receiver) = sync_channel(1); 707 let (reply_sender, reply_receiver) = sync_channel(1);
393 708
@@ -424,13 +739,13 @@ fn draw(_: &Lua, params: LuaTable) -> LuaResult<()> {
424 runtime.render(); 739 runtime.render();
425 }); 740 });
426 741
427 let content: LuaValue = params.get("content").unwrap(); 742 let content: LuaValue = params.get("content")?;
428 let _output: LuaTable = params.get("output").unwrap(); 743 let _output: LuaTable = params.get("output")?;
429 744
430 match content { 745 match content {
431 LuaValue::Table(table) => { 746 // TODO: this doesn't stop the runtime thread?
432 let () = table.call_method("draw", ()).unwrap(); 747 LuaValue::Table(table) => table.call_method("draw", ()).unwrap(),
433 } 748
434 // TODO: switch to enum 749 // TODO: switch to enum
435 // LuaValue::UserData(user_data) => { 750 // LuaValue::UserData(user_data) => {
436 // if user_data.is::<Text>() { 751 // if user_data.is::<Text>() {
@@ -455,9 +770,9 @@ fn draw(_: &Lua, params: LuaTable) -> LuaResult<()> {
455 SENDER.with(|s| { 770 SENDER.with(|s| {
456 *s.borrow_mut() = None; 771 *s.borrow_mut() = None;
457 }); 772 });
458 });
459 773
460 Ok(()) 774 Ok(())
775 })
461} 776}
462 777
463#[mlua::lua_module] 778#[mlua::lua_module]
@@ -471,6 +786,13 @@ fn libdiaphragm(lua: &Lua) -> LuaResult<LuaTable> {
471 786
472 exports.set("rectangle", lua.create_function(rectangle)?)?; 787 exports.set("rectangle", lua.create_function(rectangle)?)?;
473 788
789 exports.set("straight_path", lua.create_function(straight_path)?)?;
790
791 exports.set("complex_shape", lua.create_function(complex_shape)?)?;
792
793 exports.set("float_max", lua.create_function(float_max)?)?;
794 exports.set("float_min", lua.create_function(float_min)?)?;
795
474 exports.set("constrain", lua.create_function(constrain)?)?; 796 exports.set("constrain", lua.create_function(constrain)?)?;
475 exports.set("draw", lua.create_function(draw)?)?; 797 exports.set("draw", lua.create_function(draw)?)?;
476 798
@@ -478,25 +800,20 @@ fn libdiaphragm(lua: &Lua) -> LuaResult<LuaTable> {
478 // --- 800 // ---
479 801
480 let float_metatable = lua 802 let float_metatable = lua
481 .create_userdata(Float(CoreFloat::Fixed(0.))) 803 .create_userdata(Float(CoreFloat::Fixed(0.)))?
482 .unwrap() 804 .get_metatable()?;
483 .get_metatable()
484 .unwrap();
485 805
486 macro_rules! float_metamethod { 806 macro_rules! float_metamethod {
487 ($method: ident, $op: ident) => { 807 ($method: ident, $op: ident) => {
488 float_metatable 808 float_metatable.set(
489 .set( 809 LuaMetaMethod::$method,
490 LuaMetaMethod::$method, 810 lua.create_function(|_lua, (lhs, rhs): (Float, LuaValue)| {
491 lua.create_function(|_lua, (lhs, rhs): (Float, LuaValue)| { 811 let rhs = Float::try_from(rhs)?;
492 let rhs = Float::try_from(rhs).unwrap(); 812 Ok(Float(runtime_thread_do(Box::new(move |r| {
493 Ok(Float(runtime_thread_do(Box::new(move |r| { 813 r.solver_ctx().$op(&[lhs.0, rhs.0])
494 r.solver_ctx().$op(&[lhs.0, rhs.0]) 814 }))))
495 })))) 815 })?,
496 }) 816 )?;
497 .unwrap(),
498 )
499 .unwrap();
500 }; 817 };
501 } 818 }
502 819
@@ -504,51 +821,67 @@ fn libdiaphragm(lua: &Lua) -> LuaResult<LuaTable> {
504 float_metamethod!(Sub, float_sub); 821 float_metamethod!(Sub, float_sub);
505 float_metamethod!(Mul, float_mul); 822 float_metamethod!(Mul, float_mul);
506 823
507 float_metatable 824 float_metatable.set(
508 .set( 825 LuaMetaMethod::Div,
509 LuaMetaMethod::Div, 826 lua.create_function(|_lua, (lhs, rhs): (Float, LuaValue)| {
510 lua.create_function(|_lua, (lhs, rhs): (Float, LuaValue)| { 827 let rhs = Float::try_from(rhs)?;
511 let rhs = Float::try_from(rhs).unwrap(); 828 Ok(Float(runtime_thread_do(Box::new(move |r| {
512 Ok(Float(runtime_thread_do(Box::new(move |r| { 829 r.solver_ctx().float_div(lhs.0, rhs.0)
513 r.solver_ctx().float_div(lhs.0, rhs.0) 830 }))))
514 })))) 831 })?,
515 }) 832 )?;
516 .unwrap(), 833
517 ) 834 float_metatable.set(
518 .unwrap(); 835 LuaMetaMethod::Unm,
519 836 lua.create_function(|_lua, f: Float| {
520 float_metatable 837 Ok(Float(runtime_thread_do(Box::new(move |r| {
521 .set( 838 r.solver_ctx().float_neg(f.0)
522 LuaMetaMethod::Unm, 839 }))))
523 lua.create_function(|_lua, f: Float| { 840 })?,
524 Ok(Float(runtime_thread_do(Box::new(move |r| { 841 )?;
525 r.solver_ctx().float_neg(f.0)
526 }))))
527 })
528 .unwrap(),
529 )
530 .unwrap();
531 842
532 // Not the operators `==`, `!=`, `<`, `<=`, `>`, and `>=` because Lua converts result to bool 843 // Not the operators `==`, `!=`, `<`, `<=`, `>`, and `>=` because Lua converts result to bool
533 844
534 let bool_metatable = lua 845 let bool_metatable = lua
535 .create_userdata(Bool(CoreBool::new(VariableHandle::new(0)))) 846 .create_userdata(Bool(CoreBool::new(VariableHandle::new(0))))?
536 .unwrap() 847 .get_metatable()?;
537 .get_metatable() 848
538 .unwrap(); 849 bool_metatable.set(
539 850 LuaMetaMethod::Shl,
540 bool_metatable 851 lua.create_function(|_lua, (lhs, rhs): (Bool, LuaValue)| {
541 .set( 852 let rhs = Bool::try_from(rhs)?;
542 LuaMetaMethod::Shl, 853 Ok(Bool(runtime_thread_do(Box::new(move |r| {
543 lua.create_function(|_lua, (lhs, rhs): (Bool, LuaValue)| { 854 r.solver_ctx().bool_implies(lhs.0, rhs.0)
544 let rhs = Bool::try_from(rhs).unwrap(); 855 }))))
545 Ok(Bool(runtime_thread_do(Box::new(move |r| { 856 })?,
546 r.solver_ctx().bool_implies(lhs.0, rhs.0) 857 )?;
547 })))) 858
548 }) 859 let point_metatable = lua
549 .unwrap(), 860 .create_userdata(Point2D(CorePoint2D::new(
550 ) 861 CoreFloat::Fixed(0.),
551 .unwrap(); 862 CoreFloat::Fixed(0.),
863 )))?
864 .get_metatable()?;
865
866 macro_rules! point_metamethod {
867 ($method: ident, $op: ident) => {
868 point_metatable.set(
869 LuaMetaMethod::$method,
870 lua.create_function(|_lua, (lhs, rhs): (Point2D, LuaValue)| {
871 let rhs = Point2D::try_from(rhs)?;
872 Ok(Point2D(runtime_thread_do(Box::new(move |r| {
873 let solver = r.solver_ctx();
874 let new_x = solver.$op(&[lhs.0.x(), rhs.0.x()]);
875 let new_y = solver.$op(&[lhs.0.y(), rhs.0.y()]);
876 CorePoint2D::new(new_x, new_y)
877 }))))
878 })?,
879 )?;
880 };
881 }
882
883 point_metamethod!(Add, float_add);
884 point_metamethod!(Sub, float_sub);
552 885
553 Ok(exports) 886 Ok(exports)
554} 887}