Skip to content

Instantly share code, notes, and snippets.

@x-or
Created September 19, 2013 07:46
Show Gist options
  • Save x-or/6620238 to your computer and use it in GitHub Desktop.
Save x-or/6620238 to your computer and use it in GitHub Desktop.
Cellular Cubism
///// Cellular Cubism
///// Copyright (c) 2013, Sergey Shishmintzev
///// License: Apache/GPLv3
///// Example: http://youtu.be/bGNsrzVarfo
///// Compiler: gcc-4.7.3 (cygwin)
///// Compile command: g++ -O3 unkgen.cpp -lpng
///// P.S. Sorry for my English. It isn't my mother tongue.
/////
///// Reference:
///// [Khan1997] A.R. Khan, et.al., "VLSI architecture of a cellular automata machine", 1997,
///// http://dx.doi.org/10.1016/S0898-1221(97)00021-7
#include <assert.h>
#include <stdio.h>
#include <malloc.h>
#include <math.h>
#include <png.h>
#include <iostream>
#include <string>
#include <vector>
#include <boost/numeric/ublas/matrix.hpp>
#include "boost/random.hpp"
#include "boost/generator_iterator.hpp"
using namespace boost::numeric::ublas;
inline int mod(int a, int b) { // http://stackoverflow.com/questions/4003232/how-to-code-a-modulo-operator-in-c-c-obj-c-that-handles-negative-numbers
if(b < 0) //you can check for b == 0 separately and do what you want
return mod(-a, -b);
int ret = a % b;
if (ret < 0)
ret += b;
return ret;
}
inline int make_rgb(int r, int g, int b) {
return (b&0xFF) | ((g&0xFF)<<8) | ((r&0xFF)<<16);
}
inline int get_r(int rgb) {
return (rgb>>16) & 0xFF;
}
inline int get_g(int rgb) {
return (rgb>>8) & 0xFF;
}
inline int get_b(int rgb) {
return (rgb) & 0xFF;
}
inline void setRGB(png_byte *ptr, int val) {
ptr[0] = get_r(val);
ptr[1] = get_g(val);
ptr[2] = get_b(val);
}
int save_matrix(const matrix<int> &m, const std::string &filename, const std::vector<int> &palette) {
// http://www.labbookpages.co.uk/software/imgProc/libPNG.html
int code = 0;
FILE *fp = NULL;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
png_bytep row = NULL;
// Open file for writing (binary mode)
fp = fopen(filename.c_str(), "wb");
if (fp == NULL) {
fprintf(stderr, "Could not open file %s for writing\n", filename.c_str());
code = 1;
goto finalise;
}
// Initialize write structure
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
fprintf(stderr, "Could not allocate write struct\n");
code = 1;
goto finalise;
}
// Initialize info structure
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
fprintf(stderr, "Could not allocate info struct\n");
code = 1;
goto finalise;
}
// Setup Exception handling
if (setjmp(png_jmpbuf(png_ptr))) {
fprintf(stderr, "Error during png creation\n");
code = 1;
goto finalise;
}
png_init_io(png_ptr, fp);
// Write header (8 bit colour depth)
png_set_IHDR(png_ptr, info_ptr, m.size2(), m.size1(),
8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_write_info(png_ptr, info_ptr);
// Allocate memory for one row (3 bytes per pixel - RGB)
row = (png_bytep) malloc(3 * m.size2() * sizeof(png_byte));
// Write image data
unsigned int i, j;
for (i = 0; i < m.size1(); i++) {
for (j = 0; j < m.size2(); j++) {
setRGB(&(row[j*3]), palette[m(i, j)]);
}
png_write_row(png_ptr, row);
}
// End write
png_write_end(png_ptr, NULL);
finalise:
if (fp != NULL) fclose(fp);
if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
if (row != NULL) free(row);
return code;
}
int linear_gradient(int start_value, int stop_value, float start_offset, float stop_offset, float offset) {
return floor(start_value + ((offset - start_offset) / (stop_offset - start_offset) * (stop_value - start_value)));
}
int gradient(int start, int end, float initial_offset, float stop_offset, float offset) {
int r = linear_gradient(get_r(start), get_r(end), initial_offset, stop_offset, offset);
int g = linear_gradient(get_g(start), get_g(end), initial_offset, stop_offset, offset);
int b = linear_gradient(get_b(start), get_b(end), initial_offset, stop_offset, offset);
return make_rgb(r, g, b);
}
typedef struct point_t {
int value; // point weight (from 1 to modulo-1)
int i, j; // point coordinates
int di, dj; // coordinates increment
} point_t;
int main()
{
using std::cout;
using std::endl;
using std::min;
using std::vector;
using std::pair;
using std::make_pair;
const int modulo = 2048*16;
const int width = 1920;
const int height = 1080;
cout << "modulo = " << modulo << endl;
cout << "size = " << width << "x" << height << endl;
vector<int> palette(modulo);
const int start_color1 = make_rgb(171, 214, 121); // green
const int stop_color1 = make_rgb(65, 66, 225); // blue
//const int start_color2 = make_rgb(171, 214, 121); // green
const int start_color2 = make_rgb(204, 255, 145); // green
const int stop_color2 = make_rgb(209, 35, 42); // red
for(int i = 0; i < modulo/2; i++) {
palette[i] = gradient(start_color1, stop_color1, 0.0, 1.0, min(i*1, modulo/2-1)*1.0/(modulo/2-1));
palette[modulo-i-1] = gradient(start_color2, stop_color2, 0.0, 1.0, min(i*1, modulo/2-1)*1.0/(modulo/2-1));
}
#if 0
for(int i = 0; i < modulo; i++) {
cout << "(" << get_r(palette[i]) << ", " << get_g(palette[i]) << ", "<< get_b(palette[i]) << "), ";
}
#endif
const int point_count = 150;
vector<point_t> point(point_count);
typedef boost::mt19937 RNGType;
RNGType rng(time(0));
RNGType rng2(time(0)/2);
boost::uniform_int<> i_range( 0, height-1 );
boost::uniform_int<> j_range( 0, width-1 );
boost::uniform_int<> direction_range( 0, 3 );
boost::variate_generator< RNGType, boost::uniform_int<> > random_i(rng, i_range);
boost::variate_generator< RNGType, boost::uniform_int<> > random_j(rng2, j_range);
boost::variate_generator< RNGType, boost::uniform_int<> > random_direction_index(rng, direction_range);
const int direction[] = {-1, 1, -2, 2};
for (int p = 0; p < point_count; p++) {
int i, j;
i = random_i();
j = random_j();
assert(i >= 0);
assert(j >= 0);
assert(i < height);
assert(j < width);
point[p].i = i;
point[p].j = j;
point[p].di = direction[random_direction_index()];
point[p].dj = direction[random_direction_index()];
point[p].value = modulo/9;
}
boost::uniform_01<RNGType> zeroone(rng);
const int frames = 10000;
matrix<int> state(height, width);
matrix<int> future_state(height, width);
for (int frame = 0; frame < frames; frame++) {
cout << "frame #" << frame << endl;
future_state.clear();
for (int p = 0; p < point_count; p++) {
int i, j;
int di = point[p].di;
int dj = point[p].dj;
while (1) {
i = point[p].i + di;
j = point[p].j + dj;
if ((i >= height) || (i < 0)) {
di = direction[random_direction_index()];
continue;
}
if ((j >= width) || (j < 0)) {
dj = direction[random_direction_index()];
continue;
}
if (zeroone() < 0.01)
di = direction[random_direction_index()];
if (zeroone() < 0.01)
dj = direction[random_direction_index()];
break;
}
assert(i >= 0);
assert(j >= 0);
assert(i < height);
assert(j < width);
point[p].i = i;
point[p].j = j;
point[p].di = di;
point[p].dj = dj;
future_state(i, j) = mod(future_state(i, j) + point[p].value, modulo);
}
state.clear();
unsigned int i0, i1, j0, j1, solved_state;
for (i0 = 0; i0 < state.size1()-1; i0++) {
i1 = i0 + 1;
for (j0 = 0; j0 < state.size2()-1; j0++) {
j1 = j0 + 1;
solved_state = mod(-state(i0, j0) + state(i0, j1) + state(i1, j0) + future_state(i0, j0), modulo);
assert(solved_state >= 0);
assert(solved_state < modulo);
state(i1, j1) = solved_state;
}
}
// produce image
char fn[100];
sprintf(fn, "frame-rand%d-%dx%d-%05d.png", modulo, width, height, frame);
save_matrix(state, fn, palette);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment