package { /** * @author YOSHIDA, Akio * 2009/01/22 手抜きで実装していなかった裏面判定処理を追加 */ import flash.display.Sprite; import flash.events.Event; import flash.display.BitmapData; import flash.display.Bitmap; import flash.geom.Vector3D; import flash.geom.Matrix3D; import flash.geom.PerspectiveProjection; import flash.display.Loader; import flash.net.URLRequest; import flash.events.IEventDispatcher; import flash.geom.Matrix; import flash.text.TextField; import flash.text.TextFormat; import flash.system.Security; [SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#000000")] public class MainProxy extends Sprite { private var loader:Loader; // ローダー private var loading:TextField; // ナウローディング表示 // 画像をピクセルスキャン private var ipx:ImagePixelizer; // プロジェクション private var fov:PerspectiveProjection; // ドットのキャンバス private var canvasUpper:BitmapData; // 裏 private var canvasLower:BitmapData; // 表 // 頂点格納 Vector private var vctrVertics:Vector.<Vertex>; // 回転に関わる変数 private var aX:Number = 0; private var aY:Number = 0; // 定数 private const SW:uint = 465; // stage.stageWidth private const SH:uint = 465; // stage.stageHeight private const DIST:uint = 150; // 中心から頂点までの距離 private const XOFF:Number = SW / 2; // stageWidth / 2 private const YOFF:Number = SH / 2; // stageheight / 2 public function MainProxy():void { Security.loadPolicyFile("http://5ivestar.org/proxy/crossdomain.xml"); // Loader 生成 loader = new Loader(); // イベントリスナー登録 configureListeners(loader.contentLoaderInfo); // ロード開始 loader.load(new URLRequest("http://5ivestar.org/proxy/http://homepage3.nifty.com/aquioux/swf/as3/wonderfl/aquioux002.png")); // ナウローディング loading = new TextField(); loading.defaultTextFormat = new TextFormat("_sans", 24, 0xffffff); loading.text = "Now Loading..."; loading.autoSize = "left"; loading.x = (stage.stageWidth - loading.width) / 2; loading.y = (stage.stageHeight - loading.height) / 2; addChild(loading); } // イベントリスナー登録 private function configureListeners(dispatcher:IEventDispatcher):void { dispatcher.addEventListener(Event.COMPLETE, completeHandler); } // 読み込み完了時のイベントハンドラ private function completeHandler(e:Event):void { removeChild(loading); loading = null; // 表示イメージ var w:uint = loader.width; var h:uint = loader.height; var bmd:BitmapData = new BitmapData(w, h, true, 0x00ffffff); bmd.draw(loader.content); ipx = new ImagePixelizer(); ipx.addEventListener(PixelizerEvent.COMPLETE, init); ipx.pixelize(new Bitmap(bmd)); loader = null; } private function init(e:PixelizerEvent = null):void { createCanvas(); // キャンバス生成 createProjection(); // プロジェクション生成 createVertics(); // 頂点生成 // イベントハンドラ stage.addEventListener(Event.ENTER_FRAME, onEnterFrameHandler); } // onEnterFrame private function onEnterFrameHandler(e:Event):void { // 回転角度を増加 aX += (mouseX - XOFF) * 0.01; aY -= (mouseY - YOFF) * 0.01; // 変換行列生成 var mat:Matrix3D = new Matrix3D(); // 回転を変換行列に合成 mat.appendRotation(aX, Vector3D.Y_AXIS); mat.appendRotation(aY, Vector3D.X_AXIS); perspective(mat, fov); // 投影 render(); // レンダリング } // 投影 private function perspective(mat:Matrix3D, fov:PerspectiveProjection):void { for each (var element:Vertex in vctrVertics) { element.perspective(mat, fov); } } // レンダリング private function render():void { canvasLower.lock(); canvasUpper.lock(); canvasLower.fillRect(canvasLower.rect, 0x000000); canvasUpper.fillRect(canvasUpper.rect, 0x000000); for each (var element:Vertex in vctrVertics) { if (element.z < 0) { canvasLower.setPixel(element.x+XOFF, element.y+YOFF, 0x0033ff); } else { canvasUpper.setPixel32(element.x+XOFF, element.y+YOFF, 0xffffcc00); } } canvasLower.unlock(); canvasUpper.unlock(); } // キャンバス生成 private function createCanvas():void{ canvasLower = new BitmapData(SW, SH, false, 0x000000); canvasUpper = new BitmapData(SW, SH, true, 0x00000000); addChild(new Bitmap(canvasLower)); addChild(new Bitmap(canvasUpper)); } // プロジェクション生成 private function createProjection():void { fov = new PerspectiveProjection(); fov.fieldOfView = 55; // 視野角の設定 } // 頂点生成 private function createVertics():void { var vNum:uint = ipx.height; var hNum:uint = ipx.width; var data:Vector.<Pixel> = ipx.data; // theta:シータ(θ)は緯度、phi:ファイ(φ)は経度 var theta:Number = Math.PI / vNum; var phi:Number = Math.PI * 2 / hNum; vctrVertics = new Vector.<Vertex>(); var i:uint = 0; for (var v:int = 0; v < vNum ; v++) { for (var h:int = 0; h < hNum ; h++) { var p:Pixel = data[i++]; var alpha:uint = getAlpha(p.color); if (alpha >= 0x7f){ var px:Number = DIST * Math.sin(theta * v) * Math.cos(phi * h); var py:Number = DIST * Math.cos(theta * v); var pz:Number = DIST * Math.sin(theta * v) * Math.sin(phi * h); var vertex:Vertex = new Vertex(px, -py, -pz); vctrVertics.push(vertex); } } } ipx = null; } // 32 bit color からアルファ値を求める private function getAlpha(hex:uint):uint { return (hex >> 24) & 0xff; } } } /* * 頂点クラス */ import flash.geom.Vector3D; import flash.geom.Matrix3D; import flash.geom.PerspectiveProjection; /* * 頂点クラス */ class Vertex { private var home:Vector3D = new Vector3D(); // 座標ホームポジション private var proj:Vector3D = new Vector3D(); // 投影座標 public function Vertex(x:Number, y:Number, z:Number) { home.x = x; home.y = y; home.z = z; } // 投影処理(透視投影) public function perspective(mat:Matrix3D, fov:PerspectiveProjection):void { proj = mat.transformVector(home); proj.w = fov.focalLength / (fov.focalLength + proj.z); proj.project(); } // getter public function get x():Number { return proj.x; } public function get y():Number { return proj.y; } public function get z():Number { return proj.z; } public function get w():Number { return proj.w; } } /* * ピクセルクラス */ class Pixel { private var _x:Number; private var _y:Number; private var _color:uint; // 32 bit color public function Pixel(x:Number, y:Number, color:uint) { _x = x; _y = y; _color = color; } // getter public function get x():Number { return _x; } public function get y():Number { return _y; } public function get color():uint { return _color; } // setter public function set x(value:Number):void { _x = value; } public function set y(value:Number):void { _y = value; } public function set color(value:uint):void { _color = value; } } /* * BitmapData を1ピクセル単位でスキャン * 結果は Vector.<Pixel> で返す * バックエンドクラスとして Pixelizer を使用 */ import flash.display.Bitmap; import flash.display.BitmapData; import flash.events.Event; import flash.events.EventDispatcher; class ImagePixelizer extends EventDispatcher { // バックエンドクラス private var px:Pixelizer; public function ImagePixelizer() {} // ピクセル情報作成 public function pixelize(bm:Bitmap):void { // バックエンドクラス設定 px = new Pixelizer(); px.addEventListener(PixelizerEvent.COMPLETE, completeHandler); // BitmapData 生成 var bmd:BitmapData = new BitmapData(bm.width, bm.height, true, 0x00ffffff); bmd.draw(bm); // 走査 px.scan(bmd); } private function completeHandler(e:Event):void { dispatchEvent(new PixelizerEvent(PixelizerEvent.COMPLETE)); } // getter(pixelize 実行後に有効) public function get data():Vector.<Pixel> { return px.data; } public function get width():uint { return px.width; } public function get height():uint { return px.height; } } /* * BitmapData を1ピクセル単位でスキャン * ピクセルの32ビットカラーを二次元配列に格納 * ImagePixelizer と TextPixelizer からバックエンドクラスとして呼び出される */ import flash.display.BitmapData; import flash.events.Event; import flash.events.EventDispatcher; class Pixelizer extends EventDispatcher { // ピクセル格納ベクター private var _data:Vector.<Pixel>; // スキャンサイズ private var _width:uint = 0; // 幅 private var _height:uint = 0; // 高 public function Pixelizer() {} // 走査 public function scan(bmd:BitmapData):void { var w:uint = _width = bmd.width; var h:uint = _height = bmd.height; var i:uint = 0; _data = new Vector.<Pixel>(w*h, true); for (var y:uint = 0; y < h; y++){ for (var x:uint = 0; x < w; x++){ _data[i++] = new Pixel(x, y, bmd.getPixel32(x, y)); } } // BitmapData 消去 bmd.dispose(); // イベント発行 dispatchEvent(new PixelizerEvent(PixelizerEvent.COMPLETE)); } // getter(pixelize 実行後に有効) public function get data():Vector.<Pixel> { return _data; } public function get width():uint { return _width; } public function get height():uint { return _height; } } /* * Pixelizer イベント */ import flash.events.Event; class PixelizerEvent extends Event { public static const COMPLETE:String = "complete"; public function PixelizerEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false) { super(type, bubbles, cancelable); } public override function clone():Event { return new PixelizerEvent(type, bubbles, cancelable); } public override function toString():String { return formatToString("PixelizerEvent", "type", "bubbles", "cancelable", "eventPhase"); } } Dots the Earth - ドット地球儀 -