ドット地球儀・リファクタリング
http://wonderfl.net/code/38309e41fd7854c73d87729156be1e53a10b6e49 のリファクタリング
*
* 一番大きな変更点は以下の2点。
* (1) Matrix3D 計算を個々のパーティクルにおいて matrix3D.transformVector でおこなっていたのを、Model クラスにおいて Utils3D.projectVectors でおこなうようにした。
* (2) パーティクルを zsort することにより、描画 BitmapData を一つにした。
*
* (2) については以下を参考にさせていただきました。
* http://wonderfl.net/code/cc3d98ef63c1b65bb96dcda9c560f84dd8f6a90c
*
* その他、ムダにムリヤリ MVC に当て嵌めたクラス構成しています。
2009/10/23 反転の修正
/**
* Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/13dD
*/
/**
* http://wonderfl.net/code/38309e41fd7854c73d87729156be1e53a10b6e49 のリファクタリング
*
* 一番大きな変更点は以下の2点。
* (1) Matrix3D 計算を個々のパーティクルにおいて matrix3D.transformVector でおこなっていたのを、Model クラスにおいて Utils3D.projectVectors でおこなうようにした。
* (2) パーティクルを zsort することにより、描画 BitmapData を一つにした。
*
* (2) については以下を参考にさせていただきました。
* http://wonderfl.net/code/cc3d98ef63c1b65bb96dcda9c560f84dd8f6a90c
*
* その他、ムダにムリヤリ MVC に当て嵌めたクラス構成しています。
*/
/**
* 2009/10/23 反転の修正
*/
package {
import flash.display.Sprite;
import flash.events.Event;
import net.hires.debug.Stats;
[SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#000000")]
public class Main extends Sprite {
public function Main() {
// 外部データの読込
// (画像ファイルのロードとピクセル分解)
var URL_OF_IMAGEFILE:String = "http://assets.wonderfl.net/images/related_images/6/6f/6fb0/6fb075e04102b2f4409a896f17acac4597f3f423";
var data1:DataFactory1 = new DataFactory1(URL_OF_IMAGEFILE);
data1.addEventListener(Event.COMPLETE, completeHandler1);
data1.start();
}
private function completeHandler1(event:Event):void {
// 読み込んだ外部データの解析
// (分解した各ピクセルに座標設定)
var RADIUS_OF_SPHERE:Number = 150.0;
var data2:DataFactory2 = new DataFactory2(event.target.pixelizer, RADIUS_OF_SPHERE);
data2.addEventListener(Event.COMPLETE, completeHandler2);
data2.start();
}
private function completeHandler2(event:Event):void {
// Model を生成
var model:Model = new Model(event.target.verts.concat());
model.offsetX = stage.stageWidth / 2;
model.offsetY = stage.stageHeight / 2;
model.offsetZ = 500;
model.speed = 0.025;
model.fieldOfView = 55;
// View を生成
var view:View = new View(model);
addChild(view);
// Controller を生成
var controller:Controller = new Controller(model);
controller.setup(stage);
addChild(new Stats());
}
}
}
import flash.display.Bitmap;
import flash.display.Loader;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.net.URLRequest;
import flash.system.LoaderContext;
class DataFactory1 extends EventDispatcher {
// 外部に渡すデータ
public function get pixelizer():Pixelizer { return _pixelizer; }
private var _pixelizer:Pixelizer;
private var url:String;
public function DataFactory1(url:String) {
this.url = url;
}
internal function start():void {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadCompleteHandler);
loader.load(new URLRequest(url), new LoaderContext(true));
}
private function loadCompleteHandler(event:Event):void {
_pixelizer = new Pixelizer();
_pixelizer.addEventListener(Event.COMPLETE, scanCompleteHandler);
_pixelizer.scan(Bitmap(event.target.loader.content).bitmapData);
}
private function scanCompleteHandler(event:Event):void {
dispatchEvent(new Event(Event.COMPLETE));
}
}
import flash.display.Loader;
import flash.events.Event;
import flash.events.EventDispatcher;
class DataFactory2 extends EventDispatcher {
// 外部に渡すデータ
public function get verts():Vector.<Number> { return _verts; }
private var _verts:Vector.<Number> = new Vector.<Number>();
private var pixelizer:Pixelizer;
private var radius:Number;
public function DataFactory2(pixelizer:Pixelizer, radius:Number) {
this.pixelizer = pixelizer;
this.radius = radius;
}
internal function start():void{
var iter:PixelizerIterator = pixelizer.iterator;
var vNum:uint = iter.height;
var hNum:uint = iter.width;
// theta:シータ(θ)は緯度、phi:ファイ(φ)は経度
var theta:Number = Math.PI / vNum;
var phi:Number = Math.PI * 2 / hNum;
while (iter.hasNext()) {
var color:uint = iter.next();
var alpha:uint = ColorMath.getAlpha(color);
if (alpha > 0x7F){
var px:Number = radius * Math.sin(theta * iter.y) * Math.cos(phi * iter.x);
var py:Number = radius * Math.cos(theta * iter.y);
var pz:Number = radius * Math.sin(theta * iter.y) * Math.sin(phi * iter.x);
_verts.push(px, -py, pz);
}
}
_verts.fixed = true;
dispatchEvent(new Event(Event.COMPLETE));
}
}
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.geom.Matrix3D;
import flash.geom.PerspectiveProjection;
import flash.geom.Vector3D;
import flash.geom.Utils3D;
class Model extends EventDispatcher {
// 外部へ渡すデータ
public function get viewData():Array { return _viewData; }
private var _viewData:Array;
public function get zLevel():Number { return _zLevel; }
private var _zLevel:Number = 1 / _offsetZ;
// 外部から受けるデータ
// 引数として受ける
private var verts:Vector.<Number>;
// セッターで受ける
public function set offsetX(value:Number):void {
_offsetX = value;
}
private var _offsetX:Number = 0;
public function set offsetY(value:Number):void {
_offsetY = value;
}
private var _offsetY:Number = 0;
public function set offsetZ(value:Number):void {
_offsetZ = value;
_zLevel = 1 / _offsetZ;
}
private var _offsetZ:Number = 500;
public function set speed(value:Number):void {
_speed = value;
}
private var _speed:Number = 0.025;
public function set fieldOfView(value:Number):void {
projection.fieldOfView = value;
projectionMatrix3D = projection.toMatrix3D();
}
private var _fieldOfView:Number;
private var vx:Number = 0;
private var vy:Number = 0;
// Controller から呼び出される
internal function update(valueX:Number, valueY:Number):void {
// Controller から渡された値を加工
vx += (_offsetX - valueX) * _speed;
vy -= (_offsetY - valueY) * _speed;
// Controller から渡された値を回転に変換
m.identity();
m.appendRotation(vy, Vector3D.X_AXIS);
m.appendRotation(vx, Vector3D.Y_AXIS);
m.appendTranslation(0, 0, _offsetZ);
m.append(projectionMatrix3D);
// 外部データに回転を適用
Utils3D.projectVectors(m, verts, projectedVerts, uvts);
// 回転を適用した外部データを zsort
var n:uint = projectedVerts.length / 2;
for (var i:int = 0; i < n; i++) {
var vertex:Vertex = _viewData[i];
vertex.x = projectedVerts[i * 2] + _offsetX;
vertex.y = projectedVerts[i * 2 + 1] + _offsetY;
vertex.z = uvts[i * 3 + 2];
}
_viewData.sortOn("z", Array.NUMERIC);
// リスナー(View)へ通知
notifyListeners();
}
private var projectedVerts:Vector.<Number>;
private var uvts:Vector.<Number>;
private var projection:PerspectiveProjection;
private var projectionMatrix3D:Matrix3D;
private var m:Matrix3D;
public function Model(verts:Vector.<Number>) {
// 外部から受けるデータを引数として受ける
this.verts = verts;
// 内部でのみ使用する Vector の初期化
var n:uint = verts.length / 3;
projectedVerts = new Vector.<Number>(n * 2, true);
uvts = new Vector.<Number>(n * 3, true)
// 外部へ渡すデータの初期化
_viewData = [];
for (var i:int = 0; i < n; i++) {
_viewData.push(new Vertex());
}
// PerspectiveProjection の Matrix3D を取得
projection = new PerspectiveProjection();
projectionMatrix3D = projection.toMatrix3D();
// 外部データに適用する Matrix3D
m = new Matrix3D();
}
// View へ伝える
private function notifyListeners():void {
dispatchEvent(new Event(Event.CHANGE));
}
}
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Vector3D;
class View extends Sprite {
private function changeHandler(event:Event):void {
// Model からデータを受ける
var data:Array = model.viewData;
var zLevel:Number = model.zLevel;
// 描画
canvas.lock();
canvas.fillRect(canvas.rect, 0xFF000000);
var n:uint = data.length;
for (var i:int = 0; i < n; i++) {
var v:Vertex = data[i];
var posX:Number = v.x;
var posY:Number = v.y;
var color:uint = v.z > zLevel ? 0xFFFFCC00 : 0xFF0033FF;
canvas.setPixel32(posX, posY, color);
}
canvas.unlock();
}
private var model:Model;
public function View(model:Model) {
this.model = model;
this.model.addEventListener(Event.CHANGE, changeHandler);
addEventListener(Event.ADDED_TO_STAGE, setup);
}
private var canvas:BitmapData;
private function setup(event:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, setup);
canvas = new BitmapData(stage.stageWidth, stage.stageHeight);
addChild(new Bitmap(canvas));
}
}
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.Event;
class Controller {
private var stage:Stage;
internal function setup(stage:Stage):void {
this.stage = stage;
stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
private function enterFrameHandler(event:Event):void {
model.update(stage.mouseX, stage.mouseY);
}
private var model:Model;
public function Controller(model:Model) {
this.model = model;
}
}
class Vertex {
public var x:Number;
public var y:Number;
public var z:Number;
public function Vertex() {}
}
import flash.display.BitmapData;
import flash.events.Event;
import flash.events.EventDispatcher;
class Pixelizer extends EventDispatcher {
// イテレータ
public function get iterator():PixelizerIterator {
return new PixelizerIterator(width, height, data);
}
private var width:uint = 0;
private var height:uint = 0;
private var data:Vector.<uint>;
// スキャン
public function scan(bmd:BitmapData):void {
width = bmd.width;
height = bmd.height;
data = bmd.getVector(bmd.rect);
dispatchEvent(new Event(Event.COMPLETE));
}
public function Pixelizer() {}
}
class PixelizerIterator {
// モードフラグ
public static const NEXT:String = "next"; // 正順
public static const PREV:String = "prev"; // 逆順
// next()、nextCol() で取得したデータのX座標
public function get x():int { return positionX; }
private var positionX:int = 0;
// next()、nextRow() で取得したデータのY座標
public function get y():int { return positionY; }
private var positionY:int = 0;
// スキャンサイズ(幅)
public function get width():uint { return _width; }
private var _width:uint;
// スキャンサイズ(高)
public function get height():uint { return _height; }
private var _height:uint;
// 現在のイテレートモード
private var mode:String = "next";
// イテレーションカウンター
private var position:int = 0;
// データ格納 Vector
private var data:Vector.<uint>;
public function PixelizerIterator(width:uint, height:uint, data:Vector.<uint>) {
_width = width;
_height = height;
this.data = data;
}
// データの操作(1個ずつデータを呼び出す)
// 外部から呼び出せる hasNext
public function hasNext():Boolean {
return (mode == "prev") ? _hasPrev() : _hasNext();
}
// 外部から呼び出せる next
public function next():uint {
return (mode == "prev") ? _prev() : _next();
}
// データの同一列(横)の操作(同一列の全てのデータを呼び出す)
// 外部から呼び出せる hasNext
public function hasNextRow():Boolean {
return (mode == "prev") ? _hasPrevRow() : _hasNextRow();
}
// 外部から呼び出せる next
public function nextRow():Vector.<uint> {
return (mode == "prev") ? _prevRow() : _nextRow();
}
// データの同一行(縦)の操作(同一行のすべてのデータを呼び出す)
// 外部から呼び出せる hasNext
public function hasNextCol():Boolean {
return (mode == "next") ? _hasNextCol() : _hasPrevCol();
}
// 外部から呼び出せる next
public function nextCol():Vector.<uint> {
return (mode == "next") ? _nextCol() : _prevCol();
}
// リセット
public function reset(mode:String = "next"):void {
this.mode = mode;
var offset:uint = (mode == "next") ? 0 : 1;
position = (data.length - 1) * offset;
positionX = (_width - 1) * offset;
positionY = (_height - 1) * offset;
}
// データの操作(1個ずつデータを呼び出す)
// mode = "next" 時の hasNext
private function _hasNext():Boolean {
return position < data.length;
}
// mode = "prev" 時の hasNext
private function _hasPrev():Boolean {
return position > 0;
}
// mode = "next" 時の next
private function _next():uint {
positionX = position % _width;
positionY = position / _width;
return getData(position++);
}
// mode = "prev" 時の next
private function _prev():uint {
positionX = position % _width;
positionY = position / _width;
return getData(position--);
}
private function getData(idx:uint):uint {
if (idx >= data.length) {
throw new Error("getData#配列範囲外:" + idx + " length:" + data.length);
}
return data[idx];
}
// データの同一列(横)の操作(同一列の全てのデータを呼び出す)
// mode = "next" 時の hasNext
private function _hasNextRow():Boolean {
return positionY < _height;
}
// mode = "prev" 時の hasNext
private function _hasPrevRow():Boolean {
return positionY > -1;
}
// mode = "next" 時の next
private function _nextRow():Vector.<uint> {
return getRow(positionY++);
}
// mode = "prev" 時の next
private function _prevRow():Vector.<uint> {
return getRow(positionY--);
}
// next の実体
private function getRow(val:uint):Vector.<uint> {
if (val >= _height) {
throw new Error("getRow#配列範囲外:" + val + " _height:" + _height);
}
return data.slice(val * _width, (val + 1) * _width);
}
// データの同一行(縦)の操作(同一行のすべてのデータを呼び出す)
// mode = "next" 時の hasNext
private function _hasNextCol():Boolean {
return positionX < _width;
}
// mode = "prev" 時の hasNext
private function _hasPrevCol():Boolean {
return positionX > -1;
}
// mode = "next" 時の next
private function _nextCol():Vector.<uint> {
return getCol(positionX++);
}
// mode = "prev" 時の next
private function _prevCol():Vector.<uint> {
return getCol(positionX--);
}
// next の実体
private function getCol(idx:uint):Vector.<uint> {
if (idx >= _width) {
throw new Error("getCol#配列範囲外:" + idx + " _width:" + _width);
}
var vector:Vector.<uint> = new Vector.<uint>(_height, true);
for (var i:uint = 0; i < _height; i++) {
vector[i] = data[_width * i + idx];
}
return vector;
}
}
class ColorMath {
public function ColorMath() {}
// RGB → 0xNNNNNN
public static function rgbToHex(r:uint, g:uint, b:uint):uint {
r = adjust(r);
g = adjust(g);
b = adjust(b);
return r << 16 | g << 8 | b;
}
// 0xNNNNNN → RGB
public static function hexToRgb(hex:uint):Object {
var r:uint = (hex >> 16) & 0xFF;
var g:uint = (hex >> 8) & 0xFF;
var b:uint = hex & 0xFF;
return {r:r, g:g, b:b};
}
// ARGB → 0xNNNNNNNN
public static function argbToHex(a:uint, r:uint, g:uint, b:uint):uint {
a = adjust(a);
r = adjust(r);
g = adjust(g);
b = adjust(b);
return a<<24 | r<<16 | g<<8 | b;
}
// 0xNNNNNNNN → ARGB
public static function hexToArgb(hex:uint):Object {
var a:uint = (hex >> 24) & 0xFF;
var r:uint = (hex >> 16) & 0xFF;
var g:uint = (hex >> 8) & 0xFF;
var b:uint = hex & 0xFF;
return {a:a, r:r, g:g, b:b};
}
// RGB 各要素調整用関数
// 255 を超えていた場合は 255 に切り捨てる
private static function adjust(val:uint):uint {
return Math.min(val, 0xFF);
}
// 32 bit color のアルファ値を取得
public static function getAlpha(color:uint):uint {
return (color >> 24) & 0xFF;
}
}