Perfect Shuffle Visualization Ex
@see http://wonderfl.net/code/2733fb8d3d956ad68601f145c753de4d3be8c889
底面半径r, 高さhの円錐を展開すると、
半径R=√(r^2+h^2), 角度2πr/R [rad]の扇になる。
このとき、座標(r(1-t)cosθ,r(1-t)sinθ,ht)のうつる先は
(0,0,0)→(0,0), (r,0,0)→(R,0)とすると
(Rtcos(θ*r/R),Rtsin(θ*r/R))となる。
/**
* Copyright uwi ( http://wonderfl.net/user/uwi )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/iq7n
*/
package {
import org.papervision3d.view.BasicView;
import org.papervision3d.core.math.*;
import org.papervision3d.core.geom.*;
import org.papervision3d.core.geom.renderables.*;
import org.papervision3d.core.proto.*;
import org.papervision3d.materials.special.*;
import org.papervision3d.materials.*;
import org.papervision3d.objects.primitives.*;
import flash.display.*;
import flash.text.TextField;
import flash.events.Event;
import flash.events.MouseEvent;
import net.hires.debug.Stats;
import com.bit101.components.*;
import org.libspark.betweenas3.*;
import org.libspark.betweenas3.tweens.*;
import flash.filters.*;
import frocessing.color.ColorHSV;
// @see http://wonderfl.net/code/2733fb8d3d956ad68601f145c753de4d3be8c889
// 底面半径r, 高さhの円錐を展開すると、
// 半径R=√(r^2+h^2), 角度2πr/R [rad]の扇になる。
// このとき、座標(r(1-t)cosθ,r(1-t)sinθ,ht)のうつる先は
// (0,0,0)→(0,0), (r,0,0)→(R,0)とすると
// (Rtcos(θ*r/R),Rtsin(θ*r/R))となる。
[SWF(backgroundColor="0x000000", frameRate="60")]
public class PV3D extends BasicView {
private const R : Number = 100; // 底面半径
private const H : Number = 200; // 高さ
private const F : Number = Math.sqrt(R * R + H * H);
private var _corn : Cylinder; // 球面
private var _froms : Array;
private var _tos : Array;
private var _tween : ITween = null;
private var _tf : TextField;
public function PV3D() {
Wonderfl.capture_delay(10);
graphics.beginFill(0x000000);
graphics.drawRect(0, 0, 475, 475);
graphics.endFill();
super(0, 0, true, false);
// デバッグ用
_tf = new TextField();
_tf.textColor = 0xffffff;
_tf.width = 400;
_tf.height = 465;
// addChild(_tf);
// 円錐の初期化・配置
// var mat : MaterialObject3D = new WireframeMaterial(0xffffff, 0.5);
var mat : MaterialObject3D = new BitmapMaterial(drawPerfectShuffle(300, 200, 30), true);
mat.oneSide = false;
_corn = new Cylinder(mat, R, H, 20, 20, 0, false, false);
scene.addChild(_corn);
var v : Vertex3D;
var i : uint;
_froms = [];
_tos = [];
for(i = 0;i < _corn.geometry.vertices.length;i++){
v = _corn.geometry.vertices[i];
_froms.push({x : v.x, y : v.y, z : v.z});
var theta : Number = Math.atan2(v.z, v.x) + Math.PI;
var t : Number = (v.y - H / 2) / H;
_tos.push({
x : F * t * Math.cos(theta * R / F),
z : F * t * Math.sin(theta * R / F),
y : H / 2
});
}
var btn : PushButton = new PushButton(this, 360, 0, "retry", function(e : MouseEvent) : void { init(); });
// カメラの配置
_camera = new SphereCamera(new Number3D(0, 0, 0), 350, new Number3D(0, 0, 1), new Number3D(0, 1, 0));
init();
startRendering();
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
// addChild(new Stats());
}
// 初期化
private function init() : void
{
if(_tween != null)_tween.stop();
var tweens : Array = [];
var i : uint;
for(i = 0;i < _corn.geometry.vertices.length;i++){
var v : Vertex3D = _corn.geometry.vertices[i];
tweens.push(
BetweenAS3.delay(
BetweenAS3.tween(v, _froms[i], _tos[i], 5.0)
,0.3 * ((i + 9) % 20)
)
);
}
_tween = BetweenAS3.parallelTweens(tweens);
_tween.play();
}
private var _prevX : Number = 0;
private var _prevY : Number = 0;
// マウスを移動したときの動作
private function onMouseMove(e : MouseEvent) : void
{
// camera.orbit(stage.mouseY * 0.5, -stage.mouseX * 0.5);
if(e.buttonDown){
// ボタンをおしている状態のときのみカメラを移動
if(_prevX != 0 && _prevY != 0){
var sc : SphereCamera = SphereCamera(camera);
// 直前との差分だけ移動
sc.move((stage.mouseX - _prevX) * 0.005, (stage.mouseY - _prevY) * 0.005);
}
_prevX = stage.mouseX;
_prevY = stage.mouseY;
}else{
_prevX = 0;
_prevY = 0;
}
}
// 毎フレームの動作
override protected function onRenderTick(e : Event = null) : void
{
super.onRenderTick(e);
}
private function drawPerfectShuffle(w : Number, h : Number, size : uint) : BitmapData
{
var W : Number = w / 5;
var H : Number = h / size;
var num:int;
var s:Sprite = new Sprite();
for (var i:int = 0; i < size; i++) {
num = i;
s.graphics.lineStyle(4, new ColorHSV(i * 270 / size, .7).value, .7);
s.graphics.moveTo(0, num * H);
for (var j:int = 0; j < 5; j++) {
if (num < size / 2) {
num = num * 2 + 1;
} else {
num = (num - size / 2) * 2;
}
s.graphics.lineTo(j * W + W, num * H);
}
}
// s.filters = [new BlurFilter(2, 2)];
var ret : BitmapData = new BitmapData(w, h, true, 0x000000);
ret.draw(s);
return ret;
}
private function tr(...o : Array) : void
{
_tf.appendText(o + "\n");
_tf.scrollV = _tf.maxScrollV;
}
}
}
import org.papervision3d.core.math.*;
import org.papervision3d.objects.*;
import org.papervision3d.cameras.*;
// 球面上を動き、球の中心を見るカメラ
class SphereCamera extends Camera3D
{
private var _O : DisplayObject3D; // 球の中心
public var _front : Number3D; // カメラの前の向きの単位ベクトル
// private var _right : Number3D;
public var _up : Number3D; // カメラの上の向きの単位ベクトル
private var _R : Number; // 球の半径
public function SphereCamera(O : Number3D, R : Number, front : Number3D, up : Number3D) : void
{
_O = new DisplayObject3D();
_O.x = O.x;
_O.y = O.y;
_O.z = O.z;
_R = R;
_front = front.clone();
_front.normalize();
_up = up.clone();
_up.normalize();
update1();
}
// カメラの右方向へx[rad], 上方向へy[rad]回転させる
public function move(x : Number, y : Number) : void
{
// X方向の移動
_front = applyQuaternion([_front], _up, -x)[0];
// Y方向の移動
var right : Number3D = Number3D.cross(_up, _front);
right.normalize();
var ret : Array = applyQuaternion([_front, _up], right, y);
_front = ret[0];
_up = ret[1];
update1();
}
private function update1() : void
{
this.x = _front.x * -_R + _O.x;
this.y = _front.y * -_R + _O.y;
this.z = _front.z * -_R + _O.z;
this.lookAt(_O, _up);
}
// axisを軸にangle回転させる変換を、srcsの要素それぞれに適用する。
public static function applyQuaternion(srcs : Array, axis : Number3D, angle : Number) : Array
{
var q : Quaternion = Quaternion.createFromAxisAngle(
axis.x / axis.modulo,
axis.y / axis.modulo,
axis.z / axis.modulo,
angle
);
var qc : Quaternion = Quaternion.conjugate(q);
var ret : Array = [];
for each(var src : Number3D in srcs){
var qSrc : Quaternion = new Quaternion(src.x, src.y, src.z, 0);
var qDst : Quaternion = Quaternion.multiply(qc, qSrc);
qDst.mult(q);
ret.push(new Number3D(qDst.x, qDst.y, qDst.z));
}
return ret;
}
}