## ~amtunlimited/mandelbrot-plot

211f6fbd17d64824e95ccec9fc86b888b3fdd81b — Aaron Tagliaboschi 2 years ago
```Shifted stuff around, added hue, added interpolated bands
```
```7 files changed, 139 insertions(+), 88 deletions(-)

A hue-to-rgb.lisp
M make-plot.lisp
M mandelbrot-plot.asd
M mandelbrot.lisp
D mandelbrot.out
M packages.lisp
M plot.lisp
```
`A hue-to-rgb.lisp => hue-to-rgb.lisp +33 -0`
```@@ 0,0 1,33 @@
+(in-package :dev.aarontag.hue-to-rgb)
+
+;;; Makes a linear function out of two lines
+(defun two-points (x1 y1 x2 y2)
+  (lambda (x)
+    (let* ((m (/ (- y1 y2) (- x1 x2)))
+           (b (- y1 (* m x1))))
+       (+ (* m x) b))))
+
+;;; Take number between 0 and 1
+(defun hue-curve (x-real &key (width 1) (offset 0))
+  (let ((dn-sixth (two-points 1/6 1 2/6 0))
+        (up-sixth (two-points 4/6 0 5/6 1))
+        (x (mod (-(/ (float x-real) width) offset) 1)))
+         ;; just the decimal point...
+    (cond
+      ((< x 1/6) 1)
+      ((< x 2/6) (funcall dn-sixth x))
+      ((< x 4/6) 0)
+      ((< x 5/6) (funcall up-sixth x))
+      ((< x 6/6) 1)
+      (t 1))))
+
+(defun hue-to-rgb (x)
+  (if (= x 0) (return-from hue-to-rgb '(0 0 0)))
+  (mapcar (lambda (offset) (floor (* (hue-curve x :offset offset) 255)))
+          '(0 1/3 2/3)))
+
+;; (defun hue-plot ()
+;;   (ppm-plot (lambda (x y) (hue-to-rgb x))
+;;             (lambda (x) (apply #'format nil "~d ~d ~d " x))
+;;             (range 1 :steps 256)
+;;             (range 300)))

```
`M make-plot.lisp => make-plot.lisp +56 -4`
```@@ 1,7 1,59 @@
-(in-package "COMMON-LISP-USER")
-
(require 'asdf)
-
-(dev.aarontag.mandelbrot-plot:mandelbrot-plot)>
\ No newline at end of file
+
+(defpackage :dev.aarontag.make-plot
+  (:use :common-lisp
+        :dev.aarontag.simple-plot
+        :dev.aarontag.mandelbrot
+        :dev.aarontag.hue-to-rgb))
+
+(in-package :dev.aarontag.make-plot)
+
+(with-open-file (file "mandelbrot.ppm"
+                      :direction :output
+                      :if-exists :rename
+                      :if-does-not-exist :create)
+
+  (ppm-plot (lambda (x y) (mandelbrot (complex x y)))
+            (lambda (x) (apply #'format nil "~d ~d ~d " (hue-to-rgb (/ x 255))))
+            (range 0.50 :start -2.00 :steps 5000)
+            (range 1.25 :start -1.25 :steps 5000)
+            :file file))
+
+;; Should look something like this:
+;;                                                                   *
+;;                                                               * * * *
+;;                                                               * * * *
+;;                                                               * * * *
+;;                                                           * *
+;;                                                   *     * * * * * * * * * *
+;;                                                   * * * * * * * * * * * * * * * * *
+;;                                                   * * * * * * * * * * * * * * * * *
+;;                                                 * * * * * * * * * * * * * * * * *
+;;                                               * * * * * * * * * * * * * * * * * * *
+;;                                           * * * * * * * * * * * * * * * * * * * * * * *
+;;                                             * * * * * * * * * * * * * * * * * * * * *
+;;                         *   * *           * * * * * * * * * * * * * * * * * * * * * *
+;;                         * * * * * * *     * * * * * * * * * * * * * * * * * * * * * *
+;;                       * * * * * * * * *   * * * * * * * * * * * * * * * * * * * * * *
+;;                       * * * * * * * * *   * * * * * * * * * * * * * * * * * * * * * *
+;;                   *   * * * * * * * * *   * * * * * * * * * * * * * * * * * * * * *
+;; *             * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+;;                   *   * * * * * * * * *   * * * * * * * * * * * * * * * * * * * * *
+;;                       * * * * * * * * *   * * * * * * * * * * * * * * * * * * * * * *
+;;                       * * * * * * * * *   * * * * * * * * * * * * * * * * * * * * * *
+;;                         * * * * * * *     * * * * * * * * * * * * * * * * * * * * * *
+;;                         *   * *           * * * * * * * * * * * * * * * * * * * * * *
+;;                                             * * * * * * * * * * * * * * * * * * * * *
+;;                                           * * * * * * * * * * * * * * * * * * * * * * *
+;;                                               * * * * * * * * * * * * * * * * * * *
+;;                                             *   * * * * * * * * * * * * * * * * *
+;;                                                   * * * * * * * * * * * * * * * * *
+;;                                                   * * * * * * * * * * * * * * * * *
+;;                                                   *     * * * * * * * * * *
+
+;;                                                               * * * *
+;;                                                               * * * *
+;;                                                               * * * *
+;;                                                                   *

```
`M mandelbrot-plot.asd => mandelbrot-plot.asd +2 -1`
```@@ 7,4 7,5 @@
:serial t
:components ((:file "packages")
(:file "plot")
-               (:file "mandelbrot")))>
\ No newline at end of file
+               (:file "mandelbrot")
+               (:file "hue-to-rgb")))<
\ No newline at end of file

```
`M mandelbrot.lisp => mandelbrot.lisp +21 -21`
```@@ 1,29 1,29 @@
-(in-package :dev.aarontag.mandelbrot-plot)
+(in-package :dev.aarontag.mandelbrot)
+
+(defvar *max-iter* 255)

(defun mag (c)
(let ((r (realpart c)) (i (imagpart c)))
-    (sqrt (+ (* r r)
-             (* i i)))))
+     (+ (* r r)
+        (* i i))))

-;;; Where it's all at
-(defun mandel (z c)
-  (+ (* z z) c))
+;;; Takes z and n and makes a smooth interpolation between the different
+;;; iteration bands. tbh I don't quite understand it, but it's very pretty
+(defun interpolate (z n)
+  (let* ((x (realpart z))
+         (y (imagpart z))
+         (log-zn (/ (log (+ (* x x) (* y y))) 2))
+         (log2 (log 2))
+         (nu (/ (log (/ log-zn log2)) log2)))
+    (+ n (- 1 nu))))

;;; Ended up being a lot simpler than expected tbh
(defun mandelbrot (c)
(let ((z (complex 0 0)))
-    (dotimes (i 1000) ; TODO: make tunable?
-      (setf z (mandel z c))
-      (if (>= (mag z) 2)
-          (return-from mandelbrot nil))))
-  t)
-
-(defun mandelbrot-xy (x y)
-  (mandelbrot (complex x y)))
-
-
-;;; TODO: maybe find interesting edges
-(defun mandelbrot-plot ()
-      (plot #'mandelbrot-xy
-        (range 0.5 :start -2.0 :inc 0.05)
-        (range 1.1 :start -1.1 :inc 0.05)))
+    (dotimes (i *max-iter*)
+      ;; A lotta magic in this line; are you watching closely?
+      (setf z (+ (* z z) c))
+      (if (>= (mag z) 512)
+        (return-from mandelbrot (interpolate z i)))))
+
+  0)<
\ No newline at end of file

```
`D mandelbrot.out => mandelbrot.out +0 -44`
```@@ 1,44 0,0 @@
-
-
-
-
-
-                                                                            *
-                                                                        * * * *
-                                                                        * * * *
-                                                                        * * * *
-                                                                    * *
-                                                            *     * * * * * * * * * *
-                                                            * * * * * * * * * * * * * * * * *
-                                                            * * * * * * * * * * * * * * * * *
-                                                          * * * * * * * * * * * * * * * * *
-                                                        * * * * * * * * * * * * * * * * * * *
-                                                    * * * * * * * * * * * * * * * * * * * * * * *
-                                                      * * * * * * * * * * * * * * * * * * * * *
-                                  *   * *           * * * * * * * * * * * * * * * * * * * * * *
-                                  * * * * * * *     * * * * * * * * * * * * * * * * * * * * * *
-                                * * * * * * * * *   * * * * * * * * * * * * * * * * * * * * * *
-                                * * * * * * * * *   * * * * * * * * * * * * * * * * * * * * * *
-                            *   * * * * * * * * *   * * * * * * * * * * * * * * * * * * * * *
-          *             * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-                            *   * * * * * * * * *   * * * * * * * * * * * * * * * * * * * * *
-                                * * * * * * * * *   * * * * * * * * * * * * * * * * * * * * * *
-                                * * * * * * * * *   * * * * * * * * * * * * * * * * * * * * * *
-                                  * * * * * * *     * * * * * * * * * * * * * * * * * * * * * *
-                                  *   * *           * * * * * * * * * * * * * * * * * * * * * *
-                                                      * * * * * * * * * * * * * * * * * * * * *
-                                                    * * * * * * * * * * * * * * * * * * * * * * *
-                                                        * * * * * * * * * * * * * * * * * * *
-                                                      *   * * * * * * * * * * * * * * * * *
-                                                            * * * * * * * * * * * * * * * * *
-                                                            * * * * * * * * * * * * * * * * *
-                                                            *     * * * * * * * * * *
-
-                                                                        * * * *
-                                                                        * * * *
-                                                                        * * * *
-                                                                            *
-
-
-
-

```
`M packages.lisp => packages.lisp +8 -5`
```@@ 2,9 2,12 @@

(defpackage :dev.aarontag.simple-plot
(:use :common-lisp)
-  (:export :range :plot))
+  (:export :range :ppm-plot))

-(defpackage :dev.aarontag.mandelbrot-plot
-  (:use :dev.aarontag.simple-plot
-        :common-lisp)
-  (:export :mandelbrot-plot))
+(defpackage :dev.aarontag.mandelbrot
+  (:use :common-lisp)
+  (:export :mandelbrot))
+
+(defpackage :dev.aarontag.hue-to-rgb
+  (:use :common-lisp)
+  (:export :hue-to-rgb)) <
\ No newline at end of file

```
`M plot.lisp => plot.lisp +19 -13`
```@@ 4,23 4,29 @@

;;; utility to make a range of numbers
;;; this could probably be done with a loop but meh
-(defun range (end &key (start 0) (inc 1))
-  (if (< start end)
-      (cons start (range end
-                         :start (+ start inc)
-                         :inc inc))))
-
-(defun vis-print (b)
-  (if b "*" " "))
+(defun range (end &key (start 0) (steps nil) (inc 1))
+  (let ((inc (if steps
+                 (/ (- end start) steps)
+                 inc)))
+    (if (< start end)
+        (cons start (range end
+                           :start (+ start inc)
+                           :inc inc)))))

;;; Simple iterative design
;;; Decided to leave ranges out, makes things cleaner
-(defun plot (selector-fn x-list y-list)
+(defun plot (fn num-to-print x-list y-list &key (file t))
(dolist (y y-list)
(dolist (x x-list)
;; On my terminal, characters are ~twice as tall, so this ends up looking
;; square
-      (format t "~a "
-              (vis-print (funcall selector-fn x y))))
-    (format t "~%")))
+      (format file "~a"
+              (funcall num-to-print (funcall fn x y))))
+    (format file "~%")))
+
+(defun ppm-plot (fn num-to-print x-list y-list &key (file t))
+  (format file "P3 ~d ~d ~d ~%"
+      (length x-list)
+      (length y-list)
+      255)
+  (plot fn num-to-print x-list y-list :file file))

```