この記事は、 Lisp Advent Calendar 2019の2日目の記事です。
ABCLでマンデルブロ集合
ABCL(Armed Bear Common Lisp)でマンデルブロ集合を表示させてみました。
ABCL(Armed Bear Common Lisp)はJavaで作られたCommonLispです。そのため、Javaが動く環境ならばどこでも動きます。また、Javaの豊富なライブラリを利用することができます。JavaのSwingを使ってマンデルブロ集合を描画してみました。
jnewでJavaのインスタンスの作成、jcallでメソッドの呼び出し、jstaticでクラスメソッドの呼び出し、jfieldでフィールドの参照ができます。詳細はソースを見てください。
test/Mandelbrot.lisp
; Mandelbrot Set
; (load "test/Mandelbrot.lisp")
; Java> new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
(defun createImage (w h)
(jnew "java.awt.image.BufferedImage"
w h (jfield "java.awt.image.BufferedImage" "TYPE_4BYTE_ABGR")))
; Java> image.setRGB(x, y, rgb);
(defun putPixel (image x y rgb)
(jcall "setRGB" image x y rgb))
; Java> g.setColor(color);
; Java> g.fillRect(x1, y1, x2, y2);
(defun fillRect (g x1 y1 x2 y2 color)
(progn
(jcall "setColor" g color)
(jcall "fillRect" g x1 y1 x2 y2)))
; Java> Color.HSBtoRGB(h, s, b);
(defun convertHSBtoRGB (h s b)
(jstatic "HSBtoRGB" "java.awt.Color" h s b))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; マンデルブロ集合の漸化式
; z[n+1] = z[n]^2 + c
(defun f (x y a b)
(cons
(+ (- (* x x) (* y y)) a)
(+ (* 2 x y) b)))
; 発散するか? 絶対値が2以上になったか?
(defun isDiverge (x y)
(> (+ (* x x) (* y y)) 4.0))
; 点(a,b)の発散するまでの回数を計算する。発散しない場合は-1を返す。
(defun calc (a b maxCount)
(block exit
(let* ((zx 0) (zy 0) (i 0))
(loop
(if (> i maxCount) (return-from exit -1))
(if (isDiverge zx zy) (return-from exit i))
(setq zz (f zx zy a b))
(setq zx (car zz))
(setq zy (cdr zz))
(setq i (+ i 1))))))
; 計算した回数により色分けする。
(defun toRgb (count)
(if (= count -1)
(convertHSBtoRGB 0 0 0) ; 黒
(convertHSBtoRGB (* (mod count 1000) 0.002) 1.0 1.0))) ; グラデーション
; 指定された点を打つ
(defun drawMandelbrot1 (image a0 b0 zoom maxCount x y)
(setq a (+ a0 (* (- x 100) zoom)))
(setq b (+ b0 (* (- y 100) zoom)))
(setq count (calc a b maxCount))
(setq rgb (toRgb count))
(putPixel image x y rgb))
; 画像に描画する
(defun drawMandelbrot (image a0 b0 zoom maxCount)
(dotimes (y 200)
(dotimes (x 200)
(drawMandelbrot1 image a0 b0 zoom maxCount x y))))
; メイン
(let ((frame (jnew "javax.swing.JFrame" "Mandelbrot set"))
(image (createImage 200 200)))
(progn
(jcall "add" frame (jnew "javax.swing.JLabel" (jnew "javax.swing.ImageIcon" image)))
(jcall "pack" frame)
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.02d0 100) ; Mandelbrot_001.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.01d0 100) ; Mandelbrot_002.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.005d0 100) ; Mandelbrot_003.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.002d0 1000) ; Mandelbrot_004.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.001d0 1000) ; Mandelbrot_005.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.0005d0 1000) ; Mandelbrot_006.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.0002d0 1000) ; Mandelbrot_007.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.0001d0 1000) ; Mandelbrot_008.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.00005d0 1000) ; Mandelbrot_009.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.00002d0 1000) ; Mandelbrot_010.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.00001d0 1000) ; Mandelbrot_011.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.000005d0 1000) ; Mandelbrot_012.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.000002d0 1000) ; Mandelbrot_013.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.000001d0 1000) ; Mandelbrot_014.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.0000005d0 1000) ; Mandelbrot_015.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.0000002d0 1000) ; Mandelbrot_016.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.0000001d0 1000) ; Mandelbrot_017.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.00000005d0 1000) ; Mandelbrot_018.png
; (drawMandelbrot image -0.745428d0 -0.113009d0 0.00000002d0 2000) ; Mandelbrot_019.png
(drawMandelbrot image -0.745428d0 -0.113009d0 0.00000001d0 2000) ; Mandelbrot_020.png
(jcall "setVisible" frame T)))
実行方法は以下のとおり。実行には数秒~数十秒かかります。
C:\devel\lisp\abcl-bin-1.6.0>java -jar abcl.jar
Armed Bear Common Lisp 1.6.0
Java 10.0.1 Oracle Corporation
Java HotSpot(TM) 64-Bit Server VM
Low-level initialization completed in 0.23 seconds.
Startup completed in 4.251 seconds.
Type ":help" for a list of available commands.
CL-USER(1): (load "test/Mandelbrot.lisp")
T
CL-USER(2): (exit)
C:\devel\lisp\abcl-bin-1.6.0>
既知のツッコミ
- CommonLispでは複素数が使えます。知らなかったので、実部と虚部に分解して計算しました。
- デフォルトで浮動小数点は単精度だったので、数値の末尾に
d0
をつけて倍精度にしています。*read-default-float-format*
で指定する方法もあります。
ではでは