荷電粒子の運動
磁場(緑)、電場(橙)中での正電荷を帯びた粒子の運動のシミュレーションです。
磁場中では等速円運動、電場中では等加速度運動を行います。
/**
* Copyright Nicolas ( http://wonderfl.net/user/Nicolas )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/ryvc
*/
// forked from Nicolas's forked from: ラザフォードの実験
// forked from Nicolas's ラザフォードの実験
// forked from Nicolas's パーティクル
package {
import flash.text.TextField;
import flash.geom.Rectangle;
import flash.display.Graphics;
import flash.display.Shape;
import flash.events.*;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.display.Sprite;
import net.hires.debug.Stats;
[SWF(frameRate="60")]
public class LCForce extends Sprite
{
private const WIDTH:int = 465;
private const HEIGHT:int = 465;
private var bmp:Bitmap;
private var bmd:BitmapData;
private var updater:LCForceUpdater;
private var renderer:Renderer;
private var poolSize:int = 1000;
private var pool:ParticlePool;
private var count:int = 0;
public function LCForce()
{
bmd = new BitmapData(WIDTH, HEIGHT, false, 0x000000);
bmp = addChild(new Bitmap(bmd)) as Bitmap;
ParticlePool.setPoolSize(poolSize);
pool = ParticlePool.getInstance();
var sp:Sprite = new Sprite();
sp.graphics.beginFill(0x00FF00, 0.3);
sp.graphics.drawRect(300,0,150,500);
addChild(sp);
var text:TextField = new TextField();
text.text = "B";
text.textColor = 0xFFFFFF;
text.x = 370;
text.y = 20;
text.scaleX = text.scaleY = 1.5;
addChild(text);
var bMark:Shape = new Shape();
bMark.graphics.lineStyle(2, 0xFFFFFF);
bMark.graphics.moveTo(-10, 0);
bMark.graphics.lineTo(10, 0);
bMark.graphics.moveTo(0, -10);
bMark.graphics.lineTo(0, 10);
bMark.graphics.drawCircle(0, 0, 10);
bMark.rotation = 45;
bMark.x = 400;
bMark.y = 20;
addChild(bMark);
var field:Field = new Field(0, 0, 0.3, sp);
var sp2:Sprite = new Sprite();
sp2.graphics.beginFill(0x00FF00, 0.3);
sp2.graphics.drawRect(0,0,150,300);
addChild(sp2);
var text2:TextField = new TextField();
text2.text = "B";
text2.textColor = 0xFFFFFF;
text2.x = 70;
text2.y = 20;
text2.scaleX = text2.scaleY = 1.5;
addChild(text2);
var bMark2:Shape = new Shape();
bMark2.graphics.lineStyle(2, 0xFFFFFF);
bMark2.graphics.beginFill(0xFFFFFF);
bMark2.graphics.drawCircle(0, 0, 3);
bMark2.graphics.endFill();
bMark2.graphics.drawCircle(0, 0, 10);
bMark2.x = 100;
bMark2.y = 20;
addChild(bMark2);
var field2:Field = new Field(0, 0, -0.5, sp2);
var sp3:Sprite = new Sprite();
sp3.graphics.beginFill(0xFFDD00, 0.3);
sp3.graphics.drawRect(150,0,150,500);
addChild(sp3);
var text3:TextField = new TextField();
text3.text = "E";
text3.textColor = 0xFFFFFF;
text3.x = 220;
text3.y = 20;
text3.scaleX = text3.scaleY = 1.5;
addChild(text3);
var eMark:Shape = new Shape();
eMark.graphics.lineStyle(2, 0xFFFFFF);
eMark.graphics.moveTo(-10, 0);
eMark.graphics.lineTo(10, 0);
eMark.graphics.moveTo(5, -5);
eMark.graphics.lineTo(10, 0);
eMark.graphics.lineTo(5, 5);
eMark.x = 250;
eMark.y = 20;
addChild(eMark);
var field3:Field = new Field(3, 0, 0, sp3);
updater = new LCForceUpdater(WIDTH, HEIGHT);
updater.fields.push(field, field2, field3);
renderer = new Renderer(bmd);
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
stage.addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void {
buttonDown = true;
});
stage.addEventListener(MouseEvent.MOUSE_UP, function(e:MouseEvent):void {
buttonDown = false;
});
//addChild(new Stats());
}
private var buttonDown:Boolean = false;
private function enterFrameHandler(e:Event):void
{
if(buttonDown) {
var p:Particle = pool.acquire();
p.x = mouseX;
p.y = mouseY;
p.vx = 20;
p.vy = 0;
}
updater.update();
renderer.render();
//count++;
}
}
}
import flash.geom.Vector3D;
import flash.geom.ColorTransform;
import flash.geom.Rectangle;
import flash.display.BitmapData;
class Renderer
{
private var targetBmd:BitmapData
private var rect:Rectangle;
private var colorTransform:ColorTransform;
private var pool:ParticlePool;
private var particleRect:Rectangle = new Rectangle(0, 0, 3, 3);
public function Renderer(bmd:BitmapData)
{
targetBmd = bmd;
rect = targetBmd.rect;
colorTransform = new ColorTransform(0.9, 0.9, 0.9);
pool = ParticlePool.getInstance();
}
public function render():void
{
targetBmd.lock();
targetBmd.colorTransform(targetBmd.rect, colorTransform);
var len:int = pool.livingParticles.length;
var p:Particle;
for (var i:int = 0; i < len; i++)
{
p = pool.livingParticles[i];
//targetBmd.setPixel(p.x, p.y, 0xFFFFFF);
particleRect.x = p.x - 1;
particleRect.y = p.y - 1;
targetBmd.fillRect(particleRect, 0xFFFFFF);
}
targetBmd.unlock();
}
}
class Updater
{
private var bmdWidth:int;
private var bmdHeight:int;
protected var pool:ParticlePool;
public var t:Number = 0;
public var dt:Number = 0.1;
//temp
private var x:Number = 0;
private var y:Number = 0;
private var vx:Number = 0;
private var vy:Number = 0;
public function Updater(width:int, height:int)
{
bmdWidth = width;
bmdHeight = height;
pool = ParticlePool.getInstance();
}
public function update():void
{
var len:int = pool.livingParticles.length;
var p:Particle;
for (var i:int = 0; i < len; i++)
{
p = pool.livingParticles[i];
this.x = p.x;
this.y = p.y;
this.vx = p.vx;
this.vy = p.vy;
//4次のルンゲクッタ法を、x,y,vx,vyについて連立
var k1:Vector.<Number> = new Vector.<Number>(4);
var k2:Vector.<Number> = new Vector.<Number>(4);
var k3:Vector.<Number> = new Vector.<Number>(4);
var k4:Vector.<Number> = new Vector.<Number>(4);
k1[0] = dt * fx(t, x, y, vx, vy);
k1[1] = dt * fy(t, x, y, vx, vy);
k1[2] = dt * fvx(t, x, y, vx, vy);
k1[3] = dt * fvy(t, x, y, vx, vy);
k2[0] = dt * fx(t + dt / 2, x + k1[0] / 2, y + k1[1] / 2, vx + k1[2] / 2, vy + k1[3] / 2);
k2[1] = dt * fy(t + dt / 2, x + k1[0] / 2, y + k1[1] / 2, vx + k1[2] / 2, vy + k1[3] / 2);
k2[2] = dt * fvx(t + dt / 2, x + k1[0] / 2, y + k1[1] / 2, vx + k1[2] / 2, vy + k1[3] / 2);
k2[3] = dt * fvy(t + dt / 2, x + k1[0] / 2, y + k1[1] / 2, vx + k1[2] / 2, vy + k1[3] / 2);
k3[0] = dt * fx(t + dt / 2, x + k2[0] / 2, y + k2[1] / 2, vx + k2[2] / 2, vy + k2[3] / 2);
k3[1] = dt * fy(t + dt / 2, x + k2[0] / 2, y + k2[1] / 2, vx + k2[2] / 2, vy + k2[3] / 2);
k3[2] = dt * fvx(t + dt / 2, x + k2[0] / 2, y + k2[1] / 2, vx + k2[2] / 2, vy + k2[3] / 2);
k3[3] = dt * fvy(t + dt / 2, x + k2[0] / 2, y + k2[1] / 2, vx + k2[2] / 2, vy + k2[3] / 2);
k4[0] = dt * fx(t + dt, x + k3[0], y + k3[1], vx + k3[2], vy + k3[3]);
k4[1] = dt * fy(t + dt, x + k3[0], y + k3[1], vx + k3[2], vy + k3[3]);
k4[2] = dt * fvx(t + dt, x + k3[0], y + k3[1], vx + k3[2], vy + k3[3]);
k4[3] = dt * fvy(t + dt, x + k3[0], y + k3[1], vx + k3[2], vy + k3[3]);
p.x += (k1[0] + k2[0] * 2 + k3[0] * 2 + k4[0]) / 6;
p.y += (k1[1] + k2[1] * 2 + k3[1] * 2 + k4[1]) / 6;
p.vx += (k1[2] + k2[2] * 2 + k3[2] * 2 + k4[2]) / 6;
p.vy += (k1[3] + k2[3] * 2 + k3[3] * 2 + k4[3]) / 6;
if(p.x < 0 || p.x > bmdWidth || p.y < 0 || p.y > bmdHeight)
{
pool.release(p);
len--;
}
}
}
public function fx(t:Number, x:Number, y:Number, vx:Number, vy:Number):Number
{
return 0;
}
public function fy(t:Number, x:Number, y:Number, vx:Number, vy:Number):Number
{
return 0;
}
public function fvx(t:Number, x:Number, y:Number, vx:Number, vy:Number):Number
{
return 0;
}
public function fvy(t:Number, x:Number, y:Number, vx:Number, vy:Number):Number
{
return 0;
}
}
import flash.display.Sprite;
class LCForceUpdater extends Updater
{
public var fields:Vector.<Field>;
public function LCForceUpdater(width:int, height:int)
{
super(width, height);
fields = new Vector.<Field>();
}
override public function fx(t:Number, x:Number, y:Number, vx:Number, vy:Number):Number
{
return vx;
}
override public function fy(t:Number, x:Number, y:Number, vx:Number, vy:Number):Number
{
return vy;
}
override public function fvx(t:Number, x:Number, y:Number, vx:Number, vy:Number):Number
{
var dst:Number = 0;
var numFields:int = fields.length;
for (var i:int = 0; i < numFields; i++) {
if(fields[i].area.hitTestPoint(x, y, true)){
dst += vy * fields[i].B + fields[i].Ex;
}
}
return dst;
}
override public function fvy(t:Number, x:Number, y:Number, vx:Number, vy:Number):Number
{
var dst:Number = 0;
var numFields:int = fields.length;
for (var i:int = 0; i < numFields; i++) {
if(fields[i].area.hitTestPoint(x, y, true)){
dst += - vx * fields[i].B + fields[i].Ey;
}
}
return dst;
}
}
class Field
{
public var Ex:Number;
public var Ey:Number;
public var B:Number;
public var area:Sprite;
public function Field(Ex:Number, Ey:Number, B:Number, area:Sprite)
{
this.Ex = Ex;
this.Ey = Ey;
this.B = B;
this.area = area;
}
}
class Particle
{
public var x:Number;
public var y:Number;
public var vx:Number;
public var vy:Number;
public function Particle()
{
}
}
class NullParticle extends Particle
{
private static var _instance:NullParticle;
public function NullParticle(s:SingletonEnforcer)
{
if (!s) throw new Error("Singleton Error");
}
public static function getInstance():NullParticle
{
if (!_instance) return new NullParticle(new SingletonEnforcer());
else return _instance;
}
}
class ParticlePool
{
private static var _instance:ParticlePool;
private static var _poolSize:int;
private var _livingParticles:Vector.<Particle>;
private var _deadParticles:Vector.<Particle>;
public function ParticlePool(s:SingletonEnforcer)
{
if (!s) throw new Error("Singleton Error");
_deadParticles = new Vector.<Particle>();
for (var i:int = 0; i < _poolSize; i++)
{
_deadParticles[i] = new Particle();
}
_livingParticles = new Vector.<Particle>();
}
public static function setPoolSize(n:int):void
{
if (!_instance) _poolSize = n;
else throw new Error("setPoolSize() must be used before creating ParticlePool instance");
}
public static function getInstance():ParticlePool
{
if (!_instance) _instance = new ParticlePool(new SingletonEnforcer());
return _instance;
}
public function acquire():Particle
{
var p:Particle;
if (_deadParticles.length > 0) {
p = _deadParticles.pop();
_livingParticles.push(p);
}
else p = NullParticle.getInstance();
return p;
}
public function release(p:Particle):void
{
var index:int = _livingParticles.indexOf(p);
_livingParticles.splice(index, 1);
_deadParticles.push(p);
}
public function get livingParticles():Vector.<Particle> { return _livingParticles; }
}
class SingletonEnforcer {}