Arnold's Cat-map

canvasmath

Arnold's cat map is named after the mathematician Vladimir Arnold, who in the 1960s demonstrated the effect of repeatedly applying a linear transformation to an image of a cat (hence the name). Each animation frame transforms the pixels according to the pisano period of the largest dimension (width or height). Eventually the image will restore back to the original, but the number of cycles this takes will vary wildly depending on the period. Inspired by: http://pi3.sites.sheffield.ac.uk/tutorials/week-5. In number theory, the nth Pisano period, written π(n), is the period with which the sequence of Fibonacci numbers, modulo n repeats. For example, the Fibonacci numbers modulo 3 are 0, 1, 1, 2, 0, 2, 2, 1, 0, 1, 1, 2, 0, 2, 2, 1, etc., with the first eight numbers repeating, so π(3) = 8. Pisano periods are named after Leonardo Pisano, better known as Fibonacci.

30 Apr 2013 by rm-hull
rm-hull/5491968
arnolds-cat-map.cljs
(ns arnolds-cat-map.demo
    (:use [monet.canvas :only [draw-image get-pixel composition-operation]]
          [monet.core :only [animation-frame]]
          [enchilada :only [ctx canvas proxy-request]]
          [jayq.core :only [show]]
          [jayq.util :only [wait]])
  (:use-macros [dommy.macros :only [sel1 node]]))

(def cat-resources 
  [
    "https://gist.github.com/rm-hull/8859515c9dce89935ac2/raw/cat_01.jpg"
    "https://gist.github.com/rm-hull/8859515c9dce89935ac2/raw/cat_02.jpg"
    "https://gist.github.com/rm-hull/8859515c9dce89935ac2/raw/cat_03.jpg"
    "https://gist.github.com/rm-hull/8859515c9dce89935ac2/raw/cat_04.jpg"
    "https://gist.github.com/rm-hull/8859515c9dce89935ac2/raw/cat_05.jpg"
    "https://gist.github.com/rm-hull/8859515c9dce89935ac2/raw/cat_06.jpg"
    "https://gist.github.com/rm-hull/8859515c9dce89935ac2/raw/cat_07.jpg"
    "https://gist.github.com/rm-hull/8859515c9dce89935ac2/raw/cat_08.jpg"
  ])

(defn random-cat []
  (nth cat-resources (rand-int (count cat-resources))))

(defn load-image! [url & [callback-fn]]
  (let [img (js/Image.)]
    (when callback-fn
      (set! (.-onload img) (fn [] (callback-fn img))))
    (set! (.-crossOrigin img) "anonymous")
    (set! (.-src img) (proxy-request url)))
    img)

(defn offset [[x y] w]
  (* (+ (* y w) x) 4))

(defn pisano-period [n]
  (fn [x y]
    [(mod y n) (mod (+ x y) n)]))

(defn draw-frame! [ctx n mapper-fn]
  (let [image-in  (. ctx (getImageData 0 0 n n))
        image-out (. ctx (createImageData n n))
        data-in   (.-data image-in)
        data-out  (.-data image-out)]
  (doseq [y (range n)
          x (range n)
          :let [i (offset [x y] n)
                o (offset (mapper-fn x y) n)]
          c (range 4)]
    (aset data-out (+ o c) (aget data-in (+ i c))))
  (. ctx (putImageData image-out 0 0))
  ctx))

(defn animate [img]
  (let [width (.-width img)
        height (.-height img)]
    (draw-image ctx img { :x 0 :y 0 :w width :h height })
    (wait 
      2000 
      (fn []
        (let [n (max width height)
              mapper-fn (pisano-period n)]
          (letfn [(loop [] 
                    (animation-frame loop)
                    (draw-frame! ctx n mapper-fn))] 
            (loop)))))))

(show canvas)

(load-image! (random-cat) animate)