Newer
Older
int stepsPerSecond = 30;
float maxEnergy = 20;
float minEnergy = 4;
float minSaturation = 10;
float maxSaturation = 100;
float energyChangeRate = 0.01;
float hueShift = (100.0 / 2) + 1.2;
float alpha = 10;
float energyExp = 3.5;
OPC opc;
TriangleGrid triangle;
// Current snake
float currentHue;
int currentCell;
// Per-cell hue and energy
float[] cellHues;
int[] cellEnergies;
// Step counter
int stepNumber = 0;
void setup()
{
size(200, 200);
// Pacing
frameRate(stepsPerSecond);
// Connect to the local instance of fcserver. You can change this line to connect to another computer's fcserver
opc = new OPC(this, "127.0.0.1", 7890);
// Map our triangle grid to the center of the window
triangle = new TriangleGrid();
triangle.grid16();
triangle.mirror();
triangle.rotate(radians(60));
triangle.scale(height * 0.2);
triangle.translate(width * 0.5, height * 0.57);
triangle.leds(opc, 0);
// Initialize cells
cellHues = new float[triangle.cells.length];
cellEnergies = new int[triangle.cells.length];
currentHue = random(100);
currentCell = int(random(triangle.cells.length));
// Hide LED location dots, so we can alpha blend against the previous frame without
// these dots stomping on the pixel data we care about.
opc.showLocations(false);
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
colorMode(HSB, 100);
}
int chooseMaximum(float[] scores)
{
// Return the index of the largest nonzero member of 'scores'.
// Returns -1 if all members are <= 0.
int result = -1;
float best = 0;
for (int i = 0; i < scores.length; i++) {
if (scores[i] > best) {
result = i;
best = scores[i];
}
}
return result;
}
int chooseNeighbor(int current)
{
// Look for a neighboring cell to move to. Each neighbor gets a
// score, either a random positive number or zero if it's unsuitable.
float[] scores;
scores = new float[3];
for (int i = 0; i < scores.length; i++) {
int neighbor = triangle.cells[current].neighbors[i];
if (neighbor >= 0 && cellEnergies[neighbor] == 0) {
scores[i] = random(1, 2);
}
}
int neighbor = chooseMaximum(scores);
if (neighbor < 0) {
return -1;
}
return triangle.cells[current].neighbors[neighbor];
}
int chooseEmpty()
{
// Look for a random empty cell
float[] scores;
scores = new float[triangle.cells.length];
for (int i = 0; i < scores.length; i++) {
if (cellEnergies[i] == 0) {
scores[i] = random(1, 2);
}
}
return chooseMaximum(scores);
}
void draw()
{
stepNumber++;
// Cell energies vary in sinusoidal epochs, causing the shape of the snakes
// to shift accordingly as the space becomes more or less fragmented.
float e = map(cos(stepNumber * energyChangeRate), 1, -1, 0, 1);
e = pow(e, energyExp);
int currentEnergy = int(map(e, 0, 1, minEnergy, maxEnergy));
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// Each live cell decays by one step, no cells can live longer than currentEnergy.
// This creates a cadence to the life of each snake, and periodic waves of extinction.
for (int i = 0; i < triangle.cells.length; i++) {
cellEnergies[i] = max(0, min(currentEnergy, cellEnergies[i] - 1));
}
// Can we keep going?
if (currentCell >= 0) {
currentCell = chooseNeighbor(currentCell);
}
if (currentCell < 0) {
// New snake
currentCell = chooseEmpty();
if (currentCell >= 0) {
// Hues rotate between three complementary colors, shifting slowly around the color wheel.
currentHue = (currentHue + hueShift) % 100;
}
}
if (currentCell >= 0) {
// We have somewhere to go.
cellEnergies[currentCell] = currentEnergy;
cellHues[currentCell] = currentHue;
}
// Overall saturation shows the current energy level. Extinction periods (low currentEnergy)
// correspond with flashes of white.
float saturation = map(currentEnergy, minEnergy, maxEnergy, minSaturation, maxSaturation);
// Draw the current state of each cell
for (int i = 0; i < triangle.cells.length; i++) {
float size = height * 0.1;
fill(cellHues[i], saturation, map(cellEnergies[i], 0, currentEnergy, 0, 100), alpha);
ellipse(triangle.cells[i].center.x, triangle.cells[i].center.y, size, size);
}
}