//Simple model of a Water system using particle systems for PCS November 2014 //By Z. Wood and K. Davis for PCS 5th grade // // Run the program and watch the water particles fill the sea - when you click the // mouse over the water, the particles evaporate and become "clouds" when the clouds // become dense enough, the particles rain down //TODO for students - fix the program to draw the sun correctly! //Draw the sun as two triangles (one upside down) and an ellipse- see the handout //By default, your sun should be centered at (50, 50) - //The ellipse for the center of the sun, should be 80 pixels wide and the triangle //should have edges approximately 100 pixels long //you may turn on grid lines to help you plot the triangle vertices if you'd like //fix this void drawSun() { fill(255, 255, 255); //delete this rectangle and draw a sun instead rect(10, 10, 80, 80); } boolean grid = false; //Do not edit the program below this line /*-------------------------------------------------------------------------------------*/ void drawGrid() { //the horizontal lines stroke(0, 0, 255); line(0, 50, width, 50); line(0, 100, width, 100); line(0, 150, width, 150); line(0, 200, width, 200); line(0, 250, width, 250); line(0, 300, width, 300); line(0, 350, width, 350); //the vertical lines stroke(255, 0, 255); line(50, 0, 50, height); line(100, 0, 100, height); line(150, 0, 150, height); line(200, 0, 200, height); line(250, 0, 250, height); line(300, 0, 300, height); line(350, 0, 350, height); } boolean waterCycle = false; void setup() { size(800, 500); colorMode(RGB, 255, 255, 255, 100); numB = 8; bPerSys = 200; raining = false; bubbles = new PSys[numB]; for (int i=0; i< numB; i++) { bubbles[i] = new PSys(bPerSys, new PVector(20+i*(width/numB), height, 0)); } smooth(); frame = 0; frameRate(40); sun = false; sunStrength = 80; waterHght = 5*height/6; cloudHght = height/7; rainFrame = 0; } void mousePressed() { sun = !sun; raining = false; } void draw() { int numInWater; background(#87FCEC); int inClouds = 0; for (int i=0; i< numB; i++) { bubbles[i].run(); inClouds += bubbles[i].numInClouds(); if (sun && waterCycle) { //make all the bubble near the sun evaporate bubbles[i].nearSun(mouseX, mouseY, sunStrength); } } percInClouds = (double)inClouds / ((double)bPerSys * numB); if (percInClouds > 0.55) { println("rain!"); raining = true; sun = false; } frame++; if (sun) { pushMatrix(); translate(mouseX-50, mouseY-50); drawSun(); popMatrix(); } if (grid) { drawGrid(); } } PSys bubbles[]; int frame; int numB; int bPerSys; double percInClouds; boolean raining; boolean sun; int sunStrength; int waterHght; int cloudHght; int rainFrame; final int inWater = 0; final int inClouds = 1; final int evaporate = 2; final int precipitate = 3; //define a particle class Particle { PVector loc; PVector vel; PVector accel; float r; float life; color pcolor; int state; int white; color col; //constructor that uses specified color Particle(PVector start, color in_c) { accel = new PVector(0, -0.05, 0); //gravity vel= new PVector(random(-1, 1), random(-0.5, 0), 0); pcolor = in_c; loc = start.get(); r = 20 +random(10); life = 200; state = inWater; white = 255; } //constructor that chooses a random color Particle(PVector start) { accel.set(0, 0, 0); //gravity vel.set(random(-2, 2), random(-2, 0), 0); pcolor = color(random(255), random(255), random(255)); loc = start.get(); r = 18.0; life = 200; state = inWater; } //what to do each frame void run() { updateP(); renderP(); } void stopDraw() { renderP(); } //how to move void updateP() { //if in the clouds if (state == inClouds) { //if its raining - all particles in the cloud start to rain if (raining) { //add randomness so all particles don't rain down at once if (random(-5, 10) < 0) { state = precipitate; pcolor = color(107, 127, random(200, 250), random(56, 128)); vel.set(0, random(1, 2), 0); accel.set(0, random(0.1, 0.2), 0); } } else { //stop moving and turn a color dependent on density in the clouds accel.set(0, 0.02, 0); vel.set(0, 0, 0); pcolor= color((float)white * (float)(1 - percInClouds), random(56, 128)); } } if (state == precipitate) { //if we haven't reached the water, keep falling down if (loc.y <= waterHght) { if (r > 10) { r -= 0.05; } if (vel.y <= 0) { pcolor = color(random(80, 150), random(80, 150), random(200, 250), random(56, 128)); vel.set(0, random(1, 2), 0); accel.set(0, random(0.1, 0.2), 0); } } else { //turn into water state = inWater; r = 18.0; } } if (state == evaporate) { //add some randomness so all particles don't evaporate immediately if (random(-5, 8) < 0) { //if we haven't reached the clouds, keep going up if (loc.y > cloudHght) { pcolor = color(random(180, 190), random(180, 190), random(180, 250), 50); if (vel.y >= 0) { vel.set(0, random(-1, -2.5), 0); accel.set(0, random(-.05, -0.2), 0); } } else { //turn into water state = inClouds; } } } if (state == inWater) { r = 18; //if the particle hasn't reached the water top (ie at spawn) if (loc.y > waterHght) { //keep moving upward if (loc.y > height && vel.y >= 0) { //bounce off bottom pcolor = color(12, 34, random(160, 200), random(56, 180)); vel.set(0, random(-1, -0.1), 0); accel.set(0, random(-0.01, -0.02), 0); } } else { if (vel.y <= 0) { pcolor = color(12, 34, random(160, 200), random(56, 180)); vel.set(0, random(1, 0.1), 0); accel.set(0, random(0.01, 0.2), 0); } } } vel.add(accel); loc.add(vel); } //how to draw a particle void renderP() { pushMatrix(); ellipseMode(CENTER); stroke(pcolor-40); fill(pcolor); translate(loc.x, loc.y); ellipse(0, 0, r, r); popMatrix(); } boolean alive() { if (life <= 0.0) { return false; } else { return true; } } } //end of particle object definition //define a group of particles as a particleSys class PSys { ArrayList particles; //all the particles PVector source; //where all the particles emit from color shade; int total; //constructor PSys(int num, PVector init_loc) { particles = new ArrayList(); source = init_loc.get(); shade = color(12, 34, random(160, 200), random(56, 180)); //start with one particle particles.add(new Particle(source, shade)); total = num; } //what to do each frame void run() { //go through backwards for deletes for (int i=particles.size()-1; i >=0; i--) { Particle p = (Particle) particles.get(i); //update each particle per frame p.run(); if (!p.alive()) { particles.remove(i); } } if (particles.size() < total) { particles.add(new Particle(source, shade)); } } void stopDraw() { for (int i=0; i < particles.size(); i++) { Particle p = (Particle) particles.get(i); p.stopDraw(); } } //options for adding particles to the system - default void addParticle() { particles.add(new Particle(source)); } //add at a specific point void addParticle(float x, float y) { particles.add(new Particle(new PVector(x, y))); } //add for an already defined particle void addParticle(Particle p) { particles.add(p); } //is particle still populated? boolean dead() { if (particles.isEmpty() ) { return true; } else { return false; } } int numInClouds() { int total = 0; for (int i=0; i < particles.size(); i++) { Particle p = (Particle) particles.get(i); if (p.state == inClouds) { total++; } } return total; } //for ever particle - if its near the sun, set its state to evaporate int nearSun(int cx, int cy, int radius) { int total = 0; for (int i=0; i < particles.size(); i++) { Particle p = (Particle) particles.get(i); if (dist(p.loc.x, p.loc.y, cx, cy) < radius) { p.state = evaporate; total += 1; } } return total; } }