Last active
August 29, 2015 14:06
-
-
Save rm-hull/968c6f0ab6b14e20e4fa to your computer and use it in GitHub Desktop.
Stephen Wolfram announced a class of a one-dimensional binary cellular automaton rules in 1983 and published further analysis in his 2002 book _A New Kind of Science_. The most famous instance is "Rule 30": a Class III rule, displaying aperiodic, chaotic behaviour. This rule is of particular interest because it produces complex, seemingly random…
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
(ns big-bang.examples.nks1 | |
(:require | |
[jayq.core :refer [show]] | |
[inkspot.color-chart :as cc] | |
[big-bang.core :refer [big-bang]] | |
[enchilada :refer [ctx canvas canvas-size value-of]] | |
[monet.canvas :refer [clear-rect fill-rect fill-style draw-image]] | |
[dommy.core :refer [insert-after! set-text!]]) | |
(:require-macros | |
[dommy.macros :refer [sel1 node]])) | |
(def to-number | |
(memoize | |
(fn [xs radix] | |
(let [index (cons 1 (reductions * (repeat radix)))] | |
(reduce + (map * index (reverse xs))))))) | |
(def digits | |
(memoize | |
(fn [n radix] | |
(loop [n n | |
res nil] | |
(if (zero? n) | |
res | |
(recur | |
(quot n radix) | |
(cons (rem n radix) res))))))) | |
(defn neighbours [data] | |
(partition 3 1 data)) | |
(defn rule-bits [n] | |
(vec | |
(take 8 | |
(concat | |
(reverse (digits n 2)) | |
(repeat 0))))) | |
(defn rule [n] | |
(let [bits (rule-bits n)] | |
(fn [x] | |
(nth bits (to-number x 2))))) | |
(defn inflate [coll] | |
(vec (cons 0 (conj coll 0)))) | |
(defn generations [rule-fn data] | |
(lazy-seq | |
(cons | |
(vec data) | |
(generations | |
rule-fn | |
(inflate (mapv rule-fn (neighbours (inflate data)))))))) | |
(defn midpoint [n] | |
(/ (dec n) 2)) | |
(defn window [n] ; <-- width should be odd | |
(if (even? n) | |
(window (dec n)) | |
(let [offset (midpoint n)] | |
(fn [data] | |
(let [shortfall (- offset (midpoint (count data))) | |
data (if (pos? shortfall) | |
(nth (iterate inflate data) shortfall) | |
data) | |
data-midpoint (midpoint (count data))] | |
(subvec data (- data-midpoint offset) (+ data-midpoint offset 1))))))) | |
(defn initial-state [rule-number] | |
(let [seed [0 1 0] | |
[w h] (canvas-size) | |
block-size (js/parseInt (value-of :block-size 4)) | |
rule-fn (rule (mod rule-number 256)) | |
win-size (quot w block-size)] | |
{:t 0 | |
:rule rule-number | |
:clear? true | |
:block-size block-size | |
:width w | |
:height h | |
:color-chart (cc/ui-gradient | |
(value-of :gradient :miaka) | |
win-size) | |
:generations (map | |
(window win-size) | |
(generations rule-fn seed))})) | |
(defn update-state [event world-state] | |
(-> | |
world-state | |
(update-in [:t] inc) | |
(update-in [:generations] rest) | |
(assoc :clear? false))) | |
(defn handle-mousedown [event world-state] | |
(initial-state (rand-int 256))) | |
(defn scroll-y [ctx offset width height] | |
(let [img-data (.getImageData ctx 0 0 width height)] | |
(.putImageData ctx img-data 0 (- offset)) | |
;(clear-rect ctx {:x 0 :y (- height offset) :w width :h offset}) | |
)) | |
(def render | |
; Create a pre-rendered canvas (double-buffering) | |
(let [canvas (.createElement js/document "canvas") | |
[w h] (canvas-size)] | |
(set! (.-width canvas) w) | |
(set! (.-height canvas) h) | |
(fn [{:keys [generations t rule block-size width height color-chart clear?] :as world-state}] | |
(-> (sel1 "#rule") (set-text! (str "Rule: " rule))) | |
(-> (sel1 "#generation") (set-text! (str "Generation: " t))) | |
(let [buffer (.getContext canvas "2d")] | |
(if clear? | |
(clear-rect buffer {:x 0 :y 0 :w width :h height}) | |
(scroll-y buffer block-size width height)) | |
(loop [co-ords {:x 0 :y (- height block-size block-size) :w block-size :h block-size} | |
elems (first generations) | |
colors color-chart] | |
(when-not (empty? elems) | |
(when (pos? (first elems)) | |
(-> | |
buffer | |
(fill-style (first colors)) | |
(fill-rect co-ords))) | |
(recur | |
(update-in co-ords [:x] (partial + block-size)) | |
(rest elems) | |
(rest colors))))) | |
(clear-rect ctx {:x 0 :y 0 :w width :h height}) | |
(draw-image ctx canvas 0 0)))) | |
(->> | |
(sel1 :#canvas-area) | |
(insert-after! | |
(node | |
[:div#app | |
[:style | |
"#canvas-area { | |
cursor: pointer; | |
} | |
#app { | |
background: rgba(0, 0, 0, 0.6); | |
color: rgb(211, 211, 211); | |
font-family: sans-serif; | |
font-size: 10pt; | |
border-radius: 3px; | |
display: inline-block; | |
position: relative; | |
left: -790px; | |
top: -550px; | |
width: 110px; | |
padding: 3px; | |
}"] | |
[:p#rule] | |
[:p#generation]]))) | |
(show canvas) | |
(big-bang | |
:event-target canvas | |
:initial-state (initial-state (value-of :rule (rand-int 256))) | |
:on-mousedown handle-mousedown | |
:on-tick update-state | |
;:tick-rate 100 | |
:to-draw render) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment