chronos/src/renderer.typ

232 lines
5.3 KiB
Typst
Raw Normal View History

2024-06-18 13:39:38 +02:00
#import "@preview/cetz:0.2.2": canvas, draw
2024-06-18 16:09:06 +02:00
#import "utils.typ": get-participants-i
2024-06-18 13:39:38 +02:00
2024-06-18 16:09:06 +02:00
#let Y-SPACE = 10
2024-06-18 14:11:03 +02:00
#let PAR-PAD = (5pt, 3pt)
#let PAR-SPACE = 10
2024-06-18 16:24:11 +02:00
#let COMMENT-PAD = 8
2024-06-18 13:39:38 +02:00
#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),
2024-06-18 16:24:11 +02:00
m.width / 1pt + COMMENT-PAD
2024-06-18 13:39:38 +02:00
)
}
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
}
2024-06-18 16:09:06 +02:00
#let draw-group(x0, x1, y0, y1, group) = {
let m = measure(box(group.name))
let w = m.width / 1pt + 15
let h = m.height / 1pt + 6
draw.rect(
(x0, y0),
(x1, y1)
)
draw.merge-path(
fill: gray.lighten(20%),
close: true,
{
draw.line(
(x0, y0),
(x0 + w, y0),
(x0 + w, y0 - h / 2),
(x0 + w - 5, y0 - h),
(x0, y0 - h)
)
}
)
draw.content(
(x0, y0),
group.name,
anchor: "north-west",
padding: (left: 5pt, right: 10pt, top: 3pt, bottom: 3pt)
)
if group.desc != none {
draw.content(
(x0 + w, y0),
text([\[#group.desc\]], weight: "bold"),
anchor: "north-west",
padding: 3pt
)
}
}
2024-06-18 13:39:38 +02:00
#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
2024-06-18 16:09:06 +02:00
let groups = ()
2024-06-18 13:39:38 +02:00
// 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
if elmt.comment != none {
2024-06-18 16:24:11 +02:00
let x = calc.min(x1, x2)
if x2 < x1 {
x += COMMENT-PAD
}
2024-06-18 16:09:06 +02:00
y -= measure(box(elmt.comment)).height / 1pt + 6
2024-06-18 13:39:38 +02:00
draw.content(
2024-06-18 16:24:11 +02:00
(x, y),
2024-06-18 13:39:38 +02:00
elmt.comment,
anchor: "south-west",
padding: 3pt
)
}
2024-06-18 16:09:06 +02:00
draw.line(
(x1, y),
(x2, y),
..style
)
y -= Y-SPACE
} else if elmt.type == "grp" {
let m = measure(
box(
inset: (left: 5pt, right: 5pt, top: 3pt, bottom: 3pt),
)
)
groups = groups.map(g => {
if g.at(1).min-i == elmt.min-i { g.at(2) += 1 }
if g.at(1).max-i == elmt.max-i { g.at(3) += 1 }
g
})
groups.push((y, elmt, 0, 0))
y -= m.height / 1pt + Y-SPACE
} else if elmt.type == "grp-end" {
let (start-y, group, start-lvl, end-lvl) = groups.pop()
let x0 = x-pos.at(group.min-i) - start-lvl * 10 - 20
let x1 = x-pos.at(group.max-i) + end-lvl * 10 + 20
draw-group(x0, x1, start-y, y, group)
2024-06-18 13:39:38 +02:00
y -= Y-SPACE
2024-06-18 16:18:40 +02:00
} else if elmt.type == "sep" {
let x0 = x-pos.first() - 20
let x1 = x-pos.last() + 20
let m = measure(
box(
elmt.name,
inset: (left: 3pt, right: 3pt, top: 5pt, bottom: 5pt)
)
)
let w = m.width / 1pt
let h = m.height / 1pt
let cx = (x0 + x1) / 2
let xl = cx - w / 2
let xr = cx + w / 2
y -= h / 2
draw.line((x0, y), (xl, y))
draw.line((xr, y), (x1, y))
y -= 3
draw.line((x0, y), (xl, y))
draw.line((xr, y), (x1, y))
draw.content(
((x0 + x1) / 2, y + 1.5),
elmt.name,
anchor: "center",
padding: (5pt, 3pt),
frame: "rect"
)
y -= h / 2
y -= Y-SPACE
2024-06-18 16:24:11 +02:00
} else if elmt.type == "gap" {
y -= elmt.size
2024-06-18 13:39:38 +02:00
}
}
// 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"
)
}
})
})