diff options
author | Minijackson <minijackson@riseup.net> | 2023-02-03 19:59:43 +0100 |
---|---|---|
committer | Minijackson <minijackson@riseup.net> | 2023-02-03 19:59:43 +0100 |
commit | 82ffe3187a32bad4ecca0736882a23793a800822 (patch) | |
tree | 2d3ab9231b76dadcf2ab69b27053df6cc66bead1 | |
parent | c8bb3e4a24ca1ca6ec1a57c9e8cd8655483b3eb0 (diff) | |
download | diaphragm-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.lock | 60 | ||||
-rw-r--r-- | cairo-renderer/Cargo.toml | 1 | ||||
-rw-r--r-- | cairo-renderer/src/lib.rs | 43 | ||||
-rw-r--r-- | core/src/core_shapes.rs | 174 | ||||
-rw-r--r-- | core/src/rendering.rs | 29 | ||||
-rw-r--r-- | core/src/runtime.rs | 107 | ||||
-rw-r--r-- | core/src/solving.rs | 2 | ||||
-rw-r--r-- | flake.nix | 2 | ||||
-rw-r--r-- | lua-bindings/diaphragm.lua | 11 | ||||
-rw-r--r-- | lua-bindings/src/lib.rs | 84 |
10 files changed, 328 insertions, 185 deletions
@@ -177,6 +177,7 @@ version = "0.1.0" | |||
177 | dependencies = [ | 177 | dependencies = [ |
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]] |
296 | name = "gdk" | ||
297 | version = "0.16.2" | ||
298 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
299 | checksum = "aa9cb33da481c6c040404a11f8212d193889e9b435db2c14fd86987f630d3ce1" | ||
300 | dependencies = [ | ||
301 | "bitflags", | ||
302 | "cairo-rs", | ||
303 | "gdk-pixbuf", | ||
304 | "gdk-sys", | ||
305 | "gio", | ||
306 | "glib", | ||
307 | "libc", | ||
308 | "pango", | ||
309 | ] | ||
310 | |||
311 | [[package]] | ||
312 | name = "gdk-pixbuf" | ||
313 | version = "0.16.7" | ||
314 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
315 | checksum = "c3578c60dee9d029ad86593ed88cb40f35c1b83360e12498d055022385dd9a05" | ||
316 | dependencies = [ | ||
317 | "bitflags", | ||
318 | "gdk-pixbuf-sys", | ||
319 | "gio", | ||
320 | "glib", | ||
321 | "libc", | ||
322 | ] | ||
323 | |||
324 | [[package]] | ||
325 | name = "gdk-pixbuf-sys" | ||
326 | version = "0.16.3" | ||
327 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
328 | checksum = "3092cf797a5f1210479ea38070d9ae8a5b8e9f8f1be9f32f4643c529c7d70016" | ||
329 | dependencies = [ | ||
330 | "gio-sys", | ||
331 | "glib-sys", | ||
332 | "gobject-sys", | ||
333 | "libc", | ||
334 | "system-deps", | ||
335 | ] | ||
336 | |||
337 | [[package]] | ||
338 | name = "gdk-sys" | ||
339 | version = "0.16.0" | ||
340 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
341 | checksum = "d76354f97a913e55b984759a997b693aa7dc71068c9e98bcce51aa167a0a5c5a" | ||
342 | dependencies = [ | ||
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]] | ||
295 | name = "gio" | 355 | name = "gio" |
296 | version = "0.16.7" | 356 | version = "0.16.7" |
297 | source = "registry+https://github.com/rust-lang/crates.io-index" | 357 | source = "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" | |||
10 | diaphragm-core = { path = "../core" } | 10 | diaphragm-core = { path = "../core" } |
11 | 11 | ||
12 | cairo-rs = { version = "0.16", features = ["svg", "png"] } | 12 | cairo-rs = { version = "0.16", features = ["svg", "png"] } |
13 | gdk = "0.16" | ||
13 | pango = "0.16" | 14 | pango = "0.16" |
14 | pangocairo = "0.16" | 15 | pangocairo = "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 @@ | |||
1 | use std::{ | ||
2 | collections::HashMap, | ||
3 | path::{Path, PathBuf}, | ||
4 | }; | ||
5 | |||
6 | use gdk::{gdk_pixbuf::Pixbuf, prelude::GdkContextExt}; | ||
7 | |||
1 | use diaphragm_core::{ | 8 | use 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 | ||
7 | pub struct CairoRenderer { | 14 | pub struct CairoRenderer { |
8 | ctx: cairo::Context, | 15 | ctx: cairo::Context, |
16 | loaded_images: HashMap<PathBuf, Pixbuf>, | ||
9 | } | 17 | } |
10 | 18 | ||
11 | impl CairoRenderer { | 19 | impl 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 @@ | |||
1 | use crate::types::{CoreShapeContext, DefinedCoreShapeContext, DefinedPoint2D, Point2D}; | 1 | use std::path::PathBuf; |
2 | 2 | ||
3 | pub use super::text::{DefinedText, Text}; | 3 | use crate::{ |
4 | text::{DefinedText, Text}, | ||
5 | types::{CoreShapeContext, DefinedCoreShapeContext, DefinedPoint2D, Float, Point2D}, | ||
6 | Renderer, SolverContext, | ||
7 | }; | ||
4 | 8 | ||
9 | #[derive(Debug, Clone)] | ||
5 | pub struct CoreDrawable { | 10 | pub 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)] | ||
16 | pub enum CoreShape { | 99 | pub 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 | ||
22 | impl From<Rectangle> for CoreShape { | 106 | impl From<Rectangle> for CoreShape { |
@@ -37,6 +121,12 @@ impl From<StraightPath> for CoreShape { | |||
37 | } | 121 | } |
38 | } | 122 | } |
39 | 123 | ||
124 | impl From<Image> for CoreShape { | ||
125 | fn from(image: Image) -> Self { | ||
126 | CoreShape::Image(image) | ||
127 | } | ||
128 | } | ||
129 | |||
40 | pub struct DefinedCoreDrawable { | 130 | pub 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 | /* | ||
52 | pub 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 | |||
63 | impl<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)] |
75 | pub struct Rectangle {} | 143 | pub struct Rectangle {} |
76 | 144 | ||
77 | /* | ||
78 | impl 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 | /* | ||
87 | impl 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)] |
116 | pub struct StraightPath { | 146 | pub 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)] |
131 | impl CoreShape for StraightPath { | 161 | pub 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 | |||
166 | impl 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 @@ | |||
1 | use super::core_shapes::*; | 1 | use std::path::Path; |
2 | use super::styles::{DefinedDashStyle, DefinedStrokeStyle, FillStyle, Pattern}; | 2 | |
3 | use super::text::{DefinedFontDescription, FontDescription}; | 3 | use crate::{ |
4 | use super::types::DefinedCoreShapeContext; | 4 | core_shapes::*, |
5 | styles::{DefinedDashStyle, DefinedStrokeStyle, FillStyle, Pattern}, | ||
6 | text::{DefinedFontDescription, DefinedText, FontDescription}, | ||
7 | types::DefinedCoreShapeContext, | ||
8 | }; | ||
5 | 9 | ||
6 | pub trait Renderer { | 10 | pub 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 | ||
24 | pub trait Render { | 31 | pub 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 | |||
108 | impl 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 @@ | |||
1 | use crate::core_shapes::CoreDrawable; | 1 | use crate::{ |
2 | use crate::rendering::Render; | 2 | core_shapes::CoreDrawable, |
3 | use crate::types::Bounds; | 3 | rendering::{Render, Renderer}, |
4 | 4 | solving::{Constrainable, SolverContext}, | |
5 | // use super::complex_shapes::{ComplexShape, Drawable}; | 5 | types::Bounds, |
6 | use super::rendering::Renderer; | 6 | }; |
7 | use super::solving::{Constrainable, SolverContext}; | ||
8 | 7 | ||
8 | // TODO: | ||
9 | // const RECURSION_LIMIT: u64 = 10_000; | 9 | // const RECURSION_LIMIT: u64 = 10_000; |
10 | 10 | ||
11 | pub struct Runtime<'a> { | 11 | pub 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 { | |||
176 | impl Constrainable for CoreShape { | 176 | impl 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 | } |
@@ -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 | |||
1 | local M = {} | 3 | local M = {} |
2 | 4 | ||
3 | M.core = require("libdiaphragm") | 5 | M.core = require("libdiaphragm") |
@@ -184,6 +186,15 @@ function M.straight_path.new(params) | |||
184 | return result | 186 | return result |
185 | end | 187 | end |
186 | 188 | ||
189 | M.image = {} | ||
190 | |||
191 | function 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 | ||
196 | end | ||
197 | |||
187 | function M.shape(params) | 198 | function 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; | |||
11 | use diaphragm_core::{ | 11 | use 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)] | ||
629 | struct Image(Drawable<CoreImage>); | ||
630 | impl 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 | |||
646 | impl 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 | |||
656 | impl 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 | |||
681 | fn 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)] |
651 | struct ComplexShape(CoreShapeContext); | 687 | struct 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 | ||