diff options
Diffstat (limited to 'lua-bindings/src/lib.rs')
-rw-r--r-- | lua-bindings/src/lib.rs | 505 |
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 | |||
1 | use std::{ | 3 | use std::{ |
2 | any::Any, | 4 | any::Any, |
3 | cell::RefCell, | 5 | cell::RefCell, |
@@ -7,10 +9,14 @@ use std::{ | |||
7 | 9 | ||
8 | use diaphragm_cairo_renderer::CairoRenderer; | 10 | use diaphragm_cairo_renderer::CairoRenderer; |
9 | use diaphragm_core::{ | 11 | use 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 | }; |
16 | use diaphragm_z3_solver::{z3, Z3Context}; | 22 | use 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 | ||
88 | fn 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 | |||
95 | fn 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)] |
76 | struct Bool(CoreBool); | 103 | struct 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)] | ||
168 | struct Point2D(CorePoint2D); | ||
169 | |||
170 | impl 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 | |||
220 | impl 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)] |
141 | struct FontDescription(CoreFontDescription); | 243 | struct FontDescription(CoreFontDescription); |
142 | impl LuaUserData for FontDescription { | 244 | impl 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 | ||
206 | trait HasContext { | 309 | trait 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 | ||
210 | fn add_bound_fields<'lua, T, F>(fields: &mut F) | 314 | fn 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 | |||
373 | fn add_context_fields<'lua, T, F>(fields: &mut F) | ||
374 | where | ||
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 | ||
237 | impl<T: Into<CoreShape>> From<Drawable<T>> for CoreDrawable { | 456 | impl<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 | ||
267 | fn text(_: &Lua, params: LuaTable) -> LuaResult<Text> { | 490 | fn 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 | ||
329 | fn rectangle(_: &Lua, _params: ()) -> LuaResult<Rectangle> { | 559 | fn 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)] | ||
567 | struct StraightPath(Drawable<CoreStraightPath>); | ||
568 | impl 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 | |||
585 | impl 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 | |||
595 | impl 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 | ||
623 | fn 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)] | ||
630 | struct ComplexShape(CoreShapeContext); | ||
631 | impl LuaUserData for ComplexShape { | ||
632 | fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { | ||
633 | add_bound_fields(fields); | ||
634 | } | ||
635 | } | ||
636 | |||
637 | impl 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 | |||
647 | fn complex_shape(_: &Lua, _params: ()) -> LuaResult<ComplexShape> { | ||
648 | Ok(ComplexShape(new_shape_context())) | ||
649 | } | ||
650 | |||
336 | thread_local! { | 651 | thread_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 | } |