summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMinijackson <minijackson@riseup.net>2023-02-03 19:59:43 +0100
committerMinijackson <minijackson@riseup.net>2023-02-03 19:59:43 +0100
commit82ffe3187a32bad4ecca0736882a23793a800822 (patch)
tree2d3ab9231b76dadcf2ab69b27053df6cc66bead1
parentc8bb3e4a24ca1ca6ec1a57c9e8cd8655483b3eb0 (diff)
downloaddiaphragm-82ffe3187a32bad4ecca0736882a23793a800822.tar.gz
diaphragm-82ffe3187a32bad4ecca0736882a23793a800822.zip
add support for images, and some chores
- SVG files not yet supported - Remove dead commented code - Add inherent constraints for text, path, and images - Formatting
-rw-r--r--Cargo.lock60
-rw-r--r--cairo-renderer/Cargo.toml1
-rw-r--r--cairo-renderer/src/lib.rs43
-rw-r--r--core/src/core_shapes.rs174
-rw-r--r--core/src/rendering.rs29
-rw-r--r--core/src/runtime.rs107
-rw-r--r--core/src/solving.rs2
-rw-r--r--flake.nix2
-rw-r--r--lua-bindings/diaphragm.lua11
-rw-r--r--lua-bindings/src/lib.rs84
10 files changed, 328 insertions, 185 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6fd149f..444470e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -177,6 +177,7 @@ version = "0.1.0"
177dependencies = [ 177dependencies = [
178 "cairo-rs", 178 "cairo-rs",
179 "diaphragm-core", 179 "diaphragm-core",
180 "gdk",
180 "pango", 181 "pango",
181 "pangocairo", 182 "pangocairo",
182] 183]
@@ -292,6 +293,65 @@ dependencies = [
292] 293]
293 294
294[[package]] 295[[package]]
296name = "gdk"
297version = "0.16.2"
298source = "registry+https://github.com/rust-lang/crates.io-index"
299checksum = "aa9cb33da481c6c040404a11f8212d193889e9b435db2c14fd86987f630d3ce1"
300dependencies = [
301 "bitflags",
302 "cairo-rs",
303 "gdk-pixbuf",
304 "gdk-sys",
305 "gio",
306 "glib",
307 "libc",
308 "pango",
309]
310
311[[package]]
312name = "gdk-pixbuf"
313version = "0.16.7"
314source = "registry+https://github.com/rust-lang/crates.io-index"
315checksum = "c3578c60dee9d029ad86593ed88cb40f35c1b83360e12498d055022385dd9a05"
316dependencies = [
317 "bitflags",
318 "gdk-pixbuf-sys",
319 "gio",
320 "glib",
321 "libc",
322]
323
324[[package]]
325name = "gdk-pixbuf-sys"
326version = "0.16.3"
327source = "registry+https://github.com/rust-lang/crates.io-index"
328checksum = "3092cf797a5f1210479ea38070d9ae8a5b8e9f8f1be9f32f4643c529c7d70016"
329dependencies = [
330 "gio-sys",
331 "glib-sys",
332 "gobject-sys",
333 "libc",
334 "system-deps",
335]
336
337[[package]]
338name = "gdk-sys"
339version = "0.16.0"
340source = "registry+https://github.com/rust-lang/crates.io-index"
341checksum = "d76354f97a913e55b984759a997b693aa7dc71068c9e98bcce51aa167a0a5c5a"
342dependencies = [
343 "cairo-sys-rs",
344 "gdk-pixbuf-sys",
345 "gio-sys",
346 "glib-sys",
347 "gobject-sys",
348 "libc",
349 "pango-sys",
350 "pkg-config",
351 "system-deps",
352]
353
354[[package]]
295name = "gio" 355name = "gio"
296version = "0.16.7" 356version = "0.16.7"
297source = "registry+https://github.com/rust-lang/crates.io-index" 357source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/cairo-renderer/Cargo.toml b/cairo-renderer/Cargo.toml
index ebef7d5..53f95be 100644
--- a/cairo-renderer/Cargo.toml
+++ b/cairo-renderer/Cargo.toml
@@ -10,5 +10,6 @@ edition = "2021"
10diaphragm-core = { path = "../core" } 10diaphragm-core = { path = "../core" }
11 11
12cairo-rs = { version = "0.16", features = ["svg", "png"] } 12cairo-rs = { version = "0.16", features = ["svg", "png"] }
13gdk = "0.16"
13pango = "0.16" 14pango = "0.16"
14pangocairo = "0.16" 15pangocairo = "0.16"
diff --git a/cairo-renderer/src/lib.rs b/cairo-renderer/src/lib.rs
index 40d24d1..5a730f4 100644
--- a/cairo-renderer/src/lib.rs
+++ b/cairo-renderer/src/lib.rs
@@ -1,3 +1,10 @@
1use std::{
2 collections::HashMap,
3 path::{Path, PathBuf},
4};
5
6use gdk::{gdk_pixbuf::Pixbuf, prelude::GdkContextExt};
7
1use diaphragm_core::{ 8use diaphragm_core::{
2 styles::{DefinedDashStyle, Pattern}, 9 styles::{DefinedDashStyle, Pattern},
3 text::{DefinedFontDescription, FontDescription}, 10 text::{DefinedFontDescription, FontDescription},
@@ -6,6 +13,7 @@ use diaphragm_core::{
6 13
7pub struct CairoRenderer { 14pub struct CairoRenderer {
8 ctx: cairo::Context, 15 ctx: cairo::Context,
16 loaded_images: HashMap<PathBuf, Pixbuf>,
9} 17}
10 18
11impl CairoRenderer { 19impl CairoRenderer {
@@ -15,7 +23,17 @@ impl CairoRenderer {
15 let surface = cairo::SvgSurface::for_stream(1920., 1080., std::io::sink()).unwrap(); 23 let surface = cairo::SvgSurface::for_stream(1920., 1080., std::io::sink()).unwrap();
16 let ctx = cairo::Context::new(&surface).unwrap(); 24 let ctx = cairo::Context::new(&surface).unwrap();
17 25
18 Self { ctx } 26 Self {
27 ctx,
28 loaded_images: HashMap::new(),
29 }
30 }
31
32 fn get_image(&mut self, path: &Path) -> &Pixbuf {
33 let path = path.to_owned();
34 self.loaded_images
35 .entry(path.clone())
36 .or_insert_with(|| Pixbuf::from_file(path).unwrap())
19 } 37 }
20} 38}
21 39
@@ -102,9 +120,6 @@ impl Renderer for CairoRenderer {
102 extents.width() as f64 / TEST_ABSOLUTE_SIZE, 120 extents.width() as f64 / TEST_ABSOLUTE_SIZE,
103 extents.height() as f64 / TEST_ABSOLUTE_SIZE, 121 extents.height() as f64 / TEST_ABSOLUTE_SIZE,
104 ) 122 )
105
106 //let (width, height) = layout.get_pixel_size();
107 //(width as f64, height as f64)
108 } 123 }
109 124
110 fn show_text(&mut self, text: &str, font: &DefinedFontDescription) { 125 fn show_text(&mut self, text: &str, font: &DefinedFontDescription) {
@@ -115,7 +130,25 @@ impl Renderer for CairoRenderer {
115 layout.set_font_description(Some(&font_desc)); 130 layout.set_font_description(Some(&font_desc));
116 layout.set_markup(text); 131 layout.set_markup(text);
117 132
118 //self.ctx.set_font_size(dbg!(font.size));
119 pangocairo::show_layout(&self.ctx, &layout); 133 pangocairo::show_layout(&self.ctx, &layout);
120 } 134 }
135
136 fn show_image(&mut self, path: &Path, x: f64, y: f64, width: f64, height: f64) {
137 let image = self.get_image(path).clone();
138
139 self.ctx.save().unwrap();
140
141 self.ctx
142 .scale(width / image.width() as f64, height / image.height() as f64);
143
144 self.ctx.set_source_pixbuf(&image, x, y);
145 self.ctx.paint().unwrap();
146
147 self.ctx.restore().unwrap();
148 }
149
150 fn geometry_for_image(&mut self, path: &Path) -> (f64, f64) {
151 let image = self.get_image(path);
152 (image.width() as f64, image.height() as f64)
153 }
121} 154}
diff --git a/core/src/core_shapes.rs b/core/src/core_shapes.rs
index e6d79c9..c380870 100644
--- a/core/src/core_shapes.rs
+++ b/core/src/core_shapes.rs
@@ -1,7 +1,12 @@
1use crate::types::{CoreShapeContext, DefinedCoreShapeContext, DefinedPoint2D, Point2D}; 1use std::path::PathBuf;
2 2
3pub use super::text::{DefinedText, Text}; 3use crate::{
4 text::{DefinedText, Text},
5 types::{CoreShapeContext, DefinedCoreShapeContext, DefinedPoint2D, Float, Point2D},
6 Renderer, SolverContext,
7};
4 8
9#[derive(Debug, Clone)]
5pub struct CoreDrawable { 10pub struct CoreDrawable {
6 pub(crate) shape: CoreShape, 11 pub(crate) shape: CoreShape,
7 pub(crate) context: CoreShapeContext, 12 pub(crate) context: CoreShapeContext,
@@ -11,12 +16,91 @@ impl CoreDrawable {
11 pub fn new(shape: CoreShape, context: CoreShapeContext) -> Self { 16 pub fn new(shape: CoreShape, context: CoreShapeContext) -> Self {
12 Self { shape, context } 17 Self { shape, context }
13 } 18 }
19
20 // TODO: I don't like this design
21 // TODO: plus, having an enum for CoreShape could be bad since types have
22 // various sizes
23 pub(crate) fn inherent_constraints(
24 &self,
25 solver: &mut dyn SolverContext,
26 renderer: &mut dyn Renderer,
27 ) {
28 match &self.shape {
29 CoreShape::Text(text) => {
30 let (text_width, text_height) = renderer.text_extents(&text.content, &text.font);
31
32 let calculated_width =
33 solver.float_mul(&[Float::Fixed(text_width), text.font.size]);
34 let bounds_width = self.context.bounds().width(solver);
35 let width_constraint = solver.float_eq(bounds_width, calculated_width);
36 solver.constrain(width_constraint);
37
38 let calculated_height =
39 solver.float_mul(&[Float::Fixed(text_height), text.font.size]);
40 let bounds_height = self.context.bounds().height(solver);
41 let height_constraint = solver.float_eq(bounds_height, calculated_height);
42 solver.constrain(height_constraint);
43 }
44 CoreShape::StraightPath(path) => {
45 let (all_x, all_y): (Vec<_>, Vec<_>) =
46 path.points.iter().map(|p| (p.x, p.y)).unzip();
47
48 let min_x = solver.float_min(&all_x);
49 let max_x = solver.float_max(&all_x);
50 let min_y = solver.float_min(&all_y);
51 let max_y = solver.float_max(&all_y);
52
53 let bounds_top = self.context.bounds().top(solver);
54 let bounds_bottom = self.context.bounds().bottom(solver);
55 let bounds_left = self.context.bounds().left(solver);
56 let bounds_right = self.context.bounds().right(solver);
57
58 let top_constraint = solver.float_eq(bounds_top, min_x);
59 solver.constrain(top_constraint);
60
61 let bottom_constraint = solver.float_eq(bounds_bottom, max_x);
62 solver.constrain(bottom_constraint);
63
64 let left_constraint = solver.float_eq(bounds_left, min_y);
65 solver.constrain(left_constraint);
66
67 let right_constraint = solver.float_eq(bounds_right, max_y);
68 solver.constrain(right_constraint);
69 }
70 CoreShape::Image(image) => {
71 if !image.keep_aspect_ratio {
72 return;
73 }
74
75 let scale_x = solver.new_free_float();
76 let scale_y = solver.new_free_float();
77
78 let constraint = solver.float_eq(scale_x, scale_y);
79 solver.constrain(constraint);
80
81 let (orig_width, orig_height) = renderer.geometry_for_image(&image.path);
82
83 let orig_width_scaled = solver.float_mul(&[scale_x, Float::Fixed(orig_width)]);
84 let width_constraint =
85 solver.float_eq(self.context.bounds().width, orig_width_scaled);
86 solver.constrain(width_constraint);
87
88 let orig_height_scaled = solver.float_mul(&[scale_y, Float::Fixed(orig_height)]);
89 let height_constraint =
90 solver.float_eq(self.context.bounds().height, orig_height_scaled);
91 solver.constrain(height_constraint);
92 }
93 _ => (),
94 }
95 }
14} 96}
15 97
98#[derive(Debug, Clone)]
16pub enum CoreShape { 99pub enum CoreShape {
17 Rectangle(Rectangle), 100 Rectangle(Rectangle),
18 Text(Text), 101 Text(Text),
19 StraightPath(StraightPath), 102 StraightPath(StraightPath),
103 Image(Image),
20} 104}
21 105
22impl From<Rectangle> for CoreShape { 106impl From<Rectangle> for CoreShape {
@@ -37,6 +121,12 @@ impl From<StraightPath> for CoreShape {
37 } 121 }
38} 122}
39 123
124impl From<Image> for CoreShape {
125 fn from(image: Image) -> Self {
126 CoreShape::Image(image)
127 }
128}
129
40pub struct DefinedCoreDrawable { 130pub struct DefinedCoreDrawable {
41 pub(crate) shape: DefinedCoreShape, 131 pub(crate) shape: DefinedCoreShape,
42 pub(crate) context: DefinedCoreShapeContext, 132 pub(crate) context: DefinedCoreShapeContext,
@@ -46,72 +136,12 @@ pub enum DefinedCoreShape {
46 Rectangle(Rectangle), 136 Rectangle(Rectangle),
47 Text(DefinedText), 137 Text(DefinedText),
48 StraightPath(DefinedStraightPath), 138 StraightPath(DefinedStraightPath),
139 Image(Image),
49} 140}
50 141
51/*
52pub trait CoreShape {
53 fn constrain(
54 &self,
55 _context: &ShapeContext,
56 _solver: &mut dyn SolverContext,
57 _renderer: &dyn Renderer,
58 ) {
59 }
60 fn to_render(&self, model: &dyn SolverModel) -> Option<Box<dyn Render>>;
61}
62
63impl<T: CoreShape + Clone + 'static> ComplexShape for T {
64 fn as_core_shape(&self) -> Option<&dyn CoreShape> {
65 Some(self)
66 }
67
68 fn draw(&self, _context: &ShapeContext, _solver: &mut dyn SolverContext) -> DrawResult {
69 panic!("Tried to decompose core shape")
70 }
71}
72*/
73
74#[derive(Copy, Clone, Debug, Default)] 142#[derive(Copy, Clone, Debug, Default)]
75pub struct Rectangle {} 143pub struct Rectangle {}
76 144
77/*
78impl CoreShape for Rectangle {
79 fn to_render(&self, _model: &dyn SolverModel) -> Option<Box<dyn Render>> {
80 Some(Box::new(*self))
81 }
82}
83*/
84
85// TODO: re-enable this in some way
86/*
87impl CoreShape for Text {
88 fn constrain(
89 &self,
90 context: &ShapeContext,
91 solver: &mut dyn SolverContext,
92 renderer: &dyn Renderer,
93 ) {
94 let height_constraint = solver.float_eq(context.bounds.height, self.font.size);
95 solver.constrain(height_constraint);
96
97 // TODO: handle multiline
98 let (width, height) = renderer.text_extents(&self.content, &self.font);
99 dbg!(height);
100
101 let scale = solver.float_div(self.font.size, Float::Fixed(height));
102
103 let calculated_width = solver.float_mul(&[Float::Fixed(width), scale]);
104 let width_constraint = solver.float_eq(context.bounds.width, calculated_width);
105 solver.constrain(width_constraint);
106 }
107
108 fn to_render(&self, model: &dyn SolverModel) -> Option<Box<dyn Render>> {
109 self.fixate(model)
110 .map(|path| -> Box<dyn Render> { Box::new(path) })
111 }
112}
113*/
114
115#[derive(Clone, Debug, Default)] 145#[derive(Clone, Debug, Default)]
116pub struct StraightPath { 146pub struct StraightPath {
117 pub(crate) points: Vec<Point2D>, 147 pub(crate) points: Vec<Point2D>,
@@ -127,11 +157,17 @@ pub struct DefinedStraightPath {
127 pub(crate) points: Vec<DefinedPoint2D>, 157 pub(crate) points: Vec<DefinedPoint2D>,
128} 158}
129 159
130/* 160#[derive(Clone, Debug, Default)]
131impl CoreShape for StraightPath { 161pub struct Image {
132 fn to_render(&self, model: &dyn SolverModel) -> Option<Box<dyn Render>> { 162 pub(crate) path: PathBuf,
133 self.fixate(model) 163 pub(crate) keep_aspect_ratio: bool,
134 .map(|path| -> Box<dyn Render> { Box::new(path) }) 164}
165
166impl Image {
167 pub fn new(path: PathBuf, keep_aspect_ratio: bool) -> Self {
168 Self {
169 path,
170 keep_aspect_ratio,
171 }
135 } 172 }
136} 173}
137*/
diff --git a/core/src/rendering.rs b/core/src/rendering.rs
index 73daa2a..9c719c4 100644
--- a/core/src/rendering.rs
+++ b/core/src/rendering.rs
@@ -1,7 +1,11 @@
1use super::core_shapes::*; 1use std::path::Path;
2use super::styles::{DefinedDashStyle, DefinedStrokeStyle, FillStyle, Pattern}; 2
3use super::text::{DefinedFontDescription, FontDescription}; 3use crate::{
4use super::types::DefinedCoreShapeContext; 4 core_shapes::*,
5 styles::{DefinedDashStyle, DefinedStrokeStyle, FillStyle, Pattern},
6 text::{DefinedFontDescription, DefinedText, FontDescription},
7 types::DefinedCoreShapeContext,
8};
5 9
6pub trait Renderer { 10pub trait Renderer {
7 // Must be called once before any drawing happens 11 // Must be called once before any drawing happens
@@ -19,6 +23,9 @@ pub trait Renderer {
19 // For a font of size 1. 23 // For a font of size 1.
20 fn text_extents(&self, text: &str, font: &FontDescription) -> (f64, f64); 24 fn text_extents(&self, text: &str, font: &FontDescription) -> (f64, f64);
21 fn show_text(&mut self, text: &str, font: &DefinedFontDescription); 25 fn show_text(&mut self, text: &str, font: &DefinedFontDescription);
26
27 fn show_image(&mut self, path: &Path, x: f64, y: f64, width: f64, height: f64);
28 fn geometry_for_image(&mut self, path: &Path) -> (f64, f64);
22} 29}
23 30
24pub trait Render { 31pub trait Render {
@@ -31,6 +38,7 @@ impl Render for DefinedCoreShape {
31 Self::Rectangle(r) => r.render(context, renderer), 38 Self::Rectangle(r) => r.render(context, renderer),
32 Self::Text(t) => t.render(context, renderer), 39 Self::Text(t) => t.render(context, renderer),
33 Self::StraightPath(p) => p.render(context, renderer), 40 Self::StraightPath(p) => p.render(context, renderer),
41 Self::Image(i) => i.render(context, renderer),
34 } 42 }
35 } 43 }
36} 44}
@@ -96,3 +104,16 @@ impl Render for DefinedStraightPath {
96 draw(&context.fill, &context.stroke, renderer); 104 draw(&context.fill, &context.stroke, renderer);
97 } 105 }
98} 106}
107
108impl Render for Image {
109 fn render(&self, context: DefinedCoreShapeContext, renderer: &mut dyn Renderer) {
110 // TODO: what about pattern, and fill color? Do we do something with that?
111 renderer.show_image(
112 &self.path,
113 context.bounds.left,
114 context.bounds.top,
115 context.bounds.width,
116 context.bounds.height,
117 );
118 }
119}
diff --git a/core/src/runtime.rs b/core/src/runtime.rs
index 6b1b5d1..5fd17e9 100644
--- a/core/src/runtime.rs
+++ b/core/src/runtime.rs
@@ -1,11 +1,11 @@
1use crate::core_shapes::CoreDrawable; 1use crate::{
2use crate::rendering::Render; 2 core_shapes::CoreDrawable,
3use crate::types::Bounds; 3 rendering::{Render, Renderer},
4 4 solving::{Constrainable, SolverContext},
5// use super::complex_shapes::{ComplexShape, Drawable}; 5 types::Bounds,
6use super::rendering::Renderer; 6};
7use super::solving::{Constrainable, SolverContext};
8 7
8// TODO:
9// const RECURSION_LIMIT: u64 = 10_000; 9// const RECURSION_LIMIT: u64 = 10_000;
10 10
11pub struct Runtime<'a> { 11pub struct Runtime<'a> {
@@ -20,15 +20,10 @@ impl<'a> Runtime<'a> {
20 Self { 20 Self {
21 solver_ctx, 21 solver_ctx,
22 renderer, 22 renderer,
23 // drawables: Vec::new(),
24 drawables: Vec::new(), 23 drawables: Vec::new(),
25 } 24 }
26 } 25 }
27 26
28 // pub fn add_drawable<T: ComplexShape + 'static>(&mut self, drawable: Drawable<T>) {
29 // self.drawables.push(drawable.into())
30 // }
31
32 pub fn add_drawable(&mut self, drawable: CoreDrawable) { 27 pub fn add_drawable(&mut self, drawable: CoreDrawable) {
33 self.drawables.push(drawable) 28 self.drawables.push(drawable)
34 } 29 }
@@ -41,88 +36,34 @@ impl<'a> Runtime<'a> {
41 &mut *self.renderer 36 &mut *self.renderer
42 } 37 }
43 38
44 pub fn render(mut self, bounds: Bounds) { 39 pub fn render(self, bounds: Bounds) {
45 let model = self.solver_ctx.solve(); 40 // Separate self into several variables, so we can get mutable references to each at the
41 // same time
42 let Runtime {
43 mut solver_ctx,
44 mut renderer,
45 drawables,
46 } = self;
47
48 for drawable in &drawables {
49 drawable.inherent_constraints(&mut *solver_ctx, &mut *renderer);
50 }
51
52 let model = solver_ctx.solve();
46 53
47 let bounds = bounds 54 let bounds = bounds
48 .fixate(&*model) 55 .fixate(&*model)
49 .expect("Could not fixate figure bounds"); 56 .expect("Could not fixate figure bounds");
50 57
51 self.renderer.set_size(bounds.width, bounds.height); 58 renderer.set_size(bounds.width, bounds.height);
52 59
53 for drawable in &self.drawables { 60 for drawable in &drawables {
54 let defined_drawable = drawable 61 let defined_drawable = drawable
55 .fixate(&*model) 62 .fixate(&*model)
56 .expect("Could not fixate core shape"); 63 .expect("Could not fixate core shape");
57 defined_drawable 64 defined_drawable
58 .shape 65 .shape
59 .render(defined_drawable.context, &mut *self.renderer); 66 .render(defined_drawable.context, &mut *renderer);
60 } 67 }
61 } 68 }
62
63 // TODO: preserve ordering of shapes
64 // pub fn render(mut self) {
65 // let mut drawables = self.drawables;
66 // let mut waited_on_variables = Vec::new();
67 // let mut core_shapes = Vec::new();
68 //
69 // /*
70 // for drawable in &self.shapes {
71 // let bounds = &drawable.bounds;
72 // let shape = &drawable.shape;
73 //
74 // if let Some(core_shape) = shape.to_render() {
75 // drawables.push((*drawable).clone());
76 // continue;
77 // }
78 //
79 // let mut result = shape.draw(bounds);
80 // drawables.append(&mut result.subshapes);
81 // waited_on_variables.append(&mut result.waiting_on);
82 // }
83 // */
84 //
85 // let mut recursion_count = 0;
86 //
87 // while !drawables.is_empty() {
88 // recursion_count += 1;
89 //
90 // if recursion_count > RECURSION_LIMIT {
91 // panic!("Recursion limit reached");
92 // }
93 //
94 // let mut tmp_drawables = Vec::new();
95 //
96 // for drawable in drawables.drain(..) {
97 // let shape_ctx = &drawable.context;
98 // let shape = &drawable.shape;
99 //
100 // if let Some(core_shape) = shape.as_core_shape() {
101 // core_shape.constrain(shape_ctx, &mut *self.solver_ctx, &*self.renderer);
102 // core_shapes.push((shape.dyn_clone(), shape_ctx.clone())); // Better to Arc? Cow?
103 // continue;
104 // }
105 //
106 // let mut result = shape.draw(shape_ctx, &mut *self.solver_ctx);
107 // tmp_drawables.append(&mut result.subshapes);
108 // waited_on_variables.append(&mut result.waiting_on);
109 // }
110 //
111 // drawables = tmp_drawables;
112 // }
113 //
114 // let model = self.solver_ctx.solve();
115 //
116 // // Delay rendering core shapes until later to have all the constraints
117 // for (core_shape, shape_ctx) in core_shapes {
118 // let core_shape = core_shape.as_core_shape().unwrap();
119 //
120 // match (core_shape.to_render(&*model), shape_ctx.fixate(&*model)) {
121 // (Some(defined_shape), Some(shape_ctx)) => {
122 // defined_shape.render(shape_ctx, &mut *self.renderer)
123 // }
124 // _ => panic!("Failed to fixate core shape"),
125 // }
126 // }
127 // }
128} 69}
diff --git a/core/src/solving.rs b/core/src/solving.rs
index bfe3ff3..63e4562 100644
--- a/core/src/solving.rs
+++ b/core/src/solving.rs
@@ -176,11 +176,13 @@ pub trait Constrainable {
176impl Constrainable for CoreShape { 176impl Constrainable for CoreShape {
177 type Fixated = DefinedCoreShape; 177 type Fixated = DefinedCoreShape;
178 178
179 // TODO: why &self, why not self?
179 fn fixate(&self, model: &dyn SolverModel) -> Option<Self::Fixated> { 180 fn fixate(&self, model: &dyn SolverModel) -> Option<Self::Fixated> {
180 match self { 181 match self {
181 CoreShape::Rectangle(r) => Some(DefinedCoreShape::Rectangle(*r)), 182 CoreShape::Rectangle(r) => Some(DefinedCoreShape::Rectangle(*r)),
182 CoreShape::Text(t) => t.fixate(model).map(DefinedCoreShape::Text), 183 CoreShape::Text(t) => t.fixate(model).map(DefinedCoreShape::Text),
183 CoreShape::StraightPath(p) => p.fixate(model).map(DefinedCoreShape::StraightPath), 184 CoreShape::StraightPath(p) => p.fixate(model).map(DefinedCoreShape::StraightPath),
185 CoreShape::Image(i) => Some(DefinedCoreShape::Image(i.clone())),
184 } 186 }
185 } 187 }
186} 188}
diff --git a/flake.nix b/flake.nix
index 36a85c7..b247db4 100644
--- a/flake.nix
+++ b/flake.nix
@@ -14,6 +14,8 @@
14 buildInputs = with pkgs; [ 14 buildInputs = with pkgs; [
15 pkgconfig 15 pkgconfig
16 cairo 16 cairo
17 gdk-pixbuf
18 gtk3
17 pango 19 pango
18 z3 20 z3
19 21
diff --git a/lua-bindings/diaphragm.lua b/lua-bindings/diaphragm.lua
index a308c28..d4e3dc9 100644
--- a/lua-bindings/diaphragm.lua
+++ b/lua-bindings/diaphragm.lua
@@ -1,3 +1,5 @@
1-- TODO: split file
2
1local M = {} 3local M = {}
2 4
3M.core = require("libdiaphragm") 5M.core = require("libdiaphragm")
@@ -184,6 +186,15 @@ function M.straight_path.new(params)
184 return result 186 return result
185end 187end
186 188
189M.image = {}
190
191function M.image.new(params)
192 params = params or {}
193 local result = M.core.image(params)
194 M.util.tbl_assign_reserved(params, result, bounds_reserved)
195 return result
196end
197
187function M.shape(params) 198function M.shape(params)
188 params = params or {} 199 params = params or {}
189 200
diff --git a/lua-bindings/src/lib.rs b/lua-bindings/src/lib.rs
index 86a759d..26081c8 100644
--- a/lua-bindings/src/lib.rs
+++ b/lua-bindings/src/lib.rs
@@ -11,7 +11,8 @@ use diaphragm_cairo_renderer::CairoRenderer;
11use diaphragm_core::{ 11use diaphragm_core::{
12 colors::Color, 12 colors::Color,
13 core_shapes::{ 13 core_shapes::{
14 CoreDrawable, CoreShape, Rectangle as CoreRectangle, StraightPath as CoreStraightPath, 14 CoreDrawable, CoreShape, Image as CoreImage, Rectangle as CoreRectangle,
15 StraightPath as CoreStraightPath,
15 }, 16 },
16 solving::VariableHandle, 17 solving::VariableHandle,
17 styles::Pattern, 18 styles::Pattern,
@@ -517,28 +518,6 @@ fn text(_: &Lua, params: LuaTable) -> LuaResult<Text> {
517 518
518 let context = new_shape_context(); 519 let context = new_shape_context();
519 520
520 let content_2 = content.clone();
521 let font_2 = font.0.clone();
522 let context_2 = context.clone();
523
524 // TODO: this shouldn't be here, this should be an innate property of Text. Move the
525 // Drawable<T> struct in core?
526 runtime_thread_do(Box::new(move |r| {
527 let (text_width, text_height) = r.renderer().text_extents(&content_2, &font_2);
528 let solver = r.solver_ctx();
529 // let scale = solver.float_div(font.0.size, CoreFloat::Fixed(height));
530
531 let calculated_width = solver.float_mul(&[CoreFloat::Fixed(text_width), font.0.size]);
532 let bounds_width = context_2.bounds().width(solver);
533 let width_constraint = solver.float_eq(bounds_width, calculated_width);
534 solver.constrain(width_constraint);
535
536 let calculated_height = solver.float_mul(&[CoreFloat::Fixed(text_height), font.0.size]);
537 let bounds_height = context_2.bounds().height(solver);
538 let height_constraint = solver.float_eq(bounds_height, calculated_height);
539 solver.constrain(height_constraint);
540 }));
541
542 Ok(Text(Drawable { 521 Ok(Text(Drawable {
543 shape: CoreText { 522 shape: CoreText {
544 content, 523 content,
@@ -646,6 +625,63 @@ fn straight_path(_: &Lua, params: LuaTable) -> LuaResult<StraightPath> {
646 points.try_into() 625 points.try_into()
647} 626}
648 627
628#[derive(Debug, Clone)]
629struct Image(Drawable<CoreImage>);
630impl LuaUserData for Image {
631 fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
632 methods.add_method("draw", |_, this, _params: ()| {
633 let drawable = this.0.clone().into();
634 runtime_thread_do(Box::new(|r| {
635 r.add_drawable(drawable);
636 }));
637 Ok(())
638 })
639 }
640
641 fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
642 add_bound_fields(fields);
643 }
644}
645
646impl HasContext for Image {
647 fn get_context(&self) -> &CoreShapeContext {
648 &self.0.context
649 }
650
651 fn get_context_mut(&mut self) -> &mut CoreShapeContext {
652 &mut self.0.context
653 }
654}
655
656impl TryFrom<LuaValue<'_>> for Image {
657 type Error = LuaError;
658
659 fn try_from(value: LuaValue) -> Result<Self, Self::Error> {
660 match value {
661 LuaValue::Table(t) => {
662 let path: String = t.get("path")?;
663 let keep_aspect_ratio = t.get::<_, Option<_>>("keep_aspect_ratio")?.unwrap_or(true);
664
665 Ok(Image(Drawable {
666 shape: CoreImage::new(path.into(), keep_aspect_ratio),
667 context: new_shape_context(),
668 }))
669 }
670
671 LuaValue::UserData(u) => Ok(Image(u.borrow::<Image>()?.0.clone())),
672 _ => Err(LuaError::FromLuaConversionError {
673 from: value.type_name(),
674 to: "StraightPath",
675 message: Some("Only a list of points are allowed".to_string()),
676 }),
677 }
678 }
679}
680
681fn image(_: &Lua, params: LuaTable) -> LuaResult<Image> {
682 LuaValue::Table(params).try_into()
683}
684
649// It just has a context for bounds, the rest is handled manually in Lua 685// It just has a context for bounds, the rest is handled manually in Lua
650#[derive(Debug, Clone)] 686#[derive(Debug, Clone)]
651struct ComplexShape(CoreShapeContext); 687struct ComplexShape(CoreShapeContext);
@@ -800,8 +836,8 @@ fn libdiaphragm(lua: &Lua) -> LuaResult<LuaTable> {
800 exports.set("font", lua.create_function(font)?)?; 836 exports.set("font", lua.create_function(font)?)?;
801 837
802 exports.set("rectangle", lua.create_function(rectangle)?)?; 838 exports.set("rectangle", lua.create_function(rectangle)?)?;
803
804 exports.set("straight_path", lua.create_function(straight_path)?)?; 839 exports.set("straight_path", lua.create_function(straight_path)?)?;
840 exports.set("image", lua.create_function(image)?)?;
805 841
806 exports.set("complex_shape", lua.create_function(complex_shape)?)?; 842 exports.set("complex_shape", lua.create_function(complex_shape)?)?;
807 843