sheep-AnimatedLoop
My experience creating this piece was a lot of finnicky changing of entering into the easing functions to get the gif looking just right. Over all, I was pretty proud how it ended up. I used a lot of easing functions for this: Penner Ease Out Bounce, the Iterative Square Root, the Flat Top Window, the Blackman-Harris Window, the Hann Window, the General Sigmoid Logit Combination, the Half Blackman-Nuttall Window, the Penner Ease-In Expo, and the Double Exponential Sigmoid. The bounce was specifically selected because of its similarity to gravitational forces. I think the amount of easing functions I used shows. The initial design wasn’t that much dissimilar from the final version, though I had intended to make the view less 2D and more isometric.
This was the initial plan, including some hastily written functions and a storyboard.
// This is a template for creating a looping animation in Processing/Java. // When you press the 'F' key, this program will export a series of images // into a "frames" directory located in its sketch folder. // These can then be combined into an animated gif. // Known to work with Processing 3.3.6 // Prof. Golan Levin, January 2018 // gif loop - sheep //=================================================== // Global variables. String myNickname = "sheep"; int nFramesInLoop = 120; int nElapsedFrames; boolean bRecording; float linear = 0; //=================================================== void setup() { size (500, 500); bRecording = false; nElapsedFrames = 0; smooth(); } //=================================================== void keyPressed() { if ((key == 'f') || (key == 'F')) { bRecording = true; nElapsedFrames = 0; } } //=================================================== void draw() { // Compute a percentage (0...1) representing where we are in the loop. float percentCompleteFraction = 0; if (bRecording) { percentCompleteFraction = (float) nElapsedFrames / (float)nFramesInLoop; } else { percentCompleteFraction = (float) (frameCount % nFramesInLoop) / (float)nFramesInLoop; } // Render the design, based on that percentage. renderMyDesign (percentCompleteFraction); // If we're recording the output, save the frame to a file. if (bRecording) { saveFrame("frames/" + myNickname + "_frame_" + nf(nElapsedFrames, 4) + ".png"); nElapsedFrames++; if (nElapsedFrames >= nFramesInLoop) { bRecording = false; } } } //=================================================== void renderMyDesign (float percent) { background (234, 90, 56); smooth(); stroke (0, 0, 0); strokeWeight (2); //---------------------- // set up loop for 3 seesaws for (int i = 0; i< 3; i++) { float cx = width/2; float cy = height/2; float gx = (width/2)+30; float gy = (height/2)-100; float rot = (percent/2.5)*(TWO_PI); //set up for different seesaw variable locations if (i==0) { cx = (width/2)-150; cy = (height/2); gx = ((width/2)+30)-150; gy = (height/2)-100; rot = (percent/2.5)*(TWO_PI); } if (i==1) { cx = (width/2); cy = (height/2); gx = ((width/2)+30); gy = (height/2)-100; rot = (percent/2.5)*(TWO_PI); } if (i==2) { cx = (width/2)+150; cy = (height/2); gx = ((width/2)+30)+150; gy = (height/2)-100; rot = (percent/2.5)*(TWO_PI); } //center eveyrthing rectMode(CENTER); ellipseMode(CENTER); //shadow of seesaws pushMatrix(); noStroke(); fill(200, 0, 0, 40); if (percent<.1) { translate(cx, cy+(function_FlatTopWindow (map(percent, 0, 1, 0.5, 5.5))*300)); } else if (percent<.7) { translate(cx, cy); } else { translate(cx+(100*function_IterativeSquareRoot (map(percent, 0.7, 1, 0, 1))), cy+(850*function_HannWindow(map(percent, .7, 1, 0, .5)))); } scale(map(constrain(percent, 0.5, .7), 0.5, 1, 1, 3.8)); float radius = 35; float angle0 = function_PennerEaseInExpo((percent/2.5)*(TWO_PI)); float angle1 = function_PennerEaseInExpo((percent/2.5)*(TWO_PI))+PI; if (percent>.625) { angle0 = 52; angle1 = 55; } if (percent>.71) { scale(percent); } beginShape(); vertex(0-(radius*cos(angle1)), 0-(radius*sin(angle1))); vertex(0-(radius*cos(angle0)), 0-(radius*sin(angle0))); vertex(500-(radius*cos(angle0)), 200-(radius*sin(angle0))); vertex(500-(radius*cos(angle1)), 200-(radius*sin(angle1))); vertex(0-(radius*cos(angle1)), 0-(radius*sin(angle1))); endShape(); popMatrix(); //endshadow //pegshadow pushMatrix(); noStroke(); if (percent<.1) { translate(cx, cy+(function_FlatTopWindow (map(percent, 0, 1, 0.5, 5.5))*300)); } else if (percent<.85) { translate(cx, cy); } else { translate(cx, cy-(function_FlatTopWindow (map(percent, 0.85, 1, 0, .5))*300)); scale(1-(function_FlatTopWindow (map(percent, 0.85, 1, 0, .3)))); } fill(80, 0, 0, 40); beginShape(); vertex(-5, 0); vertex(5, 0); vertex(60, 30); vertex(60, 30); vertex(-5, 0); endShape(); popMatrix(); //endpegshadow //seesaw pushMatrix(); if (percent<.1) { translate(cx, cy+(function_FlatTopWindow (map(percent, 0, 1, 0.5, 5.5))*300)); } else if (percent<.7) { translate(cx, cy); } else { translate(cx+(100*function_IterativeSquareRoot (map(percent, 0.7, 1, 0, 1))), cy+(850*function_HannWindow(map(percent, .7, 1, 0, .5)))); } scale(map(constrain(percent, 0.5, .7), 0.5, 1, 0.5, 2)); if (percent>.71) { scale(percent); } rotate(function_PennerEaseInExpo (rot)); fill(1, 133, 148); arc(0, 0, 150, 150, 0, constrain(map(TWO_PI*(constrain(function_PennerEaseInExpo(percent)*15, 0, .99)), 0, TWO_PI, -1, TWO_PI*2), 0, TWO_PI), PIE); fill(1+(254*percent), 133+(percent*(255-133)), 148+(percent*(255-148)), 255-(percent*100)); rect(0, 0, 150, 20); fill(15, 64, 100); ellipse(0, 0, 20, 20); fill(234, 90, 56); ellipse(0, 0, 10, 10); popMatrix(); //endseesaw //spinnerenter pushMatrix(); if ((percent*2) < (1/2.75f)) { linear = 0; } else { linear+=4; } fill(1, 133, 148); translate(gx+linear, gy+500*function_PennerEaseOutBounce (percent*2)-430); scale(.5); rotate(percent*100); arc(0, 0, 150, 150, 0, TWO_PI, PIE); fill(255, 255, 255, 100); rect(0, 0, 150, 20); fill(15, 64, 100); ellipse(0, 0, 20, 20); fill(234, 90, 56); ellipse(0, 0, 10, 10); popMatrix(); //spinnter //bottompeg fill(255); pushMatrix(); if (percent<.1) { translate(cx, cy+(function_FlatTopWindow (map(percent, 0, 1, 0.5, 5.5))*300)); } else if (percent<.85) { translate(cx, cy); } else { translate(cx, cy-(function_FlatTopWindow (map(percent, 0.85, 1, 0, .5))*300)); scale(1-(function_FlatTopWindow (map(percent, 0.85, 1, 0, .3)))); } if (percent<.71 || percent> .725) { ellipse(0, 0, 10, 10); } popMatrix(); } } //=================================================== // Taken from https://github.com/golanlevin/Pattern_Master float function_DoubleExponentialSigmoid (float x, float a) { // functionName = "Double-Exponential Sigmoid"; float min_param_a = 0.0 + EPSILON; float max_param_a = 1.0 - EPSILON; a = constrain(a, min_param_a, max_param_a); a = 1-a; float y = 0; if (x<=0.5) { y = (pow(2.0*x, 1.0/a))/2.0; } else { y = 1.0 - (pow(2.0*(1.0-x), 1.0/a))/2.0; } return y; } float function_PennerEaseInExpo(float t) { //functionName = "Penner's EaseIn Exponential"; return (t==0) ? 0 : pow(2, 10 * (t - 1)); } float function_HalfBlackmanNuttallWindow (float x) { // http://en.wikipedia.org/wiki/Window_function //functionName = "Blackman–Nuttall Window (Half)"; final float a0 = 0.3635819; final float a1 = 0.4891775; final float a2 = 0.1365995; final float a3 = 0.0106411; x *= 0.5; float pix = PI*x; float y = a0 - a1*cos(2*pix) + a2*cos(4*pix) - a3*cos(6*pix); return y; } float function_GeneralSigmoidLogitCombo (float x, float a, float b) { float y = 0; if (a < 0.5) { // Logit float dy = b - 0.5; y = dy + function_NormalizedLogit (x, 1.0-(2.0*a)); } else { // Sigmoid float dx = b - 0.5; y = function_NormalizedLogisticSigmoid (x+dx, (2.0*(a-0.5))); } //functionName = "General Sigmoid-Logit Combination"; y = constrain(y, 0, 1); return y; } float function_NormalizedLogit (float x, float a) { // http://en.wikipedia.org/wiki/Logit //functionName = "Normalized Logit Function"; float min_param_a = 0.0 + EPSILON; float max_param_a = 1.0 - EPSILON; float emph = 5.0; a = constrain(a, min_param_a, max_param_a); a = (1/(1-a) - 1); a = emph * a; float minx = 1.0 / (1.0 + exp( 0.5*a )); float maxx = 1.0 / (1.0 + exp( -0.5*a )); x = map(x, 0, 1, minx, maxx); float y = log ( x / (1.0 - x)) ; y *= 1.0/a; y += 0.5; y = constrain (y, 0, 1); return y; } float function_NormalizedLogisticSigmoid (float x, float a) { //functionName = "Normalized Logistic Sigmoid"; float min_param_a = 0.0 + EPSILON; float max_param_a = 1.0 - EPSILON; float emph = 5.0; a = constrain(a, min_param_a, max_param_a); a = (1.0/(1.0-a) - 1.0); a = emph * a; float y = 1.0 / (1.0 + exp(0 - (x-0.5)*a )); float miny = 1.0 / (1.0 + exp( 0.5*a )); float maxy = 1.0 / (1.0 + exp( -0.5*a )); y = map(y, miny, maxy, 0, 1); return y; } float function_HannWindow (float x) { // http://en.wikipedia.org/wiki/Window_function //functionName = "Hann (Raised Cosine) Window"; float y = 0.5 * (1.0 - cos(TWO_PI*x)); return y; } float function_BlackmanHarrisWindow (float x) { // http://en.wikipedia.org/wiki/Window_function //functionName = "Blackman–Harris Window"; final float a0 = 0.35875; final float a1 = 0.48829; final float a2 = 0.14128; final float a3 = 0.01168; float pix = PI*x; float y = a0 - a1*cos(2*pix) + a2*cos(4*pix) - a3*cos(6*pix); return y; } float function_FlatTopWindow (float x) { // http://en.wikipedia.org/wiki/Window_function //functionName = "Flat Top Window"; final float a0 = 1.000; final float a1 = 1.930; final float a2 = 1.290; final float a3 = 0.388; final float a4 = 0.032; float pix = PI*x; float y = a0 - a1*cos(2*pix) + a2*cos(4*pix) - a3*cos(6*pix) + a4*cos(8*pix); y /= (a0 + a1 + a2 + a3 + a4); return y; } float function_IterativeSquareRoot (float x) { // http://en.wikipedia.org/wiki/Methods_of_computing_square_roots // Ancient Babylonian technology //functionName = "Iterative (Heron's) Square Root"; float y = 0.5; int n = 6; for (int i=0; i<n; i++) { y = (y + x/y)/2.0; } return y; } float function_PennerEaseOutBounce (float t) { //functionName = "Penner's EaseOut Bounce"; if ((t) < (1/2.75f)) { return (7.5625f* t*t); } else if (t < (2/2.75f)) { float postFix = t-=(1.5f/2.75f); return (7.5625f*(postFix)*t + 0.75f); } else if (t < (2.5/2.75)) { float postFix = t-=(2.25f/2.75f); return (7.5625f*(postFix)*t + 0.9375f); } else { float postFix = t-=(2.625f/2.75f); return (7.5625f*(postFix)*t + 0.984375f); } } |