Changing the easing function brings a nice flavor to the animation.
Creative coding animation of a morphing blob.
It is a creative coding morphing animation that is written in the '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.
An example code of the '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 under the terms of the GPL. 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/>
*/