125 lines
5.5 KiB
Clojure
125 lines
5.5 KiB
Clojure
(ns jeremy-website.sketches.tree
|
|
(:require [jeremy-website.l-system :as l-system :include-macros true]
|
|
[quil.core :as q]
|
|
[quil.middleware :as m]))
|
|
|
|
(defn degrees->radians [deg]
|
|
(* deg (/ js/Math.PI 180)))
|
|
|
|
;; The tree branch object
|
|
;; A branch has a coordinate, a list of segments and a map of child branches
|
|
;; A segment has an x1, y1, x2, y2
|
|
;; The child branch map's keys are the segment index at which the
|
|
;; child branch originates, and the values are the child branch itself
|
|
(defn l-system-tree
|
|
[{:keys [segment-length initial-angle initial-x initial-y l-system]}]
|
|
(let [make-tree
|
|
(fn make-tree [segment-length x y angle symbols branch idx]
|
|
(let [[symbol & rest-symbols] symbols]
|
|
(condp = symbol
|
|
nil [branch rest-symbols]
|
|
"F" (let [new-x (+ x (* segment-length
|
|
(js/Math.cos (degrees->radians angle))))
|
|
new-y (+ y (* (- segment-length)
|
|
(js/Math.sin (degrees->radians angle))))
|
|
branch (assoc branch
|
|
:segments
|
|
(conj (:segments branch)
|
|
{:x1 x
|
|
:y1 y
|
|
:x2 new-x
|
|
:y2 new-y}))]
|
|
(recur segment-length new-x new-y angle rest-symbols branch (inc idx)))
|
|
"+" (recur segment-length x y (+ angle 25) rest-symbols branch idx)
|
|
"-" (recur segment-length x y (- angle 25) rest-symbols branch idx)
|
|
"*" (recur segment-length x y (+ angle 20) rest-symbols branch idx)
|
|
"/" (recur segment-length x y (- angle 20) rest-symbols branch idx)
|
|
">" (recur segment-length x y (+ angle 15) rest-symbols branch idx)
|
|
"<" (recur segment-length x y (- angle 15) rest-symbols branch idx)
|
|
"[" (let [[new-branch rest-symbols] (make-tree segment-length
|
|
x
|
|
y
|
|
angle
|
|
rest-symbols
|
|
{:x x
|
|
:y y
|
|
:segments []
|
|
:children {}}
|
|
0)
|
|
branch (assoc-in branch [:children idx] new-branch)]
|
|
(recur segment-length x y angle rest-symbols branch idx))
|
|
"]" [branch rest-symbols] ;; stop processing current branch and pop to parent call
|
|
(recur segment-length x y angle rest-symbols branch idx))))]
|
|
(first (make-tree segment-length
|
|
initial-x
|
|
initial-y
|
|
initial-angle
|
|
(:state l-system)
|
|
{:x initial-x
|
|
:y initial-y
|
|
:segments []
|
|
:children {}}
|
|
0))))
|
|
|
|
|
|
(defn setup []
|
|
;; Hue goes from 0-360, saturation/brightness from 0-100
|
|
(q/color-mode :hsb 360 100 100)
|
|
(q/frame-rate 60)
|
|
(q/random-seed (or (-> (js/URLSearchParams. js/window.location.search)
|
|
(.get "seed"))
|
|
(.now js/Date)))
|
|
(let [l-system (-> (l-system/instantiate {:axiom "X"
|
|
:rules (l-system/rules
|
|
"X(0.4)" -> "F+[[X]-X]-F[-FX]+X"
|
|
"X(0.4)" -> "F*[[X]/X]/F[/FX]*X"
|
|
"X(0.2)" -> "F>[[X]<X]<F[<FX]>X"
|
|
"F" -> "FF")
|
|
:random-fn (partial q/random 1)})
|
|
((apply comp (repeat 6 l-system/step))))
|
|
tree (l-system-tree {:initial-angle (- 90 25)
|
|
:segment-length 6
|
|
:initial-x (* (q/width) 0.1)
|
|
:initial-y (* (q/height) 0.95)
|
|
:l-system l-system})]
|
|
{:tree tree
|
|
:current-branches [{:branch tree
|
|
:i 0}]
|
|
:drawn-branches []
|
|
:to-draw []
|
|
:drawing []
|
|
:drawn []}))
|
|
|
|
(defn update-state [state]
|
|
(let [drawn (concat (mapcat :segments (:drawn-branches state))
|
|
(mapcat #(let [i (:i %)
|
|
segs (get-in % [:branch :segments])]
|
|
(subvec segs i))
|
|
(:current-branches state)))]
|
|
(assoc state
|
|
:drawn drawn)))
|
|
|
|
{:drawing [{:x1 10 :y1 15 :x2 20 :y2 25 :lerp 0.4}]
|
|
:drawn []
|
|
:to-draw []}
|
|
|
|
(defn draw [{:keys [drawing drawn to-draw]}]
|
|
(q/background 0 0 100)
|
|
(q/fill 0 0 0)
|
|
(doseq [{:keys [x1 y1 x2 y2]} drawn]
|
|
(q/line x1 y1 x2 y2))
|
|
(doseq [{:keys [x1 y1 x2 y2 lerp]} drawing]
|
|
(let [x2 (q/lerp x1 x2 lerp)
|
|
y2 (q/lerp y1 y2 lerp)]
|
|
(q/line x1 y1 x2 y2))))
|
|
|
|
(defn sketch
|
|
[{:keys [host size]}]
|
|
(q/sketch
|
|
:host host
|
|
:middleware [m/fun-mode]
|
|
:size size
|
|
:setup setup
|
|
:update update-state
|
|
:draw draw))
|