Fractals in Clojure - Newton Fractal
Newton-Raphson method is a technique that is used to find the roots of a function \(f(x)\). Newton-Raphson method finds the values of the roots by approximating the root first then iterating it using the formula \(a' = a - \frac{f(a)}{f'(a)}\), with each iteration we get a more accurate root, this is done until we get a accurate enough root.
(ns newton.core (:refer-clojure :exclude [/ - + * > <]) (:use (clojure.contrib complex-numbers) (clojure.contrib.generic [arithmetic :only [/ - + *]] [comparison :only [> <]] [math-functions :only [abs]]))) (defn convergence [f c step delta] (let [dz #(/ (- (f (+ % (complex step step))) (f %)) (complex step step)) iter #(- % (/ (f %) (dz %)))] (loop [lz c z (iter c) i 0] (if (or (> i 31) (< (abs (- z lz)) delta)) i (recur z (iter z) (inc i))))))
Given a function f and a complex number c, we apply above formula, with each iteration we make better guesses using a step value, we stop either when we have tried 32 times and not find a accurate enough root, or we find an accurate enough root (a' - a will be smaller than the delta). We return the number of iterations required, that is what we use to paint the fractal.
(defn newton [f step delta img-size complex-plane] (let [[width height] img-size [xa xb ya yb] complex-plane] (pmap #(let [[x y] % zx (+ (/ (* x (- xb xa)) (- width 1)) xa) zy (+ (/ (* y (- yb ya)) (- height 1)) ya) c (complex zx zy)] [x y (convergence f c step delta)]) (for [y (range height) x (range width)] [x y]))))
Each pixel on our image will map to a complex number on a complex plane, first we need to calculate the complex number that corresponds to the pixel then calculate the required iterations for that pixel. When done for each pixel we get a sequence of triples representing x,y coordinates and iterations required.
(defn draw [f step delta img-size complex-plane] (let [rgb #(vector (* (mod % 4) 64) (* (mod % 8) 32) (* (mod % 16) 16)) [width height] img-size image (java.awt.image.BufferedImage. width height java.awt.image.BufferedImage/TYPE_INT_RGB) graphics (.createGraphics image) fractal (newton f step delta img-size complex-plane)] (doseq [point fractal] (let [[x y c] point [r g b] (rgb c)] (.setColor graphics (java.awt.Color. r g b)) (.drawLine graphics x y x y))) (doto (javax.swing.JFrame.) (.add (proxy [javax.swing.JPanel] [] (paint [g] (.drawImage g image 0 0 this)))) (.setSize (java.awt.Dimension. width height)) (.show))))
Drawing is done by iterating over the x,y,iteration triples and painting the pixel using a color depending on the iterations calculated (speed of convergence to a particular solution).
(draw (fn [z] (- (* z z z) 1)) 0.000006 0.003 [512 512] [-1.0 1.0 -1.0 1.0])
(draw (fn [z] (+ (- (* z z z) (* 2 z)) 2)) 0.000006 0.003 [512 512] [-1.0 1.0 -1.0 1.0])
(draw (fn [z] (complex (* (Math/sin (real z)) (Math/cosh (imag z))) (* (Math/cos (real z)) (Math/sinh (imag z))))) 0.000006 0.003 [512 512] [-2.0 2.0 -2.0 2.0])
(draw (fn [z] (- (* z z z z) 1)) 0.000006 0.003 [512 512] [-1.0 1.0 -1.0 1.0])