From b02862a937ebaea178b1c3d08f76ab6d60a7aeed Mon Sep 17 00:00:00 2001 From: Minijackson Date: Wed, 4 Jan 2023 09:38:58 +0100 Subject: examples/dfscq-log.lua: update --- examples/lib-dfscq-log/dfscq-log.lua | 438 ++++++++++++++++++++++++++++++++++- 1 file changed, 435 insertions(+), 3 deletions(-) diff --git a/examples/lib-dfscq-log/dfscq-log.lua b/examples/lib-dfscq-log/dfscq-log.lua index c54e4f2..b4e2f8e 100644 --- a/examples/lib-dfscq-log/dfscq-log.lua +++ b/examples/lib-dfscq-log/dfscq-log.lua @@ -1,7 +1,439 @@ -local diaphragm = require("diaphragm") +package.cpath = package.cpath .. ";../../target/debug/?.so" +package.path = package.path .. ";../../lua-bindings/?.lua" + +local dia = require("diaphragm") + +-- TODO: add a way to have modified prototypes of these functions +-- e.g.: local my_blocks = Blocks:new({ default_color = my_color }) +-- my_blocks:new({ elements = ... }):draw() + +dia.util.eprint("--------------------") + +-- TODO: Fanwood has a high baseline, which renders weirdly in the GroupCommit layer, +-- and "Goudy Bookletter 1911" doesn't work +-- local serif_family = "Fanwood" +-- local serif_family = "Latin Modern Math" +-- local serif_family = "Goudy Bookletter 1911 Normal" +local serif_family = "OFL Sorts Mill Goudy" +local mono_family = "Fira Mono" + +local active_color = "#5959ff" +local light_gray = "#cccccc" +local dark_gray = "#7f7f7f" + +local block_unit_width = 24 +local block_height = 36 +local block_stroke_width = 2 + +local normal_margin = 20 +local small_margin = 10 + +local function blocks(params) + local result = dia.shape(params) + + result.unit_width = result.unit_width or dia.float.new() + result.default_color = result.default_color or light_gray + result.default_stroke_width = result.default_stroke_width or block_stroke_width + + result.blocks = dia.util.tbl_map(function(el) + return dia.rectangle.new({ + fill_color = el.color or result.default_color, + stroke_width = result.default_stroke_width, + height = result.height, + width = result.unit_width * (el.grow or 1), + }) + end, params.elements) + + function result:draw() + local len = #self.elements + assert(len >= 1, "Blocks must have at least 1 element") + + local total_grow = 0 + for _, el in pairs(self.elements) do + total_grow = total_grow + (el.grow or 1) + end + + dia.constrain(self.width:eq(self.unit_width * total_grow)) + + dia.layout + .hstack({ + elements = self.blocks, + top_left = self.top_left, + bottom_right = self.bottom_right, + width = self.width, + }) + :draw() + end --- TODO: anyone can use metatable? + return result +end + +local function active_txn(params) + return blocks(dia.util.tbl_extend({ + elements = { {}, {}, {}, {}, {} }, + height = block_height, + default_color = active_color, + }, params)) +end + +local function fixed_blocks(params) + local elements = {} + for _ = 1, params.count do + table.insert(elements, {}) + end + return blocks({ + elements = elements, + height = block_height, + unit_width = block_unit_width, + default_color = params.color or dark_gray, + }) +end + +local function block_list(params) + local result = dia.shape(params) + + result.delimiter_height = result.delimiter_height or dia.float.new() + result.font = result.font or dia.text.font({ family = serif_family }) + + function result:draw() + local opening_delimiter = dia.text.new({ + content = "[", + height = self.delimiter_height, + font = self.font, + }) + + local closing_delimiter = dia.text.new({ + content = "]", + height = self.delimiter_height, + font = self.font, + }) + + local function comma() + return dia.text.new({ + content = ", ", + font = self.font, + }) + end + + local elements = dia.util.list_intersperse_with(comma, self.elements) + + elements = { opening_delimiter, table.unpack(elements) } + table.insert(elements, closing_delimiter) + + -- TODO: not really elegant + dia.constrain(self.delimiter_height:eq(self.elements[1].height * 2.5)) + + dia.layout + .hstack({ + elements = elements, + align = "center", + top_left = self.top_left, + bottom_right = self.bottom_right, + }) + :draw() + end + + return result +end + +local function disk_log_legend(params) + local result = dia.shape(params) + + local function lines(params) + local result = dia.shape(params) + + result.ticks = {} + for _, tick in ipairs(params.ticks) do + table.insert(result.ticks, + dia.straight_path.new({ + points = { + { x = tick, y = result.top }, + { x = tick, y = result.bottom }, + } + })) + end + + result.baseline = dia.straight_path.new({ + points = { result.middle_left, result.middle_right }, + width = result.width, + }) + + function result:draw() + for _, tick in ipairs(self.ticks) do + tick:draw() + end + self.baseline:draw() + end + + return result + end + result.lines = lines({ + ticks = params.ticks, + top_left = result.top_left, + height = result.height / 2, + width = result.width, + }) + + result.font = dia.text.font({ family = serif_family }) + + result.labels = {} + for i = 1, #params.ticks - 1 do + local label = dia.text.new({ + content = params.labels[i], + height = (result.height / 2) + result.height / 6, + top = result.lines.bottom - result.height / 6, + horiz_center = (params.ticks[i] + params.ticks[i + 1]) / 2, + font = result.font, + }) + table.insert(result.labels, label) + end + + function result:draw() + self.lines:draw() + for _, label in ipairs(self.labels) do + label:draw() + end + end + + return result +end + +local function disk_log(params) + local result = dia.shape(params) + + local anchor_ids = {} + local last_group_id = 0 + local labels = {} + local elements = {} + for _, global_spec in pairs(params.elements) do + for _, spec in pairs(global_spec.elements) do + local spec_params = spec.params or {} + for _ = 1, spec.count do + table.insert(elements, spec_params) + end + last_group_id = last_group_id + spec.count + end + table.insert(anchor_ids, last_group_id) + table.insert(labels, global_spec.label) + end + result.blocks = blocks({ + elements = elements, + height = block_height, + top_left = result.top_left, + width = result.width, + }) + result.anchors = { result.left } + for _, id in pairs(anchor_ids) do + table.insert(result.anchors, result.blocks.blocks[id].right) + end + + result.legend = disk_log_legend({ + ticks = result.anchors, + labels = labels, + height = normal_margin * 2, + width = result.width, + bottom_right = result.bottom_right, + }) + dia.constraint.below(result.legend, result.blocks, normal_margin) + + function result:draw() + result.blocks:draw() + result.legend:draw() + end + + return result +end + +local function layer_entry(params) + local result = dia.shape(params) + + result.text = params.text or dia.text.new({ + content = params.name .. ": ", + font = params.font, + }) + result.text_vert_center = params.text_vert_center or params.content.vert_center + + function result:draw() + dia.constraint.left_of(self.text, self.content) + dia.constrain(self.text.vert_center:eq(self.text_vert_center)) + + dia.constrain(self.left:eq(self.text.left)) + dia.constrain(self.right:eq(self.content.right)) + dia.constrain(self.top:eq(dia.float.min({ self.text.top, self.content.top }))) + dia.constrain(self.bottom:eq(dia.float.max({ self.text.bottom, self.content.bottom }))) + + self.text:draw() + self.content:draw() + end + + return result +end + +local function layer(params) + local result = dia.shape(params) + + result.name = result.name or "Layer" + result.title = dia.text.new({ + content = result.name, + font = result.title_font, + top_left = result.top_left + { x = normal_margin, y = normal_margin }, + }) + result.content = result.content or dia.rectangle.new({ + height = 100, + width = 100, + }) + + function result:draw() + dia.rectangle + .new({ + top_left = self.top_left, + bottom_right = self.bottom_right, + fill_color = "#fff", + }) + :draw() + + dia.constrain(self.right:eq(self.content.right + normal_margin)) + dia.constraint.below(self.content, self.title) + dia.constrain(self.left:lt(self.content.left - 150)) + + dia.constrain(self.bottom:eq(dia.float.max({ self.title.bottom, self.content.bottom }) + normal_margin)) + + self.title:draw() + self.content:draw() + end + + return result +end + +dia.draw({ + content = { + draw = function() + local title_font = dia.text.font({ family = serif_family, size = 50000 }) + local entry_font = dia.text.font({ family = mono_family, size = 20000 }) + local action_font = dia.text.font({ family = mono_family, height = normal_margin }) + + local function action(name) + return dia.layout.margin_top({ + margin_top = small_margin, + content = dia.text.new({ + content = '' .. name .. "", + font = action_font, + height = normal_margin, + }), + }) + end + + local layer_right = dia.float.new() + + local log_layer_entry_left = dia.float.new() + local group_commit_entry_left = dia.float.new() + local disk_log_entry_left = dia.float.new() + + local layers = { + layer({ + name = "LogAPI", + title_font = title_font, + -- top_left = { x = normal_margin, y = normal_margin }, + top_left = { x = normal_margin, y = normal_margin }, + content = dia.layout.margin_right({ + content = layer_entry({ + name = "activeTxn", + font = entry_font, + content = active_txn({ + unit_width = block_unit_width, + left = log_layer_entry_left, + }), + }), + }), + }), + action("commit"), + layer({ + name = "GroupCommit", + title_font = title_font, + -- top_left = { x = 20, y = 100 + 40 }, + -- right = layer_right, + content = dia.layout.margin_right({ + -- HACK: because I know that "GroupCommit" + "commitedTxns:" + -- takes the most space + margin_right = 0, + content = layer_entry({ + name = "commitedTxns", + font = entry_font, + content = block_list({ + elements = { + fixed_blocks({ count = 2 }), + fixed_blocks({ count = 7 }), + fixed_blocks({ count = 4 }), + active_txn({ unit_width = block_unit_width }), + }, + left = group_commit_entry_left, + }), + }), + }), + }), + action("flush"), + layer({ + name = "DiskLog", + title_font = title_font, + -- top_left = { x = 20, y = 100 + 40 }, + right = layer_right, + content = dia.layout.margin_right({ + margin_right = 0, + content = layer_entry({ + -- TODO: align to blocks + name = "disk log", + font = entry_font, + content = disk_log({ + elements = { + { + -- TODO: seems to fail solving if using other font size within text, or line returns + label = "header", + elements = { + { count = 1, params = { color = "#000", grow = 11 } }, + }, + }, + { + label = "data", + elements = { + { count = 2, params = { color = light_gray, grow = 13 + 5 } }, + { count = 13, params = { color = dark_gray } }, + { count = 5, params = { color = active_color } }, + }, + }, + { + label = "available", + elements = { + { count = 1, params = { color = "#fff", grow = 13 + 5 } }, + }, + }, + }, + left = disk_log_entry_left, + }), + }), + }), + }), + action("apply"), + layer({ + name = "Applier", + title_font = title_font, + right = layer_right, + }), + } + + dia.layout.vstack({ elements = layers, align = "right" }):draw() + + local layer_entries_text_right = dia.float.new() + for _, entry_left in pairs({ log_layer_entry_left, group_commit_entry_left, disk_log_entry_left }) do + dia.constrain(layer_entries_text_right:eq(entry_left)) + end + end, + }, + output = {}, +}) + +-- TODO: anyone can use metatable? +--[[ local Blocks = diaphragm.Drawable:new() Blocks.elements = {} Blocks.unit_width = diaphragm.float() @@ -121,4 +553,4 @@ diaphragm.draw(diaphragm.layout.vstack({ -- group_commit, -- disk_log, -- applier, -})) +})) ]] -- cgit v1.2.3