// SPIROGRAPH // classic spirograph gear tooth counts // 24, 30, 32, 36, 40, 45, 48, 50, 52, 56, 60, 63, 64, 72, 75, 80, 84 // 96/144 105/150 // tooth counts for inner and outer edges final int[] inner = { 96, 105 }; final int[] outer = { 24, 30, 32, 36, 40, 45, 48, 50, 52, 56, 60, 63, 64, 72, 75, 80, 84, 144, 150 }; ////////////// // settings // number of teeth on the fixed gear int Kf = 96; // number of teeth on the rotating gear int Kr = 24; // is the rotating gear inside the fixed gear? boolean inside = true; // number of subdivisions per loop final int S = 120; // how fast the color phase advances final float angleRate = 0.2 * PI; final float colorRate = 0.01 * PI; final float distRate = 0.1 * PI; ////////////// // global state int lastMillis = 0; float anglePhase = 0.0; float colorPhase = 0.0; float distPhase = 0.0; boolean sketchFullScreen() { return false; } void pickSettings() { inside = random(1) < 0.5; if (inside) { int pickFixed = int(random(inner.length)); Kf = inner[pickFixed]; int pickRotor = int(random(outer.length - 2)); Kr = outer[pickRotor]; } else { int pickFixed = int(random(outer.length)); Kf = outer[pickFixed]; int pickRotor = pickFixed; while (pickRotor == pickFixed) pickRotor = int(random(outer.length)); Kr = outer[pickRotor]; } anglePhase = random(PI * 2); colorPhase = random(PI * 2); distPhase = random(PI * 2); // print out some stats print("\nType:", inside ? "Hypotrochoid" : "Epitrochoid", "\n"); print("Fixed:", Kf, "\n"); print("Rotor:", Kr, "\n"); final int divisor = gcd(Kf, Kr); print("Divisor:", divisor, "\n"); final int loops = inside ? (Kf - Kr) / divisor : (Kf + Kr) / divisor; print("Loops:", loops, "\n"); final int steps = S * loops; print("Steps:", steps, "\n"); print("Subdivide:", steps * divisor / Kr, "\n"); } void setup() { //size(displayWidth, displayHeight, P2D); size(512, 512, P2D); colorMode(HSB, 1); pickSettings(); } // Euclid's algorithm for computing greatest common divisor // http://en.wikipedia.org/wiki/Greatest_common_divisor int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } // inside: hypotrochoid // http://mathworld.wolfram.com/Hypotrochoid.html // x(t) = (a - b) cos(t) + h cos(t (a - b) / b) // y(t) = (a - b) sin(t) - h sin(t (a - b) / b) void drawHypotrochoid(int a, int b, float h, float phase) { // set up loop parameters final int divisor = gcd(a, b); final int loops = abs(a - b) / divisor; final int steps = S * loops; // derived constants final float scale = width * 0.5 / max(a, 2 * b - a); final float Ra = scale * (a - b); final float Rb = scale * h; // initial theta values final float ta0 = phase / loops; final float tb0 = 0.0; // delta theta per step final float dta = 2.0f * PI * (b / divisor) / steps; final float dtb = dta * (a - b) / b; // draw the shape beginShape(); for (int step = 0; step < steps; ++step) { final float ta = ta0 + step * dta; final float tb = tb0 + step * dtb; final float x = Ra * cos(ta) + Rb * cos(tb) + width * 0.5; final float y = Ra * sin(ta) - Rb * sin(tb) + height * 0.5; vertex(x, y); } endShape(CLOSE); noFill(); stroke(0, 0, 1, 1); ellipse(width * 0.5, height * 0.5, 2 * scale * a, 2 * scale * a); } // outside: epitrochoid // http://mathworld.wolfram.com/Epitrochoid.html // x(t) = (a + b) cos(t) - h cos(t (a + b) / b) // y(t) = (a + b) sin(t) - h sin(t (a + b) / b) void drawEpitrochoid(int a, int b, float h, float phase) { // set up loop parameters final int divisor = gcd(a, b); final int loops = abs(a + b) / divisor; final int steps = S * loops; // derived constants final float scale = width * 0.5 / (a + 2 * b); final float Ra = scale * (a + b); final float Rb = scale * h; // initial theta values final float ta0 = phase / loops; final float tb0 = 0.0; // delta theta per step final float dta = 2.0f * PI * (b / divisor) / steps; final float dtb = dta * (a + b) / b; // draw the shape beginShape(); for (int step = 0; step < steps; ++step) { final float ta = ta0 + step * dta; final float tb = tb0 + step * dtb; final float x = Ra * cos(ta) + Rb * cos(tb) + width * 0.5; final float y = Ra * sin(ta) - Rb * sin(tb) + height * 0.5; vertex(x, y); } endShape(CLOSE); noFill(); stroke(0, 0, 1, 1); ellipse(width * 0.5, height * 0.5, 2 * scale * a, 2 * scale * a); } void draw() { // measure time step in seconds final int curMillis = millis(); final float dt = 0.001 * float(curMillis - lastMillis); lastMillis = curMillis; background((colorPhase + 0.3333333) % 1, 1, 0.25); stroke(colorPhase % 1, 1, 1, 1); //fill((colorPhase + 0.6666667) % 1, 1, 0.5, 1); noFill(); if (inside) drawHypotrochoid(Kf, Kr, Kr * (0.5f + 0.45 * sin(distPhase)), anglePhase); else drawEpitrochoid(Kf, Kr, Kr * (0.5f + 0.45 * sin(distPhase)), anglePhase); anglePhase += angleRate * dt; distPhase += distRate * dt; colorPhase += colorRate * dt; /* // display render time in milliseconds fill(0, 0, 1, 1); textSize(10); text(millis() - lastMillis, 16, 16); */ } void mousePressed() { pickSettings(); }