package curranPhysics;
import java.awt.Color;
import java.awt.Dimension;
import primitives3D.Cube3DEdges;
import primitives3D.Vector3D;
import drawing3D.RotatableObject3DViewingPanel;
public class CurranPhysics extends RotatableObject3DViewingPanel {
private static final long serialVersionUID = -5009278391630542659L;
/**
* The number of balls
*/
int numBalls = 60;
/**
* The viscosity, a value from 0 to 1.
*/
double viscosity = 0.01;
/**
* The gravity.
*/
double gravity = 0;
/**
* The strength of the attractive forces
*/
double attractiveForceStrength = 0;
/**
* The strength of the repulsive forces
*/
double repulsiveForceStrength = 0;
/**
* The strength of the coulombic forces
*/
double coulombicForceStrength = 0;
/**
* The array containing the balls.
*/
MovingBall3D[] balls;
/**
* The dimension which will store the size of the panel.
*/
Dimension panelSize = new Dimension();
/**
* A temporary variable used in calculations
*/
Vector3D tempVector = new Vector3D();
boolean in3D = false;
/**
* A flag which is set to true after the constructor finishes, so that
* updateForEachFrame() doesn't try to access the array of balls before it
* has been created.
*/
boolean allset = false;
public CurranPhysics() {
// turn off 3D rotation and perspective
viewer.window.drawFor3D = false;
//initialize the balls
createBalls();
// set the flag that indicates that the constructor is finished
// used by updateForEachFrame()
allset = true;
}
protected void createBalls()
{
//clear previous balls
viewer.clear3DObjects();
//add the cube if in 3D
if(viewer.window.drawFor3D)
viewer.add3DObjects((new Cube3DEdges(new Vector3D(0,0,0),20,Color.green)).getLineSegments());
// add the balls
balls = new MovingBall3D[numBalls];
for (int i = 0; i < balls.length; i++)
{
viewer.add3DObject(balls[i] = new MovingBall3D(new Vector3D(Math
.random() * 20-10, Math.random() * 20-10, viewer.window.drawFor3D?Math.random() * 20-10:0), .6));
balls[i].shade = viewer.window.drawFor3D;
}
setCharges();
}
protected void setCharges() {
double newValue = coulombicForceStrength;
// the value should be between 0 and 1
newValue = newValue < 0 ? -newValue : newValue;
newValue = newValue > 1 ? 1 : newValue;
// set the "charge" inside the balls so they
// have the right color (that is the only
// reason)
for (int i = 0; i < balls.length; i++)
balls[i].setCharge(newValue
* balls[i].chargePolarity);
}
/**
* This gets called every frame. It updates the balls.
*/
protected void updateForEachFrame() {
if (allset) {
if(numBalls != balls.length)
createBalls();
if(in3D != viewer.window.drawFor3D)
{
viewer.window.drawFor3D = in3D;
createBalls();
}
// calculate the forces for each pair of balls once
for (int j = 0; j < balls.length; j++)
for (int i = 1 + j; i < balls.length; i++)
calculateForce(balls[i], balls[j]);
// apply the forces to the balls
getSize(panelSize);
for (int i = 0; i < balls.length; i++) {
bounceIfNecessary(balls[i]);
balls[i].applyMotionVector();
// apply viscosity
balls[i].motionVector.times(1 - viscosity,
balls[i].motionVector);
// apply gravity
if(viewer.window.drawFor3D)
balls[i].motionVector.z -= gravity/1000;
else
balls[i].motionVector.y -= gravity/1000;
}
// draw the balls and do whatever else is necessary
super.updateForEachFrame();
}
}
/**
* Modifies the motion of the specified ball such that it will stay on the
* screen.
*
* @param ball
*/
private void bounceIfNecessary(MovingBall3D ball) {
if (ball.p.x > 10)
ball.motionVector.x = -Math.abs(ball.motionVector.x);
else if (ball.p.x < -10)
ball.motionVector.x = Math.abs(ball.motionVector.x);
if (ball.p.y > 10)
ball.motionVector.y = -Math.abs(ball.motionVector.y);
else if (ball.p.y < -10)
ball.motionVector.y = Math.abs(ball.motionVector.y);
if (ball.p.z > 10)
ball.motionVector.z = -Math.abs(ball.motionVector.z);
else if (ball.p.z < -10)
ball.motionVector.z = Math.abs(ball.motionVector.z);
}
/**
* Calculates force between the two specified balls, then adds that force to
* the motion vector of each ball.
*
* @param ballA
* the first of the pair of balls to perform the calculations on.
* @param ballB
* the second of the pair of balls to perform the calculations
* on.
*/
private void calculateForce(MovingBall3D ballA, MovingBall3D ballB) {
// calculate the distance between the balls
double distance = Vector3D.calculateDistance(ballA.p, ballB.p);
// calculate the unit vector for the force
ballB.p.minus(ballA.p, tempVector);
tempVector.times(1 / distance, tempVector);
double dSquared = Math.pow(Math.max(distance, 0.6),2);
//the attractive forces
double force=attractiveForceStrength/dSquared;
// the repulsive forces
force-=repulsiveForceStrength/(Math.pow(Math.max(distance, 0.4),3));
// the coulombic forces
force-=ballA.chargePolarity*ballB.chargePolarity*coulombicForceStrength/dSquared;
force/=1000;
tempVector.times(force, tempVector);
// apply the force to ballA
ballA.motionVector.plus(tempVector, ballA.motionVector);
// apply the force to ballB
ballB.motionVector.minus(tempVector, ballB.motionVector);
}
}
/*
* class DecimalVariable { DecimalValue reUsableValue = new DecimalValue(0);
*
* Variable variable;
*
* public DecimalVariable(String variableName) { variable =
* Variable.getVariable(variableName); }
*
* public double getValue() { return
* DecimalValue.extractDoubleValue(variable.evaluate()); }
*
* public void setValue(double value) { reUsableValue.value = value;
* variable.set(reUsableValue); } }
*/