/* * DLA - Crystal Simulation * * More Info: http://stungeye.com/archives/2007/10/usually_permane.php * * Wally Glutton 2007 * http://stungeye.com * * License: http://creativecommons.org/licenses/by-sa/3.0/ * */ import controlP5.*; ControlP5 controlP5; // Our state-machine states. final static int RANDOM_POSITION = 0; final static int CHECK_NEIGHBOURS = 1; final static int MOVE_ABOUT = 2; final static int DONE = -1; // Some colours. color white = color(255,255,255); color black = color(0,0,0); color grey = color(150,150,150); // Starting conditions int num_particles = 150; float radius = 10; // Variable conditions int max_particles = 1000; float radius_increment = 0.03; ArrayList particles; void setup() { size(300,300); restart(); controlP5 = new ControlP5(this); controlP5.addSlider("growth",0,5,3,0,height-20,width/2,14); } void restart() { background(black); stroke(white); radius = 10; particles = new ArrayList(); for (int i=0; i < num_particles; i++) { particles.add(new Particle()); } // Seed Points point(width/2-4,height/2-4); point(width/2+4,height/2+4); point(width/2-4,height/2+4); point(width/2+4,height/2-4); } void mousePressed() { if (mouseY < (height-20)) restart(); } void growth(int theValue) { switch (theValue) { case 0: radius_increment = 0.06; break; case 1: radius_increment = 0.05; break; case 2: radius_increment = 0.04; break; case 3: radius_increment = 0.03; break; case 4: radius_increment = 0.02; break; case 5: radius_increment = 0.01; break; } } void draw() { int num_particles = particles.size(); loadPixels(); for (int i = 0; i < num_particles; i++) { Particle p = (Particle) particles.get(i); p.run(); } // Add points while the integer component of the radius is evenly divisible by 10. // Don't add past the set maximum for particles. if ( ((int)radius % 10 == 0) && (num_particles < max_particles) ) { if (odds(15) && (radius < (width/2 +20))) { particles.add(new Particle()); //println("p:" + particles.size() + " r:" + radius); } } } // Close Draw class Particle { int x,y; int state; int ttl; Particle() { state = 0; ttl = 0; } void run() { switch(state) { // Place a particle "randomly" within a circular band centred on mid-screen. // The radius of this band grows as particles are added to the crystal. case RANDOM_POSITION: // Escape the state machine if we are shooting particles out beyond the left/right edge of the window. if (radius > (width/2 +5)) { state = -1; break; } float r = random(radius,radius+10); float theta = random(2*PI); x = int(r * cos(theta) + width/2); y = int(r * sin(theta) + height/2); if (p(x,y,white)) break; stroke(grey); point(x,y); state = CHECK_NEIGHBOURS; break; // If the particle is next to a solidified crystal point, then it too must crystalize. // Otherwise it should move about. case CHECK_NEIGHBOURS: if (has_neighbours(x,y)) { stroke(white); point(x,y); state = RANDOM_POSITION; radius += radius_increment; ttl = 0; } else { stroke(black); point(x,y); state = MOVE_ABOUT; } break; // "Randomly" move about then check for crystalized neighbours. case MOVE_ABOUT: x = int(random(x-1,x+2)); y = int(random(y-1,y+2)); int x2 = x - width/2; int y2 = y - height/2; float dist = sqrt(x2*x2 +y2*y2); // A "gravity" of sorts. Particles which are far away from the radius or have // been floating around for a while are more likely to move towards mid-screen. if (odds(dist - radius + ttl/400)) { if (odds(50)) { if (x > width/2) x--; else x++; } else { if (y > height/2) y--; else y++; } } // Don't overwrite crystalized pixels. if (p(x,y,white)) break; // If we move outside the window, set a new "random" position. if ((x > width) || (y > height)) { state = RANDOM_POSITION; break; } // Use perlin noise to make the moving particles shimmer. stroke(255*noise(x,y)); point(x,y); state = CHECK_NEIGHBOURS; ttl++; } // Close case } // Close run() } // Close class // Returns true if pixel @ (x,y) is of color c, else false. boolean p(int x, int y, color c) { if ((x < 0) || (x > width)) return false; if ((y < 0) || (y > height)) return false; if ((y*width+x) > (width * height)) return false; return (c == pixels[y*width+x]); } // Are my neighbouring pixels crystalized? boolean has_neighbours(int x, int y) { if (p(x-1,y,white)) return true; if (p(x+1,y,white)) return true; if (p(x,y-1,white)) return true; if (p(x,y+1,white)) return true; // Diagonal neighbours only cause us to crystalize 25% of the time. if (p(x+1,y+1,white)) return odds(25); if (p(x-1,y-1,white)) return odds(25); if (p(x-1,y+1,white)) return odds(25); if (p(x+1,y-1,white)) return odds(25); return false; } boolean odds(float percent) { float value = random(100); if (percent > value) { return true; } else { return false; } }