Beating cardioid example. I hope you like it.
drakezhard opened this issue · 5 comments
(ns heart.app
(:require [quil.core :as q :include-macros true]
[quil.middleware :as m]))
(defn rotate-x [theta x y]
(- (* x (Math/cos theta))
(* y (Math/sin theta))))
(defn rotate-y [theta x y]
(+ (* x (Math/sin theta))
(* y (Math/cos theta))))
(defn cardioid [s t]
(let [x (* s (- (* 2 (Math/cos t)) (Math/cos (* 2 t))))
y (* s (- (* 2 (Math/sin t)) (Math/sin (* 2 t))))
theta (- (/ (.-PI js/Math) 2))
j (rotate-x theta x y)
k (rotate-y theta x y)]
[j k]))
(defn setup []
(q/frame-rate 2)
{:points (mapv (partial cardioid (/ (q/width) 6)) (range 0 10 0.001))})
(defn draw [state]
(q/background 0)
(q/fill 255 0 0)
(q/with-translation [(/ (q/width) 2) (/ (q/height) 3)]
(q/begin-shape)
(doseq [p (:points state)]
(apply q/curve-vertex p))
(q/end-shape)))
(def scaling-fn (atom *))
(defn change [state]
(letfn [(scale [s f [x y]]
[(s x f)
(s y f)])
(select [f]
(condp = f
* /
/ *))]
(swap! scaling-fn select)
(update state :points #(map (partial scale @scaling-fn 0.95) %))))
(defn init []
(q/sketch
:setup setup
:draw draw
:update change
:host "heart"
:size [200 200]
:middleware [m/fun-mode]))
I like the idea, though I think we could make a few improvements to make it more interesting. I'd suggest few additions:
- Make beating gradual: for example going from small contracted to expanded takes several frames, the same expanded => contracted. You can even experiment with different speed for expansion and contraction so that it looks like it beats (expands) fast and then slowly contracts.
- Use more detailed parametric function to draw heart. For example take a look at these: http://mathworld.wolfram.com/HeartCurve.html.
Also it would be interesting to try changing some coefficients of the heart function on fly so that it changes form and not only size. I don't know, it might end up pretty ugly, but I'd try.
What do you think?
I hadn't have time to work on this last week, I changed the cardioid function to a prettier function called heart. Draw was updated accordingly and I implemented the more natural progressive engorging and deflating of a beating heart. I saw a video of open heart surgery and it looks like the rate of contraction, is the same as distention not faster. I'd rather not play with the constants, I tried but continuous deformation bends the curve beyond recognition.
(ns heart.app
(:require [quil.core :as q :include-macros true]
[quil.middleware :as m]))
(defn rotate-x [theta x y]
(- (* x (Math/cos theta))
(* y (Math/sin theta))))
(defn rotate-y [theta x y]
(+ (* x (Math/sin theta))
(* y (Math/cos theta))))
#_(this is the nicer cardioid curve)
(defn heart [s t]
(let [x (* s (* 16 (Math/pow (Math/sin t) 3)))
y (* s (- (* 13 (Math/cos t))
(* 5 (Math/cos (* 2 t)))
(* 2 (Math/cos (* 3 t)))
(Math/cos (* 4 t))))
theta (.-PI js/Math)
j (rotate-x theta x y)
k (rotate-y theta x y)]
[j k]))
(declare contraction)
(defn setup []
(q/frame-rate 30)
{:points (mapv (partial heart (/ (q/height) 40)) (range 0 10 0.1))
:scaling-function contraction
:counter 7})
(defn draw [state]
(q/background 0)
(q/fill 255 0 0)
(q/with-translation [(/ (q/width) 2) (/ (q/height) 2)]
(q/begin-shape)
(doseq [p (:points state)]
(apply q/curve-vertex p))
(q/end-shape)))
(defn contraction [[x y]]
[(* x 0.99)
(* y 0.99)])
(defn distention [[x y]]
[(/ x 0.99)
(/ y 0.99)])
(defn select [f]
(condp = f
contraction distention
distention contraction))
(defn beat [state]
(if (zero? (:counter state))
(-> state
(update :counter (fn [] 7))
(update :scaling-function #(select %)))
(-> state
(update :counter #(dec %))
(update :points #(mapv (:scaling-function state) %)))))
(defn init []
(q/sketch
:setup setup
:draw draw
:update beat
:host "heart"
:size [200 200]
:middleware [m/fun-mode]))
Added example but didn't push live yet. I did some changes to your code to make it simple (I think). I replaced rotate by PI with (- y). Also replaced select
function with a map that acts as function. The sketch is here: heart.cljs. If you're fine with current version - I'll publish it live.
I like it, the map change is particularly nice. If you're just going to push the changes, I'm closing the issue, also thanks for the feedback.
Deployed: http://quil.info/?example=heart. Thanks for contributing!