|
| 1 | +;; https://www.karlsims.com/rd.html |
| 2 | +(ns ex65-reaction-diffusion |
| 3 | + (:require [fastmath.core :as m] |
| 4 | + [fastmath.random :as r] |
| 5 | + [clojure2d.core :as c2d] |
| 6 | + [clojure2d.color :as c] |
| 7 | + [fastmath.vector :as v]) |
| 8 | + (:import [fastmath.java Array])) |
| 9 | + |
| 10 | +(set! *warn-on-reflection* true) |
| 11 | +(set! *unchecked-math* :warn-on-boxed) |
| 12 | + |
| 13 | +(def ^:const size 450) |
| 14 | +(def ^:const scale 2) |
| 15 | +(def ^:const csize (m/* scale size)) |
| 16 | +(def ^:const size2 (m/* size size)) |
| 17 | +(def rsize (range size)) |
| 18 | + |
| 19 | +(defn setup [] |
| 20 | + (let [A (double-array size2 1.0) |
| 21 | + dA (double-array size2) |
| 22 | + |
| 23 | + B (double-array size2 0.0) |
| 24 | + dB (double-array size2)] |
| 25 | + |
| 26 | + (doseq [^long x (range 50 100) |
| 27 | + ^long y (range 120 180)] |
| 28 | + (Array/set2d B size x y (r/drand)) |
| 29 | + (Array/set2d B size y x (r/drand))) |
| 30 | + |
| 31 | + (doseq [^long x (repeatedly 100 #(r/irand size)) |
| 32 | + ^long y (repeatedly 100 #(r/irand size))] |
| 33 | + (Array/set2d B size x y (r/drand)) |
| 34 | + (Array/set2d B size y x (r/drand))) |
| 35 | + |
| 36 | + {:A A :dA dA |
| 37 | + :B B :dB dB |
| 38 | + :Da 1.0 :Db m/THIRD |
| 39 | + :f 0.055 :k 0.062 |
| 40 | + :dt 1.0 |
| 41 | + :show-map? true})) |
| 42 | + |
| 43 | +(defn diffuse! [^double d ^doubles source ^doubles target] |
| 44 | + (doseq [^long x rsize ^long y rsize |
| 45 | + :let [x- (m/mod (m/dec x) size) |
| 46 | + x+ (m/mod (m/inc x) size) |
| 47 | + y- (m/mod (m/dec y) size) |
| 48 | + y+ (m/mod (m/inc y) size)]] |
| 49 | + (Array/set2d target size x y (m/* d (m/- (m/+ (m/* 0.05 (m/+ (Array/get2d source size x- y-) |
| 50 | + (Array/get2d source size x+ y-) |
| 51 | + (Array/get2d source size x- y+) |
| 52 | + (Array/get2d source size x+ y+))) |
| 53 | + (m/* 0.2 (m/+ (Array/get2d source size x- y) |
| 54 | + (Array/get2d source size x+ y) |
| 55 | + (Array/get2d source size x y-) |
| 56 | + (Array/get2d source size x y+)))) |
| 57 | + (Array/get2d source size x y)))))) |
| 58 | + |
| 59 | +(defn norm-f ^double [^double y] (m/mnorm y 0 size 0.15 0.001)) |
| 60 | +(defn norm-k ^double [^double x] (m/mnorm x 0 size 0.041 0.085)) |
| 61 | + |
| 62 | +(defn step [{:keys [^doubles A ^doubles dA ^doubles B ^doubles dB ^double dt |
| 63 | + ^double Da ^double Db ^double f ^double k show-map?] :as data}] |
| 64 | + (diffuse! Da A dA) |
| 65 | + (diffuse! Db B dB) |
| 66 | + (doseq [^long x rsize ^long y rsize |
| 67 | + :let [a (Array/get2d A size x y) |
| 68 | + b (Array/get2d B size x y) |
| 69 | + abb (m/* a b b) |
| 70 | + f (if show-map? (norm-f y) f) |
| 71 | + k (if show-map? (norm-k x) k)]] |
| 72 | + (Array/set2d A size x y (m/constrain (m/+ a (m/* dt (m/+ (Array/get2d dA size x y) |
| 73 | + (m/- (m/* f (m/- 1.0 a)) abb)))) 0.0 1.0)) |
| 74 | + (Array/set2d B size x y (m/constrain (m/+ b (m/* dt (m/+ (Array/get2d dB size x y) |
| 75 | + (m/- abb (m/* (m/+ f k) b))))) 0.0 1.0))) |
| 76 | + data) |
| 77 | + |
| 78 | +(defn draw |
| 79 | + [canvas window _ {:keys [^doubles A ^doubles B show-map? f k] :as curr}] |
| 80 | + (when (c2d/mouse-pressed? window) |
| 81 | + (let [[x y] (v/div (c2d/mouse-pos window) scale)] |
| 82 | + (dotimes [_ 20] (Array/set2d B size |
| 83 | + (m/mod (m/+ (r/irand -10 10) (int x)) size) |
| 84 | + (m/mod (m/+ (r/irand -10 10) (int y)) size) (r/drand))))) |
| 85 | + (let [[^double mx ^double my] (v/div (c2d/mouse-pos window) scale) |
| 86 | + curr (if (c2d/key-pressed? window) |
| 87 | + (condp = (c2d/key-code window) |
| 88 | + :space (let [] |
| 89 | + (assoc curr |
| 90 | + :f (norm-f my) |
| 91 | + :k (norm-k mx) |
| 92 | + :show-map? false)) |
| 93 | + :m (assoc curr :show-map? true) |
| 94 | + curr) |
| 95 | + curr)] |
| 96 | + |
| 97 | + (c2d/set-background canvas (c/color 10 10 20) 100) |
| 98 | + (doseq [^long x rsize ^long y rsize |
| 99 | + :let [a (Array/get2d A size x y) |
| 100 | + b (Array/get2d B size x y)]] |
| 101 | + (c2d/set-color canvas (v/mult (v/vec3 (m/- 1.0 a) b (m/sqrt (m/* a b))) 255.0)) |
| 102 | + (c2d/rect canvas (m/* x scale) (m/* y scale) scale scale)) |
| 103 | + (let [f (m/approx (if show-map? (norm-f my) f) 6) |
| 104 | + k (m/approx (if show-map? (norm-k mx) k) 6)] |
| 105 | + (-> canvas |
| 106 | + (c2d/set-color :light-blue 200) |
| 107 | + (c2d/rect 8 9 80 38) |
| 108 | + (c2d/set-color :black) |
| 109 | + (c2d/set-font-attributes 12 :bold) |
| 110 | + (c2d/text (str "f=" f) 15 25) |
| 111 | + (c2d/text (str "k=" k) 15 40))) |
| 112 | + (step curr))) |
| 113 | + |
| 114 | +(def window (c2d/show-window {:canvas (c2d/black-canvas csize csize :mid) |
| 115 | + :draw-fn draw |
| 116 | + :background :black |
| 117 | + :draw-state (step (setup))})) |
| 118 | + |
0 commit comments