Simulate: DIFFUSION-LIMITED AGGREGATION
Diffusion-limited aggregation (DLA) is a process for generating organic forms from a few simple rules. Particles moving through space, typically in a pattern called a random walk, stick together when they collide. The form is built up over time as more and more particles collide and clump together. The aggregate form often has a complex, branching structure.
The process begins with a fixed seed. Next, virtual particles are created and begin to move through the space. The motion of each particle is created by choosing a new random direction and moving a short distance at each step of the simulation. When the particle moves, it checks to see if it has collided with the seed or another fixed particle. If it collides with either, it stops moving and becomes part of the growing form.
/**
* Simulate: Diffusion-Limited Aggregation
* 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
*/
// this number might need to be smaller for some computers
int particleCount = 20000;
Particle[] particles = new Particle[particleCount];
boolean[] field;
void setup() {
size(1024, 700, P2D);
// create an array that stores the position of our particles
field = new boolean[width * height];
// add seed in the center
int fcenterX = width / 2;
int fcenterY = height / 2;
field[fcenterX + fcenterY * width] = true;
// make particles
for(int i=0; i<particleCount; i++) {
particles[i] = new Particle();
}
}
void draw() {
background(255);
loadPixels();
for(int i=0; i<particleCount; i++) {
particles[i].update();
if (particles[i].stuck) {
pixels[particles[i].y * width + particles[i].x] = color(0);
}
}
updatePixels();
}
// ---------------
// Particle.pde
// ---------------
class Particle
{
int x, y;
boolean stuck = false;
Particle() {
reset();
}
void reset() {
// keep choosing random spots until an empty one is found
do {
x = floor(random(width));
y = floor(random(height));
} while (field[y * width + x]);
}
void update() {
// move around
if (!stuck) {
x += round(random(-1, 1));
y += round(random(-1, 1));
if (x < 0 || y < 0 || x >= width || y >= height) {
reset();
return;
}
// test if something is next to us
if (!alone()) {
stuck = true;
field[y * width + x] = true;
}
}
}
// returns true if no neighboring pixels
boolean alone() {
int cx = x;
int cy = y;
// get positions
int lx = cx-1;
int rx = cx+1;
int ty = cy-1;
int by = cy+1;
if (cx <= 0 || cx >= width ||
lx <= 0 || lx >= width ||
rx <= 0 || rx >= width ||
cy <= 0 || cy >= height ||
ty <= 0 || ty >= height ||
by <= 0 || by >= height) return true;
// pre multiply the ys
cy *= width;
by *= width;
ty *= width;
// N, W, E, S
if (field[cx + ty] ||
field[lx + cy] ||
field[rx + cy] ||
field[cx + by]) return false;
// NW, NE, SW, SE
if (field[lx + ty] ||
field[lx + by] ||
field[rx + ty] ||
field[rx + by]) return false;
return true;
}
}
Contributed Examples
-
OpenFrameworks
/** * Simulate: DLA 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_DLA.h // =========================================================== #include "ofMain.h" class aggParticle { public: void setup(bool *_field, int _w, int _h); void reset(); void update(); bool alone(); int round(float _in); int x, y, w, h; bool stuck; bool *field; }; class testApp : public ofBaseApp{ public: void setup(); void update(); void draw(); ofTexture texture; unsigned char * pixels; int width, height; uint particleCount; aggParticle *mParticles; bool *field; }; // =========================================================== // - Simulate_DLA.cpp // =========================================================== #include "testApp.h" #define WIDTH 1024 #define HEIGHT 768 //-------------------------------------------------------------- void testApp::setup(){ //initialize field field = new bool[WIDTH*HEIGHT]; for(int i = 0; i < WIDTH*HEIGHT; i++){ field[i] = false; } //setup texture width = WIDTH; height = HEIGHT; pixels = new unsigned char [width * height]; texture.allocate(width, height, GL_LUMINANCE); for (int i = 0; i < width * height; i++){ pixels[i] = 255; } texture.loadData(pixels, width, height, GL_LUMINANCE); //seed center int fcenterX = width/2; int fcenterY = height/2; field[fcenterX + fcenterY * width] = true; //initialize particle array particleCount = 200000; mParticles = new aggParticle[particleCount]; for(int i = 0; i < particleCount; i++){ aggParticle temp; temp.setup(field, WIDTH, HEIGHT); mParticles[i] = temp; } } //-------------------------------------------------------------- void testApp::update(){ for(int i = 0; i < particleCount; i++){ mParticles[i].update(); if(mParticles[i].stuck){ pixels[mParticles[i].y * width + mParticles[i].x] = 0; } } texture.loadData(pixels, width, height, GL_LUMINANCE); } //-------------------------------------------------------------- void testApp::draw(){ texture.draw(0, 0); } //-------------------------------------------------------------- void aggParticle::setup(bool *_field, int _w, int _h){ field = _field; w = _w; h = _h; stuck = false; reset(); } //-------------------------------------------------------------- void aggParticle::reset(){ do { x = ofRandom(0, ofGetWidth()); y = ofRandom(0, ofGetHeight()); } while (field[y * ofGetWidth() + x]); } //-------------------------------------------------------------- void aggParticle::update(){ if(!stuck){ x += round(ofRandomf()); y += round(ofRandomf()); if(x < 0 || y < 0 || x > w || y > h){ this->reset(); return; } if(!alone()){ stuck = true; field[y * w + x] = true; } } } //-------------------------------------------------------------- int aggParticle::round(float _in){ if(_in > 0.0f) return ceil(_in); else if(_in < 0.0f) return floor(_in); else return 0; } //-------------------------------------------------------------- bool aggParticle::alone(){ int cx = x; int cy = y; int lx = cx - 1; int rx = cx + 1; int ty = cy - 1; int by = cy + 1; if(cx <= 0 || cx >= w || lx <= 0 || lx >= w || rx <= 0 || rx >= w || cy <= 0 || cy >= h || ty <= 0 || ty >= h || by <= 0 || by >= h){ return true; } cy *= w; by *= w; ty *= w; if(field[cx + ty] || field[lx + cy] || field[rx + cy] || field[cx + by]){ return false; } if(field[lx + ty] || field[lx + by] || field[rx + ty] || field[rx + by]){ return false; } return true; }
-
Cinder
/** * Simulate: Diffusion-Limited Aggregation 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 <vector> #include <boost/shared_ptr.hpp> #include <boost/date_time.hpp> #include "cinder/gl/gl.h" #include "cinder/gl/Texture.h" #include "cinder/app/AppBasic.h" #include "cinder/Rand.h" #include "cinder/CinderMath.h" class Particle; // soon to be std::shared_ptr typedef boost::shared_ptr<Particle> ParticlePtr; class Simulate_DLA : public ci::app::AppBasic { public: void prepareSettings(Settings* settings); void setup(); void update(); void draw(); void shutdown(); std::vector<bool> field; private: int particleCount; std::vector<ParticlePtr> particles; GLubyte* data; int dataSize; }; class Particle { public: Particle(Simulate_DLA& diffusionApp) : field(diffusionApp.field) { stuck = false; width = diffusionApp.getWindowWidth(); height = diffusionApp.getWindowHeight(); reset(); } void reset() { // keep choosing random spots until an empty one is found do { x = ci::Rand::randInt(width); y = ci::Rand::randInt(height); } while (field[y * width + x]); } void update() { // move around if (!stuck) { // get random int [-1, 1] (hence 2) x += ci::Rand::randInt(-1, 2); y += ci::Rand::randInt(-1, 2); if (x < 0 || y < 0 || x >= width || y >= height) { reset(); return; } // test if something is next to us if (!alone()) { stuck = true; field[y * width + x] = true; } } } // returns true if no neighboring pixels bool alone() { int cx = x; int cy = y; // get positions int lx = cx-1; int rx = cx+1; int ty = cy-1; int by = cy+1; if (cx <= 0 || cx >= width || lx <= 0 || lx >= width || rx <= 0 || rx >= width || cy <= 0 || cy >= height || ty <= 0 || ty >= height || by <= 0 || by >= height) return true; // pre multiply the ys cy *= width; by *= width; ty *= width; // N, W, E, S if (field[cx + ty] || field[lx + cy] || field[rx + cy] || field[cx + by]) return false; // NW, NE, SW, SE if (field[lx + ty] || field[lx + by] || field[rx + ty] || field[rx + by]) return false; return true; } bool stuck; int x, y; private: int width, height; std::vector<bool>& field; }; void Simulate_DLA::prepareSettings(Settings* settings) { settings->setWindowSize(1024, 700); } void Simulate_DLA::setup() { // this number might need to be smaller for some computers particleCount = 20000; particles.resize(particleCount); // create an array that stores the position of our particles and set them to false field.resize(getWindowWidth() * getWindowHeight()); for (std::vector<bool>::iterator it = field.begin(); it != field.end(); ++it) { *it = false; } // add seed in the center int fcenterX = getWindowWidth() / 2; int fcenterY = getWindowHeight() / 2; field[fcenterX + fcenterY * getWindowWidth()] = true; // make particles for (int i = 0; i < particles.size(); ++i) { particles[i] = ParticlePtr(new Particle(*this)); } // create pixel buffer dataSize = getWindowWidth() * getWindowHeight() * 3; data = new GLubyte[dataSize]; // set all pixels to white for (int i = 0; i < dataSize; i++) { data[i] = (GLubyte) 255; } } void Simulate_DLA::update() { for(int i = 0; i < particleCount; i++) { particles[i]->update(); if (particles[i]->stuck) { data[particles[i]->y * getWindowWidth() * 3 + particles[i]->x * 3] = (GLubyte) 0; data[particles[i]->y * getWindowWidth() * 3 + particles[i]->x * 3 + 1] = (GLubyte) 0; data[particles[i]->y * getWindowWidth() * 3 + particles[i]->x * 3 + 2] = (GLubyte) 0; } } } void Simulate_DLA::draw() { glDrawPixels(getWindowWidth(), getWindowHeight(), GL_RGB, GL_UNSIGNED_BYTE, data); } void Simulate_DLA::shutdown() { delete [] data; } CINDER_APP_BASIC(Simulate_DLA, 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