diff --git a/docs/notes.typ b/docs/notes.typ index 3b783eb..3c4b72e 100644 --- a/docs/notes.typ +++ b/docs/notes.typ @@ -5,13 +5,15 @@ /// - color (color): The note's color /// - shape (str): The note's shape (see @@SHAPES for accepted values) /// - aligned (bool): True if the note is aligned with another note, in which case `side` must be `"over"`, false otherwise +/// - allow-overlap (bool): If set to `false`, the note will try to reserve space in the column to avoid overlapping with neighboring participants. If set to `true`, the not will overlap other participants #let _note( side, content, pos: none, color: rgb("#FEFFDD"), shape: "default", - aligned: false + aligned: false, + allow-overlap: true ) = {} /// Accepted values for `shape` argument of @@_note() diff --git a/manual.pdf b/manual.pdf index 2875d03..8c067ce 100644 Binary files a/manual.pdf and b/manual.pdf differ diff --git a/src/core/draw/group.typ b/src/core/draw/group.typ index 25aafaf..8604eaa 100644 --- a/src/core/draw/group.typ +++ b/src/core/draw/group.typ @@ -1,7 +1,7 @@ #import "/src/cetz.typ": draw #import "/src/consts.typ": * -#import "/src/core/utils.typ": get-ctx, set-ctx +#import "/src/core/utils.typ": get-ctx, set-ctx, expand-parent-group #let render-start(grp) = get-ctx(ctx => { let grp = grp @@ -18,14 +18,21 @@ ) ) ctx.groups = ctx.groups.map(g => { - if g.at(1).min-i == grp.min-i { g.at(2) += 1 } - if g.at(1).max-i == grp.max-i { g.at(3) += 1 } + if g.group.min-i == grp.min-i { g.start-lvl += 1 } + if g.group.max-i == grp.max-i { g.end-lvl += 1 } g }) if grp.grp-type == "alt" { grp.insert("elses", ()) } - ctx.groups.push((ctx.y, grp, 0, 0)) + ctx.groups.push(( + start-y: ctx.y, + group: grp, + start-lvl: 0, + end-lvl: 0, + min-x: ctx.x-pos.at(grp.min-i) - 10, + max-x: ctx.x-pos.at(grp.max-i) + 10 + )) ctx.y -= m.height / 1pt set-ctx(c => { @@ -87,9 +94,16 @@ #let render-end(group) = get-ctx(ctx => { ctx.y -= Y-SPACE - let (start-y, group, start-lvl, end-lvl) = ctx.groups.pop() - let x0 = ctx.x-pos.at(group.min-i) - start-lvl * 10 - 20 - let x1 = ctx.x-pos.at(group.max-i) + end-lvl * 10 + 20 + let ( + start-y, + group, + start-lvl, + end-lvl, + min-x, + max-x + ) = ctx.groups.pop() + let x0 = min-x - 10 + let x1 = max-x + 10 draw-group(x0, x1, start-y, ctx.y, group) @@ -104,12 +118,14 @@ c.groups = ctx.groups return c }) + + expand-parent-group(x0, x1) }) #let render-else(else_) = set-ctx(ctx => { ctx.y -= Y-SPACE let m = measure(text([\[#else_.desc\]], weight: "bold", size: .8em)) - ctx.groups.last().at(1).elses.push(( + ctx.groups.last().group.elses.push(( ctx.y, else_ )) ctx.y -= m.height / 1pt diff --git a/src/core/draw/note.typ b/src/core/draw/note.typ index d8b353d..33b33aa 100644 --- a/src/core/draw/note.typ +++ b/src/core/draw/note.typ @@ -1,7 +1,7 @@ #import "/src/cetz.typ": draw #import "/src/consts.typ": * -#import "/src/core/utils.typ": get-ctx, set-ctx +#import "/src/core/utils.typ": get-ctx, set-ctx, expand-parent-group #let get-size(note) = { let PAD = if note.shape == "hex" {NOTE-HEX-PAD} else {NOTE-PAD} @@ -159,5 +159,7 @@ return c }) } + + expand-parent-group(x0, x2) }) } \ No newline at end of file diff --git a/src/core/draw/sequence.typ b/src/core/draw/sequence.typ index 0d0ae47..49103e6 100644 --- a/src/core/draw/sequence.typ +++ b/src/core/draw/sequence.typ @@ -67,9 +67,12 @@ if comment != none { h = calc.max(h, measure(comment).height / 1pt + 6) } - if "linked-note" in seq { - h = calc.max(h, note.get-size(seq.linked-note).height / 2) - } + h = calc.max( + h, + ..seq.linked-notes.map(n => { + note.get-size(n).height / 2 + }) + ) ctx.y -= h let start-info = ( @@ -165,9 +168,8 @@ ) let y0 = start-info.y - if "linked-note" in seq { - // TODO: adapt note.render - (seq.linked-note.draw)(seq.linked-note, y: start-info.y, forced: true) + for n in seq.linked-notes { + (n.draw)(n, y: start-info.y, forced: true) } let flip-mark = end-info.i <= start-info.i @@ -348,9 +350,14 @@ ctx.lifelines.at(i2) = dst-line } - if "linked-note" in seq { - let m = note.get-size(seq.linked-note) - end-info.y = calc.min(end-info.y, y0 - m.height / 2) + if seq.linked-notes.len() != 0 { + end-info.y = calc.min( + end-info.y, + y0 - calc.max(..seq.linked-notes.map(n => { + let m = note.get-size(n) + return m.height / 2 + })) + ) } set-ctx(c => { diff --git a/src/core/renderer.typ b/src/core/renderer.typ index 1b5418c..1bb3297 100644 --- a/src/core/renderer.typ +++ b/src/core/renderer.typ @@ -82,12 +82,12 @@ let (p1, p2) = (none, none) let cell = none if note.side == "left" { - p1 = "[" + p1 = note.pos2 p2 = note.pos cell = get-note-box(note) } else if note.side == "right" { p1 = note.pos - p2 = "]" + p2 = note.pos2 cell = get-note-box(note) } else if note.side == "over" and note.aligned-with != none { let box1 = get-note-box(note) @@ -220,7 +220,7 @@ } /// Compute remaining widths for longer sequences (spanning multiple columns) -#let long-seq-min-col-widths(cells, widths) = { +#let long-seq-min-col-widths(participants, cells, widths) = { let widths = widths let multicol-cells = cells.filter(c => c.i2 - c.i1 > 1) multicol-cells = multicol-cells.sorted(key: c => { @@ -228,13 +228,23 @@ }) for cell in multicol-cells { let m = measure(cell.cell) + + let i1 = cell.i1 + let i2 = cell.i2 - 1 + let i = i2 + if cell.i1 == 0 and participants.at(0).name == "[" { + i = 0 + i1 += 1 + i2 += 1 + } let width = ( m.width / 1pt + COMMENT-PAD - - widths.slice(cell.i1, cell.i2 - 1).sum() + widths.slice(i1, i2).sum() ) - widths.at(cell.i2 - 1) = calc.max( - widths.at(cell.i2 - 1), width + + widths.at(i) = calc.max( + widths.at(i), width ) } return widths @@ -307,7 +317,7 @@ widths = notes-min-col-widths(elements, widths, pars-i) widths = simple-seq-min-col-widths(cells, widths) widths = self-seq-min-col-widths(cells, widths) - widths = long-seq-min-col-widths(cells, widths) + widths = long-seq-min-col-widths(participants, cells, widths) widths = col-widths-add-lifelines(participants, widths) widths = process-col-elements(elements, widths, pars-i) return widths diff --git a/src/core/setup.typ b/src/core/setup.typ index 3d8f7fe..7873526 100644 --- a/src/core/setup.typ +++ b/src/core/setup.typ @@ -126,8 +126,8 @@ "linked", note.pos == none and note.side != "across" ) + let names = ctx.participants.map(p => p.name) if note.pos == none and note.side != "across" { - let names = ctx.participants.map(p => p.name) let i1 = names.position(n => n == ctx.last-seq.p1) let i2 = names.position(n => n == ctx.last-seq.p2) let pars = ( @@ -141,8 +141,8 @@ note.pos = pars.last().last() } - let seq = ctx.last-seq.seq - seq.insert("linked-note", note) + let seq = ctx.elmts.at(ctx.last-seq.i) + seq.linked-notes.push(note) ctx.elmts.at(ctx.last-seq.i) = seq } if note.aligned { @@ -150,10 +150,26 @@ n.aligned-with = note ctx.elmts.at(ctx.last-note.i) = n } - if note.side == "left" { - ctx.linked.push("[") - } else if note.side == "right" { - ctx.linked.push("]") + + if note.side in ("left", "right") { + let i = names.position(n => n == note.pos) + let pos2 = note.pos + if note.side == "left" { + if i <= 0 or note.allow-overlap { + ctx.linked.push("[") + pos2 = "[" + } else { + pos2 = names.at(i - 1) + } + } else if note.side == "right" { + if i >= names.len() - 1 or note.allow-overlap { + ctx.linked.push("]") + pos2 = "]" + } else { + pos2 = names.at(i + 1) + } + } + note.insert("pos2", pos2) } let pars = none diff --git a/src/core/utils.typ b/src/core/utils.typ index 7698037..4d6d003 100644 --- a/src/core/utils.typ +++ b/src/core/utils.typ @@ -104,4 +104,14 @@ #let get-ctx(func) = draw.get-ctx(c => { let ctx = c.shared-state.chronos func(ctx) +}) + +#let expand-parent-group(x0, x1) = set-ctx(ctx => { + if ctx.groups.len() != 0 { + let group = ctx.groups.last() + group.min-x = calc.min(group.min-x, x0) + group.max-x = calc.max(group.max-x, x1) + ctx.groups.last() = group + } + return ctx }) \ No newline at end of file diff --git a/src/misc.typ b/src/misc.typ index 86eb4a0..1bd0ce8 100644 --- a/src/misc.typ +++ b/src/misc.typ @@ -1,4 +1,5 @@ #import "core/draw/delay.typ" +#import "core/draw/event.typ": render as evt-render #import "core/draw/separator.typ" #import "core/draw/sync.typ" #import "core/utils.typ": set-ctx diff --git a/src/note.typ b/src/note.typ index 948b8d5..01e6b31 100644 --- a/src/note.typ +++ b/src/note.typ @@ -14,7 +14,15 @@ "hex" ) -#let _note(side, content, pos: none, color: COL-NOTE, shape: "default", aligned: false) = { +#let _note( + side, + content, + pos: none, + color: COL-NOTE, + shape: "default", + aligned: false, + allow-overlap: true +) = { if side == "over" { if pos == none { panic("Pos cannot be none with side 'over'") @@ -37,6 +45,7 @@ color: color, shape: shape, aligned: aligned, - aligned-with: none + aligned-with: none, + allow-overlap: allow-overlap ),) } diff --git a/src/sequence.typ b/src/sequence.typ index c99df3a..a328f4a 100644 --- a/src/sequence.typ +++ b/src/sequence.typ @@ -38,7 +38,8 @@ disable-src: disable-src, destroy-src: destroy-src, lifeline-style: lifeline-style, - slant: slant + slant: slant, + linked-notes: () ),) } diff --git a/tests/sequence/special-par/ref/1.png b/tests/sequence/special-par/ref/1.png index 1259a7e..cce43e8 100644 Binary files a/tests/sequence/special-par/ref/1.png and b/tests/sequence/special-par/ref/1.png differ