Created
January 30, 2021 22:31
-
-
Save cwillmor/30daddba73cca2a31c7136caadd95d33 to your computer and use it in GitHub Desktop.
unfold animation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// unfold animation https://twitter.com/cwillmore/status/1355644262835122176 | |
// developed in processing 3.5.4 | |
static final int FRAME_RATE = 30; | |
static final float ITER_DURATION = 2.0; // seconds | |
static final int TOTAL_FRAMES = (int)(FRAME_RATE * ITER_DURATION); | |
static final int WIDTH = 500; | |
static final int HEIGHT = 500; | |
static final boolean SAVE_FRAMES = false; | |
static final float eps = 0.00003; | |
import com.jogamp.opengl.GL; | |
import com.jogamp.opengl.GL2ES2; | |
PJOGL pgl; | |
GL2ES2 gl; | |
color[] colors; | |
void setup() { | |
frameRate(FRAME_RATE); | |
size(500, 500, P3D); | |
rectMode(CENTER); | |
fill(#ff0000); | |
noStroke(); | |
colors = new color[] { #845ec2, #ff5e78, #f9f871 }; // https://colorhunt.co/palette/252741 | |
} | |
float ease(float x) { | |
if (x < 0) { | |
return 0; | |
} else if (x < 1) { | |
return -(cos(PI * x) - 1) / 2; | |
} else { | |
return 1; | |
} | |
} | |
void enableBackfaceCulling() { | |
// https://forum.processing.org/two/discussion/25272/how-to-enable-backface-culling-in-p3d | |
pgl = (PJOGL) beginPGL(); | |
gl = pgl.gl.getGL2ES2(); | |
gl.glEnable(GL.GL_CULL_FACE); | |
gl.glCullFace(GL.GL_BACK); | |
} | |
// draw a triangular flap with fixed corners (0, 1, 0) and (0, -1, 0) and free corner rotating from (0, 0, 0) to (2, 0, 0) about the fold through +z space as s goes from 0 to 1. | |
// also draw the section of the center square under the flap, and the shadow on that section due to the flap. | |
void drawFlap(color outerColor, color innerColor, color shadowColor, float s) { | |
push(); | |
translate(1, 0, 0); | |
// draw flap | |
push(); | |
if (s == 0) { | |
translate(0, 0, 2 * eps); // avoid z-fighting | |
} | |
rotateY(PI * s); | |
// - outer face of flap | |
fill(outerColor); | |
normal(0, 0, 1); | |
triangle(0, 1, -1, 0, 0, -1); | |
// - inner face of flap | |
fill(innerColor); | |
normal(0, 0, -1); | |
triangle(-1, 0, 0, 1, 0, -1); | |
pop(); | |
// draw inner square section under flap | |
push(); | |
fill(innerColor); | |
normal(0, 0, 1); | |
triangle(0, 1, -1, 0, 0, -1); | |
pop(); | |
// draw shadow under flap | |
if (s < 0.5) { | |
push(); | |
fill(shadowColor); | |
translate(0, 0, 1 * eps); | |
normal(0, 0, 1); | |
triangle(0, 1, -cos(PI * s), 0, 0, -1); | |
pop(); | |
} | |
pop(); | |
} | |
void draw() { | |
float t = (float)(frameCount % TOTAL_FRAMES) / TOTAL_FRAMES; | |
float delta1 = 0.4; // time from flap A1 start to flap A2 start | |
float delta2 = 0.0; // time from flap A2 end to flap B1 start | |
float shadowFactor = 0.7; | |
int iter = (frameCount / TOTAL_FRAMES) % colors.length; | |
color outerColor = colors[iter]; | |
color middleColor = colors[(iter + 1) % colors.length]; | |
color middleShadowColor = lerpColor(#000000, middleColor, shadowFactor); | |
color innerColor = colors[(iter + 2) % colors.length]; | |
color innerShadowColor = lerpColor(#000000, innerColor, shadowFactor); | |
background(255); | |
enableBackfaceCulling(); | |
// set up camera, lights | |
translate(WIDTH/2, HEIGHT/2, 0); | |
scale(WIDTH/4); | |
scale(1.2); | |
translate(0, 0, -0.3); // nudge a bit so we don't get cut off at the bottom | |
rotateX(PI/4); | |
lights(); | |
rotateZ(-5*PI/8); | |
//rotateZ(-t * PI/4); | |
rotateZ(iter * PI/4); | |
rotateZ(-(t + iter) * (PI/4 - 2 * PI/3)); // make it so big flap isn't unfolding in the same place every iter | |
scale(pow(sqrt(2), -t)); // square unfolds by sqrt(2) each iteration, scale to compensate | |
scale(1/sqrt(2)); // we got outer flaps now :( | |
float flapDuration = 1 - delta1 - delta2; | |
// draw middle flaps | |
for (int i = 0; i < 4; i++) { | |
float flapStart = -1 + i * delta1; | |
float s = ease((t - flapStart) / flapDuration); | |
push(); | |
rotateZ(i * PI/2); | |
// draw flap | |
drawFlap(outerColor, middleColor, middleShadowColor, s); | |
pop(); | |
} | |
// draw outer flaps | |
push(); | |
translate(0, 0, 2 * eps); | |
for (int i = 0; i < 4; i++) { | |
float flapStart = i * delta1; | |
float s = ease((t - flapStart) / flapDuration); | |
if (s == 0) { | |
continue; | |
} | |
push(); | |
scale(sqrt(2)); | |
rotateZ(i * PI/2 + PI/4); | |
// draw flap | |
drawFlap(middleColor, innerColor, innerShadowColor, s); | |
pop(); | |
} | |
pop(); | |
// save frames | |
if (SAVE_FRAMES && frameCount < colors.length * TOTAL_FRAMES) { | |
saveFrame("fr#####.png"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment