shuann-AnimatedLoop

Overall I am pretty satisfied with how this turned out, and it looks very close to what I had on my sketch. For the background switch I decided to use the EaseInOutElastic function after trying out a hand full of different easing functions because this one looks almost exactly like what I imagined the transition to be when I made my sketch. I believe the bounciness really suites the style of the visuals that I created here and defiantly adds to the overall quality of the piece. The hardest part about this assignment was defiantly the fact that I had to think and calculate everything based on the percentage frame rate. It was not so easy to wrap my head around that and when I ran into a glitch I have to really sit down and think about where the problem is. Did I lose count on the frame somewhere or was it something else. For example this one:

I also wish that I can spend more time to make the movement of the boat more natural. Maybe with some tilt at appropriate time.s

My Sketch:

The Code:

// INSTRUCTIONS FOR EXPORTING FRAMES (from which to make a GIF): 
// 1. Run a local server, using instructions from here:
//    https://github.com/processing/p5.js/wiki/Local-server
// 2. Set the bEnableExport variable to true.
// 3. Set the myNickname variable to your name.
// 4. Run the program from Chrome, press 'f'. 
//    Look in your 'Downloads' folder for the generated frames.
// 5. Note: Retina screens may export frames at twice the resolution.
 
 
//===================================================
// User-modifiable global variables. 
var myNickname = "shuann";
var nFramesInLoop = 120;
var bEnableExport = true;
var myScale = 0.006;
var myLoopingNoiseArray0 = [];
var myLoopingNoiseArray1 = [];
var myLoopingNoiseArray2 = [];
var myLoopingNoiseArray3 = [];
var myLoopingNoiseArray4 = [];
var myLoopingNoiseArray5 = [];
var myLoopingNoiseArray6 = [];
var myLoopingNoiseArray7 = [];
var myLoopingNoiseArray8 = [];
var myLoopingNoiseArray9 = [];
 
// Other global variables you don't need to touch.
var nElapsedFrames;
var bRecording;
var theCanvas;
 
//===================================================
function setup() {
  theCanvas = createCanvas(640, 640);
  bRecording = false;
  nElapsedFrames = 0;
 
  var radius = 60;
  noiseSeed(40);
  //waves at the bottom
  //sorry the style is horrible
  for (var i = 0; i < nFramesInLoop; i++){
    var rotatingArmAngle = map(i, 0 ,120, 0, TWO_PI);
    var px = width/2 + radius * cos(rotatingArmAngle);
    var py = height/2 + radius * sin(rotatingArmAngle);
    var noiseAtLoc0 = height - 50 * noise(myScale * px, myScale * py);
    var noiseAtLoc1 = height - 80 * noise(myScale * px, myScale * py);
    var noiseAtLoc2 = height - 150 * noise(myScale * px, myScale * py);
    var noiseAtLoc3 = height - 200 * noise(myScale * px, myScale * py);
    var noiseAtLoc4 = height - 300 * noise(myScale * px, myScale * py);
    myLoopingNoiseArray0[i] = round(noiseAtLoc0);
    myLoopingNoiseArray1[i] = round(noiseAtLoc1);
    myLoopingNoiseArray2[i] = round(noiseAtLoc2);
    myLoopingNoiseArray3[i] = round(noiseAtLoc3);
    myLoopingNoiseArray4[i] = round(noiseAtLoc4);
 
    var px1 = width/2 + radius * 0.7 * cos(rotatingArmAngle);
    var py1 = height/2 + radius * 0.7 * sin(rotatingArmAngle);
    var noiseAtLoc5 = height - 60 * noise(myScale * px1, myScale * py1);
    var noiseAtLoc6 = height - 130 * noise(myScale * px1, myScale * py1);
    var noiseAtLoc7 = height - 230 * noise(myScale * px1, myScale * py1);
    var noiseAtLoc8 = height - 330 * noise(myScale * px1, myScale * py1);
    var noiseAtLoc9 = height - 450 * noise(myScale * px1, myScale * py1);
    myLoopingNoiseArray5[i] = round(noiseAtLoc5);
    myLoopingNoiseArray6[i] = round(noiseAtLoc6);
    myLoopingNoiseArray7[i] = round(noiseAtLoc7);
    myLoopingNoiseArray8[i] = round(noiseAtLoc8);
    myLoopingNoiseArray9[i] = round(noiseAtLoc9);
  }
 
}
 
 
//===================================================
function keyTyped() {
  if (bEnableExport) {
    if ((key === 'f') || (key === 'F')) {
      bRecording = true;
      nElapsedFrames = 0;
    }
  }
}
 
//===================================================
function draw() {
 
  // Compute a percentage (0...1) representing where we are in the loop.
  var percentCompleteFraction = 0;
  if (bRecording) {
    percentCompleteFraction = float(nElapsedFrames) / float(nFramesInLoop);
  } else {
    percentCompleteFraction = float(frameCount % nFramesInLoop) / float(nFramesInLoop);
  }
 
  // Render the design, based on that percentage. 
  // This function renderMyDesign() is the one for you to change. 
  renderMyDesign (percentCompleteFraction);
 
  // If we're recording the output, save the frame to a file. 
  // Note that the output images may be 2x large if you have a Retina mac. 
  // You can compile these frames into an animated GIF using a tool like: 
  if (bRecording && bEnableExport) {
    var frameOutputFilename = myNickname + "_frame_" + nf(nElapsedFrames, 4) + ".png";
    print("Saving output image: " + frameOutputFilename);
    saveCanvas(theCanvas, frameOutputFilename, 'png');
    nElapsedFrames++;
 
    if (nElapsedFrames >= nFramesInLoop) {
      bRecording = false;
    }
  }
}
 
//===================================================
function renderMyDesign (percent) {
  //
  // THIS IS WHERE YOUR ART GOES. 
  // This is an example of a function that renders a temporally looping design. 
  // It takes a "percent", between 0 and 1, indicating where we are in the loop. 
  // Use, modify, or delete whatever you prefer from this example. 
  // This example uses several different graphical techniques. 
  // Remember to SKETCH FIRST!
 
  //----------------------
  smooth();
  var cx = 100;
  var cy = 100;
 
  push();
  translate(width/2, height/2);
 
  if (percent < 0.5){
    var ang = function_PennerEaseInOutElastic(percent*2)*180;
    rotate(radians(ang));
    drawbackdrop(percent);
  } else {
    var ang = function_PennerEaseInOutElastic(map(percent, 0.5, 1, 0, 1))*180;
    rotate(radians(ang) + TWO_PI/2);
    drawbackdrop(percent);
  }
 
  drawbackdrop(percent);
  pop();
 
 
  noStroke();
  push();
  translate(0, -100);
  drawboat(percent,-0.1);
  drawWave(percent, 88, 176, 230, myLoopingNoiseArray9, -0.1);
  drawWave(percent, 80, 160, 210, myLoopingNoiseArray8, 0);
  drawWave(percent, 22, 132, 200, myLoopingNoiseArray7, 0.6);
  drawWave(percent, 49, 80, 152, myLoopingNoiseArray6, 0.8);
  drawWave(percent, 54, 100, 160, myLoopingNoiseArray5, 0.8);
  pop();
  drawWave(percent, 45, 183, 212, myLoopingNoiseArray4, -0.2);
  drawWave(percent, 45, 167, 212, myLoopingNoiseArray3, -0.2);
  drawWave(percent, 65, 130, 190, myLoopingNoiseArray2, -0.2);
  drawWave(percent, 54, 100, 160, myLoopingNoiseArray1, 0.5);
  drawWave(percent, 49, 80, 152, myLoopingNoiseArray0, 0.5);
}
 
function drawbackdrop(p){
  fill(235,219,190);
  rect(-width, -height, width*3, height);
  fill(18,24,56);
  rect(-width, 0, width*3, height);
 
  //draw sun
  push();
  strokeWeight(3);
  stroke(235,172, 45);
  fill(235,172, 45);
  ellipse(200, -200, 50, 50);
  translate(200, -200);
  rotate(0.2 * p * TWO_PI);
  push();
  for (var a = 0; a < 10; a++){
    rotate(TWO_PI/10);
    line (50, 0, 70, 0);
  }
  pop();
  pop();
 
  //draw moon
  push();
  noStroke();
  fill(255);
  translate(-200, 200);
  ellipse(0, 0, 80, 80);
  fill(18,24,56);
  ellipse(-20, -10, 50, 50);
  pop();
 
  //draw star
  push();
  translate(-100, 110);
  noStroke();
  fill(255);
  star(0, 0, 5, 10, 5); 
  star(50, 110, 5, 10, 5); 
  star(80, 90, 3, 6, 5); 
  star(100, 105, 3, 6, 5); 
  pop();
 
}
 
function drawboat(p, tilt){
  currStep = round(p * nFramesInLoop);
  var nx = map(40, 0, nFramesInLoop, 0, width);
  var ny = myLoopingNoiseArray9[(currStep+40)%nFramesInLoop]
  // var newy = doubleExponentialSigmoid(map(y, 315, 370, 0, 1), 0.7);//does not look good
  // var y = map(newy, 0, 1, 315, 370);
  fill(255);
  beginShape();
  vertex(nx - 50, ny - 10);
  vertex(nx + 50, ny - 10);
  vertex(nx + 30, ny + 20);
  vertex(nx - 30, ny + 20);
  endShape();
  stroke(255);
  line(nx, ny, nx, ny - 50);
  triangle(nx, ny - 50, nx + 20, ny - 40, nx, ny - 30);
}
 
function drawWave(p, r, g, b, wave, tilt){
  currStep = round(p * nFramesInLoop);
  fill(r, g, b);
  noStroke();
  beginShape();
  vertex(0,height+50);
  for (var i = 0; i < wave.length; i++) {
    var nx = map(i, 0, nFramesInLoop - 1, 0, width);
    if (i + currStep >= nFramesInLoop) {
      var ny = wave[(i + currStep)-nFramesInLoop] - i*tilt;
    } else {
      var ny = wave[i + currStep] - i*tilt;
    }
    vertex(nx, ny);
  }
  vertex(width,height+50);
  endShape();
}
 
 
// symmetric double-element sigmoid function (a is slope)
// See https://github.com/IDMNYU/p5.js-func/blob/master/lib/p5.func.js
// From: https://idmnyu.github.io/p5.js-func/
//===================================================
function doubleExponentialSigmoid (_x, _a){
  if(!_a) _a = 0.75; // default
 
  var min_param_a = 0.0 + Number.EPSILON;
  var max_param_a = 1.0 - Number.EPSILON;
  _a = constrain(_a, min_param_a, max_param_a);
  _a = 1-_a;
 
  var _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);
}
 
//From: https://github.com/golanlevin/Pattern_Master/blob/master/pattern_master/F00.pde
//------------------------------------------------------------------
function function_PennerEaseInOutElastic (t) {
 
  if (t===0) return 0; 
  if ((t/=0.5)==2) return 1;
  var p=(.3 * 1.5);
  var a=1;
  var s=p/4;
 
  if (t < 1) {
    var postFix = pow(2, 10*(t-=1)); // postIncrement is evil
    return -0.5 * (postFix* sin( (t-s)*(2*PI)/p ));
  } 
 
  var postFix = pow(2, -10*(t-=1)); // postIncrement is evil
  return postFix * sin( (t-s)*(2*PI)/p )*.5 + 1;
}
 
//From: https://p5js.org/examples/form-star.html
//------------------------------------------------------------------
function star(x, y, radius1, radius2, npoints) {
  var angle = TWO_PI / npoints;
  var halfAngle = angle/2.0;
  beginShape();
  for (var a = 0; a < TWO_PI; a += angle) {
    var sx = x + cos(a) * radius2;
    var sy = y + sin(a) * radius2;
    vertex(sx, sy);
    sx = x + cos(a+halfAngle) * radius1;
    sy = y + sin(a+halfAngle) * radius1;
    vertex(sx, sy);
  }
  endShape(CLOSE);
}

.