Instantly share code, notes, and snippets.

Embed
What would you like to do?
A recursively generated tree is simply _a branch with a tree on the end of it_.
(ns big-bang.demo.recursive-trees
(:require-macros
[cljs.core.async.macros :refer [go]]
[dommy.macros :refer [sel1 node]])
(:require
[cljs.core.async :refer [chan <! >!]]
[dommy.core :refer [insert-after!]]
[jayq.core :refer [$ hide show]]
[big-bang.core :refer [big-bang]]
[big-bang.components :refer [dropdown slider]]
[enchilada :refer [ctx canvas canvas-size value-of]]
[monet.canvas :refer [alpha circle stroke-style stroke-width fill-style fill-rect
begin-path line-to move-to close-path stroke composition-operation]]))
(def canvas-width (first (canvas-size)))
(def canvas-height (second (canvas-size)))
(def colors ["red" "green" "blue" "yellow" "purple" "orange"])
(def initial-state {
:color (rand-nth colors)
:rand (value-of :rand "y")
:init-height (value-of :init-height 100)
:branch-fan (value-of :branchfan 110)
:branch-density (value-of :branchdensity 3)
:input-angle-factor (value-of :inpafact 0.8)
:input-height-factor (value-of :inphf 0.8)
:depth (value-of :depth 5)})
(defn handle-incoming-msg [event world-state]
(->
world-state
(merge event)))
(defn degrees->radians [d]
(/ (* (double d) Math/PI) 180.0))
(defn draw-tree! [ctx [x y] angle branch-fan height depth world-state]
(if (zero? depth)
ctx
(let [theta (/ branch-fan (:branch-density world-state))
;start (- angle (/ branch-fan 2))
start (- angle theta)
height-factor (if (<= depth 2)
(/ (:input-height-factor world-state) 2)
(:input-height-factor world-state))
height' (* height height-factor)
branch-fan' (* branch-fan (:input-angle-factor world-state))
x' (+ x (* height (Math/cos (degrees->radians angle))))
y' (+ y (* height (Math/sin (degrees->radians angle))))
next-pos [x' y']]
(loop [angle start
i (:branch-density world-state)]
(when (pos? i)
(line-to ctx x' (- canvas-height y'))
(if (= (:rand world-state) "n")
(draw-tree!
ctx
next-pos
angle
branch-fan'
height'
(dec depth)
world-state)
(draw-tree!
ctx
next-pos
(+ angle (/ theta -2) (* (rand) theta))
branch-fan'
(* height' (/ (+ 0.5 (rand)) 1.5))
(dec depth)
world-state))
(move-to ctx x (- canvas-height y))
(recur
(+ angle theta)
(dec i))))
ctx)))
(defn render [world-state]
(->
ctx
(fill-style :white)
(fill-rect {:x 0 :y 0 :w canvas-width :h canvas-height})
(composition-operation :darker)
(stroke-style (:color world-state))
(stroke-width 2)
(begin-path)
(move-to (/ canvas-width 2) (- canvas-height 20))
(draw-tree!
[(/ canvas-width 2) 20]
90
(:branch-fan world-state)
(:init-height world-state)
(:depth world-state)
world-state)
(stroke)))
(defn box [content]
[:span {:style "width: 300px;
display: inline-block;
border: 1px solid lightgrey;
margin-right: 5px;
margin-bottom: 5px;
padding-left: 5px;
border-radius: 3px;
background: whitesmoke;"} content])
(defn start []
(let [updates-chan (chan 1)]
(go
(->>
(sel1 :#canvas-area)
(insert-after! (node
[:div
[:div
(box (slider
:id :depth
:label-text " Depth: "
:initial-value (:depth initial-state)
:min-value 2
:max-value 8
:step 1
:send-channel updates-chan))
(box (slider
:id :init-height
:label-text " Init height: "
:initial-value (:init-height initial-state)
:min-value 10
:max-value 400
:step 5
:send-channel updates-chan))
(dropdown
:id :color
:label-text " Color: "
:initial-value (:color initial-state)
:options (zipmap colors colors)
:send-channel updates-chan)]
[:div
(box (slider
:id :branch-fan
:label-text " Branch Fan: "
:initial-value (:branch-fan initial-state)
:min-value 0
:max-value 360
:step 1
:send-channel updates-chan))
(box (slider
:id :branch-density
:label-text " Branch Density: "
:initial-value (:branch-density initial-state)
:min-value 1
:max-value 8
:step 1
:send-channel updates-chan))
(dropdown
:id :rand
:label-text " Random: "
:initial-value (:rand initial-state)
:options {"y" "On" "n" "Off"}
:send-channel updates-chan)]
[:div
(box (slider
:id :input-angle-factor
:label-text " Input Angle Factor: "
:initial-value (:input-angle-factor initial-state)
:min-value 0.0
:max-value 5.0
:step 0.05
:send-channel updates-chan))
(box (slider
:id :input-height-factor
:label-text " Input Height Factor: "
:initial-value (:input-height-factor initial-state)
:min-value 0.0
:max-value 5.0
:step 0.05
:send-channel updates-chan))]]))))
(big-bang
:initial-state initial-state
:on-receive handle-incoming-msg
:receive-channel updates-chan
:to-draw render)))
(show canvas)
(start)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment