Computing a Buddhabrot Fractal - blancas/eisen GitHub Wiki

This sample is a modified version of Nurullah Akkaya's Buddhabrot fractal. In this case, we assume that the main program is to delegate the computation to Eisen code provided by the user. The program is in src/main/resources.

The Host Model

The host data model consists of two items: the square function and a place-holder for the computation function. Function square is provided because it uses the Java Math class, which is not available to Eisen code because interop is currently out of scope. The following code defines the place for the user code and the initial state of the host model.

(def user-code "src/main/resources/fractal/buddhabrot.esn")

(host-model
  sqrt     #(java.lang.Math/sqrt %)  ;; Package Java interop for user code.
  generate identity)                 ;; Generation function to be replaced.

The following function is modified so that it will evaluate the Eisen code.

(defn start [fractal]
  (init-eisen)
  (eisenf user-code)
  (future (call generate fractal)))

The User Code

The following code is a direct translation of the collection of functions that compute and paths and store the results in the fractal map.
The Eisen program is also located in src/main/resources/fractal.

Function abs calls sqrt from the host by appending an underscore (_sqrt). At the end of the definitions the code sets the local main function compute as the new value for generate which is part of the host model and is called by the main program.

The code is somewhat more verbose than it needs to be because is yet to support destructuring and pattern matching.

module buddhabrot

(* Adds two complex numbers. *)

fun add c1 c2 =  map `+` c1 c2

(* Multiplies two complex numbers. *)

fun mult c1 c2 =
  let ra = first c1;
      ia = second c1;
      rb = first c2;
      ib = second c2
  in
      #[ ra * rb - ia * ib, ra * ib + ia * rb ]
  end

(* Absolute value of a complex number. *)

fun abs complex =
  let real = first complex;
      imag = second complex
  in
      _sqrt (real * real + imag * imag);
  end

(* Computes a point's path. *)

fun calcPath x y max =
  let c = #[x, y]
  in
      loop z    = c;
           path = #[];
           iter = 0
      in
           if iter > max then #[]
           else
             if abs z > 2.0 then z : path
             else recur (add c (mult z z)) (z : path) (inc iter)
      end
  end

(* Translates a point into a screen coordinate. *)

fun point2coord size c =
  let real = first c;
      imag = second c
  in
      #[ int (0.3 * size * (real + 0.5) + size / 2), 
         int (0.3 * size * imag + size / 2) ]
  end

(* Sets a point into the fractal's buffer. *)

fun bufferSet fractal point =
  let size   = get fractal :size;
      buffer = get fractal :buffer;
      p      = point2coord size point;
      x      = first p;
      y      = second p
  in
      if x > 0 && y > 0 && x < size && y < size then
         `aset-int` buffer y x (aget buffer y x  + 1)
  end

(* Generates the fractal image. *)

fun compute fractal =
  let buffer = get fractal :buffer;
      iter   = get fractal :iteration
  in
      doseq [point <- iterate inc 1]
          let x    = rand 6 - 3;
              y    = rand 6 - 3;
              path = calcPath x y iter;
          in
              if mod point 1000000 == 0 then
                 println "Point:" point;

              doseq [p <- path] bufferSet fractal p end
          end
      end
  end

(* Installs the local function. *)

setv generate = compute

Running the Program

The following lines will load the code and start the computation. Start the REPL in the Eisen project and paste the following:

(load "fractal/buddhabrot")
(ns fractal.buddhabrot)
(def fractal {:buffer (make-array Integer/TYPE 800 800) :size 800 :iteration 50})
(start fractal)

The fractal's computation takes place in a background thread. Every so often we can display the image with:

(draw fractal)

After a pause a window comes up showing the Buddhabrot image.