implode / explode [ interactive animation ]

   3 April 2012

Here’s my interactive animation, and here’s the code:

the main program:

ArrayList squigglies; // the arraylist that holds the squigglies

int wiggleroom = 1; // amount the squiggles are allowed to wiggle
int lifespan = 20; // number of joints per squiggle
int numSquigglies =6; // number of squiggles per row
int rows = 3; // number of rows
int xborder = 200; // buffer on left/right
int yborder = 100; // buffer on top/bottom
color bgcolor = color(255, 240, 200, 127); // background color
int R = 1; // foreground red
int G = 3; // foreground green
int B = 10; // foreground blue

boolean out; // toggle to change direction (imploding or exploding)
boolean looping = true;

int tlife = 255; // life of the text at intro
float w, wp, wr; // text widths

String pause = "spacebar to pause"; // intro text
String s = "click to explode / implode"; // intro text
String reset = "r key to reset"; // intro text
PFont font; // font (unused in web version because web fonts suck)

PVector anchor = new PVector(0, 0); // explosion / implosion base point

void setup() {
  size(screen.width, screen.height);
   
  background(bgcolor);
  smooth();
  noFill();

  out = true; // initially explode
  
  font = loadFont("LetterGothic.vlw"); // setup font
  textFont(font, 24);
  
  
  w = textWidth(s);        // get text widths
  wp = textWidth(pause);
  wr = textWidth(reset);
  
  

  squigglies = new ArrayList(); // initialize the squigglies arraylist
  
  for (int b = 0; b < rows; b++) { // cycle through the rows
    for (int i = 0; i < numSquigglies; i++) { // cycle through squigglies
      float x = i*((width-xborder)/numSquigglies) + 
        random(-(width-xborder)/numSquigglies/2, (width-xborder)/numSquigglies/2); 
        // splay out along the x-axis, randomly
      float y = height-yborder; // all along one base y
      x += xborder/2 + (width-xborder)/numSquigglies/2; // add border

      PVector spawn = new PVector(x, y); // where to put the squiggly
      squigglies.add(new Squiggly(spawn, color(R, G, B, (b+1)*(255/rows)))); 
      // add a squiggly to the arraylist at spawn(x,y), 
      // with a foreground color, shaded according to row
    }
  }

  for (int j = 0; j < squigglies.size(); j++) { // loop through the (now full) array of squigglies
    Squiggly squiggle = (Squiggly) squigglies.get(j); // cast the object coming out 
    // (arraylists don't know what they hold)
    for (int k = 0; k < lifespan; k++) { // for the length of the lifespan,
    // figure out where to drop a joint
      float x2 = squiggle.root.x + random(-wiggleroom, wiggleroom); 
      float y2 = (height-yborder) - ((height-2*yborder)/lifespan)*k;
      if (k == lifespan-1) {
        y2 -= random(((height-2*yborder)/lifespan)); // add some noise
      }
      PVector Loc = new PVector(x2, y2);
      squiggle.addJoint(Loc); // add the joint into the squiggly
    }
  }
}

void draw() {
  fill(bgcolor);
  noStroke();
  rect(0, 0, width, height); // medium transparent background creates trailing
  noFill();
  
  if (squigglies.size() > 0) { // if there are squigglies

    for (int i = 0; i < squigglies.size(); i++) {
      Squiggly squiggle = (Squiggly) squigglies.get(i); // get the squiggly (& cast it)
      squiggle.wiggle(); // wiggle the squiggle!
      squiggle.show(); // show it
    }
  }
  
  if (tlife > 0) {  // intro text
    fill(red(bgcolor), green(bgcolor), blue(bgcolor), tlife);
    noStroke();
    rect(width/2-(w/2+100), height/2-110, w+200, 200);
    fill(R,G,B,tlife);
     text(s, width/2-w/2, height/2-40);
     text(pause, width/2-wr/2, height/2);
     text(reset, width/2-wr/2, height/2+40);
     tlife-=2;
    noFill();
  }
  
  
}

void mousePressed() {
  out = !out; // switch direction

  for (int i = 0; i < squigglies.size(); i++) {
    Squiggly squiggle = (Squiggly) squigglies.get(i); // get & cast squiggly
    squiggle.essplode(); // essplode!
  }
}

void keyPressed() {
  if (key == ' ') {  // pause on spacebar
    print(looping + " > ");
    if (looping) noLoop();
    else if (!looping) loop();
    looping = !looping;
    println(looping);
  }
  else if (key == 'r') {
    looping = true; // reset looping to true
    loop(); // then actually loop
    setup(); // then reset
  }
}

the squiggly class:

class Squiggly {
  
// variables: 
  PVector root;
  int speed, age;

  ArrayList joints;
  color SquigglyColor;
  boolean increasing;




  Squiggly (PVector _loc, color _c) { // gets passed a location and color
    root = new PVector(_loc.x, _loc.y);
    joints = new ArrayList(); // initialize an array of joints
    joints.add(new Joint(root)); // dump a joint at the root
    age = 1;
    SquigglyColor = _c;
  }

  void addJoint(PVector jLoc) {
    age++; // with new joints, increment age
    joints.add(new Joint(jLoc)); // and add a joint
  }


  void wiggle() { // wiggle it!
    for (int i = 0; i < age; i++) { // cycle through the number of joints
      Joint joint = (Joint) joints.get(i); // cast the joint
      joint.makeSomeNoise(); // wiggle it!
    }
  }


  void show() {
    stroke(SquigglyColor);
    beginShape();

    if (age > 1) {
      for (int i = 0; i < age; i++) { // for each joint,
        Joint joint = (Joint) joints.get(i); // get + cast
        joint.update(); // move it

        curveVertex(joint.loc.x, joint.loc.y); // add a vertex at the joint's location

        if (joint.loc.x > width+10 || joint.loc.x < -10 || 
            joint.loc.y > height+10 || joint.loc.y < -10) { // if the joint is off the screen
          joint.velocity.x = 0; // stop its motion
          joint.velocity.y = 0;
          joint.acceleration.x = 0;
          joint.acceleration.y = 0;
          age--; // make the squiggly younger
          joints.remove(i); // remove this joint from the squiggly
                            // (connects the joints on either side)

        }
      }


      Joint last = (Joint) joints.get(age-1); // put an extra vertex at the end
      curveVertex(last.loc.x, last.loc.y); // because of the way beginshape() works
    }


    endShape();
  }

  void essplode() {
    anchor = new PVector(mouseX, mouseY); // set the explosion point to the mouse location
    stroke(R,G,B, 10); // color = foreground color, alpha 10
    ellipse(anchor.x, anchor.y, 50, 50); // drop a quick circle

    for (int i = 0; i < age; i++) { // for each joint
      Joint joint = (Joint) joints.get(i); // get & cast
      joint.essploding = true; // set the joint to essplode
      
      joint.atang = atan2(joint.loc.y-anchor.y, joint.loc.x-anchor.x);
      // calculate the direction of explosion
    }
  }
}

the joint class:

class Joint {

  // all the variables
  
  PVector loc, velocity, acceleration; // physics variables
  float xadd, yadd, xoff, yoff, xinc, yinc, angle, atang;
  // animation (math) variables
  
  boolean essploding = false; // toggle for exploding
  int timer;

  Joint (PVector _loc) { // gets passed the location
    loc = new PVector(_loc.x, _loc.y);
    velocity = new PVector(0, 0);
    acceleration = new PVector(0, 0); // initialize acceleration
    xoff = random(1); // setup x noise
    yoff = random(1); // setup y noise
    xinc = random(0.003, 0.03); // setup x step
    yinc = random(0.003, 0.03); // setup y step
    angle = random(TWO_PI); // setup sin/cos calculator
  }


  void makeSomeNoise() {
    velocity.x = noise(xoff); // get perlin noise
    velocity.y = noise(yoff);

    velocity.x *= cos(angle)*wiggleroom; // scale it according to
    velocity.y *= sin(angle)*wiggleroom; // a circle, to go back + forth
    
    xoff += xinc; // increment through noise space
    yoff += yinc;
    angle+= random(.1, .5); // increment angle
    
  }

  void update() {
    
    velocity.add(acceleration); // because of derivatives...
    loc.add(velocity);          // this is how physics animation works
    
    if (essploding == true && out == true) { // if exploding,
      acceleration.x = cos(atang)*3; // x = cosine of explosion angle
      acceleration.y = sin(atang)*3; // y = sine of explosion angle
    }
    else if (essploding == true && out == false) { // if imploding,
      acceleration.x = -cos(atang)*3; // inverse of exploding
      acceleration.y = -sin(atang)*3;
    }
  }
}

squiggly forest – nonlinear animation

   3 April 2012

here’s my attempt at non-linear animation – the movement of the lines is governed by perlin noise and sin/cosine oscillation.

here’s the code:

main program:

ArrayList squigglies;
int res = 10;
int wiggleroom = 1;
int lifespan = 10;
int numSquigglies =10;
int xborder = 200;
int yborder = 100;
color bgcolor = color(255, 240, 200, 50);
int R = 1;
int G = 3;
int B = 10;

PVector anchor = new PVector(0, 0); 

void setup() {
  size(screen.width, screen.height);
  background(0);
  smooth();
  noFill();


  squigglies = new ArrayList();
  for (int b = 0; b < 5; b++) {
    for (int i = 0; i < numSquigglies; i++) {
      float x = i*((width-xborder)/numSquigglies) + 
       random(-(width-xborder)/numSquigglies/2, (width-xborder)/numSquigglies/2);
      float y = height-yborder;
      x += xborder/2 + (width-xborder)/numSquigglies/2;

      PVector spawn = new PVector(x, y);
      squigglies.add(new Squiggly(spawn, color(R, G, B, (b+1)*25)));
    }
  }

  for (int j = 0; j < squigglies.size(); j++) {
    Squiggly squiggle = (Squiggly) squigglies.get(j);
    for (int k = 0; k < lifespan; k++) {
      float x2 = squiggle.root.x + random(-wiggleroom, wiggleroom);
      float y2 = (height-yborder) - 80*k;
      if (k == lifespan-1) {
        y2 -= random(160);
      }
      PVector Loc = new PVector(x2, y2);
      squiggle.addJoint(Loc);
    }
  }
}

void draw() {
  fill(bgcolor);
  noStroke();
  rect(0, 0, width, height);
  noFill();

  if (squigglies.size() > 0) {
    for (int i = 0; i < squigglies.size(); i++) {
      Squiggly squiggle = (Squiggly) squigglies.get(i);
      squiggle.wiggle();
      squiggle.show();
    }
  }
}

the joint class:

class Joint {
  
  PVector loc, velocity, acceleration;
  float xadd, yadd, xoff, yoff, xinc, yinc, angle;

  Joint (PVector _loc) {
    loc = new PVector(_loc.x, _loc.y);
    velocity = new PVector(0,0);
    acceleration = new PVector(0,0);
    xoff = random(1);
    yoff = random(1);
    xinc = random(0.003, 0.03);
    yinc = random(0.003, 0.03);
    angle = random(TWO_PI);
  }


  void makeSomeNoise() {
    velocity.x = noise(xoff);
    velocity.y = noise(yoff);

    velocity.x *= cos(angle)*wiggleroom;
    velocity.y *= sin(angle)*wiggleroom;

    xoff += xinc;
    yoff += yinc;
    angle+= random(.1,.5);
  }
  
  void update() {
    loc.add(velocity);
    velocity.add(acceleration);
  }
  
  
  
}

the squiggly class:

class Squiggly {

  PVector root;
  int age;
  ArrayList joints;
  color SquigglyColor;
  
  


  Squiggly (PVector _loc, color _c) {
    root = new PVector(_loc.x, _loc.y);
    joints = new ArrayList();
    joints.add(new Joint(root));
    age = 1;
    stroke(R,G,B);
    ellipse(root.x, root.y, 5, 5);
    SquigglyColor = _c;
  }

  void addJoint(PVector jLoc) {
    age++; 
    joints.add(new Joint(jLoc));
  }


  void wiggle() {
    for (int i = 0; i < age; i++) {
      Joint joint = (Joint) joints.get(i);
      if (i > 1) {
        joint.makeSomeNoise();
      }
    }
  }


  void show() {
    stroke(SquigglyColor);
    beginShape();
    
    if (age > 1) {
    for (int i = 0; i < age; i++) {
      Joint joint = (Joint) joints.get(i);
      joint.update();
      
      curveVertex(joint.loc.x, joint.loc.y);
    }
    
    
      Joint last = (Joint) joints.get(age-1);
      curveVertex(last.loc.x, last.loc.y);
    }
    
      endShape();
    }
  }



midterm: sisyphus

   7 March 2012

As with most things, I dramatically underestimated the time it takes to animate a figure. As the screenshots below indicate, I got about halfway through, and faced with the staggering sisyphean task of animating hundreds more keyframes, I found a way to reverse part of the work I’d already done, to create the kind of looping effect I had in mind without redoing all the keyframes. As a result, the overall timing isn’t perfect, and it doesn’t loop back to the beginning, as I had hoped – a project that will require more time and sleep…








midterm draft (sisyphusless)

   29 February 2012

Sisyphus midterm – outline and style frames

   22 February 2012

The story of Sisyphus holds much of its power in its sheer simplicity. In the Greek mythology, Sisyphus was a brutal and cunning king who was clever enough to outwit Thanatos (the god of death) and escape from the underworld. As punishment, Sisyphus is tasked with eternally pushing a massive boulder up a mountain, only to have it roll back down again. The mind-numbingly perpetual aspect of this story (the perfect punishment for the craftiest man) is especially intriguing for my purposes, as it is perfectly suited for the creation of an animated infinite loop. Aesthetically, too, I’m hoping to create allusions to the history of looping animation – especially the rolling loop backdrops used in film to simulate driving and the waves of animated GIF fads of the last few decades. I’m hoping that, visually, a kind of rapidly-shifting digital collage will enhance the sense of universality that infuses Greek mythology.




sisyphus loop, take 1

   15 February 2012

this is sisyphus’s boulder – I realized quickly that animating sisyphus himself was far too ambitious…

storyboard here

bouncing metal balls

   8 February 2012

…and the storyboard here

exploding beastie (homework 1)

   25 January 2012

the first animation assigment (“Create a 5 sec long animation, displaying a geometric shape [which] enters the screen, and expresses a trait or an attribute”)…

…the animation:





…and the storyboard:


sam galison { contact :: resumes :: stuff :: main }

{ © sam galison 2012 }