diff options
author | Minijackson <minijackson@riseup.net> | 2022-12-22 12:19:59 +0100 |
---|---|---|
committer | Minijackson <minijackson@riseup.net> | 2022-12-22 12:19:59 +0100 |
commit | 92a02c34628343153b33602eae00cef46e28d191 (patch) | |
tree | 8622ec528d24e456be22d984d93aa9bcafc97399 /examples/lib-dfscq-log | |
download | diaphragm-92a02c34628343153b33602eae00cef46e28d191.tar.gz diaphragm-92a02c34628343153b33602eae00cef46e28d191.zip |
WIP
Diffstat (limited to 'examples/lib-dfscq-log')
-rw-r--r-- | examples/lib-dfscq-log/Cargo.toml | 15 | ||||
-rw-r--r-- | examples/lib-dfscq-log/dfscq-log.lua | 124 | ||||
-rw-r--r-- | examples/lib-dfscq-log/src/block_list.rs | 183 | ||||
-rw-r--r-- | examples/lib-dfscq-log/src/blocks.rs | 72 | ||||
-rw-r--r-- | examples/lib-dfscq-log/src/bracket.rs | 42 | ||||
-rw-r--r-- | examples/lib-dfscq-log/src/explode.rs | 86 | ||||
-rw-r--r-- | examples/lib-dfscq-log/src/labeled.rs | 64 | ||||
-rw-r--r-- | examples/lib-dfscq-log/src/labeled_delimiter.rs | 121 | ||||
-rw-r--r-- | examples/lib-dfscq-log/src/layer.rs | 169 | ||||
-rw-r--r-- | examples/lib-dfscq-log/src/main.rs | 642 | ||||
-rw-r--r-- | examples/lib-dfscq-log/src/main2.rs | 61 | ||||
-rw-r--r-- | examples/lib-dfscq-log/src/spacer.rs | 133 |
12 files changed, 1712 insertions, 0 deletions
diff --git a/examples/lib-dfscq-log/Cargo.toml b/examples/lib-dfscq-log/Cargo.toml new file mode 100644 index 0000000..8146aa4 --- /dev/null +++ b/examples/lib-dfscq-log/Cargo.toml | |||
@@ -0,0 +1,15 @@ | |||
1 | [package] | ||
2 | name = "diaphragm-examples-lib-dfscq-log" | ||
3 | version = "0.1.0" | ||
4 | authors = ["Minijackson <minijackson@riseup.net>"] | ||
5 | edition = "2021" | ||
6 | |||
7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
8 | |||
9 | [package.metadata.riff] | ||
10 | runtime-inputs = ["z3", "cairo"] | ||
11 | |||
12 | [dependencies] | ||
13 | diaphragm-core = { path = "../../core" } | ||
14 | diaphragm-z3-solver = { path = "../../z3-solver" } | ||
15 | diaphragm-cairo-renderer = { path = "../../cairo-renderer" } | ||
diff --git a/examples/lib-dfscq-log/dfscq-log.lua b/examples/lib-dfscq-log/dfscq-log.lua new file mode 100644 index 0000000..c54e4f2 --- /dev/null +++ b/examples/lib-dfscq-log/dfscq-log.lua | |||
@@ -0,0 +1,124 @@ | |||
1 | local diaphragm = require("diaphragm") | ||
2 | |||
3 | -- TODO: anyone can use metatable? | ||
4 | |||
5 | local Blocks = diaphragm.Drawable:new() | ||
6 | Blocks.elements = {} | ||
7 | Blocks.unit_width = diaphragm.float() | ||
8 | |||
9 | function Blocks:draw() | ||
10 | local len = #self.elements | ||
11 | assert(len >= 1, "Blocks must have at least 1 element") | ||
12 | |||
13 | local total_grow = 0 | ||
14 | for el in self.elements do | ||
15 | total_grow = el.grow or 1 | ||
16 | end | ||
17 | |||
18 | diaphragm.constrain(self.width == self.unit_width * total_grow) | ||
19 | |||
20 | local param = self.elements[0] | ||
21 | |||
22 | -- TODO: this would be written with layout.hstack | ||
23 | |||
24 | local block = diaphragm.shape.Rectangle:new() | ||
25 | block.fill(param.color) | ||
26 | diaphragm.constrain(block.left() == self.left()) | ||
27 | diaphragm.constrain(block.top() == self.top()) | ||
28 | diaphragm.constrain(block.bottom() == self.bottom()) | ||
29 | diaphragm.constrain(block.width() == (param.grow or 1) * self.unit_width) | ||
30 | |||
31 | block.draw() | ||
32 | |||
33 | local previous_right = block.right() | ||
34 | |||
35 | for i = 2, len do | ||
36 | param = self.elements[i] | ||
37 | |||
38 | block = diaphragm.shape.Rectangle:new() | ||
39 | block.fill(param.color) | ||
40 | |||
41 | diaphragm.constrain(block.left() == previous_right) | ||
42 | diaphragm.constrain(block.top() == self.top()) | ||
43 | diaphragm.constrain(block.bottom() == self.bottom()) | ||
44 | diaphragm.constrain(block.width() == (param.grow or 1) * self.unit_width) | ||
45 | |||
46 | block.draw() | ||
47 | |||
48 | previous_right = block.right() | ||
49 | end | ||
50 | end | ||
51 | |||
52 | -- Library | ||
53 | ---------- | ||
54 | |||
55 | local function block(color) | ||
56 | local res = diaphragm.shape.Rectangle:new() | ||
57 | res.fill(color) | ||
58 | return res | ||
59 | end | ||
60 | |||
61 | local function blocks_by_len(count, color) | ||
62 | local res = {} | ||
63 | for i = 1, count do | ||
64 | res[i] = block(color) | ||
65 | end | ||
66 | |||
67 | -- TODO: rename cons? Misleading | ||
68 | diaphragm.cons.same_size(res) | ||
69 | return diaphragm.layout.hstack(res) | ||
70 | end | ||
71 | |||
72 | local function layer(config) | ||
73 | local name = config.name or "" | ||
74 | local entries = config.entries or {} | ||
75 | |||
76 | -- TODO: how to do it without box? | ||
77 | -- TODO: implement union or combine | ||
78 | -- probably combine because | ||
79 | -- union could remove some | ||
80 | -- strokes | ||
81 | return diaphragm.layout.box({}) | ||
82 | end | ||
83 | |||
84 | -- Parameters | ||
85 | ------------- | ||
86 | |||
87 | local blue = diaphragm.color.from_rgb(0.35, 0.35, 1.) | ||
88 | |||
89 | local active_txn_count = 5 | ||
90 | |||
91 | -- Log API | ||
92 | ---------- | ||
93 | |||
94 | local log_api = layer({ | ||
95 | name = "LogAPI", | ||
96 | entries = { | ||
97 | { | ||
98 | name = "activeTxn", | ||
99 | content = blocks_by_len(active_txn_count, blue), | ||
100 | }, | ||
101 | }, | ||
102 | }) | ||
103 | |||
104 | -- Group Commit | ||
105 | --------------- | ||
106 | |||
107 | -- local group_commit = nil | ||
108 | |||
109 | -- Disk Log | ||
110 | ----------- | ||
111 | |||
112 | -- local disk_log = nil | ||
113 | |||
114 | -- Applier | ||
115 | ---------- | ||
116 | |||
117 | -- local applier = nil | ||
118 | |||
119 | diaphragm.draw(diaphragm.layout.vstack({ | ||
120 | log_api, | ||
121 | -- group_commit, | ||
122 | -- disk_log, | ||
123 | -- applier, | ||
124 | })) | ||
diff --git a/examples/lib-dfscq-log/src/block_list.rs b/examples/lib-dfscq-log/src/block_list.rs new file mode 100644 index 0000000..be0eee0 --- /dev/null +++ b/examples/lib-dfscq-log/src/block_list.rs | |||
@@ -0,0 +1,183 @@ | |||
1 | use super::blocks::Blocks; | ||
2 | use super::bracket::*; | ||
3 | |||
4 | use diaphragm_core::{ | ||
5 | core_shapes::Text, | ||
6 | text::FontDescription, | ||
7 | types::{Bounds, Float, ShapeContext}, | ||
8 | ComplexShape, DrawResult, Drawable, SolverContext, | ||
9 | }; | ||
10 | |||
11 | #[derive(Clone)] | ||
12 | pub struct BlockList { | ||
13 | pub block_list: Vec<Drawable<Blocks>>, | ||
14 | pub block_unit_width: Float, | ||
15 | pub bracket_width: Float, | ||
16 | pub blocks_vert_padding: Float, | ||
17 | } | ||
18 | |||
19 | impl ComplexShape for BlockList { | ||
20 | fn draw(&self, context: &ShapeContext, solver: &mut dyn SolverContext) -> DrawResult { | ||
21 | let mut result = DrawResult::new(); | ||
22 | |||
23 | let bounds = context.bounds(); | ||
24 | |||
25 | // Left bracket | ||
26 | let opening_bracket_left = { | ||
27 | let bracket = Drawable::builder(Bracket { | ||
28 | r#type: BracketType::Opening, | ||
29 | }) | ||
30 | .bounds( | ||
31 | Bounds::builder() | ||
32 | .top(bounds.top(solver)) | ||
33 | .left(bounds.left(solver)) | ||
34 | .width(self.bracket_width) | ||
35 | .height(bounds.height(solver)) | ||
36 | .build(solver), | ||
37 | ) | ||
38 | .stroke(context.stroke().clone()) | ||
39 | .build(solver); | ||
40 | |||
41 | let bracket_left = bracket.bounds().left(solver); | ||
42 | |||
43 | result.push(bracket); | ||
44 | |||
45 | bracket_left | ||
46 | }; | ||
47 | |||
48 | let bounds_left = bounds.left(solver); | ||
49 | let mut x_acc = solver.float_add(&[bounds_left, self.bracket_width]); | ||
50 | |||
51 | // Block list | ||
52 | |||
53 | let bounds_top = bounds.top(solver); | ||
54 | let bounds_height = bounds.height(solver); | ||
55 | let wanted_blocks_top = solver.float_add(&[bounds_top, self.blocks_vert_padding]); | ||
56 | let wanted_blocks_height = solver.float_sub(&[ | ||
57 | bounds_height, | ||
58 | self.blocks_vert_padding, | ||
59 | self.blocks_vert_padding, | ||
60 | ]); | ||
61 | |||
62 | let mut block_list_iter = self.block_list.iter(); | ||
63 | |||
64 | // First blocks | ||
65 | if let Some(first_blocks) = block_list_iter.next() { | ||
66 | // TODO: waaay too verbose | ||
67 | |||
68 | let blocks_top = first_blocks.bounds().top(solver); | ||
69 | let blocks_top_constraint = solver.float_eq(blocks_top, wanted_blocks_top); | ||
70 | solver.constrain(blocks_top_constraint); | ||
71 | |||
72 | let blocks_left = first_blocks.bounds().left(solver); | ||
73 | let blocks_left_constraint = solver.float_eq(blocks_left, x_acc); | ||
74 | solver.constrain(blocks_left_constraint); | ||
75 | |||
76 | let blocks_height = first_blocks.bounds().height(solver); | ||
77 | let blocks_height_constraint = solver.float_eq(blocks_height, wanted_blocks_height); | ||
78 | solver.constrain(blocks_height_constraint); | ||
79 | |||
80 | let block_unit_width_constraint = | ||
81 | solver.float_eq(first_blocks.shape().unit_width, self.block_unit_width); | ||
82 | solver.constrain(block_unit_width_constraint); | ||
83 | |||
84 | result.push(first_blocks.clone()); | ||
85 | |||
86 | let blocks_width = first_blocks.bounds().width(solver); | ||
87 | x_acc = solver.float_add(&[x_acc, blocks_width]); | ||
88 | } | ||
89 | |||
90 | // Rest of the blocks | ||
91 | for blocks in block_list_iter { | ||
92 | // Comma | ||
93 | let comma = Drawable::builder(Text { | ||
94 | content: String::from(", "), | ||
95 | font: FontDescription { | ||
96 | family: String::from("mono"), | ||
97 | style: Default::default(), | ||
98 | weight: Default::default(), | ||
99 | size: wanted_blocks_height, | ||
100 | }, | ||
101 | }) | ||
102 | .bounds( | ||
103 | Bounds::builder() | ||
104 | .top(wanted_blocks_top) | ||
105 | .left(x_acc) | ||
106 | .build(solver), | ||
107 | ) | ||
108 | .stroke(context.stroke().clone()) | ||
109 | .build(solver); | ||
110 | |||
111 | let comma_width = comma.bounds().width(solver); | ||
112 | |||
113 | x_acc = solver.float_add(&[x_acc, comma_width]); | ||
114 | |||
115 | result.push(comma); | ||
116 | |||
117 | // Blocks | ||
118 | /* | ||
119 | let blocks_context = ShapeContext { | ||
120 | bounds: Bounds { | ||
121 | top: blocks_top, | ||
122 | left: x_acc, | ||
123 | height: blocks_height, | ||
124 | width: blocks_width, | ||
125 | }, | ||
126 | fill: FillStyle::default(), | ||
127 | stroke: StrokeStyle::default(), | ||
128 | }; | ||
129 | */ | ||
130 | |||
131 | let blocks_top = blocks.bounds().top(solver); | ||
132 | let blocks_top_constraint = solver.float_eq(blocks_top, wanted_blocks_top); | ||
133 | solver.constrain(blocks_top_constraint); | ||
134 | |||
135 | let blocks_left = blocks.bounds().left(solver); | ||
136 | let blocks_left_constraint = solver.float_eq(blocks_left, x_acc); | ||
137 | solver.constrain(blocks_left_constraint); | ||
138 | |||
139 | let blocks_height = blocks.bounds().height(solver); | ||
140 | let blocks_height_constraint = solver.float_eq(blocks_height, wanted_blocks_height); | ||
141 | solver.constrain(blocks_height_constraint); | ||
142 | |||
143 | let block_unit_width_constraint = | ||
144 | solver.float_eq(blocks.shape().unit_width, self.block_unit_width); | ||
145 | solver.constrain(block_unit_width_constraint); | ||
146 | |||
147 | result.push(blocks.clone()); | ||
148 | |||
149 | let blocks_width = blocks.bounds().width(solver); | ||
150 | x_acc = solver.float_add(&[x_acc, blocks_width]); | ||
151 | } | ||
152 | |||
153 | // Right bracket | ||
154 | let closing_bracket_right = { | ||
155 | let bracket = Drawable::builder(Bracket { | ||
156 | r#type: BracketType::Closing, | ||
157 | }) | ||
158 | .bounds( | ||
159 | Bounds::builder() | ||
160 | .top(bounds.top(solver)) | ||
161 | .left(x_acc) | ||
162 | .width(self.bracket_width) | ||
163 | .height(bounds.height(solver)) | ||
164 | .build(solver), | ||
165 | ) | ||
166 | .stroke(context.stroke().clone()) | ||
167 | .build(solver); | ||
168 | |||
169 | let bracket_right = bracket.bounds().right(solver); | ||
170 | |||
171 | result.push(bracket); | ||
172 | |||
173 | bracket_right | ||
174 | }; | ||
175 | |||
176 | let this_width = solver.float_sub(&[closing_bracket_right, opening_bracket_left]); | ||
177 | let bounds_width = bounds.width(solver); | ||
178 | let bounds_width_constraint = solver.float_eq(bounds_width, this_width); | ||
179 | solver.constrain(bounds_width_constraint); | ||
180 | |||
181 | result | ||
182 | } | ||
183 | } | ||
diff --git a/examples/lib-dfscq-log/src/blocks.rs b/examples/lib-dfscq-log/src/blocks.rs new file mode 100644 index 0000000..0576b7d --- /dev/null +++ b/examples/lib-dfscq-log/src/blocks.rs | |||
@@ -0,0 +1,72 @@ | |||
1 | use diaphragm_core::{ | ||
2 | core_shapes::Rectangle, | ||
3 | types::{Float, ShapeContext}, | ||
4 | ComplexShape, DrawResult, Drawable, SolverContext, | ||
5 | }; | ||
6 | |||
7 | #[derive(Debug, Clone)] | ||
8 | pub struct Block { | ||
9 | pub grow: u8, | ||
10 | } | ||
11 | |||
12 | impl ComplexShape for Block { | ||
13 | fn draw(&self, context: &ShapeContext, _solver: &mut dyn SolverContext) -> DrawResult { | ||
14 | let mut result = DrawResult::new(); | ||
15 | |||
16 | // Grow is handled at the upper level | ||
17 | let block = Drawable::new(Rectangle {}, context.clone()); | ||
18 | |||
19 | result.push(block); | ||
20 | |||
21 | result | ||
22 | } | ||
23 | } | ||
24 | |||
25 | #[derive(Debug, Clone)] | ||
26 | pub struct Blocks { | ||
27 | pub blocks: Vec<Drawable<Block>>, | ||
28 | pub unit_width: Float, | ||
29 | } | ||
30 | |||
31 | impl ComplexShape for Blocks { | ||
32 | fn draw(&self, context: &ShapeContext, solver: &mut dyn SolverContext) -> DrawResult { | ||
33 | let mut result = DrawResult::new(); | ||
34 | |||
35 | let sum: u8 = self.blocks.iter().map(|block| block.shape().grow).sum(); | ||
36 | |||
37 | let mut rect_left = context.bounds().left(solver); | ||
38 | let rect_top = context.bounds().top(solver); | ||
39 | let rect_height = context.bounds().height(solver); | ||
40 | |||
41 | for block in &self.blocks { | ||
42 | let block_top = block.bounds().top(solver); | ||
43 | let rect_top_constraint = solver.float_eq(block_top, rect_top); | ||
44 | solver.constrain(rect_top_constraint); | ||
45 | |||
46 | let block_left = block.bounds().left(solver); | ||
47 | let rect_left_constraint = solver.float_eq(block_left, rect_left); | ||
48 | solver.constrain(rect_left_constraint); | ||
49 | |||
50 | let grow = Float::Fixed(block.shape().grow as f64); | ||
51 | let block_width = block.bounds().width(solver); | ||
52 | let rect_width = solver.float_mul(&[self.unit_width, grow]); | ||
53 | let rect_width_constraint = solver.float_eq(block_width, rect_width); | ||
54 | solver.constrain(rect_width_constraint); | ||
55 | |||
56 | let block_height = block.bounds().height(solver); | ||
57 | let rect_height_constraint = solver.float_eq(block_height, rect_height); | ||
58 | solver.constrain(rect_height_constraint); | ||
59 | |||
60 | result.push(block.clone()); | ||
61 | |||
62 | rect_left = solver.float_add(&[rect_left, rect_width]); | ||
63 | } | ||
64 | |||
65 | let this_width = solver.float_mul(&[self.unit_width, Float::Fixed(sum as f64)]); | ||
66 | let bounds_width = context.bounds().width(solver); | ||
67 | let bounds_width_constraint = solver.float_eq(bounds_width, this_width); | ||
68 | solver.constrain(bounds_width_constraint); | ||
69 | |||
70 | result | ||
71 | } | ||
72 | } | ||
diff --git a/examples/lib-dfscq-log/src/bracket.rs b/examples/lib-dfscq-log/src/bracket.rs new file mode 100644 index 0000000..02bca8d --- /dev/null +++ b/examples/lib-dfscq-log/src/bracket.rs | |||
@@ -0,0 +1,42 @@ | |||
1 | use diaphragm_core::{ | ||
2 | core_shapes::StraightPath, types::ShapeContext, ComplexShape, DrawResult, Drawable, | ||
3 | SolverContext, | ||
4 | }; | ||
5 | |||
6 | #[derive(Copy, Clone)] | ||
7 | pub enum BracketType { | ||
8 | Opening, | ||
9 | Closing, | ||
10 | } | ||
11 | |||
12 | #[derive(Clone)] | ||
13 | pub struct Bracket { | ||
14 | pub r#type: BracketType, | ||
15 | } | ||
16 | |||
17 | impl ComplexShape for Bracket { | ||
18 | fn draw(&self, context: &ShapeContext, solver: &mut dyn SolverContext) -> DrawResult { | ||
19 | let mut result = DrawResult::new(); | ||
20 | |||
21 | let bounds = context.bounds(); | ||
22 | |||
23 | let path = match self.r#type { | ||
24 | BracketType::Opening => StraightPath::new(vec![ | ||
25 | bounds.top_right(solver), | ||
26 | bounds.top_left(solver), | ||
27 | bounds.bottom_left(solver), | ||
28 | bounds.bottom_right(solver), | ||
29 | ]), | ||
30 | BracketType::Closing => StraightPath::new(vec![ | ||
31 | bounds.top_left(solver), | ||
32 | bounds.top_right(solver), | ||
33 | bounds.bottom_right(solver), | ||
34 | bounds.bottom_left(solver), | ||
35 | ]), | ||
36 | }; | ||
37 | |||
38 | result.push(Drawable::new(path, context.clone())); | ||
39 | |||
40 | result | ||
41 | } | ||
42 | } | ||
diff --git a/examples/lib-dfscq-log/src/explode.rs b/examples/lib-dfscq-log/src/explode.rs new file mode 100644 index 0000000..aeb85bd --- /dev/null +++ b/examples/lib-dfscq-log/src/explode.rs | |||
@@ -0,0 +1,86 @@ | |||
1 | use diaphragm_core::{ | ||
2 | core_shapes::StraightPath, | ||
3 | types::{Float, Point2D, ShapeContext}, | ||
4 | ComplexShape, DrawResult, Drawable, SolverContext, | ||
5 | }; | ||
6 | |||
7 | #[derive(Clone)] | ||
8 | pub struct Explode { | ||
9 | pub top_left: Point2D, | ||
10 | pub top_right: Point2D, | ||
11 | pub bottom_left: Point2D, | ||
12 | pub bottom_right: Point2D, | ||
13 | |||
14 | pub arm_length: Float, | ||
15 | } | ||
16 | |||
17 | impl ComplexShape for Explode { | ||
18 | fn draw(&self, context: &ShapeContext, solver: &mut dyn SolverContext) -> DrawResult { | ||
19 | let mut result = DrawResult::new(); | ||
20 | |||
21 | let wanted_top = solver.float_min(&[self.top_left.y(), self.top_right.y()]); | ||
22 | let wanted_left = solver.float_min(&[self.top_left.x(), self.bottom_left.x()]); | ||
23 | let wanted_bottom = solver.float_max(&[self.bottom_left.x(), self.bottom_right.x()]); | ||
24 | let wanted_right = solver.float_max(&[self.top_right.y(), self.bottom_right.y()]); | ||
25 | |||
26 | let bounds_top = context.bounds().top(solver); | ||
27 | let bounds_left = context.bounds().left(solver); | ||
28 | let bounds_bottom = context.bounds().bottom(solver); | ||
29 | let bounds_right = context.bounds().right(solver); | ||
30 | |||
31 | // TODO: add a facility to help this? | ||
32 | let bounds_top_constraint = solver.float_eq(bounds_top, wanted_top); | ||
33 | solver.constrain(bounds_top_constraint); | ||
34 | |||
35 | let bounds_left_constraint = solver.float_eq(bounds_left, wanted_left); | ||
36 | solver.constrain(bounds_left_constraint); | ||
37 | |||
38 | let bounds_bottom_constraint = solver.float_eq(bounds_bottom, wanted_bottom); | ||
39 | solver.constrain(bounds_bottom_constraint); | ||
40 | |||
41 | let bounds_right_constraint = solver.float_eq(bounds_right, wanted_right); | ||
42 | solver.constrain(bounds_right_constraint); | ||
43 | |||
44 | let top_left_arm_bottom = Point2D::new( | ||
45 | self.top_left.x(), | ||
46 | solver.float_add(&[self.top_left.y(), self.arm_length]), | ||
47 | ); | ||
48 | |||
49 | let bottom_left_arm_top = Point2D::new( | ||
50 | self.bottom_left.x(), | ||
51 | solver.float_sub(&[self.bottom_left.y(), self.arm_length]), | ||
52 | ); | ||
53 | |||
54 | result.push(Drawable::new( | ||
55 | StraightPath::new(vec![ | ||
56 | self.top_left.clone(), | ||
57 | top_left_arm_bottom, | ||
58 | bottom_left_arm_top, | ||
59 | self.bottom_left.clone(), | ||
60 | ]), | ||
61 | context.clone(), | ||
62 | )); | ||
63 | |||
64 | let top_right_arm_bottom = Point2D::new( | ||
65 | self.top_right.x(), | ||
66 | solver.float_add(&[self.top_right.y(), self.arm_length]), | ||
67 | ); | ||
68 | |||
69 | let bottom_right_arm_top = Point2D::new( | ||
70 | self.bottom_right.x(), | ||
71 | solver.float_sub(&[self.bottom_right.y(), self.arm_length]), | ||
72 | ); | ||
73 | |||
74 | result.push(Drawable::new( | ||
75 | StraightPath::new(vec![ | ||
76 | self.top_right.clone(), | ||
77 | top_right_arm_bottom, | ||
78 | bottom_right_arm_top, | ||
79 | self.bottom_right.clone(), | ||
80 | ]), | ||
81 | context.clone(), | ||
82 | )); | ||
83 | |||
84 | result | ||
85 | } | ||
86 | } | ||
diff --git a/examples/lib-dfscq-log/src/labeled.rs b/examples/lib-dfscq-log/src/labeled.rs new file mode 100644 index 0000000..20aebcd --- /dev/null +++ b/examples/lib-dfscq-log/src/labeled.rs | |||
@@ -0,0 +1,64 @@ | |||
1 | use diaphragm_core::{ | ||
2 | core_shapes::Text, | ||
3 | text::FontDescription, | ||
4 | types::{Bounds, Float, ShapeContext}, | ||
5 | ComplexShape, DrawResult, DynClone, SolverContext, | ||
6 | }; | ||
7 | |||
8 | pub struct Labeled { | ||
9 | pub label: String, | ||
10 | pub label_font: FontDescription, | ||
11 | pub content: Box<dyn ComplexShape>, | ||
12 | pub content_left: Float, | ||
13 | } | ||
14 | |||
15 | impl ComplexShape for Labeled { | ||
16 | fn draw(&self, context: &ShapeContext, solver: &mut dyn SolverContext) -> DrawResult { | ||
17 | let mut result = DrawResult::new(); | ||
18 | |||
19 | // Label | ||
20 | |||
21 | let label = Text { | ||
22 | content: self.label.clone(), | ||
23 | font: self.label_font.clone(), | ||
24 | }; | ||
25 | |||
26 | let mut label_context = context.clone(); | ||
27 | // TODO: make the text constrain the width | ||
28 | label_context.bounds.width = solver.new_free_float(); | ||
29 | |||
30 | let label_right = solver.float_add(&[context.bounds.left, label_context.bounds.width]); | ||
31 | let content_left_constrain = solver.float_eq(label_right, self.content_left); | ||
32 | solver.constrain(content_left_constrain); | ||
33 | |||
34 | result.push_shape(label, label_context); | ||
35 | |||
36 | // Content | ||
37 | |||
38 | let content_context = ShapeContext { | ||
39 | bounds: Bounds { | ||
40 | top: context.bounds.top, | ||
41 | left: solver.float_add(&[context.bounds.left, self.content_left]), | ||
42 | height: context.bounds.height, | ||
43 | width: context.bounds.width, | ||
44 | }, | ||
45 | fill: context.fill.clone(), | ||
46 | stroke: context.stroke.clone(), | ||
47 | }; | ||
48 | |||
49 | result.push_boxed_shape(self.content.dyn_clone(), content_context); | ||
50 | |||
51 | result | ||
52 | } | ||
53 | } | ||
54 | |||
55 | impl DynClone for Labeled { | ||
56 | fn dyn_clone(&self) -> Box<dyn ComplexShape> { | ||
57 | Box::new(Labeled { | ||
58 | label: self.label.clone(), | ||
59 | label_font: self.label_font.clone(), | ||
60 | content: self.content.dyn_clone(), | ||
61 | content_left: self.content_left.clone(), | ||
62 | }) | ||
63 | } | ||
64 | } | ||
diff --git a/examples/lib-dfscq-log/src/labeled_delimiter.rs b/examples/lib-dfscq-log/src/labeled_delimiter.rs new file mode 100644 index 0000000..5a1c610 --- /dev/null +++ b/examples/lib-dfscq-log/src/labeled_delimiter.rs | |||
@@ -0,0 +1,121 @@ | |||
1 | use diaphragm_core::{ | ||
2 | core_shapes::{StraightPath, Text}, | ||
3 | types::{Bounds, Float, Point2D, ShapeContext}, | ||
4 | ComplexShape, DrawResult, Drawable, SolverContext, | ||
5 | }; | ||
6 | |||
7 | #[derive(Debug, Clone)] | ||
8 | pub struct Delimiter { | ||
9 | pub width: Float, | ||
10 | pub label: Drawable<Text>, | ||
11 | } | ||
12 | |||
13 | #[derive(Debug, Clone)] | ||
14 | pub struct LabeledDelimiter { | ||
15 | pub delimiters: Vec<Delimiter>, | ||
16 | pub tick_height: Float, | ||
17 | } | ||
18 | |||
19 | impl ComplexShape for LabeledDelimiter { | ||
20 | fn draw(&self, context: &ShapeContext, solver: &mut dyn SolverContext) -> DrawResult { | ||
21 | let mut result = DrawResult::new(); | ||
22 | |||
23 | let bounds_left = context.bounds().left(solver); | ||
24 | let bounds_right = context.bounds().right(solver); | ||
25 | let bounds_bottom = context.bounds().bottom(solver); | ||
26 | let bounds_width = context.bounds().width(solver); | ||
27 | |||
28 | let tick_top = context.bounds().top(solver); | ||
29 | let tick_bottom = solver.float_add(&[tick_top, self.tick_height]); | ||
30 | |||
31 | let first_tick = Drawable::builder(StraightPath::new(vec![ | ||
32 | context.bounds().top_left(solver), | ||
33 | Point2D::new(bounds_left, tick_bottom), | ||
34 | ])) | ||
35 | .bounds( | ||
36 | Bounds::builder() | ||
37 | .top(tick_top) | ||
38 | .left(bounds_left) | ||
39 | .height(self.tick_height) | ||
40 | .width(Float::Fixed(0.)) | ||
41 | .build(solver), | ||
42 | ) | ||
43 | .stroke(context.stroke().clone()) | ||
44 | .build(solver); | ||
45 | |||
46 | let baseline_y = first_tick.bounds().vert_center(solver); | ||
47 | |||
48 | result.push(first_tick); | ||
49 | |||
50 | // TODO: split everything into functions | ||
51 | |||
52 | let baseline = Drawable::builder(StraightPath::new(vec![ | ||
53 | Point2D::new(bounds_left, baseline_y), | ||
54 | Point2D::new(bounds_right, baseline_y), | ||
55 | ])) | ||
56 | .bounds( | ||
57 | Bounds::builder() | ||
58 | .top(baseline_y) | ||
59 | .left(bounds_left) | ||
60 | .width(bounds_width) | ||
61 | .height(Float::Fixed(0.)) | ||
62 | .build(solver), | ||
63 | ) | ||
64 | .stroke(context.stroke().clone()) | ||
65 | .build(solver); | ||
66 | |||
67 | result.push(baseline); | ||
68 | |||
69 | let mut section_end = bounds_left; | ||
70 | let mut all_label_bottoms = vec![]; | ||
71 | |||
72 | for &Delimiter { | ||
73 | width: section_width, | ||
74 | ref label, | ||
75 | } in &self.delimiters | ||
76 | { | ||
77 | let section_begin = section_end; | ||
78 | section_end = solver.float_add(&[section_end, section_width]); | ||
79 | |||
80 | let section_half_width = solver.float_div(section_width, Float::Fixed(2.)); | ||
81 | let section_middle = solver.float_add(&[section_begin, section_half_width]); | ||
82 | |||
83 | let tick = Drawable::builder(StraightPath::new(vec![ | ||
84 | Point2D::new(section_end, tick_top), | ||
85 | Point2D::new(section_end, tick_bottom), | ||
86 | ])) | ||
87 | .bounds( | ||
88 | Bounds::builder() | ||
89 | .top(tick_top) | ||
90 | .left(section_end) | ||
91 | .height(self.tick_height) | ||
92 | .width(Float::Fixed(0.)) | ||
93 | .build(solver), | ||
94 | ) | ||
95 | .stroke(context.stroke().clone()) | ||
96 | .build(solver); | ||
97 | |||
98 | result.push(tick); | ||
99 | |||
100 | let label_top = label.bounds().top(solver); | ||
101 | let label_bottom = label.bounds().bottom(solver); | ||
102 | let label_horiz_center = label.bounds().horiz_center(solver); | ||
103 | |||
104 | let label_top_constraint = solver.float_eq(label_top, tick_bottom); | ||
105 | solver.constrain(label_top_constraint); | ||
106 | |||
107 | let label_middle_constraint = solver.float_eq(label_horiz_center, section_middle); | ||
108 | solver.constrain(label_middle_constraint); | ||
109 | |||
110 | result.push(label.clone()); | ||
111 | |||
112 | all_label_bottoms.push(label_bottom); | ||
113 | } | ||
114 | |||
115 | let wanted_bounds_bottom = solver.float_max(&all_label_bottoms); | ||
116 | let bounds_bottom_constraint = solver.float_eq(bounds_bottom, wanted_bounds_bottom); | ||
117 | solver.constrain(bounds_bottom_constraint); | ||
118 | |||
119 | result | ||
120 | } | ||
121 | } | ||
diff --git a/examples/lib-dfscq-log/src/layer.rs b/examples/lib-dfscq-log/src/layer.rs new file mode 100644 index 0000000..45edb56 --- /dev/null +++ b/examples/lib-dfscq-log/src/layer.rs | |||
@@ -0,0 +1,169 @@ | |||
1 | use diaphragm_core::{ | ||
2 | core_shapes::Rectangle, | ||
3 | core_shapes::Text, | ||
4 | text::FontDescription, | ||
5 | types::{Bounds, Float, ShapeContext}, | ||
6 | ComplexShape, DrawResult, Drawable, DynDrawable, SolverContext, | ||
7 | }; | ||
8 | |||
9 | #[derive(Clone)] | ||
10 | pub struct Entry { | ||
11 | // TODO: transform this to just Text | ||
12 | pub label: Option<String>, | ||
13 | pub label_vert_center_offset: Option<Float>, | ||
14 | pub content: DynDrawable, | ||
15 | } | ||
16 | |||
17 | #[derive(Clone)] | ||
18 | pub struct Layer { | ||
19 | // TODO: transform this to just Text | ||
20 | pub name: String, | ||
21 | pub name_font: FontDescription, | ||
22 | pub label_font: FontDescription, | ||
23 | pub padding: Float, | ||
24 | pub entries: Vec<Entry>, | ||
25 | pub entries_width: Float, | ||
26 | } | ||
27 | |||
28 | impl ComplexShape for Layer { | ||
29 | fn draw(&self, context: &ShapeContext, solver: &mut dyn SolverContext) -> DrawResult { | ||
30 | let mut result = DrawResult::new(); | ||
31 | |||
32 | let bounds_top = context.bounds().top(solver); | ||
33 | let bounds_left = context.bounds().left(solver); | ||
34 | let bounds_right = context.bounds().right(solver); | ||
35 | let bounds_height = context.bounds().height(solver); | ||
36 | |||
37 | let inner_top = solver.float_add(&[bounds_top, self.padding]); | ||
38 | let inner_right = solver.float_sub(&[bounds_right, self.padding]); | ||
39 | |||
40 | // Outer box | ||
41 | |||
42 | result.push(Drawable::new(Rectangle {}, context.clone())); | ||
43 | |||
44 | // Layer name | ||
45 | |||
46 | let layer_name = Drawable::builder(Text { | ||
47 | content: self.name.clone(), | ||
48 | font: self.name_font.clone(), | ||
49 | }) | ||
50 | .bounds( | ||
51 | Bounds::builder() | ||
52 | .top(inner_top) | ||
53 | .left(solver.float_add(&[bounds_left, self.padding])) | ||
54 | .build(solver), | ||
55 | ) | ||
56 | .stroke(context.stroke().clone()) | ||
57 | .fill(context.fill().clone()) | ||
58 | .build(solver); | ||
59 | |||
60 | let layer_name_bottom = layer_name.bounds().bottom(solver); | ||
61 | |||
62 | /* | ||
63 | let mut rect_context = layer_name_context.clone(); | ||
64 | rect_context.stroke = StrokeStyle::solid(Color::from_rgb(1., 0., 0.)); | ||
65 | result.push_shape(Rectangle {}, rect_context); | ||
66 | */ | ||
67 | |||
68 | result.push(layer_name); | ||
69 | |||
70 | // Entries | ||
71 | |||
72 | let mut entry_y_acc = inner_top; | ||
73 | |||
74 | for entry in &self.entries { | ||
75 | // Entry content | ||
76 | |||
77 | /* | ||
78 | let content_context = ShapeContext { | ||
79 | bounds: Bounds { | ||
80 | top: solver.new_free_float(), | ||
81 | left: content_left, | ||
82 | width: entry.width, | ||
83 | height: entry.height, | ||
84 | }, | ||
85 | fill: Default::default(), | ||
86 | stroke: Default::default(), | ||
87 | }; | ||
88 | */ | ||
89 | |||
90 | let content_vert_center = entry.content.bounds().vert_center(solver); | ||
91 | let content_top = entry.content.bounds().top(solver); | ||
92 | let content_left = entry.content.bounds().left(solver); | ||
93 | let content_bottom = entry.content.bounds().bottom(solver); | ||
94 | |||
95 | // TODO: to replace with label offset | ||
96 | let content_half_height = solver.float_sub(&[content_vert_center, content_top]); | ||
97 | |||
98 | result.push_dyn(entry.content.clone()); | ||
99 | |||
100 | // Entry label | ||
101 | |||
102 | if let Some(label_content) = entry.label.clone() { | ||
103 | let label_top = solver.new_free_float(); | ||
104 | let label_width = solver.new_free_float(); | ||
105 | let label_left = solver.float_sub(&[inner_right, self.entries_width, label_width]); | ||
106 | |||
107 | let label = Drawable::builder(Text { | ||
108 | content: label_content, | ||
109 | font: self.label_font.clone(), | ||
110 | }) | ||
111 | .bounds( | ||
112 | Bounds::builder() | ||
113 | .top(label_top) | ||
114 | .left(label_left) | ||
115 | .width(label_width) | ||
116 | .build(solver), | ||
117 | ) | ||
118 | .build(solver); | ||
119 | |||
120 | /* | ||
121 | let mut rect_context = label_context.clone(); | ||
122 | rect_context.stroke = StrokeStyle::solid(Color::from_rgb(1., 0., 0.)); | ||
123 | result.push_shape(Rectangle {}, rect_context); | ||
124 | */ | ||
125 | |||
126 | // TODO | ||
127 | let label_vert_center = label.bounds().vert_center(solver); | ||
128 | let label_top = label.bounds().top(solver); | ||
129 | let label_right = label.bounds().right(solver); | ||
130 | |||
131 | let label_vert_center_constraint = | ||
132 | solver.float_eq(label_vert_center, content_vert_center); | ||
133 | solver.constrain(label_vert_center_constraint); | ||
134 | |||
135 | let label_half_height = solver.float_sub(&[label_vert_center, label_top]); | ||
136 | |||
137 | let dumb_label_mid_placement = | ||
138 | solver.float_add(&[entry_y_acc, content_half_height]); | ||
139 | let safe_label_mid_placement = | ||
140 | solver.float_add(&[layer_name_bottom, label_half_height]); | ||
141 | let label_mid_placement = | ||
142 | solver.float_max(&[safe_label_mid_placement, dumb_label_mid_placement]); | ||
143 | let label_mid_placement_constraint = | ||
144 | solver.float_eq(label_vert_center, label_mid_placement); | ||
145 | solver.constrain(label_mid_placement_constraint); | ||
146 | |||
147 | let label_right_constraint = solver.float_eq(label_right, content_left); | ||
148 | solver.constrain(label_right_constraint); | ||
149 | |||
150 | result.push(label); | ||
151 | } else { | ||
152 | let content_top_constraint = solver.float_eq(content_top, entry_y_acc); | ||
153 | solver.constrain(content_top_constraint); | ||
154 | |||
155 | let wanted_content_left = solver.float_sub(&[inner_right, self.entries_width]); | ||
156 | let content_left_constraint = solver.float_eq(content_left, wanted_content_left); | ||
157 | solver.constrain(content_left_constraint); | ||
158 | } | ||
159 | |||
160 | entry_y_acc = solver.float_add(&[content_bottom, self.padding]); | ||
161 | } | ||
162 | |||
163 | let wanted_bounds_height = solver.float_sub(&[entry_y_acc, bounds_top]); | ||
164 | let height_constraint = solver.float_eq(bounds_height, wanted_bounds_height); | ||
165 | solver.constrain(height_constraint); | ||
166 | |||
167 | result | ||
168 | } | ||
169 | } | ||
diff --git a/examples/lib-dfscq-log/src/main.rs b/examples/lib-dfscq-log/src/main.rs new file mode 100644 index 0000000..fe5db9a --- /dev/null +++ b/examples/lib-dfscq-log/src/main.rs | |||
@@ -0,0 +1,642 @@ | |||
1 | mod block_list; | ||
2 | mod blocks; | ||
3 | mod bracket; | ||
4 | mod explode; | ||
5 | mod labeled_delimiter; | ||
6 | mod layer; | ||
7 | mod spacer; | ||
8 | |||
9 | //mod labeled; | ||
10 | //use labeled::Labeled; | ||
11 | |||
12 | use block_list::BlockList; | ||
13 | use blocks::*; | ||
14 | use explode::*; | ||
15 | use labeled_delimiter::*; | ||
16 | use layer::*; | ||
17 | use spacer::*; | ||
18 | |||
19 | use diaphragm_cairo_renderer::CairoRenderer; | ||
20 | use diaphragm_core::{ | ||
21 | colors::Color, | ||
22 | core_shapes::{Text}, | ||
23 | styles::*, | ||
24 | text::FontDescription, | ||
25 | types::*, | ||
26 | *, | ||
27 | }; | ||
28 | use diaphragm_z3_solver::{z3, Z3Context}; | ||
29 | |||
30 | fn main() { | ||
31 | let z3_cfg = z3::Config::new(); | ||
32 | let z3_ctx = z3::Context::new(&z3_cfg); | ||
33 | let ctx = Z3Context::new(&z3_ctx); | ||
34 | |||
35 | let cairo_renderer = CairoRenderer::new(); | ||
36 | |||
37 | let mut runtime = Runtime::new(Box::new(ctx), Box::new(cairo_renderer)); | ||
38 | |||
39 | let solver = runtime.solver_ctx(); | ||
40 | |||
41 | let blue = Color::from_rgb(0.35, 0.35, 1.); | ||
42 | let light_grey = Color::from_rgb(0.8, 0.8, 0.8); | ||
43 | let dark_grey = Color::from_rgb(0.5, 0.5, 0.5); | ||
44 | |||
45 | // ----------------8<---------------- | ||
46 | |||
47 | /* | ||
48 | let mut make_blocks = |grows: &[u8]| { | ||
49 | grows | ||
50 | .iter() | ||
51 | .map(|&grow| { | ||
52 | Drawable::builder(Block { grow }) | ||
53 | .fill(FillStyle::solid(blue)) | ||
54 | .build(solver) | ||
55 | }) | ||
56 | .collect() | ||
57 | }; | ||
58 | |||
59 | let blocks = Drawable::builder(Blocks { | ||
60 | blocks: make_blocks(&[1, 2, 1, 1, 4, 1, 1]), | ||
61 | unit_width: solver.new_free_float(), | ||
62 | }) | ||
63 | .bounds( | ||
64 | Bounds::builder() | ||
65 | .top(Float::Fixed(20.)) | ||
66 | .left(Float::Fixed(20.)) | ||
67 | .width(Float::Fixed(150.)) | ||
68 | .height(Float::Fixed(50.)) | ||
69 | .build(solver), | ||
70 | ) | ||
71 | .build(solver); | ||
72 | |||
73 | runtime.add_drawable(blocks); | ||
74 | */ | ||
75 | |||
76 | // ----------------8<---------------- | ||
77 | |||
78 | /* | ||
79 | let block_unit_width = solver.new_fixed_float(10.); | ||
80 | |||
81 | fn color_block(grow: u8, color: Color, solver: &mut dyn SolverContext) -> Drawable<Block> { | ||
82 | Drawable::builder(Block { grow }) | ||
83 | .fill(FillStyle::solid(color)) | ||
84 | .build(solver) | ||
85 | }; | ||
86 | |||
87 | fn blocks(block_descs: &[(u8, Color)], solver: &mut dyn SolverContext) -> Drawable<Blocks> { | ||
88 | let mut blocks = Vec::new(); | ||
89 | |||
90 | for &(grow, color) in block_descs { | ||
91 | blocks.push(color_block(grow, color, solver)); | ||
92 | } | ||
93 | |||
94 | Drawable::builder(Blocks { | ||
95 | blocks, | ||
96 | unit_width: solver.new_free_float(), | ||
97 | }) | ||
98 | .build(solver) | ||
99 | } | ||
100 | |||
101 | let block_list = Drawable::builder(BlockList { | ||
102 | block_list: vec![ | ||
103 | blocks(&[(1, light_grey), (1, light_grey), (1, light_grey)], solver), | ||
104 | blocks( | ||
105 | &[ | ||
106 | (1, light_grey), | ||
107 | (2, blue), | ||
108 | (1, light_grey), | ||
109 | (1, light_grey), | ||
110 | (1, light_grey), | ||
111 | ], | ||
112 | solver, | ||
113 | ), | ||
114 | blocks(&[(3, light_grey), (2, blue)], solver), | ||
115 | ], | ||
116 | block_unit_width, | ||
117 | bracket_width: block_unit_width, | ||
118 | blocks_vert_padding: block_unit_width, | ||
119 | }) | ||
120 | .bounds( | ||
121 | Bounds::builder() | ||
122 | .top(Float::Fixed(10.)) | ||
123 | .left(Float::Fixed(10.)) | ||
124 | .height(Float::Fixed(40.)) | ||
125 | .build(solver), | ||
126 | ) | ||
127 | .build(solver); | ||
128 | |||
129 | runtime.add_drawable(block_list); | ||
130 | */ | ||
131 | |||
132 | // ----------------8<---------------- | ||
133 | |||
134 | /* | ||
135 | let label_font = FontDescription { | ||
136 | family: String::from("mono"), | ||
137 | style: Default::default(), | ||
138 | weight: Default::default(), | ||
139 | size: Float::Fixed(15.), | ||
140 | }; | ||
141 | |||
142 | let layer = Drawable::builder(Layer { | ||
143 | name: String::from("Hello"), | ||
144 | name_font: FontDescription { | ||
145 | family: Default::default(), | ||
146 | style: Default::default(), | ||
147 | weight: Default::default(), | ||
148 | size: Float::Fixed(10.), | ||
149 | }, | ||
150 | entries: vec![], | ||
151 | padding: Float::Fixed(5.), | ||
152 | entries_width: Float::Fixed(50.), | ||
153 | label_font, | ||
154 | }) | ||
155 | .bounds(Bounds::builder() | ||
156 | .top(Float::Fixed(10.)) | ||
157 | .left(Float::Fixed(10.)) | ||
158 | .width(Float::Fixed(100.)) | ||
159 | .height(Float::Fixed(40.)) | ||
160 | .build(solver)) | ||
161 | .build(solver); | ||
162 | |||
163 | runtime.add_drawable(layer); | ||
164 | */ | ||
165 | |||
166 | // ----------------8<---------------- | ||
167 | |||
168 | let title_font = FontDescription { | ||
169 | family: String::from("Fanwood"), | ||
170 | style: Default::default(), | ||
171 | weight: Default::default(), | ||
172 | size: Float::Fixed(30.), | ||
173 | }; | ||
174 | |||
175 | let label_font = FontDescription { | ||
176 | family: String::from("mono"), | ||
177 | style: Default::default(), | ||
178 | weight: Default::default(), | ||
179 | size: Float::Fixed(15.), | ||
180 | }; | ||
181 | |||
182 | let layer_padding = Float::Fixed(15.); | ||
183 | let layer_margin = Float::Fixed(15.); | ||
184 | //let layer_height = Float::Fixed(40.); | ||
185 | let layer_left_content_padding = Float::Fixed(240.); | ||
186 | //let layer_width = solver.new_free_float(); | ||
187 | //let layer_width_constraint = solver.float_eq(layer_width, Float::Fixed(100.)); | ||
188 | //solver.constrain(layer_width_constraint); | ||
189 | |||
190 | let mut layer_top = Float::Fixed(15.); | ||
191 | |||
192 | let entries_width = solver.new_free_float(); | ||
193 | let mut all_entry_content_widths = vec![]; | ||
194 | |||
195 | let layer_width = solver.float_add(&[ | ||
196 | layer_left_content_padding, | ||
197 | entries_width, | ||
198 | layer_padding, | ||
199 | layer_padding, | ||
200 | ]); | ||
201 | |||
202 | let blocks_vert_padding = Float::Fixed(10.); | ||
203 | |||
204 | let block_unit_width = Float::Fixed(20.); | ||
205 | |||
206 | let blocks_height = Float::Fixed(26.); | ||
207 | let block_list_height = | ||
208 | solver.float_add(&[blocks_height, blocks_vert_padding, blocks_vert_padding]); | ||
209 | |||
210 | let blue_transaction_len = 5; | ||
211 | |||
212 | let blocks = |grows: &[u8], color, solver: &mut dyn SolverContext| { | ||
213 | let mut blocks = Vec::new(); | ||
214 | |||
215 | for &grow in grows { | ||
216 | blocks.push( | ||
217 | Drawable::builder(Block { grow }) | ||
218 | .fill(FillStyle::solid(color)) | ||
219 | .build(solver), | ||
220 | ); | ||
221 | } | ||
222 | |||
223 | Drawable::builder(Blocks { | ||
224 | blocks, | ||
225 | unit_width: block_unit_width, | ||
226 | }) | ||
227 | .bounds(Bounds::builder().height(blocks_height).build(solver)) | ||
228 | .build(solver) | ||
229 | }; | ||
230 | |||
231 | let blocks_by_len = | ||
232 | |len, color, solver: &mut dyn SolverContext| blocks(&vec![1; len], color, solver); | ||
233 | |||
234 | // Log API | ||
235 | { | ||
236 | let blocks = blocks_by_len(blue_transaction_len, blue, solver); | ||
237 | let blocks_width = blocks.bounds().width(solver); | ||
238 | |||
239 | let layer = Drawable::builder(Layer { | ||
240 | name: String::from("LogAPI"), | ||
241 | name_font: title_font.clone(), | ||
242 | label_font: label_font.clone(), | ||
243 | padding: layer_padding, | ||
244 | entries: vec![Entry { | ||
245 | label: Some(String::from("activeTxn: ")), | ||
246 | label_vert_center_offset: None, | ||
247 | content: blocks.into(), | ||
248 | }], | ||
249 | entries_width, | ||
250 | }) | ||
251 | .bounds( | ||
252 | Bounds::builder() | ||
253 | // TODO: Replace that with a "distributeHorizontally" that applies to all layer | ||
254 | .top(layer_top) | ||
255 | .left(Float::Fixed(10.)) | ||
256 | .width(layer_width) | ||
257 | .build(solver), | ||
258 | ) | ||
259 | .build(solver); | ||
260 | |||
261 | all_entry_content_widths.push(blocks_width); | ||
262 | |||
263 | let layer_height = layer.bounds().height(solver); | ||
264 | layer_top = solver.float_add(&[layer_top, layer_height, layer_margin]); | ||
265 | |||
266 | runtime.add_drawable(layer); | ||
267 | } | ||
268 | |||
269 | let solver = runtime.solver_ctx(); | ||
270 | |||
271 | let other_transactions = [2, 7, 4]; | ||
272 | |||
273 | let group_commit_left; | ||
274 | let group_commit_right; | ||
275 | let group_commit_bottom; | ||
276 | |||
277 | // Group Commit | ||
278 | { | ||
279 | let mut block_list = Vec::new(); | ||
280 | for &blocks_count in other_transactions.iter() { | ||
281 | block_list.push(blocks_by_len(blocks_count, dark_grey, solver)); | ||
282 | } | ||
283 | |||
284 | block_list.push(blocks_by_len(blue_transaction_len, blue, solver)); | ||
285 | |||
286 | let block_list = Drawable::builder(BlockList { | ||
287 | block_list, | ||
288 | block_unit_width, | ||
289 | blocks_vert_padding: Float::Fixed(10.), | ||
290 | bracket_width: Float::Fixed(10.), | ||
291 | }) | ||
292 | .bounds(Bounds::builder().height(block_list_height).build(solver)) | ||
293 | .build(solver); | ||
294 | |||
295 | let content_width = block_list.bounds().width(solver); | ||
296 | |||
297 | group_commit_left = block_list.bounds().left(solver); | ||
298 | group_commit_right = block_list.bounds().right(solver); | ||
299 | group_commit_bottom = block_list.bounds().bottom(solver); | ||
300 | |||
301 | let layer = Drawable::builder(Layer { | ||
302 | name: String::from("GroupCommit"), | ||
303 | name_font: title_font.clone(), | ||
304 | label_font: label_font.clone(), | ||
305 | padding: layer_padding, | ||
306 | entries: vec![Entry { | ||
307 | label: Some(String::from("commitedTxns: ")), | ||
308 | label_vert_center_offset: None, | ||
309 | content: block_list.into(), | ||
310 | }], | ||
311 | entries_width, | ||
312 | }) | ||
313 | .bounds( | ||
314 | Bounds::builder() | ||
315 | .top(layer_top) | ||
316 | .left(Float::Fixed(10.)) | ||
317 | .width(layer_width) | ||
318 | .build(solver), | ||
319 | ) | ||
320 | .build(solver); | ||
321 | |||
322 | all_entry_content_widths.push(content_width); | ||
323 | |||
324 | let layer_height = layer.bounds().height(solver); | ||
325 | layer_top = solver.float_add(&[layer_top, layer_height, layer_margin]); | ||
326 | |||
327 | runtime.add_drawable(layer); | ||
328 | } | ||
329 | |||
330 | let solver = runtime.solver_ctx(); | ||
331 | |||
332 | let commit_in_log_left; | ||
333 | let commit_in_log_right; | ||
334 | let commit_in_log_top; | ||
335 | let big_block_grow; | ||
336 | let small_block_unit_width; | ||
337 | let data_in_log_left; | ||
338 | let data_in_log_right; | ||
339 | let data_in_log_bottom; | ||
340 | |||
341 | // Disk log | ||
342 | { | ||
343 | big_block_grow = (blue_transaction_len + other_transactions.iter().sum::<usize>()) as u8; | ||
344 | |||
345 | let make_block = |grow, color, solver: &mut dyn SolverContext| { | ||
346 | Drawable::builder(Block { grow }) | ||
347 | .bounds(Bounds::builder().height(blocks_height).build(solver)) | ||
348 | .fill(FillStyle::solid(color)) | ||
349 | .build(solver) | ||
350 | }; | ||
351 | |||
352 | let make_blocks = |block_descs: &[_], solver: &mut dyn SolverContext| { | ||
353 | let mut blocks = Vec::new(); | ||
354 | |||
355 | for &(grow, color) in block_descs { | ||
356 | blocks.push(make_block(grow, color, solver)); | ||
357 | } | ||
358 | |||
359 | blocks | ||
360 | }; | ||
361 | |||
362 | let header_block = make_block(big_block_grow, Color::black(), solver); | ||
363 | let header_block_width = header_block.bounds().width(solver); | ||
364 | |||
365 | let mut blocks = vec![header_block]; | ||
366 | |||
367 | blocks.append(&mut make_blocks( | ||
368 | &[(big_block_grow, light_grey), (big_block_grow, light_grey)], | ||
369 | solver, | ||
370 | )); | ||
371 | |||
372 | let sample_commit_start_id = blocks.len(); | ||
373 | |||
374 | for &txn_block_count in other_transactions.iter() { | ||
375 | for _i in 0..txn_block_count { | ||
376 | blocks.push(make_block(1, dark_grey, solver)); | ||
377 | } | ||
378 | } | ||
379 | |||
380 | for _ in 0..blue_transaction_len { | ||
381 | blocks.push(make_block(1, blue, solver)); | ||
382 | } | ||
383 | |||
384 | let sample_commit_end_id = blocks.len() - 1; | ||
385 | |||
386 | commit_in_log_left = blocks[sample_commit_start_id].bounds().left(solver); | ||
387 | commit_in_log_right = blocks[sample_commit_end_id].bounds().right(solver); | ||
388 | commit_in_log_top = blocks[sample_commit_end_id].bounds().top(solver); | ||
389 | |||
390 | let log_data_blocks_widths: Vec<_> = blocks[1..] | ||
391 | .iter() | ||
392 | .map(|block| block.bounds().width(solver)) | ||
393 | .collect(); | ||
394 | let log_data_blocks_width = solver.float_add(&log_data_blocks_widths); | ||
395 | |||
396 | let log_space_block = make_block(big_block_grow, Color::white(), solver); | ||
397 | let log_space_width = log_space_block.bounds().width(solver); | ||
398 | blocks.push(log_space_block); | ||
399 | |||
400 | // Make it so that it takes as much space as the bigger one | ||
401 | small_block_unit_width = solver.new_free_float(); | ||
402 | |||
403 | let disk_log_layout = Drawable::builder(Blocks { | ||
404 | blocks, | ||
405 | unit_width: small_block_unit_width, | ||
406 | }) | ||
407 | .bounds(Bounds::builder().width(entries_width).build(solver)) | ||
408 | .build(solver); | ||
409 | |||
410 | let mut make_delimiter_label = |content| { | ||
411 | Drawable::builder(Text { | ||
412 | content: String::from(content), | ||
413 | font: FontDescription { | ||
414 | family: String::from("Fanwood"), | ||
415 | size: Float::Fixed(15.), | ||
416 | style: Default::default(), | ||
417 | weight: Default::default(), | ||
418 | }, | ||
419 | }) | ||
420 | .build(solver) | ||
421 | }; | ||
422 | |||
423 | let delimiters = vec![ | ||
424 | Delimiter { | ||
425 | label: make_delimiter_label("header"), | ||
426 | width: header_block_width, | ||
427 | }, | ||
428 | Delimiter { | ||
429 | label: make_delimiter_label("data"), | ||
430 | width: log_data_blocks_width, | ||
431 | }, | ||
432 | Delimiter { | ||
433 | label: make_delimiter_label("available"), | ||
434 | width: log_space_width, | ||
435 | }, | ||
436 | ]; | ||
437 | |||
438 | let disk_log_label = Drawable::builder(LabeledDelimiter { | ||
439 | delimiters, | ||
440 | tick_height: Float::Fixed(10.), | ||
441 | }) | ||
442 | .bounds(Bounds::builder().width(entries_width).build(solver)) | ||
443 | .build(solver); | ||
444 | |||
445 | let label_left = disk_log_label.bounds().left(solver); | ||
446 | data_in_log_left = solver.float_add(&[label_left, header_block_width]); | ||
447 | data_in_log_right = solver.float_add(&[data_in_log_left, log_data_blocks_width]); | ||
448 | data_in_log_bottom = disk_log_label.bounds().bottom(solver); | ||
449 | |||
450 | let layer = Drawable::builder(Layer { | ||
451 | name: String::from("DiskLog"), | ||
452 | name_font: title_font.clone(), | ||
453 | label_font: label_font.clone(), | ||
454 | padding: layer_padding, | ||
455 | entries: vec![ | ||
456 | Entry { | ||
457 | label: Some(String::from("disk log: ")), | ||
458 | label_vert_center_offset: None, | ||
459 | content: disk_log_layout.into(), | ||
460 | }, | ||
461 | Entry { | ||
462 | label: None, | ||
463 | label_vert_center_offset: None, | ||
464 | content: disk_log_label.into(), | ||
465 | }, | ||
466 | ], | ||
467 | entries_width, | ||
468 | }) | ||
469 | .bounds( | ||
470 | Bounds::builder() | ||
471 | .top(layer_top) | ||
472 | .left(Float::Fixed(10.)) | ||
473 | .width(layer_width) | ||
474 | .build(solver), | ||
475 | ) | ||
476 | .build(solver); | ||
477 | |||
478 | let layer_height = layer.bounds().height(solver); | ||
479 | layer_top = solver.float_add(&[layer_top, layer_height, layer_margin]); | ||
480 | |||
481 | runtime.add_drawable(layer); | ||
482 | } | ||
483 | |||
484 | let solver = runtime.solver_ctx(); | ||
485 | |||
486 | // Explode GroupLog -> DiskLog | ||
487 | { | ||
488 | let margin = Float::Fixed(6.); | ||
489 | let explode_top = solver.float_add(&[group_commit_bottom, margin]); | ||
490 | let explode_bottom = solver.float_sub(&[commit_in_log_top, margin]); | ||
491 | |||
492 | let explode = Drawable::builder(Explode { | ||
493 | top_left: Point2D::new(group_commit_left, explode_top), | ||
494 | top_right: Point2D::new(group_commit_right, explode_top), | ||
495 | bottom_left: Point2D::new(commit_in_log_left, explode_bottom), | ||
496 | bottom_right: Point2D::new(commit_in_log_right, explode_bottom), | ||
497 | |||
498 | arm_length: Float::Fixed(12.), | ||
499 | }) | ||
500 | .stroke( | ||
501 | StrokeStyle::builder() | ||
502 | .dash(DashStyle::new(vec![Float::Fixed(2.)], Float::Fixed(0.))) | ||
503 | .build(), | ||
504 | ) | ||
505 | .build(solver); | ||
506 | |||
507 | runtime.add_drawable(explode); | ||
508 | } | ||
509 | |||
510 | let solver = runtime.solver_ctx(); | ||
511 | |||
512 | let data_in_applier_top; | ||
513 | let data_in_applier_left; | ||
514 | let data_in_applier_right; | ||
515 | |||
516 | // Applier | ||
517 | { | ||
518 | // TODO: this is exactly the same as before... | ||
519 | let make_block = |grow, color, solver: &mut dyn SolverContext| { | ||
520 | Drawable::builder(Block { grow }) | ||
521 | .bounds(Bounds::builder().height(blocks_height).build(solver)) | ||
522 | .fill(FillStyle::solid(color)) | ||
523 | .build(solver) | ||
524 | }; | ||
525 | |||
526 | let _make_blocks = |block_descs: &[_], solver: &mut dyn SolverContext| { | ||
527 | let mut blocks = Vec::new(); | ||
528 | |||
529 | for &(grow, color) in block_descs { | ||
530 | blocks.push(make_block(grow, color, solver)); | ||
531 | } | ||
532 | |||
533 | blocks | ||
534 | }; | ||
535 | |||
536 | let mut blocks = vec![]; | ||
537 | |||
538 | blocks.push(make_block(big_block_grow, light_grey, solver)); | ||
539 | for &txn_block_count in other_transactions.iter() { | ||
540 | for _i in 0..txn_block_count { | ||
541 | blocks.push(make_block(1, dark_grey, solver)); | ||
542 | } | ||
543 | } | ||
544 | |||
545 | for _ in 0..blue_transaction_len { | ||
546 | blocks.push(make_block(1, blue, solver)); | ||
547 | } | ||
548 | |||
549 | let blocks = Drawable::builder(Blocks { | ||
550 | blocks, | ||
551 | unit_width: small_block_unit_width, | ||
552 | }) | ||
553 | .build(solver); | ||
554 | |||
555 | data_in_applier_top = blocks.bounds().top(solver); | ||
556 | data_in_applier_left = blocks.bounds().left(solver); | ||
557 | data_in_applier_right = blocks.bounds().right(solver); | ||
558 | |||
559 | let spaced_data = Spacer::builder(blocks) | ||
560 | .horizontal_align_center(solver) | ||
561 | .vertical_align_center_with(Float::Fixed(10.)) | ||
562 | .build(solver); | ||
563 | |||
564 | let spaced_data = Drawable::builder(spaced_data) | ||
565 | .bounds(Bounds::builder().width(entries_width).build(solver)) | ||
566 | .build(solver); | ||
567 | |||
568 | let disk_data = Drawable::builder(Block { grow: 1 }) | ||
569 | .bounds( | ||
570 | Bounds::builder() | ||
571 | .height(blocks_height) | ||
572 | .width(entries_width) | ||
573 | .build(solver), | ||
574 | ) | ||
575 | .fill(FillStyle::solid(Color::white())) | ||
576 | .build(solver); | ||
577 | |||
578 | let layer = Drawable::builder(Layer { | ||
579 | name: String::from("Applier"), | ||
580 | name_font: title_font.clone(), | ||
581 | label_font: label_font.clone(), | ||
582 | padding: layer_padding, | ||
583 | entries: vec![ | ||
584 | Entry { | ||
585 | label: None, | ||
586 | label_vert_center_offset: None, | ||
587 | content: spaced_data.into(), | ||
588 | }, | ||
589 | Entry { | ||
590 | label: Some(String::from("disk data:")), | ||
591 | label_vert_center_offset: None, | ||
592 | content: disk_data.into(), | ||
593 | }, | ||
594 | ], | ||
595 | entries_width, | ||
596 | }) | ||
597 | .bounds( | ||
598 | Bounds::builder() | ||
599 | .top(layer_top) | ||
600 | .left(Float::Fixed(10.)) | ||
601 | .width(layer_width) | ||
602 | .build(solver), | ||
603 | ) | ||
604 | .build(solver); | ||
605 | |||
606 | runtime.add_drawable(layer); | ||
607 | } | ||
608 | |||
609 | let solver = runtime.solver_ctx(); | ||
610 | |||
611 | // Explode DiskLog -> Applier | ||
612 | { | ||
613 | let margin = Float::Fixed(6.); | ||
614 | let explode_top = solver.float_add(&[data_in_log_bottom, margin]); | ||
615 | let explode_bottom = solver.float_sub(&[data_in_applier_top, margin]); | ||
616 | |||
617 | let explode = Drawable::builder(Explode { | ||
618 | top_left: Point2D::new(data_in_log_left, explode_top), | ||
619 | top_right: Point2D::new(data_in_log_right, explode_top), | ||
620 | bottom_left: Point2D::new(data_in_applier_left, explode_bottom), | ||
621 | bottom_right: Point2D::new(data_in_applier_right, explode_bottom), | ||
622 | |||
623 | arm_length: Float::Fixed(12.), | ||
624 | }) | ||
625 | .stroke( | ||
626 | StrokeStyle::builder() | ||
627 | .dash(DashStyle::new(vec![Float::Fixed(2.)], Float::Fixed(0.))) | ||
628 | .build(), | ||
629 | ) | ||
630 | .build(solver); | ||
631 | |||
632 | runtime.add_drawable(explode); | ||
633 | } | ||
634 | |||
635 | let solver = runtime.solver_ctx(); | ||
636 | |||
637 | let max_entry_content_width = solver.float_max(&all_entry_content_widths); | ||
638 | let entries_width_constraint = solver.float_eq(entries_width, max_entry_content_width); | ||
639 | solver.constrain(entries_width_constraint); | ||
640 | |||
641 | runtime.render(); | ||
642 | } | ||
diff --git a/examples/lib-dfscq-log/src/main2.rs b/examples/lib-dfscq-log/src/main2.rs new file mode 100644 index 0000000..9db235d --- /dev/null +++ b/examples/lib-dfscq-log/src/main2.rs | |||
@@ -0,0 +1,61 @@ | |||
1 | fn main() { | ||
2 | let solver = Solver::new(); | ||
3 | let output = Output::new(); | ||
4 | |||
5 | let blue_transaction_len = 5; | ||
6 | |||
7 | let blue_block = Block::builder().color(blue); | ||
8 | let dark_gray_block = Block::builder().color(dark_gray); | ||
9 | |||
10 | let log_layer = Layer::builder() | ||
11 | .name("LogAPI") | ||
12 | .push_entry( | ||
13 | Entry::builder() | ||
14 | .label("activeTxn: ") | ||
15 | .content( | ||
16 | Blocks::builder() | ||
17 | .push(blue_transaction_len, blue_block.build()) | ||
18 | .build(), | ||
19 | ) | ||
20 | .build(), | ||
21 | ) | ||
22 | .build(); | ||
23 | |||
24 | let other_transactions = [2, 7, 4]; | ||
25 | |||
26 | let group_commit_layer = Layer::builder() | ||
27 | .name("GroupCommit") | ||
28 | .push_entry( | ||
29 | Entry::builder() | ||
30 | .label("commitedTxn: ") | ||
31 | .content( | ||
32 | BlockList::builder() | ||
33 | .append( | ||
34 | other_transactions | ||
35 | .iter() | ||
36 | // TODO: need collect? | ||
37 | .map(|len| Blocks::builder().push(len, dark_gray_block.build())), | ||
38 | ) | ||
39 | .push(Blocks::builder().push(blue_transaction_len, blue_block.build())) | ||
40 | .build(), | ||
41 | ) | ||
42 | .build(), | ||
43 | ) | ||
44 | .build(); | ||
45 | |||
46 | let disk_log_layer = todo!(); | ||
47 | |||
48 | let applier_layer = todo!(); | ||
49 | |||
50 | let layers = &[ | ||
51 | &log_layer, | ||
52 | &group_commit_layer, | ||
53 | &disk_log_layer, | ||
54 | &applier_layer, | ||
55 | ]; | ||
56 | constraints::distribute_vertically(solver, layers); | ||
57 | constraints::align_left(solver, layers); | ||
58 | |||
59 | solver.solve(); | ||
60 | output.write_to_stdout(Output::Format::SVG); | ||
61 | } | ||
diff --git a/examples/lib-dfscq-log/src/spacer.rs b/examples/lib-dfscq-log/src/spacer.rs new file mode 100644 index 0000000..6878183 --- /dev/null +++ b/examples/lib-dfscq-log/src/spacer.rs | |||
@@ -0,0 +1,133 @@ | |||
1 | use diaphragm_core::{ | ||
2 | types::{Float, ShapeContext}, | ||
3 | ComplexShape, DrawResult, DynDrawable, SolverContext, | ||
4 | }; | ||
5 | |||
6 | #[derive(Clone)] | ||
7 | pub struct Spacer { | ||
8 | pub margin_left: Float, | ||
9 | pub margin_right: Float, | ||
10 | pub margin_top: Float, | ||
11 | pub margin_bottom: Float, | ||
12 | pub content: DynDrawable, | ||
13 | } | ||
14 | |||
15 | impl Spacer { | ||
16 | pub fn builder<T: Into<DynDrawable>>(drawable: T) -> SpacerBuilder { | ||
17 | SpacerBuilder::new(drawable) | ||
18 | } | ||
19 | } | ||
20 | |||
21 | impl ComplexShape for Spacer { | ||
22 | fn draw(&self, context: &ShapeContext, solver: &mut dyn SolverContext) -> DrawResult { | ||
23 | let mut result = DrawResult::new(); | ||
24 | |||
25 | let bounds_left = context.bounds().left(solver); | ||
26 | let bounds_right = context.bounds().right(solver); | ||
27 | let bounds_top = context.bounds().top(solver); | ||
28 | let bounds_bottom = context.bounds().bottom(solver); | ||
29 | |||
30 | let wanted_content_left = solver.float_add(&[bounds_left, self.margin_left]); | ||
31 | let content_left = self.content.bounds().left(solver); | ||
32 | let content_left_constraint = solver.float_eq(content_left, wanted_content_left); | ||
33 | solver.constrain(content_left_constraint); | ||
34 | |||
35 | let wanted_content_right = solver.float_sub(&[bounds_right, self.margin_right]); | ||
36 | let content_right = self.content.bounds().right(solver); | ||
37 | let content_right_constraint = solver.float_eq(content_right, wanted_content_right); | ||
38 | solver.constrain(content_right_constraint); | ||
39 | |||
40 | let wanted_content_top = solver.float_add(&[bounds_top, self.margin_top]); | ||
41 | let content_top = self.content.bounds().top(solver); | ||
42 | let content_top_constraint = solver.float_eq(content_top, wanted_content_top); | ||
43 | solver.constrain(content_top_constraint); | ||
44 | |||
45 | let wanted_content_bottom = solver.float_sub(&[bounds_bottom, self.margin_bottom]); | ||
46 | let content_bottom = self.content.bounds().bottom(solver); | ||
47 | let content_bottom_constraint = solver.float_eq(content_bottom, wanted_content_bottom); | ||
48 | solver.constrain(content_bottom_constraint); | ||
49 | |||
50 | result.push_dyn(self.content.clone()); | ||
51 | |||
52 | result | ||
53 | } | ||
54 | } | ||
55 | |||
56 | #[derive(Clone)] | ||
57 | pub struct SpacerBuilder { | ||
58 | pub margin_left: Option<Float>, | ||
59 | pub margin_right: Option<Float>, | ||
60 | pub margin_top: Option<Float>, | ||
61 | pub margin_bottom: Option<Float>, | ||
62 | pub content: DynDrawable, | ||
63 | } | ||
64 | |||
65 | impl SpacerBuilder { | ||
66 | pub fn new<T: Into<DynDrawable>>(drawable: T) -> Self { | ||
67 | SpacerBuilder { | ||
68 | margin_left: None, | ||
69 | margin_right: None, | ||
70 | margin_top: None, | ||
71 | margin_bottom: None, | ||
72 | content: drawable.into(), | ||
73 | } | ||
74 | } | ||
75 | |||
76 | pub fn margin_left(&mut self, margin_left: Float) -> &mut Self { | ||
77 | self.margin_left = Some(margin_left); | ||
78 | self | ||
79 | } | ||
80 | |||
81 | pub fn margin_right(&mut self, margin_right: Float) -> &mut Self { | ||
82 | self.margin_right = Some(margin_right); | ||
83 | self | ||
84 | } | ||
85 | |||
86 | pub fn margin_top(&mut self, margin_top: Float) -> &mut Self { | ||
87 | self.margin_top = Some(margin_top); | ||
88 | self | ||
89 | } | ||
90 | |||
91 | pub fn margin_bottom(&mut self, margin_bottom: Float) -> &mut Self { | ||
92 | self.margin_bottom = Some(margin_bottom); | ||
93 | self | ||
94 | } | ||
95 | |||
96 | pub fn vertical_align_center(&mut self, solver: &mut dyn SolverContext) -> &mut Self { | ||
97 | let vertical_margin = solver.new_free_float(); | ||
98 | self.margin_top = Some(vertical_margin); | ||
99 | self.margin_bottom = Some(vertical_margin); | ||
100 | self | ||
101 | } | ||
102 | |||
103 | pub fn vertical_align_center_with(&mut self, margin: Float) -> &mut Self { | ||
104 | self.margin_top = Some(margin); | ||
105 | self.margin_bottom = Some(margin); | ||
106 | self | ||
107 | } | ||
108 | |||
109 | pub fn horizontal_align_center(&mut self, solver: &mut dyn SolverContext) -> &mut Self { | ||
110 | let horizontal_margin = solver.new_free_float(); | ||
111 | self.margin_left = Some(horizontal_margin); | ||
112 | self.margin_right = Some(horizontal_margin); | ||
113 | self | ||
114 | } | ||
115 | |||
116 | pub fn horizontal_align_center_with(&mut self, margin: Float) -> &mut Self { | ||
117 | self.margin_left = Some(margin); | ||
118 | self.margin_right = Some(margin); | ||
119 | self | ||
120 | } | ||
121 | |||
122 | pub fn build(&self, solver: &mut dyn SolverContext) -> Spacer { | ||
123 | Spacer { | ||
124 | margin_left: self.margin_left.unwrap_or_else(|| solver.new_free_float()), | ||
125 | margin_right: self.margin_right.unwrap_or_else(|| solver.new_free_float()), | ||
126 | margin_top: self.margin_top.unwrap_or_else(|| solver.new_free_float()), | ||
127 | margin_bottom: self | ||
128 | .margin_bottom | ||
129 | .unwrap_or_else(|| solver.new_free_float()), | ||
130 | content: self.content.clone(), | ||
131 | } | ||
132 | } | ||
133 | } | ||