2D Game of Life update.

Added crc16().
Reduced errorToast popup time.
Fixed drawLine() incorrect uint16_t use and added leds[] support.
This commit is contained in:
Blaz Kristan 2022-05-20 14:48:40 +02:00
parent d8be286831
commit ba3555a66f
8 changed files with 1843 additions and 1846 deletions

View File

@ -4311,10 +4311,11 @@ uint16_t WS2812FX::mode_2DBlackHole(void) { // By: Stepko https://edi
// blur everything a bit // blur everything a bit
blur2d(leds, 16); blur2d(leds, 16);
for (y = 0; y < h; y++) for (x = 0; x < w; x++) { // for (y = 0; y < h; y++) for (x = 0; x < w; x++) {
uint16_t o = x + y * w; // uint16_t o = x + y * w;
setPixelColorXY(x, y, leds[o]); // setPixelColorXY(x, y, leds[o]);
} // }
setPixels(leds);
return FRAMETIME; return FRAMETIME;
} // mode_2DBlackHole() } // mode_2DBlackHole()
@ -4551,114 +4552,95 @@ typedef struct ColorCount {
int8_t count; int8_t count;
} colorCount; } colorCount;
static unsigned long resetMillis; //triggers reset if more than 3 seconds from now
uint16_t WS2812FX::mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ and https://github.com/DougHaber/nlife-color uint16_t WS2812FX::mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ and https://github.com/DougHaber/nlife-color
if (!isMatrix) return mode_static(); // not a 2D set-up if (!isMatrix) return mode_static(); // not a 2D set-up
uint16_t width = SEGMENT.virtualWidth(); const uint16_t width = SEGMENT.virtualWidth();
uint16_t height = SEGMENT.virtualHeight(); const uint16_t height = SEGMENT.virtualHeight();
uint16_t dataSize = sizeof(CRGB) * width * height; const uint16_t dataSize = sizeof(CRGB) * width * height;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize*2)) return mode_static(); //allocation failed
CRGB *leds = reinterpret_cast<CRGB*>(SEGENV.data); CRGB *leds = reinterpret_cast<CRGB*>(SEGENV.data);
CRGB *prevLeds = reinterpret_cast<CRGB*>(SEGENV.data) + dataSize;
if (SEGENV.call == 0) fill_solid(leds, CRGB::Black); CRGB backgroundColor = SEGCOLOR(1);
//slow down based on speed parameter if (SEGENV.call == 0 || now - resetMillis > 20000) {
if (now - SEGENV.step >= ((255-SEGMENT.speed)*4U)) { resetMillis = now;
SEGENV.step = now;
//array of patterns. Needed to identify repeating patterns. A pattern is one iteration of leds, without the color (on/off only) random16_set_seed(now); //seed the random generator
const int patternsSize = (width + height) * 2; //seems to be a good value to catch also repetition in moving patterns
unsigned long long patterns[patternsSize];
CRGB backgroundColor = SEGCOLOR(1); //give the leds random state and colors (based on intensity, colors from palette or all posible colors are chosen)
for (int x = 0; x < width; x++) for (int y = 0; y < height; y++) {
static unsigned long resetMillis = now - 3000; //triggers reset if more than 3 seconds from now uint8_t state = random8()%2;
if (state == 0)
if (SEGENV.call == 0) { //effect starts leds[XY(x,y)] = backgroundColor;
for (int i=0; i<patternsSize; i++) patterns[i] = 0; else
leds[XY(x,y)] = (CRGB)color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 0);
} }
//reset leds if effect repeats (wait 3 seconds after repetition) fill_solid(prevLeds, CRGB::Black);
if (now - resetMillis > 3000) {
resetMillis = now;
random16_set_seed(now); //seed the random generator SEGENV.aux1 = 0;
SEGENV.aux0 = 0xFFFF;
}
//give the leds random state and colors (based on intensity, colors from palette or all posible colors are chosen) //copy previous leds (save previous generation)
for (int x = 0; x < width; x++) for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) for (int y = 0; y < height; y++) prevLeds[XY(x,y)] = leds[XY(x,y)];
uint8_t state = random8()%2;
if (state == 0)
leds[XY(x,y)] = backgroundColor;
else
leds[XY(x,y)] = SEGMENT.intensity < 128?(CRGB)color_wheel(random8()):CRGB(random8(), random8(), random8());
}
//init patterns //calculate new leds
SEGENV.aux0 = 0; //ewowi20210629: pka static! patternsize: round robin index of next slot to add pattern for (int x = 0; x < width; x++) for (int y = 0; y < height; y++) {
for (int i=0; i<patternsSize; i++) patterns[i] = 0; colorCount colorsCount[9];//count the different colors in the 9*9 matrix
for (int i=0; i<9; i++) colorsCount[i] = {backgroundColor, 0}; //init colorsCount
} else { //iterate through neighbors and count them and their different colors
int neighbors = 0;
for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) { //iterate through 9*9 matrix
// wrap around segment
int16_t xx = x+i, yy = y+j;
if (x+i < 0) xx = width-1; else if (x+i >= width) xx = 0;
if (y+j < 0) yy = height-1; else if (y+j >= height) yy = 0;
uint16_t xy = XY(xx, yy); // previous cell xy to check
//calculate new leds // count different neighbours and colors, except the centre cell
for (int x = 1; x < width-1; x++) for (int y = 1; y < height-1; y++) { if (xy != XY(x,y) && prevLeds[xy] != backgroundColor) {
colorCount colorsCount[9];//count the different colors in the 9*9 matrix neighbors++;
for (int i=0; i<9; i++) colorsCount[i] = {backgroundColor, 0}; //init colorsCount bool colorFound = false;
int k;
//iterate through neighbors and count them and their different colors for (k=0; k<9 && colorsCount[i].count != 0; k++)
int neighbors = 0; if (colorsCount[k].color == prevLeds[xy]) {
for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) { //iterate through 9*9 matrix colorsCount[k].count++;
uint16_t xy = XY(x+i, y+j); //cell xy to check colorFound = true;
// count different neighbours and colors, except the centre cell
if (xy != XY(x,y) && leds[xy] != backgroundColor) {
neighbors++;
bool colorFound = false;
int i;
for (i=0; i<9 && colorsCount[i].count != 0; i++)
if (colorsCount[i].color == leds[xy]) {
colorsCount[i].count++;
colorFound = true;
}
if (!colorFound) colorsCount[i] = {leds[xy], 1}; //add new color found in the array
} }
} // i,j
// Rules of Life if (!colorFound) colorsCount[k] = {prevLeds[xy], 1}; //add new color found in the array
if ((leds[XY(x,y)] != backgroundColor) && (neighbors < 2)) leds[XY(x,y)] = backgroundColor; // Loneliness }
else if ((leds[XY(x,y)] != backgroundColor) && (neighbors > 3)) leds[XY(x,y)] = backgroundColor; // Overpopulation } // i,j
else if ((leds[XY(x,y)] == backgroundColor) && (neighbors == 3)) { // Reproduction
//find dominantcolor and assign to cell
colorCount dominantColorCount = {backgroundColor, 0};
for (int i=0; i<9 && colorsCount[i].count != 0; i++)
if (colorsCount[i].count > dominantColorCount.count) dominantColorCount = colorsCount[i];
if (dominantColorCount.count > 0) leds[XY(x,y)] = dominantColorCount.color; //assign the dominant color
}
// else do nothing!
} //x,y
//create new pattern // Rules of Life
unsigned long long pattern = 0; if ((leds[XY(x,y)] != backgroundColor) && (neighbors < 2)) leds[XY(x,y)] = backgroundColor; // Loneliness
for (int x = 0; x < width; x+=MAX(width/8,1)) for (int y = 0; y < height; y+=MAX(height/8,1)) else if ((leds[XY(x,y)] != backgroundColor) && (neighbors > 3)) leds[XY(x,y)] = backgroundColor; // Overpopulation
pattern = (pattern<<1) | !(leds[XY(x,y)] == backgroundColor); else if ((leds[XY(x,y)] == backgroundColor) && (neighbors == 3)) { // Reproduction
//find dominantcolor and assign to cell
colorCount dominantColorCount = {backgroundColor, 0};
for (int i=0; i<9 && colorsCount[i].count != 0; i++)
if (colorsCount[i].count > dominantColorCount.count) dominantColorCount = colorsCount[i];
if (dominantColorCount.count > 0) leds[XY(x,y)] = dominantColorCount.color; //assign the dominant color
}
// else do nothing!
} //x,y
//check if repetition of patterns occurs // calculate CRC16 of leds[]
bool repetition = false; uint16_t crc = crc16((const unsigned char*)leds, dataSize);
for (int i=0; i<patternsSize && !repetition; i++) // check if we had same CRC and reset if needed
repetition = patterns[(SEGENV.aux0 - 1 - i + patternsSize)%patternsSize] == pattern; if (!(crc == SEGENV.aux0 || crc == SEGENV.aux1)) resetMillis = now; //if no repetition avoid reset
// remeber last two
SEGENV.aux1 = SEGENV.aux0;
SEGENV.aux0 = crc;
//add current pattern to array and increase index (round robin) setPixels(leds);
patterns[SEGENV.aux0] = pattern; return FRAMETIME_FIXED * (128-(SEGMENT.speed>>1)); // update only when appropriate time passes (in 42 FPS slots)
SEGENV.aux0 = (++SEGENV.aux0)%patternsSize;
if (!repetition) resetMillis = now; //if no repetition avoid reset
} //not reset
setPixels(leds);
} //millis
return FRAMETIME;
} // mode_2Dgameoflife() } // mode_2Dgameoflife()
///////////////////////// /////////////////////////

View File

@ -920,10 +920,11 @@ class WS2812FX {
fadeToBlackBy(CRGB* leds, uint8_t fadeBy), fadeToBlackBy(CRGB* leds, uint8_t fadeBy),
nscale8(CRGB* leds, uint8_t scale), nscale8(CRGB* leds, uint8_t scale),
setPixels(CRGB* leds), setPixels(CRGB* leds),
drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,CRGB c); drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, CRGB *leds = nullptr);
inline void setPixelColorXY(uint16_t x, uint16_t y, uint32_t c) { setPixelColorXY(x, y, byte(c>>16), byte(c>>8), byte(c), byte(c>>24)); } inline void setPixelColorXY(uint16_t x, uint16_t y, uint32_t c) { setPixelColorXY(x, y, byte(c>>16), byte(c>>8), byte(c), byte(c>>24)); }
inline void setPixelColorXY(uint16_t x, uint16_t y, CRGB &c) { setPixelColorXY(x, y, c.red, c.green, c.blue); } inline void setPixelColorXY(uint16_t x, uint16_t y, CRGB c) { setPixelColorXY(x, y, c.red, c.green, c.blue); }
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) { drawLine(x0, y0, x1, y1, CRGB(byte(c>>16), byte(c>>8), byte(c))); }
uint16_t uint16_t
XY(uint16_t, uint16_t), XY(uint16_t, uint16_t),

View File

@ -334,13 +334,13 @@ void WS2812FX::setPixels(CRGB* leds) {
} }
//line function //line function
void WS2812FX::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,CRGB c) { void WS2812FX::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, CRGB *leds) {
int16_t dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
uint16_t dx = abs(x1-x0), sx = x0<x1 ? 1 : -1; int16_t dy = abs(y1-y0), sy = y0<y1 ? 1 : -1;
uint16_t dy = abs(y1-y0), sy = y0<y1 ? 1 : -1; int16_t err = (dx>dy ? dx : -dy)/2, e2;
uint16_t err = (dx>dy ? dx : -dy)/2, e2; for (;;) {
for(;;){ if (leds == nullptr) setPixelColorXY(x0,y0,c);
setPixelColorXY(x0,y0,c); else leds[XY(x0,y0)] = c;
if (x0==x1 && y0==y1) break; if (x0==x1 && y0==y1) break;
e2 = err; e2 = err;
if (e2 >-dx) { err -= dy; x0 += sx; } if (e2 >-dx) { err -= dy; x0 += sx; }

View File

@ -1443,7 +1443,7 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"2D Matrix@Falling speed,Spawning rate,Trail,Custom color;Spawn,Trail;", "2D Matrix@Falling speed,Spawning rate,Trail,Custom color;Spawn,Trail;",
"2D Akemi@Color speed,Dance;Head palette,Arms & Legs,Eyes & Mouth;Face palette", "2D Akemi@Color speed,Dance;Head palette,Arms & Legs,Eyes & Mouth;Face palette",
"2D Colored Bursts@Speed,Number of lines;;!", "2D Colored Bursts@Speed,Number of lines;;!",
"2D Game Of Life@!,Palette;!,!;!", "2D Game Of Life@!,;!,!;!",
"2D Julia@,Max iterations per pixel,X center,Y center,Area size;;!", "2D Julia@,Max iterations per pixel,X center,Y center,Area size;;!",
"2D Metaballs@Speed;!,!,!;!", "2D Metaballs@Speed;!,!,!;!",
"2D Noise@Speed,Scale;!,!,!;!", "2D Noise@Speed,Scale;!,!,!;!",

View File

@ -304,7 +304,7 @@ function showErrorToast()
showToast('Connection to light failed!', true); showToast('Connection to light failed!', true);
} }
function clearErrorToast(n=10000) function clearErrorToast(n=5000)
{ {
var x = gId("toast"); var x = gId("toast");
if (x.classList.contains("error")) { if (x.classList.contains("error")) {
@ -943,7 +943,7 @@ function loadNodes()
return res.json(); return res.json();
}) })
.then((json)=>{ .then((json)=>{
clearErrorToast(); clearErrorToast(100);
populateNodes(lastinfo, json); populateNodes(lastinfo, json);
}) })
.catch((e)=>{ .catch((e)=>{
@ -1429,7 +1429,7 @@ function requestJson(command=null)
}) })
.then(json => { .then(json => {
lastUpdate = new Date(); lastUpdate = new Date();
clearErrorToast(); clearErrorToast(3000);
gId('connind').style.backgroundColor = "var(--c-g)"; gId('connind').style.backgroundColor = "var(--c-g)";
if (!json) { showToast('Empty response', true); return; } if (!json) { showToast('Empty response', true); return; }
if (json.success) return; if (json.success) return;

View File

@ -278,6 +278,7 @@ bool isAsterisksOnly(const char* str, byte maxLen);
bool requestJSONBufferLock(uint8_t module=255); bool requestJSONBufferLock(uint8_t module=255);
void releaseJSONBufferLock(); void releaseJSONBufferLock();
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen); uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen);
uint16_t crc16(const unsigned char* data_p, size_t length);
//wled_eeprom.cpp //wled_eeprom.cpp
void applyMacro(byte index); void applyMacro(byte index);

File diff suppressed because it is too large Load Diff

View File

@ -260,3 +260,15 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
dest[printedChars] = '\0'; dest[printedChars] = '\0';
return strlen(dest); return strlen(dest);
} }
uint16_t crc16(const unsigned char* data_p, size_t length) {
uint8_t x;
uint16_t crc = 0xFFFF;
if (!length) return 0x1D0F;
while (length--) {
x = crc >> 8 ^ *data_p++;
x ^= x>>4;
crc = (crc << 8) ^ ((uint16_t)(x << 12)) ^ ((uint16_t)(x <<5)) ^ ((uint16_t)x);
}
return crc;
}