Digital art by the idea of rotary movement.
Brief description.
It's digital art by the creative coding code of the 'Processing'.
The recursive calculation of rotary movement creates the shape on the center. And the circles and the rectangles around the shape are created by the circle packing method.
How to made this digital art.
The original idea is this Tweet by @hisadan san.
#つぶやきProcessing
— ひさだん (@hisadan) May 28, 2021
float r;void setup(){size(600,600);}void draw(){background(0);fill(255,99);a(300,300,150,r);r+=PI/99;}void a(float x,float y,float d,float r){float s,c;if(d>9){push();translate(x,y);s=d*sin(r);c=d*cos(r);circle(0,0,d*4);a(s,c,d/2,r*2);a(-s,c,d/2,r*2);pop();}} pic.twitter.com/zdQds8VIZi
I played with this idea in several ways.
Trying to plot the dots on the orbit of the circle.
軌跡に点を打ってみる。🙂#processing #creativecoding pic.twitter.com/Ki959OpPDp
— deconbatch (@deconbatch) May 29, 2021
Trying to draw circles on the orbit.
Trying to draw orbit with curve lines.
And the interesting shape appeared when I change the calculation of the recursive function.
And I filled the opening area with the circle packing.
I treated the vertex point of the shape as the avoid area to avoid filling on the shape.
I wonder about the color coordinate even now.
The example source code of the 'Processing'.
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.
This code does not display any images on the screen but generates image files in frames directory.
/**
* Under the Waterfall.
* draw shapes with the calculation of rotary movement recursively.
*
* @author @deconbatch
* @version 0.1
* @license GPL Version 3 http://www.gnu.org/licenses/
* Processing 3.5.3
* 2021.06.11
*/
void setup() {
size(980, 980);
colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
rectMode(CENTER);
smooth();
noLoop();
}
void draw(){
int ptnMax = 3;
float baseHue = random(360.0);
for (int ptnCnt = 0; ptnCnt < ptnMax; ptnCnt++) {
baseHue += 90.0;
// background image
noiseSeed(floor(baseHue + ptnCnt));
image(noiseField(baseHue), 0.0, 0.0);
// foreground image
ArrayList<ArrayList<PVector>> shapes = getShapes();
PGraphics fg = createGraphics(width, height);
fg.beginDraw();
fg.colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
fg.rectMode(CENTER);
fg.translate(width * 0.5, height * 0.5);
fg.background(0.0, 0.0, 60.0, 100.0);
scabrous(fg, baseHue);
lattice(fg, baseHue);
drawShape(fg, baseHue, shapes);
drawBubbles(fg, baseHue, shapes);
fg.endDraw();
image(fg, 0.0, 0.0);
// fancy casing
casing();
saveFrame("frames/" + String.format("%04d", ptnCnt + 1) + ".png");
}
exit();
}
/**
* noiseField : draw noise field
*/
PGraphics noiseField(float _hue) {
int cellSize = 3;
float noiseDiv = 0.01;
PGraphics p = createGraphics(width, height);
p.beginDraw();
p.colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
p.noStroke();
for (int bx = 0; bx < width; bx += cellSize) {
float nx = bx * noiseDiv;
for (int by = 0; by < height; by += cellSize) {
float ny = by * noiseDiv;
float nHue = noise(nx, ny, noise(10.0, nx, ny) * 5.0);
float nSat = noise(nx, ny, noise(20.0, nx, ny) * 8.0);
float nBri = noise(nx, ny, noise(30.0, nx, ny) * 10.0);
p.fill(
(_hue + nHue * 120.0) % 360.0,
30.0 + nSat * 60.0,
30.0 + nBri * 60.0,
100.0
);
p.rect(bx, by, cellSize, cellSize);
}
}
p.endDraw();
return p;
}
/**
* getShapes : get shapes locations
*/
ArrayList<ArrayList<PVector>> getShapes() {
int rotaryMax = 72;
int cycleMax = floor(random(1.0, 5.0));
float cycleRadius = min(width, height) * random(4.0);
float phase = floor(random(8.0)) * 0.5 * HALF_PI;
ArrayList<ArrayList<PVector>> shapes = new ArrayList<ArrayList<PVector>>();
for (int rotaryCnt = 0; rotaryCnt < rotaryMax; rotaryCnt++) {
float rotation = map(rotaryCnt, 0, rotaryMax, 0.0, TWO_PI);
ArrayList<PVector> p = new ArrayList<PVector>();
shapes.add(calcRotary(
0,
p,
cycleRadius * cos(rotation * cycleMax),
cycleRadius * sin(rotation * cycleMax),
width,
rotation + phase
));
}
// size adjust
float minX = width * 10.0;
float minY = height * 10.0;
float maxX = -minX;
float maxY = -minY;
for (ArrayList<PVector> shape : shapes) {
for (PVector v : shape) {
minX = min(minX, v.x);
minY = min(minY, v.y);
maxX = max(maxX, v.x);
maxY = max(maxY, v.y);
}
}
float adjust = 0.7 * min(width / (maxX - minX), height / (maxY - minY));
for (ArrayList<PVector> shape : shapes) {
for (PVector v : shape) {
v.mult(adjust);
}
}
return shapes;
}
/**
* calcRotary : calculate rotary movement recursively
*/
ArrayList<PVector> calcRotary(int _cnt, ArrayList<PVector> _p, float x, float y, float d, float r){
if (_cnt > 10) {
return _p;
}
float nx = d * cos(r) + x;
float ny = d * sin(r) + y;
_p.add(new PVector(nx, ny));
_p = calcRotary(_cnt + 1, _p, nx, ny, d * 0.75, -r * 5.0);
return _p;
}
/**
* scabrous : draw scabrous surface
*/
void scabrous(PGraphics _p, float _hue) {
for (int x = 0; x < width * 0.5; x += 3) {
for (int y = 0; y < height * 0.5; y += 3) {
float pSiz = random(0.5, 1.0);
float pDiv = random(-2.0, 2.0);
float pSat = 0.0;
if ((x + y) % 3 == 0) {
pSat = 80.0;
}
_p.strokeWeight(pSiz);
_p.stroke((_hue + 240.0) % 360.0, pSat, 30.0, 100.0);
_p.point(x + pDiv, y + pDiv);
_p.point(-x + pDiv, y + pDiv);
_p.point(x + pDiv, -y + pDiv);
_p.point(-x + pDiv, -y + pDiv);
}
}
}
/**
* lattice : draw lattice
*/
void lattice(PGraphics _p, float _hue) {
for (int x = -width; x < width * 0.5; x += 50) {
for (int y = -height; y < height * 0.5; y += 80) {
_p.strokeWeight(random(10.0));
_p.stroke((_hue + 210.0) % 360.0, random(5.0), 60.0, 100.0);
_p.line(x - 50.0, y, x + 50.0, y);
_p.line(x, y - 80.0, x, y + 80.0);
}
}
}
/**
* drawShape : draw foreground shape
*/
void drawShape(PGraphics _p, float _hue, ArrayList<ArrayList<PVector>> _shapes) {
_p.noFill();
for (ArrayList<PVector> shape : _shapes) {
// outer rim
_p.blendMode(BLEND);
_p.stroke(0.0, 0.0, 0.0, 100.0);
_p.beginShape();
for (PVector v : shape) {
float d = dist(0.0, 0.0, v.x, v.y);
_p.strokeWeight(3.0 + d * 40.0 / width);
_p.curveVertex(v.x, v.y);
}
_p.endShape();
_p.blendMode(BLEND);
_p.stroke((_hue + 120.0) % 360.0, 20.0, 30.0, 100.0);
_p.beginShape();
for (PVector v : shape) {
float d = dist(0.0, 0.0, v.x, v.y);
_p.strokeWeight(1.0 + d * 40.0 / width);
_p.curveVertex(v.x, v.y);
}
_p.endShape();
// inner rim
_p.blendMode(BLEND);
_p.beginShape();
for (PVector v : shape) {
float d = dist(0.0, 0.0, v.x, v.y);
if (d > width * 0.2) {
_p.stroke(0.0, 0.0, 0.0, 100.0);
_p.strokeWeight(2.0 + d * 10.0 / width);
} else {
_p.noStroke();
}
_p.curveVertex(v.x, v.y);
}
_p.endShape();
// center hole to see the background
_p.blendMode(REPLACE);
_p.stroke(0.0, 0.0, 0.0, 0.0);
_p.beginShape();
for (PVector v : shape) {
float d = dist(0.0, 0.0, v.x, v.y);
_p.strokeWeight(1.0 + d * 10.0 / width);
_p.curveVertex(v.x, v.y);
}
_p.endShape();
}
}
/**
* drawBubbles : draw circle packing around the shape
*/
void drawBubbles(PGraphics _p, float _hue, ArrayList<ArrayList<PVector>> _shapes) {
// circle packing around the shape
ArrayList<Circle> avoids = new ArrayList<Circle>();
for (ArrayList<PVector> points : _shapes) {
for (PVector v : points) {
float d = dist(0.0, 0.0, v.x, v.y);
avoids.add(new Circle(v.x, v.y, 20.0 + 0.2 * d));
}
}
ArrayList<Circle> packed = circlePacking(avoids, 5.0);
// circle packing background
_p.blendMode(BLEND);
_p.noStroke();
for (Circle c : packed) {
float dRatio = dist(0.0, 0.0, c.x, c.y) / max(width, height);
float eR = c.r * (60.0 / (10.0 + c.r)) * (1.0 + dRatio);
float eBri = 20.0 + 80.0 * constrain(dRatio, 0.3, 1.0);
_p.fill((_hue + 150.0) % 360.0, 20.0, eBri, 100.0);
_p.ellipse(c.x, c.y, eR, eR);
}
// draw packed circles hole
_p.blendMode(REPLACE);
_p.stroke(0.0, 0.0, 0.0, 100.0);
for (Circle c : packed) {
_p.fill(0.0, 0.0, 80.0, random(30.0, 100.0));
_p.strokeWeight(c.r * 0.1);
if (random(1.0) < 0.3) {
_p.rect(c.x, c.y, c.r, c.r);
} else {
_p.ellipse(c.x, c.y, c.r, c.r);
}
}
}
/**
* casing : draw fancy casing
*/
private void casing() {
pushMatrix();
translate(width * 0.5, height * 0.5);
fill(0.0, 0.0, 0.0, 0.0);
strokeWeight(44.0);
stroke(0.0, 0.0, 0.0, 100.0);
rect(0.0, 0.0, width, height);
strokeWeight(40.0);
stroke(0.0, 0.0, 100.0, 100.0);
rect(0.0, 0.0, width, height);
popMatrix();
}
/**
* circlePacking : bloom circles with the Circle Packing method.
* @param _av : circles to avoid.
* @param _gap : gap between circles.
*/
private ArrayList<Circle> circlePacking(ArrayList<Circle> _av, float _gap) {
int tryMax = 100; // a trying count to add and grow circles.
float growMax = 30.0;
ArrayList<Circle> circles = new ArrayList<Circle>();
for (int tryCnt = 0; tryCnt < tryMax; tryCnt++) {
// add new circles on the grid
for (int i = 0; i < 1000; i++) {
float addX = floor(random(-50.0, 50.0)) * 0.01 * width;
float addY = floor(random(-50.0, 50.0)) * 0.01 * height;
boolean inner = false;
for (Circle c : circles) {
if (dist(addX, addY, c.x, c.y) < c.r + _gap) {
inner = true;
break;
}
}
for (Circle c : _av) {
if (dist(addX, addY, c.x, c.y) < c.r + _gap * 2.0) {
inner = true;
break;
}
}
if (!inner) {
circles.add(new Circle(addX, addY, 0.0));
}
}
// grow circles
for (Circle cThis : circles) {
if (cThis.r < growMax) {
int collision = 0;
for (Circle cThat : circles) {
if (cThis != cThat) {
if (dist(cThis.x, cThis.y, cThat.x, cThat.y) < (cThis.r + cThat.r) * 0.5 + _gap) {
collision++;
}
}
}
for (Circle cAvoid : _av) {
if (dist(cThis.x, cThis.y, cAvoid.x, cAvoid.y) < (cThis.r + cAvoid.r) * 0.5 + _gap) {
collision++;
}
}
if (collision == 0) {
cThis.grow();
}
}
}
}
return circles;
}
/**
* Circle : draw and hold location, size and color.
*/
public class Circle {
private float x, y; // coordinate
private float r; // radius
Circle(float _x, float _y, float _r) {
x = _x;
y = _y;
r = _r;
}
public void grow() {
r++;
}
}
/*
Copyright (C) 2021- 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/>
*/