Top | 戻る
MandelbrotSet
AVMってどのくらい速いの? っていうことで、タイトな演算で有名なアレを試してみました。
見てみたい場所を右クリックしてコンテクストメニュー表示、「ZoomIn」「ZoomOut」できます。
演算のループは、一度に実行すると ActionScript のタイムアウトとなってしまうので、
100ms 程度で実行できる範囲で少しずつ行うようにしています。
Command.as
package {
public class Command {
public var func : Function;
public var label : String;
public function Command(func : Function, label : String = null) {
this.func = func;
this.label = label;
}
}
}
Executor.as
package {
public class Executor {
private var code : Array;
private var index : int;
private var labelIndex : Object;
public function Executor(code : Array) {
this.code = code;
this.index = 0;
labelIndex = new Object();
for (var i : int = 0; i < code.length; i++) {
if (code[i] is String) {
labelIndex[code[i]] = i;
}
}
}
public function next() : Boolean {
if (code[index] is Command) {
var cmd : Command = Command(code[index]);
if (cmd.func() && cmd.label) {
index = labelIndex[cmd.label];
return true;
}
}
index++;
return index < code.length;
}
}
}
MandelbrotSet.as
package {
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.ContextMenuEvent;
import flash.events.Event;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
public class MandelbrotSet extends Sprite {
private var bitmap : BitmapData;
private var executor : Executor;
private var currentX : int;
private var currentY : int;
private var reC : Number = 0;
private var imC : Number = 0;
private var scale : Number = 4;
private var mouseRe : Number;
private var mouseIm : Number;
public function MandelbrotSet() {
bitmap = new BitmapData(400, 400);
var contextMenu : ContextMenu = new ContextMenu();
contextMenu.hideBuiltInItems();
contextMenu.addEventListener(ContextMenuEvent.MENU_SELECT, function (e : ContextMenuEvent) : void {
mouseRe = mouseX;
mouseIm = mouseY;
} );
var zoomInItem : ContextMenuItem = new ContextMenuItem("ZoomIn");
zoomInItem.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, function (e : ContextMenuEvent) : void {
reC = getRe(mouseRe);
imC = getIm(mouseIm);
scale /= 2;
draw();
} );
contextMenu.customItems.push(zoomInItem);
var zoomOutItem : ContextMenuItem = new ContextMenuItem("ZoomOut");
zoomOutItem.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, function (e : ContextMenuEvent) : void {
reC = getRe(mouseRe);
imC = getIm(mouseIm);
scale *= 2;
draw();
} );
contextMenu.customItems.push(zoomOutItem);
this.contextMenu = contextMenu;
addEventListener(Event.ENTER_FRAME, onEnterFrame);
draw();
}
private function onEnterFrame(e : Event) : void {
if (executor) {
bitmap.lock();
var beginTime : Number = new Date().getTime();
while (new Date().getTime() - beginTime < 100) {
if (!executor.next() ) {
executor = null;
break;
}
}
bitmap.unlock();
}
graphics.clear();
graphics.beginBitmapFill(bitmap);
graphics.drawRect(0, 0, bitmap.width, bitmap.height);
graphics.endFill();
}
private function draw() : void {
executor = new Executor([
new Command(function() : * { currentY = 0; }),
"yloop",
new Command(function() : * { currentX = 0; }),
"xloop",
new Command(calcPixel),
new Command(function() : * { currentX++; return currentX < bitmap.width; }, "xloop"),
new Command(function() : * { currentY++; return currentY < bitmap.height; }, "yloop")
]);
}
private function getRe(x : int) : Number {
return (x - bitmap.width / 2) / bitmap.height * scale + reC;
}
private function getIm(y : int) : Number {
return (bitmap.height / 2 - y) / bitmap.height * scale + imC;
}
private function calcPixel() : void {
var re0 : Number = getRe(currentX);
var im0 : Number = getIm(currentY);
var re : Number = re0;
var im : Number = im0;
for (var limit : int = 1000; limit > 0; limit--) {
if (re * re + im * im >= 4) {
break;
}
var tmpRe : Number = re;
re = re * re - im * im + re0;
im = 2 * tmpRe * im + im0;
}
var dark : Boolean = (limit % 2 == 1);
var color : uint = (limit == 0)? 0x000000 :
getRGB(limit / 100, dark? 0.8 : 1);
bitmap.setPixel(currentX, currentY, color);
}
private static function getRGB(hue : Number, bright : Number) : uint {
var r : int = getGrad(hue) * 255 * bright;
var g : int = getGrad(hue + 1/3) * 255 * bright;
var b : int = getGrad(hue + 2/3) * 255 * bright;
return r << 16 | g << 8 | b;
}
private static function getGrad(n : Number) : Number {
n = (n >= 0)? n % 1 : 1 - (-n) % 1;
if (n < 1/6) {
return 1;
} else if (n < 2/6) {
return -6 * n + 2;
} else if (n < 4/6) {
return 0;
} else if (n < 5/6) {
return 6 * n - 4;
} else {
return 1;
}
}
}
}
実行結果
Contents Copyright © Kazuhiko Arase