// CA_2D_9T // // cellular automata animation // // initial random noise is processed by a CA for a period of time, // then the CA rule shifts and continues to process the canvas. // // Seb Chevrel | www.seb.cc // ——————————————————————————————————————————————————————————————————————————————————————————————————— int WIDTH=320; int HEIGHT=240; int gen=0; int playhead=0; int[] rules= { 47,175,135,175,31,254,135,31}; int[] change={ 50,250,300,200,60,200,200,60}; int duration=rules.length; CellularAutomaton CA; void setup() { size(WIDTH,HEIGHT); CA=new CellularAutomaton(WIDTH,HEIGHT,rules[playhead],8); // width, height, CA rule, history depth noBackground(); CA.init(); } void loop() { CA.run(); CA.render(); if (gen++ > change[playhead]) { gen=0; playhead=(playhead+1)%duration; CA.rule=rules[playhead]; } } // ——————————————————————————————————————————————————————————————————————————————————————————————————— // class CellularAutomaton // // a 2-dimentional 9-neighbor totalistic cellular automaton // initialized with pseudo-random noise, there are 1024 possible rules // some good ones: 47, 513, 123, 135, 159, 111, 254, 175, 321 // a history buffer can average pixel values over succesive iterations to produce a smoother image // ——————————————————————————————————————————————————————————————————————————————————————————————————— class CellularAutomaton { int sizeX=0; int sizeY=0; public int rule=0; int total=0; int[][] buffer; int currentBuffer=0; int nextBuffer=1; int history=0; int buffers=0; // CONSTRUCTOR ———————————————————————————————————————————————————— CellularAutomaton(int x,int y,int r,int h) { sizeX=x; sizeY=y; rule=r; history=h; buffers=2+history; total=sizeX*sizeY; buffer=new int[buffers][total]; } // INIT CA ———————————————————————————————————————————————————————— void init() { for(int i=0;i<total;i++) { if(random(100)>50) buffer[currentBuffer][i]=1; else buffer[currentBuffer][i]=0; } } // RUN CA ————————————————————————————————————————————————————————— void run() { // Process the automaton a times for (int a=0;a<2;a++) { // For each cell in the grid for(int i=0; i<sizeX; i++) { for(int j=0; j<sizeY; j++) { // Wraparound offsets int right=(i+1) % sizeX; int bottom=(j+1) % sizeY; int left=i-1; if (left==-1) left=sizeX-1; int top=j-1; if (top==-1) top=sizeY-1; // Calculate the sum of 9 neighbors int sum=buffer[currentBuffer][left+j*sizeX]; // left middle sum+=buffer[currentBuffer][left+top*sizeX]; // left top sum+=buffer[currentBuffer][left+bottom*sizeX]; // left bottom sum+=buffer[currentBuffer][right+j*sizeX]; // right middle sum+=buffer[currentBuffer][right+top*sizeX]; // right top sum+=buffer[currentBuffer][right+bottom*sizeX]; // right bottom sum+=buffer[currentBuffer][i+j*sizeX]; // middle middle sum+=buffer[currentBuffer][i+top*sizeX]; // middle top sum+=buffer[currentBuffer][i+bottom*sizeX]; // middle bottom // Update the nextBuffer if ((rule & (1<<sum)) >0) buffer[nextBuffer][i+j*sizeX]=1; else buffer[nextBuffer][i+j*sizeX]=0; } } // Transfer copy to map System.arraycopy(buffer[nextBuffer],0,buffer[currentBuffer],0,total); } } // RENDER CA —————————————————————————————————————————————————————— void render() { float sum=0; int col=0; for(int i=0;i<total;i++) { sum=0; for(int d=0;d<buffers;d++) { sum+=buffer[d][i]; } col=int(sum/buffers*255); pixels[i]=col+(col<<8)+(col<<16); } currentBuffer=(currentBuffer+1)%buffers; nextBuffer=(nextBuffer+1)%buffers; } } // ———————————————————————————————————————————————————————————————————————————————————————————————————