Clojureを使用し、下のような模様を描いてみました。
この記事は、これをどのように作成したかを説明しています。
## 準備
本記事のように、グラフィックスを扱う場合や、デスクトップアプリケーションを作る際に便利なのが、
というライブラリーです。seesawは、Javaのswingのハンドラーなのですが、とても簡単にswingの関数やクラスを呼び出す事ができます。
本記事では、以下のネームスペースを使用します。
(ns triangle.core
(:require
[seesaw
[core :refer :all]
[graphics :refer :all]]))
図形の描画
本記事では、三角形だけしか描画していませんが、後ほど他の図形も描画する可能性があるので、一般に描画可能な図形のプロトコルを定義しておきましょう。
(defprotocol Shape
(painter [this]))
プロトコルは、Javaにおけるインターフェイスに対応しています。Shapeは図形を描画するためのプロトコルで、図形描画の際に必要となる関数としてpainterを含んでいます。
それでは、Shapeプロトコルを満たしている図形の集合を描画する関数viewを作ります。
(def h 500)
(def w 500)
(defn view
"Shapeの集合を描画する。"
[coll]
(let [draw (fn [c g]
;; flip y-axis
(scale g 1 -1)
(translate g 0 (- (height c)))
(doseq [x coll] ((painter x) c g)))
cvs (canvas :paint draw)
f (frame :width w :height h :content cvs)]
(show! f)))
三角形を定義
三角形に対応するレコードTriangleを定義し、Shapeの要件であるpainterを実装します。
Triangleは、頂点の座標と、自分自身の色をデータとして持っています。
また、利便性を考慮し、Triangleを生成する関数(コンストラクター)も用意しておきましょう。
(defrecord Triangle [a b c color]
Shape
(painter [_]
(fn [_ g] (draw g
(polygon a b c)
(style :foreground :black :background color)))))
;; constructor
(defn triangle [a b c &{:keys [color]}]
(->Triangle a b c color))
三角形の分割
冒頭のような図形はどのようにすればできるのでしょうか。
まず、三角形の各変の中点をとり、それらの中点を結ぶ事で、全体を4つの三角形に分割します。
(defn mid
"x,yの中点を返す"
[x y]
(mapv (fn [x y] (/ (+ x y) 2)) x y))
(defn parts
"三角形を4つの部分に分割する。中央の三角形を緑にする。"
[^Triangle {:keys [a b c]}]
(list (triangle a (mid a b) (mid a c))
(triangle b (mid b c) (mid b a))
(triangle c (mid c a) (mid c b))
(triangle (mid a b) (mid b c) (mid c a) :color :green)))
そのようにできた4つの三角形に対して、これと同じ操作を行えば、全体がさらに細かな三角形に分割されます。これを繰り返していけば、いくらでも細かな模様を作成できるでしょう。
このように、ある操作の結果に対し、同じ操作を適用し、それを繰り返す、という処理を行うにはiterateを使うのが便利です。
(defn parts-n
"partsをn回適用する事で、三角形を分割する"
[n ^Triangle x ]
(nth (iterate #(mapcat parts %) [x]) n))
## 結果確認
以上で準備が整いました。n=3として、結果を確認します。
(view (parts-n 3 (triangle [100 100] [250 400] [400 100])))
上のコードで、冒頭に挙げた図形ができました。nの値を大きくすると、さらに細かな三角形で全体が分割されます。