Skip to content

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
You can’t perform that action at this time.