Written by
Matthew Bilyeu
on
on
painting clouds with clojure
Over the 4th of July weekend I made this little program to generate images of clouds
(ns clouds.core
(:gen-class)
(:import [java.awt.image BufferedImage]
[java.io File]
[javax.imageio ImageIO]
[javax.swing JPanel JFrame SwingUtilities]
[java.awt Graphics Color Dimension RenderingHints]))
(def width 500)
(def height 500)
(def num-particles 1000000)
(def color-cache (atom {}))
(def output-image? true)
(defn- rand-between [min max]
(+ (rand-int (- max min)) min))
(defn- make-gray-color [color-val alpha]
(let [color-key (keyword (str color-val "-" alpha))
cached-color (color-key @color-cache)]
(if cached-color
cached-color
(let [^Color new-color (Color. color-val
color-val
color-val
alpha)]
(swap! color-cache assoc color-key new-color)
new-color))))
(defn- paint-clouds [^Graphics graphics]
(loop [n 0
last-x (rand-int width)
last-y (rand-int height)]
(let [rand-op (if (< (rand) 0.5) inc dec)
rand-axis (if (< (rand) 0.5) :vert :horiz)
new-x (if (= rand-axis :horiz)
(rand-op last-x)
last-x)
new-y (if (= rand-axis :vert)
(rand-op last-y)
last-y)
rand-gray (rand-between 250 255)
rand-alpha (rand-int 75)
neighbor-alpha-modifier 0.11
particle-color (make-gray-color rand-gray rand-alpha)
neighbor-color (make-gray-color rand-gray
(int (* rand-alpha
neighbor-alpha-modifier)))]
(doall
(for [x-offset (range -1 2)
y-offset (range -1 2)
:let [x (+ new-x x-offset)
y (+ new-y y-offset)]
:when (and (<= 0 x width)
(<= 0 y height)
(or (= x-offset y-offset 0)
(not= x-offset y-offset)))]
(let [^Color color (if (= x-offset y-offset 0)
particle-color
neighbor-color)]
(doto graphics
(.setColor color)
(.drawLine x y x y)))))
(when (< n num-particles)
(recur (inc n) new-x new-y)))))
(defn- painter []
(proxy [JPanel] []
(paint [^Graphics graphics]
(let [^int width (proxy-super getWidth)
^int height (proxy-super getHeight)]
(doto graphics
(.setRenderingHint RenderingHints/KEY_ANTIALIASING
RenderingHints/VALUE_ANTIALIAS_ON)
(.setRenderingHint RenderingHints/KEY_INTERPOLATION
RenderingHints/VALUE_INTERPOLATION_BICUBIC)
(.setColor (Color. 135 206 250))
(.fillRect 0 0 width height))
(paint-clouds graphics)))))
(defn- gen []
(let [^JPanel painting-panel (painter)
^Dimension dim (Dimension. width height)]
(doto painting-panel
(.setSize dim)
(.setPreferredSize dim))
(if output-image?
(let [^BufferedImage bi (BufferedImage. width
height
BufferedImage/TYPE_INT_ARGB)
^Graphics graphics (.createGraphics bi)]
(.paint painting-panel graphics)
(ImageIO/write bi "png" (File. (str "output/"
(System/currentTimeMillis)
".png"))))
(let [^JFrame frame (JFrame. "clouds")]
(.add (.getContentPane frame) painting-panel)
(doto frame
(.pack)
(.setVisible true))))))
(defn -main
[& args]
(gen))