/* Cafe wall illusion generator. ./cafe_wall > output.pgm https://en.wikipedia.org/wiki/Caf%C3%A9_wall_illusion */ #include #include #include #include #ifdef _WIN32 #include #include #endif #define PI 3.14159265358979323846264338327950288419716939937510 #define MIN_BRICK_COUNT 10.0 #define MAX_BRICK_COUNT 30.0 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; } int main(int argc, char **argv) { int width, height, x, y, b; double angle, scale; Transform wall; 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 from screen coordinates to brick coordinates. */ scale = (double)rand() / RAND_MAX * (MAX_BRICK_COUNT - MIN_BRICK_COUNT) + MIN_BRICK_COUNT; scale /= width > height ? width : height; angle = ((double)rand() / RAND_MAX) * 0.5 * PI; wall.a = cos(angle) * scale; wall.d = sin(angle) * scale; wall.c = (double)rand() / RAND_MAX * 4.0; wall.b = -wall.d; wall.e = wall.a; wall.f = (double)rand() / RAND_MAX * 2.0; /* 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, &wall); /* Draw mortar lines for Y values near integer boundaries. */ if( p2.y - floor(p2.y) < 0.1 ) { fputc(128, stdout); continue; } /* Draw black or white bricks based on X value, applying a shift based on Y value. */ b = (int)floor(p2.x + sin(floor(p2.y) * PI / 4)); fputc(b % 2 == 0 ? 255 : 0, stdout); } } return 0; }