diff --git a/gallery/example1.pdf b/gallery/example1.pdf new file mode 100644 index 0000000..30c3a09 Binary files /dev/null and b/gallery/example1.pdf differ diff --git a/gallery/example1.typ b/gallery/example1.typ new file mode 100644 index 0000000..5ee06c8 --- /dev/null +++ b/gallery/example1.typ @@ -0,0 +1,19 @@ +#import "/src/lib.typ" as chronos + +#chronos.from-plantuml(```plantuml +Alice -> Bob: Authentication Request +Bob --> Alice: Authentication Response + +Alice -> Bob: Another authentication Request +Alice <-- Bob: Another authentication Response +```) + + +#chronos.diagram({ + import "/src/diagram.typ": * + _seq("Alice", "Bob", comment: "Authentication Request") + _seq("Bob", "Alice", comment: "Authentication Response", style: "dashed") + + _seq("Alice", "Bob", comment: "Another authentication Request") + _seq("Bob", "Alice", comment: "Another authentication Response", style: "dashed") +}) \ No newline at end of file diff --git a/src/diagram.typ b/src/diagram.typ new file mode 100644 index 0000000..75d7241 --- /dev/null +++ b/src/diagram.typ @@ -0,0 +1,51 @@ +#import "renderer.typ": render + +#let _seq(p1, p2, comment: none, style: auto) = { + return (( + type: "seq", + p1: p1, + p2: p2, + comment: comment, + style: style + ),) +} + +#let _par(name, display-name: auto, start-at: 0) = { + return (( + type: "par", + name: name, + display-name: if display-name == auto {name} else {display-name}, + start-at: start-at + ),) +} + +#let _par-exists(participants, name) = { + for p in participants { + if name == p.name { + return true + } + } + return false +} + +#let diagram(elements) = { + let participants = () + for elmt in elements { + if elmt.type == "par" { + participants.push(elmt) + } else if elmt.type == "seq" { + if not _par-exists(participants, elmt.p1) { + participants.push(_par(elmt.p1).first()) + } + if not _par-exists(participants, elmt.p2) { + participants.push(_par(elmt.p2).first()) + } + } + } + + render(participants, elements) +} + +#let from-plantuml(code) = { + let code = code.text +} \ No newline at end of file diff --git a/src/lib.typ b/src/lib.typ new file mode 100644 index 0000000..2f20eb6 --- /dev/null +++ b/src/lib.typ @@ -0,0 +1 @@ +#import "diagram.typ": diagram, from-plantuml \ No newline at end of file diff --git a/src/renderer.typ b/src/renderer.typ new file mode 100644 index 0000000..849d8ad --- /dev/null +++ b/src/renderer.typ @@ -0,0 +1,108 @@ +#import "@preview/cetz:0.2.2": canvas, draw + +#let X-SPACE = 2 +#let Y-SPACE = 30 + +#let get-participants-i(participants) = { + let pars-i = (:) + for (i, p) in participants.enumerate() { + pars-i.insert(p.name, i) + } + return pars-i +} + +#let get-columns-width(participants, elements) = { + let pars-i = get-participants-i(participants) + let cells = () + for elmt in elements { + if elmt.type == "seq" { + let com = if elmt.comment == none {""} else {elmt.comment} + let i1 = pars-i.at(elmt.p1) + let i2 = pars-i.at(elmt.p2) + cells.push( + ( + elmt: elmt, + i1: calc.min(i1, i2), + i2: calc.max(i1, i2), + cell: box(com, inset: 3pt) + ) + ) + } + } + + let widths = participants.slice(0, -1).map(_ => 0) + for cell in cells.filter(c => c.i2 - c.i1 == 1) { + let m = measure(cell.cell) + widths.at(cell.i1) = calc.max( + widths.at(cell.i1), + m.width / 1pt + ) + } + return widths +} + +#let render(participants, elements) = context canvas(length: 1pt, { + let pars-i = get-participants-i(participants) + + let widths = get-columns-width(participants, elements) + + let x-pos = (0,) + for width in widths { + x-pos.push(x-pos.last() + width) + } + + // Draw participants + for (i, p) in participants.enumerate() { + draw.content( + (x-pos.at(i), 0), + p.display-name, + name: p.name, + frame: "rect", + padding: (5pt, 3pt), + anchor: "south" + ) + } + + let y = -Y-SPACE + // Draw sequences + for elmt in elements { + if elmt.type == "seq" { + let x1 = x-pos.at(pars-i.at(elmt.p1)) + let x2 = x-pos.at(pars-i.at(elmt.p2)) + draw.line( + (x1, y), + (x2, y), + mark: (end: "straight") + ) + if elmt.comment != none { + draw.content( + (calc.min(x1, x2), y), + elmt.comment, + anchor: "south-west", + padding: 3pt + ) + } + y -= Y-SPACE + } + } + + // Draw vertical lines + end participants + draw.on-layer(-1, { + for (i, p) in participants.enumerate() { + let x = x-pos.at(i) + draw.line( + (x, 0), + (x, y), + stroke: (dash: "dashed", paint: gray.darken(40%)) + ) + draw.content( + (x, y), + p.display-name, + name: p.name, + frame: "rect", + padding: (5pt, 3pt), + anchor: "north" + ) + } + }) +}) \ No newline at end of file diff --git a/typst.toml b/typst.toml new file mode 100644 index 0000000..e721a3c --- /dev/null +++ b/typst.toml @@ -0,0 +1,14 @@ +[package] +name = "chronos" +version = "0.0.1" +compiler = "0.11.0" +repository = "https://git.kb28.ch/HEL/chronos" +entrypoint = "src/lib.typ" +authors = [ + "Louis Heredero " +] +categories = ["visualization"] +license = "Apache-2.0" +description = "A package to draw sequence diagrams with CeTZ" +keywords = ["sequence", "diagram", "plantuml"] +exclude = [ "/gallery/*" ]