import java.util.concurrent.BlockingQueue;
import com.sun.javafx.runtime.async.AbstractAsyncOperation;
import com.sun.javafx.runtime.async.AsyncOperationListener;
public class MandelbrotWorker extends AbstractAsyncOperation<Object> {
// イベントディスパッチスレッドと居有するキュー
// スレッドセーフなBlockingQueueインタフェースを使用する
private BlockingQueue<Point> queue;
public MandelbrotWorker(BlockingQueue<Point> queue, AsyncOperationListener<Object> listener) {
super(listener);
// イベントディスパッチスレッドとキューを共有
this.queue = queue;
}
// 非同期に行う処理
@Override
public Object call() throws InterruptedException {
for (double x = -2.0; x <= 0.6; x += 0.005) {
for (double y = -1.2; y <= 1.2; y += 0.005) {
Point c = new Point(x, y, 0);
Point z = new Point(0.0, 0.0, 0);
while (true) {
// 漸化式の計算
z = calcNextPoint(z, c);
// zの大きさが100,000以上であれば無限大と見なす
// また、215回以上のイテレーションを繰り返しても
// 100,000以上にならなければマンデルブロ集合に属していると見なす
if (z.length() > 100000 || z.number > 215) {
break;
}
}
// イテレーション数をコピーする
c.number = z.number;
// 計算結果をキューに入れる
queue.put(c);
}
}
return null;
}
// マンデルブロの漸化式
private Point calcNextPoint(Point z, Point c) {
double x = z.x * z.x - z.y * z.y + c.x;
double y = 2 * z.x * z.y + c.y;
return new Point(x, y, z.number + 1);
}
}
// マンデルブロの計算結果を格納するクラス
public class Point {
public double x;
public double y;
// 無限大と見なしたイテレーション回数
// 最大215回とする
public int number;
public Point(double x, double y, int number) {
this.x = x;
this.y = y;
this.number = number;
}
public double length() {
return x * x + y * y;
}
}
import javafx.async.AbstractAsyncOperation;
import java.lang.Object;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import com.sun.javafx.runtime.async.AsyncOperationListener;
public class Mandelbrot extends AbstractAsyncOperation {
// スレッド間でデータを受け渡しするキュー
// スレッドセーフなBlockingQueueインタフェースを使用する
var queue: BlockingQueue;
// 非同期処理を行うJavaクラス
var worker: MandelbrotWorker;
public override function start(): Void {
// スレッド間の計算結果の引渡しにキューを使用する
// BlockingQueueインタフェースのコンクリートクラスとして
// LinkedBlockingQueueクラスを使用する
queue = new LinkedBlockingQueue();
// Javaの非同期処理部分
worker = new MandelbrotWorker(queue, listener);
// 非同期処理の開始
worker.start();
}
public override function cancel(): Void {
// 非同期処理のキャンセル
worker.cancel();
}
public override function onCompletion(value: Object): Void {}
// キューから計算結果を取り出す
public function poll(): Point {
queue.poll() as Point;
}
}
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.ext.swing.SwingButton;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import java.awt.image.BufferedImage;
// 色のインデックス
var colors: Integer[] = for(i in [0..7]) {
for (blue in [0, 0x66, 0xff], green in [0, 0x66, 0xff], red in [0, 0x66,0xff]) {
var color = (0xff - red) * 0x10000 + (0xff - green) * 0x100 + (0xff - blue);
}
}
// Imageクラスはピクセルにアクセスできないので、BufferedImageクラスを使用する
var bufferedImage: BufferedImage;
var image: Image;
// 非同期でマンデルブロの計算を行うクラス
var mandelbrot: Mandelbrot;
// 非同期で行われる処理から結果を受け取るためのタイムライン
var timeline: Timeline;
timeline = Timeline {
repeatCount: Timeline.INDEFINITE
keyFrames: [
KeyFrame {
time: 0.0s
action: function() {
while(true) {
// キューから計算結果の取り出し
var point = mandelbrot.poll();
if (point != null) {
// 計算結果からイメージに色をセットする
var x = (point.x * 200 + 400) as Integer;
var y = (point.y * 200 + 240) as Integer;
var color = colors[point.number];
bufferedImage.setRGB(x, y, color);
} else {
// キューが空であれば、0.1秒待つ
break;
}
}
// BufferedImageからImageオブジェクトを生成する
image = Image.fromBufferedImage(bufferedImage);
}
},
KeyFrame {
time: 0.1s
action: function() {
// 非同期処理が終了していたら、timelineもストップさせる
if (mandelbrot.done) {
timeline.stop();
}
}
}
]
}
Stage {
title: "Mandelbrot"
scene: Scene {
width: 520
height: 480
fill: Color.BLACK
content: [
ImageView {
image: bind image
},
SwingButton {
text: "DRAW"
action: function() {
timeline.stop();
if (mandelbrot != null) {
// 非同期で処理を行っている場合はキャンセル
mandelbrot.cancel();
}
// イメージとMandelbrotオブジェクトを新たに生成し、
// 非同期処理を開始する
bufferedImage = new BufferedImage(520, 480, BufferedImage.TYPE_INT_RGB);
mandelbrot = Mandelbrot {};
timeline.play();
}
}
]
}
}