532 lines
11 KiB
Markdown
532 lines
11 KiB
Markdown
# Visualization & Diagrams
|
|
|
|
Three levels of visualization in Typst, all mappable to hiccup.
|
|
|
|
---
|
|
|
|
## 1. Built-in Shapes
|
|
|
|
Typst has basic shapes built-in. Simple and direct.
|
|
|
|
### Rectangle
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:rect {:width "2cm" :height "1cm" :fill "blue"} "Label"]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
#rect(width: 2cm, height: 1cm, fill: blue)[Label]
|
|
```
|
|
|
|
### Square
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:square {:size "1cm" :stroke "2pt + red"}]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
#square(size: 1cm, stroke: 2pt + red)
|
|
```
|
|
|
|
### Circle
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:circle {:radius "0.5cm" :fill "green"}]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
#circle(radius: 0.5cm, fill: green)
|
|
```
|
|
|
|
### Ellipse
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:ellipse {:width "2cm" :height "1cm"}]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
#ellipse(width: 2cm, height: 1cm)
|
|
```
|
|
|
|
### Polygon
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:polygon {:fill "yellow"} [0 0] [1 0] [0.5 1]]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
#polygon(fill: yellow, (0pt, 0pt), (1pt, 0pt), (0.5pt, 1pt))
|
|
```
|
|
|
|
### Line
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:line {:start [0 0] :end [100 50] :stroke "2pt"}]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
#line(start: (0pt, 0pt), end: (100pt, 50pt), stroke: 2pt)
|
|
```
|
|
|
|
### Path
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:path {:closed true :fill "blue"} [0 0] [10 0] [10 10] [0 10]]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
#path(closed: true, fill: blue, (0pt, 0pt), (10pt, 0pt), (10pt, 10pt), (0pt, 10pt))
|
|
```
|
|
|
|
### Curve (Bézier)
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:curve
|
|
[:move [0 0]]
|
|
[:cubic [10 20] [30 20] [40 0]]]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
#curve(
|
|
curve.move((0pt, 0pt)),
|
|
curve.cubic((10pt, 20pt), (30pt, 20pt), (40pt, 0pt)),
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## 2. CeTZ - Canvas Drawing
|
|
|
|
For complex custom drawings. Like TikZ for LaTeX.
|
|
|
|
### Basic Canvas
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:cetz/canvas
|
|
[:cetz/line [0 0] [2 2]]
|
|
[:cetz/circle [1 1] {:radius 0.5}]
|
|
[:cetz/rect [0 0] [2 1] {:fill "blue"}]]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
#import "@preview/cetz:0.4.2"
|
|
|
|
#cetz.canvas({
|
|
import cetz.draw: *
|
|
|
|
line((0, 0), (2, 2))
|
|
circle((1, 1), radius: 0.5)
|
|
rect((0, 0), (2, 1), fill: blue)
|
|
})
|
|
```
|
|
|
|
### Line with Multiple Points
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:cetz/line [0 0] [1 0] [1 1] [0 1] {:close true}]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
line((0, 0), (1, 0), (1, 1), (0, 1), close: true)
|
|
```
|
|
|
|
### Arc
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:cetz/arc [0 0] {:start 0 :stop 90 :radius 1}]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
arc((0, 0), start: 0deg, stop: 90deg, radius: 1)
|
|
```
|
|
|
|
### Bezier Curve
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:cetz/bezier [0 0] [1 1] [0.5 2] [0.5 -1]]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
bezier((0, 0), (1, 1), (0.5, 2), (0.5, -1))
|
|
```
|
|
|
|
### Content Label in Canvas
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:cetz/content [1 1] "Label here"]
|
|
[:cetz/content [0 0] [:strong "Bold label"]]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
content((1, 1), [Label here])
|
|
content((0, 0), [#strong[Bold label]])
|
|
```
|
|
|
|
### Architecture Boxes Example
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
(defn box-with-label [pos size label]
|
|
(let [[x y] pos
|
|
[w h] size
|
|
cx (+ x (/ w 2))
|
|
cy (+ y (/ h 2))]
|
|
(list
|
|
[:cetz/rect pos [(+ x w) (+ y h)] {:fill "lightblue" :stroke "black"}]
|
|
[:cetz/content [cx cy] label])))
|
|
|
|
[:cetz/canvas
|
|
(box-with-label [0 0] [3 2] "Frontend")
|
|
(box-with-label [5 0] [3 2] "Backend")
|
|
(box-with-label [10 0] [3 2] "Database")
|
|
[:cetz/line [3 1] [5 1] {:mark {:end ">"}}]
|
|
[:cetz/line [8 1] [10 1] {:mark {:end ">"}}]]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
#cetz.canvas({
|
|
import cetz.draw: *
|
|
|
|
// Frontend
|
|
rect((0, 0), (3, 2), fill: lightblue, stroke: black)
|
|
content((1.5, 1), [Frontend])
|
|
|
|
// Backend
|
|
rect((5, 0), (8, 2), fill: lightblue, stroke: black)
|
|
content((6.5, 1), [Backend])
|
|
|
|
// Database
|
|
rect((10, 0), (13, 2), fill: lightblue, stroke: black)
|
|
content((11.5, 1), [Database])
|
|
|
|
// Arrows
|
|
line((3, 1), (5, 1), mark: (end: ">"))
|
|
line((8, 1), (10, 1), mark: (end: ">"))
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Fletcher - Diagrams & Flowcharts
|
|
|
|
High-level diagram DSL. Best for architecture, flowcharts, state machines.
|
|
|
|
### Basic Diagram
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:fletcher/diagram {:node-stroke "1pt" :spacing "2em"}
|
|
[:node [0 0] "Start"]
|
|
[:edge "->"]
|
|
[:node [1 0] "Process"]
|
|
[:edge "->"]
|
|
[:node [2 0] "End"]]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
#import "@preview/fletcher:0.5.8" as fletcher: diagram, node, edge
|
|
|
|
#diagram(
|
|
node-stroke: 1pt,
|
|
spacing: 2em,
|
|
node((0, 0), [Start]),
|
|
edge("-|>"),
|
|
node((1, 0), [Process]),
|
|
edge("-|>"),
|
|
node((2, 0), [End]),
|
|
)
|
|
```
|
|
|
|
### Node Shapes
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:node [0 0] "Default"]
|
|
[:node [1 0] "Circle" {:shape :circle}]
|
|
[:node [2 0] "Diamond" {:shape :diamond}]
|
|
[:node [3 0] "Pill" {:shape :pill}]
|
|
[:node [4 0] "Hexagon" {:shape :hexagon}]
|
|
[:node [5 0] "Cylinder" {:shape :cylinder}]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
node((0, 0), [Default]),
|
|
node((1, 0), [Circle], shape: circle),
|
|
node((2, 0), [Diamond], shape: fletcher.shapes.diamond),
|
|
node((3, 0), [Pill], shape: fletcher.shapes.pill),
|
|
node((4, 0), [Hexagon], shape: fletcher.shapes.hexagon),
|
|
node((5, 0), [Cylinder], shape: fletcher.shapes.cylinder),
|
|
```
|
|
|
|
### Edge Styles
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:edge "->"] ; simple arrow
|
|
[:edge "->>"] ; double arrow
|
|
[:edge "--"] ; no arrow (line)
|
|
[:edge "<->"] ; bidirectional
|
|
[:edge "hook->"] ; hook arrow
|
|
[:edge "|->"] ; maps-to arrow
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
edge("-|>"),
|
|
edge("-|>|>"),
|
|
edge("--"),
|
|
edge("<|-|>"),
|
|
edge("hook-|>"),
|
|
edge("|-|>"),
|
|
```
|
|
|
|
### Directional Edges
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:edge "r" "->"] ; go right
|
|
[:edge "d" "->"] ; go down
|
|
[:edge "u" "->"] ; go up
|
|
[:edge "l" "->"] ; go left
|
|
[:edge "r,d" "->"] ; right then down
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
edge("r", "-|>"),
|
|
edge("d", "-|>"),
|
|
edge("u", "-|>"),
|
|
edge("l", "-|>"),
|
|
edge("r,d", "-|>"),
|
|
```
|
|
|
|
### Edge with Label
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:edge [0 0] [1 0] "->" {:label "HTTP" :label-pos 0.5}]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
edge((0, 0), (1, 0), "-|>", label: [HTTP], label-pos: 0.5),
|
|
```
|
|
|
|
### Curved Edge
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:edge [0 0] [1 0] "->" {:bend 20}]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
edge((0, 0), (1, 0), "-|>", bend: 20deg),
|
|
```
|
|
|
|
### Flowchart Example
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:fletcher/diagram {:node-stroke "1pt" :node-fill "white" :spacing "3em"}
|
|
;; Nodes
|
|
[:node [0 0] "Start" {:shape :pill}]
|
|
[:node [0 1] "Input Data"]
|
|
[:node [0 2] "Valid?" {:shape :diamond}]
|
|
[:node [1 2] "Error" {:fill "red.lighten(80%)"}]
|
|
[:node [0 3] "Process"]
|
|
[:node [0 4] "End" {:shape :pill}]
|
|
|
|
;; Edges
|
|
[:edge [0 0] [0 1] "->"]
|
|
[:edge [0 1] [0 2] "->"]
|
|
[:edge [0 2] [1 2] "->" {:label "no"}]
|
|
[:edge [0 2] [0 3] "->" {:label "yes"}]
|
|
[:edge [1 2] [0 1] "->" {:bend -40}]
|
|
[:edge [0 3] [0 4] "->"]]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
#import "@preview/fletcher:0.5.8" as fletcher: diagram, node, edge
|
|
|
|
#diagram(
|
|
node-stroke: 1pt,
|
|
node-fill: white,
|
|
spacing: 3em,
|
|
|
|
// Nodes
|
|
node((0, 0), [Start], shape: fletcher.shapes.pill),
|
|
node((0, 1), [Input Data]),
|
|
node((0, 2), [Valid?], shape: fletcher.shapes.diamond),
|
|
node((1, 2), [Error], fill: red.lighten(80%)),
|
|
node((0, 3), [Process]),
|
|
node((0, 4), [End], shape: fletcher.shapes.pill),
|
|
|
|
// Edges
|
|
edge((0, 0), (0, 1), "-|>"),
|
|
edge((0, 1), (0, 2), "-|>"),
|
|
edge((0, 2), (1, 2), "-|>", label: [no]),
|
|
edge((0, 2), (0, 3), "-|>", label: [yes]),
|
|
edge((1, 2), (0, 1), "-|>", bend: -40deg),
|
|
edge((0, 3), (0, 4), "-|>"),
|
|
)
|
|
```
|
|
|
|
### Architecture Diagram Example
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
[:fletcher/diagram {:spacing "4em" :node-stroke "1pt"}
|
|
;; Frontend layer
|
|
[:node [0 0] "Web App" {:fill "blue.lighten(80%)"}]
|
|
[:node [1 0] "Mobile App" {:fill "blue.lighten(80%)"}]
|
|
|
|
;; API Gateway
|
|
[:node [0.5 1] "API Gateway" {:shape :hexagon :fill "green.lighten(80%)"}]
|
|
|
|
;; Services
|
|
[:node [0 2] "Auth Service" {:shape :pill}]
|
|
[:node [1 2] "User Service" {:shape :pill}]
|
|
[:node [2 2] "Order Service" {:shape :pill}]
|
|
|
|
;; Data layer
|
|
[:node [0 3] "PostgreSQL" {:shape :cylinder}]
|
|
[:node [1 3] "Redis" {:shape :cylinder :fill "red.lighten(80%)"}]
|
|
[:node [2 3] "MongoDB" {:shape :cylinder :fill "green.lighten(80%)"}]
|
|
|
|
;; Connections
|
|
[:edge [0 0] [0.5 1] "->"]
|
|
[:edge [1 0] [0.5 1] "->"]
|
|
[:edge [0.5 1] [0 2] "->"]
|
|
[:edge [0.5 1] [1 2] "->"]
|
|
[:edge [0.5 1] [2 2] "->"]
|
|
[:edge [0 2] [0 3] "->"]
|
|
[:edge [1 2] [1 3] "->"]
|
|
[:edge [2 2] [2 3] "->"]]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
#import "@preview/fletcher:0.5.8" as fletcher: diagram, node, edge
|
|
|
|
#diagram(
|
|
spacing: 4em,
|
|
node-stroke: 1pt,
|
|
|
|
// Frontend layer
|
|
node((0, 0), [Web App], fill: blue.lighten(80%)),
|
|
node((1, 0), [Mobile App], fill: blue.lighten(80%)),
|
|
|
|
// API Gateway
|
|
node((0.5, 1), [API Gateway], shape: fletcher.shapes.hexagon, fill: green.lighten(80%)),
|
|
|
|
// Services
|
|
node((0, 2), [Auth Service], shape: fletcher.shapes.pill),
|
|
node((1, 2), [User Service], shape: fletcher.shapes.pill),
|
|
node((2, 2), [Order Service], shape: fletcher.shapes.pill),
|
|
|
|
// Data layer
|
|
node((0, 3), [PostgreSQL], shape: fletcher.shapes.cylinder),
|
|
node((1, 3), [Redis], shape: fletcher.shapes.cylinder, fill: red.lighten(80%)),
|
|
node((2, 3), [MongoDB], shape: fletcher.shapes.cylinder, fill: green.lighten(80%)),
|
|
|
|
// Connections
|
|
edge((0, 0), (0.5, 1), "-|>"),
|
|
edge((1, 0), (0.5, 1), "-|>"),
|
|
edge((0.5, 1), (0, 2), "-|>"),
|
|
edge((0.5, 1), (1, 2), "-|>"),
|
|
edge((0.5, 1), (2, 2), "-|>"),
|
|
edge((0, 2), (0, 3), "-|>"),
|
|
edge((1, 2), (1, 3), "-|>"),
|
|
edge((2, 2), (2, 3), "-|>"),
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## 4. DSL Helpers
|
|
|
|
Build higher-level abstractions on top.
|
|
|
|
**Clojure:**
|
|
```clojure
|
|
(defn service [id name & {:keys [x y color] :or {color "white"}}]
|
|
[:node [x y] name {:id id :shape :pill :fill color}])
|
|
|
|
(defn database [id name & {:keys [x y color] :or {color "gray.lighten(80%)"}}]
|
|
[:node [x y] name {:id id :shape :cylinder :fill color}])
|
|
|
|
(defn connects [from to & {:keys [label style] :or {style "->"}}]
|
|
[:edge from to style (when label {:label label})])
|
|
|
|
[:fletcher/diagram {:spacing "3em"}
|
|
(service :auth "Auth" :x 0 :y 0)
|
|
(service :users "Users" :x 1 :y 0)
|
|
(database :pg "PostgreSQL" :x 0 :y 1)
|
|
(database :redis "Redis" :x 1 :y 1 :color "red.lighten(80%)")
|
|
|
|
(connects :auth :pg)
|
|
(connects :users :redis :label "cache")]
|
|
```
|
|
|
|
**Typst:**
|
|
```typst
|
|
#diagram(
|
|
spacing: 3em,
|
|
node((0, 0), [Auth], shape: fletcher.shapes.pill, fill: white),
|
|
node((1, 0), [Users], shape: fletcher.shapes.pill, fill: white),
|
|
node((0, 1), [PostgreSQL], shape: fletcher.shapes.cylinder, fill: gray.lighten(80%)),
|
|
node((1, 1), [Redis], shape: fletcher.shapes.cylinder, fill: red.lighten(80%)),
|
|
|
|
edge((0, 0), (0, 1), "-|>"),
|
|
edge((1, 0), (1, 1), "-|>", label: [cache]),
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
| Use Case | Tool | Hiccup Prefix |
|
|
|----------|------|---------------|
|
|
| Simple shapes in content | Built-in | `:rect`, `:circle`, `:line` |
|
|
| Custom drawings, illustrations | CeTZ | `:cetz/canvas`, `:cetz/line` |
|
|
| Flowcharts, architecture | Fletcher | `:fletcher/diagram`, `:node`, `:edge` |
|
|
|
|
## Sources
|
|
|
|
- [Typst Visualize Docs](https://typst.app/docs/reference/visualize/)
|
|
- [Fletcher Package](https://typst.app/universe/package/fletcher/)
|
|
- [CeTZ Package](https://typst.app/universe/package/cetz/)
|
|
- [CeTZ Documentation](https://cetz-package.github.io/)
|