First working implementation of https://inconvergent.net/generative/hyphae/ :)
This commit is contained in:
parent
b624396bd1
commit
ffc4bff955
1 changed files with 108 additions and 0 deletions
108
src/aphorisms/thirty_four.clj
Normal file
108
src/aphorisms/thirty_four.clj
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
(ns aphorisms.thirty-four
|
||||||
|
(:require [quil.core :as q]
|
||||||
|
[quil.middleware :as qm]
|
||||||
|
[thi.ng.geom.rect :as r]
|
||||||
|
[thi.ng.geom.core :as g]
|
||||||
|
[thi.ng.geom.circle :as c]
|
||||||
|
[thi.ng.geom.spatialtree :as st]
|
||||||
|
[thi.ng.math.core :as m]))
|
||||||
|
|
||||||
|
;; attempt to implement the hyphae algorithm described in
|
||||||
|
;; https://inconvergent.net/generative/hyphae/
|
||||||
|
|
||||||
|
(def bounds (r/rect 750 500))
|
||||||
|
(def canvas (g/scale bounds 0.92))
|
||||||
|
|
||||||
|
;; parameters to influence how the end result looks
|
||||||
|
|
||||||
|
(def start-size 12.0)
|
||||||
|
(def min-size 8.0)
|
||||||
|
(def shrink 0.8) ;; factor by which size decreases at each step
|
||||||
|
(def spaciousness 2.0) ;; size of neighbor-check for each node
|
||||||
|
|
||||||
|
(def max-path-length 50) ;; how long are the strings of nodes?
|
||||||
|
(def num-paths 25) ;; how many strings should be generated max?
|
||||||
|
|
||||||
|
;; all nodes look like this: {:shape circ, :direction vec2}
|
||||||
|
|
||||||
|
(defn empty-neighborhood? [quadtree node]
|
||||||
|
(empty? (st/select-with-shape quadtree (g/scale-size (:shape node) spaciousness))))
|
||||||
|
|
||||||
|
(defn nice-place? [quadtree node]
|
||||||
|
(and (g/contains-point? (g/scale-size bounds 0.8) (-> node :shape :p))
|
||||||
|
(empty-neighborhood? quadtree node)))
|
||||||
|
|
||||||
|
(defn wobble [v]
|
||||||
|
(g/rotate v (m/radians (m/random -20 20))))
|
||||||
|
|
||||||
|
(defn pick-start [quadtree]
|
||||||
|
(->> (repeatedly 10 (fn []
|
||||||
|
(let [pt (g/random-point-inside (g/scale-size bounds 0.5))
|
||||||
|
dir (wobble (m/normalize (m/- (g/centroid bounds) pt)))]
|
||||||
|
{:shape (c/circle pt start-size)
|
||||||
|
:direction dir})))
|
||||||
|
(filter #(nice-place? quadtree (update % :shape (fn [circ]
|
||||||
|
(g/scale-size circ 1.5)))))
|
||||||
|
(first)))
|
||||||
|
|
||||||
|
(defn next-node [quadtree cur]
|
||||||
|
(->> (repeatedly 10 (fn []
|
||||||
|
(-> (update cur :shape (fn [c]
|
||||||
|
(-> (update c :r #(Math/max (* % shrink) min-size))
|
||||||
|
(update :p #(g/translate % (m/* (:direction cur) (:r c)))))))
|
||||||
|
(update :direction wobble))))
|
||||||
|
(filter (fn [node]
|
||||||
|
(when-not (nice-place? quadtree node)
|
||||||
|
(prn node))
|
||||||
|
(nice-place? quadtree node)))
|
||||||
|
(first)))
|
||||||
|
|
||||||
|
|
||||||
|
;; let's generate different paths of hyphae, each a most 50 nodes long
|
||||||
|
|
||||||
|
(defn make-path [quadtree]
|
||||||
|
(when-let [seed (pick-start quadtree)]
|
||||||
|
(->>
|
||||||
|
(iterate (fn [[head & _ :as path]]
|
||||||
|
(if-let [next (next-node quadtree head)]
|
||||||
|
(conj path next)
|
||||||
|
path))
|
||||||
|
(list seed))
|
||||||
|
(partition 2)
|
||||||
|
(filter (fn [[a b]] (= a b)))
|
||||||
|
(ffirst))))
|
||||||
|
|
||||||
|
(def paths
|
||||||
|
(let [qt (st/quadtree bounds)]
|
||||||
|
(for [_ (range num-paths)
|
||||||
|
:let [path (make-path qt)]]
|
||||||
|
(do
|
||||||
|
(reduce #(g/add-point %1 (-> %2 :shape :p) %2) qt path) ;; add-point is mutable
|
||||||
|
path))))
|
||||||
|
|
||||||
|
(defn setup []
|
||||||
|
(q/ellipse-mode :center)
|
||||||
|
(q/rect-mode :corners)
|
||||||
|
(q/color-mode :hsb 255)
|
||||||
|
{})
|
||||||
|
|
||||||
|
(defn draw-state [_]
|
||||||
|
(q/background 255)
|
||||||
|
(q/no-fill)
|
||||||
|
|
||||||
|
(doseq [path paths
|
||||||
|
{{[x y] :p r :r} :shape} path]
|
||||||
|
(q/ellipse x y r r))
|
||||||
|
#_(qd/draw-scene! scene))
|
||||||
|
|
||||||
|
(when-not (resolve 'thirty-four)
|
||||||
|
#_:clj-kondo/ignore
|
||||||
|
(q/defsketch thirty-four
|
||||||
|
:title "Thirty-Four"
|
||||||
|
:size (:size bounds)
|
||||||
|
:settings #(q/pixel-density (q/display-density))
|
||||||
|
:features [:keep-on-top]
|
||||||
|
:setup setup
|
||||||
|
:update identity
|
||||||
|
:draw draw-state
|
||||||
|
:middleware [qm/pause-on-error #_(screenshottable) qm/fun-mode]))
|
||||||
Loading…
Add table
Add a link
Reference in a new issue