Category: Assignment-04-GIF

AirBoss Gif

plane3

So this assignment was really a challenge for me because, well, I’ve only ever made animated cursors through a simple program before. I still feel very limited in the amount of things I know how to do, but learning the applications of code one step at a time is really thrilling.

The plane shape I drew was really simple in the fact that I just used beginShape() and endShape, along with a few arcs. I wanted to take it one step further and add shading or curvature, but I have yet to fully comprehend the PShader tutorial on Processing.org. I learned most of the stuff in this code from Lynda.com and Golan.

I do realize that this is not a perfect loop… I’m working on it…

Here’s a sketch of my original idea:

img003

 

Code:

PFont myFont;

int X_AXIS=1;
int Y_AXIS=2;
float theta=0;
color[] sky={#155484,#79a4c5};

void setup() {
  size(600,300);
  myFont=loadFont("Optima-BoldItalic-22.vlw");
  textFont(myFont);
  frameRate(30);
}

void draw(){
  //if (frameCount< =60) {
  //  saveFrame("plane-####.png");
  //}
  setGradient(0,0,width,height,sky[0],sky[1],Y_AXIS); //sky backdrop
  float a=map(sin(theta),-1,1,-12,12);
  theta+=0.06;

  pushMatrix();
  scale(1.8);
  translate(0,-80);
  rotate(radians(3));
  drawClouds(250,40,0.006,0.60);
  popMatrix();

  pushMatrix();
  rotate(radians(7));
  translate(a,1.5*a);
  drawPlane();
  popMatrix();

  pushMatrix();
  translate(0,50);
  scale(2.0);
  rotate(radians(8));
  drawClouds(250,80,0.03,0.3);
  popMatrix();
}

//Learned from processing.org examples
void setGradient(int x, int y, float w, float h, color c1, color c2, int axis) {
  noFill();
  if(axis==X_AXIS){
    for(int i=x;i< =x+w;i++){
      float inter=map(i,x,x+w,0,1);
      color c=lerpColor(c1,c2,inter); //inter is between 0.0 and 1.0
      stroke(c);
      line(i,y,i,y+h);
    }
  }
  else if(axis==Y_AXIS){
    for(int i=y;i<=y+h;i++){
      float inter=map(i,y,y+h,0,1);
      color c=lerpColor(c1,c2,inter);
      stroke(c);
      line(x,i,x+w,i);
    }
  }
}

//Learned from Golan!! Thanks!!
void drawClouds(int col,int tra,float speed,float thresh){ //color, transparency, speed, threshold
  fill(col,tra);
  noStroke();

  for(int i=0;i
					
					
			

Winter Scene GIF

output

sketch

I started off by playing with the noise function, and when I was observing it, I thought the way it moved looked like a row of soldiers marching. Thus I decided to create infinite rows of noise soldiers walking towards the viewer. However, after I coded it up, I realized that what I had looked more like a waterfall, so I just created a scene by going from there instead.

During the entire process of creating this gif, I spend a lot of time worrying how I could get it to loop. Not taking risks made me feel like I did not learn as much as I could from this project. Also, I did not realize that there was a template uploaded, which could have saved me a lot of time. Thirdly, I found out that I enjoy watching my animation more when I am listening to some relaxing music, rather than being in silence, so I feel it might have been better if instead it is a music video with more contents. The combined effects made me feel serene, which I am glad for.

Thanks to Daniel Shiffman for his code of the particle system.

int square_width = 15;
color skycolor = color(40,40,40);
boolean sunrise = true;
int snowcolor = 140;
int stay = 0;
int frames = 0;

ParticleSystem ps1, ps2;

class Particle {
  PVector location;
  PVector velocity;
  PVector acceleration;
  float lifespan;

  Particle(PVector l) {
    acceleration = new PVector(0,0.1);
    velocity = new PVector(random(-10,10),1.0);
    location = l.get();
    lifespan = 255.0;
  }

  void run() {
    update();
    display();
  }

  // Method to update location
  void update() {
    velocity.add(acceleration);
    location.add(velocity);
    lifespan -= 1.0;
  }

  // Method to display
  void display() {
    stroke(255,200 - blue(skycolor) + 2);
    fill(255, 200 - blue(skycolor) + 2);
    ellipse(location.x,location.y,8,8);
  }
  
  // Is the particle still useful?
  boolean isDead() {
    if (lifespan < 0.0) {
      return true;
    } else {
      return false;
    }
  }
}




// A class to describe a group of Particles
// An ArrayList is used to manage the list of Particles 

class ParticleSystem {
  ArrayList particles;
  PVector origin;

  ParticleSystem(PVector location) {
    origin = location.get();
    particles = new ArrayList();
  }

  void addParticle() {
    particles.add(new Particle(origin));
  }

  void run() {
    for (int i = particles.size()-1; i >= 0; i--) {
      Particle p = particles.get(i);
      p.run();
      if (p.isDead()) {
        particles.remove(i);
      }
    }
  }
}


class Row_of_Noise{
  int start, end;
  float row;

  Row_of_Noise(int s, int e, float r)
  {
    start = s;
    end = e;
    row = r;
  }

  void walk_forward()
  {
    row *= 1.03;
    if(row > height)
      row = 0.1;

    start = width/2-(int)row*3;
    end = width/2+(int)row*3; 
  }
}

Row_of_Noise[] RoN;

void setup() {
  size(600, 600);
  stroke(0);
  smooth();
  noiseSeed(15251);
  initialize_rows_of_noise();
  ps1 = new ParticleSystem(new PVector(width/3,-50));
  ps2 = new ParticleSystem(new PVector(2*width/3,-50));
}

void initialize_rows_of_noise(){
  RoN = new Row_of_Noise[40];
  float r = 0.1;
  for(int i = 0; i < RoN.length; i++)
  {
    r *= 1.3;
    RoN[i] = new Row_of_Noise(width/2-(int)r*2, 
                              width/2+(int)r*2, 
                              r);
  }
}

void draw_row_of_noise(Row_of_Noise ron)
{
  beginShape();
  float theta = 0.0;
  for(int x = ron.start; x < ron.end; x++)
  {
    
    if(x % 50 == 0)
    {
      endShape();
      beginShape();
    }
    
    if(x % 50 < 10)
      continue;

    float y = noise((millis()/1000.0 + x)%50.0) * 
                   ((float)(ron.end - ron.start)/ 6.0) + ron.row;
    vertex(x-5,y+300);
  }
  endShape();
}

void draw() {

  if(53 < frames && frames < 285)
    saveFrame("outputgif/myname-loop-" + nf(frames,4) + ".png");
  if(blue(skycolor) > 200)
  {
    sunrise = false;
    if(stay == 0)
      stay = 50;
    else
      stay--;
  }

  if(blue(skycolor) < 10)
  {  
    sunrise = true;
    if(stay == 0)
      stay = 50;
    else
      stay--;
  }

  if(stay == 0)
  {
    if(sunrise)
    {
      skycolor = color(red(skycolor)+1, green(skycolor)+2, blue(skycolor)+3);
      snowcolor++;
    }
    else
    {
      skycolor = color(red(skycolor)-1, green(skycolor)-2, blue(skycolor)-3);
      snowcolor--;
    }
  }

  background(skycolor);
  stroke(snowcolor);

  for(int i = 0; i < width; i++)
    line(i,noise(i/400.0)*100.0 + 245,i,height);

  // draws the waterfall
  stroke(0,100,200);
  noFill();
  for(int i = 0; i < RoN.length; i++)
  {
    draw_row_of_noise(RoN[i]);
    RoN[i].walk_forward();
  }

  ps1.addParticle();
  ps1.run();
  ps2.addParticle();
  ps2.run();

  frames++;
}

Assignment-04-GIF

eugh-cropped

Originally, I wanted a gif to pair with the Hotline Miami soundtrack (ridiculously good). I had the idea of a rotating 3D diamond with glow lines emanating from it, but realized I didn’t have much experience with .obj import and Processing, and felt it best to stick with the provided primitives.

sketches from my notebook sketches from my notebook

One of my main goals was to make the emitting glow lines really nice. To get a smooth motion curve, I hopped over to Grapher and plotted out various graphs until I found one I thought would allow for good timing and spacing.

grapher plot grapher plot

I went with 60 FPS in the original processing file, which is hella decadent for animation. I thought I did a nice job with the lines, but my cube rotation was sort of flat. The background noise really brings the piece together. Next time I’d like to pay more attention to creating coherent timelines for all the separate animated components of the animation as a whole–dealing with different start/end times was a mess! Below is my code:

float a;
float b;
float c;
float d;
float e;
float f;
int i;
float multi;
int count;
float h = width / 60;
int counter = 1;

void setup() {
  size(500, 500, P3D);
  //smooth();
  fill(0, 0, 0);
  stroke(255);
}

void draw() {
  
  //setup
  background(0);
  for (int i = 0; i < height/4; i++) {
    for (int j = 0; j < width/4; j++) {
      if (random(255) > 128) {
        stroke(100);
        point(i*4, j*4);
      }
    }
  }

  counter += 1;

  stroke(255);
  //math
  float frame = frameCount % 60;
  a = (5 * sin((frame/15.0)+4.75)+5);
  b = (5 * sin(((frame+15)/15.0)+4.75)+5);
  c = (5 * sin((45/15.0)+4.75)+5);
  d = (5 * sin((frame/15.0)+12.75)+5);
  e = (5 * sin(((frame+15)/15.0)+8.75)+5);
  f = (5 * sin((45/15.0)+12.75)+5);
  if (frameCount % 90 == 0) {
    count += 1;
  }


  //drawing

  pushMatrix();
  translate(width*.5, height*.55);
  pushMatrix();
  rotateZ(.25*PI);
  if (count % 2 == 0) {
    rotateY( map(frameCount%360, 0, 360, 0, TWO_PI));
  } 
  else {
    rotateX( map(frameCount%360, 0, 360, 0, -TWO_PI));
  }
  strokeWeight(3);
  fill(0, 0, 0);
  box(150);
  popMatrix();
  for (int i = 0; i < 17; i++) {
    multi = (i % 2) + 1;
    pushMatrix();
    rotate(PI*.75);
    rotate(PI*i/16);
    translate(110, 110);
    strokeWeight(1);
    if (frame % 60 < 40) {
      line(a * multi, a * multi, 0, b * multi, b * multi, 0);
    }
    popMatrix();
  }
  popMatrix();
  
    //framez
  
 // if (frameCount < 360) {
//    saveFrame("output/filename-####.png");
 // }
}

Ticha-Ad Astra

So, after talking to Golan (thanks!), I decided to do have another go at the GIF assignment. I admit that animating in Processing was intially a difficult concept for me to grasp because I was so accustomed to highly graphical animation software – more specifically, After Effects – in which ‘clicking and dragging’ is a sufficient tactic for creating movement (well there is some programming involved, but only to a minimal extent). This second attempt at the animation was both a large step outside of my comfort zone as well as a significant mental leap for me – and I found it to be a very rewarding and enjoyable experience.

I stumbled across some code on OpenProcessing.org that beautifully simulated moving particles and used a bit of it to make these animations. The code was heavily tweaked though – the original used vectors in 3-dimensional space and did some weird isometric formula conversions (???) that I barely understood. So, I simplified it so that it could both be easier for me to understand and behave the way I wanted it to.

There is a quote by Virgil that goes, ‘sic itur ad astra’, which translates to ‘thus you shall go to the stars’. I hope these simple, yet strangely soothing GIFs will make people feel a little closer to them. (:

Here is the source code for the third animation, as it is my favorite one. The code for the other two are very similar, except that they use different trigonometric functions.

/* Credit to http://www.openprocessing.org/sketch/5582 for amazing particle reference 
 Credit to Golan Levin for the recording option
 */

int nFramesInLoop = 160;
int nElapsedFrames;
int particleCount = 1000;
Particle[] particles = new Particle[particleCount];
boolean bRecording;

void setup() 
{
  noStroke();
  size(500, 500);

  //initializing particles
  for (int i = 0; i < particleCount; i++) { 
    particles[i] = new Particle();
  }
}

void keyPressed() {
  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("output/myname-loop-" + nf(nElapsedFrames, 4) + ".png");
    nElapsedFrames++; 
    if (nElapsedFrames >= nFramesInLoop) {
      bRecording = false;
    }
  }
}

void renderMyDesign(float percent)
{
  translate(width/2, height/2); 

  background(10);

  /* Loop through the particles */
  for (int i = 0; i < particleCount; i++) { 
    Particle particle = (Particle) particles[i];
    particle.display();
  }
}

class Particle {
  float rad;
  float angle;
  float dec;

  int size;

  //particle constructor
  Particle() {
    angle = random(-10, 10);
    rad = random(0, 200);
    dec = (150 - rad) * 0.00004; 

    size = (int) random(2, 4);
  }

  void display () {
    
    fill((int)random(80, 200)); //twinkling
    ellipse(rad * sin(angle), 180 * cos(angle), size, size);
    
    fill((int)random(150, 255)); //twinkling
    ellipse(rad * cos(angle), 180 * sin(angle), size, size);

    angle += dec; //direction of particle movement
  }
}

I also made this thing for giggles:

Ticha-Bots and Bitrain

Sketch:

initial sketch

Result:

 

Coding this was awkward. I never imagined that drawing without a stylus and mouse would be so tedious and time-consuming. Thankfully, I was able to make most of the measurements + find the pixel locations using my Photoshop file and the surprisingly handy Ruler Tool. As far as the animation goes, in general I am more satisfied with the final composition than the initial draft. The use of binary bits instead of conventional rain makes for a more interesting composition, and the ‘bit splashes’ in the foreground are a nice touch. Additionally, the too-small umbrella gives the animation a more comical feel. The gif itself is actually a little slower than the real one; I tried removing some frames manually but decided not to mess with it too much to avoid losing the flow of the animation (Here’s what it’s supposed to look like). I think I somewhat succeeded in giving the animation some dimensionality despite how I was limited to working with more simple tools, as opposed to working with different special effects that are available in typical animation programs. I attempted to make the robot look round by adding some simple shading and tried to give depth to the bitrain by making its color range from medium gray to white.

Still, I wish that *more* animation could be involved and that the piece could be more compositionally interesting. I added a ‘lightning’ feature briefly but decided to remove it when I felt like I was ready to have a seizure. Perhaps what it needs the most is a more interesting environment, as gray is not very exciting to look at. However, I would also have to learn how to strike a balance between minimalism and detail – as the best animations / works of art are able to use both to strengthen the overall piece.

Here is the code which is probably longer than it should be (I have no idea why WordPress add more lines at the bottom):

/* Thanks Golan for the reference code! 
 Credit to LearningProcessing for oscillation reference.
 
 */

int     nFramesInLoop = 160;
int     nElapsedFrames;
boolean bRecording; 
float theta = 0; 
float theta2 = 0;
float x;


//===================================================
void setup() {
  size(500, 500);

  bRecording = false;
  nElapsedFrames = 0;

  x = width/2-100;
}
//===================================================
void keyPressed() {
  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("output/myname-loop-" + nf(nElapsedFrames, 4) + ".png");
    nElapsedFrames++; 
    if (nElapsedFrames >= nFramesInLoop) {
      bRecording = false;
    }
  }
} 

void renderMyDesign(float percent) {
  background(#464646);
  smooth();
  noStroke(); 
  strokeWeight(2);
  //----------------------
  // Here, I assign some handy variables. 
  float cx = 100;
  float cy = 100;

  robotLegs();
  robotBody();
  robotHead();
  robotArms(); 

  textSize(40);
  fill(255);
  text("Error 404;;", x, 100);

  textSize((int)random(10, 22));
  pushMatrix();
  translate(-75, 30);
  rotate(radians(-8));

  //rain effect
  for (int i = 0; i < 80; i++) {
    int w = int(random(width+40));
    int h = int(random(height+40));
    fill(random(70, 255));

    int rand = (int)random(2);
    String bit = (rand == 0)? "0" : "1";

    text(bit, w, h);
  }

  popMatrix();

  pushMatrix();
  translate(-60, 480); 

  textSize(18);
  //rain effect
  for (int i = 0; i < 100; i++) {
    int w = int(random(width+40));
    int h = int(random(height/8));
    fill(random(70, 255));

    int rand = (int)random(2);
    String bit = (rand == 0)? "0" : "1";

    text(bit, w, h);
  }
  popMatrix();
  /*
  // If we're recording, I include some visual feedback. 
   if (bRecording) {
   fill (255, 0, 0);
   textAlign (CENTER); 
   String percentDisplayString = nf(percent, 1, 3);
   text (percentDisplayString, cx, cy-15);
   }*/
} 

void robotHead() {
  float a = map(sin(theta2), -1, 1, -5, 5); //shaking
  theta2 += 0.6; //lower values slow down the movement

  //head 
  fill(#e24b00);
  ellipse(257 + a, 250, 124, 84);
  fill(#f25d13);
  ellipse(257 + a, 243, 115, 70);
  fill(#f67535);
  ellipse(257 + a, 235, 110, 50);
  //shiny
  pushMatrix();
  translate(270 + a, 220);
  rotate(radians(8));
  fill(#ffb793);
  ellipse(0, 0, 30, 10);
  popMatrix();

  //left eyebrow
  stroke(#1c1c1c); 
  strokeWeight(2.6);
  line(223 + a, 245, 223, 238);
  noStroke();
  fill(#1c1c1c);
  pushMatrix();
  translate(213 + a, 236);
  rotate(radians(-10));
  rect(0, 0, 20, 6);
  popMatrix();

  //left eye
  fill(#1c1c1c);
  ellipse(224 + a, 255, 20, 20);
  fill(#ebcb88);
  stroke(50); 
  strokeWeight(2);
  ellipse(224 + a, 255, 13, 13);
  noStroke();

  //right eyebrow
  stroke(#1c1c1c); 
  strokeWeight(2.6);
  line(283 + a, 235, 283, 248);
  noStroke();
  fill(#1c1c1c);
  pushMatrix();
  translate(273 + a, 230);
  rotate(radians(10));
  rect(0, 0, 25, 6);
  popMatrix();

  //right eye
  fill(#1c1c1c);
  ellipse(284 + a, 255, 25, 25);
  fill(#ebcb88);
  stroke(50); 
  strokeWeight(2.6);
  ellipse(284 + a, 255, 16, 16);
  noStroke();

  //left ear
  fill(#1c1c1c);
  rect(186 + a, 230, 15, 44, 7);
  ellipse(185 + a, 252, 10, 30);
  stroke(#1c1c1c); 
  strokeWeight(3);
  line(181 + a, 253, 165 + a, 253);
  line(165 + a, 253, 160 + a, 247);
  line(160 + a, 247, 155 + a, 253);
  line(155 + a, 253, 152 + a, 253);

  //right ear
  rect(310 + a, 230, 15, 44, 7);
  ellipse(326 + a, 252, 10, 30);
  stroke(#1c1c1c); 
  strokeWeight(3);
  line(323 + a, 253, 343 + a, 253);
  line(343 + a, 253, 348 + a, 247);
  line(348 + a, 247, 353 + a, 253);
  line(353 + a, 253, 360 + a, 247);

  //line across middle
  noFill();
  stroke(#9e3b0a); 
  strokeWeight(1.5);
  line(257 + a, 209, 257 + a, 291);

  //bolts
  int b=0;
  for (int i = 1; i <= 5; i++) {
    ellipse(250 + a, 218+b, 4, 4);
    b+=12;
  }
  noStroke();
}

void robotBody() {
  //body
  fill(#f25d13);
  arc(241, 347, 130, 159, radians(-32), radians(213), OPEN);
  fill(#fa6a22);
  arc(241, 330, 125, 100, radians(-32), radians(213), OPEN);
  fill(#e24b00);
  ellipse(241, 308, 114, 26);

  //neck
  fill(#1c1c1c);
  rect(230, 275, 26, 37, 10);
}

void robotLegs() {
  //left leg
  fill(#1c1c1c);
  pushMatrix();
  translate(212, 420);
  rotate(radians(22));
  ellipse(0, 0, 35, 20);
  popMatrix();

  rect(205, 418, 15, 90);

  //left foot
  arc(230, 500, 40, 20, radians(-180), radians(0), OPEN);

  //right leg
  pushMatrix();
  translate(280, 410);
  rotate(radians(-22));
  ellipse(0, 0, 35, 20);
  popMatrix();

  rect(273, 418, 15, 90);

  //right foot
  arc(300, 500, 40, 20, radians(-180), radians(0), OPEN);
}

void robotArms() {
  float a = map(sin(theta), -1, 1, 230, 240);
  float b = map(sin(theta), -1, 1, 220, 230);
  float c = map(sin(theta), -1, 1, 225, 235);
  float d = map(sin(theta), -1, 1, 230, 240);
  theta += 0.03; //lower values slow down the movement

  //umbrella
  fill(#0076b3);
  rect(c, 160, 8, 200, 3);
  arc(c, 165, 160, 80, radians(-180), radians(0), OPEN);

  //left arm
  fill(#1c1c1c);
  ellipse(200, 333, 18, 23);

  fill(#1c1c1c);
  rect(195, 333, 10, 60, 5);

  stroke(0); 
  strokeWeight(10);
  line(200, 390, a, 330);
  noStroke(); 

  //left hand
  fill(0);
  rect(b, 320, 20, 25, 3);
}