2024-06-18 13:39:38 +02:00
|
|
|
#import "@preview/cetz:0.2.2": canvas, draw
|
|
|
|
|
2024-06-18 13:51:01 +02:00
|
|
|
#let Y-SPACE = 20
|
2024-06-18 14:11:03 +02:00
|
|
|
#let PAR-PAD = (5pt, 3pt)
|
|
|
|
#let PAR-SPACE = 10
|
2024-06-18 13:39:38 +02:00
|
|
|
|
|
|
|
#let get-participants-i(participants) = {
|
|
|
|
let pars-i = (:)
|
|
|
|
for (i, p) in participants.enumerate() {
|
|
|
|
pars-i.insert(p.name, i)
|
|
|
|
}
|
|
|
|
return pars-i
|
|
|
|
}
|
|
|
|
|
|
|
|
#let get-columns-width(participants, elements) = {
|
|
|
|
let pars-i = get-participants-i(participants)
|
|
|
|
let cells = ()
|
|
|
|
for elmt in elements {
|
|
|
|
if elmt.type == "seq" {
|
|
|
|
let com = if elmt.comment == none {""} else {elmt.comment}
|
|
|
|
let i1 = pars-i.at(elmt.p1)
|
|
|
|
let i2 = pars-i.at(elmt.p2)
|
|
|
|
cells.push(
|
|
|
|
(
|
|
|
|
elmt: elmt,
|
|
|
|
i1: calc.min(i1, i2),
|
|
|
|
i2: calc.max(i1, i2),
|
|
|
|
cell: box(com, inset: 3pt)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-18 14:11:03 +02:00
|
|
|
let widths = ()
|
|
|
|
for i in range(participants.len() - 1) {
|
|
|
|
let p1 = participants.at(i)
|
|
|
|
let p2 = participants.at(i + 1)
|
|
|
|
let w1 = measure(box(p1.display-name)).width + PAR-PAD.last() * 2
|
|
|
|
let w2 = measure(box(p2.display-name)).width + PAR-PAD.last() * 2
|
|
|
|
widths.push(w1 / 2pt + w2 / 2pt + PAR-SPACE)
|
|
|
|
}
|
|
|
|
|
2024-06-18 13:39:38 +02:00
|
|
|
for cell in cells.filter(c => c.i2 - c.i1 == 1) {
|
|
|
|
let m = measure(cell.cell)
|
|
|
|
widths.at(cell.i1) = calc.max(
|
|
|
|
widths.at(cell.i1),
|
|
|
|
m.width / 1pt
|
|
|
|
)
|
|
|
|
}
|
2024-06-18 14:11:03 +02:00
|
|
|
|
|
|
|
let multicol-cells = cells.filter(c => c.i2 - c.i1 > 1)
|
|
|
|
multicol-cells = multicol-cells.sorted(key: c => {
|
|
|
|
c.i1 * 1000 + c.i2
|
|
|
|
})
|
|
|
|
for cell in multicol-cells {
|
|
|
|
let m = measure(cell.cell)
|
|
|
|
widths.at(cell.i2 - 1) = calc.max(
|
|
|
|
widths.at(cell.i2 - 1),
|
|
|
|
m.width / 1pt - widths.slice(0, cell.i2 - 1).sum()
|
|
|
|
)
|
|
|
|
}
|
2024-06-18 13:39:38 +02:00
|
|
|
return widths
|
|
|
|
}
|
|
|
|
|
|
|
|
#let render(participants, elements) = context canvas(length: 1pt, {
|
|
|
|
let pars-i = get-participants-i(participants)
|
|
|
|
|
|
|
|
let widths = get-columns-width(participants, elements)
|
|
|
|
|
|
|
|
let x-pos = (0,)
|
|
|
|
for width in widths {
|
|
|
|
x-pos.push(x-pos.last() + width)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw participants
|
|
|
|
for (i, p) in participants.enumerate() {
|
|
|
|
draw.content(
|
|
|
|
(x-pos.at(i), 0),
|
|
|
|
p.display-name,
|
|
|
|
name: p.name,
|
|
|
|
frame: "rect",
|
2024-06-18 14:11:03 +02:00
|
|
|
padding: PAR-PAD,
|
2024-06-18 13:39:38 +02:00
|
|
|
anchor: "south"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
let y = -Y-SPACE
|
|
|
|
// Draw sequences
|
|
|
|
for elmt in elements {
|
|
|
|
if elmt.type == "seq" {
|
|
|
|
let x1 = x-pos.at(pars-i.at(elmt.p1))
|
|
|
|
let x2 = x-pos.at(pars-i.at(elmt.p2))
|
2024-06-18 13:51:01 +02:00
|
|
|
let style = (
|
|
|
|
mark: (end: "straight"),
|
|
|
|
stroke: (
|
|
|
|
dash: if elmt.dashed {"dashed"} else {"solid"},
|
|
|
|
paint: elmt.color
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2024-06-18 13:39:38 +02:00
|
|
|
draw.line(
|
|
|
|
(x1, y),
|
|
|
|
(x2, y),
|
2024-06-18 13:51:01 +02:00
|
|
|
..style
|
2024-06-18 13:39:38 +02:00
|
|
|
)
|
|
|
|
if elmt.comment != none {
|
|
|
|
draw.content(
|
|
|
|
(calc.min(x1, x2), y),
|
|
|
|
elmt.comment,
|
|
|
|
anchor: "south-west",
|
|
|
|
padding: 3pt
|
|
|
|
)
|
|
|
|
}
|
|
|
|
y -= Y-SPACE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw vertical lines + end participants
|
|
|
|
draw.on-layer(-1, {
|
|
|
|
for (i, p) in participants.enumerate() {
|
|
|
|
let x = x-pos.at(i)
|
|
|
|
draw.line(
|
|
|
|
(x, 0),
|
|
|
|
(x, y),
|
|
|
|
stroke: (dash: "dashed", paint: gray.darken(40%))
|
|
|
|
)
|
|
|
|
draw.content(
|
|
|
|
(x, y),
|
|
|
|
p.display-name,
|
|
|
|
name: p.name,
|
|
|
|
frame: "rect",
|
2024-06-18 14:11:03 +02:00
|
|
|
padding: PAR-PAD,
|
2024-06-18 13:39:38 +02:00
|
|
|
anchor: "north"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|