Skip to content

Instantly share code, notes, and snippets.

@x-or
Created September 27, 2013 08:41
Show Gist options
  • Save x-or/6725748 to your computer and use it in GitHub Desktop.
Save x-or/6725748 to your computer and use it in GitHub Desktop.
Fantastic Lava
///// Fantastic Lava
///// Copyright (c) 2013, Sergey Shishmintzev
///// License: Apache/GPLv3
///// Example: http://youtu.be/OZ0wLfmWhHo
///// 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.
#include <assert.h>
#include <stdio.h>
#include <malloc.h>
#include <math.h>
#include <limits>
#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, int palette_offset) {
// 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)+palette_offset)%palette.size()]);
}
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;
// random generators seed
const int seed = time(0);
//const int seed = 1380113269; // http://youtu.be/OZ0wLfmWhHo
const int modulo = 256*256; // arbitrary even number
// output image size
const int width = 1920;
const int height = 1080;
const int points_count = 100; // visual granularity
cout << "seed = " << seed << endl;
cout << "modulo = " << modulo << endl;
cout << "size = " << width << "x" << height << endl;
cout << "points count = " << points_count << endl;
// palette random stuff
typedef boost::mt19937 RNGType;
RNGType rng_color(seed^1234);
RNGType rng_palette(seed^4321);
RNGType rng01(seed^0x1234);
// make multigradient palette
vector<int> palette(modulo);
//const int start_color1 = make_rgb(171, 214, 121); // green
const int start_color = make_rgb(204, 255, 145); // green
boost::uniform_int<> color_range( 0, 255 );
boost::uniform_int<> gradient_range( 256, 256*5 );
boost::variate_generator< RNGType, boost::uniform_int<> > random_color(rng_color, color_range);
boost::variate_generator< RNGType, boost::uniform_int<> > random_gradient(rng_palette, gradient_range);
boost::uniform_01<RNGType> zeroone(rng01);
int next_colors = palette.size();
int last_color = start_color;
int color_index = 0;
while (next_colors > 1024) {
const int gradient_size = random_gradient();
const int stop_color = make_rgb(random_color(), random_color(), random_color());
int last_gradient_color = 0;
for(int i = 0; i < gradient_size; i++) {
last_gradient_color = gradient(last_color, stop_color, 0.0, 1.0, i*1.0/(gradient_size-1));
palette[color_index+i] = last_gradient_color;
}
next_colors -= gradient_size;
color_index += gradient_size;
last_color = last_gradient_color;//stop_color;
}
// palette tail backwards to start color
for(int i = 0; i < next_colors; i++) {
palette[color_index+i] = gradient(last_color, start_color, 0.0, 1.0, i*1.0/(next_colors-1));
}
vector<point_t> point(points_count);
// particles random stuff
RNGType rng1(seed);
RNGType rng2(seed/2);
RNGType rng3(seed/3);
RNGType rng4(seed/4);
RNGType rng5(seed/5);
RNGType rng6(seed/6);
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, 3 );
boost::uniform_int<> bar_length_range( 20, 300 );
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, -2, 2, -3, 3, -4, 4, -5, 5, -6, 6, -7, 7, -8, 8, -9, 9, -10, 10};
// 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()];
while (1) { // failback loop
point[p].ai = bar_angle[random_bar_angle()];
point[p].aj = bar_angle[random_bar_angle()];
//if (point[p].ai && point[p].aj)
// continue; // fail
break; // ok
}
point[p].bar = random_bar_length();
}
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) { // failback loop
i = point[p].i + di;
j = point[p].j + dj;
if ((i >= height) || (i < 0)) {
di = direction[random_direction_index()];
continue; // fail
}
if ((j >= width) || (j < 0)) {
dj = direction[random_direction_index()];
continue; // fail
}
// chaotic moving
if (zeroone() < 0.005)
di = direction[random_direction_index()];
if (zeroone() < 0.005)
dj = direction[random_direction_index()];
break; // ok
} // 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).
// 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
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 with palette cycling
char fn[100];
sprintf(fn, "rand%d-%dx%d-frame%05d.png", modulo, width, height, frame);
save_matrix(state, fn, palette, frame*5);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment