forked from: Mandelbrot Set / forked from: Progressive Test
Legacy Mandelbrot Set
* コンテクストメニューからズームイン/アウト選択
// forked from broken's Mandelbrot Set / forked from: Progressive Test
/**
* Legacy Mandelbrot Set
* コンテクストメニューからズームイン/アウト選択
*/
package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.ContextMenuEvent;
import flash.events.Event;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFieldType;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import flash.utils.getTimer;
[SWF(width="465", height="465", backgroundColor="0x000000", frameRate="30")]
public class MandelbrotSet extends Sprite {
private static const CALC_LIMIT : int = 1000;
private var _bitmap : BitmapData;
private var _infoText : TextField;
private var _infoTextBg : Sprite;
private var _renderer : ProgressiveRenderer;
private var _mouseX : Number = 0;
private var _mouseY : Number = 0;
/*
private var _reC : Number = 0;
private var _imC : Number = 0;
private var _scale : Number = 4;
*/
private var _reC : Number = 0.271171875;
private var _imC : Number = 0.59072265625;
private var _scale : Number = 0.00390625;
public function MandelbrotSet() {
setupContextMenu();
_bitmap = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0x000000);
addChild(new Bitmap(_bitmap) );
_infoTextBg = new Sprite();
addChild(_infoTextBg);
_infoText = new TextField();
_infoText.textColor = 0xffffff;
_infoText.type = TextFieldType.DYNAMIC;
_infoText.autoSize = TextFieldAutoSize.LEFT;
addChild(_infoText);
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
start();
}
private function setupContextMenu() : void {
var contextMenu : ContextMenu = new ContextMenu();
contextMenu.hideBuiltInItems();
contextMenu.addEventListener(ContextMenuEvent.MENU_SELECT, function (e : ContextMenuEvent) : void {
_mouseX = mouseX;
_mouseY = mouseY;
} );
var zoomInItem : ContextMenuItem = new ContextMenuItem("ZoomIn");
zoomInItem.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, function (e : ContextMenuEvent) : void {
zoom(false);
} );
contextMenu.customItems.push(zoomInItem);
var zoomOutItem : ContextMenuItem = new ContextMenuItem("ZoomOut");
zoomOutItem.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, function (e : ContextMenuEvent) : void {
zoom(true);
} );
contextMenu.customItems.push(zoomOutItem);
var resetItem : ContextMenuItem = new ContextMenuItem("Reset");
resetItem.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, function (e : ContextMenuEvent) : void {
reset();
} );
contextMenu.customItems.push(resetItem);
this.contextMenu = contextMenu;
}
private function reset() : void {
_reC = 0;
_imC = 0;
_scale = 4;
start();
}
private function zoom(zoomOut : Boolean) : void {
_reC = getRe(_mouseX);
_imC = getIm(_mouseY);
if (zoomOut) {
_scale *= 2;
} else {
_scale /= 2;
}
start();
}
private function enterFrameHandler(event : Event) : void {
if (_renderer == null) {
return;
}
// 最大実行時間を決定
var maxTime : int = 1000 / stage.frameRate;
_bitmap.lock();
var beginTime : Number = getTimer();
// できる限りたくさん実行
while (getTimer() - beginTime < maxTime) {
if (!_renderer.next() ) {
// 処理完了
_renderer = null;
break;
}
}
_bitmap.unlock();
}
private function start() : void {
showInfo();
_renderer = new ProgressiveRenderer(_bitmap.width, _bitmap.height);
_renderer.render = render;
}
/**
* 情報表示
*/
private function showInfo() : void {
_infoText.text = "Center: " + _reC + (_imC < 0 ? "" : "+") + _imC + "i / Scale: " + _scale;
// 背景
var g : Graphics = _infoTextBg.graphics;
g.clear();
g.beginFill(0x000000, 0.5);
g.drawRect(_infoText.x, _infoText.y, _infoText.width, _infoText.height);
g.endFill();
}
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 render(x : Number, y : Number, width : Number, height : Number) : void {
var re0 : Number = getRe(x);
var im0 : Number = getIm(y);
var re : Number = re0;
var im : Number = im0;
var limit : int;
var tmpRe : Number;
for (limit = CALC_LIMIT; limit > 0 && (re * re + im * im < 4); limit--) {
tmpRe = re;
re = re * re - im * im + re0;
im = 2 * tmpRe * im + im0;
}
var weight : Number = (limit % 2 == 1)? 0.8 : 1.0;
var r : uint = tri(limit * 2.0) * 255 * weight;
var g : uint = tri(limit * 2.1) * 255 * weight;
var b : uint = tri(limit * 2.2) * 255 * weight;
var color : uint = (limit == 0)? 0x000000 :
( (r << 16) | (g << 8) | b);
_bitmap.fillRect(new Rectangle(x, y, width, height), color);
}
}
}
/**
* @return 0~1 triangle wave
*/
function tri(t : Number) : Number {
t = ( (t < 0)? -t : t) % 256;
return (t < 128)? t / 128 : 2 - t / 128;
}
class ProgressiveRenderer {
private static var DEFAULT_RENDER : Function = function(
x : Number, y : Number, width : Number, height: Number) : void {
trace(x, y, width, height);
}
private var _width : Number;
private var _height : Number;
private var _depth : uint;
private var _funcs : Array;
private var _labelIndex : Object;
private var _index : int;
private var _defualtCellSize : int;
// function(x : Number, y : Number, width : Number, height: Number) : void
public var render : Function = DEFAULT_RENDER;
public function ProgressiveRenderer(width : Number, height : Number, depth : uint = 2) {
_width = width;
_height = height;
_depth = depth;
init();
}
public function get width() : Number {
return _width;
}
public function get height() : Number {
return _height;
}
public function next() : Boolean {
if (_index >= _funcs.length) {
return false;
}
var func : Function = _funcs[_index] as Function;
if (func != null) {
var label : String = func() as String;
if (label != null) {
// jump to label.
_index = _labelIndex[label];
return true;
}
}
_index++;
return _index < _funcs.length;
}
private function init() : void {
var x : int;
var y : int;
var size1 : int = 2 << _depth;
var size2 : int = size1 << 1;
_funcs = [
function() : * { y = 0; },
"y_loop1",
function() : * { x = 0; },
"x_loop1",
function() : * { render(x, y, size2, size2); },
function() : * { x += size2; return x < _width? "x_loop1" : null; },
function() : * { y += size2; return y < _height? "y_loop1" : null; },
"size_loop",
function() : * { y = 0; },
"y_loop2",
function() : * { x = size1; },
"x_loop2",
function() : * { render(x, y, size1, size2); },
function() : * { x += size2; return x < _width? "x_loop2" : null; },
function() : * { y += size2; return y < _height? "y_loop2" : null; },
function() : * { y = size1; },
"y_loop3",
function() : * { x = 0; },
"x_loop3",
function() : * { render(x, y, size1, size1); },
function() : * { x += size1; return x < _width? "x_loop3" : null; },
function() : * { y += size2; return y < _height? "y_loop3" : null; },
function() : * { size1 >>= 1; size2 >>= 1; return size1 >= 1? "size_loop" : null; }
];
_index = 0;
_labelIndex = new Object();
for (var i : int = 0; i < _funcs.length; i++) {
if (_funcs[i] is String) {
_labelIndex[_funcs[i]] = i;
}
}
}
}