🏠

D2图形表示故事

D2 ,Declarative Diagramming,图形表示的现代描述语言。用 D2 语言可以描述图形表示,通过 d2 命令转化为图形表示。

input: "input.d2"
output: "output.svg"
input -> output: "d2 input.d2 output.svg"

d2-input-output.png

d2 的竞争对手是 Mermaid 和 PlantUML 。说实在这两个都是不好招惹的主,对比三者是一门显学。

d2 命令的安装

curl -fsSL https://d2lang.com/install.sh | sh -s --

d2 install.sh 要从 Github Release 下载,国内也不是很方便。CachyOS 仓里有 d2 命令

sudo pacman -S d2

或者用 Go 安装

go install oss.terrastruct.com/d2@latest

d2 支持 3 种布局管理器 d2 layout

d2plugin-tala 的安装

curl -fsSL https://d2lang.com/install.sh |
    sh -s -- --tala

d2plugin-tala 有个例子 sample_2.d2 ,这个例子看三种布局不同,也熟悉下 d2 的基本语法

# file: sample_2.d2
# 方向设置,left, right, up, down
# 只有 tala 布局支持局域方向设置
direction: right

# 图示:key:[value][{options}]
# value 空用 key
logs: {
  shape: page
  style.multiple: true
}

# value 里可以有空格
user: AT&T Customer {shape: person}

# 容器使用: key:[value]{shapes;connections}
network: Network {
  tower: Cell Tower {
    satellites: {
      shape: stored_data
      style.multiple: true
    }
    # 最简单图示
    transmitter

    satellites -> transmitter
    satellites -> transmitter
    satellites -> transmitter
  }
  processor: Data Processor {
    storage: Storage {
      shape: cylinder
      style.multiple: true
    }
  }
  portal: Online Portal {
    UI
  }
  # connections 用法
  tower.transmitter -> processor: phone logs
}

server: API Server

user -> network.tower: Make call
network.processor -> server
network.processor -> server
network.processor -> server

server -> logs
server -> logs
server -> logs: persist

server -> network.portal.UI: display
user -> network.portal.UI: access {
  style.stroke-dash: 3
}

sample_2_tala.png

Figure 1: d2 –layout tala sample_2.d2 sample_2_tala.png

tala 布局最好,要去掉水印,需要每月 19 美元订阅,连续订阅 12 月永久拥有。

sample_2_elk.png

Figure 2: d2 –layout elk sample_2.d2 sample_2_elk.png

elk 布局与 tala 接近,连线以直线为主。不同是容器不能有不同朝向。观察 Cell Tower 容器, tala 里 direction: down , elk 里 direction: right 和 root 一样。

sample_2_dagre.png

Figure 3: d2 –layout dagre sample_2.d2 sample_2_dagre.png

dagre 布局连线不以直线为主,感觉有些乱。 sample_2.d2 里揭示了 d2 基本元素

  1. Shapes - 图示
  2. Connections - 连线
  3. Containers - 容器

掌握基本元素用法一般的图都可以画了,除了时序图(Sequence Diagram)

d2-sequence.png

Figure 4: d2 sequence.d2 sequence.png –layout elk

诀窍在于设置 Root Board 的 shape: sequence_diagram 。时序图是 PlantUML 的看家本领。UML 类图

# file: uml-class.d2
Duck: {
  shape: class
  beakColor: String
  swim(): void
  quack(): void
}
Fish: {
  shape: class
  -sizeInFeet: int
  -canEat(): boolean
}
Zebra: {
  shape: class
  is_wild: bool
  run(): void
}
Animal: {
  shape: class
  age: int
  gender: String
  isMammal(): bool
  mate(): void
}
Duck -> Animal
Fish -> Animal
Zebra -> Animal

d2-uml-class.png

Figure 5: d2 uml-class.d2 uml-class.png –layout elk

诀窍是设图示的 shape: class 。SQL Table 图

# file: sql_table.d2
car: {
  shape: sql_table

  id: int {constraint: primary_key}
  last_updated: timestamp with time zone

  make: string
  model: string
  year: int
}

factory: {
  shape: sql_table

  id: int {constraint: primary_key}

  country: string
}

factory_car_support: {
  shape: sql_table

  id: int {constraint: primary_key}

  factory: int {constraint: foreign_key}
  car: int {constraint: foreign_key}
}

factory_car_support.factory -> factory.id
factory_car_support.car -> car.id

d2-sql-table.png

Figure 6: d2 sql-table.d2 sql-table.png –layout elk

注意 shape: sql_table 设置。活动图(activity diagram)

# file: activity.d2
Start: "" {
  shape: circle
  width: 30
}
End: "" {
  shape: circle
  width: 30
}

Start -> Still
Still -> End

Still -> Moving
Moving -> Still
Moving -> Crash
Crash -> End

d2-activity.png

Figure 7: d2 activity.d2 activity.png –layout elk

注意图示文本空格的指定。

文本不只能指定为空格,文本还可以指定为 markdown

# file: md.d2
a:|||md
  # 1. 使用 Markdown 制作文本
  - 使用标题
  - 使用代码
    ```markdown
       # 如何用 D2 输出 PPTX
       1. 使用 Markdown 制作文本
       2. 使用 layers 制作分页
       3. 使用其他元素丰富内容
    ```
  - 转换命令
    ```sh
       d2 input.d2 output.pptx
    ```
  - 使用表格
    | A | B | C |
    |---|---|---|
    | a | b | c |
  |||{
    shape: rectangle
  }

d2-md.png

Figure 8: markdown 的使用

可以使用 latex

# file: latex.d2
plankton -> formula: will steal
formula: {
  equation: |latex
    \lim_{h \rightarrow 0 } \frac{f(x+h)-f(x)}{h}
  |
}

d2-latex.png

可以用来显示代码

hash: |go
  func RegisterHash(h Hash, f func() hash.Hash) {
        if h >= maxHash {
                panic("crypto: RegisterHash of unknown hash")
        }
        hashes[h] = f
  }
|
hello world: |python
  def hello():
    print "world"
|
x -> hash -> y
x -> hello world

d2-code.png

Figure 9: 显示代码

这样原则上,引入 layers d2 可以用来画 PPTX 了。

d2 pptx.d2 pptx.pptx
# file: pptx.d2
text: |md
  # 如何用 D2 输出 PPTX
  1. 使用 Markdown 制作文本
  2. 使用 layers 制作分页
  3. 使用其他元素丰富内容
  |
layers: {
  1: {
    text: |md
      # 1. 使用 Markdown 制作文本
      - 使用标题
      - 使用代码
        ```markdown
           # 如何用 D2 输出 PPTX
           1. 使用 Markdown 制作文本
           2. 使用 layers 制作分页
           3. 使用其他元素丰富内容
        ```
      - 转换命令
        ```sh
           d2 input.d2 output.pptx
        ```
      |
  }

  2: {
    text: |md
      # 2. 使用 layers 制作分页
      - 参见本页 d2 代码
      |
  }
  3: {
    text: |md
      # 3. 使用其他元素丰富内容
      | {
        near: top-center
      }
    container: 流程图 {
      direction: right
      A -> B -> C
      near: bottom-center
    }
  } 
}

不忘初心,牢记使命。D2 故事到此转了一圈。有一点点讽刺味道。不过,注意, D2 语法并没有“太过”复杂,仍然保持简单一致。 layers 扩展了多页画板(board)

此外再无更多复杂度引入,也算是可以了。