Big-Bang - Parametric Equations

big-bangmath

An animated render of a parametric equation in ClojureScript with big-bang. A curve is swept out where the trajectory of a point is usually represented by a parametric equation with the time as parameter.

2 Feb 2014 by rm-hull
rm-hull/8776719
PARAMETRIC-EQUATIONS.md

Parametric Equations

Using:

x

y

Where t increases monotonically, and k can be varied with the slider below, the (x,y) co-ordinates sweep out the curve of a distressed Cardioid. When k = 0.5 the curve sweeps out a perfect Cardioid.

The state and rendering are maintained as separate concerns using the Big-bang library. Furthermore, the sliders at the bottom of the canvas area are implemented as self-contained composite big-bang components in their own right: as soon as a change event occurs on one, the slider re-renders the display value and emits the new value on the supplied channel.

parametric-equations.cljs
(ns big-bang.examples.parametric-equations
  (:require
    [cljs.core.async :as async]
    [dommy.core :refer [insert-after!]]
    [enchilada :refer [ctx canvas canvas-size]]
    [jayq.core :refer [show]]
    [monet.canvas :refer [fill-style fill-rect circle rotate translate]]
    [big-bang.core :refer [big-bang]]
    [big-bang.components :refer [slider]])
  (:require-macros
    [dommy.macros :refer [sel1 node]]))

(def dimensions 
  (let [[width height] (canvas-size)]
    {:x (quot width -2) :y (quot height -2) :w width :h height}))

(def initial-state 
  {:t 0
   :k 0.45
   :ctx ctx
   :persistence 95
   :clear? false})

(defn incoming [event world-state]
  (merge world-state event (if (:k event) {:clear? true})))

(defn tock [event world-state]
  (-> 
    world-state 
    (update-in [:t] inc)
    (assoc :clear? false)))

(defn draw-point! [ctx t k]
  (let [t (/ t 60)
        a 1
        b (/ a k)
        a-minus-b (- a b)
        x (+ (* a-minus-b (Math/cos t))
             (* b         (Math/cos (* t (dec k)))))
        y (- (* a-minus-b (Math/sin t))
             (* b         (Math/sin (* t (dec k)))))
        scale (if (< k 1.0) 
                (* 150 k) 
                200)]
    (->
      ctx
      (fill-style :red)
      (circle {:x (* scale x) :y (* scale y) :r 3}))))

(defn render-frame! [{:keys [clear? k t persistence ctx] :as world-state}]
  (let [color (if clear?
                :white
                (str "rgba(255,255,255," (double (/ (- 100 persistence) 100)) ")"))]
    (->
      ctx
      (fill-style color)
      (fill-rect dimensions)
      (draw-point! t k))))

(let [chan (async/chan)]

  (show canvas)
  (translate ctx (quot (dimensions :w) 2) (quot (dimensions :h) 2))

  (->>
    (sel1 :#canvas-area)
    (insert-after! (node
                     [:div
                       (slider
                         :id :persistence
                         :label-text "Persistence:"
                         :min-value 0
                         :max-value 100
                         :initial-value (initial-state :persistence)
                         :send-channel chan)
                       [:span {:style "padding-right: 100px"}]
                       (slider
                         :id :k
                         :label-text "k:"
                         :min-value 0.05
                         :max-value 10
                         :step 0.05
                         :initial-value (initial-state :k)
                         :send-channel chan)])))
  (big-bang
    :initial-state initial-state
    :receive-channel chan
    :on-receive incoming
    :on-tick tock
    :to-draw render-frame!))