Author Topic: The Demons of Cyclic Space (C++ & SDL)  (Read 4168 times)

0 Members and 1 Guest are viewing this topic.

Offline Pixel_Outlaw

  • Pentium
  • *****
  • Posts: 1390
  • Karma: 84
    • View Profile
The Demons of Cyclic Space (C++ & SDL)
« on: October 28, 2012 »
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++. :D

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
Code: [Select]
// 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

Code: [Select]
#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;
    }
}
« Last Edit: October 28, 2012 by Pixel_Outlaw »
Challenge Trophies Won:

Offline Optimus

  • DBF Aficionado
  • ******
  • Posts: 2456
  • Karma: 128
    • View Profile
    • Optimouse Demo Site
Re: The Demons of Cyclic Space (C++ & SDL)
« Reply #1 on: October 29, 2012 »
Wow, interesting algorithm and an interesting book.
I should try this later at home when I am back from work.
Challenge Trophies Won:

Offline Kirl

  • Senior Member
  • Pentium
  • ********
  • Posts: 1219
  • Karma: 231
    • View Profile
    • Homepage
Re: The Demons of Cyclic Space (C++ & SDL)
« Reply #2 on: October 29, 2012 »
Wow, that looks great, thanks for posting the book! :)
www.kirl.nl
Challenge Trophies Won: