Little refactor (WIP), more mouse interaction

This commit is contained in:
Chris Hodapp 2020-07-12 13:09:36 -04:00
parent 01642f221f
commit 6cd73c7323
2 changed files with 72 additions and 38 deletions

View File

@ -31,13 +31,9 @@ to run the ClojureScript version in the browser.
- Better docs. - Better docs.
- Make things more interactive, e.g. let the user place obstacles. - Make things more interactive, e.g. let the user place obstacles.
- Factor out the potential function rather than burying it in
`update-state`.
- Visualize amplitude function & potential function. I have - Visualize amplitude function & potential function. I have
`draw-field` which `show-fn` enables, but it is rudimentary. `draw-field` which `show-fn` enables, but it is rudimentary.
- Figure out reasonable boundary behavior. I'd like to do toroidal,
but I would need to gradually blend the potential function at the
edges for this to work right (otherwise particles are just stuck at
the boundaries due to the discontinuity).
- Use 'real' simplex or OpenSimplex noise implementation that has a - Use 'real' simplex or OpenSimplex noise implementation that has a
gradient instead of doing it numerically? gradient instead of doing it numerically.
- Add multi-octave noise and look into what the paper says about
turbulence. (Look up 'advection' technique it mentions too.)

View File

@ -6,9 +6,9 @@
(def res-x 500) (def res-x 500)
(def res-y res-x) (def res-y res-x)
;; Number of particles to use: ;; Number of particles to use:
(def particles 2000) (def particles 1000)
;; Lower alpha produces *longer* particle trails ;; Lower alpha produces *longer* particle trails
(def alpha 40) (def alpha 30)
(def renderer #?(:clj :java2d (def renderer #?(:clj :java2d
:cljs :p2d)) :cljs :p2d))
@ -56,32 +56,70 @@
l (+ (dist (max 0.0 dx) (max 0.0 dy)) (min (max dx dy) 0.0))] l (+ (dist (max 0.0 dx) (max 0.0 dy)) (min (max dx dy) 0.0))]
l)) l))
;; Domain scale for noise function:
(def scale 500.0)
;; Amplitude multiplier for noise:
(def noise-scale (* scale 5.0))
(def f-inv (/ scale))
;; Potential function (2D + time):
(defn potential [x y t]
"2D (+ time) potential function. Returns a scalar.
The absolute value of the scalar doesn't matter, but its gradient of
determines particle velocity. "
(* noise-scale
(+ (q/noise (* f-inv x) (* f-inv y) (* f-inv t))
(q/noise (* f-inv x 2.0) (* f-inv y 2.0) (* f-inv t 1.61))
)))
;; 1.61 is sort of arbitrarily chosen so that periods of the octaves
;; don't line up exactly
;; Delta used for 'gradient'. Multiplying screen width/height by 1e-3
;; to 1e-4 usually gives an acceptable value.
(def eps 0.5)
(def eps-inv (/ eps))
(defn gradient [p-fn x y t]
"Numerical gradient of potential function 'p-fn' via finite differences.
'p-fn' should be a function that takes 3 arguments - (x,y,t) - and
returns a scalar for the potential at that position and time.
Returns [d/dx, d/dy] of 'p-fn' at (x, y, t)."
(let [p (p-fn x y t)
p-dx (p-fn (+ x eps) y t)
p-dy (p-fn x (+ y eps) t)
grad-x (* (- p-dx p) eps-inv)
grad-y (* (- p-dy p) eps-inv)]
[grad-x grad-y]))
(defn move-point [x y]
"Move a particle by the potential at a point.
Returns [x y] of the 'updated' point."
)
(defn update-state [state] (defn update-state [state]
(let [w (q/width) (let [w (q/width)
h (q/height) h (q/height)
;; Overall multiplier for velocity of particle: ;; Overall multiplier for velocity of particle:
vf 0.1 vf 0.1
;; Domain scale for noise function:
scale 500.0
;; Amplitude multiplier for noise:
noise-scale (* scale 10.0)
;; Radius for mouse-thingy: ;; Radius for mouse-thingy:
rad 20.0 rad 20.0
;; Radius for rounded corners: ;; Radius for rounded corners:
rect-rad 100.0 rect-rad 100.0
margin 0 margin 0
eps (* w 1e-3)
mx (q/mouse-x) mx (q/mouse-x)
my (q/mouse-y) my (q/mouse-y)
f-inv (/ scale)
;; "width of the modified region": ;; "width of the modified region":
d0 150.0 d0 200.0
;; distance of point to a circle of radius 'rad' ;; distance of point to a circle of radius 'rad'
;; centered at mouse cursor: ;; centered at mouse cursor:
d-mouse #(if (q/mouse-pressed?) d-mouse #(if (q/mouse-pressed?)
(- (dist (- mx %1) (- my %2)) rad) (- (dist (- mx %1) (- my %2)) rad)
1e6 ;; arbitrarily large value 1e6)
)
;; function for distance to the border: ;; function for distance to the border:
d-border #(- rect-rad d-border #(- rect-rad
(sdf-box (- %1 rect-rad) ; x (sdf-box (- %1 rect-rad) ; x
@ -90,20 +128,17 @@
(- h (* rect-rad 2)) ; height (- h (* rect-rad 2)) ; height
)) ))
;; potential modulation function - takes (x,y): ;; potential modulation function - takes (x,y):
amp-fn (fn [_ _] 1.0) amp-fn (fn [x y] (ramp (/ (d-mouse x y) d0)))
;; #(ramp (min (/ (d-mouse %1 %2) d0) ;; #(ramp (min (/ (d-mouse %1 %2) d0)
;; (/ (d-border %1 %2) d0) ;; (/ (d-border %1 %2) d0)
;; )) ;; ))
mouse-drift #(if (q/mouse-pressed?) mouse-drift #(if (or (< mx 0) (< my 0) (> mx w) (> my h))
0.0
(+ (+
(* (- (/ mx w) 0.5) %2 0.01) (* (- (/ mx w) 0.5) %2 20)
(* (- (/ my h) 0.5) %1 -0.01)) (* (- (/ my h) 0.5) %1 -20)))
0.0)
;; Noise function - must take 3 arguments, (x,y,z): ;; Noise function - must take 3 arguments, (x,y,z):
n-fn #(* noise-scale n-fn #(+ (mouse-drift %1 %2) (potential %1 %2 %3))
(+
(mouse-drift %1 %2)
(q/noise (* f-inv %1) (* f-inv %2) (* f-inv %3))))
;; Overall amplitude function: ;; Overall amplitude function:
p-fn #(* vf (amp-fn %1 %2) (n-fn %1 %2 %3)) p-fn #(* vf (amp-fn %1 %2) (n-fn %1 %2 %3))
points points
@ -113,25 +148,28 @@
border (if (and (and (> x margin) (< x (- w margin))) border (if (and (and (> x margin) (< x (- w margin)))
(and (> y margin) (< x (- h margin)))) (and (> y margin) (< x (- h margin))))
1.0 0.0) 1.0 0.0)
;; Potential at (x, y, z):
n (p-fn x y z) f #(* vf
n-dx (p-fn (+ x eps) y z) (+ (mouse-drift %1 %2)
n-dy (p-fn x (+ y eps) z) (* (potential %1 %2 %3)
;; Velocity by finite differences: (amp-fn %1 %2))))
vx (/ (- n-dy n) eps) [gx gy] (gradient f x y z)
vy (/ (- n n-dx) eps)
;; Updated point position: ;; Update points (move perpendicular to gradient):
x2 (+ x vx) x2 (+ x gy)
y2 (+ y vy) y2 (- y gx)
[x3 y3] (if (or (< x2 0) (> x2 w) (< y2 0) (> y2 h)) [x3 y3] (if (or (< x2 0) (> x2 w) (< y2 0) (> y2 h))
[(q/random w) (q/random h)] [(q/random w) (q/random h)]
[x2 y2]) [x2 y2])
;; This boundary behavior is a little more ;; This boundary behavior is a little more
;; interesting: when a particle leaves the edges, ;; interesting: when a particle leaves the edges,
;; it just reappears in a random place. ;; it just reappears in a random place.
[x4 y4] (if (and (q/mouse-pressed?) (< (dist (- mx x) (- my y)) rad))
[(q/random w) (q/random h)]
[x3 y3])
] ]
[x3 y3] [x4 y4]
)) (:grid state))] )) (:grid state))]
(-> state (-> state
(update :frame inc) (update :frame inc)