forked from: Electric potential 4
電位を計算して3次元表示する教材。
画面下部の赤丸(正電荷)・青丸(負電荷)をドラッグして緑の四角の中に入れると、
上の画面が変更されます。
上の画面はマウスドラッグで視点変更、↑↓キーでカメラ距離変更できます。
2012.6.29 マテリアルを変更し、高電位を赤、低電位を青で表示するようにしました。
/**
* Copyright Nicolas ( http://wonderfl.net/user/Nicolas )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/aBUA
*/
// forked from Nicolas's Electric potential 4
// forked from Nicolas's Electric potential 3
// forked from Nicolas's Electric potential 2
// forked from Nicolas's Electric potential
/*
電位を計算して3次元表示する教材。
画面下部の赤丸(正電荷)・青丸(負電荷)をドラッグして緑の四角の中に入れると、
上の画面が変更されます。
上の画面はマウスドラッグで視点変更、↑↓キーでカメラ距離変更できます。
2012.6.29 マテリアルを変更し、高電位を赤、低電位を青で表示するようにしました。
*/
package
{
import flash.geom.Rectangle;
import flash.geom.Matrix;
import flash.display.Shape;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.Graphics;
import flash.events.Event;
import flash.events.MouseEvent;
import org.papervision3d.core.geom.renderables.Vertex3D;
import com.bit101.components.*;
[SWF(width="465",height="465",backgroundColor="#CCCCCC")]
public class Main extends Sprite
{
private var canvas:canvas3D;
private var charges:Array;
private var bd:BitmapData = new BitmapData(150, 150, false, 0xFFFFFF);
private var bm:Bitmap = new Bitmap(bd);
private var vc:ValueColor = new ValueColor();
private var lineShape:Shape = new Shape();
private var lineNum:int = 8;
private var checkBox:CheckBox;
public function Main()
{
//3Dキャンバス
canvas = new canvas3D(465, 300, bd);
addChild(canvas);
//2Dフィールド
var field2D:Sprite = new Sprite();
var fg:Graphics = field2D.graphics;
fg.lineStyle(2, 0x00CC99);
fg.drawRect(-75, -75, 150, 150);
field2D.x = 225; field2D.y = 380;
//ビットマップ
addChild(bm);
bm.x = field2D.x - 75;
bm.y = field2D.y - 75;
addChild(lineShape);
lineShape.x = field2D.x;
lineShape.y = field2D.y;
addChild(field2D);
//電荷
charges = [
new PCharge(), new PCharge(), new PCharge(), new PCharge(),
new NCharge(), new NCharge(), new NCharge(), new NCharge(),
];
for (var i:int = 0; i < charges.length; i++)
{
var c:Charge = charges[i] as Charge;
c.lineNum = lineNum;
c.x = -90;
c.y = 20 * i - 65;
field2D.addChild(c);
c.addEventListener(Event.CHANGE, changeHandler);
}
checkBox = new CheckBox(stage, 320, 320, "line");
checkBox.addEventListener(MouseEvent.CLICK, changeHandler);
}
private function changeHandler(e:Event):void
{
bd.lock();
var vertices:Array = canvas.v;
for (var i:int = 0; i < vertices.length; i++)
{
var v:Vertex3D = vertices[i];
var tempV:Number = 0;
for (var j:int = 0; j < charges.length; j++)
{
var c:Charge = charges[j];
//2Dフィールドの座標を3Dキャンバスの座標に対応させる
var cx:Number = c.x / 150 * 500;
var cy:Number = -c.y / 150 * 500;
//電位を計算
if(c.x > -75 && c.y > -75 && c.x < 75 && c.y < 75)
tempV += 500 * c.q / Math.sqrt((v.x - cx)*(v.x - cx) + (v.z - cy)*(v.z - cy));
}
if (tempV > 300) tempV = 300;
if (tempV < -300) tempV = -300;
v.y = tempV;
}
var phase:Number = 0;
for (var jj:int = 0; jj < bd.height; jj++) {
for (var ii:int = 0; ii < bd.width; ii++) {
tempV = 0;
for (var n:int = 0; n < charges.length; n++) {
c = charges[n];
if(c.x > -75 && c.y > -75 && c.x < 75 && c.y < 75) {
var dx:int = ii - c.x - 75;
var dy:int = jj - c.y - 75;
tempV += 100 * c.q / Math.sqrt(dx * dx + dy * dy);
phase += (c.q * (Math.atan2(dy, dx) + Math.PI)) / 10;
}
}
tempV = Math.round(tempV/10)*10;
while (phase < 0) {
phase += Math.PI * 2;
}
phase %= Math.PI / 4;
phase *= 4 / Math.PI;
if (phase < Math.PI / 128) {
phase = 0.3;
} else if (phase < Math.PI / 64) {
//phase = 0.6;
} else if (phase < Math.PI / 32) {
//phase = 0.8;
} else phase = 1;
if (checkBox.selected) bd.setPixel(ii, jj, vc.getColor(tempV, phase));
else bd.setPixel(ii, jj, vc.getColor(tempV));
}
}
bd.unlock();
//if(checkBox.selected) drawLine();
//else lineShape.graphics.clear();
}
//電気力線を描きます。(オイラー法)
private function drawLine():void {
var g:Graphics = lineShape.graphics;
g.clear();
g.lineStyle(1, 0x000000);
var r:Number = 4;
initLineNum();
for each (var c:Charge in charges) {
if(c.x > -75 && c.y > -75 && c.x < 75 && c.y < 75) {
start: for (var i:int = 0; i < c.lineStartAngles.length; i++) {
if(isNaN(c.lineStartAngles[i])) continue start;
var tempX:Number = c.x + r * Math.cos(c.lineStartAngles[i]);
var tempY:Number = c.y + r * Math.sin(c.lineStartAngles[i]);
g.moveTo(tempX, tempY);
var num:int = 0;
drawing: while(tempX > -75 && tempY > -75 && tempX < 75 && tempY < 75) {
//無限ループって怖くね?
if(++num > 200000) break;
var E:Number = 0;
var Ex:Number = 0;
var Ey:Number = 0;
var angle:Number = 0;
for each (var cc:Charge in charges) {
if(cc.x < -75 || cc.y < -75 || cc.x > 75 || cc.y > 75) continue;
var distanceSquare:Number = ((cc.x - tempX)*(cc.x - tempX) + (cc.y - tempY)*(cc.y - tempY));
angle = Math.atan2(tempY - cc.y, tempX - cc.x);
//電気力線が電荷に入ったら、ループ終了
if(distanceSquare + 1 < r * r) {
var step:Number = 2 * Math.PI / lineNum;
for (var j:int = - lineNum / 2; j < lineNum / 2; j++) {
if (step * (j - 0.5) < angle && angle <= step * (j + 0.5)) {
cc.lineStartAngles[j] = NaN;
}
}
break drawing;
}
E = 1 * cc.q / distanceSquare;
if (c is PCharge) {
Ex += E * Math.cos(angle);
Ey += E * Math.sin(angle);
} else {
Ex -= E * Math.cos(angle);
Ey -= E * Math.sin(angle);
}
}
var nextX:Number = tempX += Ex;
var nextY:Number = tempY += Ey;
g.lineTo(nextX, nextY);
tempX = nextX;
tempY = nextY;
}
}
}
}
bd.draw(lineShape, mat);
}
private var mat:Matrix = new Matrix(1, 0, 0, 1, 75, 75);
//各電荷から出る電気力線の本数を初期値に戻します
private function initLineNum():void {
for each (var c:Charge in charges) {
c.lineNum = lineNum;
c.lineStartAngles = [];
var step:Number = 2 * Math.PI / lineNum;
for (var i:int = 0; i < lineNum; i++) {
c.lineStartAngles.push(i * step);
}
}
}
}
}
import flash.geom.Matrix3D;
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.events.Event;
import flash.events.KeyboardEvent;
import org.papervision3d.view.BasicView;
import org.papervision3d.materials.WireframeMaterial;
import org.papervision3d.materials.BitmapMaterial;
import org.papervision3d.objects.primitives.Plane;
import org.papervision3d.core.math.Matrix3D;
class canvas3D extends BasicView
{
public var v:Array;
private var r:Number = 1200;
private var theta:Number = 60;
private var phi:Number = -90;
private var mouseDownX:Number;
private var mouseDownY:Number;
public function canvas3D(w:uint, h:uint, texture:BitmapData)
{
super(w, h, false);
var g:Graphics = this.graphics;
g.beginFill(0x000000);
g.drawRect(0, 0, w, h);
g.endFill();
var m:BitmapMaterial = new BitmapMaterial(texture);
m.doubleSided = true;
var field:Plane = new Plane(m, 500, 500, 20, 20);
field.transformVertices(org.papervision3d.core.math.Matrix3D.rotationX(Math.PI / 2));
scene.addChild(field);
setCameraPosition(r, theta, phi);
camera.zoom = 80;
startRendering();
v = field.geometry.vertices;
addEventListener(Event.ADDED_TO_STAGE, function(e:Event):void
{
addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void
{
//マウスダウン位置を記録
mouseDownX = mouseX;
mouseDownY = mouseY;
addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
});
stage.addEventListener(MouseEvent.MOUSE_UP, function(e:MouseEvent):void
{
removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
});
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
});
}
/**
* カメラの位置を極座標から直交座標に変換する。
* @param $r
* @param $theta
* @param $phi
*/
private function setCameraPosition($r:Number, $theta:Number, $phi:Number):void {
var thetaRad:Number = $theta / 180 * Math.PI;
var phiRad:Number = $phi / 180 * Math.PI;
camera.x = -$r * Math.sin(thetaRad) * Math.cos(phiRad);
camera.y = $r * Math.cos(thetaRad);
camera.z = $r * Math.sin(thetaRad) * Math.sin(phiRad);
}
/**
* ドラッグ量を角度に変換し、カメラの位置を設定する。
* @param e
*/
private function mouseMoveHandler(e:MouseEvent):void
{
if (e.buttonDown) {
phi += e.stageX - mouseDownX;
theta -= e.stageY - mouseDownY;
if (theta > 179) theta = 179;
if (theta < 1) theta = 1;
if (phi < -180) phi = -179;
if (phi > 0) phi = -1;
setCameraPosition(r, theta, phi);//直交座標に変換
mouseDownX = e.stageX;
mouseDownY = e.stageY;
}
}
/**
* カメラの中心からの距離を設定する。↑:近づく、↓:遠ざかる。
* @param e
*/
private function keyDownHandler(e:KeyboardEvent):void
{
switch(e.keyCode)
{
case 38:
r -= 50;
setCameraPosition(r, theta, phi);
break;
case 40:
r += 50;
setCameraPosition(r, theta, phi);
break;
}
}
}
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.Event;
class Charge extends Sprite
{
public var q:Number = 0;
public var lineNum:int = 0;
public var lineStartAngles:Array = [];
private var ev:Event = new Event(Event.CHANGE);
public function Charge()
{
init();
}
private function init():void
{
buttonMode = true;
addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void {
startDrag();
} );
addEventListener(MouseEvent.MOUSE_UP, function(e:MouseEvent):void {
stopDrag();
if(y < -75) y = -75;
dispatchEvent(ev);
} );
}
}
import flash.display.Sprite;
class PCharge extends Charge
{
public function PCharge()
{
q = 10;
var g:Graphics = this.graphics;
g.beginFill(0xFF0000);
g.lineStyle(1, 0x0);
g.drawCircle(0, 0, 6);
g.endFill();
g.lineStyle(2, 0xFFFFFF);
g.moveTo(-3, 0);
g.lineTo(3, 0);
g.moveTo(0, -3);
g.lineTo(0, 3);
}
}
import flash.display.Sprite;
class NCharge extends Charge
{
public function NCharge()
{
q = -10;
var g:Graphics = this.graphics;
g.beginFill(0x0000FF);
g.lineStyle(1, 0x0);
g.drawCircle(0, 0, 6);
g.endFill();
g.lineStyle(2, 0xFFFFFF);
g.moveTo(-3, 0);
g.lineTo(3, 0);
}
}
import frocessing.color.ColorHSV;
class ValueColor {
private var hsv:ColorHSV = new ColorHSV();
public var max:Number = 100;
public function ValueColor(){}
public function getColor(v:Number, p:Number = 1):uint {
if(v < 0) {
hsv.h = 240;
hsv.s = - v / max;
} else {
hsv.h = 0;
hsv.s = v / max;
}
hsv.v = p * 1;
return hsv.toRGB().value;
}
}