From 5231f4b324fb912a82a47c862ef3ad3cc6aa34ea Mon Sep 17 00:00:00 2001 From: LordBaryhobal Date: Sun, 20 Oct 2024 18:12:42 +0200 Subject: [PATCH] added basis for node/flow creation + node layout --- gallery/test.pdf | Bin 0 -> 23632 bytes gallery/test.typ | 7 ++ src/diagram.typ | 180 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.typ | 2 + 4 files changed, 189 insertions(+) create mode 100644 gallery/test.pdf create mode 100644 gallery/test.typ create mode 100644 src/diagram.typ create mode 100644 src/lib.typ diff --git a/gallery/test.pdf b/gallery/test.pdf new file mode 100644 index 0000000000000000000000000000000000000000..fce3a6e2ec64ed7543a47c7d7f639790c9e28d47 GIT binary patch literal 23632 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 literal 0 HcmV?d00001 diff --git a/gallery/test.typ b/gallery/test.typ new file mode 100644 index 0000000..7f54df4 --- /dev/null +++ b/gallery/test.typ @@ -0,0 +1,7 @@ +#import "../src/lib.typ": * + +#diagram({ + _flow("Wages", 1500, "Budget") + _flow("Other", 250, "Budget") + _flow("Budget", 450, "Taxes") +}, 10cm) \ No newline at end of file diff --git a/src/diagram.typ b/src/diagram.typ new file mode 100644 index 0000000..66480f6 --- /dev/null +++ b/src/diagram.typ @@ -0,0 +1,180 @@ +#import "@preview/cetz:0.3.0": canvas, draw + +#let _flow(from, amount, to) = { + return (( + type: "flow", + from: from, + to: to, + amount: amount + ),) +} + +#let _node(name, display-name: auto, color: auto) = { + return (( + type: "node", + name: name, + display-name: if display-name == auto { + name + } else { + display-name + }, + color: color, + is-src: false, + is-dst: false, + sources: (), + targets: (), + max-depth: 0, + total: ( + "in": 0, + "out": 0, + "max": 0 + ) + ),) +} + +#let process-depth(nodes, flows, node, visited: ()) = { + let next-depth = node.max-depth + 1 + let total-depth = node.max-depth + let depth + for flow in node.targets { + if flow.to in visited { + panic("Loop") + } + nodes.at(flow.to).max-depth = calc.max( + nodes.at(flow.to).max-depth, + next-depth + ) + + (depth, nodes) = process-depth(nodes, flows, nodes.at(flow.to), visited: visited + (flow.to,)) + total-depth = calc.max(total-depth, depth) + } + return (total-depth, nodes) +} + +#let diagram( + elmts, + height, + min-spacing: 1cm +) = { + let columns = () + let nodes = (:) + let flows = () + + for elmt in elmts { + if elmt.type == "node" { + nodes.insert(elmt.name, elmt) + } else if elmt.type == "flow" { + flows.push(elmt) + } + } + + for flow in flows { + if not flow.from in nodes { + nodes.insert(flow.from, _node(flow.from).first()) + } + if not flow.to in nodes { + nodes.insert(flow.to, _node(flow.to).first()) + } + nodes.at(flow.from).is-src = true + nodes.at(flow.from).targets.push(flow) + nodes.at(flow.to).is-dst = true + nodes.at(flow.to).sources.push(flow) + } + + for (nid, node) in nodes.pairs() { + let tot-in = node.targets.map(t => t.amount).sum(default: 0) + let tot-out = node.sources.map(t => t.amount).sum(default: 0) + nodes.at(nid).total = ( + "in": tot-in, + "out": tot-out, + "max": calc.max(tot-in, tot-out), + ) + } + + let total-depth = 0 + let d + for node in nodes.values().filter(n => n.is-src and not n.is-dst) { + (d, nodes) = process-depth(nodes, flows, node) + total-depth = calc.max(total-depth, d) + } + + columns = ((),) * (total-depth + 1) + let column-totals = (0,) * columns.len() + for (key, node) in nodes.pairs() { + columns.at(node.max-depth).push(key) + column-totals.at(node.max-depth) += node.total.max + } + + //total-depth + [ + *nodes*: #nodes + + *total-depth*: #total-depth + + *columns*: #columns + + *column-totals*: #column-totals + ] + //flows + + let h-factor = calc.min( + ..columns.zip(column-totals).map(((col, total)) => { + let available-h = height - (col.len() - 1) * min-spacing + return available-h / total + }) + ) + + let colors = (red, orange, yellow, green, blue, purple).map(c => c.lighten(50%)) + + canvas({ + let c = 0 + for (i, column) in columns.enumerate() { + let x = i * 3 + let total = column.map(n => nodes.at(n).total.max).sum() + let total-h = total * h-factor + let remaining-h = height - total-h + let spacing = remaining-h / (column.len() + 1) + + let y = -spacing + for (j, nid) in column.enumerate() { + let node = nodes.at(nid) + + let h = h-factor * node.total.max + draw.rect( + (x, y), + (x + 1, y - h), + name: nid, + fill: colors.at(c) + ) + c += 1 + + if node.is-src and not node.is-dst { + draw.content( + nid + ".west", + anchor: "east", + node.display-name, + padding: 5pt + ) + } else if node.is-src and node.is-dst { + draw.content( + nid + ".center", + anchor: "center", + node.display-name, + padding: 5pt, + frame: "rect", + fill: white.transparentize(20%) + ) + } else if not node.is-src and node.is-dst { + draw.content( + nid + ".east", + anchor: "west", + node.display-name, + padding: 5pt + ) + } + + y -= spacing + h + } + } + }) +} \ No newline at end of file diff --git a/src/lib.typ b/src/lib.typ new file mode 100644 index 0000000..dd5bad9 --- /dev/null +++ b/src/lib.typ @@ -0,0 +1,2 @@ +#import "diagram.typ": diagram +#import "diagram.typ": _flow, _node \ No newline at end of file