Different presets:
Last active
August 29, 2015 14:09
-
-
Save rm-hull/07d0706f21551e4249ce to your computer and use it in GitHub Desktop.
In 1952, Alan Turing wrote a paper proposing a reaction–diffusion model as the basis of the development of patterns such as the spots and stripes seen in animal skin. Mathematically, reaction–diffusion systems take the form of semi-linear parabolic partial differential equations. By iteration 300, it should be clear how the two elements have sep…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; Turing's reaction-diffusion model. | |
;; For more information, see: | |
;; [1] Rafael Collantes. Algorithm Alley. Dr. Dobb's Journal, December 1996. | |
;; [2] Alan M. Turing. The chemical basis of morphogenesis. Philosophical | |
;; Transactions of the Royal Society of London. B 327, 37–72 (1952) | |
;; [3] http://www.cgjennings.ca/toybox/turingmorph/ | |
(ns enchilada.reaction-diffusion-morphogenesis | |
(:require | |
[enchilada :refer [canvas ctx value-of canvas-size proxy-request]] | |
[cljs.core.async :as async :refer [<!]] | |
[jayq.core :refer [show]] | |
[big-bang.core :refer [big-bang]] | |
[inkspot.color :as c] | |
[inkspot.color-chart :as cc] | |
[monet.canvas :refer [fill-style text]])) | |
(defn gaussian-seq [] | |
; See Knuth, ACP, Section 3.4.1 Algorithm C. | |
(let [v1 (dec (* 2.0 (rand))) | |
v2 (dec (* 2.0 (rand))) | |
s (+ (* v1 v1) (* v2 v2))] | |
(if (or (>= s 1.0) (= s 0.0)) | |
(recur) | |
(let [mult (Math/sqrt (* -2.0 (/ (Math/log s) s)))] | |
(lazy-cat [(* v1 mult) (* v2 mult)] (gaussian-seq)))))) | |
(defn randomize [width height] | |
(->> | |
(gaussian-seq) | |
(take (* width height)) | |
(mapv #(+ (* (rand) 12.0) (* % 2.0))))) | |
(defn zeros [width height] | |
(vec (repeat (* width height) 0))) | |
(def presets | |
{:cheetah {:ca 3.5 :cb 16.0} | |
:colony {:ca 1.6 :cb 6.0} | |
:fine {:ca 0.1 :cb 1.0} | |
:fingerprint {:ca 1.0 :cb 16.0} | |
:maze {:ca 2.6 :cb 24.0} | |
:pocked {:ca 1.0 :cb 3.0}}) | |
(def gradients | |
[:dance-to-forget | |
:behongo | |
:dracula | |
:frozen | |
:misty-meadow | |
:opa | |
:shore | |
:starfall | |
:vasily | |
:virgin | |
:winter ]) | |
(defn opaque [rgba] | |
(assoc rgba 3 0xff)) | |
(defn initial-state [width height] | |
(color-mapper | |
(solve | |
(let [{:keys [ca cb]} (presets (keyword (value-of :preset :fingerprint)))] | |
{:t 0 | |
:ca ca | |
:cb cb | |
:ao (randomize width height) | |
:an (zeros width height) | |
:bo (randomize width height) | |
:bn (zeros width height) | |
:width width | |
:height height | |
:gradient (mapv | |
(comp opaque c/string->color) | |
(cc/ui-gradient (rand-nth gradients) 256)) | |
})))) | |
(defn swap-buffers [{:keys [ao an bo bn] :as world-state}] | |
(assoc world-state | |
:ao an | |
:an ao | |
:bo bn | |
:bn bo)) | |
(defn offsets [i width height] | |
(let [m (mod i width) | |
h (* width (dec height))] | |
[(if (< i width) (+ i h) (- i width)) ; NORTH | |
(inc (if (= m (dec width)) (- i width) i)) ; EAST | |
(if (>= i h) (- i h) (+ i width)) ; SOUTH | |
(dec (if (zero? m) (+ i width) i))])) ; WEST | |
(defn solve [{:keys [ca cb ao an bo bn width height] :as world-state}] | |
(let [an (transient an) | |
bn (transient bn)] | |
(dotimes [i (* width height)] | |
(let [[n e s w] (offsets i width height) | |
di-a (* ca (+ (ao n) (ao e) (ao s) (ao w) (* -4.0 (ao i)))) | |
re-a (- (* (ao i) (bo i)) (ao i) 12.0) | |
di-b (* cb (+ (bo n) (bo e) (bo s) (bo w) (* -4.0 (bo i)))) | |
re-b (- 16.0 (* (ao i) (bo i)))] | |
(assoc! an i (max 0.0 (+ (ao i) (* 0.01 (+ re-a di-a))))) | |
(assoc! bn i (max 0.0 (+ (bo i) (* 0.01 (+ re-b di-b))))))) | |
(assoc world-state | |
:an (persistent! an) | |
:bn (persistent! bn)))) | |
(defn minmax [[x & xs]] | |
(reduce | |
(fn [[tiny big] n] [(min tiny n) (max big n)]) | |
[x x] | |
xs)) | |
(defn color-mapper [{:keys [an gradient] :as world-state}] | |
(assoc world-state | |
:color-mapper (apply cc/color-mapper gradient (minmax an)))) | |
(defn set-rgba! [pixels offset rgba] | |
(when-not (empty? rgba) | |
(aset pixels offset (first rgba)) | |
(recur pixels (inc offset) (next rgba)))) | |
(defn render [{:keys [t width height an color-mapper]}] | |
(let [img (.getImageData ctx 0 0 width height) | |
data (.-data img)] | |
(dotimes [i (* width height)] | |
(set-rgba! | |
data | |
(* i 4) | |
(color-mapper (an i)))) | |
(dotimes [y 6] | |
(dotimes [x 8] | |
(.putImageData ctx img (* x 100) (* y 100)))) | |
(-> | |
ctx | |
(fill-style :#333) | |
(text {:text (str "Iteration: " t) :x 10 :y 10})))) | |
(defn update-state [event world-state] | |
(-> world-state (update-in [:t] inc) swap-buffers solve color-mapper)) | |
(show canvas) | |
(big-bang | |
:initial-state (initial-state 100 100) | |
:on-tick update-state | |
:to-draw render) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment