In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

安定な立体

球面にn個の反発する物体を置いておくと、どういう立体に収束するか。
高校のときからの疑問だったのでPV3Dで実装。正多面体になると思ったけどあんまり綺麗にならないね。
どうみてもnengafl用ネタじゃないです。
/**
 * Copyright uwi ( http://wonderfl.net/user/uwi )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/aazJ
 */

// 球面にn個の反発する物体を置いておくと、どういう立体に収束するか。
// 高校のときからの疑問だったのでPV3Dで実装。正多面体になると思ったけどあんまり綺麗にならないね。
// どうみてもnengafl用ネタじゃないです。
package {
    import org.papervision3d.view.BasicView;
    import org.papervision3d.objects.primitives.*;
    import org.papervision3d.core.math.*;
    import org.papervision3d.core.geom.*;
    import org.papervision3d.core.geom.renderables.*;
    import org.papervision3d.materials.special.*;
    import org.papervision3d.materials.*;
    import org.papervision3d.core.utils.Mouse3D;
    import flash.text.TextField;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import net.hires.debug.Stats;
    import com.bit101.components.*;
    
    [SWF(backgroundColor="0xffffff", frameRate="60")]
    public class PV3D extends BasicView {
        private const N : uint = 6; // 物体の個数
        private const R : Number = 100; // 球の半径
        private var _sp : Sphere; // 球面
        private var _particles : Particles; // 頂点
        private var _edges : Lines3D; // 辺
        private var _vs : Array; // 物体の速度
        private var _c : SphereCamera;
        
        private var _tf : TextField;
        
        public function PV3D() {
            super(0, 0, true, false);
            
            // デバッグ用
            _tf = new TextField();
//            _tf.textColor = 0xffffff;
            _tf.width = 400;
            _tf.height = 465;
//            addChild(_tf);
            
            // 球の初期化・配置
//            _sp = new Sphere(new ColorMaterial(0x0000ff, 0.1), R, 20, 20);
            _sp = new Sphere(new WireframeMaterial(0x0000ff, 0.1), R, 20, 20);
            scene.addChild(_sp);
            
            // 頂点の初期化
            var pm : ParticleMaterial = new ParticleMaterial(0xff0000, 0.7, ParticleMaterial.SHAPE_CIRCLE);
            _particles = new Particles("Forced Points");
            _particles.addParticle(new Particle(pm, 10, R, 0, 0));
            var i : uint, j : uint;
            for(i = 1;i < N;i++){
                var phi : Number = Math.random() * 2 * 3.14;
                var z : Number = Math.random() * 2 - 1;
                _particles.addParticle(new Particle(
                    pm, 10,
                    R * Math.sqrt(1 - z * z) * Math.cos(phi),
                    R * Math.sqrt(1 - z * z) * Math.sin(phi),
                    R * z
                    ));
            }
            scene.addChild(_particles);
            
            // 辺の初期化
            var lm : LineMaterial = new LineMaterial(0x000000, 0.3);
            _edges = new Lines3D(lm, "Edges");
            for(i = 0;i < N;i++){
                for(j = i+1;j < N;j++){
                    _edges.addNewLine(2, 0, 0, 0, 0, 0, 0);
                }
            }
            scene.addChild(_edges);
            
            // retryボタン
            var btn : PushButton = new PushButton(this, 360, 0, "retry", function(e : MouseEvent) : void { init(); });
            
            init();
            startRendering();
            stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
            
//            addChild(new Stats());
        }
        
        // 初期化
        private function init() : void
        {
            // 頂点の初期化
            var i : uint;
            // 一点は固定
            _particles.particles[0].x = R;
            _particles.particles[0].y = 0;
            _particles.particles[0].z = 0;
            for(i = 1;i < N;i++){
                var phi : Number = Math.random() * 2 * 3.14;
                var z : Number = Math.random() * 2 - 1;
                _particles.particles[i].x = R * Math.sqrt(1 - z * z) * Math.cos(phi);
                _particles.particles[i].y = R * Math.sqrt(1 - z * z) * Math.sin(phi);
                _particles.particles[i].z = R * z;
            }
            
            // カメラの配置
            _camera = new SphereCamera(new Number3D(0, 0, 0), 200, new Number3D(0, 0, 1), new Number3D(0, 1, 0));
            
            // 速度の初期化
            _vs = [];
            for(i = 0;i < N;i++){
                _vs.push(new Number3D(0, 0, 0));
            }
            
            updateEdges();
        }
        
        // 辺を更新
        private function updateEdges() : void
        {
            var p : uint = 0;
            for(var i : uint = 0;i < N;i++){
                for(var j : uint = i + 1;j < N;j++){
                    _edges.lines[p].v0.x = _particles.particles[i].x;
                    _edges.lines[p].v0.y = _particles.particles[i].y;
                    _edges.lines[p].v0.z = _particles.particles[i].z;
                    _edges.lines[p].v1.x = _particles.particles[j].x;
                    _edges.lines[p].v1.y = _particles.particles[j].y;
                    _edges.lines[p].v1.z = _particles.particles[j].z;
                    p++;
                }
            }
        }
        
        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
        {
            var fs : Array = []; // 各物体にかかる力。実質加速度
            var i : uint, j : uint;
            for(i = 0;i < N;i++){
                fs.push(new Number3D(0, 0, 0));
            }
            
            // 各頂点間の斥力を計算する
            for(i = 0;i < N;i++){ 
                var xi : Number3D = _particles.particles[i].vertex3D.toNumber3D();
                for(j = i+1;j < N;j++){
                    var xj : Number3D = _particles.particles[j].vertex3D.toNumber3D();
                    var sub : Number3D = Number3D.sub(xj, xi);
                    sub.multiplyEq(100000000.0 / (sub.modulo * sub.modulo * sub.modulo));
                    fs[j].plusEq(sub);
                    fs[i].minusEq(sub);
                }
            }
            
            // 力を速度に加え、速度を位置に加える
            // インデックス0の点は固定
            for(i = 1;i < N;i++){
                _vs[i].plusEq(fs[i]);
//                _vs[i].multiplyEq(0.999);
                var x : Number3D = _particles.particles[i].vertex3D.toNumber3D();
                x.plusEq(_vs[i]);
                x.normalize();
                _particles.particles[i].x = x.x * R;
                _particles.particles[i].y = x.y * R;
                _particles.particles[i].z = x.z * R;
            }
            
            // 辺を更新
            updateEdges();
            
            super.onRenderTick(e);
        }
        
        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;
    }
    
}