;;; Mandelbrot set fractal.

(define centerx -0.5) ; real axis coordinate
(define centery 0.0) ; imaginary axis coordinate
(define width 4.5) ; real axis view width
(define ITERATIONS 20)

(define radius^2 16.0)

(define zr 0.0)
(define zi 0.0)

(define (map-monochrome value) ; input value : 0 .. 511
  (if (<= value 255)
    value
    (begin
      (set! value (- value 256))
      (+ (* value 256 256) (* value 256) 255))))

(define (modulus x y)
  (sqrt (+ (* x x) (* y y))))

(define (plot x y r g b)
  (plotRGB ctx x y r g b)) ; interface to javascript

(define (plot-pixel x y value)
  (let
    (((remainder value 256))
     ((remainder (quotient value 256) 256))
     ((remainder (quotient value (* 256 256)) 256)))
    (plot x y r g b)))

(define (display-pixel-value n-iter x y)
  (let
    ((mu
      (if (= n-iter ITERATIONS)
        0.0
        (+ n-iter (- 1.0 (/ (log (log (modulus zr zi))) (log 2.0)))))))
    (let
      ((value (inexact->exact (truncate
        (* 512.0 (/ mu ITERATIONS))))))
      (plot-pixel x y (map-monochrome value)))))

(define (save c x y)
  (set! zr x)
  (set! zi y)
  c)

(define (iterate-pixel r i step x y radius^2)
  (let ((cr (+ r (* (exact->inexact x) step)))
        (ci (+ i (* (exact->inexact y) step))))
    (let loop ((zr cr)
               (zi ci)
               (0))
      (if (= c ITERATIONS)
        (save c zr zi)
        (let ((zr^2 (* zr zr))
              (zi^2 (* zi zi)))
          (if (> (+ zr^2 zi^2) radius^2)
            (save c zr zi)
            (let ((new-zr (+ (- zr^2 zi^2) cr))
                  (new-zi (+ (* 2.0 (* zr zi)) ci)))
              (loop new-zr new-zi (+ c 1)))))))))

(define (mandel r i step width height)
  (do (((- height 1) (- y 1))) ((< y 0))
    (do ((0 (+ x 1))) ((= x width))
      (display-pixel-value (iterate-pixel r i step x y radius^2) x y))))

(define (upper-left-x center-x width)
  (- center-x (* width 0.5)))

(define (upper-left-y center-y width)
  (- center-y (* width 0.5)))

(define (get-step image-size width)
  (/ width image-size))
  
(define (mandelbrot)
  (let
     ((image-width 320) ; display resolution
      (image-height 240)) ; display resolution
    (let
      ((height (* image-height (/ width image-width))))
      (mandel
        (upper-left-x centerx width)
        (upper-left-y centery height)
        (get-step image-width width)
        image-width image-height))))

(define (mouseClick x y)
  (set! centerx
    (+ (upper-left-x centerx width) (* width (/ (exact->inexact x) 320))))
  (let
    ((height (* 240 (/ width 320))))
    (set! centery
      (+ (upper-left-y centery height) (* height (/ (exact->inexact y) 240)))))
  (set! width (* 0.3 width)))