Created
January 22, 2014 11:44
-
-
Save ckirkendall/8557364 to your computer and use it in GitHub Desktop.
This was just for fun to see how much effort it would take. It is a functional clojure version of the java bouncy ball tutorial.
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 bouncy.core | |
(:require [goog.math :as math :refer [Vec2]] | |
[goog.math.Vec2 :as vec])) | |
(declare step draw-circle) | |
(def app {:elems [{:pos (Vec2. 100 100) :speed (Vec2. -3 5) :radius 20} | |
{:pos (Vec2. 200 200) :speed (Vec2. 5 3) :radius 30} | |
{:pos (Vec2. 300 200) :speed (Vec2. 7 6) :radius 5} | |
{:pos (Vec2. 400 200) :speed (Vec2. -5 -6) :radius 10} | |
{:pos (Vec2. 200 400) :speed (Vec2. 2 -2) :radius 15} | |
{:pos (Vec2. 200 200) :speed (Vec2. 1 -6) :radius 25} | |
{:pos (Vec2. 200 300) :speed (Vec2. -1 -4) :radius 35} | |
{:pos (Vec2. 250 250) :speed (Vec2. 5 -4) :radius 40} | |
{:pos (Vec2. 300 250) :speed (Vec2. -3 -6) :radius 10} | |
{:pos (Vec2. 250 300) :speed (Vec2. 2 -3) :radius 15} | |
{:pos (Vec2. 200 400) :speed (Vec2. 1 -9) :radius 25} | |
{:pos (Vec2. 400 200) :speed (Vec2. -9 -1) :radius 35}] | |
:bounds [(Vec2. 0 0) (Vec2. 500 500)] | |
:last-time (.now js/window.performance) | |
:background "black"}) | |
(defprotocol Lens | |
(fetch [_]) | |
(push [_ el])) | |
(defn e-lens [app idx] | |
(reify Lens | |
(fetch [_] (get-in app [:elems idx])) | |
(push [_ el] (assoc-in app [:elems idx] el)))) | |
(def pi*2 (* 2 Math/PI)) | |
(defn draw [app ctx] | |
(.beginPath ctx) | |
(.rect ctx 0 0 500 500) | |
(set! (.-fillStyle ctx) (:background app)) | |
(.fill ctx) | |
(doseq [el (:elems app)] | |
(draw-circle el ctx)) | |
app) | |
(defn draw-circle [el ctx] | |
(.beginPath ctx) | |
(.arc ctx | |
(.-x (:pos el)) | |
(.-y (:pos el)) | |
(:radius el) | |
0 | |
pi*2) | |
(set! (.-fillStyle ctx) "red") | |
(.fill ctx) | |
(set! (.-strokeStyle ctx) "#330000") | |
(.stroke ctx)) | |
(defn delta-vec [vec dt] | |
(Vec2. (* (.-x vec) dt) (* (.-y vec) dt))) | |
(defn update-loc [el dt] | |
(assoc el :pos (vec/sum (:pos el) (delta-vec (:speed el) dt)))) | |
(defn handle-bounds [el [top bottom]] | |
(let [pos (:pos el) | |
sp (:speed el) | |
x (.-x pos) | |
y (.-y pos) | |
vx (.-x sp) | |
vy (.-y sp) | |
r (:radius el) | |
tx (.-x top) | |
ty (.-y top) | |
bx (.-x bottom) | |
by (.-y bottom)] | |
(cond-> el | |
(< (- x r) tx) (assoc :speed (Vec2. (* -1 vx) vy) | |
:pos (Vec2. r y)) | |
(> (+ x r) bx) (assoc :speed (Vec2. (* -1 vx) vy) | |
:pos (Vec2. (- bx r) y)) | |
(< (- y r) ty) (assoc :speed (Vec2. vx (* -1 vy)) | |
:pos (Vec2. x r)) | |
(> (+ y r) by) (assoc :speed (Vec2. vx (* -1 vy)) | |
:pos (Vec2. x (- by r)))))) | |
(defn callback [app ctx] | |
(js/requestAnimationFrame (step app ctx))) | |
(defn e-step [app idx dt] | |
(let [lens (e-lens app idx) | |
el (fetch lens)] | |
(-> el | |
(update-loc dt) | |
(handle-bounds (:bounds app)) | |
(#(push lens %))))) | |
(defn e-steps [app dt] | |
(reduce #(e-step %1 %2 dt) app (range (count (:elems app))))) | |
(defn step [app ctx] | |
(fn [now] | |
(let [dt (/ (- now (:last-time app)) 16)] | |
(-> app | |
(e-steps dt) | |
(draw ctx) | |
(assoc :last-time now) | |
(callback ctx))))) | |
(defn run [] | |
(let [canvas (.createElement js/document "canvas")] | |
(.setAttribute canvas "width" (.-x (second (:bounds app)))) | |
(.setAttribute canvas "height" (.-y (second (:bounds app)))) | |
(.appendChild (.-body js/document) canvas) | |
((step app (.getContext canvas "2d")) (.now js/window.performance)))) | |
(set! (.-onload js/window) run) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment