読者です 読者をやめる 読者になる 読者になる

Haskell組み込みDSLでSVGを書く

概要

プログラムで画像を作りたい時に便利そうなのでメモ。

diagrams-svg: SVG backend for diagrams drawing EDSL. | Hackage
これはHaskellVector画像を描くライブラリである、diagramsライブラリのSVG出力ライブラリとなっている。
diagrams-lib: Embedded domain-specific language for declarative graphics | Hackage

一応githubチュートリアルっぽいものがあるのだが、
それは型エラーでコンパイルできなかった。

今回は使い方を確認するために有名なフラクタル図形を書いてみた。
シェルピンスキーのギャスケット - Wikipedia

コンパイルと実行

$ ghc --make Sierpinski.hs
$ ./Sierpinski -o image.svg 10 5

コマンドライン引数で渡した10と5が図形のパラメータとしてコード中で使用することができて便利だ。

出力される画像はこんな感じになる。
f:id:autotaker:20150226140328p:plain

簡単なコード解説

main :: IO ()
main = mainWith d where
    d c k = pad 1.1 $ sierpinski c k # lcA transparent # fc (sRGB 0.3 0.3 0.3)

mainWithという関数がdの型から適切にコマンドライン引数をパーズしてdに渡してくれる。
関数dはパラメータcとkをつけとって図形を返す。
ここでcとkはそれぞれ最小の三角形のサイズと再帰の深さを表すパラメータで、
ではsierpinski c kという図形の線の色(lcA)や塗りつぶしの色(fc)を指定して、
1割の余白(pad)を付与する。

sierpinski :: Double -> Int -> Diagram B R2
sierpinski c 0 = triangle c 
sierpinski c k = 
    let d' = sierpinski c (k-1)
        l  = c * 2^(k-1) 
        z  = sqrt 3 / 2 * l / 3 
        ps = [(0,z*2),(-l/2,-z),(l/2,-z)] in
    position $ map (\p -> (p2 p,d')) ps

Diagram B R2という型においてBはバックエンドの型(ここではSVG)、R2は2次元画像を意味する。*1
再帰の深さがリミットに達した場合は一辺の長さがcの正三角形(triangle c)を返し、
そうでない場合は、siperpinski図形を生成し(sierpinski c (k-1))、それを3箇所に並べた(position)ものが
siperpinski図形となる。

このようにして図形を代数的にかけるのでとてもイイ感じのライブラリだ。

*1:ちなみにDiagram B R2はMonoidのインスタンスになっているので、複数の図形を書きたい場合はd1 <> d2みたいに すればよい。