LoginSignup
1
0

More than 3 years have passed since last update.

Clojureで模様を作る

Last updated at Posted at 2021-01-28

Clojureを使用し、下のような模様を描いてみました。
この記事は、これをどのように作成したかを説明しています。

スクリーンショット 2021-01-27 13.01.43.png

 準備

本記事のように、グラフィックスを扱う場合や、デスクトップアプリケーションを作る際に便利なのが、

seesaw

というライブラリーです。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の値を大きくすると、さらに細かな三角形で全体が分割されます。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0