Neon Sphere
/**
* Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/hGTP
*/
package {
import flash.display.Sprite;
import flash.events.Event;
[SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#000000")]
/**
* Neon Sphere
* @author YOSHIDA, Akio(Aquioux)
*/
public class Main extends Sprite {
private var viewer_:Viewer; // ビューア
public function Main() {
setup();
addEventListener(Event.ENTER_FRAME, update);
}
// セットアップ
private function setup():void {
// ステージサイズ
var w:int = stage.stageWidth;
var h:int = stage.stageHeight;
// 球座標の経度緯度それぞれの分割数
var longitude:int = 36;
var latitude:int = longitude * 2;
// 座標データ生成クラス初期化
CreateData.setup(longitude, latitude, 150);
// プロジェクションクラス初期化
Projection.offsetX = w / 2;
Projection.offsetY = h / 2;
Projection.offsetZ = 500;
Projection.longitude = longitude;
Projection.latitude = latitude;
Projection.setup(CreateData.data);
// マウス挙動クラス初期化
MouseBehavior.setup(this);
// ビューアの作成
viewer_ = new Viewer(w, h);
viewer_.buttonMode = true;
addChild(viewer_);
}
// アップデート
private function update(e:Event):void {
MouseBehavior.update();
viewer_.update(Projection.update(MouseBehavior.moveX, MouseBehavior.moveY));
}
}
}
//package {
/**
* 座標データ生成クラス
* @author YOSHIDA, Akio(Aquioux)
*/
/*public*/ class CreateData {
/**
* 座標データ Vector
*/
static public function get data():Vector.<Number> { return _data; }
static private var _data:Vector.<Number>;
/**
* セットアップ
*/
static public function setup(longitude:int, latitude:int, scale:int):void {
_data = new Vector.<Number>();
var xRadian:Number = Math.PI * 2 / longitude;
latitude++; // 極点を省くため
var yRadian:Number = Math.PI / latitude;
for (var y:int = 1; y < latitude; y++) {
for (var x:int = 0; x < longitude; x++) {
var px:Number = scale * Math.sin(yRadian * y) * Math.cos(xRadian * x);
var py:Number = scale * Math.cos(yRadian * y);
var pz:Number = scale * Math.sin(yRadian * y) * Math.sin(xRadian * x);
_data.push(px, py, pz);
}
}
_data.fixed = true;
}
}
//}
//package {
//import aquioux.display.colorUtil.CycleRGB;
import flash.geom.Matrix3D;
import flash.geom.PerspectiveProjection;
import flash.geom.Utils3D;
import flash.geom.Vector3D;
/**
* 三次元座標を二次元座標に投射する
* @author YOSHIDA, Akio(Aquioux)
*/
/*public*/ class Projection {
/**
* X座標オフセット値
*/
static public function get offsetX():Number { return _offsetX; }
static public function set offsetX(value:Number):void { _offsetX = value; }
static private var _offsetX:Number = 0;
/**
* Y座標オフセット値
*/
static public function get offsetY():Number { return _offsetY; }
static public function set offsetY(value:Number):void { _offsetY = value; }
static private var _offsetY:Number = 0;
/**
* Z座標オフセット値
*/
static public function get offsetZ():Number { return _offsetZ; }
static public function set offsetZ(value:Number):void {
_offsetZ = value;
zLevel_ = 1 / value;
}
static private var _offsetZ:Number = 500;
/**
* 経度方向分割数
*/
static public function get longitude():int { return _longitude; }
static public function set longitude(value:int):void {
_longitude = value;
//colorOffset_ = 360 / (value + 1);
}
static private var _longitude:int;
/**
* 経度方向分割数
*/
static public function get latitude():int { return _latitude; }
static public function set latitude(value:int):void {
_latitude = value;
colorOffset_ = 360 / (value + 2);
}
static private var _latitude:int;
// 座標 Vecotr
static private var verts_:Vector.<Number>; // 三次元座標
static private var projectedVerts_:Vector.<Number>; // 二次元投射後
static private var uvts_:Vector.<Number>; // uvts
static private var data_:Vector.<Number>; // #update の返値
// パースペクティブ・プロジェクション
static private var projection_:PerspectiveProjection;
static private var projectionMatrix3D_:Matrix3D;
// 回転計算用マトリクス
static private var matrix_:Matrix3D;
// 移動量保持
static private var vx_:Number = 0;
static private var vy_:Number = 0;
// 色関連
static private var colorOffset_:Number = 360 / (_longitude + 1);
static private var colorShift_:int = 0;
// z座標関連
static private var zLevel_:Number = 1 / _offsetZ;
/**
* セットアップ
* @param data 三次元座標データ
*/
static public function setup(verts:Vector.<Number>):void {
// 座標 Vecotr
verts_ = verts;
var n:uint = verts_.length;
projectedVerts_ = new Vector.<Number>(n * 2 / 3, true);
uvts_ = new Vector.<Number>(n, true);
data_ = new Vector.<Number>(n, true);
// パースペクティブ・プロジェクション
projection_ = new PerspectiveProjection();
projectionMatrix3D_ = projection_.toMatrix3D();
// 回転計算用マトリクス
matrix_ = new Matrix3D();
}
/**
* アップデート
* @param moveX 移動量(X軸方向)
* @param moveY 移動量(Y軸方向)
* @return 三次元座標を投射した二次元座標データ
*/
static public function update(moveX:Number, moveY:Number):Vector.<Number> {
// 外部からの移動量 moveX、moveY を内部の移動量変数 vx_、vy_ に加算
vx_ -= moveX;
vy_ += moveY;
// マトリクス計算
matrix_.identity();
matrix_.appendRotation(vy_, Vector3D.X_AXIS);
matrix_.appendRotation(vx_, Vector3D.Y_AXIS);
matrix_.appendTranslation(0, 0, _offsetZ);
matrix_.append(projectionMatrix3D_);
// 座標データに回転を適用
Utils3D.projectVectors(matrix_, verts_, projectedVerts_, uvts_);
// sort
var array:Array = [];
var currentLongitude:int = 0;
var currentLatitude:int = 0;
var len:int = projectedVerts_.length / 2;
for (var i:int = 0; i < len; i++) {
var vertex:Vertex = new Vertex();
vertex.x = projectedVerts_[i * 2] + _offsetX;
vertex.y = projectedVerts_[i * 2 + 1] + _offsetY;
vertex.z = uvts_[i * 3 + 2];
vertex.longitude = currentLongitude;
vertex.latitude = currentLatitude;
array[i] = vertex;
currentLongitude++;
currentLongitude %= _longitude;
if (currentLongitude == 0) currentLatitude++;
}
array.sortOn("z", Array.NUMERIC);
// 返値生成
data_.fixed = false;
data_.length = 0;
colorShift_ += 5;
len = i;
for (i = 0; i < len; i++) {
vertex = array[i];
var alpha:uint = vertex.z < zLevel_ ? 0x33 : 0xFF;
//var c:uint = CycleRGB.getColor(vertex.longitude * colorOffset_ + colorShift_);
var c:uint = CycleRGB.getColor(vertex.latitude * colorOffset_ +colorShift_);
var color:uint = alpha << 24 | c;
data_.push(vertex.x, vertex.y, Number(color));
}
data_.fixed = true;
return data_;
}
}
//}
//package {
import flash.display.DisplayObject;
import flash.events.MouseEvent;
/**
* ビューア
* @author YOSHIDA, Akio(Aquioux)
*/
/*public*/ class MouseBehavior {
// MOUSW_MOVE による移動量
static public function get moveX():Number { return _moveX; }
static private var _moveX:Number = 0;
static public function get moveY():Number { return _moveY; }
static private var _moveY:Number = 0;
// 前回の MOUSW_MOVE 時のマウス座標
static private var prevMouseX_:Number = 0;
static private var prevMouseY_:Number = 0;
// マウスをダウンしているか否か
static private var isMouseDown_:Boolean = false;
// 摩擦係数
static private var friction_:Number = 0.98;
// マウスイベントを addEvent する対象
static private var target_:DisplayObject;
/**
* セットアップ
*/
static public function setup(target:DisplayObject):void {
target_ = target;
target.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
target.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
target.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
}
// マウスハンドラ
static private function mouseDownHandler(e:MouseEvent):void {
isMouseDown_ = true;
}
static private function mouseUpHandler(e:MouseEvent):void {
isMouseDown_ = false;
}
static private function mouseMoveHandler(e:MouseEvent):void {
if (!isMouseDown_) {
prevMouseX_ = target_.mouseX;
prevMouseY_ = target_.mouseY;
}
}
/**
* アップデート
*/
static public function update():void {
if (isMouseDown_) {
_moveX = target_.mouseX - prevMouseX_;
_moveY = target_.mouseY - prevMouseY_;
prevMouseX_ = target_.mouseX;
prevMouseY_ = target_.mouseY;
} else {
_moveX *= friction_;
_moveY *= friction_;
}
}
}
//}
//package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.filters.BitmapFilterQuality;
import flash.filters.BlurFilter;
import flash.geom.ColorTransform;
import flash.geom.Point;
import flash.geom.Rectangle;
/**
* ビューア
* @author YOSHIDA, Akio(Aquioux)
*/
/*public*/ class Viewer extends Sprite {
//フェードアウトのための定義
private const FADE:ColorTransform = new ColorTransform(0.999, 0.999, 0.999);
private const BLUR:BlurFilter = new BlurFilter(4, 4, BitmapFilterQuality.HIGH);
// BitmapData 関連
private var bmd_:BitmapData; // 表示 BitmapData
private var bufferBmd_:BitmapData; // バッファ
private var rect_:Rectangle; // ColorTransform, Blur 共用
private const ZERO_POINT:Point = new Point(0, 0);
private var start_:int = 0;
/**
* コンストラクタ
* @param sw ステージ幅
* @param sh ステージ高
*/
public function Viewer(sw:int, sh:int) {
// BitmapData 関連
bufferBmd_ = new BitmapData(sw, sh, true, 0xFF000000);
bmd_ = bufferBmd_.clone();
rect_ = bmd_.rect;
addChild(new Bitmap(bmd_));
}
/**
* アップデート
* @param data 描画座標データ
*/
public function update(data:Vector.<Number>):void {
// bufferBmd_ の更新
bufferBmd_.lock();
bufferBmd_.fillRect(bufferBmd_.rect, 0x00000000);
var len:uint = data.length;
for (var i:int = 0; i < len; i += 3) {
var px:int = data[i] >> 0;
var py:int = data[i + 1] >> 0;
var color:int = data[i + 2] >> 0;
bufferBmd_.setPixel32(px, py, color);
}
bufferBmd_.unlock();
// bmd_ の更新
bmd_.lock();
bmd_.colorTransform(rect_, FADE);
bmd_.applyFilter(bmd_, rect_, ZERO_POINT, BLUR);
bmd_.draw(bufferBmd_);
bmd_.unlock();
}
}
//}
//package {
/**
* 座標
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ class Vertex {
// X座標値
public function get x():Number { return _x; }
public function set x(value:Number):void { _x = value; }
private var _x:Number;
// Y座標値
public function get y():Number { return _y; }
public function set y(value:Number):void { _y = value; }
private var _y:Number;
// Z座標値
public function get z():Number { return _z; }
public function set z(value:Number):void { _z = value; }
private var _z:Number;
// 経度上の位置
public function get longitude():int { return _longitude; }
public function set longitude(value:int):void { _longitude = value; }
private var _longitude:int;
// 緯度上の位置
public function get latitude():int { return _latitude; }
public function set latitude(value:int):void { _latitude = value; }
private var _latitude:int;
public function Vertex() { }
}
//}
//package aquioux.display.colorUtil {
/**
* コサインカーブで色相環的な RGB を計算
* @author Aquioux(YOSHIDA, Akio)
*/
/*public*/ class CycleRGB {
/**
* 32bit カラーのためのアルファ値(0~255)
*/
static public function get alpha():uint { return _alpha; }
static public function set alpha(value:uint):void {
_alpha = (value > 0xFF) ? 0xFF : value;
}
private static var _alpha:uint = 0xFF;
private static const PI:Number = Math.PI; // 円周率
private static const DEGREE120:Number = PI * 2 / 3; // 120度(弧度法形式)
/**
* 角度に応じた RGB を得る
* @param angle HSV のように角度(度数法)を指定
* @return 色(0xNNNNNN)
*/
public static function getColor(angle:Number):uint {
var radian:Number = angle * PI / 180;
var r:uint = (Math.cos(radian) + 1) * 0xFF >> 1;
var g:uint = (Math.cos(radian + DEGREE120) + 1) * 0xFF >> 1;
var b:uint = (Math.cos(radian - DEGREE120) + 1) * 0xFF >> 1;
return r << 16 | g << 8 | b;
}
/**
* 角度に応じた RGB を得る(32bit カラー)
* @param angle HSV のように角度(度数法)を指定
* @return 色(0xNNNNNNNN)
*/
public static function getColor32(angle:Number):uint {
return _alpha << 24 | getColor(angle);
}
}
//}