PostScript で拡大してもぼやけない図を書こう(理系レポート/卒論/修論向け)

# あなたのレポートの図、拡大したときぼやけませんか？

言うまでもなくレポートや論文は正確性が肝心だ。
ここでポイントは、画像をpdfかepsと呼ばれる形式で作成することで、間違ってもpngだとかjpgとかで保存した日には教員に呼び出されて小一時間は説教を聞く羽目になるだろう。
**何故か?**
jpgやpngでは、画像を拡大したとき、ぼやけるからだ。
せっかく実験して生成したグラフでも、せっかく徹夜して考えた模式図でも、ぼやけてしまってはなんの意味もない。
だからこそ、pdfやepsで保存して、正確かつ綺麗な画像を提出することが重要になる。
(jpg/png と pdf/eps の違いについて、詳しいことはこの記事では触れない)
**残念ながら、Qiitaに載せるために、この記事ではeps画像をpngに変換して載せています。**
**latexではepsのまま文章に埋め込めるので安心してください。**

## epsの利点は「ぼやけない」だけじゃない！

一部のお絵かきソフトやpowerpointやkeynoteを駆使しても、ぼやけない図は描ける。
が、そういう職人芸で作った図は、意図せず歪んでしまったり、左右非対称になっていたりする。
この記事では、そういう歪んだ図にならない方法を紹介する。

## windowsでもepsを見たい！

残念ながら、windows標準の画像viewerはepsに対応していない。(macは大丈夫)
windows向けのlatexパッケージをインストールすると、GhostScriptというviewerが付属してくるが、画像サイズを適切に読み取ってくれなかったりして、せっかくの綺麗な画像が本来の美しさを発揮できない。
windowsユーザはGhostScriptに付属するps2pdfを使って

> ps2pdf -dEPSCrop hoge.eps

としてpdfに変換することをおすすめする。
(pdfならwindowsでも見れる)

# PostScriptで綺麗な図を描こう

## まずは実際にやってみる

ぼやけないepsファイルとは一体どんな構造になっているのか？
実はこれ、画像ファイルと見せかけて内部はPostScriptというプログラミング言語で書かれたソースコードになっている。
画像viewerはこのコードを実行して、結果を画像として見せてくれる。
論ずるより産むが易し。実際にPostScriptでドイツの国旗(らしきもの)を描いてみる。
以下のコードをgermany.epsとして保存し、epsに対応した画像viewerで開けばいい。

```PostScript:germany.eps
%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: 0 0 640 480

newpath           % 上側 1/3 の領域を
  0 320 moveto
640 320 lineto
640 480 lineto
  0 480 lineto
closepath
0 0 0 setrgbcolor % 黒で
fill              % 塗る！

newpath           % 中央 1/3 の領域を
  0 160 moveto
640 160 lineto
640 320 lineto
  0 320 lineto
closepath
1 0 0 setrgbcolor % 赤で
fill              % 塗る！

newpath           % 下側 1/3 の領域を
  0   0 moveto
640   0 lineto
640 160 lineto
  0 160 lineto
closepath
1 1 0 setrgbcolor % 黄で
fill              % 塗る！
```

![germany.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558059/d2f30862-9652-3b19-e580-28a60bfd193e.png)

最初の二行はいわゆる「おまじない」で、バージョン情報(一行目)と画像の左下と右上の座標(二行目)を指定している。
そのあとやっていることは単純で、4点を座標で指定して四角形を作り、色を決めて、塗る、の繰り返し。
PostScriptではpathという概念を導入して、図形を区分しているので、newpathからclosepathの間にいま描きたい図形の形について記述してやることになる。
これ以上詳しく説明するためには、PostScriptが採用している逆ポーランド記法というやつについてあーだこーだと言わねばならないが、知らなくても何となく分かってもらえると思う。

## 想像以上に大変そう？

まあ、苦情が来るのは分からなくはない。
逆ポーランド記法とか意味わからんし、三回も似たようなこと書くのはダサいし、画像サイズを変更しようと思ったら全部書き直すのが面倒くさい。
**もちろん対策はある。**
PostScriptを出力するための、別のプログラムを書けばいい。
(一種のメタプログラミング)

```Ruby:gen_germany.rb
def color2str(color)
  color.map{ |x| x.to_s }.join(" ")
end

bound_left   = 0.0
bound_bottom = 0.0
bound_right  = 640.0
bound_top    = 480.0

width  = bound_right - bound_left
height = bound_top   - bound_bottom

black  = [0.0, 0.0, 0.0]
red    = [1.0, 0.0, 0.0]
yellow = [1.0, 1.0, 0.0]
bar_colors = [black, red, yellow]  # 上から順に黒赤黄

bar_height = height / bar_colors.size  # 横帯の幅

puts "%!PS-Adobe-3.0 EPSF-3.0"
puts "%%BoundingBox: #{bound_left} #{bound_bottom} #{bound_right} #{bound_top}"

bar_colors.reverse.each_with_index do |color, index|
  bar_left   = bound_left
  bar_right  = bound_right
  bar_bottom = bar_height *  index
  bar_top    = bar_height * (index + 1)
  puts "newpath"
  puts "#{bar_left } #{bar_bottom} moveto"
  puts "#{bar_right} #{bar_bottom} lineto"
  puts "#{bar_right} #{bar_top   } lineto"
  puts "#{bar_left } #{bar_top   } lineto"
  puts "closepath"
  puts "#{color2str(color)} setrgbcolor"
  puts "fill"
end
```

このRubyスクリプトを実行すれば、ドイツ国旗(らしきもの)を出力するepsファイルが生成される。
このようなスクリプトを用意しておけば、途中で気が変わってロシアの国旗(色が違う)を作りたくなったり、ウクライナの国旗(帯の数が違う)を作りたくなったりしても簡単に対応できる。
何ならフランス国旗(帯が縦方向)でも、変更は難しくない。

# 小技集

## 任意の多角形を描く

pathの形は、もちろん四角形でなくても良い。
例として、チェコ国旗らしきものを描く。
背景色と紛らわしいので、白の部分を明るいグレーに変えている。

```Ruby:gen_czech.rb

# color2strの定義とサイズの定義はgen_germanyと同じなので省略

black  = [0.0, 0.0, 0.0]
blue   = [0.0, 0.0, 1.0]
red    = [1.0, 0.0, 0.0]
yellow = [1.0, 1.0, 0.0]
white  = [0.9, 0.9, 0.9]
bar_colors     = [white, red]  # 上から順に白赤
triangle_color = blue          # 左に青

bar_height = height / bar_colors.size  # 横帯の幅

# 「おまじない」と帯の出力もgen_germanyと同じなので省略

center_x = (bound_left + bound_right ) / 2
center_y = (bound_top  + bound_bottom) / 2
puts "newpath"
puts "#{bound_left} #{bound_top   } moveto"
puts "#{bound_left} #{bound_bottom} lineto"
puts "#{center_x  } #{center_y    } lineto"
puts "closepath"
puts "#{color2str(triangle_color)} setrgbcolor"
puts "fill"
```

![czech.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558059/585fb151-25f9-3966-598b-95b8beddcaca.png)

## 円を描く

arcコマンドを用いれば、円形のpathを作ることができる。
命令は「_中心点のx座標 中心点のy座標 半径 始点角度 終点角度 arc_」の順で書く。
例えば半円を描きたい場合は、始点角度 = 0、終点角度 = 180、などとすれば良い。
例として、ニジェール国旗らしきものを描く。

```Ruby:gen_niger.rb
# color2strの定義とサイズの定義はgen_germanyと同じなので省略

black  = [0.0, 0.0, 0.0]
blue   = [0.0, 0.0, 1.0]
green  = [0.0, 1.0, 0.0]
red    = [1.0, 0.0, 0.0]
yellow = [1.0, 1.0, 0.0]
orange = [1.0, 0.5, 0.0]
white  = [0.9, 0.9, 0.9]
bar_colors   = [orange, white, green]  # 上から順に橙白緑
circle_color = orange                  # 中央に橙

bar_height    = height / bar_colors.size  # 横帯の幅
circle_radius = bar_height / 2 * 0.8

# 「おまじない」と帯の出力もgen_germanyと同じなので省略

center_x = (bound_left + bound_right ) / 2
center_y = (bound_top  + bound_bottom) / 2
puts "newpath"
puts "#{center_x} #{center_y} #{circle_radius} 0 360 arc"
puts "closepath"
puts "#{color2str(circle_color)} setrgbcolor"
puts "fill"
```

![niger.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558059/1b43de49-3983-db34-3243-d41adbce3b17.png)

# まとめ？

ぼやけない画像ファイルで、ライバルに差をつけろ!!(適当)

続きはそのうち書きます。