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!