Jackalope-AnimatedLoop.

My initial idea was to do something that was like hills, how they ebb and flow into one another in beautiful landscapes. That is one of the things I feel I have succeeded in so I’m happy with that. However, there are many problems I still have with this piece. I’ve never used Processing before and I feel like that considerably slowed my making of this. There is a lot that I didn’t realize was possible with Processing until midway into this project and a lot that I didn’t think I’d be able to do, and so I also feel like the gif I made is a bit simplistic. The easing function I used was a Double-Circular Ogee, which I chose because I liked how it caused the sun and moon to slow as they neared the the end of their cycles. I also liked the lazier, more serene drift of the moon across the sky compared to the sun.

//Special thanks to Golan Levin for providing code to generate frames and this blog
//about using noise https://necessarydisorder.wordpress.com/2017/11/15/drawing-from-noise-and-then-making-animated-loopy-gifs-from-there/
 
// 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
 
//===================================================
// Global variables. 
String  myNickname = "Jackalope"; 
int     nFramesInLoop = 120;
int     nElapsedFrames;
boolean bRecording; 
 
//===================================================
void setup() {
  size (500, 900); 
  bRecording = false;
  nElapsedFrames = 0;
}
int start = 300; 
float function_PennerEaseInOutCubic (float x) {
  String functionName = "Penner's EaseInOut Cubic";
 
  x *= 2.0; 
  float y = 0; 
 
  if (x < 1) {
    y = 0.5 * x*x*x;
  } else {
    x -= 2.0;
    y = 0.5 * (x*x*x + 2.0);
  }
  return y;
}
 
//===================================================
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) {
  //
  // YOUR ART GOES HERE.
  // 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. 
  // This example uses two different graphical techniques. 
  // Use or delete whatever you prefer from this example. 
  // Remember to SKETCH FIRST!
 
  //----------------------
  // here, I set the background and some other graphical properties
  background (138, 165, 30);
  smooth(); 
  stroke(255);
  strokeWeight (2);
  float damping; 
  float t = map(percent, 0, 1, 0, TWO_PI);
  float px = width/2.0 + .05* cos(t); 
  float py = width/2.0 + .05* sin(t);
 
  loadPixels();
  for (int x = 0; x<width; x+=1) {
    for (int y = 0; y<height; y+=1) {
      float ns = (float)noise(.01*x, .01*y, px*100);
      float col = map(ns, -1, 1, 0, 100);
      pixels[x + width*y] = color(255, col);
    }
  }
  updatePixels();
  //moon and sun
  float easeP = function_DoubleCircularOgee(.07, percent);
  if (percent<.5)
    background((1-percent)*255);
  else
    background(percent*255);
  fill(222, 95, 20);
  float cx = width*.5;
  float cy = height*.25;
  float sx = cx+250*cos(2.5*TWO_PI*easeP);
  float sy = cy-150*sin(2.5*TWO_PI*easeP);
  ellipse(sx, sy, 50, 50);
 
  fill(255);
  float mx = cx-250*cos(2.5*TWO_PI*easeP);
  float my = cy+150*sin(2.5*TWO_PI*easeP);
  ellipse(mx, my, 50, 50);
 
 
 
  //some sin waves
 
  int lines=10;
  fill(184, 176, 90);
  pushMatrix();
  shearY(PI/10.0);
  for (int line = start; line<height*.5; line+=(height/lines)) {
    beginShape();
    vertex(0, height);
    for (int i = 0; i<width; i++) {
      float x = i;
      float y = line/2-height/5+160*noise(px+line+line*.003, py+line+i*.002)+15*sin(i*.01*PI);
      if (x>=0 &&x<=width) 
      vertex(x, y);
    }
    vertex(width, height*2);
    endShape(CLOSE);
  }
  popMatrix();
  loadPixels();
  for (int x = 0; x<width; x+=2) {
    for (int y = 0; y<height; y+=2) {
      float ns = (float)noise(.01*x, .01*y, px*100);
      float col = map(ns, -1, 1, 0, 255);
      pixels[x + width*y] = color(col);
    }
  }
  updatePixels();
  px = width/2.0 + .09 * cos(t); 
  py = width/2.0 + .09 * sin(t);
  pushMatrix();
  shearY(-PI/15.0);
  for (int line = 0; line<height*.7; line+=(height/lines)) {
    beginShape();
    vertex(0, height);
    for (int i = 0; i<width; i++) {
      float x = i;
      float y = line*.4+height*.2+160*noise(px+1.5*line+line*.003, py+line+i*.004)+15*sin(px*20+i*.009*PI);
      if (x>=0 &&x<=width) 
      vertex(x, y);
    }
    vertex(width, height*2);
    endShape(CLOSE);
  }
  popMatrix();
 
  fill(110, 137, 14);
  loadPixels();
  for (int x = 0; x<width; x+=3) {
    for (int y = 0; y<height; y+=3) {
      float ns = (float)noise(.01*x, .01*y, px*100);
      float col = map(ns, -1, 1, 0, 255);
      pixels[x + width*y] = color(col);
    }
  }
  updatePixels();
  px = width/2.0 + .1 * cos(t); 
  py = width/2.0 + .1 * sin(t);
  pushMatrix();
  shearY(PI/20.0);
  for (int line = 0; line<height; line+=(height/2)) {
    beginShape();
    vertex(0, height);
    for (int i = 0; i<width; i++) {
      float x = i;
      float y = line*.09+height*.3+300*noise(px+5.5*line+line*.0001, py+ line+i*.001)+10*sin(px*10+i*.001*PI);
      if (x>=0 &&x<=width) 
      vertex(x, y);
    }
    vertex(width, height*2);
    endShape(CLOSE);
  }
  popMatrix();
 
  loadPixels();
  for (int x = 0; x<width; x+=3) {
    for (int y = 0; y<height; y+=3) {
      float ns = (float)noise(.01*x, .01*y, px*100);
      float col = map(ns, -1, 1, 0, 255);
      pixels[x + width*y] = color(255, col);
    }
  }
  updatePixels();
 
 
  fill(96, 129, 71);
  px = width/2.0 + .2 * cos(t); 
  py = width/2.0 + .1 * sin(t);
  pushMatrix();
  shearY(-PI/5.0);
  for (int line = 0; line<height; line+=(height/8)) {
    beginShape();
    vertex(0, height);
    for (int i = 0; i<width; i++) {
      damping  = .0001*sq(i);
      float x = i;
      float y = line+height*.7+300*noise(px+5.5*line+line*.003, py+line+i*.003)+((30-damping)/100)*(15+1.1*damping)*sin(px*40+i*.01*PI);
      if (x>=0 &&x<=width) 
      vertex(x, y);
    }
    vertex(width, height*2);
    endShape(CLOSE);
  }
  popMatrix();
}
 
 
 
//===================================================
// Taken from https://github.com/golanlevin/Pattern_Master
 
float function_DoubleCircularOgee (float x, float a){
 // functionName = "Double-Circular Ogee";
 
  float min_param_a = 0.0;
  float max_param_a = 1.0;
 
  a = constrain(a, min_param_a, max_param_a); 
  float y = 0;
  if (x<=a){
    y = sqrt(sq(a) - sq(x-a));
  } 
  else {
    y = 1 - sqrt(sq(1-a) - sq(x-a));
  }
  return y;
}