lass-AnimatedLoop

For my looping gif, I was really interested in doing a pinching/pulling motion, similar to cell division. At first I was very unsure of how to do this, since I had seen it before but didn't know a technical term. Connie Ye told me to look into metaballs, and I learned the process for making them from this coding challenge by Dan Shiffman. My initial plan involved several components, but after I had gotten the metaballs to work initially, many people urged me to keep it simple instead. I ended up only creating the first main component from the sketches below.   

In the end, I'm glad I went with the simple route. It allowed me to have more flexibility with colors, and it resulted in something much cleaner and not so chaotic. In the future I might try to create something more complex with the code that I wrote, since I planned it out to allow for several "groupings" of metaballs. I think it might be very laggy.

Thank you Golan, Connie, and Lauren for helping me out with this project!

debug view: 

BallGroup metaballs;
int numFrames = 30;
int currentFrame = 0;
int colorChoice = 0;
color[] ballColors, backgroundColors;
 
void setup() {
 size(640, 640);
 noStroke();
 
 ballColors = new color[] {
  color(250, 192, 133), color(169, 67, 117), color(50, 70, 171)
 };
 backgroundColors = new color[] {
  color(200, 94, 141), color(250, 200, 113), color(169, 200, 117)
 };
 
 metaballs = new BallGroup();
 metaballs.addBall(new Metaball(width / 2, height - 40, width / 2, -100, 80, 80));
 metaballs.addBall(new Metaball(width / 2, -100, width / 2, -100, 80, 0));
 metaballs.addBall(new Metaball(width / 2 - 50, height - 50, width / 2 - 50, height - 50, 130, 130));
 metaballs.addBall(new Metaball(width / 2 + 50, height - 50, width / 2 + 50, height - 50, 130, 130));
 metaballs.addBall(new Metaball(width / 2, height * 2, width / 2, height - 40, 0, 80));
 
 metaballs.setColors(ballColors[0], ballColors[1]);
}
 
void draw() {
 currentFrame = (currentFrame + 1) % 30;
 if (currentFrame == 0) {
  colorChoice = (colorChoice + 1) % 3;
  metaballs.setColors(ballColors[colorChoice], ballColors[(colorChoice + 1) % 3]);
 }
 float movement = easing(1.0 * currentFrame / numFrames);
 metaballs.fadeCol = (lerpColor(ballColors[(colorChoice + 1) % 3], ballColors[(colorChoice + 2) % 3], movement));
 background(lerpColor(ballColors[(colorChoice + 0) % 3], ballColors[(colorChoice + 1) % 3], movement));
 //background(255, 200, 200); 
 metaballs.update(movement);
 metaballs.show();
}
 
float easing(float x) {
 //this is where i would put an easing function if i had one 
 return x;
}
 
class Metaball {
 PVector position;
 PVector beginning;
 PVector destination;
 float radius;
 float radius1;
 float radius2;
 Metaball(float x1, float y1, float x2, float y2, float r1, float r2) {
  position = new PVector(x1, y1);
  beginning = new PVector(x1, y1);
  destination = new PVector(x2, y2);
  radius = 100;
  radius1 = r1;
  radius2 = r2;
 }
 void show() { //shows the centers (for debugging) 
  noFill();
  stroke(0);
  strokeWeight(1);
  //ellipse(position.x, position.y, radius, radius);
 }
 void update(float movement) {
  position = position.lerp(beginning, destination, movement);
  radius = lerp(radius1, radius2, movement);
 }
}
 
class BallGroup {
 ArrayList < Metaball > arr;
 color startCol;
 color endCol;
 color col;
 color fadeCol;
 
 BallGroup() {
  arr = new ArrayList < Metaball > ();
  col = color(0, 0, 0);
  startCol = color(0, 0, 0);
  endCol = color(0, 0, 0);
 }
 void setColor(color c) {
  col = c;
 }
 void setColors(color c1, color c2) {
  startCol = c1;
  endCol = c2;
 }
 void addBall(Metaball mb) {
  arr.add(mb);
 }
 void show() {//metaball code from Dan Shiffman: https://www.youtube.com/watch?v=ccYLb7cLB1I
  loadPixels();
  for (int x = 0; x < width; x++) {
   for (int y = 0; y < height; y++) {
    float sum = 0;
    for (Metaball mb: arr) {
     float dst = dist(x, y, mb.position.x, mb.position.y);
     sum += 100 * mb.radius / dst;
    }
    if (sum > 200) {
     //adds a border
     //if (sum < 220)
     //  pixels[x + y * width] = col; 
     //else
     pixels[x + y * width] = lerpColor(col, fadeCol, y * 1.0 / height);
    }
   }
  }
  updatePixels();
  for (Metaball mb: arr)
   mb.show();
 }
 void update(float movement) {
  for (Metaball mb: arr)
   mb.update(movement);
  col = lerpColor(startCol, endCol, movement);
 }
}