// coral // cellular automata that looks like coral // algorithmic art by j at lux.vu 2003.11.01, updated 2006.03.20 /* Copyright 2003,2006 Joshua Minor This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // built with processing (www.proce55ing.net) // // each automaton moves a bit like a snowflake, but // adjusts its behavior based on the relative color difference // between itself and its neighbors. this results in an // emergent self-sorting behavior that leaves color gradients // in the display. boolean takeSnap = false; boolean mouseWasPressed = false; float density = 0.15; Automaton autos[]; int numAutos = 0; color empty; float shake = 0; void setup() { size(400, 400, P3D); background(0); framerate(60); // limit the frame rate to at most 60fps empty = get(0,0); // sample the screen to find out the true value of black (probably has some alpha in it) fill(255); rect(width/9*1,height/9*7,width/9,10); rect(width/9*3,height/9*7,width/9,10); rect(width/9*5,height/9*7,width/9,10); rect(width/9*7,height/9*7,width/9,10); if( numAutos == 0 ) { numAutos = (int)(width*height*density); autos = new Automaton[numAutos]; } for (int i=0; i 0 ) { shake--; } // take a snapshot? if( takeSnap ) { saveFrame(); takeSnap = false; } } void keyPressed() { if( key == 's' || key == 'S' ) { takeSnap = true; }else if( key == 'z' ) { int index = (int)random(numAutos); for( int i=0; i<100; i++ ) { autos[index].move( mouseX-autos[index].x+(int)random(10)-5, mouseY-autos[index].y+(int)random(10)-5 ); index++; if( index>=numAutos ) index = 0; } }else{ reset(); shake += 10; } } class Automaton { int x, y; color c; Automaton( int ix, int iy, int ic ) { x = ix; y = iy; c = ic; } // this is the logic that makes the whole thing work // changing this will totally alter the look of the artwork void go() { int nc; // neighbor color float dhue; // difference in hue if( (nc=myget(x-1,y-1))!=empty || (nc=myget(x-1,y))!=empty || (nc=myget(x-1,y+1))!=empty ) { dhue = hue(nc) - hue(c); if( dhue < -20 ) { move( -1, (int)random(3)-1 ); }else if( dhue > 20 ) { move( 1, (int)random(3)-1 ); }else if( abs(dhue) > 10 ) { move( 0, 1 ); //(int)random(3)-1 ); } }else if( (nc=myget(x+1,y-1))!=empty || (nc=myget(x+1,y))!=empty || (nc=myget(x+1,y+1))!=empty ) { dhue = hue(nc) - hue(c); if( dhue < -20 ) { move( 1, (int)random(3)-1 ); }else if( dhue > 20 ) { move( -1, (int)random(3)-1 ); }else if( abs(dhue) > 10 ) { move( 0, 1 ); //(int)random(3)-1 ); } }else{ //move( (int)(red(c)/200f*(random(3)-1)), (int)(green(c)/200f*(random(3)-1)) ); move( (int)(hue(c)/100f*((int)random(3)-1)), 1 ); //(int)(saturation(c)/100f*((int)random(3)-1)) ); } // if we are near the mouse, move around excitedly if( abs(x-mouseX)<20 && abs(y-mouseY)<20 ) move( (int)random(3)-1, (int)random(3)-1 ); if( shake > 0 ) { move( (int)(random(shake)-shake/2), (int)(random(shake)-shake/2) ); } } // try to move in the specified direction (offset in x and y) // but only if that space is not already occupied void move( int dx, int dy ) { // is that space empty? if( myget( x+dx, y+dy ) == empty ) { // erase our old spot myset( x, y, empty ); // move to the new position (wraping around if needed) x = wrapX(x+dx); y = wrapY(y+dy); // fill in our new spot myset( x, y, c ); } } } // get the pixel at x,y taking wraparound into account in case x or y are out of bounds color myget( int x, int y ) { //return get(wrapX(x), wrapY(y)); return pixels[wrapX(x) + wrapY(y)*width]; } // set the pixel at x,y taking wraparound into account in case x or y are out of bounds void myset( int x, int y, color c ) { //set(wrapX(x), wrapY(y), c); //pixels[wrapX(x) + wrapY(y)*width] = c; pixels[x + y*width] = c; } int wrapX( int x ) { while( x<0 ) x+=width; return x % width; } int wrapY( int y ) { while( y<0 ) y+=height; return y % height; }