Greetings again. Recently I've purchased a supurb book "The Magic Machine: A Handbook of Computer Sorcery" ISBN 0-7167-2125-2
The author outlines an algorithm to produce a gorgeous cycle of color from random noise. Here is my implimentation.
I don't have a Windows computer but it is in SDL so it should be compilable. Ensure that both the .h file and .cpp file are in the same folder.

If you are running SDL on the Pi it will probably work with the following compile line (assuming you have installed SDL and g++)
Ensure both files are in the same folder when you compile.
g++ -o cyclic_space cyclic_space.cpp -lSDL
If a member here could offer a Windows executable it would be great for those not used to compiling C++.

Feel free to hack on it and speed it up, it is not my algorithm so feel free to adopt it in your demos and such.
I've simply ported it from the book to a modern language.
The main file cyclic_space.cpp
// Ryan Burnside
// 10-26-2012
// "The Demons of Cyclic Space"
// Original algorithm - David Griffeath
// Header Section
#include <SDL/SDL.h>
#include <iostream>
#include <time.h>
#include <vector>
#include "HSLtoRGB.h"
// *** Function Section ***
void putpixel(SDL_Surface* surface, int x, int y, Uint32 pixel);
bool user_quit(SDL_Event &event);
void gen_palette(std::vector<SDL_Color> &colors, int states);
void seed_buffer(int* buffer_start,unsigned int num_elements, int states);
void buffer_flip(SDL_Surface* surface, int* buffer_start,
unsigned int width, unsigned int height,
std::vector<SDL_Color>& color);
int get_index(int* buffer, int width, int x, int y);
void set_index(int* buffer, int width, int x, int y, int value);
Uint32 TimeLeft(const int TICK_INTERVAL);
void advance_pattern(int* old_buffer, int * new_buffer,
unsigned int width, unsigned int height, int max_value);
// *** Body Loop ***
int main (int argc, char** argv)
{
srand (time(NULL));
// Define Constants Section
const int SCREEN_WIDTH = 256;
const int SCREEN_HEIGHT = 256;
const int NUM_STATES = 19; // between 12 and 16 are ideal
const unsigned int NUM_ELEMENTS = SCREEN_WIDTH * SCREEN_HEIGHT;
const int TICK_INTERVAL = 10;
// 2 buffers to hold states of the cells
int old_buf[SCREEN_WIDTH * SCREEN_HEIGHT];
int new_buf[SCREEN_WIDTH * SCREEN_HEIGHT];
seed_buffer(old_buf, NUM_ELEMENTS, NUM_STATES);
seed_buffer(new_buf, NUM_ELEMENTS, NUM_STATES);
// Initialize SDL video
SDL_Init(SDL_INIT_VIDEO);
// SDL cleans up before exit
atexit(SDL_Quit);
// Create a new window
SDL_Surface* screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 16,
SDL_HWSURFACE|SDL_DOUBLEBUF);
// Generate an array of precalculated lookup colors
std::vector<SDL_Color> colors;
gen_palette(colors, NUM_STATES);
SDL_Event event;
while(user_quit(event) == false)
{
advance_pattern(old_buf, new_buf, SCREEN_WIDTH, SCREEN_HEIGHT,
NUM_STATES - 1);
buffer_flip(screen, new_buf, SCREEN_WIDTH, SCREEN_HEIGHT, colors);
SDL_Flip(screen);
// Delay for atleast TICK_INTERVAL
SDL_Delay(TimeLeft(TICK_INTERVAL));
}
return 0;
}
// *** Function Definitions ***
bool user_quit(SDL_Event &event)
{
// These poll for user quit events.
SDL_PollEvent(&event);
switch (event.type)
{
case SDL_QUIT:
return true;
break;
case SDL_KEYDOWN:
{
if (event.key.keysym.sym == SDLK_ESCAPE)
return true;
break;
}
default:
return false;
}
}
void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
// Places a pixel of color Uint32 on a SDL_Surface surface.
int bpp = surface->format->BytesPerPixel;
/* Here p is the address to the pixel we want to set */
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
switch(bpp)
{
case 1:
*p = pixel;
break;
case 2:
*(Uint16 *)p = pixel;
break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
{
p[0] = (pixel >> 16) & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = pixel & 0xff;
}
else
{
p[0] = pixel & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = (pixel >> 16) & 0xff;
}
break;
case 4:
*(Uint32 *)p = pixel;
break;
}
}
void gen_palette(std::vector<SDL_Color>& colors, int states)
{
// Fills a std::vector with SDL_Colors for x many states
// Colors differ by hue for a rainbow effect
for(int i = 0; i < states; ++i)
{
SDL_Color temp;
float r, g, b; // Used to hold values from function
float step = 360.0/states;
HSLtoRGB(i * step, 1, .5, r, g, b);
temp.r = r;
temp.g = g;
temp.b = b;
colors.push_back(temp);
}
}
void seed_buffer(int* buffer_start, unsigned int num_elements, int states)
{
// Assign a random color index to the color array.
for(int i = 0; i < num_elements; ++i)
{
*(buffer_start + i) = rand() % states;
}
}
void buffer_flip(SDL_Surface* surface, int* buffer_start,
unsigned int width, unsigned int height,
std::vector<SDL_Color>& color)
{
// Blits a state buffer to the SDL_surface screen
// using mapped colors defined in the std::vector color.
int counter = 0;
int color_index = 0;
// Lock surface for modification
SDL_LockSurface(surface);
for(int y = 0; y < height; ++y)
{
for(int x = 0; x < width; ++x)
{
color_index = *(buffer_start + counter);
putpixel(surface, x, y, SDL_MapRGB(surface->format,
color[color_index].r,
color[color_index].g,
color[color_index].b));
counter++;
}
}
// Unlock surface
SDL_UnlockSurface(surface);
}
int get_index(int* buffer, int width, int x, int y)
{
// Adapt 2D coordinates to 1 dimension and return index.
return *(buffer + (width * y + x));
}
void set_index(int* buffer, int width, int x, int y, int value)
{
// Adapt 2D coordinates to 1 dimension and set index.
*(buffer + (width * y + x)) = value;
}
Uint32 TimeLeft(const int TICK_INTERVAL)
{
// Taken from SDL homepage
// Returns how many ticls left to wait
static Uint32 next_time = 0;
Uint32 now;
now = SDL_GetTicks();
if ( next_time <= now ) {
next_time = now+TICK_INTERVAL;
return(0);
}
return(next_time-now);
}
void advance_pattern(int* old_buffer, int * new_buffer,
unsigned int width, unsigned int height, int max_value)
{
// Advances the cellular automation pattern 1 step
// Create a list of points to check for neigbours colors
int xpart[] = {0, 1, 0, -1};
int ypart[] = {-1, 0, 1, 0};
int test_x, test_y, old_color, neighbor_color;
for(int y = 0; y < height; ++y)
{
for(int x = 0; x < width; ++x)
{
// Check 8 neighbors using parallel offset arrays
for(int n = 0; n < 4; ++n)
{
test_x = x + xpart[n];
test_y = y + ypart[n];
// Wrap test point if need be
if(test_x < 0)
test_x += width;
if(test_y < 0)
test_y += height;
if(test_x >= width)
test_x -= width;
if(test_y >= height)
test_y -= height;
// Find colors for center point and neighbour
old_color = get_index(old_buffer, width, x, y);
neighbor_color = get_index(old_buffer, width, test_x, test_y);
// If the neighbour is one stage more adopt it for the middle
if(neighbor_color == old_color + 1)
set_index(new_buffer, width, x, y, neighbor_color);
else if(neighbor_color == 0 && old_color == max_value)
set_index(new_buffer, width, x, y, neighbor_color);
}
}
}
// Copy the new buffer into the old
memcpy(old_buffer, new_buffer, width * height * sizeof(int));
}
The HSL to RGB function.
HSLtoRGB.h
#include <cmath>
void HSLtoRGB(float H, float S, float L,
float& outR, float& outG, float& outB,
float channel_max = 255, bool fraction = false)
{
// H [0 - 360] S [0 - 1] V [0 - 1]
float C = (1 - std::abs(2.0 * L - 1)) * S;
float H2 = H/60.0;
float X = C * (1 - std::abs(std::fmod(H2, 2) - 1));
// define base RGB values
float r,g,b = 0;
if(0 <= H2 && H2 < 1)
{
r = C;
g = X;
b = 0;
}
else if(1 <= H2 && H2 < 2)
{
r = X;
g = C;
b = 0;
}
else if(2 <= H2 && H2 < 3)
{
r = 0;
g = C;
b = X;
}
else if(3 <= H2 && H2 < 4)
{
r = 0;
g = X;
b = C;
}
else if(4 <= H2 && H2 < 5)
{
r = X;
g = 0;
b = C;
}
else if(5 <= H2 && H2 < 6)
{
r = C;
g = 0;
b = X;
}
float m = L - C * .5;
if(fraction == false)
{
outR = int((r + m) * channel_max + .5);
outG = int((g + m) * channel_max + .5);
outB = int((b + m) * channel_max + .5);
}
else
{
outR = (r + m) * channel_max;
outG = (g + m) * channel_max;
outB = (b + m) * channel_max;
}
}