/** *

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