Skip to content

Instantly share code, notes, and snippets.

@x-or
Created September 19, 2013 07:42
Show Gist options
  • Save x-or/6620212 to your computer and use it in GitHub Desktop.
Save x-or/6620212 to your computer and use it in GitHub Desktop.
Abstract Plasma
///// Abstract Plasma
///// Copyright (c) 2013, Sergey Shishmintzev
///// License: Apache/GPLv3
///// Example: http://youtu.be/xImkiaobRJA
///// 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) {
// make gradient color
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; // partcle coordinates increment
int ai, aj; // bar angle
int bar; // bar length
} point_t;
int main() {
using std::cout;
using std::endl;
using std::min;
using std::max;
using std::vector;
using std::pair;
using std::make_pair;
const int modulo = 1920; // arbitrary even number
// output image size
const int width = 1920;
const int height = 1080;
const int points_count = 99; // visual granularity
cout << "modulo = " << modulo << endl;
cout << "size = " << width << "x" << height << endl;
cout << "points count = " << points_count << endl;
// make double gradient palette
vector<int> palette(modulo);
//const int start_color1 = make_rgb(171, 214, 121); // green
const int start_color1 = make_rgb(204, 255, 145); // 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, modulo/2-1)*1.0/(modulo/2-1));
palette[modulo-i-1] = gradient(start_color2, stop_color2, 0.0, 1.0, min(i, modulo/2-1)*1.0/(modulo/2-1));
}
const int intermediate_color = make_rgb(145, 35, 145); // red
for(int i = 0; i < modulo/4; i++) {
palette[modulo/4+i] = gradient(palette[modulo/4], intermediate_color, 0.0, 1.0, i*1.0/(modulo/4-1));
palette[-modulo/4+modulo-i-1] = gradient(palette[-modulo/4+modulo-1], intermediate_color, 0.0, 1.0, i*1.0/(modulo/4-1));
}
vector<point_t> point(points_count);
// random stuff
typedef boost::mt19937 RNGType;
RNGType rng1(time(0));
RNGType rng2(time(0)/2);
RNGType rng3(time(0)/3);
RNGType rng4(time(0)/4);
RNGType rng5(time(0)/5);
RNGType rng6(time(0)/6);
RNGType rng7(time(0)/7);
boost::uniform_int<> i_range( 0, height-1 );
boost::uniform_int<> j_range( 0, width-1 );
boost::uniform_int<> direction_range( 0, 3 );
boost::uniform_int<> value_range( -10, 10 );
boost::uniform_int<> bar_angle_range( 0, 1 );
boost::uniform_int<> bar_length_range( 1, 200 );
boost::variate_generator< RNGType, boost::uniform_int<> > random_i(rng1, i_range);
boost::variate_generator< RNGType, boost::uniform_int<> > random_j(rng2, j_range);
boost::variate_generator< RNGType, boost::uniform_int<> > random_direction_index(rng3, direction_range);
boost::variate_generator< RNGType, boost::uniform_int<> > random_value(rng4, value_range);
boost::variate_generator< RNGType, boost::uniform_int<> > random_bar_angle(rng5, bar_angle_range);
boost::variate_generator< RNGType, boost::uniform_int<> > random_bar_length(rng6, bar_length_range);
const int direction[] = {-2, 2, -3, 3}; // slow and fast moves
const int bar_angle[] = {-1, 1, 0};
// generate random positions for points
for (int p = 0; p < points_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].value = random_value();
point[p].i = i;
point[p].j = j;
point[p].di = direction[random_direction_index()];
point[p].dj = direction[random_direction_index()];
point[p].ai = bar_angle[random_bar_angle()];
point[p].aj = bar_angle[random_bar_angle()];
point[p].bar = random_bar_length();
}
boost::uniform_01<RNGType> zeroone(rng7);
const int frames = 10000; // count of the images to produce
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 < points_count; p++) {
int i, j;
int di = point[p].di;
int dj = point[p].dj;
int bi, bj;
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;
}
// chaotic moving
if (zeroone() < 0.01)
di = direction[random_direction_index()];
if (zeroone() < 0.01)
dj = direction[random_direction_index()];
break;
} // retry ...
assert(i >= 0);
assert(j >= 0);
assert(i < height);
assert(j < width);
// new point position
point[p].i = i;
point[p].j = j;
// new point position increment
point[p].di = di;
point[p].dj = dj;
for (int x = 0; x < point[p].bar; x++) {
bi = i + x*point[p].ai;
if ((bi < 0) || (bi >= height))
break;
bj = j + x*point[p].aj;
if ((bj < 0) || (bj >= width))
break;
future_state(bi, bj) = mod(future_state(bi, bj) + point[p].value, modulo);
}
} // p = ...
state.clear();
// Backwards an automata rule: future_state(i0, j0) == state(i0, j0) - state(i0, j1) - state(i1, j0) + state(i1, j1) (mod modulo).
// It is the rule 15 [Khan1997] when modulo == 2.
// It is the unambiguous when we know initial values of the state (state(0, k) == 0 and state(k, 0) == 0).
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, "rand%d-%dx%d-frame%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