I recently read on Hacker News about an intriguing fractal graphic called the Barnsley Fern, and decided to investigate it with the help of uLisp. I wrote an implementation in uLisp and ran it on a MAiX One Dock:
Introduction
The Barnsley Fern is an Iterated Function System (IFS) created by the British mathematician Michael Barnsley that draws a fractal resembling a fern. It uses four affine transformations that, when called over and over again, transform a single point around the screen.
The transformations are defined in uLisp using a two-dimensional array. For example, the soft bracken fern above (Calochlaena dubia) is generated by the following values designed by David Nicholls (see Fractal ferns):
(defvar f
#2A((0 0 0 0.25 0 -0.14 0.02)
(0.85 0.02 -0.02 0.83 0 1.0 0.84)
(0.09 -0.28 0.3 0.11 0 0.6 0.07)
(-0.09 0.28 0.3 0.09 0 0.7 0.07)))
Which of the transformations is picked depends on the last value in each row of the array. So the first transformation is picked with a probability of 0.02, the second with a probability of 0.84, etc.
By altering the numbers you can create fractals with a different appearance. For example, the following values were the ones originally suggested by Michael Barnsley:
(defvar f
#2A((0 0 0 0.16 0 0 0.01)
(0.85 0.04 -0.04 0.85 0 1.6 0.85)
(0.2 -0.26 0.23 0.22 0 1.6 0.07)
(-0.15 0.28 0.26 0.24 0 0.44 0.07)))
(defvar *factor* (/ *height* 12.5))
This gives a fern resembling the black spleenwort (Asplenium adiantum-nigrum):
Here’s the whole program, suitable for running on a 360x240 display. To run it on a different display change these constants:
;
; Barnsley Fern
(defvar *width* 240)
(defvar *height* 360)
(defvar *factor* (/ *height* 7))
(defvar *x-offset* (/ *width* 2))
(defvar *y-offset* (/ *height* 24))
(defvar *dark-green* #b0000001111100000)
The array f defines the transformations:
(defvar f
#2A((0 0 0 0.25 0 -0.14 0.02)
(0.85 0.02 -0.02 0.83 0 1.0 0.84)
(0.09 -0.28 0.3 0.11 0 0.6 0.07)
(-0.09 0.28 0.3 0.09 0 0.7 0.07)))
The program uses these functions:
(defun fn (n)
#'(lambda (x y)
(list (+ (* (aref f n 0) x) (* (aref f n 1) y) (aref f n 4))
(+ (* (aref f n 2) x) (* (aref f n 3) y) (aref f n 5)))))
(defun choose-transform ()
(let ((r (random 1.0)) (p 0))
(dotimes (i 4)
(when (<= r (incf p (aref f i 6))) (return (fn i))))))
(defun plot-pixel (x y)
(let ((xx (round (+ (* *factor* y) *y-offset*)))
(yy (round (- *width* (+ (* *factor* x) *x-offset*)))))
(draw-pixel xx yy *dark-green*)))
Finally, here’s the main function:
(defun fern (&optional (iterations 50000))
(fill-screen #xFFFF)
(let ((x 0) (y 0))
(dotimes (i iterations)
(plot-pixel x y)
(let ((xy (funcall (choose-transform) x y)))
(setq x (first xy))
(setq y (second xy))))))
To plot the fern call:
(fern)