It's a creative coding battlefield of Reaction vs. Diffusion.
An animation with a Reaction-Diffusion system.
It's a creative coding animation made with the 'Processing'. It draws the expansion of the territory.
I wanted to make some another display of Reaction-Diffusion when I was playing with the code from @pal_rex.
https://www.openprocessing.org/sketch/861742
And I had an idea to draw lines with each point that has similar attributes. I drew a line if the neighbor's point has a similar Reaction-Diffusion calculated value.
And I also changed Reaction-Diffusion feed/kill parameters to add a variation to the animation.
The 'Processing' example code.
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.
/**
* The Battle Rages On.
* One of the Reaction-Diffusion implementation.
*
* Processing 3.5.3
* @author @deconbatch
* @version 0.1
* created 0.1 2020.03.31
*/
void setup() {
size(720, 720);
colorMode(HSB, 360, 100, 100, 100);
smooth();
}
void draw() {
int frmMax = 8 * 10; // 8fps x 10sec animation
int frmChg = 12; // change per frames
ReactionDiffusion rd = new ReactionDiffusion(random(360.0), 10, 10);
// to start with proceeded frames
for (int i = 0; i < 30; i++) {
rd.calculate();
}
for (int frmCnt = 0; frmCnt < frmMax; frmCnt++) {
background(0, 0, 90, 100);
rectMode(CENTER);
rd.drawResults();
rectMode(CORNER);
casing();
saveFrame("frames/" + String.format("%04d", frmCnt) + ".png");
// to change animation style
if (frmCnt % (frmChg * 2) == 0) {
rd.randomDot();
}
if (frmCnt % frmChg == 0) {
rd.randomParam();
}
rd.calculate();
}
exit();
}
/**
* ReactionDiffusion : calculate and draw Reaction-Diffusion
*/
public class ReactionDiffusion {
float dA = 1.2;
float dB = 1.5;
float feed = 0.05;
float kill = 0.05;
float rd[][][][];
float hueBase;
int xMult;
int yMult;
int xGrid;
int yGrid;
int curr;
int prev;
int a;
int b;
/**
* ReactionDiffusion Constructor :
* @param _hueBase : drawing color.
* @param _xMult, _yMult: Reaction-Diffusion particle size.
*/
ReactionDiffusion(float _hueBase, int _xMult, int _yMult) {
hueBase = _hueBase;
xMult = _xMult;
yMult = _yMult;
xGrid = floor(width / xMult);
yGrid = floor(height / yMult);
rd = new float[xGrid][yGrid][2][2]; // x, y, a/b, current/previous
// index value of array rd
a = 0;
b = 1;
curr = 0;
prev = 1;
for (int x = 0; x < xGrid; x++) {
for (int y = 0; y < yGrid; y++) {
rd[x][y][a][curr] = 1.0;
rd[x][y][b][curr] = 0.0;
rd[x][y][a][prev] = 1.0;
rd[x][y][b][prev] = 0.0;
}
}
randomDot();
randomParam();
}
/**
* randomDot : put Reaction-Diffusion seed.
*/
public void randomDot() {
if (random(1.0) < 0.5) {
rd[floor((xGrid * 0.25))][floor((yGrid * 0.25))][a][prev] = 0.0;
rd[floor((xGrid * 0.25))][floor((yGrid * 0.25))][b][prev] = 1.0;
rd[floor((xGrid * 0.75))][floor((yGrid * 0.75))][a][prev] = 0.0;
rd[floor((xGrid * 0.75))][floor((yGrid * 0.75))][b][prev] = 1.0;
} else {
rd[floor((xGrid * 0.25))][floor((yGrid * 0.75))][a][prev] = 0.0;
rd[floor((xGrid * 0.25))][floor((yGrid * 0.75))][b][prev] = 1.0;
rd[floor((xGrid * 0.75))][floor((yGrid * 0.25))][a][prev] = 0.0;
rd[floor((xGrid * 0.75))][floor((yGrid * 0.25))][b][prev] = 1.0;
}
}
/**
* randomParam : change Reaction-Diffusion feed/kill parameters.
*/
public void randomParam () {
feed = random(0.01, 0.09);
kill = random(0.01, 0.09);
}
/**
* calculate : Reaction-Diffusion calculate.
*/
public void calculate() {
// swap
curr = (curr == 0) ? 1 : 0;
prev = (prev == 0) ? 1 : 0;
// calculate
for (int x = 1; x < xGrid - 1; x++) {
for (int y = 1; y < yGrid - 1; y++) {
float vA = rd[x][y][a][curr];
float vB = rd[x][y][b][curr];
rd[x][y][a][prev] = vA + dA * laplace(a, x, y) - vA * vB * vB + feed * (1 - vA);
rd[x][y][b][prev] = vB + dB * laplace(b, x, y) + vA * vB * vB - (kill + feed) * vB;
rd[x][y][a][prev] = constrain(rd[x][y][a][prev], 0.0, 1.0);
rd[x][y][b][prev] = constrain(rd[x][y][b][prev], 0.0, 1.0);
}
}
}
/**
* laplace : laplace equation in Reaction-Diffusion calculate.
* @param _val : value of A or B.
* @param _x, _y : coordinate.
*/
public float laplace(int _val, int _x, int _y) {
float vSum = 0.0;
vSum += rd[_x][_y][_val][curr] * -1;
vSum += rd[_x - 1][_y][_val][curr] * 0.2;
vSum += rd[_x + 1][_y][_val][curr] * 0.2;
vSum += rd[_x][_y + 1][_val][curr] * 0.2;
vSum += rd[_x][_y - 1][_val][curr] * 0.2;
vSum += rd[_x - 1][_y - 1][_val][curr] * 0.05;
vSum += rd[_x + 1][_y - 1][_val][curr] * 0.05;
vSum += rd[_x + 1][_y + 1][_val][curr] * 0.05;
vSum += rd[_x - 1][_y + 1][_val][curr] * 0.05;
return vSum;
}
/**
* drawLines : draw a line between particles that have similar attributes.
* @param _rd : attributes array.
* @param _fX, _fY : coordinate of from point.
* @param _tX, _tY : coordinate of from point.
* @param _xMult, _yMult : particle size.
* @param _diff : particle attibute value difference.
*/
public void drawLines(float[][][][] _rd, int _fX, int _fY, int _tX, int _tY, float _xMult, float _yMult, float _diff) {
if (
abs(_rd[_fX][_fY][b][prev] - _rd[_tX][_tY][b][prev]) < _diff &&
abs(_rd[_fX][_fY][b][prev] - _rd[_tX][_tY][b][prev]) < _diff
) {
line(_fX * _xMult, _fY * _yMult, _tX * _xMult, _tY * _yMult);
}
}
/**
* drawResults : draw Reaction-Diffusion calculation results.
*/
public void drawResults() {
float diff = 0.1; // attibute value difference
pushMatrix();
translate(width * 0.5 / xGrid, height * 0.5 / yGrid);
noStroke();
for (int x = 0; x < xGrid; x++) {
for (int y = 0; y < yGrid; y++) {
float fHue = map(rd[x][y][a][prev], 0.0, 1.0, -120.0, 120.0);
float fSat = map(rd[x][y][b][prev], 0.0, 1.0, 0.0, 80.0);
float fBri = map(abs(rd[x][y][a][prev] - rd[x][y][b][prev]), 0.0, 1.0, 0.0, 90.0);
fill((hueBase + 360.0 + fHue) % 360, fSat, fBri, 100);
ellipse(x * xMult, y * yMult, xMult * 0.2, yMult * 0.2);
}
}
noFill();
strokeWeight(max(xMult, yMult) * 0.3);
for (int x = 1; x < xGrid - 1; x++) {
for (int y = 1; y < yGrid; y++) {
float fHue = map(rd[x][y][a][prev], 0.0, 1.0, -60.0, 60.0);
float fSat = map(rd[x][y][b][prev], 0.0, 1.0, 20.0, 50.0);
float fBri = map(abs(rd[x][y][a][prev] - rd[x][y][b][prev]), 0.0, 1.0, 30.0, 80.0);
if (fSat > 30.0) {
stroke((hueBase + 360.0 + fHue) % 360, fSat, fBri, 100);
drawLines(rd, x, y, x - 1, y - 1, xMult, yMult, diff);
drawLines(rd, x, y, x, y - 1, xMult, yMult, diff);
drawLines(rd, x, y, x + 1, y - 1, xMult, yMult, diff);
drawLines(rd, x, y, x + 1, y, xMult, yMult, diff);
}
}
}
popMatrix();
}
}
/**
* casing : draw fancy casing
*/
private void casing() {
fill(0.0, 0.0, 0.0, 0.0);
strokeWeight(50.0);
stroke(0.0, 0.0, 30.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);
noStroke();
noFill();
}
/*
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/>
*/