/**
*
Pseudo nuclear reaction simulator
* Click to introduce a neutron particle.
* Spacebar to reset.
* + and - to change the density.
* -Chris Hossenlopp
*/
/*
* List of atoms.
*/
ArrayList atoms;
/*
* List of reactor particles (neutrons).
*/
ArrayList reactors;
/*
* List of particles. The leftovers... protons and electrons/
*/
ArrayList particles;
/*
* Spacing between the atoms (inverse density kind of).
*/
float spacing;
/*
* Initiates the window and builds the first
*/
void setup() {
size(700, 600);
background(0);
smooth();
noStroke();
atoms = new ArrayList(); //make atoms
buildAtoms();
reactors = new ArrayList();
particles = new ArrayList();
spacing = 30;
loop();
}
/*
* Build a list of atoms;
*/
void buildAtoms() {
for(int i = ((width/2)-150); i < ((width/2)+150); i += 35) {
for(int j = ((height/2)-150); j < ((height/2)+150); j += 35) {
atoms.add(new Atom(i, j));
}
}
}
/*
* The main draw function. Called on each time the screen refreshes.
*/
void draw() {
fill(0, 15); //using a black fill with 15 alpha. This makes the trail effect.
rect(0,0,width,height); //draw the background.
for(int i = 0; i < particles.size(); i++) { //draw all the particles
((Particle) particles.get(i)).render();
}
for(int i = 0; i < reactors.size(); i++) { //draw all the reactors
((Reactor) reactors.get(i)).render();
}
for(int i = 0; i < atoms.size(); i++) { //draw all the atoms
((Atom) atoms.get(i)).render();
}
updateAtomLocations(); //update the atom locations and account for collisions
checkReactorCollisions(); //check for reactor collions and handle them
}
/*
* Updates the atom locations and accounts for collisions.
*/
void updateAtomLocations() {
//First set the possible change vectors to move towards the cursor
for(int i = 0; i < atoms.size(); i++)
((Atom) atoms.get(i)).moveToCursor();
//Now account for collisions
for(int i = 0; i < atoms.size(); i++)
((Atom) atoms.get(i)).processCollisions(i);
//Now update the locations of the atoms.
for(int i = 0; i < atoms.size(); i++)
((Atom) atoms.get(i)).updateLocation();
}
/*
* Check for reactor collisions and turn atoms into particles or reactors.
*/
void checkReactorCollisions() {
for(int j = 0; j < reactors.size(); j++) { //Iterate through each reactor
Reactor curReactor = (Reactor) reactors.get(j);
for(int i = 0; i < atoms.size(); i++) { //Iterate through each atom
Atom curAtom = (Atom) atoms.get(i);
//Check if they are within the collision distance of each other.
if(dist(curReactor.x, curReactor.y, curAtom.x, curAtom.y) < 10) {
reactors.add(curAtom.getReactor()); //Get the resultant reactor and add to the list
particles.add(curAtom.getEParticle()); //Get the resultant electron and add to the list
particles.add(curAtom.getNParticle()); //Get the resultant null particle (proton?) and add it to the list
atoms.remove(curAtom); //Remove this atom... it is now dead.
break; //Break out of the for loop we should only have one reaction.
}
}
}
}
/*
* Checks for particles that have left the screen. Has not been implemented.
*/
void checkForDeadParticles() {
for(int i = 0; i < particles.size(); i++) {
if( ((Particle) particles.get(i)).isGone() ) {
particles.remove(i);
i--;
}
}
}
/*
* Take care of the mouse click event. It adds a new reactor to the list that shoots toward the
* mouse from the 50px above the top center border of the canvas.
*/
void mousePressed() {
float angle = atan2(mouseY+50, mouseX-(width/2));
float vx = cos(angle)*3;
float vy = sin(angle)*3;
reactors.add(new Reactor(width/2, -50, vx, vy));
}
/*
* Take care of proper events when keys are pressed
*/
void keyPressed() {
if(key == ' ') { //Resets the simulation
atoms.clear();
particles.clear();
reactors.clear();
buildAtoms();
background(0);
} else if(key == '+') { //Increases density
spacing = constrain(spacing-1, 3, 60);
} else if(key == '-') { //Decreases density
spacing = constrain(spacing+1, 3, 60);
}
}
/*
* Model for left over particules from a collision. (Electrons & Protons);
*/
class Particle {
float x, y; //particle's location
float vx, vy; //particle's velocity
float totalX; //totalX distance traveled. Not implemented yet.
float totalY; //totalY distance traveled. Not implemented yet.
float radius; //The radius of the particle
/*
* Constructs a new Particle
*/
Particle(float x, float y, float vx, float vy, float radius) {
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.totalX = 0;
this.totalY = 0;
this.radius = radius;
}
/*
* Draws the particle
*/
void render() {
fill(255); //White
ellipse(x, y, radius, radius);
updateLocation(); //Lets update the location for the next draw.
}
/*
* Updates the location. Simple linear motion here.
*/
void updateLocation() {
x += vx;
y += vy;
//Add to the total distances traveled. Not really used yet.
totalX += vx;
totalY += vy;
}
/*
* Check if we can be sure the particle is off the canvas. Not used yet.
*/
boolean isGone() {
if(abs(totalX) > width || abs(totalY) > height)
return true;
else
return false;
}
} //Particle
/*
* Model of a reactor particle (neutron) that reacts with the atoms.
*/
class Reactor {
float x, y; //Position
float vx, vy; //Velocity
color[] colors; //Array of colors to swap through so it pulses.
int curColor = 0; //Current color index
/*
* Build the array of colors.
*/
void initializeColors() {
colors = new color[4];
colors[0] = color(255,236,139);
colors[1] = color(238,220,130);
colors[2] = color(205,190,112);
colors[3] = color(139,129,76);
}
/*
* Constructs a new reactor particle with no velocity.
*/
Reactor(float x, float y) {
initializeColors();
this.x = x;
this.y = y;
this.vx = 0;
this.vy = 0;
}
/*
* Constructs a new reactor particle.
*/
Reactor(float x, float y, float vx, float vy) {
initializeColors();
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
}
/*
* Draws the reactor particle.
*/
void render() {
fill(colors[curColor]); //Set the color.
ellipse(x,y,5,5);
curColor++; //Increment the color counter
if(curColor == 4) //Wrap it around if its 4
curColor = 0;
updateLocation(); //Update the location for the next draw.
}
/*
* Updates the location for the next draw. Simple linear motion.
*/
void updateLocation() {
x += vx;
y += vy;
}
} //Reactor
/*
* Model of an atom.
*/
class Atom {
float x, y; //Location
float futureX, futureY; //The possible change in location
float nucRot; //Rotation of the nucleus
float nucRotDelta; //Rotational velocity of the nucleus
float elecRot; //Rotation of the electron
float elecRotDelta; //Rotational velocity of the electron
/*
* Default constructor for an atom. Sits in the middle of the screen and has random rotations.
*/
Atom() {
x = width/2;
y = height/2;
nucRotDelta = random(-0.2, 0.2);
nucRot = 0;
elecRot = 0;
elecRotDelta = random(0.01, 0.2);
}
/*
* Constructs an Atom with specifying location. Random rotational velocities are also chosen.
*/
Atom(int x, int y) {
this.x = (float) x;
this.y = (float) y;
nucRotDelta = random(-0.2, 0.2);
nucRot = 0;
elecRot = 0;
elecRotDelta = random(0.01, 0.2);
}
/*
* Draws the atom.
*/
void render() {
fill(255); //White
pushMatrix(); //push the default coordinate system onto the stack.
translate(x,y); //Set the location of the atom as the center of our coord system
pushMatrix(); //push this onto the stack
//Drawing the nucleus
rotate(nucRot); //Rotate
ellipse(2,2,6,6);
ellipse(-2,-2,6,6);
popMatrix(); //Go back to the previous coordinate system to draw the electron.
//Drawing the electron
rotate(elecRot); //Rotate
ellipse(0,15, 2,2);
popMatrix(); //Go back to the default coordinate system.
nucRot += nucRotDelta; //Increment the rotation of the nucleus for the next draw.
elecRot += elecRotDelta; //Increment the rotation of the electron for the next draw.
}
/*
* Determine the change in position we want in the next frame to move towards the mouse.
* This is 2% of the total distance to the mouse.
*/
void moveToCursor() {
this.futureX = 0.02*(mouseX-x);
this.futureY = 0.02*(mouseY-y);
}
/*
* Process all the collisions that regard this atom. This changes the change in position for
* the next step if there is a collision. The integer tells us how far in the collision processing
* we are. This allows to skip interactions we have already checked.
*/
void processCollisions(int j) {
for(int i = j; i < atoms.size(); i++) { //Iterate through the rest of the atoms.
Atom other = (Atom) atoms.get(i);
if(other != this) { //We dont want to be checking ourselves.
//Determine the X distance between this node and the other one in the next frame.
float xdist = (this.x + this.futureX) - (other.x + other.futureX);
//Determine the Y distance between this node and the other one in the next frame.
float ydist = (this.y + this.futureY) - (other.y + other.futureY);
//Calculate the magnitude of the distance
float distance = sqrt(xdist*xdist + ydist*ydist);
if(distance < spacing - 0.5) { //If they will be closer than they should be.
float collisionAngle = atan2(ydist,xdist); //angle between them
//Determine the change in location to avoid this.
this.futureX = cos(collisionAngle)*(spacing - distance);
this.futureY = sin(collisionAngle)*(spacing - distance);
}
}
}
}
/*
* Update the location of the atom.
*/
void updateLocation() {
x += futureX;
y += futureY;
}
/*
* Sets the location of this atom. Not currently used.
*/
void setLocation(float x, float y) {
this.x = x;
this.y = y;
}
/*
* Creates a new Reactor. This figures a 'proper' velocity and location for the reactor.
* Essentially turns the neuron into a reactor. This might not be doing it correctly.
* Its hard to tell.
*/
Reactor getReactor() {
float v = 3*nucRotDelta*10; //determine the magnitude of the velocity. Its current tangental velocity.
float rotation = nucRot + (PI/4); //Add 45 degrees to the current rotation?
//Determine the x and y components. Considers the current translation and rotational velocities into this.
float vx = cos(rotation)*v + this.futureX;
float vy = sin(rotation)*v + this.futureY;
//Determine the location of the Reactor. 2.828 =~ the radius here.
float x = this.x + cos(rotation)*2.828;
float y = this.y + sin(rotation)*2.828;
return new Reactor(x, y, vx, vy);
}
/*
* Creates a new electron Particle. This figures a 'proper' velocity and location for the particle.
* Essentially turns the electron into a free electron.
*/
Particle getEParticle() {
float v = -15*elecRotDelta; //determine the magnitude of the velocity. Its current tangental velocity.
//Determine the x and y components. Considers the current translation and rotational velocities into this.
float vx = cos(elecRot)*v + this.futureX;
float vy = sin(elecRot)*v + this.futureY;
//Determine the location of the Particle. 15 is the radius here.
float x = this.x - cos(elecRot - (PI/2))*15;
float y = this.y - sin(elecRot - (PI/2))*15;
return new Particle(x, y, vx, vy, 2);
}
/*
* Creates a new proton Particle. This figures a 'proper' velocity and location for the particle.
* Essentially turns the proton into a free proton. This might not be doing it correctly.
* Its hard to tell.
*/
Particle getNParticle() {
float v = 3*nucRotDelta*10; //determine the magnitude of the velocity. Its current tangental velocity.
float rotation = nucRot - ((3*PI)/4); //Subtract 135 degrees from the current rotation?
//Determine the x and y components. Considers the current translation and rotational velocities into this.
float vx = cos(rotation)*v + this.futureX;
float vy = sin(rotation)*v + this.futureY;
//Determine the location of the Particle. 2.828 =~ the radius here.
float x = this.x + cos(rotation)*2.828;
float y = this.y + sin(rotation)*2.828;
return new Particle(x, y, vx, vy, 6);
}
} //Atom