An abyssal luminescent creature swiming silently
Creative coding creates a creature.
It's a creative coding animation made with 'Processing'. It draws an imitate abyssal luminescent creature that is investigating the distribution of plankton.
I used a formula similar to the De Jong attractor formula to calculate the shape and the movement of this creature. The marine snow background is the same code with 'Aquarius'.
I found that I can change stroke width and color if I use size(..., ..., P2D). And I also found that I can't use the ellipse() between beginShape() and endShape(). If I use it, this code stop to draw. I can't understand this behavior. 😟
'Processing' code example.
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.
/**
* Silent Sea.
* An abyssal luminescent creature is investigating the distribution of plankton.
*
* Processing 3.5.3
* @author @deconbatch
* @version 0.1
* created 0.1 2020.02.22
*/
void setup() {
size(720, 720, P2D);
colorMode(HSB, 360, 100, 100, 100);
blendMode(ADD);
smooth();
}
void draw() {
int frmMax = 24 * 10;
int starMax = 500;
int barMax = 8;
int armMax = 10;
float baseHue = random(360);
PVector stars[] = new PVector[starMax];
Joint joints[] = new Joint[barMax * armMax];
// creature's DNA
Dna dna = new Dna();
float[] rndDna = dna.getRandomDna();
float djA = rndDna[0] * TWO_PI;
float djB = rndDna[1] * TWO_PI;
float djC = rndDna[2] * TWO_PI;
float djD = rndDna[3] * TWO_PI;
// make marine snow field
for (int lNum = 0; lNum < starMax; lNum++) {
if (lNum < starMax * 0.5) {
float sY = map(lNum, 0, starMax * 0.5, -height * 0.5, height * 1.5);
stars[lNum] = new PVector(random(-width * 0.5, width * 1.5), sY);
} else {
float sX = map(lNum, starMax * 0.5, starMax, -width * 0.5, width * 1.5);
stars[lNum] = new PVector(sX, random(-height * 0.5, height * 1.5));
}
}
translate(width * 0.5, height * 0.6);
for (int frmCnt = 0; frmCnt < frmMax; ++frmCnt) {
background((baseHue + 180.0) % 360.0, 100, 60, 100);
float frmRate = map(frmCnt, 0, frmMax, 0.0, 1.0);
float easeRate = easeInOutCubic(frmRate);
// draw marine snow field
noStroke();
for (int starCnt = 0; starCnt < starMax; starCnt++) {
float stRate = map(starCnt, 0, starMax, 0.0, 1.0);
float stNoise = map(noise(stRate), 0.0, 1.0, -1.0, 1.0);
float frNoise = noise(frmRate);
float esNoise = noise(easeRate);
float coNoise = map(noise(stars[starCnt].x / width, stars[starCnt].y / height), 0.0, 1.0, -1.0, 1.0);
float szRate = sin(TWO_PI * (stNoise * 2.0 + frNoise * 3.0));
stars[starCnt].x += cos(TWO_PI * stRate) * 0.5 + coNoise + stNoise - frNoise + easeRate * easeRate * 2.0;
stars[starCnt].y += sin(TWO_PI * stRate) * 0.5 + coNoise + stNoise - frNoise + easeRate * 1.2;
float sSiz = map(szRate, -1.0, 1.0, 2.0, 8.0);
float sBri = map(szRate, -1.0, 1.0, 100.0, 5.0);
fill((baseHue + 0.0 + stRate * 60.0) % 360.0, 60.0, sBri, 100.0);
ellipse(stars[starCnt].x, stars[starCnt].y, sSiz, sSiz);
}
// make creature
for (int armCnt = 0; armCnt < armMax; ++armCnt) {
float armRate = map(armCnt, 0, armMax, 0.0, 1.0);
float time = (frmRate + armRate) * TWO_PI;
float prevX = 0.0;
float prevY = 0.0;
float currX = 0.0;
float currY = 0.0;
for (int barCnt = 0; barCnt < barMax; ++barCnt) {
float barRate = map(barCnt, 0, barMax, 0.0, 1.0);
// imitation De Jong attractor
currX = sin((djA + sin(time) * 0.00005) * prevY) - cos((djB + cos(time) * 0.00005) * prevX);
currY = cos((djC + sin(time) * 0.00005) * prevX) - sin((djD + cos(time) * 0.00005) * prevY);
joints[barCnt + armCnt * barMax] = new Joint(
currX * width * 0.1,
currY * height * 0.1,
armRate,
barRate
);
prevX = currX;
prevY = currY;
}
}
// draw creature
// I can't draw curveVertex and ellipse at the same time
pushMatrix();
translate(width * (0.3 - frmRate), -height * easeRate * 0.25);
noFill();
beginShape();
for (Joint p : joints) {
strokeWeight(2.0 + p.barRate * 5.0);
stroke(
(baseHue + p.barRate * 120.0) % 360,
map(p.armRate, 0.0, 1.0, 40.0, 90.0),
map(p.armRate, 0.0, 1.0, 40.0, 5.0),
100
);
curveVertex(
p.x,
p.y
);
}
endShape();
noStroke();
for (Joint p : joints) {
float eSize = p.barRate * 8.0;
fill(
(baseHue + p.barRate * 60.0) % 360,
map(p.armRate, 0.0, 1.0, 90.0, 40.0),
map(p.armRate, 0.0, 1.0, 10.0, 40.0),
100
);
ellipse(
p.x,
p.y,
eSize,
eSize
);
}
popMatrix();
saveFrame("frames/" + String.format("%04d", frmCnt) + ".png");
}
exit();
}
/**
* 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;
}
/**
* Dna : hold creature's DNAs
* a random DNS convergent sometimes. so I choose a "nice" DNA from this dnaList.
*/
class Dna {
ArrayList<float[]> dnaList;
Dna() {
float[][] dnas = {
// Big Oval
{1.182705, 0.96390736, 1.9748241, -0.812695},
{1.0139189, -1.6474402, -1.8250722, -1.7590555},
{-0.6682879, 1.1899773, -1.0501943, 0.97608304},
{-0.6992879, -1.4193265, -1.2935817, -1.3413115},
{1.9872496, -1.2223938, 0.75760174, 0.8577473},
{1.7736316, -0.99833524, 1.2907677, -1.2401295},
{1.2138364, 1.8424428, 1.0873783, -1.5026784},
{-1.0097787, -1.7620895, -1.0556421, -1.9306729},
{0.35385942, -1.1933832, -1.7852755, -1.5394485},
{-1.9807187, -0.98512995, 0.90511715, -1.1383728},
{1.4183311, -0.7739556, 0.8932247, -1.6508615},
{-0.6002734, 1.6509838, 1.8099892, -1.0964627},
{-1.0212829, 1.8065041, -1.065195, -1.2893174},
{-0.39753437, -1.8430407, -1.5989869, 1.97157},
// Big Round
{1.8544704, -0.5590235, 0.8619487, 1.7108389},
{-1.2797397, -0.87958217, -0.67786217, 1.278327},
// Big Rect
{-1.5126178, 0.96751165, -1.4654857, 0.39162695},
{-0.6, -1.4539845, -0.2113738, -1.7798028},
{-1.882327, 0.7454052, -1.8220787, 0.59406745},
{1.0037864, 1.04777, 0.3759683, -1.353185},
};
dnaList = new ArrayList<float[]>();
for (float[] dna : dnas) {
dnaList.add(dna);
};
}
float[] getRandomDna() {
float[] dnaChose = dnaList.get(floor(random(dnaList.size())));
return dnaChose;
}
}
/**
* Joint : hold creature's joint
*/
private class Joint {
public float x, y;
public float armRate;
public float barRate;
Joint(float _x, float _y, float _armRate, float _barRate) {
x = _x;
y = _y;
armRate = _armRate;
barRate = _barRate;
}
}
/*
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/>
*/