Log-polar graph paper

Code:
/*
gcc -std=c99 -Wall -pedantic -Wextra graph-paper.c -o graph-paper -lGLEW -lGL -lGLU -lglut
./graph-paper
for i in *.pgm
do
pnmtopng -force -interlace -compression 9 -phys 11811 11811 1 <$i >${i%pgm}png
done
*/
#include <stdio.h>
#include <string.h>
#include <GL/glew.h>
#include <GL/glut.h>
#define GLSL(s) #s
static const int width = 2480;
static const int height = 3508;
static const float size = 4.0;
static int gcd(int x, int y) {
if (y == 0) { return x; }
else { return gcd(y, x % y); }
}
static const char *src = GLSL(
uniform vec3 twist;
vec2 clog(vec2 x) {
return vec2(log(length(x)), atan2(x.y, x.x)) * 0.15915494309189535;
}
void main() {
vec2 p = gl_TexCoord[0].xy;
p *= mat2(0.0, -1.0, 1.0, 0.0);
vec2 q = clog(p);
float a = atan2(twist.y, twist.x);
float h = length(vec2(twist.x, twist.y));
q *= mat2(cos(a), sin(a), -sin(a), cos(a)) * h;
float d = length(vec4(dFdx(q), dFdy(q)));
float l = ceil(-log2(d));
float f = pow(2.0, l + log2(d)) - 1.0;
l -= 6.0;
float o[2];
for (int i = 0; i < 2; ++i) {
l += 1.0;
vec2 u = q * pow(2.0, l);
u *= twist.z;
u -= floor(u);
float r = min
( min(length(u), length(u - vec2(1.0, 0.0)))
, min(length(u - vec2(0.0, 1.0)), length(u - vec2(1.0, 1.0)))
);
float c = clamp(1.5 * r / (pow(2.0, l) * d), 0.0, 1.0);
vec2 v = q * pow(2.0, l - 2.0);
v *= twist.z;
v -= floor(v);
float s = min(min(v.x, v.y), min(1.0 - v.x, 1.0 - v.y));
float k = clamp(0.75 + 0.25 * s / (pow(2.0, l - 2.0) * d), 0.0, 1.0);
o[i] = c * k;
}
gl_FragColor = vec4(vec3(mix(o[1], o[0], f)), 1.0);
}
);
int main(int argc, char **argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutCreateWindow("graphpaper");
glewInit();
GLint success;
int prog = glCreateProgram();
int frag = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(frag, 1, (const GLchar **) &src, 0);
glCompileShader(frag);
glAttachShader(prog, frag);
glLinkProgram(prog);
glGetProgramiv(prog, GL_LINK_STATUS, &success);
if (!success) exit(1);
glUseProgram(prog);
GLuint utwist = glGetUniformLocation(prog, "twist");
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 4096, 4096, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);
GLuint fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
glViewport(0, 0, width, height);
glLoadIdentity();
gluOrtho2D(0, 1, 1, 0);
unsigned char *buffer = malloc(width * height);
for (int x = 1; x <= 5; ++x) {
for (int y = 0; y <= x; ++y) {
if ((x > 1 && y == 0) || (y > 0 && gcd(x, y) != 1)) { continue; }
for (int n = 5; n <= 8; ++n) {
glUniform3f(utwist, x, y, n / 8.0);
glBegin(GL_QUADS); {
float u = size / 2.0;
float v = size * (height - width / 2.0) / width;
glTexCoord2f( u, -v); glVertex2f(1, 0);
glTexCoord2f( u, u); glVertex2f(1, 1);
glTexCoord2f(-u, u); glVertex2f(0, 1);
glTexCoord2f(-u, -v); glVertex2f(0, 0);
} glEnd();
glReadPixels(0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, buffer);
char fname[200];
snprintf(fname, 100, "graphpaper-%d-%d-%d.pgm", x, y, n);
FILE *f = fopen(fname, "wb");
fputs("P5\n2480 3508\n255\n", f);
fflush(f);
fwrite(buffer, width * height, 1, f);
fflush(f);
fclose(f);
}
}
}
free(buffer);
glDeleteFramebuffers(1, &fbo);
glDeleteTextures(1, &tex);
glDeleteShader(frag);
glDeleteProgram(prog);
glutReportErrors();
return 0;
}
Output:
This is an excavation from my archives, around 2013 or so. Print out your favourites and enjoy doodling!