(ql:quickload '(:alexandria :png :iterate :flexi-streams :lparallel :cl-cpus)) (setf lparallel:*kernel* (lparallel:make-kernel (cpus:get-number-of-processors))) (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))) (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)) (make-instance 'julia-widget)