Compare commits
	
		
			7 Commits
		
	
	
		
			96fa33b055
			...
			dev
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 1000a3a19a | |||
| f3763cb1f7 | |||
| c19d507486 | |||
| fe01e63dd0 | |||
| d6c390f3c5 | |||
| bbc8bb0339 | |||
| f39e14654a | 
| @@ -21,10 +21,10 @@ | |||||||
|   _par("a", display-name: "Alice") |   _par("a", display-name: "Alice") | ||||||
|   _par("b", display-name: "Bob") |   _par("b", display-name: "Bob") | ||||||
|  |  | ||||||
|   _note("left", [This is displayed\ left of Alice.], pos: "a", fill: rgb("#00FFFF")) |   _note("left", [This is displayed\ left of Alice.], pos: "a", color: rgb("#00FFFF")) | ||||||
|   _note("right", [This is displayed right of Alice.], pos: "a") |   _note("right", [This is displayed right of Alice.], pos: "a") | ||||||
|   _note("over", [This is displayed over Alice.], pos: "a") |   _note("over", [This is displayed over Alice.], pos: "a") | ||||||
|   _note("over", [This is displayed\ over Bob and Alice.], pos: ("a", "b"), fill: rgb("#FFAAAA")) |   _note("over", [This is displayed\ over Bob and Alice.], pos: ("a", "b"), color: rgb("#FFAAAA")) | ||||||
|   _note("over", [This is yet another\ example of\ a long note.], pos: ("a", "b")) |   _note("over", [This is yet another\ example of\ a long note.], pos: ("a", "b")) | ||||||
| }) | }) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -27,10 +27,4 @@ | |||||||
| #let COL-DESTRUCTION = rgb("#A80238") | #let COL-DESTRUCTION = rgb("#A80238") | ||||||
| #let COL-GRP-NAME = rgb("#EEEEEE") | #let COL-GRP-NAME = rgb("#EEEEEE") | ||||||
| #let COL-SEP-NAME = rgb("#EEEEEE") | #let COL-SEP-NAME = rgb("#EEEEEE") | ||||||
| #let COL-NOTE = rgb("#FEFFDD") | #let COL-NOTE = rgb("#FEFFDD") | ||||||
|  |  | ||||||
| #let default-style = ( |  | ||||||
|   y-space: 10pt, |  | ||||||
|   fill: rgb("#EEEEEE"), |  | ||||||
|   stroke: black + 1pt, |  | ||||||
| ) |  | ||||||
| @@ -1,30 +1,13 @@ | |||||||
| #import "/src/cetz.typ": draw, styles | #import "/src/cetz.typ": draw | ||||||
|  |  | ||||||
| #import "/src/core/utils.typ": get-ctx, normalize-units, set-ctx | #import "/src/core/utils.typ": get-ctx, set-ctx | ||||||
|  |  | ||||||
| #let delay-default-style = ( |  | ||||||
|   stroke: ( |  | ||||||
|     dash: "loosely-dotted", |  | ||||||
|     paint: gray.darken(40%), |  | ||||||
|     thickness: .8pt |  | ||||||
|   ), |  | ||||||
|   size: 30pt |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| #let render(delay) = get-ctx(ctx => { | #let render(delay) = get-ctx(ctx => { | ||||||
|   let style = styles.resolve( |  | ||||||
|     ctx.style, |  | ||||||
|     merge: delay.style, |  | ||||||
|     root: "delay", |  | ||||||
|     base: delay-default-style |  | ||||||
|   ) |  | ||||||
|   let size = normalize-units(style.size) |  | ||||||
|  |  | ||||||
|   let y0 = ctx.y |   let y0 = ctx.y | ||||||
|   let y1 = ctx.y - size |   let y1 = ctx.y - delay.size | ||||||
|   for (i, line) in ctx.lifelines.enumerate() { |   for (i, line) in ctx.lifelines.enumerate() { | ||||||
|     line.events.push((type: "delay-start", y: y0)) |     line.lines.push(("delay-start", y0)) | ||||||
|     line.events.push((type: "delay-end", y: y1, style: style)) |     line.lines.push(("delay-end", y1)) | ||||||
|     ctx.lifelines.at(i) = line |     ctx.lifelines.at(i) = line | ||||||
|   } |   } | ||||||
|   if delay.name != none { |   if delay.name != none { | ||||||
| @@ -32,13 +15,13 @@ | |||||||
|     let x1 = ctx.x-pos.last() |     let x1 = ctx.x-pos.last() | ||||||
|     draw.content( |     draw.content( | ||||||
|       ((x0 + x1) / 2, (y0 + y1) / 2), |       ((x0 + x1) / 2, (y0 + y1) / 2), | ||||||
|       anchor: "mid", |       anchor: "center", | ||||||
|       delay.name |       delay.name | ||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
|    |   ctx.y = y1 | ||||||
|   set-ctx(c => { |   set-ctx(c => { | ||||||
|     c.y = y1 |     c.y = ctx.y | ||||||
|     c.lifelines = ctx.lifelines |     c.lifelines = ctx.lifelines | ||||||
|     return c |     return c | ||||||
|   }) |   }) | ||||||
|   | |||||||
| @@ -1,36 +1,28 @@ | |||||||
| #import "/src/cetz.typ": styles |  | ||||||
|  |  | ||||||
| #import "/src/consts.typ": * | #import "/src/consts.typ": * | ||||||
| #import "/src/core/utils.typ": get-ctx, set-ctx | #import "/src/core/utils.typ": get-ctx, set-ctx | ||||||
|  |  | ||||||
| #let render(evt) = get-ctx(ctx => { | #let render(evt) = get-ctx(ctx => { | ||||||
|   let style = styles.resolve( |  | ||||||
|     ctx.style, |  | ||||||
|     merge: evt.style, |  | ||||||
|     root: "event", |  | ||||||
|     base: () |  | ||||||
|   ) |  | ||||||
|  |  | ||||||
|   let par-name = evt.participant |   let par-name = evt.participant | ||||||
|   let i = ctx.pars-i.at(par-name) |   let i = ctx.pars-i.at(par-name) | ||||||
|   let par = ctx.participants.at(i) |   let par = ctx.participants.at(i) | ||||||
|   let line = ctx.lifelines.at(i) |   let line = ctx.lifelines.at(i) | ||||||
|   let entry = (type: evt.event, y: ctx.y, style: style) |   let entry = (evt.event, ctx.y) | ||||||
|  |  | ||||||
|   if evt.event == "disable" { |   if evt.event == "disable" { | ||||||
|     line.level -= 1 |     line.level -= 1 | ||||||
|   } else if evt.event == "enable" { |   } else if evt.event == "enable" { | ||||||
|     line.level += 1 |     line.level += 1 | ||||||
|  |     entry.push(evt.lifeline-style) | ||||||
|   } else if evt.event == "create" { |   } else if evt.event == "create" { | ||||||
|     ctx.y -= CREATE-OFFSET |     ctx.y -= CREATE-OFFSET | ||||||
|     entry.y = ctx.y |     entry.at(1) = ctx.y | ||||||
|     (par.draw)(par, y: ctx.y) |     (par.draw)(par, y: ctx.y) | ||||||
|   } else if evt.event == "destroy" { |   } else if evt.event == "destroy" { | ||||||
|   } else { |   } else { | ||||||
|     panic("Unknown event '" + evt.event + "'") |     panic("Unknown event '" + evt.event + "'") | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   line.events.push(entry) |   line.lines.push(entry) | ||||||
|   set-ctx(c => { |   set-ctx(c => { | ||||||
|     c.lifelines.at(i) = line |     c.lifelines.at(i) = line | ||||||
|     c.y = ctx.y |     c.y = ctx.y | ||||||
|   | |||||||
| @@ -1,50 +1,25 @@ | |||||||
| #import "/src/cetz.typ": draw, styles | #import "/src/cetz.typ": draw | ||||||
|  |  | ||||||
| #import "/src/consts.typ": * | #import "/src/consts.typ": * | ||||||
| #import "/src/core/utils.typ": expand-parent-group, get-ctx, normalize-units, set-ctx | #import "/src/core/utils.typ": get-ctx, set-ctx, expand-parent-group | ||||||
|  |  | ||||||
| #let group-default-style = ( | #let display-name(name) = text(name, weight: "bold") | ||||||
|   name: ( | #let display-desc(desc) = text([\[#desc\]], weight: "bold", size: .8em) | ||||||
|     inset: ( |  | ||||||
|       x: 5pt, |  | ||||||
|       y: 3pt |  | ||||||
|     ), |  | ||||||
|     stroke: auto, |  | ||||||
|     fill: auto |  | ||||||
|   ), |  | ||||||
|   desc: ( |  | ||||||
|     inset: 3pt |  | ||||||
|   ), |  | ||||||
|   divider: (dash: (2pt, 1pt), thickness: .5pt), |  | ||||||
|   padding: 10pt, |  | ||||||
|   stroke: auto |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| #let render-start(grp) = get-ctx(ctx => { | #let render-start(grp) = get-ctx(ctx => { | ||||||
|   let style = styles.resolve( |  | ||||||
|     ctx.style, |  | ||||||
|     merge: grp.style, |  | ||||||
|     root: "group", |  | ||||||
|     base: group-default-style |  | ||||||
|   ) |  | ||||||
|  |  | ||||||
|   let grp = grp |   let grp = grp | ||||||
|   grp.insert("resolved-style", style) |   ctx.y -= Y-SPACE | ||||||
|   ctx.y -= ctx.style.y-space |   let m = measure( | ||||||
|  |     box( | ||||||
|   let name = box( |       grp.name, | ||||||
|     text(grp.name, weight: "bold"), |       inset: ( | ||||||
|     inset: style.name.inset |         left: 5pt, | ||||||
|  |         right: 5pt, | ||||||
|  |         top: 3pt, | ||||||
|  |         bottom: 3pt | ||||||
|  |       ), | ||||||
|  |     ) | ||||||
|   ) |   ) | ||||||
|   grp.insert("rendered-name", name) |  | ||||||
|  |  | ||||||
|   let desc = box( |  | ||||||
|     text([\[#grp.desc\]], weight: "bold", size: .8em), |  | ||||||
|     inset: style.desc.inset |  | ||||||
|   ) |  | ||||||
|   grp.insert("rendered-desc", desc) |  | ||||||
|  |  | ||||||
|   let m = measure(name) |  | ||||||
|   ctx.groups = ctx.groups.map(g => { |   ctx.groups = ctx.groups.map(g => { | ||||||
|     if g.group.min-i == grp.min-i { g.start-lvl += 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 } |     if g.group.max-i == grp.max-i { g.end-lvl += 1 } | ||||||
| @@ -62,7 +37,6 @@ | |||||||
|     max-x: ctx.x-pos.at(grp.max-i) + 10 |     max-x: ctx.x-pos.at(grp.max-i) + 10 | ||||||
|   )) |   )) | ||||||
|   ctx.y -= m.height / 1pt |   ctx.y -= m.height / 1pt | ||||||
|   ctx.y += ctx.style.y-space / 2 |  | ||||||
|  |  | ||||||
|   set-ctx(c => { |   set-ctx(c => { | ||||||
|     c.y = ctx.y |     c.y = ctx.y | ||||||
| @@ -73,62 +47,56 @@ | |||||||
|  |  | ||||||
|  |  | ||||||
| #let draw-group(x0, x1, y0, y1, group) = { | #let draw-group(x0, x1, y0, y1, group) = { | ||||||
|   let style = group.resolved-style |   let name = display-name(group.name) | ||||||
|   let name = group.rendered-name |  | ||||||
|   let desc = group.rendered-desc |  | ||||||
|   let m = measure(name) |   let m = measure(name) | ||||||
|   let w = m.width / 1pt |   let w = m.width / 1pt + 15 | ||||||
|   let h = m.height / 1pt |   let h = m.height / 1pt + 6 | ||||||
|   draw.rect( |   draw.rect( | ||||||
|     (x0, y0), |     (x0, y0), | ||||||
|     (x1, y1), |     (x1, y1) | ||||||
|     stroke: style.stroke |  | ||||||
|   ) |   ) | ||||||
|  |  | ||||||
|   let x1 = x0 + w |  | ||||||
|   let x2 = x1 + 5 |  | ||||||
|   draw.line( |   draw.line( | ||||||
|     (x0, y0), |     (x0, y0), | ||||||
|     (x2, y0), |     (x0 + w, y0), | ||||||
|     (x2, y0 - h / 2), |     (x0 + w, y0 - h / 2), | ||||||
|     (x1, y0 - h), |     (x0 + w - 5, y0 - h), | ||||||
|     (x0, y0 - h), |     (x0, y0 - h), | ||||||
|     stroke: style.name.stroke, |     fill: COL-GRP-NAME, | ||||||
|     fill: style.name.fill, |  | ||||||
|     close: true |     close: true | ||||||
|   ) |   ) | ||||||
|   draw.content( |   draw.content( | ||||||
|     (x0, y0), |     (x0, y0), | ||||||
|     name, |     name, | ||||||
|     anchor: "north-west" |     anchor: "north-west", | ||||||
|  |     padding: (left: 5pt, right: 10pt, top: 3pt, bottom: 3pt) | ||||||
|   ) |   ) | ||||||
|  |  | ||||||
|   if group.desc != none { |   if group.desc != none { | ||||||
|     draw.content( |     draw.content( | ||||||
|       (x2, y0), |       (x0 + w, y0), | ||||||
|       desc, |       display-desc(group.desc), | ||||||
|       anchor: "north-west" |       anchor: "north-west", | ||||||
|  |       padding: 3pt | ||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| #let draw-else(x0, x1, y, elmt) = { | #let draw-else(x0, x1, y, elmt) = { | ||||||
|   let style = elmt.resolved-style |  | ||||||
|   draw.line( |   draw.line( | ||||||
|     (x0, y), |     (x0, y), | ||||||
|     (x1, y), |     (x1, y), | ||||||
|     stroke: style.divider |     stroke: (dash: (2pt, 1pt), thickness: .5pt) | ||||||
|   ) |   ) | ||||||
|   draw.content( |   draw.content( | ||||||
|     (x0, y), |     (x0, y), | ||||||
|     elmt.rendered-desc, |     display-desc(elmt.desc), | ||||||
|     anchor: "north-west" |     anchor: "north-west", | ||||||
|  |     padding: 3pt | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
| #let render-end(group) = get-ctx(ctx => { | #let render-end(group) = get-ctx(ctx => { | ||||||
|   let y = ctx.y - ctx.style.y-space / 2 |   ctx.y -= Y-SPACE | ||||||
|   ctx.y -= ctx.style.y-space / 2 |  | ||||||
|   let ( |   let ( | ||||||
|     start-y, |     start-y, | ||||||
|     group, |     group, | ||||||
| @@ -137,11 +105,26 @@ | |||||||
|     min-x, |     min-x, | ||||||
|     max-x |     max-x | ||||||
|   ) = ctx.groups.pop() |   ) = ctx.groups.pop() | ||||||
|   let padding = normalize-units(group.resolved-style.padding) |   let x0 = min-x - 10 | ||||||
|   let x0 = min-x - padding |   let x1 = max-x + 10 | ||||||
|   let x1 = max-x + padding |  | ||||||
|    |    | ||||||
|   draw-group(x0, x1, start-y, y, group) |   // Fit name and descriptions | ||||||
|  |   let name-m = measure(display-name(group.name)) | ||||||
|  |   let width = name-m.width / 1pt + 15 | ||||||
|  |   if group.desc != none { | ||||||
|  |     let desc-m = measure(display-desc(group.desc)) | ||||||
|  |     width += desc-m.width / 1pt + 6 | ||||||
|  |   } | ||||||
|  |   if group.grp-type == "alt" { | ||||||
|  |     width = calc.max(width, ..group.elses.map(e => { | ||||||
|  |       let elmt = e.at(1) | ||||||
|  |       let desc-m = measure(display-desc(elmt.desc)) | ||||||
|  |       return desc-m.width / 1pt + 6 | ||||||
|  |     })) | ||||||
|  |   } | ||||||
|  |   x1 = calc.max(x1, x0 + width + 3) | ||||||
|  |    | ||||||
|  |   draw-group(x0, x1, start-y, ctx.y, group) | ||||||
|  |  | ||||||
|   if group.grp-type == "alt" { |   if group.grp-type == "alt" { | ||||||
|     for (else-y, else-elmt) in group.elses { |     for (else-y, else-elmt) in group.elses { | ||||||
| @@ -158,33 +141,12 @@ | |||||||
|   expand-parent-group(x0, x1) |   expand-parent-group(x0, x1) | ||||||
| }) | }) | ||||||
|  |  | ||||||
| #let render-else(else_) = get-ctx(ctx => { | #let render-else(else_) = set-ctx(ctx => { | ||||||
|   let group = ctx.groups.last().group |   ctx.y -= Y-SPACE | ||||||
|   let style = styles.resolve( |   let m = measure(text([\[#else_.desc\]], weight: "bold", size: .8em)) | ||||||
|     ctx.style, |  | ||||||
|     merge: group.style, |  | ||||||
|     root: "group", |  | ||||||
|     base: group-default-style |  | ||||||
|   ) |  | ||||||
|   ctx.y -= ctx.style.y-space / 2 |  | ||||||
|  |  | ||||||
|   let desc = box( |  | ||||||
|     text([\[#else_.desc\]], weight: "bold", size: .8em), |  | ||||||
|     inset: style.desc.inset |  | ||||||
|   ) |  | ||||||
|   let m = measure(desc) |  | ||||||
|   let else_ = else_ |  | ||||||
|   else_.insert("resolved-style", style) |  | ||||||
|   else_.insert("rendered-desc", desc) |  | ||||||
|   ctx.groups.last().group.elses.push(( |   ctx.groups.last().group.elses.push(( | ||||||
|     ctx.y, else_ |     ctx.y, else_ | ||||||
|   )) |   )) | ||||||
|   ctx.y -= m.height / 1pt |   ctx.y -= m.height / 1pt | ||||||
|   ctx.y += ctx.style.y-space / 2 |   return ctx | ||||||
|    |  | ||||||
|   set-ctx(c => { |  | ||||||
|     c.y = ctx.y |  | ||||||
|     c.groups = ctx.groups |  | ||||||
|     return c |  | ||||||
|   }) |  | ||||||
| }) | }) | ||||||
| @@ -1,51 +1,14 @@ | |||||||
| #import "/src/cetz.typ": draw, styles | #import "/src/cetz.typ": draw | ||||||
|  |  | ||||||
| #import "/src/consts.typ": * | #import "/src/consts.typ": * | ||||||
| #import "/src/core/utils.typ": get-ctx, is-elmt, set-ctx, expand-parent-group, normalize-measure | #import "/src/core/utils.typ": get-ctx, set-ctx, expand-parent-group | ||||||
|  |  | ||||||
| #let note-default-style = ( |  | ||||||
|   shape: "default", |  | ||||||
|   fill: rgb("#FEFFDD"), |  | ||||||
|   stroke: black + .5pt |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| #let resolve-style(ctx, note) = { |  | ||||||
|   return styles.resolve( |  | ||||||
|     ctx.style, |  | ||||||
|     merge: note.style, |  | ||||||
|     root: "note", |  | ||||||
|     base: note-default-style |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let pre-resolve-styles() = get-ctx(ctx => { |  | ||||||
|   let ctx = ctx |  | ||||||
|   let notes = ctx.setup.notes |  | ||||||
|  |  | ||||||
|   for (i, elmt) in ctx.setup.elements.enumerate() { |  | ||||||
|     if type(elmt) == function { |  | ||||||
|       ctx = elmt(ctx).ctx |  | ||||||
|     } else if is-elmt(elmt) { |  | ||||||
|       if elmt.type == "note" { |  | ||||||
|         let style = resolve-style(ctx, elmt) |  | ||||||
|         notes.at(elmt.id).insert("resolved-style", style) |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   set-ctx(c => { |  | ||||||
|     c.setup.notes = notes |  | ||||||
|     return c |  | ||||||
|   }) |  | ||||||
| }) |  | ||||||
|  |  | ||||||
| #let get-size(note) = { | #let get-size(note) = { | ||||||
|   let style = note.resolved-style |   let PAD = if note.shape == "hex" {NOTE-HEX-PAD} else {NOTE-PAD} | ||||||
|   let PAD = if style.shape == "hex" {NOTE-HEX-PAD} else {NOTE-PAD} |  | ||||||
|   let m = measure(box(note.content)) |   let m = measure(box(note.content)) | ||||||
|   let w = m.width / 1pt + PAD.last() * 2 |   let w = m.width / 1pt + PAD.last() * 2 | ||||||
|   let h = m.height / 1pt + PAD.first() * 2 |   let h = m.height / 1pt + PAD.first() * 2 | ||||||
|   if style.shape == "default" { |   if note.shape == "default" { | ||||||
|     w += NOTE-CORNER-SIZE |     w += NOTE-CORNER-SIZE | ||||||
|   } |   } | ||||||
|   return ( |   return ( | ||||||
| @@ -68,15 +31,14 @@ | |||||||
| } | } | ||||||
|  |  | ||||||
| #let get-box(note) = { | #let get-box(note) = { | ||||||
|   let style = note.resolved-style |   let PAD = if note.shape == "hex" {NOTE-HEX-PAD} else {NOTE-PAD} | ||||||
|   let PAD = if style.shape == "hex" {NOTE-HEX-PAD} else {NOTE-PAD} |  | ||||||
|   let inset = ( |   let inset = ( | ||||||
|     left: PAD.last() * 1pt, |     left: PAD.last() * 1pt, | ||||||
|     right: PAD.last() * 1pt, |     right: PAD.last() * 1pt, | ||||||
|     top: PAD.first() * 1pt, |     top: PAD.first() * 1pt, | ||||||
|     bottom: PAD.first() * 1pt, |     bottom: PAD.first() * 1pt, | ||||||
|   ) |   ) | ||||||
|   if style.shape == "default" { |   if note.shape == "default" { | ||||||
|     inset.right += NOTE-CORNER-SIZE * 1pt |     inset.right += NOTE-CORNER-SIZE * 1pt | ||||||
|   } |   } | ||||||
|   if note.side == "left" { |   if note.side == "left" { | ||||||
| @@ -100,21 +62,18 @@ | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   get-ctx(ctx => { |   get-ctx(ctx => { | ||||||
|     let note = ctx.notes.at(note.id) |  | ||||||
|     let y = y |     let y = y | ||||||
|     if y == auto { |     if y == auto { | ||||||
|       y = ctx.y |       y = ctx.y | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let style = note.resolved-style |  | ||||||
|     let shape = style.shape |  | ||||||
|      |      | ||||||
|     let PAD = if shape == "hex" {NOTE-HEX-PAD} else {NOTE-PAD} |  | ||||||
|     let m = normalize-measure(box(note.content)) |     let PAD = if note.shape == "hex" {NOTE-HEX-PAD} else {NOTE-PAD} | ||||||
|     let w = m.width + PAD.last() * 2 |     let m = measure(box(note.content)) | ||||||
|     let h = m.height + PAD.first() * 2 |     let w = m.width / 1pt + PAD.last() * 2 | ||||||
|  |     let h = m.height / 1pt + PAD.first() * 2 | ||||||
|     let total-w = w |     let total-w = w | ||||||
|     if shape == "default" { |     if note.shape == "default" { | ||||||
|       total-w += NOTE-CORNER-SIZE |       total-w += NOTE-CORNER-SIZE | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -147,31 +106,31 @@ | |||||||
|     } |     } | ||||||
|     let y1 = y0 - h |     let y1 = y0 - h | ||||||
|  |  | ||||||
|     if shape == "default" { |     if note.shape == "default" { | ||||||
|       draw.line( |       draw.line( | ||||||
|         (x0, y0), |         (x0, y0), | ||||||
|         (x1, y0), |         (x1, y0), | ||||||
|         (x2, y0 - NOTE-CORNER-SIZE), |         (x2, y0 - NOTE-CORNER-SIZE), | ||||||
|         (x2, y1), |         (x2, y1), | ||||||
|         (x0, y1), |         (x0, y1), | ||||||
|         stroke: style.stroke, |         stroke: black + .5pt, | ||||||
|         fill: style.fill, |         fill: note.color, | ||||||
|         close: true |         close: true | ||||||
|       ) |       ) | ||||||
|       draw.line( |       draw.line( | ||||||
|         (x1, y0), |         (x1, y0), | ||||||
|         (x1, y0 - NOTE-CORNER-SIZE), |         (x1, y0 - NOTE-CORNER-SIZE), | ||||||
|         (x2, y0 - NOTE-CORNER-SIZE), |         (x2, y0 - NOTE-CORNER-SIZE), | ||||||
|         stroke: style.stroke |         stroke: black + .5pt | ||||||
|       ) |       ) | ||||||
|     } else if shape == "rect" { |     } else if note.shape == "rect" { | ||||||
|       draw.rect( |       draw.rect( | ||||||
|         (x0, y0), |         (x0, y0), | ||||||
|         (x2, y1), |         (x2, y1), | ||||||
|         stroke: style.stroke, |         stroke: black + .5pt, | ||||||
|         fill: style.fill |         fill: note.color | ||||||
|       ) |       ) | ||||||
|     } else if shape == "hex" { |     } else if note.shape == "hex" { | ||||||
|       let lx = x0 + PAD.last() |       let lx = x0 + PAD.last() | ||||||
|       let rx = x2 - PAD.last() |       let rx = x2 - PAD.last() | ||||||
|       let my = (y0 + y1) / 2 |       let my = (y0 + y1) / 2 | ||||||
| @@ -182,8 +141,8 @@ | |||||||
|         (rx, y1), |         (rx, y1), | ||||||
|         (lx, y1), |         (lx, y1), | ||||||
|         (x0, my), |         (x0, my), | ||||||
|         stroke: style.stroke, |         stroke: black + .5pt, | ||||||
|         fill: style.fill, |         fill: note.color, | ||||||
|         close: true |         close: true | ||||||
|       ) |       ) | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,109 +1,316 @@ | |||||||
| #import "/src/cetz.typ": draw, styles | #import "/src/cetz.typ": draw | ||||||
|  |  | ||||||
| #import "/src/consts.typ": * | #import "/src/consts.typ": * | ||||||
| #import "/src/core/utils.typ": get-ctx, get-style, is-elmt, set-ctx | #import "/src/core/utils.typ": get-ctx, get-style, set-ctx | ||||||
|  |  | ||||||
| #let shapes = { |  | ||||||
|   let from-module(path) = { |  | ||||||
|     import path as mod |  | ||||||
|     return (mod.name: ( |  | ||||||
|       get-size: mod.get-size, |  | ||||||
|       render: mod.render, |  | ||||||
|       default-style: mod.default-style |  | ||||||
|     )) |  | ||||||
|   } |  | ||||||
|   from-module("participant/default.typ") |  | ||||||
|   from-module("participant/actor.typ") |  | ||||||
|   from-module("participant/boundary.typ") |  | ||||||
|   from-module("participant/control.typ") |  | ||||||
|   from-module("participant/entity.typ") |  | ||||||
|   from-module("participant/database.typ") |  | ||||||
|   from-module("participant/collections.typ") |  | ||||||
|   from-module("participant/queue.typ") |  | ||||||
|   from-module("participant/custom.typ") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let participant-default-style = ( |  | ||||||
|   fill: rgb("#E2E2F0"), |  | ||||||
|   stroke: black + .5pt, |  | ||||||
|   from-start: true, |  | ||||||
|   show-bottom: true, |  | ||||||
|   show-top: true, |  | ||||||
|   shape: "participant", |  | ||||||
|   track: ( |  | ||||||
|     dash: "dashed", |  | ||||||
|     paint: gray.darken(40%), |  | ||||||
|     thickness: .5pt |  | ||||||
|   ) |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| #let resolve-style(ctx, par) = { |  | ||||||
|   let style = styles.resolve( |  | ||||||
|     ctx.style, |  | ||||||
|     merge: par.style, |  | ||||||
|     root: "participant", |  | ||||||
|     base: participant-default-style |  | ||||||
|   ) |  | ||||||
|   let shape-style = shapes.at(style.shape, default: (:)) |  | ||||||
|                           .at("default-style", default: (:)) |  | ||||||
|   style = styles.resolve( |  | ||||||
|     ctx.style, |  | ||||||
|     merge: style, |  | ||||||
|     base: shape-style |  | ||||||
|   ) |  | ||||||
|   return style |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let pre-resolve-styles() = get-ctx(ctx => { |  | ||||||
|   let idx = (:) |  | ||||||
|   let elements = ctx.setup.elements |  | ||||||
|   let participants = ctx.setup.participants |  | ||||||
|   for (i, par) in participants.enumerate() { |  | ||||||
|     par.insert("resolved-style", resolve-style(ctx, par)) |  | ||||||
|     participants.at(i) = par |  | ||||||
|     idx.insert(par.name, i) |  | ||||||
|   } |  | ||||||
|   for (i, elmt) in elements.enumerate() { |  | ||||||
|     if type(elmt) == function { |  | ||||||
|       ctx = elmt(ctx).ctx |  | ||||||
|     } else if is-elmt(elmt) { |  | ||||||
|       if elmt.type == "par" { |  | ||||||
|         let style = resolve-style(ctx, elmt) |  | ||||||
|         elements.at(i).insert("resolved-style", style) |  | ||||||
|         let i = idx.at(elmt.name) |  | ||||||
|         participants.at(i).resolved-style = style |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|    |  | ||||||
|   set-ctx(c => { |  | ||||||
|     c.setup.elements = elements |  | ||||||
|     c.setup.participants = participants |  | ||||||
|     return c |  | ||||||
|   }) |  | ||||||
| }) |  | ||||||
|  |  | ||||||
| #let get-size(par) = { | #let get-size(par) = { | ||||||
|   if par.invisible { |   if par.invisible { | ||||||
|     return (width: 0, height: 0) |     return (width: 0pt, height: 0pt) | ||||||
|   } |   } | ||||||
|   let style = par.resolved-style |   let m = measure(box(par.display-name)) | ||||||
|   let func = shapes.at(style.shape).get-size |   let w = m.width | ||||||
|   return func(par) |   let h = m.height | ||||||
|  |   let (shape-w, shape-h) = ( | ||||||
|  |     participant: (w + PAR-PAD.last() * 2, h + PAR-PAD.first() * 2), | ||||||
|  |     actor: (ACTOR-WIDTH * 1pt, ACTOR-WIDTH * 2pt + SYM-GAP * 1pt + h), | ||||||
|  |     boundary: (BOUNDARY-HEIGHT * 2pt, BOUNDARY-HEIGHT * 1pt + SYM-GAP * 1pt + h), | ||||||
|  |     control: (CONTROL-HEIGHT * 1pt, CONTROL-HEIGHT * 1pt + SYM-GAP * 1pt + h), | ||||||
|  |     entity: (ENTITY-HEIGHT * 1pt, ENTITY-HEIGHT * 1pt + 2pt + SYM-GAP * 1pt + h), | ||||||
|  |     database: (DATABASE-WIDTH * 1pt, DATABASE-WIDTH * 4pt / 3 + SYM-GAP * 1pt + h), | ||||||
|  |     collections: ( | ||||||
|  |       w + COLLECTIONS-PAD.last() * 2 + calc.abs(COLLECTIONS-DX) * 1pt, | ||||||
|  |       h + COLLECTIONS-PAD.first() * 2 + calc.abs(COLLECTIONS-DY) * 1pt, | ||||||
|  |     ), | ||||||
|  |     queue: ( | ||||||
|  |       w + QUEUE-PAD.last() * 2 + 3 * (h + QUEUE-PAD.first() * 2) / 4, | ||||||
|  |       h + QUEUE-PAD.first() * 2 | ||||||
|  |     ), | ||||||
|  |     custom: ( | ||||||
|  |       measure(par.custom-image).width, | ||||||
|  |       measure(par.custom-image).height + SYM-GAP * 1pt + h | ||||||
|  |     ) | ||||||
|  |   ).at(par.shape) | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     width: calc.max(w, shape-w), | ||||||
|  |     height: calc.max(h, shape-h) | ||||||
|  |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
| #let render(par, y: 0, bottom: false) = get-ctx(ctx => { | #let _render-participant(x, y, p, m, bottom) = { | ||||||
|   let style = resolve-style(ctx, par) |   let w = m.width / 1pt | ||||||
|   let func = shapes.at(style.shape).render |   let h = m.height / 1pt | ||||||
|   let par = par |   let x0 = x - w / 2 - PAR-PAD.last() / 1pt | ||||||
|   par.resolved-style = style |   let x1 = x + w / 2 + PAR-PAD.last() / 1pt | ||||||
|   func(ctx.x-pos.at(par.i), y, par, bottom) |   let y0 = y + h + PAR-PAD.first() / 1pt * 2 | ||||||
|  |   if bottom { | ||||||
|  |     y0 = y | ||||||
|  |   } | ||||||
|  |   let y1 = y0 - h - PAR-PAD.first() / 1pt * 2 | ||||||
|  |  | ||||||
|  |   draw.rect( | ||||||
|  |     (x0, y0), | ||||||
|  |     (x1, y1), | ||||||
|  |     radius: 2pt, | ||||||
|  |     fill: p.color, | ||||||
|  |     stroke: black + .5pt | ||||||
|  |   ) | ||||||
|  |   draw.content( | ||||||
|  |     ((x0 + x1) / 2, (y0 + y1) / 2), | ||||||
|  |     p.display-name, | ||||||
|  |     anchor: "center" | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #let _render-actor(x, y, p, m, bottom) = { | ||||||
|  |   let w2 = ACTOR-WIDTH / 2 | ||||||
|  |   let head-r = ACTOR-WIDTH / 4 | ||||||
|  |   let height = ACTOR-WIDTH * 2 | ||||||
|  |   let arms-y = height * 0.375 | ||||||
|  |  | ||||||
|  |   let y0 = if bottom {y - m.height / 1pt - SYM-GAP} else {y + m.height / 1pt + height + SYM-GAP} | ||||||
|  |   draw.circle( | ||||||
|  |     (x, y0 - head-r), | ||||||
|  |     radius: head-r, | ||||||
|  |     fill: p.color, | ||||||
|  |     stroke: black + .5pt | ||||||
|  |   ) | ||||||
|  |   draw.line((x, y0 - head-r * 2), (x, y0 - height + w2), stroke: black + .5pt) | ||||||
|  |   draw.line((x - w2, y0 - arms-y), (x + w2, y0 - arms-y), stroke: black + .5pt) | ||||||
|  |   draw.line((x - w2, y0 - height), (x, y0 - height + w2), (x + w2, y0 - height), stroke: black + .5pt) | ||||||
|  |   draw.content( | ||||||
|  |     (x, y), | ||||||
|  |     p.display-name, | ||||||
|  |     anchor: if bottom {"north"} else {"base"} | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #let _render-boundary(x, y, p, m, bottom) = { | ||||||
|  |   let circle-r = BOUNDARY-HEIGHT / 2 | ||||||
|  |   let y0 = if bottom {y - m.height / 1pt - SYM-GAP} else {y + m.height / 1pt + BOUNDARY-HEIGHT + SYM-GAP} | ||||||
|  |   let x0 = x - BOUNDARY-HEIGHT | ||||||
|  |   let y1 = y0 - circle-r | ||||||
|  |   let y2 = y0 - BOUNDARY-HEIGHT | ||||||
|  |  | ||||||
|  |   draw.circle( | ||||||
|  |     (x + circle-r, y1), | ||||||
|  |     radius: circle-r, | ||||||
|  |     fill: p.color, | ||||||
|  |     stroke: black + .5pt | ||||||
|  |   ) | ||||||
|  |   draw.line( | ||||||
|  |     (x0, y0), (x0, y2), | ||||||
|  |     stroke: black + .5pt | ||||||
|  |   ) | ||||||
|  |   draw.line( | ||||||
|  |     (x0, y1), (x, y1), | ||||||
|  |     stroke: black + .5pt | ||||||
|  |   ) | ||||||
|  |   draw.content( | ||||||
|  |     (x, y), | ||||||
|  |     p.display-name, | ||||||
|  |     anchor: if bottom {"north"} else {"base"} | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #let _render-control(x, y, p, m, bottom) = { | ||||||
|  |   let r = CONTROL-HEIGHT / 2 | ||||||
|  |   let y0 = if bottom {y - m.height / 1pt - SYM-GAP} else {y + m.height / 1pt + CONTROL-HEIGHT + SYM-GAP} | ||||||
|  |  | ||||||
|  |   draw.circle( | ||||||
|  |     (x, y0 - r), | ||||||
|  |     radius: r, | ||||||
|  |     fill: p.color, | ||||||
|  |     stroke: black + .5pt | ||||||
|  |   ) | ||||||
|  |   draw.mark((x, y0), (x - r / 2, y0), symbol: "stealth", fill: black) | ||||||
|  |   draw.content( | ||||||
|  |     (x, y), | ||||||
|  |     p.display-name, | ||||||
|  |     anchor: if bottom {"north"} else {"base"} | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #let _render-entity(x, y, p, m, bottom) = { | ||||||
|  |   let r = ENTITY-HEIGHT / 2 | ||||||
|  |   let y0 = if bottom {y - m.height / 1pt - SYM-GAP} else {y + m.height / 1pt + ENTITY-HEIGHT + SYM-GAP} | ||||||
|  |   let y1 = y0 - ENTITY-HEIGHT - 1.5 | ||||||
|  |  | ||||||
|  |   draw.circle( | ||||||
|  |     (x, y0 - r), | ||||||
|  |     radius: r, | ||||||
|  |     fill: p.color, | ||||||
|  |     stroke: black + .5pt | ||||||
|  |   ) | ||||||
|  |   draw.line( | ||||||
|  |     (x - r, y1), | ||||||
|  |     (x + r, y1), | ||||||
|  |     stroke: black + .5pt | ||||||
|  |   ) | ||||||
|  |   draw.content( | ||||||
|  |     (x, y), | ||||||
|  |     p.display-name, | ||||||
|  |     anchor: if bottom {"north"} else {"base"} | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #let _render-database(x, y, p, m, bottom) = { | ||||||
|  |   let height = DATABASE-WIDTH * 4 / 3 | ||||||
|  |   let rx = DATABASE-WIDTH / 2 | ||||||
|  |   let ry = rx / 2 | ||||||
|  |   let y0 = if bottom {y - m.height / 1pt - SYM-GAP} else {y + m.height / 1pt + height + SYM-GAP} | ||||||
|  |   let y1 = y0 - height | ||||||
|  |  | ||||||
|  |   draw.merge-path( | ||||||
|  |     close: true, | ||||||
|  |     fill: p.color, | ||||||
|  |     stroke: black + .5pt, | ||||||
|  |     { | ||||||
|  |       draw.bezier((x - rx, y0 - ry), (x, y0), (x - rx, y0 - ry/2), (x - rx/2, y0)) | ||||||
|  |       draw.bezier((), (x + rx, y0 - ry), (x + rx/2, y0), (x + rx, y0 - ry/2)) | ||||||
|  |       draw.line((), (x + rx, y1 + ry)) | ||||||
|  |       draw.bezier((), (x, y1), (x + rx, y1 + ry/2), (x + rx/2, y1)) | ||||||
|  |       draw.bezier((), (x - rx, y1 + ry), (x - rx/2, y1), (x - rx, y1 + ry/2)) | ||||||
|  |     } | ||||||
|  |   ) | ||||||
|  |   draw.merge-path( | ||||||
|  |     stroke: black + .5pt, | ||||||
|  |     { | ||||||
|  |       draw.bezier((x - rx, y0 - ry), (x, y0 - ry*2), (x - rx, y0 - 3*ry/2), (x - rx/2, y0 - ry*2)) | ||||||
|  |       draw.bezier((), (x + rx, y0 - ry), (x + rx/2, y0 - ry*2), (x + rx, y0 - 3*ry/2)) | ||||||
|  |     } | ||||||
|  |   ) | ||||||
|  |   draw.content( | ||||||
|  |     (x, y), | ||||||
|  |     p.display-name, | ||||||
|  |     anchor: if bottom {"north"} else {"base"} | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #let _render-collections(x, y, p, m, bottom) = { | ||||||
|  |   let w = m.width / 1pt | ||||||
|  |   let h = m.height / 1pt | ||||||
|  |   let dx = COLLECTIONS-DX | ||||||
|  |   let dy = COLLECTIONS-DY | ||||||
|  |   let total-w = w + PAR-PAD.last() * 2 / 1pt + calc.abs(dx) | ||||||
|  |   let total-h = h + PAR-PAD.first() * 2 / 1pt + calc.abs(dy) | ||||||
|  |  | ||||||
|  |   let x0 = x - total-w / 2 | ||||||
|  |   let x1 = x0 + calc.abs(dx) | ||||||
|  |   let x3 = x0 + total-w | ||||||
|  |   let x2 = x3 - calc.abs(dx) | ||||||
|  |    | ||||||
|  |   let y0 = if bottom {y} else {y + total-h} | ||||||
|  |   let y1 = y0 - calc.abs(dy) | ||||||
|  |   let y3 = y0 - total-h | ||||||
|  |   let y2 = y3 + calc.abs(dy) | ||||||
|  |  | ||||||
|  |   let r1 = (x1, y0, x3, y2) | ||||||
|  |   let r2 = (x0, y1, x2, y3) | ||||||
|  |  | ||||||
|  |   if dx < 0 { | ||||||
|  |     r1.at(0) = x0 | ||||||
|  |     r1.at(2) = x2 | ||||||
|  |     r2.at(0) = x1 | ||||||
|  |     r2.at(2) = x3 | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if dy < 0 { | ||||||
|  |     r1.at(1) = y1 | ||||||
|  |     r1.at(3) = y3 | ||||||
|  |     r2.at(1) = y0 | ||||||
|  |     r2.at(3) = y2 | ||||||
|  |   } | ||||||
|  |   draw.rect( | ||||||
|  |     (r1.at(0), r1.at(1)), | ||||||
|  |     (r1.at(2), r1.at(3)), | ||||||
|  |     fill: p.color, | ||||||
|  |     stroke: black + .5pt | ||||||
|  |   ) | ||||||
|  |   draw.rect( | ||||||
|  |     (r2.at(0), r2.at(1)), | ||||||
|  |     (r2.at(2), r2.at(3)), | ||||||
|  |     fill: p.color, | ||||||
|  |     stroke: black + .5pt | ||||||
|  |   ) | ||||||
|  |    | ||||||
|  |   draw.content( | ||||||
|  |     ((r2.at(0) + r2.at(2)) / 2, (r2.at(1) + r2.at(3)) / 2), | ||||||
|  |     p.display-name, | ||||||
|  |     anchor: "center" | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #let _render-queue(x, y, p, m, bottom) = { | ||||||
|  |   let w = (m.width + QUEUE-PAD.last() * 2) / 1pt | ||||||
|  |   let h = (m.height + QUEUE-PAD.first() * 2) / 1pt | ||||||
|  |   let total-h = h | ||||||
|  |   let ry = total-h / 2 | ||||||
|  |   let rx = ry / 2 | ||||||
|  |   let total-w = w + 3 + 3 * rx | ||||||
|  |  | ||||||
|  |   let x0 = x - total-w / 2 | ||||||
|  |   let y0 = if bottom {y} else {y + total-h} | ||||||
|  |   let y1 = y0 - total-h | ||||||
|  |   let x-left = x0 + rx | ||||||
|  |   let x-right = x-left + w + rx | ||||||
|  |   draw.merge-path( | ||||||
|  |     close: true, | ||||||
|  |     fill: p.color, | ||||||
|  |     stroke: black + .5pt, | ||||||
|  |     { | ||||||
|  |       draw.bezier((x-right, y0), (x-right + rx, y0 - ry), (x-right + rx/2, y0), (x-right + rx, y0 - ry/2)) | ||||||
|  |       draw.bezier((), (x-right, y1), (x-right + rx, y1 + ry/2), (x-right + rx/2, y1)) | ||||||
|  |       draw.line((), (x-left, y1)) | ||||||
|  |       draw.bezier((), (x-left - rx, y0 - ry), (x-left - rx/2, y1), (x-left - rx, y1 + ry/2)) | ||||||
|  |       draw.bezier((), (x-left, y0), (x-left - rx, y0 - ry/2), (x-left - rx/2, y0)) | ||||||
|  |     } | ||||||
|  |   ) | ||||||
|  |   draw.merge-path( | ||||||
|  |     stroke: black + .5pt, | ||||||
|  |     { | ||||||
|  |       draw.bezier((x-right, y0), (x-right - rx, y0 - ry), (x-right - rx/2, y0), (x-right - rx, y0 - ry/2)) | ||||||
|  |       draw.bezier((), (x-right, y1), (x-right - rx, y1 + ry/2), (x-right - rx/2, y1)) | ||||||
|  |     } | ||||||
|  |   ) | ||||||
|  |   draw.content( | ||||||
|  |     ((x-left + x-right - rx) / 2, y0 - ry), | ||||||
|  |     p.display-name, | ||||||
|  |     anchor: "center" | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #let _render-custom(x, y, p, m, bottom) = { | ||||||
|  |   let image-m = measure(p.custom-image) | ||||||
|  |   let y0 = if bottom {y - m.height / 1pt - SYM-GAP} else {y + m.height / 1pt + image-m.height / 1pt + SYM-GAP} | ||||||
|  |   draw.content((x - image-m.width / 2pt, y0), p.custom-image, anchor: "north-west") | ||||||
|  |   draw.content( | ||||||
|  |     (x, y), | ||||||
|  |     p.display-name, | ||||||
|  |     anchor: if bottom {"north"} else {"base"} | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #let render(par, y: 0, bottom: false) = draw.group(cetz-ctx => { | ||||||
|  |   let ctx = cetz-ctx.shared-state.chronos | ||||||
|  |   let m = measure(box(par.display-name)) | ||||||
|  |   let func = ( | ||||||
|  |     participant: _render-participant, | ||||||
|  |     actor: _render-actor, | ||||||
|  |     boundary: _render-boundary, | ||||||
|  |     control: _render-control, | ||||||
|  |     entity: _render-entity, | ||||||
|  |     database: _render-database, | ||||||
|  |     collections: _render-collections, | ||||||
|  |     queue: _render-queue, | ||||||
|  |     custom: _render-custom, | ||||||
|  |   ).at(par.shape) | ||||||
|  |   func(ctx.x-pos.at(par.i), y, par, m, bottom) | ||||||
| },) | },) | ||||||
|  |  | ||||||
| #let render-lifelines() = get-ctx(ctx => { | #let render-lifelines() = get-ctx(ctx => { | ||||||
|   let participants = ctx.participants |   let participants = ctx.participants | ||||||
|   for p in participants.filter(p => not p.invisible) { |   for p in participants.filter(p => not p.invisible) { | ||||||
|     let style = p.resolved-style |  | ||||||
|     let x = ctx.x-pos.at(p.i) |     let x = ctx.x-pos.at(p.i) | ||||||
|  |  | ||||||
|     // Draw vertical line |     // Draw vertical line | ||||||
| @@ -111,61 +318,66 @@ | |||||||
|  |  | ||||||
|     let rects = () |     let rects = () | ||||||
|     let destructions = () |     let destructions = () | ||||||
|     let stack = () |     let lines = () | ||||||
|  |  | ||||||
|     // Compute lifeline rectangles + destruction positions |     // Compute lifeline rectangles + destruction positions | ||||||
|     for event in ctx.lifelines.at(p.i).events { |     for line in ctx.lifelines.at(p.i).lines { | ||||||
|       if event.type == "create" { |       let event = line.first() | ||||||
|         last-y = event.y |       if event == "create" { | ||||||
|  |         last-y = line.at(1) | ||||||
|  |  | ||||||
|       } else if event.type == "enable" { |       } else if event == "enable" { | ||||||
|         if stack.len() == 0 { |         if lines.len() == 0 { | ||||||
|           draw.line( |           draw.line( | ||||||
|             (x, last-y), |             (x, last-y), | ||||||
|             (x, event.y), |             (x, line.at(1)), | ||||||
|             stroke: style.track |             stroke: p.line-stroke | ||||||
|           ) |           ) | ||||||
|         } |         } | ||||||
|         stack.push(event) |         lines.push(line) | ||||||
|        |        | ||||||
|       } else if event.type == "disable" or event.type == "destroy" { |       } else if event == "disable" or event == "destroy" { | ||||||
|         let lvl = 0 |         let lvl = 0 | ||||||
|         if stack.len() != 0 { |         if lines.len() != 0 { | ||||||
|           let e = stack.pop() |           let l = lines.pop() | ||||||
|           lvl = stack.len() |           lvl = lines.len() | ||||||
|           rects.push(( |           rects.push(( | ||||||
|             x + lvl * LIFELINE-W / 2, |             x + lvl * LIFELINE-W / 2, | ||||||
|             e.y, |             l.at(1), | ||||||
|             event.y, |             line.at(1), | ||||||
|             e.style |             l.at(2) | ||||||
|           )) |           )) | ||||||
|           last-y = event.y |           last-y = line.at(1) | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if event.type == "destroy" { |         if event == "destroy" { | ||||||
|           destructions.push((x + lvl * LIFELINE-W / 2, event.y)) |           destructions.push((x + lvl * LIFELINE-W / 2, line.at(1))) | ||||||
|         } |         } | ||||||
|       } else if event.type == "delay-start" { |       } else if event == "delay-start" { | ||||||
|         draw.line( |         draw.line( | ||||||
|           (x, last-y), |           (x, last-y), | ||||||
|           (x, event.y), |           (x, line.at(1)), | ||||||
|           stroke: style.track |           stroke: p.line-stroke | ||||||
|         ) |         ) | ||||||
|         last-y = event.y |         last-y = line.at(1) | ||||||
|       } else if event.type == "delay-end" { |       } else if event == "delay-end" { | ||||||
|         draw.line( |         draw.line( | ||||||
|           (x, last-y), |           (x, last-y), | ||||||
|           (x, event.y), |           (x, line.at(1)), | ||||||
|           stroke: event.style.stroke |           stroke: ( | ||||||
|  |             dash: "loosely-dotted", | ||||||
|  |             paint: gray.darken(40%), | ||||||
|  |             thickness: .8pt | ||||||
|  |           ) | ||||||
|         ) |         ) | ||||||
|         last-y = event.y |         last-y = line.at(1) | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     draw.line( |     draw.line( | ||||||
|       (x, last-y), |       (x, last-y), | ||||||
|       (x, ctx.y), |       (x, ctx.y), | ||||||
|       stroke: style.track |       stroke: p.line-stroke | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     // Draw lifeline rectangles (reverse for bottom to top) |     // Draw lifeline rectangles (reverse for bottom to top) | ||||||
| @@ -187,7 +399,7 @@ | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Draw participants (end) |     // Draw participants (end) | ||||||
|     if style.show-bottom { |     if p.show-bottom { | ||||||
|       (p.draw)(p, y: ctx.y, bottom: true) |       (p.draw)(p, y: ctx.y, bottom: true) | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -1,75 +0,0 @@ | |||||||
| #import "/src/cetz.typ": draw |  | ||||||
|  |  | ||||||
| #import "/src/core/utils.typ": normalize-measure, normalize-units |  | ||||||
|  |  | ||||||
| #let name = "actor" |  | ||||||
|  |  | ||||||
| #let render(x, y, p, bottom) = { |  | ||||||
|   let m = normalize-measure(p.display-name) |  | ||||||
|   let style = p.resolved-style |  | ||||||
|   let spacing = normalize-units(style.spacing) |  | ||||||
|   let width = normalize-units(style.width) |  | ||||||
|   let height = width * 2 |  | ||||||
|   let w2 = width / 2 |  | ||||||
|   let head-r = width / 4 |  | ||||||
|   let arms-y = height * 0.375 |  | ||||||
|  |  | ||||||
|   let y0 = if bottom { |  | ||||||
|     y - m.height - spacing |  | ||||||
|   } else { |  | ||||||
|     y + m.height + height + spacing |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Head |  | ||||||
|   draw.circle( |  | ||||||
|     (x, y0 - head-r), |  | ||||||
|     radius: head-r, |  | ||||||
|     fill: style.fill, |  | ||||||
|     stroke: style.stroke |  | ||||||
|   ) |  | ||||||
|  |  | ||||||
|   // Body |  | ||||||
|   draw.line( |  | ||||||
|     (x, y0 - head-r * 2), |  | ||||||
|     (x, y0 - height + w2), |  | ||||||
|     stroke: style.stroke |  | ||||||
|   ) |  | ||||||
|    |  | ||||||
|   // Arms |  | ||||||
|   draw.line( |  | ||||||
|     (x - w2, y0 - arms-y), |  | ||||||
|     (x + w2, y0 - arms-y), |  | ||||||
|     stroke: style.stroke |  | ||||||
|   ) |  | ||||||
|  |  | ||||||
|   // Legs |  | ||||||
|   draw.line( |  | ||||||
|     (x - w2, y0 - height), |  | ||||||
|     (x, y0 - height + w2), |  | ||||||
|     (x + w2, y0 - height), |  | ||||||
|     stroke: style.stroke |  | ||||||
|   ) |  | ||||||
|   draw.content( |  | ||||||
|     (x, y), |  | ||||||
|     p.display-name, |  | ||||||
|     anchor: if bottom {"north"} else {"base"} |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let get-size(par) = { |  | ||||||
|   let m = normalize-measure(par.display-name) |  | ||||||
|  |  | ||||||
|   let width = normalize-units(par.resolved-style.width) |  | ||||||
|   let height = width * 2 |  | ||||||
|   let spacing = normalize-units(par.resolved-style.spacing) |  | ||||||
|  |  | ||||||
|   return ( |  | ||||||
|     width: calc.max(m.width, width), |  | ||||||
|     height: height + spacing + m.height |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let default-style = ( |  | ||||||
|   width: 20pt, |  | ||||||
|   spacing: 5pt |  | ||||||
| ) |  | ||||||
| @@ -1,59 +0,0 @@ | |||||||
| #import "/src/cetz.typ": draw |  | ||||||
|  |  | ||||||
| #import "/src/core/utils.typ": normalize-measure, normalize-units |  | ||||||
|  |  | ||||||
| #let name = "boundary" |  | ||||||
|  |  | ||||||
| #let render(x, y, p, bottom) = { |  | ||||||
|   let m = normalize-measure(p.display-name) |  | ||||||
|   let style = p.resolved-style |  | ||||||
|   let height = normalize-units(style.height) |  | ||||||
|   let spacing = normalize-units(style.spacing) |  | ||||||
|   let circle-r = height / 2 |  | ||||||
|   let y0 = if bottom { |  | ||||||
|     y - m.height - spacing |  | ||||||
|   } else { |  | ||||||
|     y + m.height + height + spacing |  | ||||||
|   } |  | ||||||
|   let x0 = x - height |  | ||||||
|   let y1 = y0 - circle-r |  | ||||||
|   let y2 = y0 - height |  | ||||||
|  |  | ||||||
|   draw.circle( |  | ||||||
|     (x + circle-r, y1), |  | ||||||
|     radius: circle-r, |  | ||||||
|     fill: style.fill, |  | ||||||
|     stroke: style.stroke |  | ||||||
|   ) |  | ||||||
|   draw.line( |  | ||||||
|     (x0, y0), (x0, y2), |  | ||||||
|     stroke: style.stroke |  | ||||||
|   ) |  | ||||||
|   draw.line( |  | ||||||
|     (x0, y1), (x, y1), |  | ||||||
|     stroke: style.stroke |  | ||||||
|   ) |  | ||||||
|   draw.content( |  | ||||||
|     (x, y), |  | ||||||
|     p.display-name, |  | ||||||
|     anchor: if bottom {"north"} else {"base"} |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let get-size(par) = { |  | ||||||
|   let m = normalize-measure(par.display-name) |  | ||||||
|  |  | ||||||
|   let height = normalize-units(par.resolved-style.height) |  | ||||||
|   let width = height * 2 |  | ||||||
|   let spacing = normalize-units(par.resolved-style.spacing) |  | ||||||
|  |  | ||||||
|   return ( |  | ||||||
|     width: calc.max(m.width, width), |  | ||||||
|     height: height + spacing + m.height |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let default-style = ( |  | ||||||
|   height: 20pt, |  | ||||||
|   spacing: 5pt |  | ||||||
| ) |  | ||||||
| @@ -1,108 +0,0 @@ | |||||||
| #import "/src/cetz.typ": draw |  | ||||||
|  |  | ||||||
| #import "/src/core/utils.typ": normalize-measure, normalize-units |  | ||||||
|  |  | ||||||
| #let name = "collections" |  | ||||||
|  |  | ||||||
| #let normalize-offset(offset) = { |  | ||||||
|   let dx = 0pt |  | ||||||
|   let dy = 0pt |  | ||||||
|   if type(offset) == array { |  | ||||||
|     if offset.len() >= 2 { |  | ||||||
|       dx = offset.at(0) |  | ||||||
|       dy = offset.at(1) |  | ||||||
|     } |  | ||||||
|   } else if type(offset) == dictionary { |  | ||||||
|     dx = offset.at("dx", default: dx) |  | ||||||
|     dy = offset.at("dx", default: dy) |  | ||||||
|   } else { |  | ||||||
|     dx = offset |  | ||||||
|     dy = offset |  | ||||||
|   } |  | ||||||
|   return ( |  | ||||||
|     x: normalize-units(dx), |  | ||||||
|     y: normalize-units(dy) |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let render(x, y, p, bottom) = { |  | ||||||
|   let style = p.resolved-style |  | ||||||
|   let name = box( |  | ||||||
|     p.display-name, |  | ||||||
|     inset: style.inset, |  | ||||||
|     fill: style.fill, |  | ||||||
|     stroke: style.stroke |  | ||||||
|   ) |  | ||||||
|   let m = normalize-measure(name) |  | ||||||
|   let offset = normalize-offset(style.offset) |  | ||||||
|  |  | ||||||
|   let w = m.width |  | ||||||
|   let h = m.height |  | ||||||
|   let dx = offset.x |  | ||||||
|   let dy = offset.y |  | ||||||
|   let total-w = w + calc.abs(dx) |  | ||||||
|   let total-h = h + calc.abs(dy) |  | ||||||
|  |  | ||||||
|   let x0 = x - total-w / 2 |  | ||||||
|   let x1 = x0 + calc.abs(dx) |  | ||||||
|   let x3 = x0 + total-w |  | ||||||
|   let x2 = x3 - calc.abs(dx) |  | ||||||
|    |  | ||||||
|   let y0 = if bottom {y} else {y + total-h} |  | ||||||
|   let y1 = y0 - calc.abs(dy) |  | ||||||
|   let y3 = y0 - total-h |  | ||||||
|   let y2 = y3 + calc.abs(dy) |  | ||||||
|  |  | ||||||
|   let r1 = (x1, y0, x3, y2) |  | ||||||
|   let r2 = (x0, y1, x2, y3) |  | ||||||
|  |  | ||||||
|   if dx < 0 { |  | ||||||
|     r1.at(0) = x0 |  | ||||||
|     r1.at(2) = x2 |  | ||||||
|     r2.at(0) = x1 |  | ||||||
|     r2.at(2) = x3 |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if dy < 0 { |  | ||||||
|     r1.at(1) = y1 |  | ||||||
|     r1.at(3) = y3 |  | ||||||
|     r2.at(1) = y0 |  | ||||||
|     r2.at(3) = y2 |  | ||||||
|   } |  | ||||||
|   draw.rect( |  | ||||||
|     (r1.at(0), r1.at(1)), |  | ||||||
|     (r1.at(2), r1.at(3)), |  | ||||||
|     fill: style.fill, |  | ||||||
|     stroke: style.stroke |  | ||||||
|   ) |  | ||||||
|    |  | ||||||
|   draw.content( |  | ||||||
|     ( |  | ||||||
|       (r2.at(0) + r2.at(2)) / 2, |  | ||||||
|       (r2.at(1) + r2.at(3)) / 2 |  | ||||||
|     ), |  | ||||||
|     name, |  | ||||||
|     anchor: "mid" |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let get-size(par) = { |  | ||||||
|   let m = normalize-measure(box( |  | ||||||
|     par.display-name, |  | ||||||
|     inset: par.resolved-style.inset |  | ||||||
|   )) |  | ||||||
|    |  | ||||||
|   let offset = normalize-offset(par.resolved-style.offset) |  | ||||||
|   let dx = offset.x |  | ||||||
|   let dy = offset.y |  | ||||||
|  |  | ||||||
|   return ( |  | ||||||
|     width: m.width + calc.abs(dx), |  | ||||||
|     height: m.height + calc.abs(dy) |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let default-style = ( |  | ||||||
|   inset: (x: 3pt, y: 5pt), |  | ||||||
|   offset: (3pt, 3pt) |  | ||||||
| ) |  | ||||||
| @@ -1,54 +0,0 @@ | |||||||
| #import "/src/cetz.typ": draw |  | ||||||
|  |  | ||||||
| #import "/src/core/utils.typ": normalize-measure, normalize-units |  | ||||||
|  |  | ||||||
| #let name = "control" |  | ||||||
|  |  | ||||||
| #let render(x, y, p, bottom) = { |  | ||||||
|   let m = normalize-measure(p.display-name) |  | ||||||
|   let style = p.resolved-style |  | ||||||
|   let size = normalize-units(style.size) |  | ||||||
|   let spacing = normalize-units(style.spacing) |  | ||||||
|   let r = size / 2 |  | ||||||
|   let y0 = if bottom { |  | ||||||
|     y - m.height - spacing |  | ||||||
|   } else { |  | ||||||
|     y + m.height + size + spacing |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   draw.circle( |  | ||||||
|     (x, y0 - r), |  | ||||||
|     radius: r, |  | ||||||
|     fill: style.fill, |  | ||||||
|     stroke: style.stroke |  | ||||||
|   ) |  | ||||||
|   let s = stroke(style.stroke) |  | ||||||
|   draw.mark( |  | ||||||
|     (x, y0), (x - r / 2, y0), |  | ||||||
|     symbol: "stealth", |  | ||||||
|     fill: s.paint, |  | ||||||
|     stroke: s.paint |  | ||||||
|   ) |  | ||||||
|   draw.content( |  | ||||||
|     (x, y), |  | ||||||
|     p.display-name, |  | ||||||
|     anchor: if bottom {"north"} else {"base"} |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let get-size(par) = { |  | ||||||
|   let m = normalize-measure(par.display-name) |  | ||||||
|  |  | ||||||
|   let size = normalize-units(par.resolved-style.size) |  | ||||||
|   let spacing = normalize-units(par.resolved-style.spacing) |  | ||||||
|  |  | ||||||
|   return ( |  | ||||||
|     width: calc.max(m.width, size), |  | ||||||
|     height: size + spacing + m.height |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let default-style = ( |  | ||||||
|   size: 20pt, |  | ||||||
|   spacing: 5pt |  | ||||||
| ) |  | ||||||
| @@ -1,43 +0,0 @@ | |||||||
| #import "/src/cetz.typ": draw |  | ||||||
|  |  | ||||||
| #import "/src/core/utils.typ": normalize-measure, normalize-units |  | ||||||
|  |  | ||||||
| #let name = "custom" |  | ||||||
|  |  | ||||||
| #let render(x, y, p, bottom) = { |  | ||||||
|   let style = p.resolved-style |  | ||||||
|   let elmts = (style.image, p.display-name) |  | ||||||
|   if bottom { |  | ||||||
|     elmts = elmts.rev() |  | ||||||
|   } |  | ||||||
|   let shape = align( |  | ||||||
|     center, |  | ||||||
|     stack( |  | ||||||
|       dir: ttb, |  | ||||||
|       spacing: normalize-units(style.spacing) * 1pt, |  | ||||||
|       ..elmts |  | ||||||
|     ) |  | ||||||
|   ) |  | ||||||
|   let anchor = if bottom {"north"} else {"base"} |  | ||||||
|   draw.content( |  | ||||||
|     (x, y), |  | ||||||
|     shape, |  | ||||||
|     anchor: anchor |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let get-size(par) = { |  | ||||||
|   let name-m = normalize-measure(par.display-name) |  | ||||||
|   let img-m = normalize-measure(par.resolved-style.image) |  | ||||||
|   let spacing = normalize-units(par.resolved-style.spacing) |  | ||||||
|  |  | ||||||
|   return ( |  | ||||||
|     width: calc.max(name-m.width, img-m.width), |  | ||||||
|     height: name-m.height + spacing + img-m.height |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let default-style = ( |  | ||||||
|   image: none, |  | ||||||
|   spacing: 5pt |  | ||||||
| ) |  | ||||||
| @@ -1,64 +0,0 @@ | |||||||
| #import "/src/cetz.typ": draw |  | ||||||
|  |  | ||||||
| #import "/src/core/utils.typ": normalize-measure, normalize-units |  | ||||||
|  |  | ||||||
| #let name = "database" |  | ||||||
|  |  | ||||||
| #let render(x, y, p, bottom) = { |  | ||||||
|   let m = normalize-measure(p.display-name) |  | ||||||
|   let style = p.resolved-style |  | ||||||
|   let width = normalize-units(style.width) |  | ||||||
|   let spacing = normalize-units(style.spacing) |  | ||||||
|   let height = width * 4 / 3 |  | ||||||
|   let rx = width / 2 |  | ||||||
|   let ry = rx / 2 |  | ||||||
|   let y0 = if bottom { |  | ||||||
|     y - m.height - spacing |  | ||||||
|   } else { |  | ||||||
|     y + m.height + height + spacing |  | ||||||
|   } |  | ||||||
|   let y1 = y0 - height |  | ||||||
|  |  | ||||||
|   draw.merge-path( |  | ||||||
|     close: true, |  | ||||||
|     fill: style.fill, |  | ||||||
|     stroke: style.stroke, |  | ||||||
|     { |  | ||||||
|       draw.bezier((x - rx, y0 - ry), (x, y0), (x - rx, y0 - ry/2), (x - rx/2, y0)) |  | ||||||
|       draw.bezier((), (x + rx, y0 - ry), (x + rx/2, y0), (x + rx, y0 - ry/2)) |  | ||||||
|       draw.line((), (x + rx, y1 + ry)) |  | ||||||
|       draw.bezier((), (x, y1), (x + rx, y1 + ry/2), (x + rx/2, y1)) |  | ||||||
|       draw.bezier((), (x - rx, y1 + ry), (x - rx/2, y1), (x - rx, y1 + ry/2)) |  | ||||||
|     } |  | ||||||
|   ) |  | ||||||
|   draw.merge-path( |  | ||||||
|     stroke: style.stroke, |  | ||||||
|     { |  | ||||||
|       draw.bezier((x - rx, y0 - ry), (x, y0 - ry*2), (x - rx, y0 - 3*ry/2), (x - rx/2, y0 - ry*2)) |  | ||||||
|       draw.bezier((), (x + rx, y0 - ry), (x + rx/2, y0 - ry*2), (x + rx, y0 - 3*ry/2)) |  | ||||||
|     } |  | ||||||
|   ) |  | ||||||
|   draw.content( |  | ||||||
|     (x, y), |  | ||||||
|     p.display-name, |  | ||||||
|     anchor: if bottom {"north"} else {"base"} |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let get-size(par) = { |  | ||||||
|   let m = normalize-measure(par.display-name) |  | ||||||
|  |  | ||||||
|   let width = normalize-units(par.resolved-style.width) |  | ||||||
|   let height = width * 4 / 3 |  | ||||||
|   let spacing = normalize-units(par.resolved-style.spacing) |  | ||||||
|  |  | ||||||
|   return ( |  | ||||||
|     width: calc.max(m.width, width), |  | ||||||
|     height: height + spacing + m.height |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let default-style = ( |  | ||||||
|   width: 24pt, |  | ||||||
|   spacing: 5pt |  | ||||||
| ) |  | ||||||
| @@ -1,37 +0,0 @@ | |||||||
| #import "/src/cetz.typ": draw |  | ||||||
|  |  | ||||||
| #import "/src/core/utils.typ": normalize-measure |  | ||||||
|  |  | ||||||
| #let name = "participant" |  | ||||||
|  |  | ||||||
| #let render(x, y, p, bottom) = { |  | ||||||
|   let style = p.resolved-style |  | ||||||
|   let name = box( |  | ||||||
|     p.display-name, |  | ||||||
|     inset: style.inset, |  | ||||||
|     radius: style.radius, |  | ||||||
|     fill: style.fill, |  | ||||||
|     stroke: style.stroke |  | ||||||
|   ) |  | ||||||
|   let anchor = if bottom {"north"} else {"base"} |  | ||||||
|   draw.content( |  | ||||||
|     (x, y), |  | ||||||
|     name, |  | ||||||
|     anchor: anchor |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let get-size(par) = { |  | ||||||
|   return normalize-measure(box( |  | ||||||
|     par.display-name, |  | ||||||
|     inset: par.resolved-style.inset |  | ||||||
|   )) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let default-style = ( |  | ||||||
|   inset: ( |  | ||||||
|     x: 3pt, |  | ||||||
|     y: 5pt |  | ||||||
|   ), |  | ||||||
|   radius: 2pt |  | ||||||
| ) |  | ||||||
| @@ -1,55 +0,0 @@ | |||||||
| #import "/src/cetz.typ": draw |  | ||||||
|  |  | ||||||
| #import "/src/core/utils.typ": normalize-measure, normalize-units |  | ||||||
|  |  | ||||||
| #let name = "entity" |  | ||||||
|  |  | ||||||
| #let render(x, y, p, bottom) = { |  | ||||||
|   let m = normalize-measure(p.display-name) |  | ||||||
|   let style = p.resolved-style |  | ||||||
|   let size = normalize-units(style.size) |  | ||||||
|   let spacing = normalize-units(style.spacing) |  | ||||||
|   let gap = normalize-units(style.gap) |  | ||||||
|   let r = size / 2 |  | ||||||
|   let y0 = if bottom { |  | ||||||
|     y - m.height - spacing |  | ||||||
|   } else { |  | ||||||
|     y + m.height + size + spacing |  | ||||||
|   } |  | ||||||
|   let y1 = y0 - size - gap |  | ||||||
|  |  | ||||||
|   draw.circle( |  | ||||||
|     (x, y0 - r), |  | ||||||
|     radius: r, |  | ||||||
|     fill: style.fill, |  | ||||||
|     stroke: style.stroke |  | ||||||
|   ) |  | ||||||
|   draw.line( |  | ||||||
|     (x - r, y1), |  | ||||||
|     (x + r, y1), |  | ||||||
|     stroke: style.stroke |  | ||||||
|   ) |  | ||||||
|   draw.content( |  | ||||||
|     (x, y), |  | ||||||
|     p.display-name, |  | ||||||
|     anchor: if bottom {"north"} else {"base"} |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let get-size(par) = { |  | ||||||
|   let m = normalize-measure(par.display-name) |  | ||||||
|  |  | ||||||
|   let size = normalize-units(par.resolved-style.size) |  | ||||||
|   let spacing = normalize-units(par.resolved-style.spacing) |  | ||||||
|  |  | ||||||
|   return ( |  | ||||||
|     width: calc.max(m.width, size), |  | ||||||
|     height: size + spacing + m.height |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let default-style = ( |  | ||||||
|   size: 20pt, |  | ||||||
|   gap: 1.5pt, |  | ||||||
|   spacing: 5pt |  | ||||||
| ) |  | ||||||
| @@ -1,120 +0,0 @@ | |||||||
| #import "/src/cetz.typ": draw |  | ||||||
|  |  | ||||||
| #import "/src/core/utils.typ": normalize-measure |  | ||||||
|  |  | ||||||
| #let name = "queue" |  | ||||||
|  |  | ||||||
| #let render(x, y, p, bottom) = { |  | ||||||
|   let style = p.resolved-style |  | ||||||
|   let m = normalize-measure(box( |  | ||||||
|     p.display-name, |  | ||||||
|     inset: style.inset |  | ||||||
|   )) |  | ||||||
|    |  | ||||||
|   let w = m.width |  | ||||||
|   let h = m.height |  | ||||||
|   let ry = h / 2 |  | ||||||
|   let rx = ry / 2 |  | ||||||
|   let total-w = w + 3 * rx |  | ||||||
|  |  | ||||||
|   let y0 = if bottom {y} else {y + h} |  | ||||||
|   let y1 = y0 - h |  | ||||||
|   let ym = y0 - ry |  | ||||||
|  |  | ||||||
|   let xll = x - total-w / 2 |  | ||||||
|   let xrr = x + total-w / 2 |  | ||||||
|   let xlm = xll + rx |  | ||||||
|   let xrm = xrr - rx |  | ||||||
|   let xrl = xrm - rx |  | ||||||
|    |  | ||||||
|   /* |  | ||||||
|    /A----------/B\   --- y0 |  | ||||||
|    Fh          G C   --- ym |  | ||||||
|    \E----------\D/   --- y1 |  | ||||||
|    |  | ||||||
|    ||          ||| |  | ||||||
|    ||          ||\-- xrr |  | ||||||
|    ||          |\--- xrm |  | ||||||
|    ||          \---- xrl |  | ||||||
|    |\--------------- xlm |  | ||||||
|    \---------------- xll |  | ||||||
|    |  | ||||||
|   h <-> G == w |  | ||||||
|   F <-> h == rx |  | ||||||
|   G <-> C == 2 * rx == ry |  | ||||||
|   A <-> E == B <-> D == 2 * ry == h |  | ||||||
|   */ |  | ||||||
|  |  | ||||||
|   draw.merge-path( |  | ||||||
|     close: true, |  | ||||||
|     fill: style.fill, |  | ||||||
|     stroke: style.stroke, |  | ||||||
|     { |  | ||||||
|       draw.bezier( |  | ||||||
|         (xrm, y0), |  | ||||||
|         (xrr, ym), |  | ||||||
|         (xrm + rx/2, y0), |  | ||||||
|         (xrr, ym + ry/2) |  | ||||||
|       ) |  | ||||||
|       draw.bezier( |  | ||||||
|         (), |  | ||||||
|         (xrm, y1), |  | ||||||
|         (xrr, ym - ry/2), |  | ||||||
|         (xrm + rx/2, y1) |  | ||||||
|       ) |  | ||||||
|       draw.line((), (xlm, y1)) |  | ||||||
|       draw.bezier( |  | ||||||
|         (), |  | ||||||
|         (xll, ym), |  | ||||||
|         (xlm - rx/2, y1), |  | ||||||
|         (xll, ym - ry/2) |  | ||||||
|       ) |  | ||||||
|       draw.bezier( |  | ||||||
|         (), |  | ||||||
|         (xlm, y0), |  | ||||||
|         (xll, ym + ry/2), |  | ||||||
|         (xlm - rx/2, y0) |  | ||||||
|       ) |  | ||||||
|     } |  | ||||||
|   ) |  | ||||||
|   draw.merge-path( |  | ||||||
|     stroke: style.stroke, |  | ||||||
|     { |  | ||||||
|       draw.bezier( |  | ||||||
|         (xrm, y0), |  | ||||||
|         (xrl, ym), |  | ||||||
|         (xrm - rx/2, y0), |  | ||||||
|         (xrl, ym + ry/2) |  | ||||||
|       ) |  | ||||||
|       draw.bezier( |  | ||||||
|         (), |  | ||||||
|         (xrm, y1), |  | ||||||
|         (xrl, ym - ry/2), |  | ||||||
|         (xrm - rx/2, y1) |  | ||||||
|       ) |  | ||||||
|     } |  | ||||||
|   ) |  | ||||||
|   draw.content( |  | ||||||
|     ((xlm + xrl) / 2, ym), |  | ||||||
|     p.display-name, |  | ||||||
|     anchor: "mid" |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let get-size(par) = { |  | ||||||
|   let m = normalize-measure(box( |  | ||||||
|     par.display-name, |  | ||||||
|     inset: par.resolved-style.inset |  | ||||||
|   )) |  | ||||||
|  |  | ||||||
|   let rx = m.height / 4 |  | ||||||
|  |  | ||||||
|   return ( |  | ||||||
|     width: m.width + 3 * rx, |  | ||||||
|     height: m.height |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let default-style = ( |  | ||||||
|   inset: (x: 3pt, y: 5pt) |  | ||||||
| ) |  | ||||||
| @@ -1,65 +1,47 @@ | |||||||
| #import "/src/cetz.typ": draw, styles | #import "/src/cetz.typ": draw | ||||||
|  |  | ||||||
| #import "/src/consts.typ": * | #import "/src/consts.typ": * | ||||||
| #import "/src/core/utils.typ": get-ctx, normalize-units, set-y | #import "/src/core/utils.typ": get-ctx, set-ctx | ||||||
|  |  | ||||||
| #let separator-default-style = ( |  | ||||||
|   inset: ( |  | ||||||
|     x: 3pt, y: 5pt |  | ||||||
|   ), |  | ||||||
|   fill: auto, |  | ||||||
|   stroke: auto, |  | ||||||
|   gap: 3pt, |  | ||||||
|   outset: 20pt |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| #let render(sep) = get-ctx(ctx => { | #let render(sep) = get-ctx(ctx => { | ||||||
|   let style = styles.resolve( |   ctx.y -= Y-SPACE | ||||||
|     ctx.style, |  | ||||||
|     merge: sep.style, |  | ||||||
|     root: "separator", |  | ||||||
|     base: separator-default-style |  | ||||||
|   ) |  | ||||||
|   let gap = normalize-units(style.gap) |  | ||||||
|   let outset = normalize-units(style.outset) |  | ||||||
|  |  | ||||||
|   ctx.y -= ctx.style.y-space |   let x0 = ctx.x-pos.first() - 20 | ||||||
|  |   let x1 = ctx.x-pos.last() + 20 | ||||||
|   let x0 = ctx.x-pos.first() - outset |   let m = measure( | ||||||
|   let x1 = ctx.x-pos.last() + outset |     box( | ||||||
|   let name = box( |       sep.name, | ||||||
|     sep.name, |       inset: (left: 3pt, right: 3pt, top: 5pt, bottom: 5pt) | ||||||
|     inset: style.inset, |     ) | ||||||
|     stroke: style.stroke, |  | ||||||
|     fill: style.fill |  | ||||||
|   ) |   ) | ||||||
|   let m = measure(name) |  | ||||||
|   let w = m.width / 1pt |   let w = m.width / 1pt | ||||||
|   let h = m.height / 1pt |   let h = m.height / 1pt | ||||||
|   let cx = (x0 + x1) / 2 |   let cx = (x0 + x1) / 2 | ||||||
|   let xl = cx - w / 2 |   let xl = cx - w / 2 | ||||||
|   let xr = cx + w / 2 |   let xr = cx + w / 2 | ||||||
|  |  | ||||||
|   let y0 = ctx.y |   ctx.y -= h / 2 | ||||||
|   let y2 = y0 - h |  | ||||||
|   let y1 = (y0 + y2) / 2 |  | ||||||
|  |  | ||||||
|   let gap-y0 = y1 + gap / 2 |  | ||||||
|   let gap-y1 = gap-y0 - gap |  | ||||||
|  |  | ||||||
|   draw.rect( |   draw.rect( | ||||||
|     (x0, gap-y0), |     (x0, ctx.y), | ||||||
|     (x1, gap-y1), |     (x1, ctx.y - 3), | ||||||
|     stroke: none, |     stroke: none, | ||||||
|     fill: style.fill |     fill: white | ||||||
|   ) |   ) | ||||||
|   draw.line((x0, gap-y0), (x1, gap-y0)) |   draw.line((x0, ctx.y), (x1, ctx.y)) | ||||||
|   draw.line((x0, gap-y1), (x1, gap-y1)) |   ctx.y -= 3 | ||||||
|  |   draw.line((x0, ctx.y), (x1, ctx.y)) | ||||||
|   draw.content( |   draw.content( | ||||||
|     (cx, y1), |     ((x0 + x1) / 2, ctx.y + 1.5), | ||||||
|     name, |     sep.name, | ||||||
|     anchor: "mid" |     anchor: "center", | ||||||
|  |     padding: (5pt, 3pt), | ||||||
|  |     frame: "rect", | ||||||
|  |     fill: COL-SEP-NAME | ||||||
|   ) |   ) | ||||||
|  |   ctx.y -= h / 2 | ||||||
|  |  | ||||||
|   set-y(y2) |   set-ctx(c => { | ||||||
|  |     c.y = ctx.y | ||||||
|  |     return c | ||||||
|  |   }) | ||||||
| }) | }) | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| #import "/src/cetz.typ": draw, vector | #import "/src/cetz.typ": draw, vector, coordinate | ||||||
|  |  | ||||||
| #import "note.typ" | #import "note.typ" | ||||||
| #import "/src/consts.typ": * | #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-arrow-marks(sym, color) = { | #let get-arrow-marks(sym, color) = { | ||||||
|   if sym == none { |   if sym == none { | ||||||
| @@ -70,8 +70,7 @@ | |||||||
|   h = calc.max( |   h = calc.max( | ||||||
|     h, |     h, | ||||||
|     ..seq.linked-notes.map(n => { |     ..seq.linked-notes.map(n => { | ||||||
|       let nt = ctx.notes.at(n.id) |       note.get-size(n).height / 2 | ||||||
|       note.get-size(nt).height / 2 |  | ||||||
|     }) |     }) | ||||||
|   ) |   ) | ||||||
|   ctx.y -= h |   ctx.y -= h | ||||||
| @@ -103,23 +102,23 @@ | |||||||
|   if seq.disable-src { |   if seq.disable-src { | ||||||
|     let src-line = ctx.lifelines.at(i1) |     let src-line = ctx.lifelines.at(i1) | ||||||
|     src-line.level -= 1 |     src-line.level -= 1 | ||||||
|     src-line.events.push((type: "disable", y: start-info.y)) |     src-line.lines.push(("disable", start-info.y)) | ||||||
|     ctx.lifelines.at(i1) = src-line |     ctx.lifelines.at(i1) = src-line | ||||||
|   } |   } | ||||||
|   if seq.destroy-src { |   if seq.destroy-src { | ||||||
|     let src-line = ctx.lifelines.at(i1) |     let src-line = ctx.lifelines.at(i1) | ||||||
|     src-line.events.push((type: "destroy", y: start-info.y)) |     src-line.lines.push(("destroy", start-info.y)) | ||||||
|     ctx.lifelines.at(i1) = src-line |     ctx.lifelines.at(i1) = src-line | ||||||
|   } |   } | ||||||
|   if seq.disable-dst { |   if seq.disable-dst { | ||||||
|     let dst-line = ctx.lifelines.at(i2) |     let dst-line = ctx.lifelines.at(i2) | ||||||
|     dst-line.level -= 1 |     dst-line.level -= 1 | ||||||
|     dst-line.events.push((type: "disable", y: end-info.y)) |     dst-line.lines.push(("disable", end-info.y)) | ||||||
|     ctx.lifelines.at(i2) = dst-line |     ctx.lifelines.at(i2) = dst-line | ||||||
|   } |   } | ||||||
|   if seq.destroy-dst { |   if seq.destroy-dst { | ||||||
|     let dst-line = ctx.lifelines.at(i2) |     let dst-line = ctx.lifelines.at(i2) | ||||||
|     dst-line.events.push((type: "destroy", y: end-info.y)) |     dst-line.lines.push(("destroy", end-info.y)) | ||||||
|     ctx.lifelines.at(i2) = dst-line |     ctx.lifelines.at(i2) = dst-line | ||||||
|   } |   } | ||||||
|   if seq.enable-dst { |   if seq.enable-dst { | ||||||
| @@ -224,6 +223,11 @@ | |||||||
|       ).at(seq.comment-align) |       ).at(seq.comment-align) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     expand-parent-group( | ||||||
|  |       calc.min(x1, x2, x-mid), | ||||||
|  |       calc.max(x1, x2, x-mid) | ||||||
|  |     ) | ||||||
|  |  | ||||||
|   } else { |   } else { | ||||||
|     pts = ( |     pts = ( | ||||||
|       (x1, start-info.y), |       (x1, start-info.y), | ||||||
| @@ -262,6 +266,11 @@ | |||||||
|       (p1, p2) = (p2, p1) |       (p1, p2) = (p2, p1) | ||||||
|     } |     } | ||||||
|     comment-angle = vector.angle2(p1, p2) |     comment-angle = vector.angle2(p1, p2) | ||||||
|  |  | ||||||
|  |     expand-parent-group( | ||||||
|  |       calc.min(x1, x2), | ||||||
|  |       calc.max(x1, x2) | ||||||
|  |     ) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Start circle tip |   // Start circle tip | ||||||
| @@ -336,18 +345,33 @@ | |||||||
|       comment, |       comment, | ||||||
|       anchor: comment-anchor, |       anchor: comment-anchor, | ||||||
|       angle: comment-angle, |       angle: comment-angle, | ||||||
|       padding: 3pt |       padding: 3pt, | ||||||
|  |       name: "comment" | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|  |     // TODO: Improve this | ||||||
|  |     draw.get-ctx(c => { | ||||||
|  |       let (_, left, right) = coordinate.resolve( | ||||||
|  |         c, | ||||||
|  |         "comment.west", | ||||||
|  |         "comment.east" | ||||||
|  |       ) | ||||||
|  |       expand-parent-group( | ||||||
|  |         left.at(0), | ||||||
|  |         right.at(0) | ||||||
|  |       ) | ||||||
|  |     }) | ||||||
|  |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if seq.create-dst { |   if seq.create-dst { | ||||||
|     let dst-line = ctx.lifelines.at(i2) |     let dst-line = ctx.lifelines.at(i2) | ||||||
|     dst-line.events.push((type: "create", y: end-info.y)) |     dst-line.lines.push(("create", end-info.y)) | ||||||
|     ctx.lifelines.at(i2) = dst-line |     ctx.lifelines.at(i2) = dst-line | ||||||
|   } |   } | ||||||
|   if seq.enable-dst { |   if seq.enable-dst { | ||||||
|     let dst-line = ctx.lifelines.at(i2) |     let dst-line = ctx.lifelines.at(i2) | ||||||
|     dst-line.events.push((type: "enable", y: end-info.y, style: seq.lifeline-style)) |     dst-line.lines.push(("enable", end-info.y, seq.lifeline-style)) | ||||||
|     ctx.lifelines.at(i2) = dst-line |     ctx.lifelines.at(i2) = dst-line | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -355,8 +379,7 @@ | |||||||
|     end-info.y = calc.min( |     end-info.y = calc.min( | ||||||
|       end-info.y, |       end-info.y, | ||||||
|       y0 - calc.max(..seq.linked-notes.map(n => { |       y0 - calc.max(..seq.linked-notes.map(n => { | ||||||
|         let nt = ctx.notes.at(n.id) |         let m = note.get-size(n) | ||||||
|         let m = note.get-size(nt) |  | ||||||
|         return m.height / 2 |         return m.height / 2 | ||||||
|       })) |       })) | ||||||
|     ) |     ) | ||||||
|   | |||||||
| @@ -1,28 +1,19 @@ | |||||||
| #import "/src/cetz.typ": canvas, draw | #import "/src/cetz.typ": canvas, draw | ||||||
|  |  | ||||||
| #import "draw/note.typ" | #import "draw/note.typ": get-box as get-note-box, get-size as get-note-size | ||||||
| #import "draw/participant.typ" | #import "draw/participant.typ" | ||||||
| #import "utils.typ": * | #import "utils.typ": * | ||||||
| #import "/src/consts.typ": * | #import "/src/consts.typ": * | ||||||
|  |  | ||||||
| #let DEBUG-INVISIBLE = false | #let DEBUG-INVISIBLE = false | ||||||
|  |  | ||||||
| #let init-lifelines() = set-ctx(ctx => { | #let init-lifelines(participants) = { | ||||||
|   ctx.setup.participants = ctx.setup.participants.map(p => { |   return participants.map(p => { | ||||||
|     p.insert("lifeline-lvl", 0) |     p.insert("lifeline-lvl", 0) | ||||||
|     p.insert("max-lifelines", 0) |     p.insert("max-lifelines", 0) | ||||||
|     p |     p | ||||||
|   }) |   }) | ||||||
|   return ctx | } | ||||||
| }) |  | ||||||
|  |  | ||||||
| #let set-participants-i() = set-ctx(ctx => { |  | ||||||
|   ctx.setup.insert( |  | ||||||
|     "pars-i", |  | ||||||
|     get-participants-i(ctx.setup.participants) |  | ||||||
|   ) |  | ||||||
|   return ctx |  | ||||||
| }) |  | ||||||
|  |  | ||||||
| #let unwrap-syncs(elements) = { | #let unwrap-syncs(elements) = { | ||||||
|   let i = 0 |   let i = 0 | ||||||
| @@ -87,29 +78,28 @@ | |||||||
|   return participants |   return participants | ||||||
| } | } | ||||||
|  |  | ||||||
| #let note-get-cell(notes, pars-i, n) = { | #let note-get-cell(pars-i, note) = { | ||||||
|   let (p1, p2) = (none, none) |   let (p1, p2) = (none, none) | ||||||
|   let cell = none |   let cell = none | ||||||
|   if n.side == "left" { |   if note.side == "left" { | ||||||
|     p1 = n.pos2 |     p1 = note.pos2 | ||||||
|     p2 = n.pos |     p2 = note.pos | ||||||
|     cell = note.get-box(n) |     cell = get-note-box(note) | ||||||
|   } else if n.side == "right" { |   } else if note.side == "right" { | ||||||
|     p1 = n.pos |     p1 = note.pos | ||||||
|     p2 = n.pos2 |     p2 = note.pos2 | ||||||
|     cell = note.get-box(n) |     cell = get-note-box(note) | ||||||
|   } else if n.side == "over" and n.aligned-with != none { |   } else if note.side == "over" and note.aligned-with != none { | ||||||
|     let aligned-with = notes.at(n.aligned-with.id) |     let box1 = get-note-box(note) | ||||||
|     let box1 = note.get-box(n) |     let box2 = get-note-box(note.aligned-with) | ||||||
|     let box2 = note.get-box(aligned-with) |  | ||||||
|     let m1 = measure(box1) |     let m1 = measure(box1) | ||||||
|     let m2 = measure(box2) |     let m2 = measure(box2) | ||||||
|     cell = box( |     cell = box( | ||||||
|       width: (m1.width + m2.width) / 2, |       width: (m1.width + m2.width) / 2, | ||||||
|       height: calc.max(m1.height, m2.height) |       height: calc.max(m1.height, m2.height) | ||||||
|     ) |     ) | ||||||
|     p1 = n.pos |     p1 = note.pos | ||||||
|     p2 = n.aligned-with.pos |     p2 = note.aligned-with.pos | ||||||
|   } else { |   } else { | ||||||
|     return none |     return none | ||||||
|   } |   } | ||||||
| @@ -117,7 +107,7 @@ | |||||||
|   let i1 = pars-i.at(p1) |   let i1 = pars-i.at(p1) | ||||||
|   let i2 = pars-i.at(p2) |   let i2 = pars-i.at(p2) | ||||||
|   cell = ( |   cell = ( | ||||||
|     elmt: n, |     elmt: note, | ||||||
|     i1: calc.min(i1, i2), |     i1: calc.min(i1, i2), | ||||||
|     i2: calc.max(i1, i2), |     i2: calc.max(i1, i2), | ||||||
|     cell: cell |     cell: cell | ||||||
| @@ -126,7 +116,7 @@ | |||||||
|   return cell |   return cell | ||||||
| } | } | ||||||
|  |  | ||||||
| #let compute-max-lifeline-levels(participants, elements, notes, pars-i) = { | #let compute-max-lifeline-levels(participants, elements, pars-i) = { | ||||||
|   let cells = () |   let cells = () | ||||||
|   for elmt in elements { |   for elmt in elements { | ||||||
|     if elmt.type == "seq" { |     if elmt.type == "seq" { | ||||||
| @@ -145,14 +135,14 @@ | |||||||
|       ) |       ) | ||||||
|      |      | ||||||
|     } else if elmt.type == "note" { |     } else if elmt.type == "note" { | ||||||
|       let cell = note-get-cell(notes, pars-i, notes.at(elmt.id)) |       let cell = note-get-cell(pars-i, elmt) | ||||||
|       if cell != none { |       if cell != none { | ||||||
|         cells.push(cell) |         cells.push(cell) | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return (participants, cells) |   return (participants, elements, cells) | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Compute minimum widths for participant names and shapes | /// Compute minimum widths for participant names and shapes | ||||||
| @@ -165,18 +155,19 @@ | |||||||
|     let m2 = participant.get-size(p2) |     let m2 = participant.get-size(p2) | ||||||
|     let w1 = m1.width |     let w1 = m1.width | ||||||
|     let w2 = m2.width |     let w2 = m2.width | ||||||
|     widths.push(w1 / 2 + w2 / 2 + PAR-SPACE) |     widths.push(w1 / 2pt + w2 / 2pt + PAR-SPACE) | ||||||
|   } |   } | ||||||
|   return widths |   return widths | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Compute minimum width for over notes | /// Compute minimum width for over notes | ||||||
| #let notes-min-col-widths(notes, widths, pars-i) = { | #let notes-min-col-widths(elements, widths, pars-i) = { | ||||||
|   let widths = widths |   let widths = widths | ||||||
|  |   let notes = elements.filter(e => e.type == "note") | ||||||
|   for n in notes.filter(e => (e.side == "over" and  |   for n in notes.filter(e => (e.side == "over" and  | ||||||
|                               type(e.pos) == str)) { |                               type(e.pos) == str)) { | ||||||
|      |      | ||||||
|     let m = note.get-size(n) |     let m = get-note-size(n) | ||||||
|     let i = pars-i.at(n.pos) |     let i = pars-i.at(n.pos) | ||||||
|  |  | ||||||
|     if i < widths.len() { |     if i < widths.len() { | ||||||
| @@ -315,71 +306,56 @@ | |||||||
|   return widths |   return widths | ||||||
| } | } | ||||||
|  |  | ||||||
| #let compute-columns-width() = set-ctx(ctx => { | #let compute-columns-width(participants, elements, pars-i) = { | ||||||
|   let elements = ctx.setup.elements.filter(is-elmt) |   elements = elements.filter(is-elmt) | ||||||
|   let participants = ctx.setup.participants |  | ||||||
|   let notes = ctx.setup.notes |  | ||||||
|   let pars-i = ctx.setup.pars-i |  | ||||||
|   elements = unwrap-syncs(elements) |   elements = unwrap-syncs(elements) | ||||||
|  |  | ||||||
|   let cells |   let cells | ||||||
|   (participants, cells) = compute-max-lifeline-levels(participants, elements, notes, pars-i) |   (participants, elements, cells) = compute-max-lifeline-levels(participants, elements, pars-i) | ||||||
|  |  | ||||||
|   let widths = participants-min-col-widths(participants) |   let widths = participants-min-col-widths(participants) | ||||||
|   widths = notes-min-col-widths(notes, widths, pars-i) |   widths = notes-min-col-widths(elements, widths, pars-i) | ||||||
|   widths = simple-seq-min-col-widths(cells, widths) |   widths = simple-seq-min-col-widths(cells, widths) | ||||||
|   widths = self-seq-min-col-widths(cells, widths) |   widths = self-seq-min-col-widths(cells, widths) | ||||||
|   widths = long-seq-min-col-widths(participants, cells, widths) |   widths = long-seq-min-col-widths(participants, cells, widths) | ||||||
|   widths = col-widths-add-lifelines(participants, widths) |   widths = col-widths-add-lifelines(participants, widths) | ||||||
|   widths = process-col-elements(elements, widths, pars-i) |   widths = process-col-elements(elements, widths, pars-i) | ||||||
|  |   return widths | ||||||
|  | } | ||||||
|  |  | ||||||
|   let ctx = ctx | #let setup-ctx(participants, elements) = (ctx => { | ||||||
|   ctx.insert("widths", widths) |   let state = ctx.at("shared-state", default: (:)) | ||||||
|  |  | ||||||
|  |   let chronos-ctx = ( | ||||||
|  |     participants: init-lifelines(participants), | ||||||
|  |     pars-i: get-participants-i(participants), | ||||||
|  |     y: 0, | ||||||
|  |     groups: (), | ||||||
|  |     lifelines: participants.map(_ => ( | ||||||
|  |       level: 0, | ||||||
|  |       lines: () | ||||||
|  |     )) | ||||||
|  |   ) | ||||||
|  |   chronos-ctx.insert( | ||||||
|  |     "widths", | ||||||
|  |     compute-columns-width( | ||||||
|  |       chronos-ctx.participants, | ||||||
|  |       elements, | ||||||
|  |       chronos-ctx.pars-i | ||||||
|  |     ) | ||||||
|  |   ) | ||||||
|  |  | ||||||
|  |   // Compute each column's X position | ||||||
|   let x-pos = (0,) |   let x-pos = (0,) | ||||||
|   for width in widths { |   for width in chronos-ctx.widths { | ||||||
|     x-pos.push(x-pos.last() + width) |     x-pos.push(x-pos.last() + width) | ||||||
|   } |   } | ||||||
|   ctx.insert("x-pos", x-pos) |   chronos-ctx.insert("x-pos", x-pos) | ||||||
|  |   state.insert("chronos", chronos-ctx) | ||||||
|   return ctx |   ctx.shared-state = state | ||||||
| }) |   return ( | ||||||
|  |     ctx: ctx | ||||||
| #let setup-ctx(participants, elements, notes) = draw.get-ctx(_ => { |   ) | ||||||
|   set-ctx(c => { |  | ||||||
|     c.insert("participants", participants) |  | ||||||
|     c.insert("setup", ( |  | ||||||
|       elements: elements, |  | ||||||
|       participants: participants, |  | ||||||
|       notes: notes |  | ||||||
|     )) |  | ||||||
|     return c |  | ||||||
|   }) |  | ||||||
|  |  | ||||||
|   participant.pre-resolve-styles() |  | ||||||
|  |  | ||||||
|   init-lifelines() |  | ||||||
|   set-participants-i() |  | ||||||
|    |  | ||||||
|   note.pre-resolve-styles() |  | ||||||
|   compute-columns-width() |  | ||||||
|  |  | ||||||
|   set-ctx(c => { |  | ||||||
|     let setup = c.setup |  | ||||||
|     c += ( |  | ||||||
|       participants: setup.participants, |  | ||||||
|       pars-i: setup.pars-i, |  | ||||||
|       y: 0, |  | ||||||
|       groups: (), |  | ||||||
|       lifelines: setup.participants.map(_ => ( |  | ||||||
|         level: 0, |  | ||||||
|         events: () |  | ||||||
|       )), |  | ||||||
|       notes: setup.notes |  | ||||||
|     ) |  | ||||||
|     c.remove("setup") |  | ||||||
|     return c |  | ||||||
|   }) |  | ||||||
| },) | },) | ||||||
|  |  | ||||||
| #let render-debug() = get-ctx(ctx => { | #let render-debug() = get-ctx(ctx => { | ||||||
| @@ -400,14 +376,13 @@ | |||||||
|   } |   } | ||||||
| }) | }) | ||||||
|  |  | ||||||
| #let render(participants, elements, notes) = context canvas(length: 1pt, { | #let render(participants, elements) = context canvas(length: 1pt, { | ||||||
|   setup-ctx(participants, elements, notes) |   setup-ctx(participants, elements) | ||||||
|  |    | ||||||
|   // Draw participants (start) |   // Draw participants (start) | ||||||
|   get-ctx(ctx => { |   get-ctx(ctx => { | ||||||
|     for p in ctx.participants { |     for p in ctx.participants { | ||||||
|       let style = p.resolved-style |       if p.from-start and not p.invisible and p.show-top { | ||||||
|       if style.from-start and not p.invisible and style.show-top { |  | ||||||
|         (p.draw)(p) |         (p.draw)(p) | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -126,7 +126,6 @@ | |||||||
|     "linked", |     "linked", | ||||||
|     note.pos == none and note.side != "across" |     note.pos == none and note.side != "across" | ||||||
|   ) |   ) | ||||||
|   note.insert("id", ctx.notes.len()) |  | ||||||
|   let names = ctx.participants.map(p => p.name) |   let names = ctx.participants.map(p => p.name) | ||||||
|   if note.pos == none and note.side != "across" { |   if note.pos == none and note.side != "across" { | ||||||
|     let i1 = names.position(n => n == ctx.last-seq.p1) |     let i1 = names.position(n => n == ctx.last-seq.p1) | ||||||
| @@ -150,7 +149,6 @@ | |||||||
|     let n = ctx.last-note.note |     let n = ctx.last-note.note | ||||||
|     n.aligned-with = note |     n.aligned-with = note | ||||||
|     ctx.elmts.at(ctx.last-note.i) = n |     ctx.elmts.at(ctx.last-note.i) = n | ||||||
|     ctx.notes.at(ctx.last-note.note.id) = n |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if note.side in ("left", "right") { |   if note.side in ("left", "right") { | ||||||
| @@ -188,8 +186,6 @@ | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ctx.notes.push(note) |  | ||||||
|  |  | ||||||
|   ctx.elmts.at(ctx.i) = note |   ctx.elmts.at(ctx.i) = note | ||||||
|  |  | ||||||
|   ctx.last-note = ( |   ctx.last-note = ( | ||||||
| @@ -230,7 +226,6 @@ | |||||||
|     linked: (), |     linked: (), | ||||||
|     last-seq: none, |     last-seq: none, | ||||||
|     last-note: none, |     last-note: none, | ||||||
|     notes: (), |  | ||||||
|     participants: (), |     participants: (), | ||||||
|     elmts: elmts, |     elmts: elmts, | ||||||
|     i: 0 |     i: 0 | ||||||
| @@ -295,10 +290,10 @@ | |||||||
|     ).first()) |     ).first()) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return (ctx.elmts, participants, ctx.notes) |   return (ctx.elmts, participants) | ||||||
| } | } | ||||||
|  |  | ||||||
| #let finalize-setup(elmts, participants, notes) = { | #let finalize-setup(elmts, participants) = { | ||||||
|   for (i, p) in participants.enumerate() { |   for (i, p) in participants.enumerate() { | ||||||
|     p.insert("i", i) |     p.insert("i", i) | ||||||
|     participants.at(i) = p |     participants.at(i) = p | ||||||
| @@ -328,11 +323,14 @@ | |||||||
|     elmts.at(i).insert("max-i", max-i) |     elmts.at(i).insert("max-i", max-i) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return (elmts, participants, notes) |   return (elmts, participants) | ||||||
| } | } | ||||||
|  |  | ||||||
| #let setup(elements) = { | #let setup(elements) = { | ||||||
|   let (elmts, activation-history) = unwrap-containers(elements) |   let (elmts, activation-history) = unwrap-containers(elements) | ||||||
|   let (elmts, participants, notes) = prepare-participants(elmts) |    | ||||||
|   return finalize-setup(elmts, participants, notes) |   let participants | ||||||
|  |   (elmts, participants) = prepare-participants(elmts) | ||||||
|  |  | ||||||
|  |   return finalize-setup(elmts, participants) | ||||||
| } | } | ||||||
| @@ -1,6 +1,4 @@ | |||||||
| #import "/src/cetz.typ": draw, styles | #import "/src/cetz.typ": draw | ||||||
|  |  | ||||||
| #import "/src/consts.typ": default-style |  | ||||||
|  |  | ||||||
| #let is-elmt(elmt) = { | #let is-elmt(elmt) = { | ||||||
|   if type(elmt) != dictionary { |   if type(elmt) != dictionary { | ||||||
| @@ -21,15 +19,6 @@ | |||||||
|   } |   } | ||||||
|   panic("Unsupported type '" + str(type(value)) + "'") |   panic("Unsupported type '" + str(type(value)) + "'") | ||||||
| } | } | ||||||
|  |  | ||||||
| #let normalize-measure(body) = { |  | ||||||
|   let m = measure(body) |  | ||||||
|   return ( |  | ||||||
|     width: normalize-units(m.width), |  | ||||||
|     height: normalize-units(m.height) |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let get-participants-i(participants) = { | #let get-participants-i(participants) = { | ||||||
|   let pars-i = (:) |   let pars-i = (:) | ||||||
|   for (i, p) in participants.enumerate() { |   for (i, p) in participants.enumerate() { | ||||||
| @@ -44,9 +33,6 @@ | |||||||
|   let pars-i = get-participants-i(participants) |   let pars-i = get-participants-i(participants) | ||||||
|  |  | ||||||
|   for elmt in group.elmts { |   for elmt in group.elmts { | ||||||
|     if not is-elmt(elmt) { |  | ||||||
|       continue |  | ||||||
|     } |  | ||||||
|     if elmt.type == "seq" { |     if elmt.type == "seq" { | ||||||
|       let i1 = pars-i.at(elmt.p1) |       let i1 = pars-i.at(elmt.p1) | ||||||
|       let i2 = pars-i.at(elmt.p2) |       let i2 = pars-i.at(elmt.p2) | ||||||
| @@ -107,41 +93,19 @@ | |||||||
|   ) |   ) | ||||||
| }) | }) | ||||||
|  |  | ||||||
| #let extract-ctx(cetz-ctx, with-style: false) = { |  | ||||||
|   let state = cetz-ctx.at("shared-state", default: (:)) |  | ||||||
|   let ctx = state.at("chronos", default: (:)) |  | ||||||
|   if with-style { |  | ||||||
|     ctx.style = styles.resolve( |  | ||||||
|       cetz-ctx.style, |  | ||||||
|       root: "chronos", |  | ||||||
|       base: default-style |  | ||||||
|     ) |  | ||||||
|     // Normalize because it is used very frequently |  | ||||||
|     ctx.style.y-space = normalize-units(ctx.style.y-space) |  | ||||||
|   } |  | ||||||
|   return ctx |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #let set-ctx(func) = draw.set-ctx(c => { | #let set-ctx(func) = draw.set-ctx(c => { | ||||||
|   let ctx = extract-ctx(c) |   let ctx = c.shared-state.chronos | ||||||
|   let new-ctx = func(ctx) |   let new-ctx = func(ctx) | ||||||
|   assert(new-ctx != none, message: "set-ctx must return a context!") |   assert(new-ctx != none, message: "set-ctx must return a context!") | ||||||
|   let state = c.at("shared-state", default: (:)) |   c.shared-state.chronos = new-ctx | ||||||
|   state.chronos = new-ctx |  | ||||||
|   c.shared-state = state |  | ||||||
|   return c |   return c | ||||||
| }) | }) | ||||||
|  |  | ||||||
| #let get-ctx(func) = draw.get-ctx(c => { | #let get-ctx(func) = draw.get-ctx(c => { | ||||||
|   let ctx = extract-ctx(c, with-style: true) |   let ctx = c.shared-state.chronos | ||||||
|   func(ctx) |   func(ctx) | ||||||
| }) | }) | ||||||
|  |  | ||||||
| #let set-y(new-y) = set-ctx(c => { |  | ||||||
|   c.y = new-y |  | ||||||
|   return c |  | ||||||
| }) |  | ||||||
|  |  | ||||||
| #let expand-parent-group(x0, x1) = set-ctx(ctx => { | #let expand-parent-group(x0, x1) = set-ctx(ctx => { | ||||||
|   if ctx.groups.len() != 0 { |   if ctx.groups.len() != 0 { | ||||||
|     let group = ctx.groups.last() |     let group = ctx.groups.last() | ||||||
| @@ -150,4 +114,4 @@ | |||||||
|     ctx.groups.last() = group |     ctx.groups.last() = group | ||||||
|   } |   } | ||||||
|   return ctx |   return ctx | ||||||
| }) | }) | ||||||
| @@ -1,15 +1,16 @@ | |||||||
|  | #import "core/draw/event.typ": render as evt-render | ||||||
| #import "core/renderer.typ": render | #import "core/renderer.typ": render | ||||||
| #import "core/setup.typ": setup | #import "core/setup.typ": setup | ||||||
| #import "core/utils.typ": fit-canvas | #import "core/utils.typ": fit-canvas, set-ctx | ||||||
|  |  | ||||||
| #let diagram(elements, width: auto) = { | #let diagram(elements, width: auto) = { | ||||||
|   if elements == none { |   if elements == none { | ||||||
|     return |     return | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   let (elmts, participants, notes) = setup(elements) |   let (elmts, participants) = setup(elements) | ||||||
|  |  | ||||||
|   let canvas = render(participants, elmts, notes) |   let canvas = render(participants, elmts) | ||||||
|   fit-canvas(canvas, width: width) |   fit-canvas(canvas, width: width) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,25 +1,23 @@ | |||||||
| #import "core/draw/group.typ" | #import "core/draw/group.typ" | ||||||
|  |  | ||||||
| #let _grp(name, desc: none, type: "default", elmts, ..style) = { | #let _grp(name, desc: none, type: "default", elmts) = { | ||||||
|   return (( |   return (( | ||||||
|     type: "grp", |     type: "grp", | ||||||
|     draw: group.render-start, |     draw: group.render-start, | ||||||
|     name: name, |     name: name, | ||||||
|     desc: desc, |     desc: desc, | ||||||
|     grp-type: type, |     grp-type: type, | ||||||
|     elmts: elmts, |     elmts: elmts | ||||||
|     style: style.named() |  | ||||||
|   ),) |   ),) | ||||||
| } | } | ||||||
|  |  | ||||||
| #let _alt(desc, elmts, ..elses-style) = { | #let _alt(desc, elmts, ..args) = { | ||||||
|   let all-elmts = () |   let all-elmts = () | ||||||
|   all-elmts += elmts |   all-elmts += elmts | ||||||
|   let elses = elses-style.pos() |   let args = args.pos() | ||||||
|   let style = elses-style.named() |   for i in range(0, args.len(), step: 2) { | ||||||
|   for i in range(0, elses.len(), step: 2) { |     let else-desc = args.at(i) | ||||||
|     let else-desc = elses.at(i) |     let else-elmts = args.at(i + 1, default: ()) | ||||||
|     let else-elmts = elses.at(i + 1, default: ()) |  | ||||||
|     all-elmts.push(( |     all-elmts.push(( | ||||||
|       type: "else", |       type: "else", | ||||||
|       draw: group.render-else, |       draw: group.render-else, | ||||||
| @@ -28,10 +26,10 @@ | |||||||
|     all-elmts += else-elmts |     all-elmts += else-elmts | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return _grp("alt", desc: desc, type: "alt", all-elmts, ..style) |   return _grp("alt", desc: desc, type: "alt", all-elmts) | ||||||
| } | } | ||||||
|  |  | ||||||
| #let _loop(desc, min: none, max: auto, elmts, ..style) = { | #let _loop(desc, min: none, max: auto, elmts) = { | ||||||
|   let name = "loop" |   let name = "loop" | ||||||
|   if min != none { |   if min != none { | ||||||
|     if max == auto { |     if max == auto { | ||||||
| @@ -39,7 +37,7 @@ | |||||||
|     } |     } | ||||||
|     name += "(" + str(min) + "," + str(max) + ")" |     name += "(" + str(min) + "," + str(max) + ")" | ||||||
|   } |   } | ||||||
|   _grp(name, desc: desc, type: "loop", elmts, ..style) |   _grp(name, desc: desc, type: "loop", elmts) | ||||||
| } | } | ||||||
| #let _opt(desc, elmts, ..style) = _grp("opt", desc: desc, type: "opt", elmts, ..style.named()) | #let _opt(desc, elmts) = _grp("opt", desc: desc, type: "opt", elmts) | ||||||
| #let _break(desc, elmts, ..style) = _grp("break", desc: desc, type: "break", elmts, ..style.named()) | #let _break(desc, elmts) = _grp("break", desc: desc, type: "break", elmts) | ||||||
|   | |||||||
							
								
								
									
										37
									
								
								src/misc.typ
									
									
									
									
									
								
							
							
						
						| @@ -1,26 +1,23 @@ | |||||||
| #import "cetz.typ": styles |  | ||||||
|  |  | ||||||
| #import "core/draw/delay.typ" | #import "core/draw/delay.typ" | ||||||
| #import "core/draw/event.typ": render as evt-render | #import "core/draw/event.typ": render as evt-render | ||||||
| #import "core/draw/separator.typ" | #import "core/draw/separator.typ" | ||||||
| #import "core/draw/sync.typ" | #import "core/draw/sync.typ" | ||||||
| #import "core/utils.typ": get-ctx, normalize-units, set-y | #import "core/utils.typ": set-ctx | ||||||
|  |  | ||||||
| #let _sep(name, ..style) = { | #let _sep(name) = { | ||||||
|   return (( |   return (( | ||||||
|     type: "sep", |     type: "sep", | ||||||
|     draw: separator.render, |     draw: separator.render, | ||||||
|     name: name, |     name: name | ||||||
|     style: style.named() |  | ||||||
|   ),) |   ),) | ||||||
| } | } | ||||||
|  |  | ||||||
| #let _delay(name: none, ..style) = { | #let _delay(name: none, size: 30) = { | ||||||
|   return (( |   return (( | ||||||
|     type: "delay", |     type: "delay", | ||||||
|     draw: delay.render, |     draw: delay.render, | ||||||
|     name: name, |     name: name, | ||||||
|     style: style.named() |     size: size | ||||||
|   ),) |   ),) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -32,36 +29,26 @@ | |||||||
|   ),) |   ),) | ||||||
| } | } | ||||||
|  |  | ||||||
| #let gap-default-style = ( | #let gap-render(gap) = set-ctx(ctx => { | ||||||
|   size: 20pt |   ctx.y -= gap.size | ||||||
| ) |   return ctx | ||||||
|  |  | ||||||
| #let gap-render(gap) = get-ctx(ctx => { |  | ||||||
|   let style = styles.resolve( |  | ||||||
|     ctx.style, |  | ||||||
|     merge: gap.style, |  | ||||||
|     root: "gap", |  | ||||||
|     base: gap-default-style |  | ||||||
|   ) |  | ||||||
|   let size = normalize-units(style.size) |  | ||||||
|   set-y(ctx.y - size) |  | ||||||
| }) | }) | ||||||
|  |  | ||||||
| #let _gap(..style) = { | #let _gap(size: 20) = { | ||||||
|   return (( |   return (( | ||||||
|     type: "gap", |     type: "gap", | ||||||
|     draw: gap-render, |     draw: gap-render, | ||||||
|     style: style.named() |     size: size | ||||||
|   ),) |   ),) | ||||||
| } | } | ||||||
|  |  | ||||||
| #let _evt(participant, event, ..style) = { | #let _evt(participant, event) = { | ||||||
|   return (( |   return (( | ||||||
|     type: "evt", |     type: "evt", | ||||||
|     draw: evt-render, |     draw: evt-render, | ||||||
|     participant: participant, |     participant: participant, | ||||||
|     event: event, |     event: event, | ||||||
|     style: style.named() |     lifeline-style: auto | ||||||
|   ),) |   ),) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								src/note.typ
									
									
									
									
									
								
							
							
						
						| @@ -18,9 +18,10 @@ | |||||||
|   side, |   side, | ||||||
|   content, |   content, | ||||||
|   pos: none, |   pos: none, | ||||||
|  |   color: COL-NOTE, | ||||||
|  |   shape: "default", | ||||||
|   aligned: false, |   aligned: false, | ||||||
|   allow-overlap: true, |   allow-overlap: true | ||||||
|   ..style |  | ||||||
| ) = { | ) = { | ||||||
|   if side == "over" { |   if side == "over" { | ||||||
|     if pos == none { |     if pos == none { | ||||||
| @@ -32,15 +33,19 @@ | |||||||
|       panic("Aligned notes can only be over a participant (got side '" + side + "')") |       panic("Aligned notes can only be over a participant (got side '" + side + "')") | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |   if color == auto { | ||||||
|  |     color = COL-NOTE | ||||||
|  |   } | ||||||
|   return (( |   return (( | ||||||
|     type: "note", |     type: "note", | ||||||
|     draw: note.render, |     draw: note.render, | ||||||
|     side: side, |     side: side, | ||||||
|     content: content, |     content: content, | ||||||
|     pos: pos, |     pos: pos, | ||||||
|  |     color: color, | ||||||
|  |     shape: shape, | ||||||
|     aligned: aligned, |     aligned: aligned, | ||||||
|     aligned-with: none, |     aligned-with: none, | ||||||
|     allow-overlap: allow-overlap, |     allow-overlap: allow-overlap | ||||||
|     style: style.named() |  | ||||||
|   ),) |   ),) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -17,16 +17,35 @@ | |||||||
| #let _par( | #let _par( | ||||||
|   name, |   name, | ||||||
|   display-name: auto, |   display-name: auto, | ||||||
|  |   from-start: true, | ||||||
|   invisible: false, |   invisible: false, | ||||||
|   ..style |   shape: "participant", | ||||||
|  |   color: DEFAULT-COLOR, | ||||||
|  |   line-stroke: ( | ||||||
|  |     dash: "dashed", | ||||||
|  |     paint: gray.darken(40%), | ||||||
|  |     thickness: .5pt | ||||||
|  |   ), | ||||||
|  |   custom-image: none, | ||||||
|  |   show-bottom: true, | ||||||
|  |   show-top: true, | ||||||
| ) = { | ) = { | ||||||
|  |   if color == auto { | ||||||
|  |     color = DEFAULT-COLOR | ||||||
|  |   } | ||||||
|   return (( |   return (( | ||||||
|     type: "par", |     type: "par", | ||||||
|     draw: participant.render, |     draw: participant.render, | ||||||
|     name: name, |     name: name, | ||||||
|     display-name: if display-name == auto {name} else {display-name}, |     display-name: if display-name == auto {name} else {display-name}, | ||||||
|  |     from-start: from-start, | ||||||
|     invisible: invisible, |     invisible: invisible, | ||||||
|     style: style.named() |     shape: shape, | ||||||
|  |     color: color, | ||||||
|  |     line-stroke: line-stroke, | ||||||
|  |     custom-image: custom-image, | ||||||
|  |     show-bottom: show-bottom, | ||||||
|  |     show-top: show-top | ||||||
|   ),) |   ),) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,9 +5,9 @@ | |||||||
|   _par("a", display-name: "Alice") |   _par("a", display-name: "Alice") | ||||||
|   _par("b", display-name: "Bob") |   _par("b", display-name: "Bob") | ||||||
|  |  | ||||||
|   _note("left", [This is displayed\ left of Alice.], pos: "a", fill: rgb("#00FFFF")) |   _note("left", [This is displayed\ left of Alice.], pos: "a", color: rgb("#00FFFF")) | ||||||
|   _note("right", [This is displayed right of Alice.], pos: "a") |   _note("right", [This is displayed right of Alice.], pos: "a") | ||||||
|   _note("over", [This is displayed over Alice.], pos: "a") |   _note("over", [This is displayed over Alice.], pos: "a") | ||||||
|   _note("over", [This is displayed\ over Bob and Alice.], pos: ("a", "b"), fill: rgb("#FFAAAA")) |   _note("over", [This is displayed\ over Bob and Alice.], pos: ("a", "b"), color: rgb("#FFAAAA")) | ||||||
|   _note("over", [This is yet another\ example of\ a long note.], pos: ("a", "b")) |   _note("over", [This is yet another\ example of\ a long note.], pos: ("a", "b")) | ||||||
| }) | }) | ||||||
| Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 102 KiB | 
| @@ -15,7 +15,7 @@ | |||||||
|   _par("Foo5", display-name: "Database", shape: "database") |   _par("Foo5", display-name: "Database", shape: "database") | ||||||
|   _par("Foo6", display-name: "Collections", shape: "collections") |   _par("Foo6", display-name: "Collections", shape: "collections") | ||||||
|   _par("Foo7", display-name: "Queue", shape: "queue") |   _par("Foo7", display-name: "Queue", shape: "queue") | ||||||
|   _par("Foo8", display-name: "Typst", shape: "custom", image: TYPST) |   _par("Foo8", display-name: "Typst", shape: "custom", custom-image: TYPST) | ||||||
|   _par("Foo9", display-name: "Ferris", shape: "custom", image: FERRIS) |   _par("Foo9", display-name: "Ferris", shape: "custom", custom-image: FERRIS) | ||||||
|   _par("Foo10", display-name: "Baryhobal", shape: "custom", image: ME) |   _par("Foo10", display-name: "Baryhobal", shape: "custom", custom-image: ME) | ||||||
| }) | }) | ||||||
							
								
								
									
										4
									
								
								tests/special-group/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | |||||||
|  | # generated by tytanic, do not edit | ||||||
|  |  | ||||||
|  | diff/** | ||||||
|  | out/** | ||||||
							
								
								
									
										
											BIN
										
									
								
								tests/special-group/ref/1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 12 KiB | 
							
								
								
									
										
											BIN
										
									
								
								tests/special-group/ref/2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 11 KiB | 
							
								
								
									
										
											BIN
										
									
								
								tests/special-group/ref/3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 17 KiB | 
							
								
								
									
										
											BIN
										
									
								
								tests/special-group/ref/4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 8.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								tests/special-group/ref/5.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 8.3 KiB | 
							
								
								
									
										71
									
								
								tests/special-group/test.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,71 @@ | |||||||
|  | #set page(width: auto, height: auto) | ||||||
|  | #import "/src/lib.typ": * | ||||||
|  |  | ||||||
|  | #let preamble = { | ||||||
|  |   _par("a", display-name: [Alice]) | ||||||
|  |   _par("b", display-name: [Bob]) | ||||||
|  |   _col("a", "b", width: 2cm) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #diagram({ | ||||||
|  |   preamble | ||||||
|  |   _grp("Group 1", { | ||||||
|  |     _seq("a", "b") | ||||||
|  |   }) | ||||||
|  |   _grp("Group 2", desc: [Description], { | ||||||
|  |     _seq("a", "b") | ||||||
|  |   }) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | #pagebreak() | ||||||
|  |  | ||||||
|  | #diagram({ | ||||||
|  |   preamble | ||||||
|  |   _alt( | ||||||
|  |     "case 1", { | ||||||
|  |       _seq("a", "b") | ||||||
|  |     }, | ||||||
|  |     "case 2", { | ||||||
|  |       _seq("a", "b") | ||||||
|  |     }, | ||||||
|  |     "case 3", { | ||||||
|  |       _seq("a", "b") | ||||||
|  |     } | ||||||
|  |   ) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | #pagebreak() | ||||||
|  |  | ||||||
|  | #diagram({ | ||||||
|  |   preamble | ||||||
|  |   _loop("loop 1", { | ||||||
|  |     _seq("a", "b") | ||||||
|  |   }) | ||||||
|  |   _loop("loop 2", min: 1, { | ||||||
|  |     _seq("a", "b") | ||||||
|  |   }) | ||||||
|  |   _loop("loop 3", max: 10, { | ||||||
|  |     _seq("a", "b") | ||||||
|  |   }) | ||||||
|  |   _loop("loop 3", min: 1, max: 10, { | ||||||
|  |     _seq("a", "b") | ||||||
|  |   }) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | #pagebreak() | ||||||
|  |  | ||||||
|  | #diagram({ | ||||||
|  |   preamble | ||||||
|  |   _opt("Optional", { | ||||||
|  |     _seq("a", "b") | ||||||
|  |   }) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | #pagebreak() | ||||||
|  |  | ||||||
|  | #diagram({ | ||||||
|  |   preamble | ||||||
|  |   _break("Break", { | ||||||
|  |     _seq("a", "b") | ||||||
|  |   }) | ||||||
|  | }) | ||||||