forked from HEL/chronos
		
	restructured code in separated files
This commit is contained in:
		
										
											Binary file not shown.
										
									
								
							| @@ -10,7 +10,7 @@ Alice <-- Bob: Another authentication Response | |||||||
|  |  | ||||||
|  |  | ||||||
| #chronos.diagram({ | #chronos.diagram({ | ||||||
|   import "/src/diagram.typ": * |   import chronos: * | ||||||
|   _seq("Alice", "Bob", comment: "Authentication Request") |   _seq("Alice", "Bob", comment: "Authentication Request") | ||||||
|   _seq("Bob", "Alice", comment: "Authentication Response", dashed: true) |   _seq("Bob", "Alice", comment: "Authentication Response", dashed: true) | ||||||
|    |    | ||||||
| @@ -19,19 +19,19 @@ Alice <-- Bob: Another authentication Response | |||||||
| }) | }) | ||||||
|  |  | ||||||
| #chronos.diagram({ | #chronos.diagram({ | ||||||
|   import "/src/diagram.typ": * |   import chronos: * | ||||||
|   _seq("Bob", "Alice", comment: "bonjour", color: red) |   _seq("Bob", "Alice", comment: "bonjour", color: red) | ||||||
|   _seq("Alice", "Bob", comment: "ok", color: blue) |   _seq("Alice", "Bob", comment: "ok", color: blue) | ||||||
| }) | }) | ||||||
|  |  | ||||||
| #chronos.diagram({ | #chronos.diagram({ | ||||||
|   import "/src/diagram.typ": * |   import chronos: * | ||||||
|   _seq("Alice", "Bob", comment: "This is a test") |   _seq("Alice", "Bob", comment: "This is a test") | ||||||
|   _seq("Alice", "Callum", comment: "This is another test with a long text") |   _seq("Alice", "Callum", comment: "This is another test with a long text") | ||||||
| }) | }) | ||||||
|  |  | ||||||
| #chronos.diagram({ | #chronos.diagram({ | ||||||
|   import "/src/diagram.typ": * |   import chronos: * | ||||||
|   _seq("Alice", "Bob", comment: "Authentication Request") |   _seq("Alice", "Bob", comment: "Authentication Request") | ||||||
|   _seq("Bob", "Alice", comment: "Authentication Failure") |   _seq("Bob", "Alice", comment: "Authentication Failure") | ||||||
|  |  | ||||||
| @@ -45,7 +45,7 @@ Alice <-- Bob: Another authentication Response | |||||||
| }) | }) | ||||||
|  |  | ||||||
| #chronos.diagram({ | #chronos.diagram({ | ||||||
|   import "/src/diagram.typ": * |   import chronos: * | ||||||
|   _sep("Initialization") |   _sep("Initialization") | ||||||
|   _seq("Alice", "Bob", comment: "Authentication Request") |   _seq("Alice", "Bob", comment: "Authentication Request") | ||||||
|   _seq("Bob", "Alice", comment: "Authentication Response", dashed: true) |   _seq("Bob", "Alice", comment: "Authentication Response", dashed: true) | ||||||
| @@ -56,7 +56,7 @@ Alice <-- Bob: Another authentication Response | |||||||
| }) | }) | ||||||
|  |  | ||||||
| #chronos.diagram({ | #chronos.diagram({ | ||||||
|   import "/src/diagram.typ": * |   import chronos: * | ||||||
|   _seq("Alice", "Bob", comment: "message 1") |   _seq("Alice", "Bob", comment: "message 1") | ||||||
|   _seq("Bob", "Alice", comment: "ok", dashed: true) |   _seq("Bob", "Alice", comment: "ok", dashed: true) | ||||||
|   _gap() |   _gap() | ||||||
| @@ -68,7 +68,7 @@ Alice <-- Bob: Another authentication Response | |||||||
| }) | }) | ||||||
|  |  | ||||||
| #chronos.diagram({ | #chronos.diagram({ | ||||||
|   import "/src/diagram.typ": * |   import chronos: * | ||||||
|   _seq("Alice", "Alice", comment: "On the\nright") |   _seq("Alice", "Alice", comment: "On the\nright") | ||||||
|   _seq("Alice", "Alice", flip: true, comment: "On the\nleft") |   _seq("Alice", "Alice", flip: true, comment: "On the\nleft") | ||||||
| }) | }) | ||||||
										
											Binary file not shown.
										
									
								
							| @@ -2,7 +2,7 @@ | |||||||
|  |  | ||||||
|  |  | ||||||
| #chronos.diagram({ | #chronos.diagram({ | ||||||
|   import "/src/diagram.typ": * |   import chronos: * | ||||||
|   _seq("User", "A", comment: "DoWork", enable-dst: true) |   _seq("User", "A", comment: "DoWork", enable-dst: true) | ||||||
|   _seq("A", "B", comment: [#sym.quote.angle.l createRequest #sym.quote.angle.r], enable-dst: true) |   _seq("A", "B", comment: [#sym.quote.angle.l createRequest #sym.quote.angle.r], enable-dst: true) | ||||||
|   _seq("B", "C", comment: "DoWork", enable-dst: true) |   _seq("B", "C", comment: "DoWork", enable-dst: true) | ||||||
| @@ -12,7 +12,7 @@ | |||||||
| }) | }) | ||||||
|  |  | ||||||
| #chronos.diagram({ | #chronos.diagram({ | ||||||
|   import "/src/diagram.typ": * |   import chronos: * | ||||||
|   _seq("User", "A", comment: "DoWork", enable-dst: true) |   _seq("User", "A", comment: "DoWork", enable-dst: true) | ||||||
|   _seq("A", "A", comment: "Internal call", enable-dst: true) |   _seq("A", "A", comment: "Internal call", enable-dst: true) | ||||||
|   _seq("A", "B", comment: [#sym.quote.angle.l createRequest #sym.quote.angle.r], enable-dst: true) |   _seq("A", "B", comment: [#sym.quote.angle.l createRequest #sym.quote.angle.r], enable-dst: true) | ||||||
| @@ -21,7 +21,7 @@ | |||||||
| }) | }) | ||||||
|  |  | ||||||
| #chronos.diagram({ | #chronos.diagram({ | ||||||
|   import "/src/diagram.typ": * |   import chronos: * | ||||||
|   _seq("alice", "bob", comment: "hello", enable-dst: true) |   _seq("alice", "bob", comment: "hello", enable-dst: true) | ||||||
|   _seq("bob", "bob", comment: "self call", enable-dst: true) |   _seq("bob", "bob", comment: "self call", enable-dst: true) | ||||||
|   _seq("bill", "bob", comment: "hello from thread 2", enable-dst: true) |   _seq("bill", "bob", comment: "hello from thread 2", enable-dst: true) | ||||||
| @@ -31,3 +31,10 @@ | |||||||
|   _seq("bob", "george", comment: "delete", destroy-dst: true) |   _seq("bob", "george", comment: "delete", destroy-dst: true) | ||||||
|   _seq("bob", "alice", comment: "success", disable-src: true, dashed: true) |   _seq("bob", "alice", comment: "success", disable-src: true, dashed: true) | ||||||
| }) | }) | ||||||
|  |  | ||||||
|  | #chronos.diagram({ | ||||||
|  |   import chronos: * | ||||||
|  |   _seq("alice", "bob", comment: "hello1", enable-dst: true) | ||||||
|  |   _seq("bob", "charlie", comment: "hello2", enable-dst: true, disable-src: true) | ||||||
|  |   _seq("charlie", "alice", comment: "ok", dashed: true, disable-src: true) | ||||||
|  | }) | ||||||
							
								
								
									
										5
									
								
								src/consts.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/consts.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | #let Y-SPACE = 10 | ||||||
|  | #let PAR-PAD = (5pt, 3pt) | ||||||
|  | #let PAR-SPACE = 10 | ||||||
|  | #let COMMENT-PAD = 8 | ||||||
|  | #let LIFELINE-W = 10 | ||||||
| @@ -1,71 +1,6 @@ | |||||||
| #import "utils.typ": get-group-span | #import "utils.typ": get-group-span | ||||||
| #import "renderer.typ": render | #import "renderer.typ": render | ||||||
|  | #import "participant.typ" as participant: _par | ||||||
| #let _seq( |  | ||||||
|   p1, |  | ||||||
|   p2, |  | ||||||
|   comment: none, |  | ||||||
|   dashed: false, |  | ||||||
|   tip: "default", |  | ||||||
|   color: black, |  | ||||||
|   flip: false, |  | ||||||
|   enable-dst: false, |  | ||||||
|   disable-dst: false, |  | ||||||
|   destroy-dst: false, |  | ||||||
|   disable-src: false, |  | ||||||
|   destroy-src: false, |  | ||||||
| ) = { |  | ||||||
|   return (( |  | ||||||
|     type: "seq", |  | ||||||
|     p1: p1, |  | ||||||
|     p2: p2, |  | ||||||
|     comment: comment, |  | ||||||
|     dashed: dashed, |  | ||||||
|     tip: tip, |  | ||||||
|     color: color, |  | ||||||
|     flip: flip, |  | ||||||
|     enable-dst: enable-dst, |  | ||||||
|     disable-dst: disable-dst, |  | ||||||
|     destroy-dst: destroy-dst, |  | ||||||
|     disable-src: disable-src, |  | ||||||
|     destroy-src: destroy-src, |  | ||||||
|   ),) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let _par(name, display-name: auto, start-at: 0) = { |  | ||||||
|   return (( |  | ||||||
|     type: "par", |  | ||||||
|     name: name, |  | ||||||
|     display-name: if display-name == auto {name} else {display-name}, |  | ||||||
|     start-at: start-at |  | ||||||
|   ),) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let _par-exists(participants, name) = { |  | ||||||
|   for p in participants { |  | ||||||
|     if name == p.name { |  | ||||||
|       return true |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   return false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let _grp(name, desc: none, type: "default", elmts) = { |  | ||||||
|   return (( |  | ||||||
|     type: "grp", |  | ||||||
|     name: name, |  | ||||||
|     desc: desc, |  | ||||||
|     grp-type: type, |  | ||||||
|     elmts: elmts |  | ||||||
|   ),) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let _sep(name) = { |  | ||||||
|   return (( |  | ||||||
|     type: "sep", |  | ||||||
|     name: name |  | ||||||
|   ),) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let _gap(size: 20) = { | #let _gap(size: 20) = { | ||||||
|   return (( |   return (( | ||||||
| @@ -78,6 +13,8 @@ | |||||||
|   let participants = () |   let participants = () | ||||||
|   let elmts = elements |   let elmts = elements | ||||||
|   let i = 0 |   let i = 0 | ||||||
|  |  | ||||||
|  |   // Flatten groups | ||||||
|   while i < elmts.len() { |   while i < elmts.len() { | ||||||
|     let elmt = elmts.at(i) |     let elmt = elmts.at(i) | ||||||
|     if elmt.type == "grp" { |     if elmt.type == "grp" { | ||||||
| @@ -93,19 +30,21 @@ | |||||||
|     i += 1 |     i += 1 | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   // List participants | ||||||
|   for elmt in elmts { |   for elmt in elmts { | ||||||
|     if elmt.type == "par" { |     if elmt.type == "par" { | ||||||
|       participants.push(elmt) |       participants.push(elmt) | ||||||
|     } else if elmt.type == "seq" { |     } else if elmt.type == "seq" { | ||||||
|       if not _par-exists(participants, elmt.p1) { |       if not participant._exists(participants, elmt.p1) { | ||||||
|         participants.push(_par(elmt.p1).first()) |         participants.push(_par(elmt.p1).first()) | ||||||
|       } |       } | ||||||
|       if not _par-exists(participants, elmt.p2) { |       if not participant._exists(participants, elmt.p2) { | ||||||
|         participants.push(_par(elmt.p2).first()) |         participants.push(_par(elmt.p2).first()) | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   // Compute groups spans (horizontal) | ||||||
|   for (i, elmt) in elmts.enumerate() { |   for (i, elmt) in elmts.enumerate() { | ||||||
|     if elmt.type == "grp" { |     if elmt.type == "grp" { | ||||||
|       let (min-i, max-i) = get-group-span(participants, elmt) |       let (min-i, max-i) = get-group-span(participants, elmt) | ||||||
|   | |||||||
							
								
								
									
										52
									
								
								src/group.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/group.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | |||||||
|  | #import "@preview/cetz:0.2.2": draw | ||||||
|  |  | ||||||
|  | #let _grp(name, desc: none, type: "default", elmts) = { | ||||||
|  |   return (( | ||||||
|  |     type: "grp", | ||||||
|  |     name: name, | ||||||
|  |     desc: desc, | ||||||
|  |     grp-type: type, | ||||||
|  |     elmts: elmts | ||||||
|  |   ),) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #let render(x0, x1, y0, y1, group) = { | ||||||
|  |   let shapes = () | ||||||
|  |   let m = measure(box(group.name)) | ||||||
|  |   let w = m.width / 1pt + 15 | ||||||
|  |   let h = m.height / 1pt + 6 | ||||||
|  |   shapes += draw.rect( | ||||||
|  |     (x0, y0), | ||||||
|  |     (x1, y1) | ||||||
|  |   ) | ||||||
|  |   shapes += 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) | ||||||
|  |       ) | ||||||
|  |     } | ||||||
|  |   ) | ||||||
|  |   shapes += draw.content( | ||||||
|  |     (x0, y0), | ||||||
|  |     group.name, | ||||||
|  |     anchor: "north-west", | ||||||
|  |     padding: (left: 5pt, right: 10pt, top: 3pt, bottom: 3pt) | ||||||
|  |   ) | ||||||
|  |  | ||||||
|  |   if group.desc != none { | ||||||
|  |     shapes += draw.content( | ||||||
|  |       (x0 + w, y0), | ||||||
|  |       text([\[#group.desc\]], weight: "bold"), | ||||||
|  |       anchor: "north-west", | ||||||
|  |       padding: 3pt | ||||||
|  |     ) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return shapes | ||||||
|  | } | ||||||
| @@ -1 +1,6 @@ | |||||||
| #import "diagram.typ": diagram, from-plantuml | #import "diagram.typ": diagram, from-plantuml, _gap | ||||||
|  |  | ||||||
|  | #import "sequence.typ": _seq | ||||||
|  | #import "group.typ": _grp | ||||||
|  | #import "participant.typ": _par | ||||||
|  | #import "separator.typ": _sep | ||||||
							
								
								
									
										17
									
								
								src/participant.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/participant.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | #let _par(name, display-name: auto, start-at: 0) = { | ||||||
|  |   return (( | ||||||
|  |     type: "par", | ||||||
|  |     name: name, | ||||||
|  |     display-name: if display-name == auto {name} else {display-name}, | ||||||
|  |     start-at: start-at | ||||||
|  |   ),) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #let _exists(participants, name) = { | ||||||
|  |   for p in participants { | ||||||
|  |     if name == p.name { | ||||||
|  |       return true | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return false | ||||||
|  | } | ||||||
							
								
								
									
										238
									
								
								src/renderer.typ
									
									
									
									
									
								
							
							
						
						
									
										238
									
								
								src/renderer.typ
									
									
									
									
									
								
							| @@ -1,11 +1,9 @@ | |||||||
| #import "@preview/cetz:0.2.2": canvas, draw | #import "@preview/cetz:0.2.2": canvas, draw | ||||||
| #import "utils.typ": get-participants-i | #import "utils.typ": get-participants-i | ||||||
|  | #import "group.typ" | ||||||
| #let Y-SPACE = 10 | #import "sequence.typ" | ||||||
| #let PAR-PAD = (5pt, 3pt) | #import "separator.typ" | ||||||
| #let PAR-SPACE = 10 | #import "consts.typ": * | ||||||
| #let COMMENT-PAD = 8 |  | ||||||
| #let LIFELINE-W = 10 |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #let get-columns-width(participants, elements) = { | #let get-columns-width(participants, elements) = { | ||||||
| @@ -16,6 +14,8 @@ | |||||||
|   }) |   }) | ||||||
|   let pars-i = get-participants-i(participants) |   let pars-i = get-participants-i(participants) | ||||||
|   let cells = () |   let cells = () | ||||||
|  |  | ||||||
|  |   // Compute max lifeline levels | ||||||
|   for elmt in elements { |   for elmt in elements { | ||||||
|     if elmt.type == "seq" { |     if elmt.type == "seq" { | ||||||
|       let com = if elmt.comment == none {""} else {elmt.comment} |       let com = if elmt.comment == none {""} else {elmt.comment} | ||||||
| @@ -49,6 +49,8 @@ | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   // Compute column widths | ||||||
|  |   // Compute minimum widths for participant names | ||||||
|   let widths = () |   let widths = () | ||||||
|   for i in range(participants.len() - 1) { |   for i in range(participants.len() - 1) { | ||||||
|     let p1 = participants.at(i) |     let p1 = participants.at(i) | ||||||
| @@ -58,6 +60,7 @@ | |||||||
|     widths.push(w1 / 2pt + w2 / 2pt + PAR-SPACE) |     widths.push(w1 / 2pt + w2 / 2pt + PAR-SPACE) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   // Compute minimum width for simple sequences (spanning 1 column) | ||||||
|   for cell in cells.filter(c => c.i2 - c.i1 == 1) { |   for cell in cells.filter(c => c.i2 - c.i1 == 1) { | ||||||
|     let m = measure(cell.cell) |     let m = measure(cell.cell) | ||||||
|     widths.at(cell.i1) = calc.max( |     widths.at(cell.i1) = calc.max( | ||||||
| @@ -66,6 +69,7 @@ | |||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   // Compute remaining widths for longer sequences (spanning multiple columns) | ||||||
|   let multicol-cells = cells.filter(c => c.i2 - c.i1 > 1) |   let multicol-cells = cells.filter(c => c.i2 - c.i1 > 1) | ||||||
|   multicol-cells = multicol-cells.sorted(key: c => { |   multicol-cells = multicol-cells.sorted(key: c => { | ||||||
|     c.i1 * 1000 + c.i2 |     c.i1 * 1000 + c.i2 | ||||||
| @@ -77,6 +81,8 @@ | |||||||
|       m.width / 1pt - widths.slice(0, cell.i2 - 1).sum() |       m.width / 1pt - widths.slice(0, cell.i2 - 1).sum() | ||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   // Add lifeline widths | ||||||
|   for (i, w) in widths.enumerate() { |   for (i, w) in widths.enumerate() { | ||||||
|     let p1 = participants.at(i) |     let p1 = participants.at(i) | ||||||
|     let p2 = participants.at(i + 1) |     let p2 = participants.at(i + 1) | ||||||
| @@ -89,57 +95,21 @@ | |||||||
|   return widths |   return widths | ||||||
| } | } | ||||||
|  |  | ||||||
| #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 |  | ||||||
|     ) |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let render(participants, elements) = context canvas(length: 1pt, { | #let render(participants, elements) = context canvas(length: 1pt, { | ||||||
|  |   let shapes = () | ||||||
|   let pars-i = get-participants-i(participants) |   let pars-i = get-participants-i(participants) | ||||||
|  |  | ||||||
|   let widths = get-columns-width(participants, elements) |   let widths = get-columns-width(participants, elements) | ||||||
|  |  | ||||||
|  |   // Compute each column's X position | ||||||
|   let x-pos = (0,) |   let x-pos = (0,) | ||||||
|   for width in widths { |   for width in widths { | ||||||
|     x-pos.push(x-pos.last() + width) |     x-pos.push(x-pos.last() + width) | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   // Draw participants |   // Draw participants (start) | ||||||
|   for (i, p) in participants.enumerate() { |   for (i, p) in participants.enumerate() { | ||||||
|     draw.content( |     shapes += draw.content( | ||||||
|       (x-pos.at(i), 0), |       (x-pos.at(i), 0), | ||||||
|       p.display-name, |       p.display-name, | ||||||
|       name: p.name, |       name: p.name, | ||||||
| @@ -156,124 +126,23 @@ | |||||||
|     lines: () |     lines: () | ||||||
|   )) |   )) | ||||||
|  |  | ||||||
|   // Draw sequences |   let draw-seq = sequence.render.with(pars-i, x-pos) | ||||||
|  |   let draw-group = group.render.with() | ||||||
|  |   let draw-sep = separator.render.with(x-pos) | ||||||
|  |  | ||||||
|  |   // Draw elemnts | ||||||
|   for elmt in elements { |   for elmt in elements { | ||||||
|  |     // Sequences | ||||||
|     if elmt.type == "seq" { |     if elmt.type == "seq" { | ||||||
|       let i1 = pars-i.at(elmt.p1) |       let shps | ||||||
|       let i2 = pars-i.at(elmt.p2) |       (y, lifelines, shps) = draw-seq(elmt, y, lifelines) | ||||||
|  |       shapes += shps | ||||||
|       if elmt.comment != none { |  | ||||||
|         y -= measure(box(elmt.comment)).height / 1pt + 6 |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       if elmt.disable-src { |  | ||||||
|         let src-line = lifelines.at(i1) |  | ||||||
|         src-line.level -= 1 |  | ||||||
|         src-line.lines.push(("disable", y, auto)) |  | ||||||
|         lifelines.at(i1) = src-line |  | ||||||
|       } |  | ||||||
|       if elmt.destroy-src { |  | ||||||
|         let src-line = lifelines.at(i1) |  | ||||||
|         src-line.level -= 1 |  | ||||||
|         src-line.lines.push(("destroy", y, auto)) |  | ||||||
|         lifelines.at(i1) = src-line |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       let ll-lvl1 = lifelines.at(i1).level * LIFELINE-W / 2 |  | ||||||
|  |  | ||||||
|       if elmt.disable-dst { |  | ||||||
|         let dst-line = lifelines.at(i2) |  | ||||||
|         dst-line.level -= 1 |  | ||||||
|         dst-line.lines.push(("disable", y, auto)) |  | ||||||
|         lifelines.at(i2) = dst-line |  | ||||||
|       } |  | ||||||
|       if elmt.destroy-dst { |  | ||||||
|         let dst-line = lifelines.at(i2) |  | ||||||
|         dst-line.level -= 1 |  | ||||||
|         dst-line.lines.push(("destroy", y, auto)) |  | ||||||
|         lifelines.at(i2) = dst-line |  | ||||||
|       } |  | ||||||
|       if elmt.enable-dst { |  | ||||||
|         let dst-line = lifelines.at(i2) |  | ||||||
|         dst-line.level += 1 |  | ||||||
|         lifelines.at(i2) = dst-line |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       let x1 = x-pos.at(i1) |  | ||||||
|       let x2 = x-pos.at(i2) |  | ||||||
|  |  | ||||||
|       let ll-lvl2 = lifelines.at(i2).level * LIFELINE-W / 2 |  | ||||||
|  |  | ||||||
|       let f = if elmt.flip {-1} else {1} |  | ||||||
|       if i1 <= i2 { |  | ||||||
|         x1 += ll-lvl1 * f |  | ||||||
|         x2 -= ll-lvl2 * f |  | ||||||
|       } else { |  | ||||||
|         x1 -= ll-lvl1 * f |  | ||||||
|         x2 += ll-lvl2 * f |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       let style = ( |  | ||||||
|         mark: (end: "straight"), |  | ||||||
|         stroke: ( |  | ||||||
|           dash: if elmt.dashed {"dashed"} else {"solid"}, |  | ||||||
|           paint: elmt.color |  | ||||||
|         ) |  | ||||||
|       ) |  | ||||||
|  |  | ||||||
|       if elmt.p1 == elmt.p2 { |  | ||||||
|         let x3 = x1 - ll-lvl1 + ll-lvl2 |  | ||||||
|  |  | ||||||
|         x2 = if elmt.flip {x1 - 20} else {x1 + 20} |  | ||||||
|  |  | ||||||
|         if elmt.comment != none { |  | ||||||
|           draw.content( |  | ||||||
|             (x1, y), |  | ||||||
|             elmt.comment, |  | ||||||
|             anchor: if elmt.flip {"south-east"} else {"south-west"}, |  | ||||||
|             padding: 3pt |  | ||||||
|           ) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         draw.line( |  | ||||||
|           (x1, y), |  | ||||||
|           (x2, y), |  | ||||||
|           (x2, y - 10), |  | ||||||
|           (x3, y - 10), |  | ||||||
|           ..style |  | ||||||
|         ) |  | ||||||
|         y -= 10 |  | ||||||
|  |  | ||||||
|       } else { |  | ||||||
|         if elmt.comment != none { |  | ||||||
|           let x = calc.min(x1, x2) |  | ||||||
|           if x2 < x1 { |  | ||||||
|             x += COMMENT-PAD |  | ||||||
|           } |  | ||||||
|           draw.content( |  | ||||||
|             (x, y), |  | ||||||
|             elmt.comment, |  | ||||||
|             anchor: "south-west", |  | ||||||
|             padding: 3pt |  | ||||||
|           ) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         draw.line( |  | ||||||
|           (x1, y), |  | ||||||
|           (x2, y), |  | ||||||
|           ..style |  | ||||||
|         ) |  | ||||||
|       } |  | ||||||
|       if elmt.enable-dst { |  | ||||||
|         let dst-line = lifelines.at(i2) |  | ||||||
|         dst-line.lines.push(("enable", y, auto)) |  | ||||||
|         lifelines.at(i2) = dst-line |  | ||||||
|       } |  | ||||||
|       y -= Y-SPACE |  | ||||||
|  |  | ||||||
|  |     // Groups (start) -> reserve space for labels + store position | ||||||
|     } else if elmt.type == "grp" { |     } else if elmt.type == "grp" { | ||||||
|       let m = measure( |       let m = measure( | ||||||
|         box( |         box( | ||||||
|  |           elmt.name, | ||||||
|           inset: (left: 5pt, right: 5pt, top: 3pt, bottom: 3pt), |           inset: (left: 5pt, right: 5pt, top: 3pt, bottom: 3pt), | ||||||
|         ) |         ) | ||||||
|       ) |       ) | ||||||
| @@ -285,53 +154,33 @@ | |||||||
|       groups.push((y, elmt, 0, 0)) |       groups.push((y, elmt, 0, 0)) | ||||||
|       y -= m.height / 1pt + Y-SPACE |       y -= m.height / 1pt + Y-SPACE | ||||||
|      |      | ||||||
|  |     // Groups (end) -> actual drawing | ||||||
|     } else if elmt.type == "grp-end" { |     } else if elmt.type == "grp-end" { | ||||||
|       let (start-y, group, start-lvl, end-lvl) = groups.pop() |       let (start-y, group, start-lvl, end-lvl) = groups.pop() | ||||||
|       let x0 = x-pos.at(group.min-i) - start-lvl * 10 - 20 |       let x0 = x-pos.at(group.min-i) - start-lvl * 10 - 20 | ||||||
|       let x1 = x-pos.at(group.max-i) + end-lvl * 10 + 20 |       let x1 = x-pos.at(group.max-i) + end-lvl * 10 + 20 | ||||||
|       draw-group(x0, x1, start-y, y, group) |       shapes += draw-group(x0, x1, start-y, y, group) | ||||||
|  |  | ||||||
|       y -= Y-SPACE |       y -= Y-SPACE | ||||||
|  |  | ||||||
|  |     // Separator | ||||||
|     } else if elmt.type == "sep" { |     } else if elmt.type == "sep" { | ||||||
|       let x0 = x-pos.first() - 20 |       let shps | ||||||
|       let x1 = x-pos.last() + 20 |       (y, shps) = draw-sep(elmt, y) | ||||||
|       let m = measure( |       shapes += shps | ||||||
|         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 |     // Gap | ||||||
|       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 |  | ||||||
|     } else if elmt.type == "gap" { |     } else if elmt.type == "gap" { | ||||||
|       y -= elmt.size |       y -= elmt.size | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Draw vertical lines + end participants |   // Draw vertical lines + lifelines + end participants | ||||||
|   draw.on-layer(-1, { |   shapes += draw.on-layer(-1, { | ||||||
|     for (i, p) in participants.enumerate() { |     for (i, p) in participants.enumerate() { | ||||||
|       let x = x-pos.at(i) |       let x = x-pos.at(i) | ||||||
|  |  | ||||||
|  |       // Draw vertical line | ||||||
|       draw.line( |       draw.line( | ||||||
|         (x, 0), |         (x, 0), | ||||||
|         (x, y), |         (x, y), | ||||||
| @@ -341,10 +190,13 @@ | |||||||
|       let rects = () |       let rects = () | ||||||
|       let destructions = () |       let destructions = () | ||||||
|       let lines = () |       let lines = () | ||||||
|  |  | ||||||
|  |       // Compute lifeline rectangles + destruction positions | ||||||
|       for line in lifelines.at(i).lines { |       for line in lifelines.at(i).lines { | ||||||
|         let event = line.first() |         let event = line.first() | ||||||
|         if event == "enable" { |         if event == "enable" { | ||||||
|           lines.push(line) |           lines.push(line) | ||||||
|  |          | ||||||
|         } else if event == "disable" or event == "destroy" { |         } else if event == "disable" or event == "destroy" { | ||||||
|           let l = lines.pop() |           let l = lines.pop() | ||||||
|           let lvl = lines.len() |           let lvl = lines.len() | ||||||
| @@ -355,19 +207,23 @@ | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |  | ||||||
|  |       // Draw lifeline rectangles (reverse for bottom to top) | ||||||
|       for rect in rects.rev() { |       for rect in rects.rev() { | ||||||
|         let (cx, y0, y1) = rect |         let (cx, y0, y1) = rect | ||||||
|         draw.rect( |         draw.rect( | ||||||
|           (cx - LIFELINE-W / 2, y0), |           (cx - LIFELINE-W / 2, y0), | ||||||
|           (cx + LIFELINE-W / 2, y1), |           (cx + LIFELINE-W / 2, y1) | ||||||
|         ) |         ) | ||||||
|       } |       } | ||||||
|  |  | ||||||
|  |       // Draw lifeline destructions | ||||||
|       for dest in destructions { |       for dest in destructions { | ||||||
|         let (cx, cy) = dest |         let (cx, cy) = dest | ||||||
|         draw.line((cx - 8, cy - 8), (cx + 8, cy + 8), stroke: red + 2pt) |         draw.line((cx - 8, cy - 8), (cx + 8, cy + 8), stroke: red + 2pt) | ||||||
|         draw.line((cx - 8, cy + 8), (cx + 8, cy - 8), stroke: red + 2pt) |         draw.line((cx - 8, cy + 8), (cx + 8, cy - 8), stroke: red + 2pt) | ||||||
|       } |       } | ||||||
|  |  | ||||||
|  |       // Draw participants (end) | ||||||
|       draw.content( |       draw.content( | ||||||
|         (x, y), |         (x, y), | ||||||
|         p.display-name, |         p.display-name, | ||||||
| @@ -378,4 +234,6 @@ | |||||||
|       ) |       ) | ||||||
|     } |     } | ||||||
|   }) |   }) | ||||||
|  |  | ||||||
|  |   shapes | ||||||
| }) | }) | ||||||
							
								
								
									
										46
									
								
								src/separator.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/separator.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | #import "@preview/cetz:0.2.2": draw | ||||||
|  | #import "consts.typ": * | ||||||
|  |  | ||||||
|  | #let _sep(name) = { | ||||||
|  |   return (( | ||||||
|  |     type: "sep", | ||||||
|  |     name: name | ||||||
|  |   ),) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #let render(x-pos, elmt, y) = { | ||||||
|  |   let shapes = () | ||||||
|  |  | ||||||
|  |   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 | ||||||
|  |   shapes += draw.line((x0, y), (xl, y)) | ||||||
|  |   shapes += draw.line((xr, y), (x1, y)) | ||||||
|  |   y -= 3 | ||||||
|  |   shapes += draw.line((x0, y), (xl, y)) | ||||||
|  |   shapes += draw.line((xr, y), (x1, y)) | ||||||
|  |   shapes += draw.content( | ||||||
|  |     ((x0 + x1) / 2, y + 1.5), | ||||||
|  |     elmt.name, | ||||||
|  |     anchor: "center", | ||||||
|  |     padding: (5pt, 3pt), | ||||||
|  |     frame: "rect" | ||||||
|  |   ) | ||||||
|  |   y -= h / 2 | ||||||
|  |   y -= Y-SPACE | ||||||
|  |  | ||||||
|  |   let r = (y, shapes) | ||||||
|  |   return r | ||||||
|  | } | ||||||
							
								
								
									
										152
									
								
								src/sequence.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								src/sequence.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | |||||||
|  | #import "consts.typ": * | ||||||
|  | #import "@preview/cetz:0.2.2": draw | ||||||
|  |  | ||||||
|  | #let _seq( | ||||||
|  |   p1, | ||||||
|  |   p2, | ||||||
|  |   comment: none, | ||||||
|  |   dashed: false, | ||||||
|  |   tip: "default", | ||||||
|  |   color: black, | ||||||
|  |   flip: false, | ||||||
|  |   enable-dst: false, | ||||||
|  |   disable-dst: false, | ||||||
|  |   destroy-dst: false, | ||||||
|  |   disable-src: false, | ||||||
|  |   destroy-src: false, | ||||||
|  | ) = { | ||||||
|  |   return (( | ||||||
|  |     type: "seq", | ||||||
|  |     p1: p1, | ||||||
|  |     p2: p2, | ||||||
|  |     comment: comment, | ||||||
|  |     dashed: dashed, | ||||||
|  |     tip: tip, | ||||||
|  |     color: color, | ||||||
|  |     flip: flip, | ||||||
|  |     enable-dst: enable-dst, | ||||||
|  |     disable-dst: disable-dst, | ||||||
|  |     destroy-dst: destroy-dst, | ||||||
|  |     disable-src: disable-src, | ||||||
|  |     destroy-src: destroy-src, | ||||||
|  |   ),) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #let render(pars-i, x-pos, elmt, y, lifelines) = { | ||||||
|  |   let shapes = () | ||||||
|  |  | ||||||
|  |   let i1 = pars-i.at(elmt.p1) | ||||||
|  |   let i2 = pars-i.at(elmt.p2) | ||||||
|  |  | ||||||
|  |   if elmt.comment != none { | ||||||
|  |     y -= measure(box(elmt.comment)).height / 1pt + 6 | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if elmt.disable-src { | ||||||
|  |     let src-line = lifelines.at(i1) | ||||||
|  |     src-line.level -= 1 | ||||||
|  |     src-line.lines.push(("disable", y, auto)) | ||||||
|  |     lifelines.at(i1) = src-line | ||||||
|  |   } | ||||||
|  |   if elmt.destroy-src { | ||||||
|  |     let src-line = lifelines.at(i1) | ||||||
|  |     src-line.level -= 1 | ||||||
|  |     src-line.lines.push(("destroy", y, auto)) | ||||||
|  |     lifelines.at(i1) = src-line | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   let ll-lvl1 = lifelines.at(i1).level * LIFELINE-W / 2 | ||||||
|  |  | ||||||
|  |   if elmt.disable-dst { | ||||||
|  |     let dst-line = lifelines.at(i2) | ||||||
|  |     dst-line.level -= 1 | ||||||
|  |     dst-line.lines.push(("disable", y, auto)) | ||||||
|  |     lifelines.at(i2) = dst-line | ||||||
|  |   } | ||||||
|  |   if elmt.destroy-dst { | ||||||
|  |     let dst-line = lifelines.at(i2) | ||||||
|  |     dst-line.level -= 1 | ||||||
|  |     dst-line.lines.push(("destroy", y, auto)) | ||||||
|  |     lifelines.at(i2) = dst-line | ||||||
|  |   } | ||||||
|  |   if elmt.enable-dst { | ||||||
|  |     let dst-line = lifelines.at(i2) | ||||||
|  |     dst-line.level += 1 | ||||||
|  |     lifelines.at(i2) = dst-line | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   let x1 = x-pos.at(i1) | ||||||
|  |   let x2 = x-pos.at(i2) | ||||||
|  |  | ||||||
|  |   let ll-lvl2 = lifelines.at(i2).level * LIFELINE-W / 2 | ||||||
|  |  | ||||||
|  |   let f = if elmt.flip {-1} else {1} | ||||||
|  |   if i1 <= i2 { | ||||||
|  |     x1 += ll-lvl1 * f | ||||||
|  |     x2 -= ll-lvl2 * f | ||||||
|  |   } else { | ||||||
|  |     x1 -= ll-lvl1 * f | ||||||
|  |     x2 += ll-lvl2 * f | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   let style = ( | ||||||
|  |     mark: (end: "straight"), | ||||||
|  |     stroke: ( | ||||||
|  |       dash: if elmt.dashed {"dashed"} else {"solid"}, | ||||||
|  |       paint: elmt.color | ||||||
|  |     ) | ||||||
|  |   ) | ||||||
|  |  | ||||||
|  |   if elmt.p1 == elmt.p2 { | ||||||
|  |     let x3 = x1 - ll-lvl1 + ll-lvl2 | ||||||
|  |  | ||||||
|  |     x2 = if elmt.flip {x1 - 20} else {x1 + 20} | ||||||
|  |  | ||||||
|  |     if elmt.comment != none { | ||||||
|  |       shapes += draw.content( | ||||||
|  |         (x1, y), | ||||||
|  |         elmt.comment, | ||||||
|  |         anchor: if elmt.flip {"south-east"} else {"south-west"}, | ||||||
|  |         padding: 3pt | ||||||
|  |       ) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     shapes += draw.line( | ||||||
|  |       (x1, y), | ||||||
|  |       (x2, y), | ||||||
|  |       (x2, y - 10), | ||||||
|  |       (x3, y - 10), | ||||||
|  |       ..style | ||||||
|  |     ) | ||||||
|  |     y -= 10 | ||||||
|  |  | ||||||
|  |   } else { | ||||||
|  |     if elmt.comment != none { | ||||||
|  |       let x = calc.min(x1, x2) | ||||||
|  |       if x2 < x1 { | ||||||
|  |         x += COMMENT-PAD | ||||||
|  |       } | ||||||
|  |       shapes += draw.content( | ||||||
|  |         (x, y), | ||||||
|  |         elmt.comment, | ||||||
|  |         anchor: "south-west", | ||||||
|  |         padding: 3pt | ||||||
|  |       ) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     shapes += draw.line( | ||||||
|  |       (x1, y), | ||||||
|  |       (x2, y), | ||||||
|  |       ..style | ||||||
|  |     ) | ||||||
|  |   } | ||||||
|  |   if elmt.enable-dst { | ||||||
|  |     let dst-line = lifelines.at(i2) | ||||||
|  |     dst-line.lines.push(("enable", y, auto)) | ||||||
|  |     lifelines.at(i2) = dst-line | ||||||
|  |   } | ||||||
|  |   y -= Y-SPACE | ||||||
|  |  | ||||||
|  |   let r = (y, lifelines, shapes) | ||||||
|  |   return r | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user