0. はじめに
今回の記事の執筆にあたり,ビリヤードの玉の配置図を大量に描くことになったのだが,わざわざビットマップイメージを作成して貼り付けるのも面倒である。ということで Qiita のサポートする二大作図言語の Mermaid と PlantUML で作図に挑戦した。
1. Mermaid の場合
頑張って描くと以下のようになるが,CとDの線を繋げられない。これではネックレスにならない!
Mermaid のスクリプトは以下のようになる。
```mermaid
---
title: 図1-1 頑張って描いた図
---
graph TD
A((A))
B((B))
C((C))
D((D))
E((E))
A --- B --- C
A --- E --- D
style A fill:#FF0000,stroke:#000000,scale:1.5
style B fill:#00FF00,stroke:#000000,scale:1.5
style C fill:#FFFF00,stroke:#000000,scale:1.5
style D fill:#00FFFF,stroke:#000000,scale:1.5
style E fill:#FF00FF,stroke:#000000,scale:1.5
```
CとDを繋げると以下のようにCとDの縦の位置を揃えられず,どちらか一方が下になる。もともとフローチャートを描くものなので,これは仕方ないところではある。
ボールが6個になると,それなりにいい感じにはなる。
2. PlantUML の場合
一方,PlantUML のほうは図形をループ状に繋げられる。ただし PlantUML で図形を所望の場所に配置するのは難しく,中央にダミー図形を配置して三角形を結ぶようにするとうまくいく。こうして正五角形っぽいAタイプと,省スペース型のBタイプの二種類を作ってみた。
実際の図ではダミーの境界線を black から white とし,また配線の [dotted] を [hidden] として見えないようにする。本当は真円(circle)を描きたかったのだが,PlantUML の circle は図形の外に文字が描かれるため,やむを得ず楕円(storage)で代用した。少しでも真円っぽく見せるために文字の左右に空白を入れて横幅を微調整している。また C と D の間の配線にラベルをつけて空白で間隔を微調整している。
```plantuml:Aタイプ
@startuml
storage #FF0000 " A " as A
storage #00FF00 " B " as B
storage #FFFF00 " C " as C
storage #00FFFF " D " as D
storage #FF00FF " E " as E
circle #white;line:black " " as dummy1
circle #white;line:black " " as dummy2
A -- B
A -- E
B -- C
E -- D
C -right- D : " "
B -[dotted]right- dummy1
dummy2 -[dotted]right- E
dummy1 -[dotted]- C
dummy2 -[dotted]- D
footer 図2-1 Aタイプ
@enduml
```
```plantuml:Bタイプ
@startuml
storage #FF0000 " A " as A
storage #00FF00 " B " as B
storage #FFFF00 " C " as C
storage #00FFFF " D " as D
storage #FF00FF " E " as E
circle #white;line:black " " as dummy
A -- B
A -- E
B -- C
E -- D
C -right- D : " "
B -[dotted]right- dummy
dummy -[dotted]right- E
dummy -[dotted]- C
dummy -[dotted]- D
footer 図2-2 Bタイプ
@enduml
```
さらに以下のように重複している箇所を cloud で囲んで分かり易く図示したかったのだが,囲んでしまうとレイアウトが崩れてしまうので断念した。
3. PlantUML の裏技!インライン SVG スプライトを使う
いや,もう直接 SVG 使って描きたいと思って PlantUML のマニュアルを調べていたところ,ようやく見つけた!PlantUML にはインライン SVG スプライトという機能がある。これはスプライト(絵文字,アイコンのようなもの)を SVG を使って定義し,後で再利用しようというものだ。ただ,サポートしている SVG の機能は極めて限定的である。
こうして超頑張って描いたものが以下の図になる。
円の塗り潰しと境界線描画を同時には行えないので,五つのボールは先に塗り潰しを行い,後から境界線のみ描くという二度手間をかけている。また直線を描くということが難しいので,五つのボールを円で結んでいる。まあ,もともと「ビリヤードの玉を真珠のネックレスのようにリングに繋げる」のだからコチラが正しいのだが。
```plantuml
@startuml
sprite billiard <svg width="250" height="250" viewbox="0 0 250 250">
<circle cx="125" cy="125" r="100" stroke="#000000" fill="none"/>
<circle cx="125" cy="25" r="20" fill="#FF0000"/>
<circle cx="125" cy="25" r="20" stroke="#000000" fill="none"/>
<circle cx="29.9" cy="94.1" r="20" fill="#00FF00"/>
<circle cx="29.9" cy="94.1" r="20" stroke="#000000" fill="none"/>
<circle cx="66.2" cy="205.9" r="20" fill="#FFFF00"/>
<circle cx="66.2" cy="205.9" r="20" stroke="#000000" fill="none"/>
<circle cx="183.8" cy="205.9" r="20" fill="#00FFFF"/>
<circle cx="183.8" cy="205.9" r="20" stroke="#000000" fill="none"/>
<circle cx="220.1" cy="94.1" r="20" fill="#FF00FF"/>
<circle cx="220.1" cy="94.1" r="20" stroke="#000000" fill="none"/>
<text x="120" y="31" fill="black" font-size="16">A</text>
<text x="24.9" y="100.1" fill="black" font-size="16">B</text>
<text x="61.2" y="211.9" fill="black" font-size="16">C</text>
<text x="178.8" y="211.9" fill="black" font-size="16">D</text>
<text x="215.1" y="100.1" fill="black" font-size="16">E</text>
</svg>
title <$billiard>
caption 図3-1 超頑張って描いた図
@enduml
```
重複するボールの配置も以下のように矩形塗り潰しで示すことができる。
傾いた矩形はまず <path> を用いて水平・垂直な矩形を描き,transform オプションを使って回転させると座標計算が楽になる。回転の基準座標を指定できるので,1の玉の位置 (125, 25) を中心にして 54 度回転させた。
<path d="M101 1 h48 v165.6 h-48" fill="#FFFFC0" transform="rotate(0,125,25)"/>
<path d="M101 1 h48 v165.6 h-48" fill="#FFFFC0" transform="rotate(54,125,25)"/>
4. 結論
Mermaid の場合,図のタイトルが上端固定なのも融通が利かないところで残念だ。ただし PlantUML の場合,図形の外枠線が描かれてしまうし,図形内のテキストもコピーできないのに対し,Mermaid の図のほうは外枠線がなくて本文と一体化しており,テキストのコピーも可能であるなど利点も多い。Mermaid 陣営にはもうちっと頑張って欲しいところだ。
PlantUML のインライン SVG スプライトは座標計算がひたすら面倒なのでお勧めしない。テキストの起点が文字(グリフ)の左下端になるので,本図のように中央揃えで表示しようとすると文字列の横幅や高さを計測して微調整しなくてはならない。ただし,非常に強力な作図手段ではある。
PlantUML のインライン SVG スプライト機能については別記事でまとめることにしよう。