In [ ]:
(ql:quickload '(:alexandria :png :iterate :flexi-streams))
(use-package :alexandria)
(use-package :iterate)
(use-package :jupyter-widgets)
In [ ]:
(defun julia-count (z c &key (max 255))
    (iter
        (for i from 0 to (1- max))
        (for p first z then (+ (expt p 2) c))
        (while (<= (abs p) 2))
        (finally (return i))))
In [ ]:
(defclass julia-widget ()
    ((image :reader julia-image
            :initform (make-instance 'image :width 640 :height 640))
     (x :reader julia-x
        :initform (make-instance 'float-text :value 0 :description "x"))
     (y :reader julia-y
        :initform (make-instance 'float-text :value 0 :description "y"))
     (size :reader julia-size
           :initform (make-instance 'float-text :value 4 :description "size"))
     (ca :reader julia-ca
         :initform (make-instance 'float-text :value -0.8 :step 0.001 :description "ca"))
     (cb :reader julia-cb
         :initform (make-instance 'float-text :value 0.156 :step 0.001 :description "cb"))
     (progress :reader julia-progress
               :initform (make-instance 'int-progress :max 640 :description "Complete"))
     (controls :reader julia-controls
               :initform (make-instance 'v-box))
     (container :reader julia-container
                :initform (make-instance 'h-box))))

(defvar jw (make-instance 'julia-widget))

(defparameter *update-thread* nil)

(defun make-julia (image progress c &key (width 100) (height 100) (xmin -2.5) (xmax 2.5) (ymin -2.5) (ymax 2.5))
    (let ((img (png:make-image height width 1 8)))
        (iter
            (for x from 0 to (1- width))
            (for zx next (+ xmin (* (coerce (/ x width) 'float) (- xmax xmin))))
            (setf (widget-value progress) x)
            (iter
                (for y from 0 to (1- height))
                (for zy next (+ ymin (* (coerce (/ y height) 'float) (- ymax ymin))))
                (for j next (julia-count (complex zx zy) c))
                (setf (aref img y x 0) (- 255 j))))
        (setf (widget-value image) (flexi-streams:with-output-to-sequence (o)
            (png:encode img o)))
        nil))

(defun update ()
    (when (and *update-thread* (bordeaux-threads:thread-alive-p *update-thread*))
        (bordeaux-threads:destroy-thread *update-thread*))
    (setq *update-thread* (bordeaux-threads:make-thread
        (lambda ()
            (with-slots (image x y size ca cb progress) jw
                (when-let ((ca-value (widget-value ca))
                           (cb-value (widget-value cb))
                           (x-value (widget-value x))
                           (y-value (widget-value y))
                           (size-value (widget-value size)))
                    (make-julia image progress (complex ca-value cb-value)
                                :width (widget-width image) :height (widget-height image)
                                :xmin (- x-value (/ size-value 2))
                                :xmax (+ x-value (/ size-value 2))
                                :ymin (- y-value (/ size-value 2))
                                :ymax (+ y-value (/ size-value 2)))))))))
                    

(with-slots (image x y size ca cb controls container progress) jw
    (setf (widget-children controls) (list x y size ca cb progress))
    (setf (widget-children container) (list image controls))
    (defmethod jupyter-widgets:on-trait-change :after ((object (eql x)) type name old-value new-value)
        (update))
    (defmethod jupyter-widgets:on-trait-change :after ((object (eql y)) type name old-value new-value)
        (update))
    (defmethod jupyter-widgets:on-trait-change :after ((object (eql size)) type name old-value new-value)
        (update))
    (defmethod jupyter-widgets:on-trait-change :after ((object (eql ca)) type name old-value new-value)
        (update))
    (defmethod jupyter-widgets:on-trait-change :after ((object (eql cb)) type name old-value new-value)
        (update))
    (update))
In [ ]:
(julia-container jw)
In [ ]: