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); } } |