ER図の鳥の足をSVGで部品化する
ExcelでIE記法のER図を書くときに、鳥の足 (Crow’s Foot) をどう描くか。(え? ER図書くならもっと便利なツールを使う? そりゃ恵まれてますね)
今までは、線と円のシェイプを組み合わせて、それをグループ化した部品を作って描いていた。「0..1」とか「1..n」とかのパーツを用意しておいて、それをコネクタで結べば、エンティティの移動とか整列とかには割と耐えられる。けど、グループ化を解除しすぎちゃったり、手が滑って線を1本だけ動かしちゃったりすると、形が崩れてしまう。それと、Excelってやつはズームの倍率を変えると微妙にシェイプの位置やサイズが変わるので、編集をくり返しているうちに、なーんか形が歪んできたりもする。丸が潰れて真円じゃなくなったりとか。これなんとかならんじゃろか。
ふと、SVG画像の鳥足パーツがあれば、型崩れの心配もなく、拡大・縮小・回転・コネクタ接続などもできて便利なのではと思った。最近のExcelならSVGもそのまま取り込めたはず。SVGならフォアグランドカラーも好きに変えられたはず。こんなもん絶対もう誰かが作ってるはずだけど、検索しても意外に手頃なものがなかったので、自作してみた。最初は1方向だけ作って回転すればいいやと思ったけど、画像のハンドルがバラバラの方向を向いて使いづらいので、4方向分作ってみた。使うとき極力軽くなるようにということで、SVGのrotate()とかは使っていない。
さすがに量産するのが面倒くさくなってきたので、SVG生成用のbashスクリプトも作った。それもついでに載せておく。bashとawkが使える環境があるなら、SVGをダウンロードするよりこのスクリプトで再生成した方が早い。サイズとか色とか太さとか丸の大きさとか変えたいときも使えそうだし。
場合によっては、古いExcelしか使えずSVGが取り込めないこともありそうなので、いちおう透過PNGにも変換したものも置いておく。(ImageMagickでconvert -background none
しただけのもの)
SVG版
カーディナリティ | 東 | 西 | 南 | 北 |
---|---|---|---|---|
1 | ||||
n | ||||
1..1 | ||||
1..n | ||||
0..1 | ||||
0..n |
PNG版
カーディナリティ | 東 | 西 | 南 | 北 |
---|---|---|---|---|
1 | ||||
n | ||||
1..1 | ||||
1..n | ||||
0..1 | ||||
0..n |
SVG生成スクリプト
#!/bin/bash
main() {
base_filename=crows-foot
canvas_size=20
foot_width=$(calc $canvas_size / 2)
circle_radius=$(calc $canvas_size / 5)
for rotation in "east" "south"; do
generate "none" "one" $rotation
generate "one" "one" $rotation
done
for rotation in "east" "west" "south" "north"; do
generate "none" "many" $rotation
generate "zero" "one" $rotation
generate "zero" "many" $rotation
generate "one" "many" $rotation
done
}
generate() {
local parent=$1
local child=$2
local rotation=$3
exec > ${base_filename}-${parent}-${child}-${rotation}.svg
echo '<?xml version="1.0"?>'
echo '<svg width="'$canvas_size'" height="'$canvas_size'" version="1.1" xmlns="http://www.w3.org/2000/svg">'
echo ' <g stroke="black">'
local grid=$(calc $canvas_size / 5)
local x_left_end=$(calc $canvas_size / -2)
local x_center=0
local x_right_end=$(calc $canvas_size / 2)
local y_center=0
if [[ $parent = "zero" ]]; then
# stem and circle
local x_circle_center=$(calc $grid / -2)
line $x_left_end $y_center $(calc $x_circle_center - $circle_radius) $y_center $rotation
circle $x_circle_center $y_center $rotation
line $(calc $x_circle_center + $circle_radius) $y_center $x_right_end $y_center $rotation
else
# stem
line $x_left_end $y_center $x_right_end $y_center $rotation
fi
if [[ $parent = "one" ]]; then
# vertical bar
line $(calc $grid / 2) $(calc $foot_width / 2) $(calc $grid / 2) $(calc $foot_width / -2) $rotation
fi
if [[ $child = "one" ]]; then
# another vertical bar
case $parent in
"none") line $x_center $(calc $foot_width / 2) $x_center $(calc $foot_width / -2) $rotation;;
"zero") line $(calc $x_right_end - $grid) $(calc $foot_width / 2) $(calc $x_right_end - $grid) $(calc $foot_width / -2) $rotation;;
"one") line $(calc $grid / -2) $(calc $foot_width / 2) $(calc $grid / -2) $(calc $foot_width / -2) $rotation;;
esac
else
# crow's foot
line $(calc $grid / 2) $y_center $(calc $canvas_size / 2) $(calc $foot_width / 2) $rotation
line $(calc $grid / 2) $y_center $(calc $canvas_size / 2) $(calc $foot_width / -2) $rotation
fi
echo ' </g>'
echo '</svg>'
}
line() {
local x1=$1
local y1=$2
local x2=$3
local y2=$4
local rotation=$5
rotate $x1 $y1 $rotation
x1=$new_x
y1=$new_y
rotate $x2 $y2 $rotation
x2=$new_x
y2=$new_y
local offset=$(calc $canvas_size / 2)
x1=$(calc $x1 + $offset)
y1=$(calc - $y1 + $offset)
x2=$(calc $x2 + $offset)
y2=$(calc - $y2 + $offset)
echo ' <line x1="'$x1'" y1="'$y1'" x2="'$x2'" y2="'$y2'" />'
}
circle() {
local cx=$1
local cy=$2
local rotation=$3
rotate $cx $cy $rotation
cx=$new_x
cy=$new_y
local offset=$(calc $canvas_size / 2)
cx=$(calc $cx + $offset)
cy=$(calc - $cy + $offset)
echo ' <circle cx="'$cx'" cy="'$cy'" r="'$circle_radius'" fill="none" />'
}
rotate() {
local x=$1
local y=$2
local rotation=$3
case $rotation in
"east")
new_x=$x
new_y=$y
;;
"north")
new_x=$(calc - $y)
new_y=$x
;;
"west")
new_x=$(calc - $x)
new_y=$(calc - $y)
;;
"south")
new_x=$(calc - $y)
new_y=$(calc - $x)
;;
esac
}
calc() {
awk "BEGIN{print $*}"
}
main
※更新履歴
- 2024-10-19 4方向化、生成スクリプト追加。