Creating Art with Clojure

Akaash Patnaik

Jackson Pollock

Number 8
Number 8
Number 8
Code & concept based on Painting in Clojure by Tom Booth

Why learn it?

I suppose I should learn Lisp, but it seems so foreign.
-- Paul Graham (1983)

Homoiconicity (AKA Code-As-Data)

(from the Greek words homo meaning the same and icon meaning representation)

Homoiconicity (AKA Code-As-Data)

  • Syntax becomes easy to learn
  • Enables metaprogramming
  • Makes language extensible
Type Example
String "foo"
Number 55, 3/2, 3.14
Keyword :abc
Boolean true, false
Type Access Example
List Sequential (1 2 3)
Vector Sequential & Random [1 2 3]
Map Associative {:a 1 :b 2 :c 3}
Set Membership #{"a" 1 :xyz}
Clojure Expression

(+ 1 1) ;=> 2 
						

(defn greet
	“Returns a friendly greeting”
	[your-name]
	(str “Hello, ” your-name)) 

(greet "World!") ;=> "Hello, World!"
					

Persistent Data Structures

  • Immutable
  • Changing a list returns a new list.
  • No "copy-on-write". Internally re-use old list.

Persistent Data Structures

  • Thread-safe. No synchronisation issues.
  • Allows lazy fetching.
  • Not as expensive, considering current memory & CPU capacities.
### ClojureScript * Clojure compiler that emits JavaScript. * Uses the Google Closure compiler for compact, high-performance JS. * Brings Clojure features to the browser. * Can also run on server-side with Node.js.

Productivity

Java
Clojure
### Great fit for Java devs * Interoperable with Java. Clojure+Java usable in the same codebase. * JVM understanding * Leiningen uses Maven semantics. * Java library ecosystem * Webapps deployable on Java app servers.

Show me the money!

Salary/Demand Source
### Relevance to Intuit * Productivity gains help in rapid iteration * Emphasis on composability * Less framework-bloat, more maintainable codebases * Higher quality code -> fewer bugs * Macros allow customising the language for your domain
## _Talk is cheap. Show me the code._ -- _Linus Torvalds_
### Steps * Define our space + canvas * Simulate flinging paint onto canvas * Determine splatter pattern formed * Render pattern on canvas

Define our space + canvas

Define space

(def space [8   ;; width
            5   ;; height
            6]) ;; depth

(def gravity [0 -9.8 0])

(def canvas-normal [0 1 0])
					
Starting point

(defn starting-point []
  (map rand space))
					

Simulate flinging paint onto canvas

Splatter
$at^2 + 2v_0t + 2(r_0 - r) = 0$

(defn time-to-canvas [position velocity acceleration]
  (let [a acceleration
        b (* 2 velocity)
        c (* 2 position)
        discriminant (- (* b b) (* 4 a c))
        minus-b (- 0 b)
        add-sqrt (/ (+ minus-b (Math/sqrt discriminant)) (* 2 a))
        minus-sqrt (/ (- minus-b (Math/sqrt discriminant)) (* 2 a))]
    (max add-sqrt minus-sqrt)))
						
$r = r_0 + v_0t + \frac{at^2}{2}$

(defn position-at [time initial-position initial-velocity acceleration]
  (+ initial-position
     (* initial-velocity time)
     (/ (* acceleration time time) 2)))
						
$v = v_0 + at$

(defn velocity-at [time initial-velocity acceleration]
  (+ (* acceleration time) initial-velocity))
						

(defn project-point [position velocity]
  (let [[i j k]            position
        [vi vj vk]         velocity
        [ai aj ak]         gravity
        time               (time-to-canvas j vj aj)
        projected-position [(position-at time i vi ai)
                            0
                            (position-at time k vk ak)]
        projected-velocity [(velocity-at time vi ai)
                            (velocity-at time vj aj)
                            (velocity-at time vk ak)]]
    [projected-position
     projected-velocity]))
						
Splatter
$B = V - (2 * (V.N) * N)$

(defn dot-product [vector1 vector2]
  (reduce + (map * vector1 vector2)))

(defn vector-subtraction [vector1 vector2]
  (map - vector1 vector2))

(defn vector-multiply-by-constant [vector constant]
  (map #(* % constant) vector))

(defn bounce-vector [vector normal]
  (let [vector-dot-normal (dot-product vector normal)
        extreme (vector-multiply-by-constant normal (* 2 vector-dot-normal))]
    (vector-subtraction vector extreme)))
						

Determine splatter pattern formed

Paths-II

(defn random-path [position step-vector bounds]
  (cons position
        (lazy-seq 
        	(random-path (vector-add (vector-add position step-vector)
							(random-vector-between (- 0 bounds) bounds))
                               step-vector bounds))))
						

(defn anchor-points [position min-distance max-distance min-steps max-steps variation]
  (let [direction        (random-unit-vector)
        distance         (random-between min-distance max-distance)
        steps            (random-between min-steps max-steps)
        step-vector      (vector-multiply-by-constant direction (/ distance steps))
        random-positions (take steps (random-path position step-vector variation))
        end-position     (vector-add position
                                     (vector-multiply-by-constant step-vector steps))]
    (conj (vec random-positions) end-position)))
						

(defn recur-relation [t a b]
  (+ (* t b) (* a (- 1 t))))

(defn for-component [t component-vals]
  (if (= (count component-vals) 1)
    (first component-vals)
    (for-component t
      (map #(recur-relation t %1 %2) 
      	component-vals (rest component-vals)))))

(defn for-t [t components]
  (map #(for-component t %) components))

						

(defn de-casteljau [control-points step-amount]
  (let [x-vals (map first control-points)
        y-vals (map second control-points)
        z-vals (map #(nth % 2) control-points)
        points (map #(for-t % [x-vals y-vals z-vals]) 
        		(range 0 1 step-amount))]
    points))
						
Motion

(defn path-masses [path initial-mass]
  (let [number-of-points (count path)
        step (- 0 (/ initial-mass number-of-points))]
    (take number-of-points (range initial-mass 0 step))))
						

Render pattern on canvas


(:require [quil.core :as q :include-macros true])							

(defn fling-and-render [& any]
  (q/with-sketch (q/get-sketch-by-id "pollock")
    (let [{:keys [colour canvas-path splatter]} (fling-paint)]
      (q/stroke (apply q/color colour))
      (draw-path canvas-path)
      (doall (map draw-splats splatter)))))

(.addEventListener (.querySelector js/document "#add")
                   "click"
                   fling-and-render)
						

Links

Please do rate this talk


  • Go to http://bit.ly/tf5idc
  • Sign in using your corp credentials
  • Click on the session "Creating Art with Clojure"
  • On the right side click on "Rate this session" and rate the session
  • Thank you!