In [1]:
(ql:quickload '(:alexandria :png :iterate :flexi-streams :lparallel :cl-cpus))
To load "alexandria":
  Load 1 ASDF system:
    alexandria

; Loading "alexandria"

To load "png":
  Load 1 ASDF system:
    png

; Loading "png"
[package image]...................................
[package png]; cc -o /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/grovel__grovel-tmpAR3FSGEY.o -c -march=x86-64 -mtune=generic -O2 -pipe -fno-plt -D_GNU_SOURCE -fno-omit-frame-pointer -DSBCL_HOME=/usr/lib/sbcl -g -Wall -Wundef -Wsign-compare -Wpointer-arith -O3 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wunused-parameter -Wimplicit-fallthrough -fno-omit-frame-pointer -momit-leaf-frame-pointer -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/usr/X11/include -I/opt/local/include -fPIC -I/home/yitzi/quicklisp/dists/quicklisp/software/cffi_0.23.0/ /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/grovel__grovel.c
.; cc -o /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/grovel__grovel-tmpJAIDFZTC -Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now -g -Wl,--export-dynamic /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/grovel__grovel.o
; /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/grovel__grovel /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/grovel__grovel.grovel-tmp.lisp
; cc -o /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/wrappers__wrapper-tmp9V47YWQF.o -c -march=x86-64 -mtune=generic -O2 -pipe -fno-plt -D_GNU_SOURCE -fno-omit-frame-pointer -DSBCL_HOME=/usr/lib/sbcl -g -Wall -Wundef -Wsign-compare -Wpointer-arith -O3 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wunused-parameter -Wimplicit-fallthrough -fno-omit-frame-pointer -momit-leaf-frame-pointer -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/usr/X11/include -I/opt/local/include -fPIC -I/home/yitzi/quicklisp/dists/quicklisp/software/cffi_0.23.0/ /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/wrappers__wrapper.c
; cc -o /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/wrappers.so -shared -march=x86-64 -mtune=generic -O2 -pipe -fno-plt -D_GNU_SOURCE -fno-omit-frame-pointer -DSBCL_HOME=/usr/lib/sbcl -g -Wall -Wundef -Wsign-compare -Wpointer-arith -O3 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wunused-parameter -Wimplicit-fallthrough -fno-omit-frame-pointer -momit-leaf-frame-pointer /home/yitzi/.cache/common-lisp/sbcl-2.0.8-linux-x64/home/yitzi/quicklisp/dists/quicklisp/software/cl-png-vl-anyversion-11b965fe-git/wrappers__wrapper.o
....................................
[package bmp].....................................
[package pnm]......
To load "iterate":
  Load 1 ASDF system:
    iterate

; Loading "iterate"

To load "flexi-streams":
  Load 1 ASDF system:
    flexi-streams

; Loading "flexi-streams"

To load "lparallel":
  Load 1 ASDF system:
    lparallel

; Loading "lparallel"
[package lparallel.util]..........................
[package lparallel.thread-util]...................
[package lparallel.raw-queue].....................
[package lparallel.cons-queue]....................
[package lparallel.vector-queue]..................
[package lparallel.queue].........................
[package lparallel.counter].......................
[package lparallel.spin-queue]....................
[package lparallel.kernel]........................
[package lparallel.kernel-util]...................
[package lparallel.promise].......................
[package lparallel.ptree].........................
[package lparallel.slet]..........................
[package lparallel.defpun]........................
[package lparallel.cognate].......................
[package lparallel]
To load "cl-cpus":
  Load 1 ASDF system:
    cl-cpus

; Loading "cl-cpus"
[package cl-cpus]
Out[1]:
(:ALEXANDRIA :PNG :ITERATE :FLEXI-STREAMS :LPARALLEL :CL-CPUS)

In [2]:
(setf lparallel:*kernel* (lparallel:make-kernel (cpus:get-number-of-processors)))
Out[2]:
#<LPARALLEL.KERNEL:KERNEL :NAME "lparallel" :WORKER-COUNT 16 :USE-CALLER NIL :ALIVE T :SPIN-COUNT 2000 {1004117613}>
In [3]:
(defun julia-count (z c &key (max 255))
  (do ((i 0 (1+ i))
       (p z (+ (expt p 2) c)))
      ((or (= i max)
           (> (abs p) 2))
        i)))
Out[3]:
JULIA-COUNT
In [4]:
(defclass julia-widget (jupyter-widgets:grid-box)
  ((image
     :reader julia-image
     :initform (make-instance 'jupyter-widgets:image
                              :width 640 :height 640
                              :layout (make-instance 'jupyter-widgets:layout :grid-area "image")))
   (frame
     :reader julia-frame
     :initform (png:make-image 640 640 1 8))
   (x
     :reader julia-x
     :initform (make-instance 'jupyter-widgets:float-text
                              :value 0 :description "x"
                              :layout (make-instance 'jupyter-widgets:layout :grid-area "x")))
   (y
     :reader julia-y
     :initform (make-instance 'jupyter-widgets:float-text
                              :value 0 :description "y"
                              :layout (make-instance 'jupyter-widgets:layout :grid-area "y")))
   (size
     :reader julia-size
     :initform (make-instance 'jupyter-widgets:float-text
                              :value 4 :description "size"
                              :layout (make-instance 'jupyter-widgets:layout :grid-area "size")))
   (ca
     :reader julia-ca
     :initform (make-instance 'jupyter-widgets:float-text
                              :value -0.8 :step 0.001 :description "ca"
                              :layout (make-instance 'jupyter-widgets:layout :grid-area "ca")))
   (cb
     :reader julia-cb
     :initform (make-instance 'jupyter-widgets:float-text
                              :value 0.156 :step 0.001 :description "cb"
                              :layout (make-instance 'jupyter-widgets:layout :grid-area "cb")))
   (progress
     :reader julia-progress
     :initform (make-instance 'jupyter-widgets:int-progress
                              :description "Progress"
                              :max 640
                              :layout (make-instance 'jupyter-widgets:layout :grid-area "progress")))
   (task-channel
     :reader julia-task-channel
     :initform (lparallel:make-channel)))
  (:metaclass jupyter-widgets:trait-metaclass)
  (:default-initargs
    :layout (make-instance 'jupyter-widgets:layout
                           :grid-gap ".25em"
                           :grid-template-columns "1fr min-content"
                           :grid-template-rows "min-content min-content min-content min-content min-content min-content 1fr"
                           :grid-template-areas "\"image x\" \"image y\" \"image size\" \"image ca\" \"image cb\" \"image progress\" \"image .\"")))


(defun calculate-row (frame y c width height xmin xmax ymin ymax)
  (let ((zy (+ ymin (* (coerce (/ y height) 'float) (- ymax ymin)))))
    (dotimes (x width)
      (setf (aref frame y x 0)
            (- 255 (julia-count (complex (+ xmin (* (coerce (/ x width) 'float) (- xmax xmin))) zy) c))))))


(defun update (instance)
  (bordeaux-threads:make-thread
    (lambda ()
      (with-slots (task-channel image frame x y size ca cb progress) instance
        (let* ((c (complex (jupyter-widgets:widget-value ca)
                           (jupyter-widgets:widget-value cb)))
               (x-value (jupyter-widgets:widget-value x))
               (y-value (jupyter-widgets:widget-value y))
               (size-value (jupyter-widgets:widget-value size))
               (width (jupyter-widgets:widget-width image))
               (height (jupyter-widgets: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))))
          (dotimes (y height)
            (lparallel:submit-task task-channel #'calculate-row frame y c width height xmin xmax ymin ymax))
          (dotimes (y height)
            (lparallel:receive-result task-channel)
            (setf (jupyter-widgets:widget-value progress) y))
          (setf (jupyter-widgets:widget-value image)
                (flexi-streams:with-output-to-sequence (o)
                                                       (png:encode frame o))))))))



(defmethod initialize-instance :after ((instance julia-widget) &rest initargs &key &allow-other-keys)
  (declare (ignore initargs))
  (jupyter-widgets:observe (julia-x instance) :value
                           (lambda (inst type name old-value new-value source)
                             (declare (ignore inst type name old-value new-value source))
                             (update instance)))
  (jupyter-widgets:observe (julia-y instance) :value
                           (lambda (inst type name old-value new-value source)
                             (declare (ignore inst type name old-value new-value source))
                             (update instance)))
  (jupyter-widgets:observe (julia-size instance) :value
                           (lambda (inst type name old-value new-value source)
                             (declare (ignore inst type name old-value new-value source))
                             (update instance)))
  (jupyter-widgets:observe (julia-ca instance) :value
                           (lambda (inst type name old-value new-value source)
                             (declare (ignore inst type name old-value new-value source))
                             (update instance)))
  (jupyter-widgets:observe (julia-cb instance) :value
                           (lambda (inst type name old-value new-value source)
                             (declare (ignore inst type name old-value new-value source))
                             (update instance)))
  (setf (jupyter-widgets:widget-children instance)
        (list (julia-image instance)
              (julia-x instance)
              (julia-y instance)
              (julia-size instance)
              (julia-ca instance)
              (julia-cb instance)
              (julia-progress instance)))
  (update instance))
Out[4]:
#<JUPYTER-WIDGETS:TRAIT-METACLASS COMMON-LISP-USER::JULIA-WIDGET>
Out[4]:
CALCULATE-ROW
Out[4]:
UPDATE
Out[4]:
#<STANDARD-METHOD COMMON-LISP:INITIALIZE-INSTANCE :AFTER (JULIA-WIDGET) {100480FA03}>
In [5]:
(make-instance 'julia-widget)
In [ ]: