It draws a Mandala image in many circles.
I previously saw ripples spreading on the water's surface and envisioned a mandala. I wrote code with my limited skills back in 2007, but when I shared the resulting video on Twitter, the image quality turned out to be terrible.
This time, I will write an entirely new code, different from before, and take on the challenge of compression on Twitter!
Processing example code.
This code does not display any images on the screen but generates image files in frames directory. 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.
/**
* MandaRipple.
* It draws a Mandala image in many circles.
*
* @author @deconbatch
* @version 0.2
* @license GPL Version 3 http://www.gnu.org/licenses/
* Processing 3.5.3
* 2017.07.08 : ver 0.1
* 2023.05.08 : ver 0.2 rewrite
*/
void setup() {
size(720, 720);
colorMode(HSB, 360, 100, 100, 100);
smooth();
int frmRate = 24;
int duration = 6;
int frmMax = frmRate * duration;
float hueInit = random(360.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);
background((hueInit + 135.0) % 360.0, 90.0, 30.0, 100.0);
drawMandala(hueInit, frmRatio);
saveFrame("frames/" + String.format("%04d", frmCnt) + ".png");
}
exit();
}
/**
* drawMandala : draw mandala with circles.
* @param _hue 0.0 - 360.0 : base hue value to draw.
* @param _ratio 0.0 - 1.0 : animation ratio.
*/
private void drawMandala(float _hue, float _ratio) {
int orbitMax = 16;
int cornerMax = 8;
float silverR = 1.0 / sqrt(2.0); // silver ratio
float diaBase = width * silverR / orbitMax;
float easeForth = sin(PI * easeInOutCos(_ratio));
float easeCubic = sin(PI * easeInOutCubic(_ratio));
float hueBase = _hue + 360.0 + sin(TWO_PI * _ratio) * 60.0;
noFill();
for (int orbitCnt = 0; orbitCnt < orbitMax; orbitCnt++) {
float orbitRatio = map(orbitCnt, 0, orbitMax, 0.0, 1.0);
float diaCenter = (1.0 + orbitCnt + easeCubic) * diaBase;
float diaCorner = (1.0 + orbitCnt + (1.0 - easeForth)) * diaBase;
strokeWeight(2.0 + orbitRatio * 6.0);
// corner circles
pushMatrix();
for (int corner = 0; corner < cornerMax; corner++) {
float cornerT = corner * TWO_PI / cornerMax;
float rotateT = (orbitCnt % 2 == 0 ? 1 : -1) * _ratio * TWO_PI / cornerMax;
float cornerR = width * 0.5 * silverR;
pushMatrix();
translate(
cornerR * cos(cornerT + rotateT),
cornerR * sin(cornerT + rotateT)
);
stroke((hueBase + 180.0 + (1.0 - orbitRatio) * 90.0) % 360.0, 30.0, 70.0);
circle(0.0, 0.0, diaCorner);
popMatrix();
}
popMatrix();
// center circle
stroke((hueBase + orbitRatio * 90.0) % 360.0, 50.0, 60.0);
circle(0.0, 0.0, diaCenter);
}
}
/**
* easeInOutCos : easing function.
* @param _t 0.0 - 1.0 : linear value.
* @return 0.0 - 1.0 : eased value.
*/
private float easeInOutCos(float _t) {
return 0.5 - cos(PI * _t) * 0.5;
}
/**
* easeInOutCubic easing function.
* @param _t 0.0 - 1.0 : linear value.
* @return float 0.0 - 1.0 : eased value.
*/
private 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;
}
MandaRipple ver 0.1 2017
About this video.
It's creative coding artwork video made with Processing and Kdenlive.
Many circles seem Mandala images or ripples on the water.
Thanks to nice music.
Interlude 4 by Quiet Music for Tiny Robots
2015/05/26
http://freemusicarchive.org/music/Quiet_Music_for_Tiny_Robots/The_February_Album/08_Interlude_4
Interlude 4 by Quiet Music for Tiny Robots is licensed under a Attribution License.
For more permissions:contact artist
Processing example code.
// MandaRipple // Processing 3.2.1 // 2017.07.08 /* ---------------------------------------------------------------------- */ abstract class PshapeElement { PShape anElement; float elementColor, elementSaturation, elementBright, elementAlpha; PshapeElement() { anElement = pscreateElement(); elementColor = 0; elementSaturation = 0; elementBright = 0; elementAlpha = 0; } abstract PShape pscreateElement(); void setElementFill(float pcolor, float psaturation, float pbright, float palpha) { elementColor = pcolor; elementSaturation = psaturation; elementBright = pbright; elementAlpha = palpha; resetColor(); } void resetColor() { anElement.setStroke(color(elementColor, elementSaturation, elementBright, elementAlpha)); } void changeColor(float scolor) { elementColor = scolor; resetColor(); } void changeBright(float sbright) { elementBright = sbright; resetColor(); } void resetSize() { anElement.resetMatrix(); } void changeSize(float scaleX, float scaleY) { anElement.scale(scaleX, scaleY); } void rotate(float radX) { anElement.rotate(radX); } void show() { shape(anElement); } } /* ---------------------------------------------------------------------- */ class CircleBrush extends PshapeElement { CircleBrush() { super(); } PShape pscreateElement() { stroke(0.0, 0.0, 0.0, 0.0); noFill(); PShape psDp = createShape(ELLIPSE, 0.0, 0.0, 5.0, 5.0); return psDp; } } /* ---------------------------------------------------------------------- */ PshapeElement pCircle; float canvasW, canvasH; float rateSize; float noiseSizWStarter; float noiseHueWStarter; float noiseSatWStarter; float noiseBriWStarter; float noiseAlpWStarter; float baseColor; float circleBase; float circleMult; void setup() { size(504, 504); colorMode(HSB, 360, 100, 100, 100); blendMode(SCREEN); strokeWeight(0.008); smooth(8); frameRate(12); pCircle = new CircleBrush(); noiseSizWStarter = random(50.0); noiseHueWStarter = random(50.0); noiseSatWStarter = random(50.0); noiseBriWStarter = random(50.0); noiseAlpWStarter = random(50.0); baseColor = random(170, 360); // yellow-green are no good for me. circleBase = width / 10.0 + map(random(1.0), 0.0, 1.0, 0.0, width / 20.0); circleMult = map(random(1.0), 0.0, 1.0, 2.0, 3.0); } void draw() { background(0, 0, 0); drawPixels(); noiseSizWStarter += 0.002; noiseHueWStarter += 0.015; noiseSatWStarter += 0.008; noiseBriWStarter += 0.004; noiseAlpWStarter += 0.006; saveFrame("frames/####.png"); if (frameCount >= 180) { exit(); } } void drawPixels() { float noiseSizW = noiseSizWStarter; float noiseHueW = noiseHueWStarter; float noiseSatW = noiseSatWStarter; float noiseBriW = noiseBriWStarter; float noiseAlpW = noiseAlpWStarter; int drawCntMax = round(min(30.0, max(2.0, map(frameCount, 10, 100, 2.0, 30.0)))); float idxDiv = width / min(4.0, max(1.0, map(frameCount, 10, 130, 1.0, 4.0))); for (int drawCnt = 0; drawCnt < drawCntMax; ++drawCnt) { for (float idxW = 0; idxW < width + idxDiv; idxW += idxDiv) { for (float idxH = 0; idxH < height + idxDiv; idxH += idxDiv) { float valSize = map(noise(noiseSizW), 0.0, 1.0, circleBase, circleMult * circleBase); float valHue = (baseColor + map(noise(noiseHueW), 0.0, 1.0, -60.0, 60.0)) % 360.0; float valSat = map(noise(noiseSatW), 0.0, 1.0, 60.0, 100.0); float valBri = map(noise(noiseBriW), 0.0, 1.0, 40.0, 100.0); float valAlp = map(noise(noiseAlpW), 0.0, 1.0, 50.0, 100.0); pushMatrix(); translate(idxW, idxH); pCircle.resetSize(); pCircle.changeSize(valSize, valSize); pCircle.setElementFill(valHue, valSat, valBri, valAlp); pCircle.show(); popMatrix(); } } noiseSizW += 10.0; noiseHueW += 10.0; noiseSatW += 10.0; noiseBriW += 10.0; noiseAlpW += 10.0; } } /* Copyright (C) 2017- 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/> */