diff --git a/gallery/notes.pdf b/gallery/notes.pdf index 7e65f04..e114639 100644 Binary files a/gallery/notes.pdf and b/gallery/notes.pdf differ diff --git a/gallery/notes.typ b/gallery/notes.typ index f2f20fa..7e4ba7e 100644 --- a/gallery/notes.typ +++ b/gallery/notes.typ @@ -74,10 +74,19 @@ #chronos.diagram({ _par("a", display-name: "Alice") _par("b", display-name: "Bob") + _par("c", display-name: "Charlie") + _par("d", display-name: "Donald") + _par("e", display-name: "Eddie") _note("over", [initial state of Alice], pos: "a") - _note("over", [initial state of Bob], pos: "b", aligned: true) - _seq("b", "a", comment: [hello]) + _note("over", [initial state of Bob the builder], pos: "b", aligned: true) + + _note("over", [Note 1], pos: "a") + _note("over", [Note 2], pos: "b", aligned: true) + _note("over", [Note 3], pos: "c", aligned: true) + + _seq("a", "d") + _note("over", [this is an extremely long note], pos: ("d", "e")) }) #pagebreak() @@ -111,4 +120,19 @@ _note("over", [ #underline([This is hosted], stroke: rgb("#FF33FF")) by #box(baseline: 50%, image("gitea.png", width: 1cm, height: 1cm, fit: "contain")) ], pos: ("a", "b")) -}) \ No newline at end of file +}) + +// TODO +/* +#pagebreak() + +#chronos.diagram({ + _par("a", display-name: [Alice]) + _par("b", display-name: [Bob]) + + _seq("a", "b", comment: [Hello]) + _note("left", [This is a note]) + + _seq("[", "a", comment: [Test]) + _note("left", [This is also a note]) +})*/ \ No newline at end of file diff --git a/src/diagram.typ b/src/diagram.typ index 48685dc..03c41b8 100644 --- a/src/diagram.typ +++ b/src/diagram.typ @@ -58,6 +58,7 @@ // List participants let linked = () let last-seq = none + let last-note = none for (i, elmt) in elmts.enumerate() { if elmt.type == "par" { participants.push(elmt) @@ -107,12 +108,21 @@ seq.insert("linked-note", elmt) elmts.at(last-seq.i) = seq } + if elmt.aligned { + let n = last-note.elmt + n.aligned-with = elmt + elmts.at(last-note.i) = n + } elmts.at(i) = elmt if elmt.side == "left" { linked.push("[") } else if elmt.side == "right" { linked.push("]") } + last-note = ( + elmt: elmt, + i: i + ) } } linked = linked.dedup() diff --git a/src/note.typ b/src/note.typ index 39281da..4c12eb3 100644 --- a/src/note.typ +++ b/src/note.typ @@ -15,6 +15,16 @@ ) #let _note(side, content, pos: none, color: COL-NOTE, shape: "default", aligned: false) = { + if side == "over" { + if pos == none { + panic("Pos cannot be none with side 'over'") + } + } + if aligned { + if side != "over" { + panic("Aligned notes can only be over a participant (got side '" + side + "')") + } + } return (( type: "note", side: side, @@ -22,7 +32,8 @@ pos: pos, color: color, shape: shape, - aligned: aligned + aligned: aligned, + aligned-with: none ),) } @@ -66,7 +77,7 @@ if note.side == "over" { if type(note.pos) == array { let xs = note.pos.map(par => x-pos.at(pars-i.at(par))) - return xs.sum() / xs.len() + return (calc.min(..xs) + calc.max(..xs)) / 2 } } return x-pos.at(pars-i.at(note.pos)) @@ -162,7 +173,7 @@ anchor: "center" ) - if note.pos != none or note.side == "across" { + if note.aligned-with == none and (note.pos != none or note.side == "across") { y -= h } diff --git a/src/renderer.typ b/src/renderer.typ index 540a4ce..42d8f9f 100644 --- a/src/renderer.typ +++ b/src/renderer.typ @@ -65,23 +65,36 @@ } else if elmt.type == "note" { let (p1, p2) = (none, none) + let cell = none if elmt.side == "left" { p1 = "[" p2 = elmt.pos + cell = get-note-box(elmt) } else if elmt.side == "right" { p1 = elmt.pos p2 = "]" + cell = get-note-box(elmt) + } else if elmt.side == "over" { + if elmt.aligned-with != none { + let box1 = get-note-box(elmt) + let box2 = get-note-box(elmt.aligned-with) + let m1 = measure(box1) + let m2 = measure(box2) + cell = box(width: (m1.width + m2.width) / 2, height: calc.max(m1.height, m2.height)) + p1 = elmt.pos + p2 = elmt.aligned-with.pos + } } - if p1 != none and p2 != none { + if p1 != none and p2 != none and cell != none { let i1 = pars-i.at(p1) let i2 = pars-i.at(p2) cells.push( ( elmt: elmt, - i1: i1, - i2: i2, - cell: get-note-box(elmt) + i1: calc.min(i1, i2), + i2: calc.max(i1, i2), + cell: cell ) ) } @@ -101,6 +114,28 @@ widths.push(w1 / 2pt + w2 / 2pt + PAR-SPACE) } + // Compute minimum width for over notes + for n in elements.filter(e => (e.type == "note" and + e.side == "over" and + type(e.pos) == str)) { + + let m = note.get-size(n) + let i = pars-i.at(n.pos) + + if i < widths.len() { + widths.at(i) = calc.max( + widths.at(i), + m.width / 2 + NOTE-GAP + ) + } + if i > 0 { + widths.at(i - 1) = calc.max( + widths.at(i - 1), + m.width / 2 + NOTE-GAP + ) + } + } + // Compute minimum width for simple sequences (spanning 1 column) for cell in cells.filter(c => c.i2 - c.i1 == 1) { let m = measure(cell.cell) @@ -111,7 +146,7 @@ } // Compute minimum width for self sequences - for cell in cells.filter(c => c.i1 == c.i2) { + for cell in cells.filter(c => c.elmt.type == "seq" and c.i1 == c.i2) { let m = measure(cell.cell) let i = cell.i1 if cell.elmt.flip { @@ -252,7 +287,9 @@ // Note } else if elmt.type == "note" { if not elmt.linked { - y -= Y-SPACE + if not elmt.aligned { + y -= Y-SPACE + } let shps (y, shps) = draw-note(elmt, y, lifelines) shapes += shps