added flow rendering

This commit is contained in:
Louis Heredero 2024-10-21 10:16:14 +02:00
parent 5231f4b324
commit 047b679656
Signed by: HEL
GPG Key ID: 8D83DE470F8544E7

View File

@ -51,20 +51,37 @@
return (total-depth, nodes) return (total-depth, nodes)
} }
#let FLOW-COLOR = (
"source",
"target",
"gradient"
)
#let diagram( #let diagram(
elmts, elmts,
height, height,
min-spacing: 1cm width: auto,
h-space: 2cm,
min-spacing: 1cm,
node-width: 0.5cm,
flow-color: "source",
roundiness: 50%,
align-sources: false,
align-sinks: false
) = { ) = {
let columns = () let columns = ()
let nodes = (:) let nodes = (:)
let flows = () let flows = ()
let order = ()
for elmt in elmts { for elmt in elmts {
if elmt.type == "node" { if elmt.type == "node" {
order.push(elmt.name)
nodes.insert(elmt.name, elmt) nodes.insert(elmt.name, elmt)
} else if elmt.type == "flow" { } else if elmt.type == "flow" {
flows.push(elmt) flows.push(elmt)
order.push(elmt.from)
order.push(elmt.to)
} }
} }
@ -100,22 +117,22 @@
columns = ((),) * (total-depth + 1) columns = ((),) * (total-depth + 1)
let column-totals = (0,) * columns.len() let column-totals = (0,) * columns.len()
for (key, node) in nodes.pairs() { let pairs = nodes.pairs()
columns.at(node.max-depth).push(key) // Order nodes by reference order
column-totals.at(node.max-depth) += node.total.max pairs = pairs.sorted(key: p => {
order.position(n => n == p.first())
})
for (key, node) in pairs {
let col = node.max-depth
if not node.is-dst and align-sources {
col = 0
}
if not node.is-src and align-sinks {
col = columns.len() - 1
}
columns.at(col).push(key)
column-totals.at(col) += node.total.max
} }
//total-depth
[
*nodes*: #nodes
*total-depth*: #total-depth
*columns*: #columns
*column-totals*: #column-totals
]
//flows
let h-factor = calc.min( let h-factor = calc.min(
..columns.zip(column-totals).map(((col, total)) => { ..columns.zip(column-totals).map(((col, total)) => {
@ -124,12 +141,21 @@
}) })
) )
let get-color(nid) = {
let r = nodes.keys().position(n => n == nid) / nodes.len()
return color.hsv(r * 270deg, 60%, 90%)
}
let colors = (red, orange, yellow, green, blue, purple).map(c => c.lighten(50%)) let colors = (red, orange, yellow, green, blue, purple).map(c => c.lighten(50%))
let h-space = h-space
if width != auto and columns.len() > 1 {
let remaining-w = width - columns.len() * node-width
h-space = remaining-w / (columns.len() - 1)
}
canvas({ canvas({
let c = 0 let x = 0pt
for (i, column) in columns.enumerate() { for (i, column) in columns.enumerate() {
let x = i * 3
let total = column.map(n => nodes.at(n).total.max).sum() let total = column.map(n => nodes.at(n).total.max).sum()
let total-h = total * h-factor let total-h = total * h-factor
let remaining-h = height - total-h let remaining-h = height - total-h
@ -142,39 +168,111 @@
let h = h-factor * node.total.max let h = h-factor * node.total.max
draw.rect( draw.rect(
(x, y), (x, y),
(x + 1, y - h), (x + node-width, y - h),
name: nid, name: nid,
fill: colors.at(c) fill: get-color(nid),
stroke: none
) )
c += 1
let pos = nid
let algn = center
let txt = [
#node.display-name\
#node.total.max
]
let args = (
anchor: "center"
)
if node.is-src and not node.is-dst { if node.is-src and not node.is-dst {
draw.content( pos = nid + ".west"
nid + ".west", args.anchor = "east"
anchor: "east", algn = right
node.display-name,
padding: 5pt
)
} else if node.is-src and node.is-dst { } else if node.is-src and node.is-dst {
draw.content( pos = nid + ".center"
nid + ".center", args.anchor = "center"
anchor: "center", algn = center
node.display-name, args.insert("frame", "rect")
padding: 5pt, args.insert("fill", white.transparentize(50%))
frame: "rect", args.insert("stroke", none)
fill: white.transparentize(20%)
)
} else if not node.is-src and node.is-dst { } else if not node.is-src and node.is-dst {
pos = nid + ".east"
args.anchor = "west"
algn = left
}
draw.content( draw.content(
nid + ".east", pos,
anchor: "west", ..args,
node.display-name, align(algn, txt),
padding: 5pt padding: 5pt
) )
}
y -= spacing + h y -= spacing + h
} }
x += node-width + h-space
}
let offsets = (:)
for nid in nodes.keys() {
offsets.insert(nid, (
"in": 0pt,
"out": 0pt
))
}
draw.on-layer(-1, {
for flow in flows {
let tl = flow.from + ".north-east"
let tr = flow.to + ".north-west"
let off-out = offsets.at(flow.from).out
let off-in = offsets.at(flow.to).in
let h = flow.amount * h-factor
offsets.at(flow.from).out += h
offsets.at(flow.to).in += h
tl = (rel: (0, -off-out), to: tl)
tr = (rel: (0, -off-in), to: tr)
let br = (rel: (0, -h), to: tr)
let bl = (rel: (0, -h), to: tl)
let fill = gray.lighten(50%).transparentize(50%)
let col1 = get-color(flow.from).desaturate(50%).lighten(50%).transparentize(50%)
let col2 = get-color(flow.to).desaturate(50%).lighten(50%).transparentize(50%)
if flow-color == "source" {
fill = col1
} else if flow-color == "target" {
fill = col2
} else if flow-color == "gradient" {
fill = gradient.linear(col1, col2)
} else {
fill = flow-color
}
let dx = roundiness * h-space
draw.merge-path(
fill: fill,
stroke: none,
{
draw.bezier(
tl,
tr,
(rel: (dx, 0), to: tl),
(rel: (-dx, 0), to: tr),
)
draw.bezier(
br,
bl,
(rel: (-dx, 0), to: br),
(rel: (dx, 0), to: bl),
)
}
)
} }
}) })
})
} }