From 92a02c34628343153b33602eae00cef46e28d191 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Thu, 22 Dec 2022 12:19:59 +0100 Subject: WIP --- examples/lib-dfscq-log/src/block_list.rs | 183 +++++++ examples/lib-dfscq-log/src/blocks.rs | 72 +++ examples/lib-dfscq-log/src/bracket.rs | 42 ++ examples/lib-dfscq-log/src/explode.rs | 86 ++++ examples/lib-dfscq-log/src/labeled.rs | 64 +++ examples/lib-dfscq-log/src/labeled_delimiter.rs | 121 +++++ examples/lib-dfscq-log/src/layer.rs | 169 +++++++ examples/lib-dfscq-log/src/main.rs | 642 ++++++++++++++++++++++++ examples/lib-dfscq-log/src/main2.rs | 61 +++ examples/lib-dfscq-log/src/spacer.rs | 133 +++++ 10 files changed, 1573 insertions(+) create mode 100644 examples/lib-dfscq-log/src/block_list.rs create mode 100644 examples/lib-dfscq-log/src/blocks.rs create mode 100644 examples/lib-dfscq-log/src/bracket.rs create mode 100644 examples/lib-dfscq-log/src/explode.rs create mode 100644 examples/lib-dfscq-log/src/labeled.rs create mode 100644 examples/lib-dfscq-log/src/labeled_delimiter.rs create mode 100644 examples/lib-dfscq-log/src/layer.rs create mode 100644 examples/lib-dfscq-log/src/main.rs create mode 100644 examples/lib-dfscq-log/src/main2.rs create mode 100644 examples/lib-dfscq-log/src/spacer.rs (limited to 'examples/lib-dfscq-log/src') 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 @@ +use super::blocks::Blocks; +use super::bracket::*; + +use diaphragm_core::{ + core_shapes::Text, + text::FontDescription, + types::{Bounds, Float, ShapeContext}, + ComplexShape, DrawResult, Drawable, SolverContext, +}; + +#[derive(Clone)] +pub struct BlockList { + pub block_list: Vec>, + pub block_unit_width: Float, + pub bracket_width: Float, + pub blocks_vert_padding: Float, +} + +impl ComplexShape for BlockList { + fn draw(&self, context: &ShapeContext, solver: &mut dyn SolverContext) -> DrawResult { + let mut result = DrawResult::new(); + + let bounds = context.bounds(); + + // Left bracket + let opening_bracket_left = { + let bracket = Drawable::builder(Bracket { + r#type: BracketType::Opening, + }) + .bounds( + Bounds::builder() + .top(bounds.top(solver)) + .left(bounds.left(solver)) + .width(self.bracket_width) + .height(bounds.height(solver)) + .build(solver), + ) + .stroke(context.stroke().clone()) + .build(solver); + + let bracket_left = bracket.bounds().left(solver); + + result.push(bracket); + + bracket_left + }; + + let bounds_left = bounds.left(solver); + let mut x_acc = solver.float_add(&[bounds_left, self.bracket_width]); + + // Block list + + let bounds_top = bounds.top(solver); + let bounds_height = bounds.height(solver); + let wanted_blocks_top = solver.float_add(&[bounds_top, self.blocks_vert_padding]); + let wanted_blocks_height = solver.float_sub(&[ + bounds_height, + self.blocks_vert_padding, + self.blocks_vert_padding, + ]); + + let mut block_list_iter = self.block_list.iter(); + + // First blocks + if let Some(first_blocks) = block_list_iter.next() { + // TODO: waaay too verbose + + let blocks_top = first_blocks.bounds().top(solver); + let blocks_top_constraint = solver.float_eq(blocks_top, wanted_blocks_top); + solver.constrain(blocks_top_constraint); + + let blocks_left = first_blocks.bounds().left(solver); + let blocks_left_constraint = solver.float_eq(blocks_left, x_acc); + solver.constrain(blocks_left_constraint); + + let blocks_height = first_blocks.bounds().height(solver); + let blocks_height_constraint = solver.float_eq(blocks_height, wanted_blocks_height); + solver.constrain(blocks_height_constraint); + + let block_unit_width_constraint = + solver.float_eq(first_blocks.shape().unit_width, self.block_unit_width); + solver.constrain(block_unit_width_constraint); + + result.push(first_blocks.clone()); + + let blocks_width = first_blocks.bounds().width(solver); + x_acc = solver.float_add(&[x_acc, blocks_width]); + } + + // Rest of the blocks + for blocks in block_list_iter { + // Comma + let comma = Drawable::builder(Text { + content: String::from(", "), + font: FontDescription { + family: String::from("mono"), + style: Default::default(), + weight: Default::default(), + size: wanted_blocks_height, + }, + }) + .bounds( + Bounds::builder() + .top(wanted_blocks_top) + .left(x_acc) + .build(solver), + ) + .stroke(context.stroke().clone()) + .build(solver); + + let comma_width = comma.bounds().width(solver); + + x_acc = solver.float_add(&[x_acc, comma_width]); + + result.push(comma); + + // Blocks + /* + let blocks_context = ShapeContext { + bounds: Bounds { + top: blocks_top, + left: x_acc, + height: blocks_height, + width: blocks_width, + }, + fill: FillStyle::default(), + stroke: StrokeStyle::default(), + }; + */ + + let blocks_top = blocks.bounds().top(solver); + let blocks_top_constraint = solver.float_eq(blocks_top, wanted_blocks_top); + solver.constrain(blocks_top_constraint); + + let blocks_left = blocks.bounds().left(solver); + let blocks_left_constraint = solver.float_eq(blocks_left, x_acc); + solver.constrain(blocks_left_constraint); + + let blocks_height = blocks.bounds().height(solver); + let blocks_height_constraint = solver.float_eq(blocks_height, wanted_blocks_height); + solver.constrain(blocks_height_constraint); + + let block_unit_width_constraint = + solver.float_eq(blocks.shape().unit_width, self.block_unit_width); + solver.constrain(block_unit_width_constraint); + + result.push(blocks.clone()); + + let blocks_width = blocks.bounds().width(solver); + x_acc = solver.float_add(&[x_acc, blocks_width]); + } + + // Right bracket + let closing_bracket_right = { + let bracket = Drawable::builder(Bracket { + r#type: BracketType::Closing, + }) + .bounds( + Bounds::builder() + .top(bounds.top(solver)) + .left(x_acc) + .width(self.bracket_width) + .height(bounds.height(solver)) + .build(solver), + ) + .stroke(context.stroke().clone()) + .build(solver); + + let bracket_right = bracket.bounds().right(solver); + + result.push(bracket); + + bracket_right + }; + + let this_width = solver.float_sub(&[closing_bracket_right, opening_bracket_left]); + let bounds_width = bounds.width(solver); + let bounds_width_constraint = solver.float_eq(bounds_width, this_width); + solver.constrain(bounds_width_constraint); + + result + } +} 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 @@ +use diaphragm_core::{ + core_shapes::Rectangle, + types::{Float, ShapeContext}, + ComplexShape, DrawResult, Drawable, SolverContext, +}; + +#[derive(Debug, Clone)] +pub struct Block { + pub grow: u8, +} + +impl ComplexShape for Block { + fn draw(&self, context: &ShapeContext, _solver: &mut dyn SolverContext) -> DrawResult { + let mut result = DrawResult::new(); + + // Grow is handled at the upper level + let block = Drawable::new(Rectangle {}, context.clone()); + + result.push(block); + + result + } +} + +#[derive(Debug, Clone)] +pub struct Blocks { + pub blocks: Vec>, + pub unit_width: Float, +} + +impl ComplexShape for Blocks { + fn draw(&self, context: &ShapeContext, solver: &mut dyn SolverContext) -> DrawResult { + let mut result = DrawResult::new(); + + let sum: u8 = self.blocks.iter().map(|block| block.shape().grow).sum(); + + let mut rect_left = context.bounds().left(solver); + let rect_top = context.bounds().top(solver); + let rect_height = context.bounds().height(solver); + + for block in &self.blocks { + let block_top = block.bounds().top(solver); + let rect_top_constraint = solver.float_eq(block_top, rect_top); + solver.constrain(rect_top_constraint); + + let block_left = block.bounds().left(solver); + let rect_left_constraint = solver.float_eq(block_left, rect_left); + solver.constrain(rect_left_constraint); + + let grow = Float::Fixed(block.shape().grow as f64); + let block_width = block.bounds().width(solver); + let rect_width = solver.float_mul(&[self.unit_width, grow]); + let rect_width_constraint = solver.float_eq(block_width, rect_width); + solver.constrain(rect_width_constraint); + + let block_height = block.bounds().height(solver); + let rect_height_constraint = solver.float_eq(block_height, rect_height); + solver.constrain(rect_height_constraint); + + result.push(block.clone()); + + rect_left = solver.float_add(&[rect_left, rect_width]); + } + + let this_width = solver.float_mul(&[self.unit_width, Float::Fixed(sum as f64)]); + let bounds_width = context.bounds().width(solver); + let bounds_width_constraint = solver.float_eq(bounds_width, this_width); + solver.constrain(bounds_width_constraint); + + result + } +} 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 @@ +use diaphragm_core::{ + core_shapes::StraightPath, types::ShapeContext, ComplexShape, DrawResult, Drawable, + SolverContext, +}; + +#[derive(Copy, Clone)] +pub enum BracketType { + Opening, + Closing, +} + +#[derive(Clone)] +pub struct Bracket { + pub r#type: BracketType, +} + +impl ComplexShape for Bracket { + fn draw(&self, context: &ShapeContext, solver: &mut dyn SolverContext) -> DrawResult { + let mut result = DrawResult::new(); + + let bounds = context.bounds(); + + let path = match self.r#type { + BracketType::Opening => StraightPath::new(vec![ + bounds.top_right(solver), + bounds.top_left(solver), + bounds.bottom_left(solver), + bounds.bottom_right(solver), + ]), + BracketType::Closing => StraightPath::new(vec![ + bounds.top_left(solver), + bounds.top_right(solver), + bounds.bottom_right(solver), + bounds.bottom_left(solver), + ]), + }; + + result.push(Drawable::new(path, context.clone())); + + result + } +} 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 @@ +use diaphragm_core::{ + core_shapes::StraightPath, + types::{Float, Point2D, ShapeContext}, + ComplexShape, DrawResult, Drawable, SolverContext, +}; + +#[derive(Clone)] +pub struct Explode { + pub top_left: Point2D, + pub top_right: Point2D, + pub bottom_left: Point2D, + pub bottom_right: Point2D, + + pub arm_length: Float, +} + +impl ComplexShape for Explode { + fn draw(&self, context: &ShapeContext, solver: &mut dyn SolverContext) -> DrawResult { + let mut result = DrawResult::new(); + + let wanted_top = solver.float_min(&[self.top_left.y(), self.top_right.y()]); + let wanted_left = solver.float_min(&[self.top_left.x(), self.bottom_left.x()]); + let wanted_bottom = solver.float_max(&[self.bottom_left.x(), self.bottom_right.x()]); + let wanted_right = solver.float_max(&[self.top_right.y(), self.bottom_right.y()]); + + let bounds_top = context.bounds().top(solver); + let bounds_left = context.bounds().left(solver); + let bounds_bottom = context.bounds().bottom(solver); + let bounds_right = context.bounds().right(solver); + + // TODO: add a facility to help this? + let bounds_top_constraint = solver.float_eq(bounds_top, wanted_top); + solver.constrain(bounds_top_constraint); + + let bounds_left_constraint = solver.float_eq(bounds_left, wanted_left); + solver.constrain(bounds_left_constraint); + + let bounds_bottom_constraint = solver.float_eq(bounds_bottom, wanted_bottom); + solver.constrain(bounds_bottom_constraint); + + let bounds_right_constraint = solver.float_eq(bounds_right, wanted_right); + solver.constrain(bounds_right_constraint); + + let top_left_arm_bottom = Point2D::new( + self.top_left.x(), + solver.float_add(&[self.top_left.y(), self.arm_length]), + ); + + let bottom_left_arm_top = Point2D::new( + self.bottom_left.x(), + solver.float_sub(&[self.bottom_left.y(), self.arm_length]), + ); + + result.push(Drawable::new( + StraightPath::new(vec![ + self.top_left.clone(), + top_left_arm_bottom, + bottom_left_arm_top, + self.bottom_left.clone(), + ]), + context.clone(), + )); + + let top_right_arm_bottom = Point2D::new( + self.top_right.x(), + solver.float_add(&[self.top_right.y(), self.arm_length]), + ); + + let bottom_right_arm_top = Point2D::new( + self.bottom_right.x(), + solver.float_sub(&[self.bottom_right.y(), self.arm_length]), + ); + + result.push(Drawable::new( + StraightPath::new(vec![ + self.top_right.clone(), + top_right_arm_bottom, + bottom_right_arm_top, + self.bottom_right.clone(), + ]), + context.clone(), + )); + + result + } +} 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 @@ +use diaphragm_core::{ + core_shapes::Text, + text::FontDescription, + types::{Bounds, Float, ShapeContext}, + ComplexShape, DrawResult, DynClone, SolverContext, +}; + +pub struct Labeled { + pub label: String, + pub label_font: FontDescription, + pub content: Box, + pub content_left: Float, +} + +impl ComplexShape for Labeled { + fn draw(&self, context: &ShapeContext, solver: &mut dyn SolverContext) -> DrawResult { + let mut result = DrawResult::new(); + + // Label + + let label = Text { + content: self.label.clone(), + font: self.label_font.clone(), + }; + + let mut label_context = context.clone(); + // TODO: make the text constrain the width + label_context.bounds.width = solver.new_free_float(); + + let label_right = solver.float_add(&[context.bounds.left, label_context.bounds.width]); + let content_left_constrain = solver.float_eq(label_right, self.content_left); + solver.constrain(content_left_constrain); + + result.push_shape(label, label_context); + + // Content + + let content_context = ShapeContext { + bounds: Bounds { + top: context.bounds.top, + left: solver.float_add(&[context.bounds.left, self.content_left]), + height: context.bounds.height, + width: context.bounds.width, + }, + fill: context.fill.clone(), + stroke: context.stroke.clone(), + }; + + result.push_boxed_shape(self.content.dyn_clone(), content_context); + + result + } +} + +impl DynClone for Labeled { + fn dyn_clone(&self) -> Box { + Box::new(Labeled { + label: self.label.clone(), + label_font: self.label_font.clone(), + content: self.content.dyn_clone(), + content_left: self.content_left.clone(), + }) + } +} 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 @@ +use diaphragm_core::{ + core_shapes::{StraightPath, Text}, + types::{Bounds, Float, Point2D, ShapeContext}, + ComplexShape, DrawResult, Drawable, SolverContext, +}; + +#[derive(Debug, Clone)] +pub struct Delimiter { + pub width: Float, + pub label: Drawable, +} + +#[derive(Debug, Clone)] +pub struct LabeledDelimiter { + pub delimiters: Vec, + pub tick_height: Float, +} + +impl ComplexShape for LabeledDelimiter { + fn draw(&self, context: &ShapeContext, solver: &mut dyn SolverContext) -> DrawResult { + let mut result = DrawResult::new(); + + let bounds_left = context.bounds().left(solver); + let bounds_right = context.bounds().right(solver); + let bounds_bottom = context.bounds().bottom(solver); + let bounds_width = context.bounds().width(solver); + + let tick_top = context.bounds().top(solver); + let tick_bottom = solver.float_add(&[tick_top, self.tick_height]); + + let first_tick = Drawable::builder(StraightPath::new(vec![ + context.bounds().top_left(solver), + Point2D::new(bounds_left, tick_bottom), + ])) + .bounds( + Bounds::builder() + .top(tick_top) + .left(bounds_left) + .height(self.tick_height) + .width(Float::Fixed(0.)) + .build(solver), + ) + .stroke(context.stroke().clone()) + .build(solver); + + let baseline_y = first_tick.bounds().vert_center(solver); + + result.push(first_tick); + + // TODO: split everything into functions + + let baseline = Drawable::builder(StraightPath::new(vec![ + Point2D::new(bounds_left, baseline_y), + Point2D::new(bounds_right, baseline_y), + ])) + .bounds( + Bounds::builder() + .top(baseline_y) + .left(bounds_left) + .width(bounds_width) + .height(Float::Fixed(0.)) + .build(solver), + ) + .stroke(context.stroke().clone()) + .build(solver); + + result.push(baseline); + + let mut section_end = bounds_left; + let mut all_label_bottoms = vec![]; + + for &Delimiter { + width: section_width, + ref label, + } in &self.delimiters + { + let section_begin = section_end; + section_end = solver.float_add(&[section_end, section_width]); + + let section_half_width = solver.float_div(section_width, Float::Fixed(2.)); + let section_middle = solver.float_add(&[section_begin, section_half_width]); + + let tick = Drawable::builder(StraightPath::new(vec![ + Point2D::new(section_end, tick_top), + Point2D::new(section_end, tick_bottom), + ])) + .bounds( + Bounds::builder() + .top(tick_top) + .left(section_end) + .height(self.tick_height) + .width(Float::Fixed(0.)) + .build(solver), + ) + .stroke(context.stroke().clone()) + .build(solver); + + result.push(tick); + + let label_top = label.bounds().top(solver); + let label_bottom = label.bounds().bottom(solver); + let label_horiz_center = label.bounds().horiz_center(solver); + + let label_top_constraint = solver.float_eq(label_top, tick_bottom); + solver.constrain(label_top_constraint); + + let label_middle_constraint = solver.float_eq(label_horiz_center, section_middle); + solver.constrain(label_middle_constraint); + + result.push(label.clone()); + + all_label_bottoms.push(label_bottom); + } + + let wanted_bounds_bottom = solver.float_max(&all_label_bottoms); + let bounds_bottom_constraint = solver.float_eq(bounds_bottom, wanted_bounds_bottom); + solver.constrain(bounds_bottom_constraint); + + result + } +} 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 @@ +use diaphragm_core::{ + core_shapes::Rectangle, + core_shapes::Text, + text::FontDescription, + types::{Bounds, Float, ShapeContext}, + ComplexShape, DrawResult, Drawable, DynDrawable, SolverContext, +}; + +#[derive(Clone)] +pub struct Entry { + // TODO: transform this to just Text + pub label: Option, + pub label_vert_center_offset: Option, + pub content: DynDrawable, +} + +#[derive(Clone)] +pub struct Layer { + // TODO: transform this to just Text + pub name: String, + pub name_font: FontDescription, + pub label_font: FontDescription, + pub padding: Float, + pub entries: Vec, + pub entries_width: Float, +} + +impl ComplexShape for Layer { + fn draw(&self, context: &ShapeContext, solver: &mut dyn SolverContext) -> DrawResult { + let mut result = DrawResult::new(); + + let bounds_top = context.bounds().top(solver); + let bounds_left = context.bounds().left(solver); + let bounds_right = context.bounds().right(solver); + let bounds_height = context.bounds().height(solver); + + let inner_top = solver.float_add(&[bounds_top, self.padding]); + let inner_right = solver.float_sub(&[bounds_right, self.padding]); + + // Outer box + + result.push(Drawable::new(Rectangle {}, context.clone())); + + // Layer name + + let layer_name = Drawable::builder(Text { + content: self.name.clone(), + font: self.name_font.clone(), + }) + .bounds( + Bounds::builder() + .top(inner_top) + .left(solver.float_add(&[bounds_left, self.padding])) + .build(solver), + ) + .stroke(context.stroke().clone()) + .fill(context.fill().clone()) + .build(solver); + + let layer_name_bottom = layer_name.bounds().bottom(solver); + + /* + let mut rect_context = layer_name_context.clone(); + rect_context.stroke = StrokeStyle::solid(Color::from_rgb(1., 0., 0.)); + result.push_shape(Rectangle {}, rect_context); + */ + + result.push(layer_name); + + // Entries + + let mut entry_y_acc = inner_top; + + for entry in &self.entries { + // Entry content + + /* + let content_context = ShapeContext { + bounds: Bounds { + top: solver.new_free_float(), + left: content_left, + width: entry.width, + height: entry.height, + }, + fill: Default::default(), + stroke: Default::default(), + }; + */ + + let content_vert_center = entry.content.bounds().vert_center(solver); + let content_top = entry.content.bounds().top(solver); + let content_left = entry.content.bounds().left(solver); + let content_bottom = entry.content.bounds().bottom(solver); + + // TODO: to replace with label offset + let content_half_height = solver.float_sub(&[content_vert_center, content_top]); + + result.push_dyn(entry.content.clone()); + + // Entry label + + if let Some(label_content) = entry.label.clone() { + let label_top = solver.new_free_float(); + let label_width = solver.new_free_float(); + let label_left = solver.float_sub(&[inner_right, self.entries_width, label_width]); + + let label = Drawable::builder(Text { + content: label_content, + font: self.label_font.clone(), + }) + .bounds( + Bounds::builder() + .top(label_top) + .left(label_left) + .width(label_width) + .build(solver), + ) + .build(solver); + + /* + let mut rect_context = label_context.clone(); + rect_context.stroke = StrokeStyle::solid(Color::from_rgb(1., 0., 0.)); + result.push_shape(Rectangle {}, rect_context); + */ + + // TODO + let label_vert_center = label.bounds().vert_center(solver); + let label_top = label.bounds().top(solver); + let label_right = label.bounds().right(solver); + + let label_vert_center_constraint = + solver.float_eq(label_vert_center, content_vert_center); + solver.constrain(label_vert_center_constraint); + + let label_half_height = solver.float_sub(&[label_vert_center, label_top]); + + let dumb_label_mid_placement = + solver.float_add(&[entry_y_acc, content_half_height]); + let safe_label_mid_placement = + solver.float_add(&[layer_name_bottom, label_half_height]); + let label_mid_placement = + solver.float_max(&[safe_label_mid_placement, dumb_label_mid_placement]); + let label_mid_placement_constraint = + solver.float_eq(label_vert_center, label_mid_placement); + solver.constrain(label_mid_placement_constraint); + + let label_right_constraint = solver.float_eq(label_right, content_left); + solver.constrain(label_right_constraint); + + result.push(label); + } else { + let content_top_constraint = solver.float_eq(content_top, entry_y_acc); + solver.constrain(content_top_constraint); + + let wanted_content_left = solver.float_sub(&[inner_right, self.entries_width]); + let content_left_constraint = solver.float_eq(content_left, wanted_content_left); + solver.constrain(content_left_constraint); + } + + entry_y_acc = solver.float_add(&[content_bottom, self.padding]); + } + + let wanted_bounds_height = solver.float_sub(&[entry_y_acc, bounds_top]); + let height_constraint = solver.float_eq(bounds_height, wanted_bounds_height); + solver.constrain(height_constraint); + + result + } +} 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 @@ +mod block_list; +mod blocks; +mod bracket; +mod explode; +mod labeled_delimiter; +mod layer; +mod spacer; + +//mod labeled; +//use labeled::Labeled; + +use block_list::BlockList; +use blocks::*; +use explode::*; +use labeled_delimiter::*; +use layer::*; +use spacer::*; + +use diaphragm_cairo_renderer::CairoRenderer; +use diaphragm_core::{ + colors::Color, + core_shapes::{Text}, + styles::*, + text::FontDescription, + types::*, + *, +}; +use diaphragm_z3_solver::{z3, Z3Context}; + +fn main() { + let z3_cfg = z3::Config::new(); + let z3_ctx = z3::Context::new(&z3_cfg); + let ctx = Z3Context::new(&z3_ctx); + + let cairo_renderer = CairoRenderer::new(); + + let mut runtime = Runtime::new(Box::new(ctx), Box::new(cairo_renderer)); + + let solver = runtime.solver_ctx(); + + let blue = Color::from_rgb(0.35, 0.35, 1.); + let light_grey = Color::from_rgb(0.8, 0.8, 0.8); + let dark_grey = Color::from_rgb(0.5, 0.5, 0.5); + + // ----------------8<---------------- + + /* + let mut make_blocks = |grows: &[u8]| { + grows + .iter() + .map(|&grow| { + Drawable::builder(Block { grow }) + .fill(FillStyle::solid(blue)) + .build(solver) + }) + .collect() + }; + + let blocks = Drawable::builder(Blocks { + blocks: make_blocks(&[1, 2, 1, 1, 4, 1, 1]), + unit_width: solver.new_free_float(), + }) + .bounds( + Bounds::builder() + .top(Float::Fixed(20.)) + .left(Float::Fixed(20.)) + .width(Float::Fixed(150.)) + .height(Float::Fixed(50.)) + .build(solver), + ) + .build(solver); + + runtime.add_drawable(blocks); + */ + + // ----------------8<---------------- + + /* + let block_unit_width = solver.new_fixed_float(10.); + + fn color_block(grow: u8, color: Color, solver: &mut dyn SolverContext) -> Drawable { + Drawable::builder(Block { grow }) + .fill(FillStyle::solid(color)) + .build(solver) + }; + + fn blocks(block_descs: &[(u8, Color)], solver: &mut dyn SolverContext) -> Drawable { + let mut blocks = Vec::new(); + + for &(grow, color) in block_descs { + blocks.push(color_block(grow, color, solver)); + } + + Drawable::builder(Blocks { + blocks, + unit_width: solver.new_free_float(), + }) + .build(solver) + } + + let block_list = Drawable::builder(BlockList { + block_list: vec![ + blocks(&[(1, light_grey), (1, light_grey), (1, light_grey)], solver), + blocks( + &[ + (1, light_grey), + (2, blue), + (1, light_grey), + (1, light_grey), + (1, light_grey), + ], + solver, + ), + blocks(&[(3, light_grey), (2, blue)], solver), + ], + block_unit_width, + bracket_width: block_unit_width, + blocks_vert_padding: block_unit_width, + }) + .bounds( + Bounds::builder() + .top(Float::Fixed(10.)) + .left(Float::Fixed(10.)) + .height(Float::Fixed(40.)) + .build(solver), + ) + .build(solver); + + runtime.add_drawable(block_list); + */ + + // ----------------8<---------------- + + /* + let label_font = FontDescription { + family: String::from("mono"), + style: Default::default(), + weight: Default::default(), + size: Float::Fixed(15.), + }; + + let layer = Drawable::builder(Layer { + name: String::from("Hello"), + name_font: FontDescription { + family: Default::default(), + style: Default::default(), + weight: Default::default(), + size: Float::Fixed(10.), + }, + entries: vec![], + padding: Float::Fixed(5.), + entries_width: Float::Fixed(50.), + label_font, + }) + .bounds(Bounds::builder() + .top(Float::Fixed(10.)) + .left(Float::Fixed(10.)) + .width(Float::Fixed(100.)) + .height(Float::Fixed(40.)) + .build(solver)) + .build(solver); + + runtime.add_drawable(layer); + */ + + // ----------------8<---------------- + + let title_font = FontDescription { + family: String::from("Fanwood"), + style: Default::default(), + weight: Default::default(), + size: Float::Fixed(30.), + }; + + let label_font = FontDescription { + family: String::from("mono"), + style: Default::default(), + weight: Default::default(), + size: Float::Fixed(15.), + }; + + let layer_padding = Float::Fixed(15.); + let layer_margin = Float::Fixed(15.); + //let layer_height = Float::Fixed(40.); + let layer_left_content_padding = Float::Fixed(240.); + //let layer_width = solver.new_free_float(); + //let layer_width_constraint = solver.float_eq(layer_width, Float::Fixed(100.)); + //solver.constrain(layer_width_constraint); + + let mut layer_top = Float::Fixed(15.); + + let entries_width = solver.new_free_float(); + let mut all_entry_content_widths = vec![]; + + let layer_width = solver.float_add(&[ + layer_left_content_padding, + entries_width, + layer_padding, + layer_padding, + ]); + + let blocks_vert_padding = Float::Fixed(10.); + + let block_unit_width = Float::Fixed(20.); + + let blocks_height = Float::Fixed(26.); + let block_list_height = + solver.float_add(&[blocks_height, blocks_vert_padding, blocks_vert_padding]); + + let blue_transaction_len = 5; + + let blocks = |grows: &[u8], color, solver: &mut dyn SolverContext| { + let mut blocks = Vec::new(); + + for &grow in grows { + blocks.push( + Drawable::builder(Block { grow }) + .fill(FillStyle::solid(color)) + .build(solver), + ); + } + + Drawable::builder(Blocks { + blocks, + unit_width: block_unit_width, + }) + .bounds(Bounds::builder().height(blocks_height).build(solver)) + .build(solver) + }; + + let blocks_by_len = + |len, color, solver: &mut dyn SolverContext| blocks(&vec![1; len], color, solver); + + // Log API + { + let blocks = blocks_by_len(blue_transaction_len, blue, solver); + let blocks_width = blocks.bounds().width(solver); + + let layer = Drawable::builder(Layer { + name: String::from("LogAPI"), + name_font: title_font.clone(), + label_font: label_font.clone(), + padding: layer_padding, + entries: vec![Entry { + label: Some(String::from("activeTxn: ")), + label_vert_center_offset: None, + content: blocks.into(), + }], + entries_width, + }) + .bounds( + Bounds::builder() + // TODO: Replace that with a "distributeHorizontally" that applies to all layer + .top(layer_top) + .left(Float::Fixed(10.)) + .width(layer_width) + .build(solver), + ) + .build(solver); + + all_entry_content_widths.push(blocks_width); + + let layer_height = layer.bounds().height(solver); + layer_top = solver.float_add(&[layer_top, layer_height, layer_margin]); + + runtime.add_drawable(layer); + } + + let solver = runtime.solver_ctx(); + + let other_transactions = [2, 7, 4]; + + let group_commit_left; + let group_commit_right; + let group_commit_bottom; + + // Group Commit + { + let mut block_list = Vec::new(); + for &blocks_count in other_transactions.iter() { + block_list.push(blocks_by_len(blocks_count, dark_grey, solver)); + } + + block_list.push(blocks_by_len(blue_transaction_len, blue, solver)); + + let block_list = Drawable::builder(BlockList { + block_list, + block_unit_width, + blocks_vert_padding: Float::Fixed(10.), + bracket_width: Float::Fixed(10.), + }) + .bounds(Bounds::builder().height(block_list_height).build(solver)) + .build(solver); + + let content_width = block_list.bounds().width(solver); + + group_commit_left = block_list.bounds().left(solver); + group_commit_right = block_list.bounds().right(solver); + group_commit_bottom = block_list.bounds().bottom(solver); + + let layer = Drawable::builder(Layer { + name: String::from("GroupCommit"), + name_font: title_font.clone(), + label_font: label_font.clone(), + padding: layer_padding, + entries: vec![Entry { + label: Some(String::from("commitedTxns: ")), + label_vert_center_offset: None, + content: block_list.into(), + }], + entries_width, + }) + .bounds( + Bounds::builder() + .top(layer_top) + .left(Float::Fixed(10.)) + .width(layer_width) + .build(solver), + ) + .build(solver); + + all_entry_content_widths.push(content_width); + + let layer_height = layer.bounds().height(solver); + layer_top = solver.float_add(&[layer_top, layer_height, layer_margin]); + + runtime.add_drawable(layer); + } + + let solver = runtime.solver_ctx(); + + let commit_in_log_left; + let commit_in_log_right; + let commit_in_log_top; + let big_block_grow; + let small_block_unit_width; + let data_in_log_left; + let data_in_log_right; + let data_in_log_bottom; + + // Disk log + { + big_block_grow = (blue_transaction_len + other_transactions.iter().sum::()) as u8; + + let make_block = |grow, color, solver: &mut dyn SolverContext| { + Drawable::builder(Block { grow }) + .bounds(Bounds::builder().height(blocks_height).build(solver)) + .fill(FillStyle::solid(color)) + .build(solver) + }; + + let make_blocks = |block_descs: &[_], solver: &mut dyn SolverContext| { + let mut blocks = Vec::new(); + + for &(grow, color) in block_descs { + blocks.push(make_block(grow, color, solver)); + } + + blocks + }; + + let header_block = make_block(big_block_grow, Color::black(), solver); + let header_block_width = header_block.bounds().width(solver); + + let mut blocks = vec![header_block]; + + blocks.append(&mut make_blocks( + &[(big_block_grow, light_grey), (big_block_grow, light_grey)], + solver, + )); + + let sample_commit_start_id = blocks.len(); + + for &txn_block_count in other_transactions.iter() { + for _i in 0..txn_block_count { + blocks.push(make_block(1, dark_grey, solver)); + } + } + + for _ in 0..blue_transaction_len { + blocks.push(make_block(1, blue, solver)); + } + + let sample_commit_end_id = blocks.len() - 1; + + commit_in_log_left = blocks[sample_commit_start_id].bounds().left(solver); + commit_in_log_right = blocks[sample_commit_end_id].bounds().right(solver); + commit_in_log_top = blocks[sample_commit_end_id].bounds().top(solver); + + let log_data_blocks_widths: Vec<_> = blocks[1..] + .iter() + .map(|block| block.bounds().width(solver)) + .collect(); + let log_data_blocks_width = solver.float_add(&log_data_blocks_widths); + + let log_space_block = make_block(big_block_grow, Color::white(), solver); + let log_space_width = log_space_block.bounds().width(solver); + blocks.push(log_space_block); + + // Make it so that it takes as much space as the bigger one + small_block_unit_width = solver.new_free_float(); + + let disk_log_layout = Drawable::builder(Blocks { + blocks, + unit_width: small_block_unit_width, + }) + .bounds(Bounds::builder().width(entries_width).build(solver)) + .build(solver); + + let mut make_delimiter_label = |content| { + Drawable::builder(Text { + content: String::from(content), + font: FontDescription { + family: String::from("Fanwood"), + size: Float::Fixed(15.), + style: Default::default(), + weight: Default::default(), + }, + }) + .build(solver) + }; + + let delimiters = vec![ + Delimiter { + label: make_delimiter_label("header"), + width: header_block_width, + }, + Delimiter { + label: make_delimiter_label("data"), + width: log_data_blocks_width, + }, + Delimiter { + label: make_delimiter_label("available"), + width: log_space_width, + }, + ]; + + let disk_log_label = Drawable::builder(LabeledDelimiter { + delimiters, + tick_height: Float::Fixed(10.), + }) + .bounds(Bounds::builder().width(entries_width).build(solver)) + .build(solver); + + let label_left = disk_log_label.bounds().left(solver); + data_in_log_left = solver.float_add(&[label_left, header_block_width]); + data_in_log_right = solver.float_add(&[data_in_log_left, log_data_blocks_width]); + data_in_log_bottom = disk_log_label.bounds().bottom(solver); + + let layer = Drawable::builder(Layer { + name: String::from("DiskLog"), + name_font: title_font.clone(), + label_font: label_font.clone(), + padding: layer_padding, + entries: vec![ + Entry { + label: Some(String::from("disk log: ")), + label_vert_center_offset: None, + content: disk_log_layout.into(), + }, + Entry { + label: None, + label_vert_center_offset: None, + content: disk_log_label.into(), + }, + ], + entries_width, + }) + .bounds( + Bounds::builder() + .top(layer_top) + .left(Float::Fixed(10.)) + .width(layer_width) + .build(solver), + ) + .build(solver); + + let layer_height = layer.bounds().height(solver); + layer_top = solver.float_add(&[layer_top, layer_height, layer_margin]); + + runtime.add_drawable(layer); + } + + let solver = runtime.solver_ctx(); + + // Explode GroupLog -> DiskLog + { + let margin = Float::Fixed(6.); + let explode_top = solver.float_add(&[group_commit_bottom, margin]); + let explode_bottom = solver.float_sub(&[commit_in_log_top, margin]); + + let explode = Drawable::builder(Explode { + top_left: Point2D::new(group_commit_left, explode_top), + top_right: Point2D::new(group_commit_right, explode_top), + bottom_left: Point2D::new(commit_in_log_left, explode_bottom), + bottom_right: Point2D::new(commit_in_log_right, explode_bottom), + + arm_length: Float::Fixed(12.), + }) + .stroke( + StrokeStyle::builder() + .dash(DashStyle::new(vec![Float::Fixed(2.)], Float::Fixed(0.))) + .build(), + ) + .build(solver); + + runtime.add_drawable(explode); + } + + let solver = runtime.solver_ctx(); + + let data_in_applier_top; + let data_in_applier_left; + let data_in_applier_right; + + // Applier + { + // TODO: this is exactly the same as before... + let make_block = |grow, color, solver: &mut dyn SolverContext| { + Drawable::builder(Block { grow }) + .bounds(Bounds::builder().height(blocks_height).build(solver)) + .fill(FillStyle::solid(color)) + .build(solver) + }; + + let _make_blocks = |block_descs: &[_], solver: &mut dyn SolverContext| { + let mut blocks = Vec::new(); + + for &(grow, color) in block_descs { + blocks.push(make_block(grow, color, solver)); + } + + blocks + }; + + let mut blocks = vec![]; + + blocks.push(make_block(big_block_grow, light_grey, solver)); + for &txn_block_count in other_transactions.iter() { + for _i in 0..txn_block_count { + blocks.push(make_block(1, dark_grey, solver)); + } + } + + for _ in 0..blue_transaction_len { + blocks.push(make_block(1, blue, solver)); + } + + let blocks = Drawable::builder(Blocks { + blocks, + unit_width: small_block_unit_width, + }) + .build(solver); + + data_in_applier_top = blocks.bounds().top(solver); + data_in_applier_left = blocks.bounds().left(solver); + data_in_applier_right = blocks.bounds().right(solver); + + let spaced_data = Spacer::builder(blocks) + .horizontal_align_center(solver) + .vertical_align_center_with(Float::Fixed(10.)) + .build(solver); + + let spaced_data = Drawable::builder(spaced_data) + .bounds(Bounds::builder().width(entries_width).build(solver)) + .build(solver); + + let disk_data = Drawable::builder(Block { grow: 1 }) + .bounds( + Bounds::builder() + .height(blocks_height) + .width(entries_width) + .build(solver), + ) + .fill(FillStyle::solid(Color::white())) + .build(solver); + + let layer = Drawable::builder(Layer { + name: String::from("Applier"), + name_font: title_font.clone(), + label_font: label_font.clone(), + padding: layer_padding, + entries: vec![ + Entry { + label: None, + label_vert_center_offset: None, + content: spaced_data.into(), + }, + Entry { + label: Some(String::from("disk data:")), + label_vert_center_offset: None, + content: disk_data.into(), + }, + ], + entries_width, + }) + .bounds( + Bounds::builder() + .top(layer_top) + .left(Float::Fixed(10.)) + .width(layer_width) + .build(solver), + ) + .build(solver); + + runtime.add_drawable(layer); + } + + let solver = runtime.solver_ctx(); + + // Explode DiskLog -> Applier + { + let margin = Float::Fixed(6.); + let explode_top = solver.float_add(&[data_in_log_bottom, margin]); + let explode_bottom = solver.float_sub(&[data_in_applier_top, margin]); + + let explode = Drawable::builder(Explode { + top_left: Point2D::new(data_in_log_left, explode_top), + top_right: Point2D::new(data_in_log_right, explode_top), + bottom_left: Point2D::new(data_in_applier_left, explode_bottom), + bottom_right: Point2D::new(data_in_applier_right, explode_bottom), + + arm_length: Float::Fixed(12.), + }) + .stroke( + StrokeStyle::builder() + .dash(DashStyle::new(vec![Float::Fixed(2.)], Float::Fixed(0.))) + .build(), + ) + .build(solver); + + runtime.add_drawable(explode); + } + + let solver = runtime.solver_ctx(); + + let max_entry_content_width = solver.float_max(&all_entry_content_widths); + let entries_width_constraint = solver.float_eq(entries_width, max_entry_content_width); + solver.constrain(entries_width_constraint); + + runtime.render(); +} 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 @@ +fn main() { + let solver = Solver::new(); + let output = Output::new(); + + let blue_transaction_len = 5; + + let blue_block = Block::builder().color(blue); + let dark_gray_block = Block::builder().color(dark_gray); + + let log_layer = Layer::builder() + .name("LogAPI") + .push_entry( + Entry::builder() + .label("activeTxn: ") + .content( + Blocks::builder() + .push(blue_transaction_len, blue_block.build()) + .build(), + ) + .build(), + ) + .build(); + + let other_transactions = [2, 7, 4]; + + let group_commit_layer = Layer::builder() + .name("GroupCommit") + .push_entry( + Entry::builder() + .label("commitedTxn: ") + .content( + BlockList::builder() + .append( + other_transactions + .iter() + // TODO: need collect? + .map(|len| Blocks::builder().push(len, dark_gray_block.build())), + ) + .push(Blocks::builder().push(blue_transaction_len, blue_block.build())) + .build(), + ) + .build(), + ) + .build(); + + let disk_log_layer = todo!(); + + let applier_layer = todo!(); + + let layers = &[ + &log_layer, + &group_commit_layer, + &disk_log_layer, + &applier_layer, + ]; + constraints::distribute_vertically(solver, layers); + constraints::align_left(solver, layers); + + solver.solve(); + output.write_to_stdout(Output::Format::SVG); +} 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 @@ +use diaphragm_core::{ + types::{Float, ShapeContext}, + ComplexShape, DrawResult, DynDrawable, SolverContext, +}; + +#[derive(Clone)] +pub struct Spacer { + pub margin_left: Float, + pub margin_right: Float, + pub margin_top: Float, + pub margin_bottom: Float, + pub content: DynDrawable, +} + +impl Spacer { + pub fn builder>(drawable: T) -> SpacerBuilder { + SpacerBuilder::new(drawable) + } +} + +impl ComplexShape for Spacer { + fn draw(&self, context: &ShapeContext, solver: &mut dyn SolverContext) -> DrawResult { + let mut result = DrawResult::new(); + + let bounds_left = context.bounds().left(solver); + let bounds_right = context.bounds().right(solver); + let bounds_top = context.bounds().top(solver); + let bounds_bottom = context.bounds().bottom(solver); + + let wanted_content_left = solver.float_add(&[bounds_left, self.margin_left]); + let content_left = self.content.bounds().left(solver); + let content_left_constraint = solver.float_eq(content_left, wanted_content_left); + solver.constrain(content_left_constraint); + + let wanted_content_right = solver.float_sub(&[bounds_right, self.margin_right]); + let content_right = self.content.bounds().right(solver); + let content_right_constraint = solver.float_eq(content_right, wanted_content_right); + solver.constrain(content_right_constraint); + + let wanted_content_top = solver.float_add(&[bounds_top, self.margin_top]); + let content_top = self.content.bounds().top(solver); + let content_top_constraint = solver.float_eq(content_top, wanted_content_top); + solver.constrain(content_top_constraint); + + let wanted_content_bottom = solver.float_sub(&[bounds_bottom, self.margin_bottom]); + let content_bottom = self.content.bounds().bottom(solver); + let content_bottom_constraint = solver.float_eq(content_bottom, wanted_content_bottom); + solver.constrain(content_bottom_constraint); + + result.push_dyn(self.content.clone()); + + result + } +} + +#[derive(Clone)] +pub struct SpacerBuilder { + pub margin_left: Option, + pub margin_right: Option, + pub margin_top: Option, + pub margin_bottom: Option, + pub content: DynDrawable, +} + +impl SpacerBuilder { + pub fn new>(drawable: T) -> Self { + SpacerBuilder { + margin_left: None, + margin_right: None, + margin_top: None, + margin_bottom: None, + content: drawable.into(), + } + } + + pub fn margin_left(&mut self, margin_left: Float) -> &mut Self { + self.margin_left = Some(margin_left); + self + } + + pub fn margin_right(&mut self, margin_right: Float) -> &mut Self { + self.margin_right = Some(margin_right); + self + } + + pub fn margin_top(&mut self, margin_top: Float) -> &mut Self { + self.margin_top = Some(margin_top); + self + } + + pub fn margin_bottom(&mut self, margin_bottom: Float) -> &mut Self { + self.margin_bottom = Some(margin_bottom); + self + } + + pub fn vertical_align_center(&mut self, solver: &mut dyn SolverContext) -> &mut Self { + let vertical_margin = solver.new_free_float(); + self.margin_top = Some(vertical_margin); + self.margin_bottom = Some(vertical_margin); + self + } + + pub fn vertical_align_center_with(&mut self, margin: Float) -> &mut Self { + self.margin_top = Some(margin); + self.margin_bottom = Some(margin); + self + } + + pub fn horizontal_align_center(&mut self, solver: &mut dyn SolverContext) -> &mut Self { + let horizontal_margin = solver.new_free_float(); + self.margin_left = Some(horizontal_margin); + self.margin_right = Some(horizontal_margin); + self + } + + pub fn horizontal_align_center_with(&mut self, margin: Float) -> &mut Self { + self.margin_left = Some(margin); + self.margin_right = Some(margin); + self + } + + pub fn build(&self, solver: &mut dyn SolverContext) -> Spacer { + Spacer { + margin_left: self.margin_left.unwrap_or_else(|| solver.new_free_float()), + margin_right: self.margin_right.unwrap_or_else(|| solver.new_free_float()), + margin_top: self.margin_top.unwrap_or_else(|| solver.new_free_float()), + margin_bottom: self + .margin_bottom + .unwrap_or_else(|| solver.new_free_float()), + content: self.content.clone(), + } + } +} -- cgit v1.2.3