安定な立体
球面に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;
}
}