Simulate: PARTICLES
Particle systems are an essential technique for simulating nature. These systems can replicate the look and behavior of water, fire, smoke, explosions, clouds, fog, hair, fur, stars, and more. In a basic particle system, each element has a position and a velocity. In more advanced systems to simulate water or the movement of stars, each particle is influenced by virtual forces such as gravity and friction. A particle can be rendered on-screen as either a single pixel, a small image, or a 3-D object.
This basic particle system is created by making multiple copies of a simple particle. Each particle stores some information about itself: its position in space, velocity, and acceleration. At each step in the simulation, every particle calculates its next location using its current position, velocity, and acceleration. Finally, each particle is drawn to the screen and the process repeats.
/**
* Simulate: Particles
* from Form+Code in Design, Art, and Architecture
* by Casey Reas, Chandler McWilliams, and LUST
* Princeton Architectural Press, 2010
* ISBN 9781568989372
*
* This code was written for Processing 1.2+
* Get Processing at http://www.processing.org/download
*/
Particle[] particles = new Particle[1000];
boolean saving = false;
void setup() {
size(1024, 768);
smooth();
// create particles
for (int i = 0; i < particles.length; i++) {
particles[i] = new Particle(new PVector(100, height-100));
}
}
void draw() {
background(255);
// draw the particles
for (int i = 0; i < particles.length; i++) {
particles[i].update();
particles[i].draw();
}
}
// ---------------
// Particle.pde
// Based on the Particle class from: http://www.shiffman.net/teaching/nature/particles/
// ---------------
class Particle {
PVector loc;
PVector vel;
PVector acc;
PVector[] hist;
int counter = 0;
// create the particle
Particle(PVector l) {
float randmin = -HALF_PI;
float randmax = 0;
float r = random(0, TWO_PI);
float x = cos(r);
float y = sin(r);
acc = new PVector(x / 250, y / 250);
float q = random(0, 1);
r = random(randmin, randmax);
x = cos(r) * q;
y = sin(r) * q;
vel = new PVector(x, y);
loc = l.get();
hist = new PVector[1000];
}
// update location
void update() {
vel.add(acc);
loc.add(vel);
// save location every 10 frames
if (frameCount % 10 == 0 && counter < hist.length) {
hist[counter] = loc.get();
counter++;
}
}
// draw particle
void draw() {
fill(100,50);
drawArrowHead(vel,loc,10);
noFill();
// draw history path
stroke(0, 100);
beginShape();
for (int i=0; i < counter; i++) {
vertex(hist[i].x,hist[i].y);
}
if (counter > 0) vertex(loc.x, loc.y);
endShape();
}
void drawArrowHead(PVector v, PVector loc, float scale) {
pushMatrix();
float arrowsize = 4;
// Translate to location to render vector
translate(loc.x, loc.y);
// rotate to heading
rotate(v.heading2D());
// Calculate length of vector & scale it to be bigger or smaller if necessary
float len = v.mag()*scale;
arrowsize = map(len, 0, 10, 0, 1) * arrowsize;
// Draw point
stroke(0, 100);
fill(0, 100);
line(0,0,len-arrowsize,0);
noStroke();
beginShape();
vertex(len,0);
vertex(len-arrowsize,+arrowsize/2);
vertex(len-arrowsize,-arrowsize/2);
endShape(CLOSE);
popMatrix();
}
}
Contributed Examples
-
OpenFrameworks
/** * Simulate: Particles from Form+Code in Design, Art, and Architecture * implemented in OpenFrameworks by Anthony Stellato <http://rabbitattack.com/> * * Requires OpenFrameworks available at http://openframeworks.cc/ * * For more information about Form+Code visit http://formandcode.com */ // =========================================================== // - Simulate_Particles.h // =========================================================== #include "ofMain.h" class Particle { public: ofPoint loc, vel, acc; ofPoint *hist; int counter; Particle(){}; Particle(ofPoint l); void draw(); void drawArrowHead(ofPoint v, ofPoint loc, float scale); }; class testApp : public ofBaseApp{ public: void setup(); void draw(); }; // =========================================================== // - Simulate_Particles.cpp // =========================================================== #include "Simulate_Particles.h" Particle *particles; bool saving = false; float mag(ofPoint in){ float retf = sqrt(in.x * in.x + in.y * in.y); return retf; } //-------------------------------------------------------------- void testApp::setup(){ ofSetFrameRate(60); ofEnableSmoothing(); ofEnableAlphaBlending(); particles = new Particle[1000]; for(int i = 0; i < 1000; i++){ particles[i] = Particle(ofPoint(100, ofGetHeight()-100)); } } //-------------------------------------------------------------- void testApp::draw(){ ofBackground(255, 255, 255); ofSetColor(0, 0, 0); for(int i = 0; i < 1000; i++){ particles[i].update(); particles[i].draw(); } } Particle::Particle(ofPoint l){ counter = 0; float randmin = -HALF_PI; float randmax = 0; float r = ofRandom(0, TWO_PI); float x = cos(r); float y = sin(r); acc = ofPoint(x / 250, y / 250); float q = ofRandom(0, 1); r = ofRandom(randmin, randmax); x = cos(r) * q; y = sin(r) * q; vel = ofPoint(x, y); loc = l; hist = new ofPoint[1000]; } void Particle::update(){ vel += acc; loc += vel; if(ofGetFrameNum() % 10 == 0 && counter < 1000){ hist[counter].x = loc.x; hist[counter].y = loc.y; counter++; } } void Particle::draw(){ ofFill(); ofSetColor(100, 100, 100, 100); drawArrowHead(vel, loc, 10); ofNoFill(); ofSetColor(0, 0, 0, 100); ofBeginShape(); for(int i = 0; i < counter; i++){ ofVertex(hist[i].x, hist[i].y); } if(counter > 0) ofVertex(loc.x, loc.y); ofEndShape(false); } void Particle::drawArrowHead(ofPoint v, ofPoint loc, float scale){ ofPushMatrix(); float arrowsize = 5.0f; ofTranslate(loc.x, loc.y, 0); float rotate = atan2(v.y, v.x); ofRotate(ofRadToDeg(rotate), 0, 0, 1); float len = mag(v) * scale; arrowsize = ofMap(len, 0.f, 10.f, 0.f, 1.f, false) * arrowsize; ofLine(0, 0, len-arrowsize, 0); ofBeginShape(); ofVertex(len, 0); ofVertex(len-arrowsize, arrowsize/2); ofVertex(len-arrowsize, -arrowsize/2); ofEndShape(true); ofPopMatrix(); }
-
Cinder
/** * Simulate: Particles from Form+Code in Art, Design, and Architecture * implemented in C++ by Patrick Tierney <http://ptierney.com> * * Requires Cinder 0.8.2 available at http://libcinder.org * * Project files are located at https://github.com/hlp/form-and-code * * For more information about Form+Code visit http://formandcode.com */ #include <boost/date_time.hpp> #include "cinder/app/AppBasic.h" #include "cinder/Rand.h" #include "cinder/CinderMath.h" class Particle; class Simulate_Particles : public ci::app::AppBasic { public: void prepareSettings(Settings* settings); void setup(); void draw(); private: std::vector<Particle> particles; bool saving; }; class Particle { public: Particle(ci::Vec2f l) { counter = 0; float randmin = -M_PI / 2.0f; float randmax = 0; float r = ci::Rand::randFloat(0, M_PI * 2.0f); float x = ci::math<float>::cos(r); float y = ci::math<float>::sin(r); acc = ci::Vec2f(x / 250.0f, y / 250.0f); float q = ci::Rand::randFloat(0, 1); r = ci::Rand::randFloat(randmin, randmax); x = ci::math<float>::cos(r) * q; y = ci::math<float>::sin(r) * q; vel = ci::Vec2f(x, y); loc = l; counter = 0; hist.resize(1000); } void update() { vel += acc; loc += vel; // save location every 10 frames if (ci::app::getElapsedFrames() % 10 == 0) { hist[counter] = (loc); counter++; } } void drawArrowHead(ci::Vec2f v, ci::Vec2f loc, float scale) { ci::gl::pushMatrices(); float arrowsize = 4; // Translate to location to render vector ci::gl::translate(ci::Vec2f(loc.x, loc.y)); // Rotate to the vector heading ci::Quatf q(0.0f, 0.0f, ci::math<float>::atan2(v.normalized().y, v.normalized().x)); ci::gl::rotate(q); // Calculate length of vector & scale it to be bigger or smaller if necessary float len = v.length()*scale; arrowsize = ci::lmap<float>(len, 0, 10, 0, 1) * arrowsize; // Draw point glColor4f(0.0f, 0.0f, 0.0f, 100.0f/255.0f); ci::gl::drawLine(ci::Vec2f(0,0),ci::Vec2f(len-arrowsize,0)); glBegin(GL_TRIANGLES); glVertex2f(len,0); glVertex2f(len-arrowsize,+arrowsize/2); glVertex2f(len-arrowsize,-arrowsize/2); glEnd(); ci::gl::popMatrices(); } void draw() { float c = 100.0f/255.0f; glColor4f(c, c, c, 50.0f/255.0f); drawArrowHead(vel,loc,10); // draw history path glColor4f(0.0f, 0.0f, 0.0f, 100.0f/255.0f); glBegin(GL_LINE_STRIP); for (int i = 0; i < counter; i++) { glVertex2f(hist[i].x, hist[i].y); } if (!hist.empty()) glVertex2f(loc.x, loc.y); glEnd(); } private: ci::Vec2f loc; ci::Vec2f vel; ci::Vec2f acc; std::vector<ci::Vec2f> hist; int counter; }; void Simulate_Particles::prepareSettings(Settings* settings) { settings->setWindowSize(1024, 768); } void Simulate_Particles::setup() { glEnable(GL_LINE_SMOOTH); ci::gl::enableAlphaBlending(); for (int i = 0; i < 1000; i++) { particles.push_back(Particle(ci::Vec2f(100, getWindowHeight()-100))); } } void Simulate_Particles::draw() { ci::gl::setMatricesWindow(getWindowSize()); ci::gl::clear(ci::Color::white()); for (std::vector<Particle>::iterator it = particles.begin(); it != particles.end(); ++it) { it->update(); it->draw(); } } CINDER_APP_BASIC(Simulate_Particles, ci::app::RendererGl)
We are looking for implementations of the code examples in other programming languages to post on the site. If you would like to submit a sample, or if you find a bug, please write to