Last active
August 29, 2015 13:56
-
-
Save rm-hull/9261323 to your computer and use it in GitHub Desktop.
Explore a variety of polyhedric solids: ClojureScript demo using big-bang for orchestration between GUI components, and the wireframes library for 3D rendering. The polygon data files are derived from http://www.netlib.org/polyhedra/, and read using abstractions over XmlHttpRequests and core.async. NOTE: Some of the shape files do not have prope…
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 polyhedra.examples.demo | |
(:refer-clojure :exclude [identity]) | |
(:require-macros | |
[cljs.core.async.macros :refer [go]] | |
[dommy.macros :refer [sel1 node]]) | |
(:require | |
[clojure.string :refer [split-lines]] | |
[cljs.core.async :refer [chan <! >!]] | |
[dommy.core :refer [insert-after!]] | |
[enchilada :refer [ctx canvas canvas-size proxy-request]] | |
[wireframes.renderer.canvas :refer [draw-solid ->canvas]] | |
[wireframes.renderer.color :refer [wireframe solid]] | |
[wireframes.transform :refer [combine rotate scale translate identity degrees->radians]] | |
[wireframes.shapes.primitives :refer [transform-shape center-at-origin]] | |
[big-bang.core :refer [big-bang]] | |
[big-bang.components :refer [dropdown slider color-picker]] | |
[dataview.loader :refer [fetch-blob fetch-text]] | |
[dataview.ops :refer [create-reader]] | |
[polyhedra.core :refer [shape-spec]] | |
[jayq.core :refer [show]])) | |
(def width (first (canvas-size))) | |
(def height (second (canvas-size))) | |
(def url-prefix "https://raw.github.com/rm-hull/polyhedra/master/resources/netlib/") | |
(defn shapes> [dest-chan] | |
(let [src-chan (chan 1)] | |
(go | |
(loop [] | |
(when-let [msg (<! src-chan)] | |
(->> | |
(:polyhedra msg) | |
(str url-prefix) | |
proxy-request | |
fetch-blob | |
<! | |
create-reader | |
shape-spec | |
center-at-origin | |
(hash-map :shape) | |
(>! dest-chan)) | |
(recur)))) | |
src-chan)) | |
(defn to-keyword> [dest-chan] | |
(let [src-chan (chan 1)] | |
(go | |
(loop [] | |
(when-let [msg (<! src-chan)] | |
(>! dest-chan (update-in msg [:style] keyword)) | |
(recur)))) | |
src-chan)) | |
(defn color-fn [style color] | |
(condp = style | |
:transparent (wireframe color style) | |
:translucent (wireframe color style) | |
:opaque (wireframe color style) | |
:shaded (solid color))) | |
(def transform | |
(memoize | |
(fn [x y z depth offset?] | |
(combine | |
(if offset? (translate 1.17 2.28 0) identity) | |
(rotate :x (degrees->radians x)) | |
(rotate :y (degrees->radians y)) | |
(rotate :z (degrees->radians z)) | |
(translate 0 0 depth))))) | |
(def styles (map name [:transparent :translucent :opaque :shaded])) | |
(def initial-state | |
{:depth 12 | |
:focal-length 3 | |
:style (second styles) | |
:color "#EAF5FC" | |
:offset? false | |
:x 0 | |
:y 0 | |
:z 0 }) | |
(defn render-shape | |
"Draws the shape at the given state of the world (the x,y,z rotation angles)" | |
[{:keys [shape style color focal-length depth offset? x y z]}] | |
(when shape | |
(set! (.-fillStyle ctx) "rgba(255,255,255,0.75") | |
(.fillRect ctx 0 0 width height) | |
((->canvas ctx) | |
(partial draw-solid | |
{:style style | |
:focal-length focal-length | |
:color-fn (color-fn style color) | |
:shape shape | |
:transform (transform x y z depth offset?)}) | |
[width height]))) | |
(defn update-state | |
"Increment/decrement the rotation angles around the x,y and z axes" | |
[event {:keys [x y z] :as world-state}] | |
(-> | |
world-state | |
(assoc :x (+ x 0.3)) | |
(assoc :y (- y 0.7)) | |
(assoc :z (+ z 0.5)))) | |
(defn not-well-formed? [shape] | |
(nil? (:polygons shape))) | |
(defn handle-incoming-msg [event world-state] | |
(when (and (:shape event) (not-well-formed? (:shape event))) | |
(js/alert | |
(str | |
"Shape '" (get-in event [:shape :name]) | |
"' is not a well-formed 3D solid: please try another."))) | |
(merge world-state event)) | |
(defn start [] | |
(let [updates-chan (chan 1)] | |
(go | |
(let [catalog-url (proxy-request (str url-prefix "names.txt")) | |
names (vec (split-lines (<! (fetch-text catalog-url))))] | |
(->> | |
(sel1 :#canvas-area) | |
(insert-after! (node | |
[:div | |
[:div | |
(dropdown | |
:id :polyhedra | |
:label-text " Polyhedra:" | |
:initial-value (rand-int (count names)) | |
:options (zipmap (iterate inc 0) names) | |
:send-channel (shapes> updates-chan)) | |
(dropdown | |
:id :style | |
:label-text " Style:" | |
:initial-value (:style initial-state) | |
:options (zipmap styles styles) | |
:send-channel (to-keyword> updates-chan)) | |
(color-picker | |
:id :color | |
:label-text " Color:" | |
:initial-value (:color initial-state) | |
:send-channel updates-chan)] | |
[:div | |
(slider | |
:id :focal-length | |
:label-text " Focal Length:" | |
:initial-value (:focal-length initial-state) | |
:min-value 1 | |
:max-value 25 | |
:send-channel updates-chan) | |
(slider | |
:id :depth | |
:label-text " Z-Depth:" | |
:initial-value (:depth initial-state) | |
:min-value 0 | |
:max-value 50 | |
:send-channel updates-chan)]]))))) | |
(big-bang | |
:initial-state initial-state | |
:on-tick update-state | |
:to-draw render-shape | |
:receive-channel updates-chan | |
:on-receive handle-incoming-msg))) | |
(set! (.-fillStyle ctx) "white") | |
(.fillRect ctx 0 0 width height) | |
(show canvas) | |
(start) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment