Create a gist now

Instantly share code, notes, and snippets.

Metaballs examples
(ns metaballs
(:import
[javax.swing JFrame]
[java.awt Canvas Graphics Color]
java.awt.image.BufferStrategy))
(set! *warn-on-reflection* true)
(def SIZE 250)
;START:direction-and-position
;;reverse direction if we hit the edge of the screen
(defn direction [p v]
(if (or (> p SIZE) (neg? p)) (- v) v))
;;compute the position and velocity of the ball
(defn move [{:keys [x y vx vy] :as ball}]
(let [vx (direction x vx)
vy (direction y vy)]
(assoc ball :x (+ x vx) :y (+ y vy) :vx vx :vy vy)))
;END:direction-and-position
;START:color
(defn color-in-range [c]
(int (if (< c 0) 0 (if (> c 255) 255 c))))
(defn color [r g b]
(Color. (color-in-range r) (color-in-range g) (color-in-range b)))
;END:color
;START:influence-and-color
;;compute influence of each metaball
(defn influence [{:keys [x y radius]} px py]
(let [dx (- x px)
dy (- y py)]
(/ radius (Math/sqrt (+ (* dx dx) (* dy dy))))))
;;compute the resulting RGB values based on influence
(defn compute-color [x y rgb ball]
(let [influence (influence ball x y)
[r g b] (:color ball)]
(map #(+ %1 (* influence %2)) rgb (:color ball))))
;END:influence-and-color
(defn paint-square [g [red green blue] x y size]
(doto g
(.setColor (color red green blue))
(.fillRect x y size size)))
(defn draw [canvas balls]
(let [buffer (.getBufferStrategy canvas)
g (.getDrawGraphics buffer)
step 2]
(try
(loop [x 0]
(loop [y 0]
(paint-square g
;START:compute-colors
;;for each x,y coordinate compute the color
(reduce (partial compute-color x y) [0 0 0] balls)
;END:compute-colors
x y step)
(if (< y (- SIZE step)) (recur (+ y step))))
(if (< x (- SIZE step)) (recur (+ x step))))
(finally (.dispose g)))
(if-not (.contentsLost buffer)
(.show buffer)) ))
(defn metaball []
{:x (rand-int SIZE)
:y (rand-int SIZE)
:vx (inc (rand-int 6))
:vy (inc (rand-int 6))
:radius (+ 40 (rand-int 15))
:color (repeatedly 3 #(rand-int 256))})
(defn start [& [num-balls]]
(let [frame (JFrame. "Metaballs")
canvas (Canvas.)]
(doto frame
(.setSize SIZE SIZE)
(.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
(.setResizable false)
(.add canvas)
(.setVisible true))
(doto canvas
(.createBufferStrategy 2)
(.setVisible true)
(.requestFocus))
;START:main-loop
;;run this in a loop where we move the
;;balls around and render them
(loop [balls (repeatedly (or num-balls 2) metaball)]
(draw canvas balls)
(recur (map move balls)))
;END:main-loop
))
(ns metaballs-optimized
(:import
[javax.swing JFrame]
[java.awt Canvas Graphics Color]
java.awt.image.BufferStrategy))
(set! *warn-on-reflection* true)
(def SIZE 250)
;START:direction-and-position
;;reverse direction if we hit the edge of the screen
(defn direction [p v]
(if (or (> p SIZE) (neg? p)) (- v) v))
;;compute the position and velocity of the ball
(defn move [{:keys [x y vx vy] :as ball}]
(let [vx (direction x vx)
vy (direction y vy)]
(assoc ball :x (+ x vx) :y (+ y vy) :vx vx :vy vy)))
;END:direction-and-position
;START:color
(defn color-in-range [c]
(if (< c 0) 0 (if (> c 255) 255 c)))
(defn color [[r g b]]
(Color. ^int (color-in-range r)
^int (color-in-range r)
^int (color-in-range r)))
;END:color
;START:influence-and-color
;;compute influence of each metaball
(defn influence [{:keys [x y radius]} px py]
(let [dx (double (- x px))
dy (double (- y py))]
(double (/ radius (Math/sqrt (+ (* dx dx) (* dy dy)))))))
;;compute the resulting RGB values based on influence
(defn compute-color [x y rgb ball]
(let [influence (influence ball x y)
[r g b] (:color ball)]
(map #(+ %1 (* influence %2)) rgb (:color ball))))
;END:influence-and-color
(defn paint-square [^Graphics g rgb x y size]
(doto g
(.setColor ^Color (color rgb))
(.fillRect x y size size)))
(defn draw [canvas balls]
(let [buffer (.getBufferStrategy canvas)
g (.getDrawGraphics buffer)
step 2]
(try
(loop [x 0]
(loop [y 0]
(paint-square g
;START:compute-colors
;;for each x,y coordinate compute the color
(reduce (partial compute-color x y) [0 0 0] balls)
;END:compute-colors
x y step)
(if (< y (- SIZE step)) (recur (+ y step))))
(if (< x (- SIZE step)) (recur (+ x step))))
(finally (.dispose g)))
(if-not (.contentsLost buffer)
(.show buffer)) ))
(defn metaball []
{:x (rand-int SIZE)
:y (rand-int SIZE)
:vx (inc (rand-int 6))
:vy (inc (rand-int 6))
:radius (+ 40 (rand-int 15))
:color (repeatedly 3 #(rand-int 256))})
(defn start [& [num-balls]]
(let [frame (JFrame. "Metaballs")
canvas (Canvas.)]
(doto frame
(.setSize SIZE SIZE)
(.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
(.setResizable false)
(.add canvas)
(.setVisible true))
(doto canvas
(.createBufferStrategy 2)
(.setVisible true)
(.requestFocus))
;START:main-loop
;;run this in a loop where we move the
;;balls around and render them
(loop [balls (repeatedly (or num-balls 2) metaball)]
(draw canvas balls)
(recur (map move balls)))
;END:main-loop
))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment