I tried to express the rhythm and density in this animation.
Expressing the rhythm and density in a creative coding animation.
I tried to express the rhythm and density in a creative coding animation.
I've been reading Paul Klee's book. It's an exercise that will help me to understand this book well.
At first, I drew a still image that expresses density.
And I added some deviation to it.
I drew still images that express rhythm also.
Then, I combined these and added some deviations.
And I added express of 'weight' and some structure instead of a simple rectangle.
Yet another example images.
An example source 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.
/**
* White Room.
* Expressing the rhythm and density in a creative coding animation.
*
* @author @deconbatch
* @version 0.1
* @license GPL Version 3 http://www.gnu.org/licenses/
* Processing 3.5.3
* 2020.10.25
*/
void setup() {
size(720, 720);
colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
noLoop();
}
void draw() {
int rhythm = 3;
int virtMax = 6;
int horiMax = 6;
int frmMorph = 24 * 5; // morphing duration frames
int cycles = 3; // animation cycle no.
int frmMax = frmMorph * cycles; // whole frames
float margin = 20.0;
float baseBri = 800.0 * (virtMax + horiMax);
float rndHue = random(360.0);
// set whole nodes
ArrayList<ArrayList<Node>> nodes = new ArrayList<ArrayList<Node>>();
for (int i = 0; i < cycles; i++) {
nodes.add(setNodes(divide(virtMax), divide(horiMax), margin, rhythm, baseBri));
}
ArrayList<Node> nodesFrom = new ArrayList<Node>();
ArrayList<Node> nodesTo = new ArrayList<Node>();
for (int frmCnt = 0; frmCnt < frmMax; frmCnt++) {
// nodes for morphing animation. loops cyclic.
if (frmCnt % frmMorph == 0) {
nodesFrom = nodes.get(frmCnt / frmMorph);
nodesTo = nodes.get((frmCnt / frmMorph + 1) % cycles);
}
background(0.0, 0.0, 100.0, 100.0);
stroke(0.0, 0.0, 30.0, 100.0);
float toRatio = InFourthPow(map(frmCnt % frmMorph, 0, frmMorph - 1, 0.0, 1.0));
float frRatio = 1.0 - toRatio;
for (int i = 0; i < nodesTo.size(); i++) {
// morphing calculations
float nB = nodesFrom.get(i).bri * frRatio + nodesTo.get(i).bri * toRatio;
float nX = nodesFrom.get(i).x * frRatio + nodesTo.get(i).x * toRatio;
float nY = nodesFrom.get(i).y * frRatio + nodesTo.get(i).y * toRatio;
float nW = nodesFrom.get(i).w * frRatio + nodesTo.get(i).w * toRatio;
float nH = nodesFrom.get(i).h * frRatio + nodesTo.get(i).h * toRatio;
float eX = nodesFrom.get(i).ex * frRatio + nodesTo.get(i).ex * toRatio;
float eY = nodesFrom.get(i).ey * frRatio + nodesTo.get(i).ey * toRatio;
float eR = nodesFrom.get(i).er * frRatio + nodesTo.get(i).er * toRatio;
// draw base matrix
strokeWeight(5);
fill(0.0, 0.0, nB, 100.0);
rect(nX, nY, nW, nH);
// draw some construction
strokeWeight(1);
pushMatrix();
translate(nX, nY);
for (int j = 0; j < 10; j++) {
rect(0.0, 0.0, nW - j * 10, nH - j * 10, eR * 0.25);
translate(5, 5);
}
popMatrix();
// draw light/heavy objects
float eRatio = (nH * eR) / (height * 15.0 / horiMax);
strokeWeight(constrain(eRatio, 0.0, 3.0));
if (eRatio > 0.5 && eRatio < 2.0) {
if (nB > 67.0) {
// heavy
line(eX, eY, eX, nY);
fill(0.0, 0.0, 100.0 - nB * 0.5, 100.0);
ellipse(eX, eY, eR, eR);
fill(0.0, 0.0, nB, 100.0);
ellipse(eX, eY, eR * 0.5, eR * 0.5);
} else {
// light
line(eX, eY, eX, nY + nH);
fill(0.0, 0.0, 100.0 - nB * 0.5, 100.0);
ellipse(eX, eY, eR, eR);
}
}
}
// draw casing
strokeWeight(10);
stroke(0.0, 0.0, 30.0, 100.0);
noFill();
rect(margin, margin, width - margin * 2.0, height - margin * 2.0);
saveFrame("frames/" + String.format("%04d", frmCnt) + ".png");
}
exit();
}
/**
* Node : holds node informations.
*/
class Node {
public float x, y, w, h; // rectangle
public float ex, ey, er; // ellipse (navel)
public float bri; // brightness
Node(float _x, float _y, float _w, float _h, float _ex, float _ey, float _bri) {
x = _x;
y = _y;
w = _w;
h = _h;
ex = _ex;
ey = _ey;
bri = _bri;
er = 0.0;
}
void setNavel() {
er = (w + h) * 0.07;
}
}
/**
* setNodes : calculate the nodes location and size.
* @param _xs, _ys : divided ratios of canvas.
* @param _margin : canvas margin.
* @param _rhythm : rhythm number.
* @param _baseBri : base brightness.
* @return : array of nodes.
*/
ArrayList<Node> setNodes(ArrayList<Float> _xs, ArrayList<Float> _ys, float _margin, int _rhythm, float _baseBri) {
ArrayList<Node> ns = new ArrayList<Node>();
float w = width - _margin * 2.0;
float h = height - _margin * 2.0;
int xCnt = 0;
float xSum = 0.0;
for (float x : _xs) {
int yCnt = 0;
float ySum = 0.0;
for (float y : _ys) {
float rW = x * w;
float rH = y * h;
float pX = xSum * w + _margin;
float pY = ySum * h + _margin;
float eX = pX + rW * 0.5;
float eY = pY + rH * 0.5;
float rVal = (1.0 + (xCnt + yCnt) % _rhythm) / _rhythm;
float rBri = constrain((x * y * _baseBri * rVal), 0.0, 100.0);
// brightness deviation
if (random(1.0) < 0.4) {
rBri = rVal * 90.0;
}
// size deviation
if (random(1.0) < 0.2) {
rW = x * w * random(0.5, 2.5);
rW = (pX + rW >= w + _margin) ? rW - (rW - _margin) : rW;
rH = y * h * random(0.5, 2.5);
rH = (pY + rH >= h + _margin) ? rH - (rH - _margin) : rH;
}
Node n = new Node(pX, pY, rW, rH, eX, eY, rBri);
if (xCnt % (_rhythm) == 0 || yCnt % (_rhythm - 1) == 0) {
if (random(1.0) < 0.5) {
n.setNavel();
}
}
ns.add(n);
yCnt++;
ySum += y;
}
xCnt++;
xSum += x;
}
return ns;
}
/**
* divide : divide the canvas randomly and returns ratios of divided spaces.
* @param _div : divide number.
* @return : ratios(0.0 - 1.0) of divided spaces per canvas.
*/
ArrayList<Float> divide(int _div) {
ArrayList<Float> ratios = new ArrayList<Float>();
float rndPhase = random(TWO_PI);
float rndCycle = random(0.5, 2.5);
float rndMulti = random(0.5, 1.0);
float lenSum = 0.0;
for (int i = 0; i < _div; i++) {
float len = 2.0 + sin(map(i, 0, _div, 0.0, TWO_PI) * rndCycle + rndPhase) * rndMulti;
ratios.add(len);
lenSum += len;
}
for (int i = 0; i < _div; i++) {
ratios.set(i, ratios.get(i) / lenSum); // normalize
}
return ratios;
}
/**
* 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);
}
/*
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/>
*/