forked from HEL/chronos
		
	added notes
This commit is contained in:
		| @@ -15,6 +15,12 @@ | ||||
| #let COLLECTIONS-DY = 3 | ||||
| #let QUEUE-PAD = (5pt, 3pt) | ||||
|  | ||||
| #let NOTE-PAD = (6, 3) | ||||
| #let NOTE-CORNER-SIZE = 6 | ||||
| #let NOTE-GAP = 3 | ||||
| #let NOTE-HEX-PAD = (6, 8) | ||||
|  | ||||
| #let COL-DESTRUCTION = rgb("#A80238") | ||||
| #let COL-GRP-NAME = rgb("#EEEEEE") | ||||
| #let COL-SEP-NAME = rgb("#EEEEEE") | ||||
| #let COL-SEP-NAME = rgb("#EEEEEE") | ||||
| #let COL-NOTE = rgb("#FEFFDD") | ||||
| @@ -57,7 +57,8 @@ | ||||
|  | ||||
|   // List participants | ||||
|   let linked = () | ||||
|   for elmt in elmts { | ||||
|   let last-seq = none | ||||
|   for (i, elmt) in elmts.enumerate() { | ||||
|     if elmt.type == "par" { | ||||
|       participants.push(elmt) | ||||
|     } else if elmt.type == "seq" { | ||||
| @@ -73,15 +74,44 @@ | ||||
|         participants.at(i).from-start = false | ||||
|       } | ||||
|  | ||||
|       let p1 = elmt.p1 | ||||
|       let p2 = elmt.p2 | ||||
|       if elmt.p1 == "?" { | ||||
|         linked.push("?" + elmt.p2) | ||||
|       } else { | ||||
|         linked.push(elmt.p1) | ||||
|         p1 = "?" + elmt.p2 | ||||
|       } | ||||
|       if elmt.p2 == "?" { | ||||
|         linked.push(elmt.p1 + "?") | ||||
|       } else { | ||||
|         linked.push(elmt.p2) | ||||
|         p2 = elmt.p1 + "?" | ||||
|       } | ||||
|       linked.push(p1) | ||||
|       linked.push(p2) | ||||
|       last-seq = ( | ||||
|         elmt: elmt, | ||||
|         i: i, | ||||
|         p1: p1, | ||||
|         p2: p2 | ||||
|       ) | ||||
|     } else if elmt.type == "note" { | ||||
|       elmt.insert("linked", elmt.pos == none and elmt.side != "across") | ||||
|       if elmt.pos == none and elmt.side != "across" { | ||||
|         let names = participants.map(p => p.name) | ||||
|         let i1 = names.position(n => n == last-seq.p1) | ||||
|         let i2 = names.position(n => n == last-seq.p2) | ||||
|         let pars = ((i1, last-seq.p1), (i2, last-seq.p2)).sorted(key: p => p.first()) | ||||
|         if elmt.side == "left" { | ||||
|           elmt.pos = pars.first().last() | ||||
|         } else if elmt.side == "right" { | ||||
|           elmt.pos = pars.last().last() | ||||
|         } | ||||
|  | ||||
|         let seq = last-seq.elmt | ||||
|         seq.insert("linked-note", elmt) | ||||
|         elmts.at(last-seq.i) = seq | ||||
|       } | ||||
|       elmts.at(i) = elmt | ||||
|       if elmt.side == "left" { | ||||
|         linked.push("[") | ||||
|       } else if elmt.side == "right" { | ||||
|         linked.push("]") | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -137,6 +167,7 @@ | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   set text(font: "Source Sans 3") | ||||
|   render(participants, elmts) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -3,4 +3,5 @@ | ||||
| #import "sequence.typ": _seq | ||||
| #import "group.typ": _grp | ||||
| #import "participant.typ": _par | ||||
| #import "separator.typ": _sep | ||||
| #import "separator.typ": _sep | ||||
| #import "note.typ": _note | ||||
							
								
								
									
										171
									
								
								src/note.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								src/note.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,171 @@ | ||||
| #import "@preview/cetz:0.2.2": draw | ||||
| #import "consts.typ": * | ||||
|  | ||||
| #let SIDES = ( | ||||
|   "left", | ||||
|   "right", | ||||
|   "over", | ||||
|   "across" | ||||
| ) | ||||
|  | ||||
| #let SHAPES = ( | ||||
|   "default", | ||||
|   "rect", | ||||
|   "hex" | ||||
| ) | ||||
|  | ||||
| #let _note(side, content, pos: none, color: COL-NOTE, shape: "default", aligned: false) = { | ||||
|   return (( | ||||
|     type: "note", | ||||
|     side: side, | ||||
|     content: content, | ||||
|     pos: pos, | ||||
|     color: color, | ||||
|     shape: shape, | ||||
|     aligned: aligned | ||||
|   ),) | ||||
| } | ||||
|  | ||||
| #let get-note-box(note) = { | ||||
|   let PAD = if note.shape == "hex" {NOTE-HEX-PAD} else {NOTE-PAD} | ||||
|   let inset = ( | ||||
|     left: PAD.last() * 1pt, | ||||
|     right: PAD.last() * 1pt, | ||||
|     top: PAD.first() * 1pt, | ||||
|     bottom: PAD.first() * 1pt, | ||||
|   ) | ||||
|   if note.shape == "default" { | ||||
|     inset.right += NOTE-CORNER-SIZE * 1pt | ||||
|   } | ||||
|   if note.side == "left" { | ||||
|     inset.right += NOTE-GAP * 1pt | ||||
|   } else if note.side == "right" { | ||||
|     inset.left += NOTE-GAP * 1pt | ||||
|   } | ||||
|   return box(note.content, inset: inset) | ||||
| } | ||||
|  | ||||
| #let get-size(note) = { | ||||
|   let PAD = if note.shape == "hex" {NOTE-HEX-PAD} else {NOTE-PAD} | ||||
|   let m = measure(box(note.content)) | ||||
|   let w = m.width / 1pt + PAD.last() * 2 | ||||
|   let h = m.height / 1pt + PAD.first() * 2 | ||||
|   if note.shape == "default" { | ||||
|     w += NOTE-CORNER-SIZE | ||||
|   } | ||||
|   return ( | ||||
|     width: w, | ||||
|     height: h | ||||
|   ) | ||||
| } | ||||
|  | ||||
| #let _get-base-x(pars-i, x-pos, note) = { | ||||
|   if note.side == "across" { | ||||
|     return (x-pos.first() + x-pos.last()) / 2 | ||||
|   } | ||||
|   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 x-pos.at(pars-i.at(note.pos)) | ||||
| } | ||||
|  | ||||
| #let render(pars-i, x-pos, note, y, lifelines) = { | ||||
|   let shapes = () | ||||
|   let PAD = if note.shape == "hex" {NOTE-HEX-PAD} else {NOTE-PAD} | ||||
|   let m = measure(box(note.content)) | ||||
|   let w = m.width / 1pt + PAD.last() * 2 | ||||
|   let h = m.height / 1pt + PAD.first() * 2 | ||||
|   let total-w = w | ||||
|   if note.shape == "default" { | ||||
|     total-w += NOTE-CORNER-SIZE | ||||
|   } | ||||
|  | ||||
|   let base-x = _get-base-x(pars-i, x-pos, note) | ||||
|  | ||||
|   let i = none | ||||
|   if note.pos != none and type(note.pos) == str { | ||||
|     i = pars-i.at(note.pos) | ||||
|   } | ||||
|   let x0 = base-x | ||||
|   if note.side == "left" { | ||||
|     x0 -= NOTE-GAP | ||||
|     x0 -= total-w | ||||
|     if lifelines.at(i).level != 0 { | ||||
|       x0 -= LIFELINE-W / 2 | ||||
|     } | ||||
|   } else if note.side == "right" { | ||||
|     x0 += NOTE-GAP | ||||
|     x0 -= lifelines.at(i).level * LIFELINE-W / 2 | ||||
|   } else if note.side == "over" or note.side == "across" { | ||||
|     x0 -= total-w / 2 | ||||
|   } | ||||
|  | ||||
|   let x1 = x0 + w | ||||
|   let x2 = x0 + total-w | ||||
|   let y0 = y | ||||
|  | ||||
|   if note.linked { | ||||
|     y0 += h / 2 | ||||
|   } | ||||
|   let y1 = y0 - h | ||||
|  | ||||
|   if note.shape == "default" { | ||||
|     shapes += draw.merge-path( | ||||
|       stroke: black + .5pt, | ||||
|       fill: note.color, | ||||
|       close: true, | ||||
|       { | ||||
|         draw.line( | ||||
|           (x0, y0), | ||||
|           (x1, y0), | ||||
|           (x2, y0 - NOTE-CORNER-SIZE), | ||||
|           (x2, y1), | ||||
|           (x0, y1) | ||||
|         ) | ||||
|       } | ||||
|     ) | ||||
|     shapes += draw.line((x1, y0), (x1, y0 - NOTE-CORNER-SIZE), (x2, y0 - NOTE-CORNER-SIZE), stroke: black + .5pt) | ||||
|   } else if note.shape == "rect" { | ||||
|     shapes += draw.rect( | ||||
|       (x0, y0), | ||||
|       (x2, y1), | ||||
|       stroke: black + .5pt, | ||||
|       fill: note.color | ||||
|     ) | ||||
|   } else if note.shape == "hex" { | ||||
|     let lx = x0 + PAD.last() | ||||
|     let rx = x2 - PAD.last() | ||||
|     let my = (y0 + y1) / 2 | ||||
|     shapes += draw.merge-path( | ||||
|       stroke: black + .5pt, | ||||
|       fill: note.color, | ||||
|       close: true, | ||||
|       { | ||||
|         draw.line( | ||||
|           (lx, y0), | ||||
|           (rx, y0), | ||||
|           (x2, my), | ||||
|           (rx, y1), | ||||
|           (lx, y1), | ||||
|           (x0, my), | ||||
|         ) | ||||
|       } | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   shapes += draw.content( | ||||
|     ((x0 + x1)/2, (y0 + y1)/2), | ||||
|     note.content, | ||||
|     anchor: "center" | ||||
|   ) | ||||
|  | ||||
|   if note.pos != none or note.side == "across" { | ||||
|     y -= h | ||||
|   } | ||||
|  | ||||
|   let r = (y, shapes) | ||||
|   return r | ||||
| } | ||||
| @@ -6,6 +6,7 @@ | ||||
| #import "sequence.typ" | ||||
| #import "separator.typ" | ||||
| #import "consts.typ": * | ||||
| #import "note.typ" as note: get-note-box | ||||
|  | ||||
| #let DEBUG-INVISIBLE = false | ||||
|  | ||||
| @@ -61,6 +62,29 @@ | ||||
|         par.max-lifelines = calc.max(par.max-lifelines, par.lifeline-lvl) | ||||
|       } | ||||
|       participants.at(i) = par | ||||
|      | ||||
|     } else if elmt.type == "note" { | ||||
|       let (p1, p2) = (none, none) | ||||
|       if elmt.side == "left" { | ||||
|         p1 = "[" | ||||
|         p2 = elmt.pos | ||||
|       } else if elmt.side == "right" { | ||||
|         p1 = elmt.pos | ||||
|         p2 = "]" | ||||
|       } | ||||
|  | ||||
|       if p1 != none and p2 != 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) | ||||
|           ) | ||||
|         ) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -143,6 +167,7 @@ | ||||
|   let draw-group = group.render.with() | ||||
|   let draw-sep = separator.render.with(x-pos) | ||||
|   let draw-par = participant.render.with(x-pos) | ||||
|   let draw-note = note.render.with(pars-i, x-pos) | ||||
|    | ||||
|   // Draw participants (start) | ||||
|   for p in participants { | ||||
| @@ -223,6 +248,15 @@ | ||||
|         line.lines.push(("create", y)) | ||||
|       } | ||||
|       lifelines.at(i) = line | ||||
|      | ||||
|     // Note | ||||
|     } else if elmt.type == "note" { | ||||
|       if not elmt.linked { | ||||
|         y -= Y-SPACE | ||||
|         let shps | ||||
|         (y, shps) = draw-note(elmt, y, lifelines) | ||||
|         shapes += shps | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| #import "@preview/cetz:0.2.2": draw | ||||
| #import "consts.typ": * | ||||
| #import "participant.typ" | ||||
| #import "note.typ" | ||||
|  | ||||
| #let get-arrow-marks(sym, color) = { | ||||
|   if type(sym) == array { | ||||
| @@ -61,10 +62,15 @@ | ||||
|  | ||||
|   y -= Y-SPACE | ||||
|  | ||||
|   let h = 0 | ||||
|   // Reserve space for comment | ||||
|   if elmt.comment != none { | ||||
|     y -= measure(box(elmt.comment)).height / 1pt + 6 | ||||
|     h = calc.max(h, measure(box(elmt.comment)).height / 1pt + 6) | ||||
|   } | ||||
|   if "linked-note" in elmt { | ||||
|     h = calc.max(h, note.get-size(elmt.linked-note).height / 2) | ||||
|   } | ||||
|   y -= h | ||||
|  | ||||
|   let i1 = pars-i.at(elmt.p1) | ||||
|   let i2 = pars-i.at(elmt.p2) | ||||
| @@ -148,6 +154,12 @@ | ||||
|     ) | ||||
|   ) | ||||
|  | ||||
|   let y0 = y | ||||
|   if "linked-note" in elmt { | ||||
|     let shps = note.render(pars-i, x-pos, elmt.linked-note, y, lifelines).last() | ||||
|     shapes += shps | ||||
|   } | ||||
|  | ||||
|   if elmt.p1 == elmt.p2 { | ||||
|     if elmt.flip { | ||||
|       x1 = start-info.lx | ||||
| @@ -210,6 +222,11 @@ | ||||
|     lifelines.at(i2) = dst-line | ||||
|   } | ||||
|  | ||||
|   if "linked-note" in elmt { | ||||
|     let m = note.get-size(elmt.linked-note) | ||||
|     y = calc.min(y, y0 - m.height / 2) | ||||
|   } | ||||
|  | ||||
|   let r = (y, lifelines, shapes) | ||||
|   return r | ||||
| } | ||||
		Reference in New Issue
	
	Block a user