Add thi.ng.geom.physics based sketch

This commit is contained in:
arne 2024-05-05 21:01:11 +02:00
commit 2716edb747

View file

@ -0,0 +1,107 @@
(ns aphorisms.thirty-eight
(:require [thi.ng.geom.core :as g]
[thi.ng.geom.physics.core :as phys]
[thi.ng.geom.vector :as v]
[quil.core :as q]
[quil.middleware :as qm]
[thi.ng.geom.rect :as r]
[thi.ng.geom.circle :as c]))
;; toying around with thi.ng.geom.physics; loosely based on
;; https://piped.video/watch?v=3v7SsOdxxww, which explains toxiclibs verlet
;; physics and trying to get close to the differential line growth algorithm
;; described in https://inconvergent.net/generative/differential-line
(def bounds (r/rect 500 500))
(def radius 10)
(def shape (-> (c/circle 100)
(g/translate (g/centroid bounds))
(g/as-polygon)))
(defn world []
;; play around with this, re-evaluate it and hit `r` in a running sketch!
(let [particles (mapv (fn [p]
(phys/particle p 10)) (g/vertices shape))
edges (->> (cycle particles)
(partition-all 2 1)
(take (count particles)))]
(phys/physics
{:particles particles
:springs (mapv (fn [[a b]]
(phys/spring a b (/ (g/circumference shape) (count particles) 2) 0.1)) edges)
:drag 1
:constraints {:bounds (phys/shape-constraint-inside bounds)}
#_#_:listeners {:iter (fn [world _n]
;; record a history of all particle positions
(swap! state update :history conj (mapv phys/position (:particles world))))}})))
(def tmp (world))
(defn setup []
(q/frame-rate 30)
(q/color-mode :hsb 360 100 100)
(q/rect-mode :center)
(q/ellipse-mode :center)
(q/background 350)
{:world (world)})
(defn mouse-pressed [state ev]
(let [rsq (* radius radius)
mouse (v/vec2 ev)
hits (into #{}
(filter #(<= (g/dist-squared (phys/position %) mouse) rsq))
(-> state :world :particles))]
(when hits
(doseq [hit hits] ; dirty dirty side effects
(phys/lock hit)))
(assoc state :hits hits)))
(defn mouse-dragged [state ev]
(let [mouse (v/vec2 ev)]
(doseq [p (:hits state)] ; again, embracing the side-effects in thi.ng.geom.physics
(phys/set-position p mouse)))
state)
(defn mouse-released [state _ev]
(doseq [hit (:hits state)] ; side effects everywhere
(phys/unlock hit))
(dissoc state :hits))
(defn key-pressed [state ev]
(case (:key ev)
:r (assoc state :world (world))
state))
(defn update-state [state]
(update state :world phys/timestep 1))
(defn draw-state [state]
(q/background 180 0 98)
(q/no-stroke)
(doseq [particle (-> state :world :particles)]
(let [[x y] (phys/position particle)
color (if ((:hits state #{}) particle)
[100 80 80]
[100 20 80])]
(apply q/fill color)
(q/ellipse x y radius radius)))
(doseq [{pa :a pb :b} (-> state :world :springs)]
(let [color [100 20 80]]
(apply q/stroke color)
(q/line (phys/position pa) (phys/position pb)))))
#_:clj-kondo/ignore
(q/defsketch thirty-eight
:title "Thirty eight"
:size [(g/width bounds) (g/height bounds)]
:settings #(q/pixel-density (q/display-density))
:setup setup
:key-pressed key-pressed
:mouse-pressed mouse-pressed
:mouse-dragged mouse-dragged
:mouse-released mouse-released
:update update-state
:draw draw-state
#_#_:renderer :p2d
:features [:keep-on-top :no-bind-output]
:middleware [qm/pause-on-error qm/fun-mode])