~amtunlimited/mandelbrot-plot

211f6fbd17d64824e95ccec9fc86b888b3fdd81b — Aaron Tagliaboschi 1 year, 11 months ago abcd003
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)

(load "mandelbrot-plot.asd")
(asdf:load-system :mandelbrot-plot)
(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)))) 

;;; the deadist of simplist
(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))