Charges-and-fields-Flex
package edu.colorado.phet.chargesandfields {
import flash.display.DisplayObject;
import flash.display.Graphics;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.display.Stage
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.ColorTransform;
import flash.text.TextField;
import flash.ui.Keyboard;
public class BackgroundSprite extends Sprite {
private var myWidth : Number;
private var myHeight : Number;
public function BackgroundSprite(w : Number, h : Number) {
myWidth = w;
myHeight = h;
drawBackground();
}
public function changeSize(w : Number, h : Number) : void {
myWidth = w;
myHeight = h;
drawBackground();
}
public function drawBackground() : void {
this.graphics.beginFill(0xFFFFFF);
this.graphics.drawRect(0, 0, myWidth, myHeight);
this.graphics.endFill();
}
}
}
package edu.colorado.phet.chargesandfields {
public class Charge extends Sprite {
public var q : Number = 0;
public var modelX : Number;
public var modelY : Number;
private var mosaic : VoltageMosaic;
public function Charge(mosaic : VoltageMosaic) {
this.mosaic = mosaic;
// make it appear hand-like
this.useHandCursor = true;
this.buttonMode = true;
addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
addEventListener(MouseEvent.MOUSE_UP, mouseUp);
}
public function setDisplayPosition(x : Number, y : Number) : void {
this.x = x;
this.y = y;
displayPositionToModel();
}
private function displayPositionToModel() : void {
// TODO: integrate in scale. if we find this in the GUI
modelX = x;
modelY = y;
}
public function mouseDown(evt : MouseEvent) : void {
startDrag();
}
public function mouseUp(evt : MouseEvent) : void {
stopDrag();
displayPositionToModel();
mosaic.draw();
}
}
}
package edu.colorado.phet.chargesandfields {
public class ChargesAndFieldsApplication extends UIComponent {
public static const EFAC : Number = 0.2046; // E-field conversion factor: E_true = E_model*EFAC
public static const VFAC : Number = 1.917E-3; // Voltage conversion factor: V_true = V_model*VFAC
public var display : ChargesAndFieldsDisplay;
public function ChargesAndFieldsApplication() {
this.addEventListener(Event.ADDED_TO_STAGE, init);
}
public function init(evt : Event) : void {
display = new ChargesAndFieldsDisplay(stage);
this.addChild(display);
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.addEventListener(Event.RESIZE, display.onResize);
}
}
}
package edu.colorado.phet.chargesandfields {
public class ChargesAndFieldsDisplay extends Sprite {
private var myWidth : Number;
private var myHeight : Number;
public var model : Model;
private var background : BackgroundSprite;
private var mosaic : VoltageMosaic;
private var charges : Array = new Array();
public function ChargesAndFieldsDisplay(tempStage : Stage) {
myWidth = tempStage.stageWidth;
myHeight = tempStage.stageHeight;
model = new Model();
background = new BackgroundSprite(myWidth, myHeight);
addChild(background);
mosaic = new VoltageMosaic(model, myWidth, myHeight);
addChild(mosaic);
for(var i : uint = 0; i < 20; i++) {
var charge : Charge;
if(i % 2 == 0) {
charge = new MinusCharge(mosaic);
} else {
charge = new PlusCharge(mosaic);
}
addChild(charge);
charges.push(charge);
model.addCharge(charge);
charge.setDisplayPosition(Math.random() * myWidth, Math.random() * myHeight);
}
mosaic.draw();
var txt1 : TextField = new TextField();
txt1.text = "foobar";
txt1.alpha = 50;
addChild(txt1);
var txt2 : TextField = new TextField();
txt2.text = "foobar";
txt2.alpha = 100;
txt2.x = 100;
addChild(txt2);
}
public function onResize(evt : Event) : void {
myWidth = this.stage.stageWidth;
myHeight = this.stage.stageHeight;
background.changeSize(myWidth, myHeight);
mosaic.changeSize(myWidth, myHeight);
}
}
}
package edu.colorado.phet.chargesandfields {
public class MinusCharge extends Charge {
[Embed(source='assets.swf', symbol='minusCharge_mc')]
public static var minusMC : Class;
public function MinusCharge(mosaic : VoltageMosaic) {
// create a child sprite containing the graphics
var mc : Sprite = new minusMC();
addChild(mc);
q = -1;
super(mosaic);
}
}
}
package edu.colorado.phet.chargesandfields {
public class Model {
// named ChargeGroup.as in the Flash version
private static const k : Number = 0.5*1E6; // prefactor in E-field equation: E= k*Q/r^2
private static const RtoD : Number = 180/Math.PI; //convert radians to degrees
public var chargeArray : Array;
public var sensorArray : Array;
public function Model() {
chargeArray = new Array();
sensorArray = new Array();
}
public function addCharge(charge : Charge) : void {
chargeArray.push(charge);
// TODO: setChanged or notifyObservers?
}
/*
public function addSensor(sensor : Sensor) {
// TODO: lots of stuff
}
*/
public function removeCharge(charge : Charge) : Boolean {
// pull out removal from array into helper function somewhere
var len : uint = chargeArray.length;
for(var i : uint = 0; i < chargeArray.length; i++) {
if(charge == chargeArray[i]) {
chargeArray.splice(i, 1);
// TODO: setChanged or notifyObservers?
return true;
}
}
return false;
}
public function hasCharges() : Boolean {
return chargeArray.length != 0;
}
/*
public function removeSensor(sensor : Sensor) {
// TODO: implement
}
*/
public function getE(x : Number, y : Number) : Array {
// TODO: optimize function for AS3
var eMag : Number; //Magnitude of E-field
var eAng : Number; //Angle of E-field
var len : uint = chargeArray.length;
var sumX : Number = 0;
var sumY : Number = 0;
for(var i : uint = 0; i < len; i++) {
var xi : Number = chargeArray[i].modelX;
var yi : Number = chargeArray[i].modelY;
var distSq : Number = (x - xi)*(x - xi) + (y - yi)*(y - yi)
var distPow : Number = Math.pow(distSq, 1.5);
sumX = sumX + chargeArray[i].q*(x - xi)/distPow;
sumY = sumY + chargeArray[i].q*(y - yi)/distPow;
}
var EX : Number = k*sumX; //prefactor depends on units
var EY : Number = k*sumY;
eMag = Math.sqrt(EX*EX+EY*EY);
eAng = RtoD*Math.atan2(EY, EX);
//trace(" sumX: "+sumX+" sumY: "+sumY+" sumY/sumX: "+sumY/sumX+" angel: "+EAng);
// TODO: turn this returny goodness into a class
return [eMag, eAng, EX, EY];
}
private function combineRGB(red : Number, green : Number, blue : Number) : uint {
return (red << 16) | (green << 8) | blue;
}
//returns voltage and color nbr (RGB values) associated with voltage
public function getV(x : Number, y : Number) : Array{
var len : uint = chargeArray.length;
var sumV : Number = 0;
var maxV : Number = 20000;//voltage at which color will saturate
var red : Number;
var green : Number;
var blue : Number;
var colorNbr : uint; //RGB number of color associated with voltage
var xi : Number;
var yi : Number;
var dist : Number;
for(var i : uint = 0; i < len ; i++){
var charge : Charge = chargeArray[i];
xi = charge.modelX;
yi = charge.modelY;
dist = Math.sqrt((x - xi)*(x - xi) + (y - yi)*(y - yi));
sumV = sumV + charge.q/dist;
}
sumV = k*sumV; //prefactor depends on units
//set color associated with voltage
if(sumV>0){
red =255;
green = blue = Math.max(0,(1-(sumV/maxV))*255);
}else{
blue = 255;
red = green = Math.max(0,(1-(-sumV/maxV))*255);
}
colorNbr = combineRGB(red,green,blue);
return [sumV,colorNbr];
}
public function getEX(x : Number, y : Number) : Number{
var sum : Number = 0;
for(var i : uint = 0; i < chargeArray.length ; i++){
var xi : Number = chargeArray[i].modelX;
var yi : Number = chargeArray[i].modelY;
var distSq : Number = (x - xi)*(x - xi) + (y - yi)*(y - yi);
sum = sum + chargeArray[i].q*(x - xi)/Math.pow(distSq,1.5);
}
sum = k*sum; //prefactor depends on units
return sum;
}
//return angle of E-field at position (x,y)
public function getEY(x:Number, y:Number):Number{
var sum : Number = 0;
for(var i : uint = 0; i < chargeArray.length ; i++){
var xi : Number = chargeArray[i].modelX;
var yi : Number = chargeArray[i].modelY;
var distSq : Number = (x - xi)*(x - xi) + (y - yi)*(y - yi);
sum = sum + chargeArray[i].q*(y - yi)/Math.pow(distSq,1.5);
}
sum = k*sum; //prefactor depends on units
return sum;
}
//starting at (xInit, yInit), find final position distance delS along equipotential
public function getMoveToSameVPos(VInit:Number, delS:Number, xInit:Number, yInit:Number):Array{
var E0_array : Array = this.getE(xInit,yInit); //E_array = [EMag, EAng, EX, EY]
//var VInit = getV(xInit, yInit)[0]; //getV(x,y) returns [V,color]
var EInit : Number = E0_array[0];
//var EAngInit = E0_array[1];
var EXInit : Number = E0_array[2];
var EYInit : Number = E0_array[3];
var xMid : Number = xInit - delS*EYInit/EInit;
var yMid : Number = yInit + delS*EXInit/EInit;
var E1_array : Array = this.getE(xMid,yMid); //E_array = [EMag, EAng, EX, EY]
var VMid : Number = this.getV(xMid, yMid)[0]; //returns [voltage:Number, colorNbr:Number]
var EMid : Number = E1_array[0];
//var EAngPost = E1_array[1];
var EXMid : Number = E1_array[2];
var EYMid : Number = E1_array[3];
var delX : Number = (VMid - VInit)*EXMid/(EMid*EMid);
var xFinal : Number = xMid + delX;
var yFinal : Number = yMid + delX*EYMid/EXMid;
return [xFinal,yFinal];
}
//starting at (xInit,yInit) move along E-field direction and get (x,y) position at which voltage is targetV
//this function unused at present
public function getTargetVPos(targetV:Number,xInit:Number,yInit:Number):Array{
var E_array : Array = this.getE(xInit,yInit); //E_array = [EMag, EAng, EX, EY]
var VInit : Number = this.getV(xInit, yInit)[0]; //returns [voltage:Number, colorNbr:Number]
var EInit : Number = E_array[0];
// UNUSED!!! var EAngPost : Number = E_array[1];
var EXInit : Number = E_array[2];
var EYInit : Number = E_array[3];
var delX : Number = (VInit - targetV)*EXInit/(EInit*EInit);
var xFinal : Number = xInit + delX;
var yFinal : Number = yInit + delX*EYInit/EXInit ;
//var delEAng = Math.abs(EAngPost-EAngPre);
//delEAng = Math.min(delEAng, 360-delEAng);
//trace("delEAng: "+delEAng);
return [xFinal,yFinal];
}//end of getTargetVPos
// TODO: event handlers, etc
}
}
package edu.colorado.phet.chargesandfields {
public class PlusCharge extends Charge {
[Embed(source='assets.swf', symbol='plusCharge_mc')]
public static var plusMC : Class;
public function PlusCharge(mosaic : VoltageMosaic) {
// create a child sprite containing the graphics
var mc : Sprite = new plusMC();
addChild(mc);
q = 1;
super(mosaic);
}
}
}
package edu.colorado.phet.chargesandfields {
public class VoltageMosaic extends Sprite {
private var myWidth : Number;
private var myHeight : Number;
private var model : Model;
public function VoltageMosaic(model : Model, w : Number, h : Number) {
this.model = model;
myWidth = w;
myHeight = h;
draw();
}
public function changeSize(w : Number, h : Number) : void {
myWidth = w;
myHeight = h;
draw();
}
public function draw() : void {
this.graphics.clear();
var step : Number = 10;
for(var ox : Number = 0; ox < myWidth; ox += step) {
for(var oy : Number = 0; oy < myHeight; oy += step) {
var color : uint = model.getV(ox + step / 2, oy + step / 2)[1];
this.graphics.beginFill(color);
this.graphics.drawRect(ox, oy, ox + step, oy + step);
this.graphics.endFill();
}
}
}
}
}