/* Hexagon pattern generator. ./hex_pattern_stripes > output.pgm This is a fork of hex_pattern_dots.c to use a stripe pattern instead of fixed dots. It looks better at higher resolutions. */ #include #include #include #include #ifdef _WIN32 #include #include #endif #define PI 3.14159265358979323846264338327950288419716939937510 #define MIN_BLOCK_COUNT 10 #define MAX_BLOCK_COUNT 50 #define COLOR_COUNT 4 typedef struct { double x, y; } XY; typedef struct { double a, b, c, d, e, f; } Transform; /* Apply transformations to point. */ static double ApplyTransformX(XY *input, Transform *t) { return input->x * t->a + input->y * t->b + t->c; } static double ApplyTransformY(XY *input, Transform *t) { return input->x * t->d + input->y * t->e + t->f; } static XY ApplyTransform(XY *input, Transform *t) { XY output; output.x = ApplyTransformX(input, t); output.y = ApplyTransformY(input, t); return output; } /* Integer modulus that guarantees positive return value. */ static int Mod(int n, int d) { return n > 0 ? n % d : (d - (-n % d)) % d; } int main(int argc, char **argv) { int width, height, x, y, q, r, t, s1a, s1b, s2a, s2b; int colors[MAX_BLOCK_COUNT][MAX_BLOCK_COUNT]; double angle, scale; Transform grid; XY p1, p2; if( argc != 3 || (width = atoi(argv[1])) <= 0 || (height = atoi(argv[2])) <= 0 ) { return printf("%s \n", *argv); } if( width >= 0x8000 || height >= 0x8000 ) return !puts("Output size too large."); #ifdef _WIN32 setmode(STDOUT_FILENO, O_BINARY); #endif srand(time(NULL)); /* Generate transformation for scaling and rotation. */ scale = (double)rand() / RAND_MAX * (MAX_BLOCK_COUNT - MIN_BLOCK_COUNT) + MIN_BLOCK_COUNT; scale /= width > height ? width : height; angle = ((double)rand() / RAND_MAX) * 0.5 * PI; grid.a = cos(angle) * scale; grid.d = sin(angle) * scale; grid.b = -grid.d; grid.e = grid.a; grid.c = (double)rand() / RAND_MAX * 4.0; grid.f = (double)rand() / RAND_MAX * 4.0; /* Stretch in X axis to compensate for hexagonal coordinates. */ grid.a /= sqrt(3.0); grid.b /= sqrt(3.0); /* Build table of random colors. */ for(y = 0; y < MAX_BLOCK_COUNT; y++) { for(x = 0; x < MAX_BLOCK_COUNT; x++) colors[y][x] = (int)((double)rand() / (RAND_MAX + 1u) * COLOR_COUNT); } /* Generate stripe parameters. Note that even though grid spacing scales with output size, stripe sizes are fixed. There is a range where stripes are neither too thin or too thick, outside that range they just don't seem to look as good. */ s2a = (int)((double)rand() / RAND_MAX * 8) + 2; s2b = (int)((double)rand() / RAND_MAX * 8) + 2; s1a = s2b * 2; s1b = s2a * 2; /* Render pixels. */ printf("P5\n%d %d\n255\n", width, height); for(y = 0; y < height; y++) { p1.y = y; for(x = 0; x < width; x++) { p1.x = x; p2 = ApplyTransform(&p1, &grid); t = (int)floor(p2.x + p2.y); r = (int)floor((floor(p2.y - p2.x) + t) / 3); q = (int)floor((floor(2 * p2.x + 1) + t) / 3) - r; t = colors[Mod(q, MAX_BLOCK_COUNT)][Mod(r, MAX_BLOCK_COUNT)]; switch( t ) { case 1: q = (x % s1a * s1b > (s1b - 1 - y % s1b) * s1a) ^ ((x / s1a + y / s1b) % 2); q = (double)rand() / RAND_MAX * 2 < q; fputc(q ? 255 : 1, stdout); break; case 2: q = (x % s2a * s2b > y % s2b * s2a) ^ ((x / s2a + y / s2b) % 2); q = (double)rand() / RAND_MAX * 2 > q; fputc(q ? 255 : 1, stdout); break; case 3: fputc(255, stdout); break; default: fputc(0, stdout); break; } } } return 0; }