0

I wanted to learn Clojure and started with a Mandelbrot generator using quil, I got it to work - however it takes some time to generate images and is a massive resource hog. Any advice for how to make it faster or if you spot any un-clojure-esque parts of my code.

Core.clj

(ns frac.core
  (:require [quil.core :as q])
  (:require [frac.complex :refer :all]))

(def scale (atom 0.01))
(def xoff (atom -200))
(def yoff (atom -200))
(def its 50)

(defn mandelbrot [r i]
 (count (take its (take-while #(<= (modu %) 2) (iterate #( add (mul % %) [r i]) [r i])))))

(defn gen []
  (let [p (q/pixels)
        w (q/width)
        h (q/height)]
    (doseq [x (range w) y (range h)]
      (let [m (mandelbrot (* @scale (+ x @xoff)) (* @scale (+ y @yoff)))
            c (if (= m its) 0 m)]
        (aset p (+ x (* y w)) (q/color (* 1.5 c) (* 4 c) (* 5.2 c))))))
  (q/update-pixels))

(defn setup []
  (gen))

(defn draw [])

(defn click []
  (swap! xoff #(+ (q/mouse-x) (- (/ (q/width) 2)) %))
  (swap! yoff #(+ (q/mouse-y) (- (/ (q/height) 2)) %))
  (gen))

(defn wheel [z] 
  (swap! scale #(if (pos? z) (* 1.1 %) (* 0.9 %)))
  (prn @scale)
  (gen))

(q/defsketch example
  :title "Mandel"
  :setup setup
  :draw draw
  :size [400 400]
  :mouse-clicked click
  :mouse-wheel wheel)

(defn -main [& args])

Complex.clj

(ns frac.complex)

(defn mul [z1 z2]
  (let [r1 (first z1)
        i1 (second z1)
        r2 (first z2)
        i2 (second z2)]
    [(- (* r1 r2) (* i1 i2)) (+ (* r1 i2) (* r2 i1))]))

(defn add [z1 z2]
  (let [r1 (first z1)
        i1 (second z1)
        r2 (first z2)
        i2 (second z2)]
    [(+ r1 r2) (+ i1 i2)]))

(defn modu [z]
  (let [r (first z)
        i (second z)]
    (Math/sqrt (+ (* r r) (* i i)))))
Xycaan
  • 1
  • maybe not related to performance, but you might consider using destructuring with your mul/add fn (e.g. `defn mul [[r1 i1] [r2 i2]]` for easier readability – cfrick Nov 24 '15 at 13:08
  • 3
    This should be moved to [Code Review](http://codereview.stackexchange.com/). – Sam Estep Nov 24 '15 at 13:09
  • 1
    I don't follow Clojure, but to be efficient; for each generation, precalculate two 1-D arrays scaling the pixel positions to the map. More importantly take whatever steps you can to reduce the execution time of each iteration. There is no need to take a square root to find the modulus. Instead of comparing the modulus with an "escape value" of say 2, compare its square with 4. And as you need x^2 and y^2 (squared) for each iteration, don't calculate them twice. Try not to call functions from within the iteration loop, and note that you don't even need complex functions. – Weather Vane Nov 24 '15 at 13:44

1 Answers1

0

Try set this:

(set! *unchecked-math* :warn-on-boxed)

and remove all warnings. Use type hints as needed.

Davyzhu
  • 1,109
  • 9
  • 17