Creative coding animation of a morphing blob.



Creative coding animation of a morphing blob.

It is a creative coding morphing animation that is written in Processing. It creates an animation of a morphing blob.

I created this blob shape by chance with this formula.


 float cX = _radius * cos(sin(a * t)) * cos(b * t);
 float cY = _radius * sin(cos(a * t)) * sin(b * t);

And I moved to the next shape by the morphing method. It's a near relation work of  'Johnny on the Monorail' about this morphing way.
I changed the easing function between drawing lines and drawing the painted planes. It added a nice flavor to this animation.

Yet another implementation with dots, not a plane.

Yet another implementation with dots, not a plane.


 


An example code of Processing.

This code does not display any images on the screen but generates image files in frames directory. You can make an animation with these files.
Please feel free to use this example code. To see other works based on my code is my pleasure. And my honor.


/**
 * Strawberry Swing.
 * Creative coding animation of a morphing blob.
 * 
 * Processing 3.5.3
 * @author @deconbatch
 * @version 0.1
 * created 0.1 2020.12.19
 */

public void setup() {
  size(720, 720);
  colorMode(HSB, 360, 100, 100, 100);
  rectMode(CENTER);
  smooth();
  noLoop();
}

public void draw() {

  int   frmRate  = 24;
  int   frmMorph = frmRate * 2;       // morphing duration frames
  int   cycles   = 5;                 // animation cycle no
  int   frmMax   = frmMorph * cycles; // whole frames
  int   nodeMax  = 300;               // nodes number
  float hueBase  = random(360.0);

  // calculate whole nodes location
  ArrayList<ArrayList<PVector>> shapes = new ArrayList<ArrayList<PVector>>();
  for (int i = 0; i < cycles; i++) {
    float radius = min(width, height) * random(0.3, 0.4);
    shapes.add(setNodes(nodeMax, radius));
  }

  ArrayList<PVector> nodesFrom = new ArrayList<PVector>();
  ArrayList<PVector> nodesTo   = new ArrayList<PVector>();
  int cycleCnt = 0;

  translate(width * 0.5, height * 0.5);
  for (int frmCnt = 0; frmCnt < frmMax; frmCnt++) {
    float frmRatio = map(frmCnt, 0, frmMax, 0.0, 1.0);

    blendMode(BLEND);
    background(0.0, 0.0, 100.0, 100.0);

    // nodes for morphing animation. loops cyclic.
    if (frmCnt % frmMorph == 0) {
      cycleCnt = frmCnt / frmMorph;
      nodesFrom = shapes.get(cycleCnt);
      nodesTo   = shapes.get((cycleCnt + 1) % cycles);
    }

    // easing calculation
    float morphRatio = easeInOutCubic(map(frmCnt % frmMorph, 0, frmMorph - 1, 0.0, 1.0));
    float easeRatio  = InFourthPow(map(frmCnt % frmMorph, 0, frmMorph - 1, 0.0, 1.0));

    blendMode(SUBTRACT);
    pushMatrix();
    rotate((morphRatio + cycleCnt) * TWO_PI / cycles);

    // plane
    noStroke();
    fill((hueBase + 360.0 * frmRatio) % 360.0, 60.0, 40.0, 100.0);
    drawNodes(nodesFrom, nodesTo, nodeMax, morphRatio, 0.0);

    // lines
    stroke(0.0, 0.0, 50.0 - 20.0 * sin(PI * morphRatio), 100.0);
    strokeWeight(2.0);
    noFill();
    drawNodes(nodesFrom, nodesTo, nodeMax, morphRatio, easeRatio);

    popMatrix();

    blendMode(BLEND);
    casing();

    // for stop motion
    if (frmCnt % frmMorph == 0) {
      for (int i = 0; i < frmRate; i++) {
        saveFrame("frames/" + String.format("%04d", cycleCnt) + ".00." + String.format("%04d", i) + ".png");
      }
    }
    // for moving motion
    saveFrame("frames/" + String.format("%04d", cycleCnt) + ".01." + String.format("%04d", frmCnt) + ".png");
  }
  exit();
}

/**
 * drawNodes : draw nodes with morphing calculation.
 * @param  _nodeF, _nodeT : nodes to draw, morphing from and to
 * @param  _nodeMax : node number
 * @param  _ratioM  : morphing ratio
 * @param  _ratioS  : size ratio
 */
void drawNodes(ArrayList<PVector> _nodeF, ArrayList<PVector> _nodeT, int _nodeMax, float _ratioM, float _ratioS) {
  float rT = _ratioM;
  float rF = 1.0 - _ratioM;
  float rS = 1.0 + sin(_ratioS * PI) * 0.3;
  beginShape();
  // last node for smooth connection
  curveVertex(
              (_nodeF.get(_nodeMax - 1).x * rF + _nodeT.get(_nodeMax - 1).x * rT) * rS,
              (_nodeF.get(_nodeMax - 1).y * rF + _nodeT.get(_nodeMax - 1).y * rT) * rS
              );
  // all nodes
  for (int i = 0; i < _nodeMax; i++) {
    float nX = (_nodeF.get(i).x * rF + _nodeT.get(i).x * rT) * rS;
    float nY = (_nodeF.get(i).y * rF + _nodeT.get(i).y * rT) * rS;
    curveVertex(nX, nY);
  }
  // first node for smooth connection
  curveVertex(
              (_nodeF.get(0).x * rF + _nodeT.get(0).x * rT) * rS,
              (_nodeF.get(0).y * rF + _nodeT.get(0).y * rT) * rS
              );
  endShape();
}

/**
 * setNodes : calculate the nodes locations by random walking method. 
 * @param  _nodeMax : node number to calculate
 * @param  _radius    : random walking step
 */
ArrayList<PVector> setNodes(int _nodeMax, float _radius) {
  float a = floor(random(1.0, 10.0)) * 2.0;
  float b = floor(random(1.0, 10.0)) * 2.0 + 1.0;
  float c = random(TWO_PI); // phase
  ArrayList<PVector> node = new ArrayList<PVector>();
  for (int i = 0; i < _nodeMax; i++) {
    float t = map(i, 0, _nodeMax, c, c + TWO_PI);
    float cX = _radius * cos(sin(a * t)) * cos(b * t);
    float cY = _radius * sin(cos(a * t)) * sin(b * t);
    node.add(new PVector(cX, cY));
  }
  return node;
}

/**
 * easeInOutCubic easing function.
 * @param  t     0.0 - 1.0 : linear value.
 * @return float 0.0 - 1.0 : eased value.
 */
float easeInOutCubic(float t) {
  t *= 2.0;
  if (t < 1.0) {
    return pow(t, 3) / 2.0;
  }
  t -= 2.0;
  return (pow(t, 3) + 2.0) / 2.0;
}
  
/**
 * InFourthPow : easing function.
 * @param  _t    0.0 - 1.0 : linear value.
 * @return       0.0 - 1.0 : eased value.
 */
private float InFourthPow(float _t) {
  return 1.0 - pow(1.0 - _t, 4);
}

/**
 * casing : draw fancy casing
 */
public void casing() {
  fill(0.0, 0.0, 0.0, 0.0);
  strokeWeight(40.0);
  stroke(0.0, 0.0, 0.0, 100.0);
  rect(0.0, 0.0, width, height);
  strokeWeight(36.0);
  stroke(0.0, 0.0, 100.0, 100.0);
  rect(0.0, 0.0, width, height);
}


/*
Copyright (C) 2020- deconbatch

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>
*/



Yet another example images.

Creative coding animation of a morphing blob.

Creative coding animation of a morphing blob.