diff --git a/CHANGELOG.md b/CHANGELOG.md
index c99fe417..0fc3789e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,65 @@
## WLED changelog
+#### Build 2301240
+
+- Version bump to v0.14.0-b2 "Hoshi"
+- PixelArt converter (convert any image to pixel art and display it on a matrix) (PR #3042)
+- various effect updates and optimisations
+ - added Overlay option to some effects (allows overlapping segments)
+ - added gradient text on Scrolling Text
+ - added #DDMM, #MMDD & #HHMM date and time options for Scrolling Text effect (PR #2990)
+ - deprecated: Dynamic Smooth, Dissolve Rnd, Solid Glitter
+ - optimised & enhanced loading of default values
+ - new effect: Distortion Waves (2D)
+ - 2D support for Ripple effect
+ - slower minimum speed for Railway effect
+- DMX effect mode & segment controls (PR #2891)
+- Optimisations for conditional compiles (further reduction of code size)
+- better UX with effect sliders (PR #3012)
+- enhanced support for ESP32 variants: C3, S2 & S3
+- usermod enhancements (PIR, Temperature, Battery (PR #2975), Analog Clock (PR #2993))
+- new usermod SHT (PR #2963)
+- 2D matrix set up with gaps or irregular panels (breaking change!) (PR #2892)
+- random palette smooth changes
+- hex color notations in custom palettes
+- allow more virtual buses
+- plethora of bugfixes
+
### WLED release 0.14.0-b1
#### Build 2212222
- Version bump to v0.14.0-b1 "Hoshi"
-- Full changelog TBD
+- 2D matrix support (including mapping 1D effects to 2D and 2D peek)
+- [internal] completely rewritten Segment & WS2812FX handling code
+- [internal] ability to add custom effects via usermods
+- [internal] set of 2D drawing functions
+- enhanced old and new 2D effects (metadata: default values)
+- custom palettes (up to 10; upload palette0.json, palette1.json, ...)
+- custom effect sliders and options, quick filters
+- global I2C and SPI GPIO allocation (for usermods)
+- usermod settings page enhancements (dropdown & info)
+- asynchronous preset loading (and added "pd" JSON API call for direct preset apply)
+- new usermod Boblight (PR #2917)
+- new usermod PWM Outputs (PR #2912)
+- new usermod Audioreactive
+- new usermod Word Clock Matrix (PR #2743)
+- new usermod Ping Pong Clock (PR #2746)
+- new usermod ADS1115 (PR #2752)
+- new usermod Analog Clock (PR #2736)
+- various usermod enhancements and updates
+- allow disabling pull-up resistors on buttons
+- SD card support (PR #2877)
+- enhanced HTTP API to support custom effect sliders & options (X1, X2, X3, M1, M2, M3)
+- network debug printer (PR #2870)
+- automatic UI PC mode on large displays
+- removed support for upgrading from pre-0.10 (EEPROM)
+- support for setting GPIO level when LEDs are off (RMT idle level, ESP32 only) (PR #2478)
+- Pakistan time-zone (PKT)
+- ArtPoll support
+- TM1829 LED support
+- experimental support for ESP32 S2, S3 and C3
+- general improvements and bugfixes
### WLED release 0.13.3
diff --git a/package-lock.json b/package-lock.json
index 59c4b368..026730dc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "wled",
- "version": "0.14.0-b1",
+ "version": "0.14.0-b2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index d9468756..91d4e6e3 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "wled",
- "version": "0.14.0-b1",
+ "version": "0.14.0-b2",
"description": "Tools for WLED project",
"main": "tools/cdata.js",
"directories": {
diff --git a/tools/cdata.js b/tools/cdata.js
index d01c3e35..bf5a65e8 100644
--- a/tools/cdata.js
+++ b/tools/cdata.js
@@ -220,6 +220,7 @@ function writeChunks(srcDir, specs, resultFile) {
writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index');
writeHtmlGzipped("wled00/data/simple.htm", "wled00/html_simple.h", 'simple');
+writeHtmlGzipped("wled00/data/pixart/pixart.htm", "wled00/html_pixart.h", 'pixart');
/*
writeChunks(
"wled00/data",
diff --git a/usermods/Temperature/usermod_temperature.h b/usermods/Temperature/usermod_temperature.h
index 8950f928..b55076c7 100644
--- a/usermods/Temperature/usermod_temperature.h
+++ b/usermods/Temperature/usermod_temperature.h
@@ -29,6 +29,7 @@ class UsermodTemperature : public Usermod {
bool degC = true;
// using parasite power on the sensor
bool parasite = false;
+ int8_t parasitePin = -1;
// how often do we read from sensor?
unsigned long readingInterval = USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL;
// set last reading as "40 sec before boot", so first reading is taken after 20 sec
@@ -53,6 +54,7 @@ class UsermodTemperature : public Usermod {
static const char _enabled[];
static const char _readInterval[];
static const char _parasite[];
+ static const char _parasitePin[];
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
float readDallas() {
@@ -94,12 +96,14 @@ class UsermodTemperature : public Usermod {
DEBUG_PRINTLN(F("Requesting temperature."));
oneWire->reset();
oneWire->skip(); // skip ROM
- oneWire->write(0x44,parasite); // request new temperature reading (TODO: parasite would need special handling)
+ oneWire->write(0x44,parasite); // request new temperature reading
+ if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, HIGH); // has to happen within 10us (open MOSFET)
lastTemperaturesRequest = millis();
waitingForConversion = true;
}
void readTemperature() {
+ if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET)
temperature = readDallas();
lastMeasurement = millis();
waitingForConversion = false;
@@ -175,6 +179,12 @@ class UsermodTemperature : public Usermod {
delay(25); // try to find sensor
}
}
+ if (parasite && pinManager.allocatePin(parasitePin, true, PinOwner::UM_Temperature)) {
+ pinMode(parasitePin, OUTPUT);
+ digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET)
+ } else {
+ parasitePin = -1;
+ }
} else {
if (temperaturePin >= 0) {
DEBUG_PRINTLN(F("Temperature pin allocation failed."));
@@ -321,6 +331,7 @@ class UsermodTemperature : public Usermod {
top["degC"] = degC; // usermodparam
top[FPSTR(_readInterval)] = readingInterval / 1000;
top[FPSTR(_parasite)] = parasite;
+ top[FPSTR(_parasitePin)] = parasitePin;
DEBUG_PRINTLN(F("Temperature config saved."));
}
@@ -346,6 +357,7 @@ class UsermodTemperature : public Usermod {
readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000;
readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms
parasite = top[FPSTR(_parasite)] | parasite;
+ parasitePin = top[FPSTR(_parasitePin)] | parasitePin;
if (!initDone) {
// first run: reading from cfg.json
@@ -360,12 +372,21 @@ class UsermodTemperature : public Usermod {
delete oneWire;
pinManager.deallocatePin(temperaturePin, PinOwner::UM_Temperature);
temperaturePin = newTemperaturePin;
+ pinManager.deallocatePin(parasitePin, PinOwner::UM_Temperature);
// initialise
setup();
}
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
- return !top[FPSTR(_parasite)].isNull();
+ return !top[FPSTR(_parasitePin)].isNull();
+ }
+
+ void appendConfigData()
+ {
+ oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":")); oappend(String(FPSTR(_parasite)).c_str());
+ oappend(SET_F("',1,'(if no Vcc connected)');")); // 0 is field type, 1 is actual field
+ oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":")); oappend(String(FPSTR(_parasitePin)).c_str());
+ oappend(SET_F("',1,'(for external MOSFET)');")); // 0 is field type, 1 is actual field
}
uint16_t getId()
@@ -379,3 +400,4 @@ const char UsermodTemperature::_name[] PROGMEM = "Temperature";
const char UsermodTemperature::_enabled[] PROGMEM = "enabled";
const char UsermodTemperature::_readInterval[] PROGMEM = "read-interval-s";
const char UsermodTemperature::_parasite[] PROGMEM = "parasite-pwr";
+const char UsermodTemperature::_parasitePin[] PROGMEM = "parasite-pwr-pin";
diff --git a/usermods/multi_relay/usermod_multi_relay.h b/usermods/multi_relay/usermod_multi_relay.h
index b8bd4f6e..de68e11b 100644
--- a/usermods/multi_relay/usermod_multi_relay.h
+++ b/usermods/multi_relay/usermod_multi_relay.h
@@ -184,7 +184,7 @@ class MultiRelay : public Usermod {
*/
MultiRelay() {
const int8_t defPins[] = {MULTI_RELAY_PINS};
- for (int i=0; i (255 - SEGMENT.speed) + 15U)
- {
+ if (SEGENV.step > (255 - SEGMENT.speed) + 15U) {
SEGENV.aux0 = !SEGENV.aux0;
- SEGENV.call = 0;
+ SEGENV.step = 0;
+ } else {
+ SEGENV.step++;
}
return FRAMETIME;
@@ -640,9 +643,9 @@ uint16_t dissolve(uint32_t color) {
* Blink several LEDs on and then off
*/
uint16_t mode_dissolve(void) {
- return dissolve(SEGCOLOR(0));
+ return dissolve(SEGMENT.check1 ? SEGMENT.color_wheel(random8()) : SEGCOLOR(0));
}
-static const char _data_FX_MODE_DISSOLVE[] PROGMEM = "Dissolve@Repeat speed,Dissolve speed;!,!;!";
+static const char _data_FX_MODE_DISSOLVE[] PROGMEM = "Dissolve@Repeat speed,Dissolve speed,,,,Random;!,!;!";
/*
@@ -1193,17 +1196,19 @@ uint16_t mode_fireworks() {
const uint16_t width = strip.isMatrix ? SEGMENT.virtualWidth() : SEGMENT.virtualLength();
const uint16_t height = SEGMENT.virtualHeight();
- SEGMENT.fade_out(0);
-
if (SEGENV.call == 0) {
+ SEGMENT.setUpLeds(); //lossless getPixelColor()
+ SEGMENT.fill(SEGCOLOR(1));
SEGENV.aux0 = UINT16_MAX;
SEGENV.aux1 = UINT16_MAX;
}
+ SEGMENT.fade_out(128);
+
bool valid1 = (SEGENV.aux0 < width*height);
bool valid2 = (SEGENV.aux1 < width*height);
uint32_t sv1 = 0, sv2 = 0;
- if (valid1) sv1 = strip.isMatrix ? SEGMENT.getPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width) : SEGMENT.getPixelColor(SEGENV.aux0); // TODO get spark color
- if (valid2) sv2 = strip.isMatrix ? SEGMENT.getPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width) : SEGMENT.getPixelColor(SEGENV.aux1); // TODO
+ if (valid1) sv1 = strip.isMatrix ? SEGMENT.getPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width) : SEGMENT.getPixelColor(SEGENV.aux0); // get spark color
+ if (valid2) sv2 = strip.isMatrix ? SEGMENT.getPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width) : SEGMENT.getPixelColor(SEGENV.aux1);
if (!SEGENV.step) SEGMENT.blur(16);
if (valid1) { if (strip.isMatrix) SEGMENT.setPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width, sv1); else SEGMENT.setPixelColor(SEGENV.aux0, sv1); } // restore spark color after blur
if (valid2) { if (strip.isMatrix) SEGMENT.setPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width, sv2); else SEGMENT.setPixelColor(SEGENV.aux1, sv2); } // restore old spark color after blur
@@ -1230,7 +1235,7 @@ uint16_t mode_rain()
const uint16_t width = SEGMENT.virtualWidth();
const uint16_t height = SEGMENT.virtualHeight();
SEGENV.step += FRAMETIME;
- if (SEGENV.step > SPEED_FORMULA_L) {
+ if (SEGENV.call && SEGENV.step > SPEED_FORMULA_L) {
SEGENV.step = 1;
if (strip.isMatrix) {
uint32_t ctemp[width];
@@ -1241,9 +1246,9 @@ uint16_t mode_rain()
SEGENV.aux1 = (SEGENV.aux1 % width) + (SEGENV.aux1 / width + 1) * width;
} else {
//shift all leds left
- uint32_t ctemp = SEGMENT.getPixelColor(0); // TODO
+ uint32_t ctemp = SEGMENT.getPixelColor(0);
for (int i = 0; i < SEGLEN - 1; i++) {
- SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // TODO
+ SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1));
}
SEGMENT.setPixelColor(SEGLEN -1, ctemp); // wrap around
SEGENV.aux0++; // increase spark index
@@ -1585,8 +1590,7 @@ static const char _data_FX_MODE_ICU[] PROGMEM = "ICU@!,!,,,,,Overlay;!,!;!";
/*
* Custom mode by Aircoookie. Color Wipe, but with 3 colors
*/
-uint16_t mode_tricolor_wipe(void)
-{
+uint16_t mode_tricolor_wipe(void) {
uint32_t cycleTime = 1000 + (255 - SEGMENT.speed)*200;
uint32_t perc = strip.now % cycleTime;
uint16_t prog = (perc * 65535) / cycleTime;
@@ -1628,8 +1632,7 @@ static const char _data_FX_MODE_TRICOLOR_WIPE[] PROGMEM = "Tri Wipe@!;1,2,3;!";
* Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/TriFade.h
* Modified by Aircoookie
*/
-uint16_t mode_tricolor_fade(void)
-{
+uint16_t mode_tricolor_fade(void) {
uint16_t counter = strip.now * ((SEGMENT.speed >> 3) +1);
uint32_t prog = (counter * 768) >> 16;
@@ -1672,8 +1675,7 @@ static const char _data_FX_MODE_TRICOLOR_FADE[] PROGMEM = "Tri Fade@!;1,2,3;!";
* Creates random comets
* Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/MultiComet.h
*/
-uint16_t mode_multi_comet(void)
-{
+uint16_t mode_multi_comet(void) {
uint32_t cycleTime = 10 + (uint32_t)(255 - SEGMENT.speed);
uint32_t it = strip.now / cycleTime;
if (SEGENV.step == it) return FRAMETIME;
@@ -1711,8 +1713,7 @@ static const char _data_FX_MODE_MULTI_COMET[] PROGMEM = "Multi Comet";
* Running random pixels ("Stream 2")
* Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/RandomChase.h
*/
-uint16_t mode_random_chase(void)
-{
+uint16_t mode_random_chase(void) {
if (SEGENV.call == 0) {
SEGENV.step = RGBW32(random8(), random8(), random8(), 0);
SEGENV.aux0 = random16();
@@ -1754,8 +1755,7 @@ typedef struct Oscillator {
/*
/ Oscillating bars of color, updated with standard framerate
*/
-uint16_t mode_oscillate(void)
-{
+uint16_t mode_oscillate(void) {
uint8_t numOscillators = 3;
uint16_t dataSize = sizeof(oscillator) * numOscillators;
@@ -1807,8 +1807,7 @@ static const char _data_FX_MODE_OSCILLATE[] PROGMEM = "Oscillate";
//TODO
-uint16_t mode_lightning(void)
-{
+uint16_t mode_lightning(void) {
uint16_t ledstart = random16(SEGLEN); // Determine starting location of flash
uint16_t ledlen = 1 + random16(SEGLEN -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1)
uint8_t bri = 255/random8(1, 3);
@@ -1853,8 +1852,7 @@ static const char _data_FX_MODE_LIGHTNING[] PROGMEM = "Lightning@!,!,,,,,Overlay
// Pride2015
// Animated, ever-changing rainbows.
// by Mark Kriegsman: https://gist.github.com/kriegsman/964de772d64c502760e5
-uint16_t mode_pride_2015(void)
-{
+uint16_t mode_pride_2015(void) {
uint16_t duration = 10 + SEGMENT.speed;
uint16_t sPseudotime = SEGENV.step;
uint16_t sHue16 = SEGENV.aux0;
@@ -1883,11 +1881,8 @@ uint16_t mode_pride_2015(void)
uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536;
bri8 += (255 - brightdepth);
- CRGB newcolor = CHSV( hue8, sat8, bri8);
- fastled_col = CRGB(SEGMENT.getPixelColor(i)); // TODO
-
- nblend(fastled_col, newcolor, 64);
- SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue);
+ CRGB newcolor = CHSV(hue8, sat8, bri8);
+ SEGMENT.blendPixelColor(i, newcolor, 64);
}
SEGENV.step = sPseudotime;
SEGENV.aux0 = sHue16;
@@ -1898,24 +1893,29 @@ static const char _data_FX_MODE_PRIDE_2015[] PROGMEM = "Pride 2015@!;;";
//eight colored dots, weaving in and out of sync with each other
-uint16_t mode_juggle(void){
- SEGMENT.fade_out(SEGMENT.intensity);
+uint16_t mode_juggle(void) {
+ if (SEGENV.call == 0) {
+ SEGMENT.setUpLeds(); //lossless getPixelColor()
+ SEGMENT.fill(BLACK);
+ }
+
+ SEGMENT.fadeToBlackBy(192 - (3*SEGMENT.intensity/4));
+
CRGB fastled_col;
byte dothue = 0;
for (int i = 0; i < 8; i++) {
- uint16_t index = 0 + beatsin88((128 + SEGMENT.speed)*(i + 7), 0, SEGLEN -1);
- fastled_col = CRGB(SEGMENT.getPixelColor(index)); // TODO
+ uint16_t index = 0 + beatsin88((16 + SEGMENT.speed)*(i + 7), 0, SEGLEN -1);
+ fastled_col = CRGB(SEGMENT.getPixelColor(index));
fastled_col |= (SEGMENT.palette==0)?CHSV(dothue, 220, 255):ColorFromPalette(SEGPALETTE, dothue, 255);
- SEGMENT.setPixelColor(index, fastled_col.red, fastled_col.green, fastled_col.blue);
+ SEGMENT.setPixelColor(index, fastled_col);
dothue += 32;
}
return FRAMETIME;
}
-static const char _data_FX_MODE_JUGGLE[] PROGMEM = "Juggle@!,Trail;;!;;sx=16,ix=240";
+static const char _data_FX_MODE_JUGGLE[] PROGMEM = "Juggle@!,Trail;;!;;sx=64,ix=128";
-uint16_t mode_palette()
-{
+uint16_t mode_palette() {
uint16_t counter = 0;
if (SEGMENT.speed != 0)
{
@@ -1963,43 +1963,42 @@ static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Cycle speed;;!;;c3=
// There are two main parameters you can play with to control the look and
// feel of your fire: COOLING (used in step 1 above) (Speed = COOLING), and SPARKING (used
// in step 3 above) (Effect Intensity = Sparking).
-uint16_t mode_fire_2012()
-{
- uint16_t strips = SEGMENT.nrOfVStrips();
+uint16_t mode_fire_2012() {
+ const uint16_t strips = SEGMENT.nrOfVStrips();
if (!SEGENV.allocateData(strips * SEGLEN)) return mode_static(); //allocation failed
byte* heat = SEGENV.data;
- uint32_t it = strip.now >> 5; //div 32
+ const uint32_t it = strip.now >> 6; //div 64
struct virtualStrip {
static void runStrip(uint16_t stripNr, byte* heat, uint32_t it) {
+ const uint8_t ignition = max(3,SEGLEN/10); // ignition area: 10% of segment length or minimum 3 pixels
+
+ // Step 1. Cool down every cell a little
+ for (int i = 0; i < SEGLEN; i++) {
+ uint8_t cool = (it != SEGENV.step) ? random8((((20 + SEGMENT.speed/3) * 16) / SEGLEN)+2) : random(8);
+ uint8_t minTemp = 0;
+ if (i 1; k--) {
heat[k] = (heat[k - 1] + (heat[k - 2]<<1) ) / 3; // heat[k-2] multiplied by 2
}
+ }
- // Step 3. Randomly ignite new 'sparks' of heat near the bottom
- if (random8() <= SEGMENT.intensity) {
- uint8_t y = random8(ignition);
- heat[y] = qadd8(heat[y], random8(160,255));
- }
+ // Step 3. Randomly ignite new 'sparks' of heat near the bottom
+ if (random8() <= SEGMENT.intensity) {
+ uint8_t y = random8(ignition);
+ uint8_t boost = (32+SEGMENT.custom3*2) * (2*ignition-y) / (2*ignition);
+ heat[y] = qadd8(heat[y], random8(64+boost,128+boost));
}
// Step 4. Map from heat cells to LED colors
@@ -2012,19 +2011,20 @@ uint16_t mode_fire_2012()
for (int stripNr=0; stripNr> 1));
@@ -2157,8 +2144,7 @@ uint16_t mode_noise16_2()
static const char _data_FX_MODE_NOISE16_2[] PROGMEM = "Noise 2@!;!;!";
-uint16_t mode_noise16_3()
-{
+uint16_t mode_noise16_3() {
uint16_t scale = 800; // the "zoom factor" for the noise
//CRGB fastled_col;
SEGENV.step += (1 + SEGMENT.speed);
@@ -2183,8 +2169,7 @@ static const char _data_FX_MODE_NOISE16_3[] PROGMEM = "Noise 3@!;!;!";
//https://github.com/aykevl/ledstrip-spark/blob/master/ledstrip.ino
-uint16_t mode_noise16_4()
-{
+uint16_t mode_noise16_4() {
//CRGB fastled_col;
uint32_t stp = (strip.now * SEGMENT.speed) >> 7;
for (int i = 0; i < SEGLEN; i++) {
@@ -2199,8 +2184,7 @@ static const char _data_FX_MODE_NOISE16_4[] PROGMEM = "Noise 4@!;!;!";
//based on https://gist.github.com/kriegsman/5408ecd397744ba0393e
-uint16_t mode_colortwinkle()
-{
+uint16_t mode_colortwinkle() {
uint16_t dataSize = (SEGLEN+7) >> 3; //1 bit per LED
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@@ -2353,8 +2337,7 @@ static const char _data_FX_MODE_METEOR_SMOOTH[] PROGMEM = "Meteor Smooth@!,Trail
//Railway Crossing / Christmas Fairy lights
-uint16_t mode_railway()
-{
+uint16_t mode_railway() {
uint16_t dur = (256 - SEGMENT.speed) * 40;
uint16_t rampdur = (dur * SEGMENT.intensity) >> 8;
if (SEGENV.step > dur)
@@ -2829,7 +2812,7 @@ uint16_t mode_bouncing_balls(void) {
balls[i].lastBounceTime = time;
if (balls[i].impactVelocity < 0.015f) {
- float impactVelocityStart = sqrt(-2 * gravity) * random8(5,11)/10.0f; // randomize impact velocity
+ float impactVelocityStart = sqrtf(-2.0f * gravity) * random8(5,11)/10.0f; // randomize impact velocity
balls[i].impactVelocity = impactVelocityStart;
}
} else if (balls[i].height > 1.0f) {
@@ -2987,7 +2970,7 @@ uint16_t mode_popcorn(void) {
uint16_t peakHeight = 128 + random8(128); //0-255
peakHeight = (peakHeight * (SEGLEN -1)) >> 8;
- popcorn[i].vel = sqrt(-2.0 * gravity * peakHeight);
+ popcorn[i].vel = sqrtf(-2.0f * gravity * peakHeight);
if (SEGMENT.palette)
{
@@ -3286,7 +3269,7 @@ uint16_t mode_exploding_fireworks(void)
flare->posX = strip.isMatrix ? random16(2,cols-1) : (SEGMENT.intensity > random8()); // will enable random firing side on 1D
uint16_t peakHeight = 75 + random8(180); //0-255
peakHeight = (peakHeight * (rows -1)) >> 8;
- flare->vel = sqrt(-2.0f * gravity * peakHeight);
+ flare->vel = sqrtf(-2.0f * gravity * peakHeight);
flare->velX = strip.isMatrix ? (random8(8)-4)/32.f : 0; // no X velocity on 1D
flare->col = 255; //brightness
SEGENV.aux0 = 1;
@@ -4844,7 +4827,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
const uint16_t dataSize = sizeof(CRGB) * SEGMENT.length(); // using width*height prevents reallocation if mirroring is enabled
- const uint16_t crcBufferLen = (SEGMENT.width() + SEGMENT.height())*71/100; // roughly sqrt(2)/2 for better repetition detection (Ewowi)
+ const uint16_t crcBufferLen = 2; //(SEGMENT.width() + SEGMENT.height())*71/100; // roughly sqrt(2)/2 for better repetition detection (Ewowi)
if (!SEGENV.allocateData(dataSize + sizeof(uint16_t)*crcBufferLen)) return mode_static(); //allocation failed
CRGB *prevLeds = reinterpret_cast(SEGENV.data);
@@ -4852,7 +4835,9 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
CRGB backgroundColor = SEGCOLOR(1);
- if (SEGENV.call == 0 || strip.now - SEGMENT.step > 5000) {
+ if (SEGENV.call == 0) SEGMENT.setUpLeds();
+
+ if (SEGENV.call == 0 || strip.now - SEGMENT.step > 3000) {
SEGENV.step = strip.now;
SEGENV.aux0 = 0;
random16_set_seed(millis()>>2); //seed the random generator
@@ -4868,7 +4853,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) prevLeds[XY(x,y)] = CRGB::Black;
memset(crcBuffer, 0, sizeof(uint16_t)*crcBufferLen);
- } else if (strip.now - SEGENV.step < FRAMETIME_FIXED * map(SEGMENT.speed,0,255,64,4)) {
+ } else if (strip.now - SEGENV.step < FRAMETIME_FIXED * (uint32_t)map(SEGMENT.speed,0,255,64,4)) {
// update only when appropriate time passes (in 42 FPS slots)
return FRAMETIME;
}
@@ -4879,20 +4864,22 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
//calculate new leds
for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) {
- 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
- //iterate through neighbors and count them and their different colors
+ colorCount colorsCount[9]; // count the different colors in the 3*3 matrix
+ for (int i=0; i<9; i++) colorsCount[i] = {backgroundColor, 0}; // init colorsCount
+
+ // 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
+ for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) { // iterate through 3*3 matrix
+ if (i==0 && j==0) continue; // ignore itself
// wrap around segment
int16_t xx = x+i, yy = y+j;
- if (x+i < 0) xx = cols-1; else if (x+i >= cols) xx = 0;
+ if (x+i < 0) xx = cols-1; else if (x+i >= cols) xx = 0;
if (y+j < 0) yy = rows-1; else if (y+j >= rows) yy = 0;
- uint16_t xy = XY(xx, yy); // previous cell xy to check
- // count different neighbours and colors, except the centre cell
- if (xy != XY(x,y) && prevLeds[xy] != backgroundColor) {
+ uint16_t xy = XY(xx, yy); // previous cell xy to check
+ // count different neighbours and colors
+ if (prevLeds[xy] != backgroundColor) {
neighbors++;
bool colorFound = false;
int k;
@@ -4901,22 +4888,24 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
colorsCount[k].count++;
colorFound = true;
}
-
if (!colorFound) colorsCount[k] = {prevLeds[xy], 1}; //add new color found in the array
}
} // i,j
// Rules of Life
- uint32_t col = SEGMENT.getPixelColorXY(x,y);
+ uint32_t col = prevLeds[XY(x,y)];
uint32_t bgc = RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0);
if ((col != bgc) && (neighbors < 2)) SEGMENT.setPixelColorXY(x,y, bgc); // Loneliness
else if ((col != bgc) && (neighbors > 3)) SEGMENT.setPixelColorXY(x,y, bgc); // Overpopulation
else if ((col == bgc) && (neighbors == 3)) { // Reproduction
- //find dominant color and assign to cell
+ // find dominant color and assign it to a 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) SEGMENT.setPixelColorXY(x,y, dominantColorCount.color); //assign the dominant color
+ // assign the dominant color w/ a bit of randomness to avoid "gliders"
+ if (dominantColorCount.count > 0 && random8(128)) SEGMENT.setPixelColorXY(x,y, dominantColorCount.color);
+ } else if ((col == bgc) && (neighbors == 2) && !random8(128)) { // Mutation
+ SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 255));
}
// else do nothing!
} //x,y
@@ -5310,7 +5299,7 @@ uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https
SEGENV.step = 0;
}
- float adjustHeight = (float)map(rows, 8, 32, 28, 12);
+ float adjustHeight = (float)map(rows, 8, 32, 28, 12); // maybe use mapf() ???
uint16_t adjScale = map(cols, 8, 64, 310, 63);
/*
if (SEGENV.aux1 != SEGMENT.custom1/12) { // Hacky palette rotation. We need that black.
@@ -5336,7 +5325,7 @@ uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https
SEGMENT.setPixelColorXY(x, y, ColorFromPalette(auroraPalette,
qsub8(
inoise8((SEGENV.step%2) + x * _scale, y * 16 + SEGENV.step % 16, SEGENV.step / _speed),
- fabs((float)rows / 2 - (float)y) * adjustHeight)));
+ fabsf((float)rows / 2.0f - (float)y) * adjustHeight)));
}
}
@@ -5351,7 +5340,7 @@ static const char _data_FX_MODE_2DPOLARLIGHTS[] PROGMEM = "Polar Lights@!,Scale;
uint16_t mode_2DPulser(void) { // By: ldirko https://editor.soulmatelights.com/gallery/878-pulse-test , modifed by: Andrew Tuline
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
- //const uint16_t cols = SEGMENT.virtualWidth();
+ const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
@@ -5361,8 +5350,8 @@ uint16_t mode_2DPulser(void) { // By: ldirko https://edi
SEGMENT.fadeToBlackBy(8 - (SEGMENT.intensity>>5));
- uint16_t a = strip.now / (18 - SEGMENT.speed / 16);
- uint16_t x = (a / 14);
+ uint32_t a = strip.now / (18 - SEGMENT.speed / 16);
+ uint16_t x = (a / 14) % cols;
uint16_t y = map((sin8(a * 5) + sin8(a * 4) + sin8(a * 2)), 0, 765, rows-1, 0);
SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, map(y, 0, rows-1, 0, 255), 255, LINEARBLEND));
@@ -5788,13 +5777,13 @@ uint16_t mode_2Dfloatingblobs(void) {
// change radius if needed
if (blob->grow[i]) {
// enlarge radius until it is >= 4
- blob->r[i] += (fabs(blob->sX[i]) > fabs(blob->sY[i]) ? fabs(blob->sX[i]) : fabs(blob->sY[i])) * 0.05f;
+ blob->r[i] += (fabsf(blob->sX[i]) > fabsf(blob->sY[i]) ? fabsf(blob->sX[i]) : fabsf(blob->sY[i])) * 0.05f;
if (blob->r[i] >= MIN(cols/4.f,2.f)) {
blob->grow[i] = false;
}
} else {
// reduce radius until it is < 1
- blob->r[i] -= (fabs(blob->sX[i]) > fabs(blob->sY[i]) ? fabs(blob->sX[i]) : fabs(blob->sY[i])) * 0.05f;
+ blob->r[i] -= (fabsf(blob->sX[i]) > fabsf(blob->sY[i]) ? fabsf(blob->sX[i]) : fabsf(blob->sY[i])) * 0.05f;
if (blob->r[i] < 1.f) {
blob->grow[i] = true;
}
@@ -5887,20 +5876,24 @@ uint16_t mode_2Dscrollingtext(void) {
++SEGENV.aux1 &= 0xFF; // color shift
SEGENV.step = millis() + map(SEGMENT.speed, 0, 255, 10*FRAMETIME_FIXED, 2*FRAMETIME_FIXED);
if (!SEGMENT.check2) {
- // we need it 3 times
- SEGMENT.fade_out(255 - (SEGMENT.custom1>>5)); // fade to background color
- SEGMENT.fade_out(255 - (SEGMENT.custom1>>5)); // fade to background color
- SEGMENT.fade_out(255 - (SEGMENT.custom1>>5)); // fade to background color
+ for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++ )
+ SEGMENT.blendPixelColorXY(x, y, SEGCOLOR(1), 255 - (SEGMENT.custom1>>1));
}
-}
+ }
for (int i = 0; i < numberOfLetters; i++) {
if (int(cols) - int(SEGENV.aux0) + letterWidth*(i+1) < 0) continue; // don't draw characters off-screen
- SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0));
+ uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0);
+ uint32_t col2 = BLACK;
+ if (SEGMENT.check1 && SEGMENT.palette == 0) {
+ col1 = SEGCOLOR(0);
+ col2 = SEGCOLOR(2);
+ }
+ SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, col1, col2);
}
return FRAMETIME;
}
-static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,,,Overlay;!,!;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0";
+static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,,Gradient,Overlay;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0";
////////////////////////////
@@ -6711,11 +6704,10 @@ uint16_t mode_DJLight(void) { // Written by ??? Adapted by Wil
if (SEGENV.aux0 != secondHand) { // Triggered millis timing.
SEGENV.aux0 = secondHand;
- SEGMENT.setPixelColor(mid, CRGB(fftResult[15]/2, fftResult[5]/2, fftResult[0]/2)); // 16-> 15 as 16 is out of bounds
- CRGB color = SEGMENT.getPixelColor(mid);
- SEGMENT.setPixelColor(mid, color.fadeToBlackBy(map(fftResult[1*4], 0, 255, 255, 10))); // TODO - Update
+ CRGB color = CRGB(fftResult[15]/2, fftResult[5]/2, fftResult[0]/2); // 16-> 15 as 16 is out of bounds
+ SEGMENT.setPixelColor(mid, color.fadeToBlackBy(map(fftResult[4], 0, 255, 255, 4))); // TODO - Update
- for (int i = SEGLEN - 1; i > mid; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left
+ for (int i = SEGLEN - 1; i > mid; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); // move to the left
for (int i = 0; i < mid; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right
}
@@ -6736,8 +6728,8 @@ uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN.
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
- float FFT_MajorPeak = *(float*) um_data->u_data[4];
- float my_magnitude = *(float*) um_data->u_data[5] / 4.0f;
+ float FFT_MajorPeak = *(float*)um_data->u_data[4];
+ float my_magnitude = *(float*)um_data->u_data[5] / 4.0f;
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
@@ -6770,7 +6762,7 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch
um_data = simulateSound(SEGMENT.soundSim);
}
float FFT_MajorPeak = *(float*)um_data->u_data[4];
- float volumeSmth = *(float*) um_data->u_data[0];
+ float volumeSmth = *(float*)um_data->u_data[0];
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
@@ -6807,7 +6799,7 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch
// shift the pixels one pixel up
SEGMENT.setPixelColor(0, color);
- for (int i = SEGLEN - 1; i > 0; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left
+ for (int i = SEGLEN - 1; i > 0; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left
}
return FRAMETIME;
@@ -6828,8 +6820,8 @@ uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline.
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
- float FFT_MajorPeak = *(float*) um_data->u_data[4];
- float my_magnitude = *(float*) um_data->u_data[5] / 16.0f;
+ float FFT_MajorPeak = *(float*)um_data->u_data[4];
+ float my_magnitude = *(float*)um_data->u_data[5] / 16.0f;
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
uint16_t fadeRate = 2*SEGMENT.speed - SEGMENT.speed*SEGMENT.speed/255; // Get to 255 as quick as you can.
@@ -6871,8 +6863,8 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
- float FFT_MajorPeak = *(float*) um_data->u_data[4];
- float volumeSmth = *(float*) um_data->u_data[0];
+ float FFT_MajorPeak = *(float*)um_data->u_data[4];
+ float volumeSmth = *(float*)um_data->u_data[0];
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
@@ -6933,16 +6925,16 @@ uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline.
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
- float FFT_MajorPeak = *(float*) um_data->u_data[4];
- float volumeSmth = *(float*) um_data->u_data[0];
+ float FFT_MajorPeak = *(float*)um_data->u_data[4];
+ float volumeSmth = *(float*)um_data->u_data[0];
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
SEGMENT.fade_out(250);
- float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0;
+ float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f;
segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling
- float mySampleAvg = mapf(segmentSampleAvg*2.0, 0,32, 0, (float)SEGLEN/2.0); // map to pixels availeable in current segment
+ float mySampleAvg = mapf(segmentSampleAvg*2.0f, 0,32, 0, (float)SEGLEN/2.0); // map to pixels availeable in current segment
int tempsamp = constrain(mySampleAvg,0,SEGLEN/2); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32;
@@ -7028,7 +7020,7 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac
}
frTemp -=132; // This should give us a base musical note of C3
- frTemp = fabs(frTemp * 2.1); // Fudge factors to compress octave range starting at 0 and going to 255;
+ frTemp = fabsf(frTemp * 2.1f); // Fudge factors to compress octave range starting at 0 and going to 255;
uint16_t i = map(beatsin8(8+octCount*4, 0, 255, 0, octCount*8), 0, 255, 0, SEGLEN-1);
i = constrain(i, 0, SEGLEN-1);
diff --git a/wled00/FX.h b/wled00/FX.h
index 5de4aff5..43095780 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -143,7 +143,7 @@
#define FX_MODE_SAW 16
#define FX_MODE_TWINKLE 17
#define FX_MODE_DISSOLVE 18
-#define FX_MODE_DISSOLVE_RANDOM 19
+#define FX_MODE_DISSOLVE_RANDOM 19 // candidate for removal (use Dissolve with with check 3)
#define FX_MODE_SPARKLE 20
#define FX_MODE_FLASH_SPARKLE 21
#define FX_MODE_HYPER_SPARKLE 22
@@ -227,7 +227,7 @@
#define FX_MODE_HEARTBEAT 100
#define FX_MODE_PACIFICA 101
#define FX_MODE_CANDLE_MULTI 102
-#define FX_MODE_SOLID_GLITTER 103
+#define FX_MODE_SOLID_GLITTER 103 // candidate for removal (use glitter)
#define FX_MODE_SUNRISE 104
#define FX_MODE_PHASED 105
#define FX_MODE_TWINKLEUP 106
@@ -241,7 +241,7 @@
// #define FX_MODE_CANDY_CANE 114 // removed in 0.14!
#define FX_MODE_BLENDS 115
#define FX_MODE_TV_SIMULATOR 116
-#define FX_MODE_DYNAMIC_SMOOTH 117
+#define FX_MODE_DYNAMIC_SMOOTH 117 // candidate for removal (check3 in dynamic)
// new 0.14 2D effects
#define FX_MODE_2DSPACESHIPS 118 //gap fill
@@ -593,8 +593,9 @@ typedef struct Segment {
void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c);
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c);
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0)); } // automatic inline
- void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color);
+ void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0);
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0)); } // automatic inline
+ void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0)); } // automatic inline
void wu_pixel(uint32_t x, uint32_t y, CRGB c);
void blur1d(fract8 blur_amount); // blur all rows in 1 dimension
void blur2d(fract8 blur_amount) { blur(blur_amount); }
diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp
index 17882fe8..ad7f3a10 100644
--- a/wled00/FX_2Dfcn.cpp
+++ b/wled00/FX_2Dfcn.cpp
@@ -64,6 +64,7 @@ void WS2812FX::setUpMatrix() {
Segment::maxHeight = 1;
panels = 0;
panel.clear(); // release memory allocated by panels
+ resetSegments();
return;
}
@@ -107,8 +108,8 @@ void WS2812FX::setUpMatrix() {
panel.clear();
Segment::maxWidth = _length;
Segment::maxHeight = 1;
- return;
}
+ resetSegments();
}
#else
isMatrix = false; // no matter what config says
@@ -499,13 +500,16 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
// draws a raster font character on canvas
// only supports: 4x6=24, 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM
-void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color) {
+void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2) {
if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported
chr -= 32; // align with font table entries
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
const int font = w*h;
+ CRGB col = CRGB(color);
+ CRGBPalette16 grad = CRGBPalette16(col, col2 ? CRGB(col2) : col);
+
//if (w<5 || w>6 || h!=8) return;
for (int i = 0; i= 0 || x0 < cols) && ((bits>>(j+(8-w))) & 0x01)) { // bit set & drawing on-screen
- addPixelColorXY(x0, y0, color);
+ setPixelColorXY(x0, y0, col);
}
}
}
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 147f625b..235c61d0 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -893,7 +893,7 @@ void Segment::blur(uint8_t blur_amount)
* The colours are a transition r -> g -> b -> back to r
* Inspired by the Adafruit examples.
*/
-uint32_t Segment::color_wheel(uint8_t pos) { // TODO
+uint32_t Segment::color_wheel(uint8_t pos) {
if (palette) return color_from_palette(pos, false, true, 0);
pos = 255 - pos;
if(pos < 85) {
diff --git a/wled00/data/index.css b/wled00/data/index.css
index 9a2e2642..0908ed86 100644
--- a/wled00/data/index.css
+++ b/wled00/data/index.css
@@ -1189,7 +1189,7 @@ TD .checkmark, TD .radiomark {
}
.bp {
- margin-bottom: 5px;
+ margin-bottom: 8px;
}
/* segment & preset wrapper */
diff --git a/wled00/data/index.js b/wled00/data/index.js
index 131fd122..68a66e3e 100644
--- a/wled00/data/index.js
+++ b/wled00/data/index.js
@@ -772,7 +772,7 @@ function populateSegments(s)
-
+
@@ -1047,13 +1047,13 @@ function updateLen(s)
{
if (!gId(`seg${s}s`)) return;
var start = parseInt(gId(`seg${s}s`).value);
- var stop = parseInt(gId(`seg${s}e`).value);
- var len = stop - (cfg.comp.seglen?0:start);
+ var stop = parseInt(gId(`seg${s}e`).value) + (cfg.comp.seglen?start:0);
+ var len = stop - start;
if (isM) {
// matrix setup
let startY = parseInt(gId(`seg${s}sY`).value);
- let stopY = parseInt(gId(`seg${s}eY`).value);
- len *= (stopY-(cfg.comp.seglen?0:startY));
+ let stopY = parseInt(gId(`seg${s}eY`).value) + (cfg.comp.seglen?startY:0);
+ len *= (stopY-startY);
let tPL = gId(`seg${s}lbtm`);
if (stop-start>1 && stopY-startY>1) {
// 2D segment
@@ -1662,19 +1662,23 @@ function toggleNodes()
function makeSeg()
{
- var ns = 0;
+ var ns = 0, ct = 0;
var lu = lowestUnused;
let li = lastinfo;
if (lu > 0) {
- var pend = parseInt(gId(`seg${lu -1}e`).value,10) + (cfg.comp.seglen?parseInt(gId(`seg${lu -1}s`).value,10):0);
- if (pend < ledCount) ns = pend;
+ let xend = parseInt(gId(`seg${lu -1}e`).value,10) + (cfg.comp.seglen?parseInt(gId(`seg${lu -1}s`).value,10):0);
+ if (isM) {
+ ns = 0;
+ ct = mw;
+ } else {
+ if (xend < ledCount) ns = xend;
+ ct = ledCount-(cfg.comp.seglen?ns:0)
+ }
}
gId('segutil').scrollIntoView({
behavior: 'smooth',
block: 'start',
});
- var ct = (isM?mw:ledCount)-(cfg.comp.seglen?ns:0);
- //TODO: add calculation for Y in case of 2D matrix
var cn = `
diff --git a/wled00/data/pixart/boxdraw.js b/wled00/data/pixart/boxdraw.js
new file mode 100644
index 00000000..c000c2e6
--- /dev/null
+++ b/wled00/data/pixart/boxdraw.js
@@ -0,0 +1,62 @@
+function drawBoxes(inputPixelArray, widthPixels, heightPixels) {
+
+ var w = window;
+
+ // Get the canvas context
+ var ctx = canvas.getContext('2d', { willReadFrequently: true });
+
+ // Set the width and height of the canvas
+ if (w.innerHeight < w.innerWidth) {
+ canvas.width = Math.floor(w.innerHeight * 0.98);
+ }
+ else{
+ canvas.width = Math.floor(w.innerWidth * 0.98);
+ }
+ //canvas.height = w.innerWidth;
+
+ let pixelSize = Math.floor(canvas.width/widthPixels);
+
+ let xOffset = (w.innerWidth - (widthPixels * pixelSize))/2
+
+ //Set the canvas height to fit the right number of pixelrows
+ canvas.height = (pixelSize * heightPixels) + 10
+
+ //Iterate through the matrix
+ for (let y = 0; y < heightPixels; y++) {
+ for (let x = 0; x < widthPixels; x++) {
+
+ // Calculate the index of the current pixel
+ let i = (y*widthPixels) + x;
+
+ //Gets the RGB of the current pixel
+ let pixel = inputPixelArray[i];
+
+ let pixelColor = 'rgb(' + pixel[0] + ', ' + pixel[1] + ', ' + pixel[2] + ')';
+
+ let textColor = 'rgb(128,128,128)';
+
+ // Set the fill style to the pixel color
+ ctx.fillStyle = pixelColor;
+
+ //Draw the rectangle
+ ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
+
+ // Draw a border on the box
+ ctx.strokeStyle = '#888888';
+ ctx.lineWidth = 1;
+ ctx.strokeRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
+
+ //Write text to box
+ ctx.font = "10px Arial";
+ ctx.fillStyle = textColor;
+ ctx.textAlign = "center";
+ ctx.textBaseline = 'middle';
+ ctx.fillText((pixel[4] + 1), (x * pixelSize) + (pixelSize /2), (y * pixelSize) + (pixelSize /2));
+ }
+ }
+ var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ canvas.width = w.innerWidth;
+ ctx.putImageData(imageData, xOffset, 0);
+}
+
diff --git a/wled00/data/pixart/favicon-16x16.png b/wled00/data/pixart/favicon-16x16.png
new file mode 100644
index 00000000..feb51ca0
Binary files /dev/null and b/wled00/data/pixart/favicon-16x16.png differ
diff --git a/wled00/data/pixart/favicon-32x32.png b/wled00/data/pixart/favicon-32x32.png
new file mode 100644
index 00000000..a3b5cceb
Binary files /dev/null and b/wled00/data/pixart/favicon-32x32.png differ
diff --git a/wled00/data/pixart/favicon.ico b/wled00/data/pixart/favicon.ico
new file mode 100644
index 00000000..bde8945e
Binary files /dev/null and b/wled00/data/pixart/favicon.ico differ
diff --git a/wled00/data/pixart/getPixelValues.js b/wled00/data/pixart/getPixelValues.js
new file mode 100644
index 00000000..ffbf94a7
--- /dev/null
+++ b/wled00/data/pixart/getPixelValues.js
@@ -0,0 +1,320 @@
+function getPixelRGBValues(base64Image) {
+ httpArray = [];
+ fileJSON = `{"on":true,"bri":${brgh.value},"seg":{"id":${tSg.value},"i":[`;
+
+ //Which object holds the secret to the segment ID
+
+ let segID = 0;
+ if(tSg.style.display == "flex"){
+ segID = tSg.value
+ } else {
+ segID = sID.value;
+ }
+
+
+ //const copyJSONledbutton = gId('copyJSONledbutton');
+ const maxNoOfColorsInCommandSting = parseInt(cLN.value);
+
+ let hybridAddressing = false;
+ let selectedIndex = -1;
+
+ selectedIndex = frm.selectedIndex;
+ const formatSelection = frm.options[selectedIndex].value;
+
+
+ selectedIndex = lSS.selectedIndex;
+ const ledSetupSelection = lSS.options[selectedIndex].value;
+
+ selectedIndex = cFS.selectedIndex;
+ let hexValueCheck = true;
+ if (cFS.options[selectedIndex].value == 'dec'){
+ hexValueCheck = false
+ }
+
+ selectedIndex = aS.selectedIndex;
+ let segmentValueCheck = true; //If Range or Hybrid
+ if (aS.options[selectedIndex].value == 'single'){
+ segmentValueCheck = false
+ } else if (aS.options[selectedIndex].value == 'hybrid'){
+ hybridAddressing = true;
+ }
+
+ let curlString = ''
+ let haString = ''
+
+ let colorSeparatorStart = '"';
+ let colorSeparatorEnd = '"';
+ if (!hexValueCheck){
+ colorSeparatorStart = '[';
+ colorSeparatorEnd = ']';
+ }
+ // Warnings
+ let hasTransparency = false; //If alpha < 255 is detected on any pixel, this is set to true in code below
+ let imageInfo = '';
+
+ // Create an off-screen canvas
+ var canvas = cE('canvas');
+ var context = canvas.getContext('2d', { willReadFrequently: true });
+
+ // Create an image element and set its src to the base64 image
+ var image = new Image();
+ image.src = base64Image;
+
+ // Wait for the image to load before drawing it onto the canvas
+ image.onload = function() {
+
+ let scalePath = scDiv.children[0].children[0];
+ let color = scalePath.getAttribute("fill");
+ let sizeX = szX.value;
+ let sizeY = szY.value;
+
+ if (color != accentColor || sizeX < 1 || sizeY < 1){
+ //image will not be rezised Set desitred size to original size
+ sizeX = image.width;
+ sizeY = image.height;
+ //failsafe for not generating huge images automatically
+ if (image.width > 512 || image.height > 512)
+ {
+ sizeX = 16;
+ sizeY = 16;
+ }
+ }
+
+ // Set the canvas size to the same as the desired image size
+ canvas.width = sizeX;
+ canvas.height = sizeY;
+
+ imageInfo = '
Width: ' + sizeX + ', Height: ' + sizeY + ' (make sure this matches your led matrix setup)
'
+
+ // Draw the image onto the canvas
+ context.drawImage(image, 0, 0, sizeX, sizeY);
+
+ // Get the pixel data from the canvas
+ var pixelData = context.getImageData(0, 0, sizeX, sizeY).data;
+
+ // Create an array to hold the RGB values of each pixel
+ var pixelRGBValues = [];
+
+ // If the first row of the led matrix is right -> left
+ let right2leftAdjust = 1;
+
+ if (ledSetupSelection == 'l2r'){
+ right2leftAdjust = 0;
+ }
+
+ // Loop through the pixel data and get the RGB values of each pixel
+ for (var i = 0; i < pixelData.length; i += 4) {
+ var r = pixelData[i];
+ var g = pixelData[i + 1];
+ var b = pixelData[i + 2];
+ var a = pixelData[i + 3];
+
+ let pixel = i/4
+ let row = Math.floor(pixel/sizeX);
+ let led = pixel;
+ if (ledSetupSelection == 'matrix'){
+ //Do nothing, the matrix is set upp like the index in the image
+ //Every row starts from the left, i.e. no zigzagging
+ }
+ else if ((row + right2leftAdjust) % 2 === 0) {
+ //Setup is traditional zigzag
+ //right2leftAdjust basically flips the row order if = 1
+ //Row is left to right
+ //Leave led index as pixel index
+
+ } else {
+ //Setup is traditional zigzag
+ //Row is right to left
+ //Invert index of row for led
+ let indexOnRow = led - (row * sizeX);
+ let maxIndexOnRow = sizeX - 1;
+ let reversedIndexOnRow = maxIndexOnRow - indexOnRow;
+ led = (row * sizeX) + reversedIndexOnRow;
+ }
+
+ // Add the RGB values to the pixel RGB values array
+ pixelRGBValues.push([r, g, b, a, led, pixel, row]);
+ }
+
+ pixelRGBValues.sort((a, b) => a[5] - b[5]);
+
+ //Copy the values to a new array for resorting
+ let ledRGBValues = [... pixelRGBValues];
+
+ //Sort the array based on led index
+ ledRGBValues.sort((a, b) => a[4] - b[4]);
+
+ //Generate JSON in WLED format
+ let JSONledString = '';
+
+ //Set starting values for the segment check to something that is no color
+ let segmentStart = -1;
+ let maxi = ledRGBValues.length;
+ let curentColorIndex = 0
+ let commandArray = [];
+
+ //For evry pixel in the LED array
+ for (let i = 0; i < maxi; i++) {
+ let pixel = ledRGBValues[i];
+ let r = pixel[0];
+ let g = pixel[1];
+ let b = pixel[2];
+ let a = pixel[3];
+ let segmentString = '';
+ let segmentEnd = -1;
+
+ if(segmentValueCheck){
+ if (segmentStart < 0){
+ //This is the first led of a new segment
+ segmentStart = i;
+ } //Else we allready have a start index
+
+ if (i < maxi - 1){
+
+ let iNext = i + 1;
+ let nextPixel = ledRGBValues[iNext];
+
+ if (nextPixel[0] != r || nextPixel[1] != g || nextPixel[2] != b ){
+ //Next pixel has new color
+ //The current segment ends with this pixel
+ segmentEnd = i + 1 //WLED wants the NEXT LED as the stop led...
+ if (segmentStart == i && hybridAddressing){
+ //If only one led/pixel, no segment info needed
+ if (JSONledString == ''){
+ //If addressing is single, we need to start every command with a starting possition
+ segmentString = '' + i + ',';
+ //Fixed to b2
+ } else{
+ segmentString = ''
+ }
+ }
+ else {
+ segmentString = segmentStart + ',' + segmentEnd + ',';
+ }
+ }
+
+ } else {
+ //This is the last pixel, so the segment must end
+ segmentEnd = i + 1;
+
+ if (segmentStart + 1 == segmentEnd && hybridAddressing){
+ //If only one led/pixel, no segment info needed
+ if (JSONledString == ''){
+ //If addressing is single, we need to start every command with a starting possition
+ segmentString = '' + i + ',';
+ //Fixed to b2
+ } else{
+ segmentString = ''
+ }
+ }
+ else {
+ segmentString = segmentStart + ',' + segmentEnd + ',';
+ }
+ }
+ } else{
+ //Write every pixel
+ if (JSONledString == ''){
+ //If addressing is single, we need to start every command with a starting possition
+ JSONledString = i
+ //Fixed to b2
+ }
+
+ segmentStart = i
+ segmentEnd = i
+ //Segment string should be empty for when addressing single. So no need to set it again.
+ }
+
+ if (a < 255){
+ hasTransparency = true; //If ANY pixel has alpha < 255 then this is set to true to warn the user
+ }
+
+ if (segmentEnd > -1){
+ //This is the last pixel in the segment, write to the JSONledString
+ //Return color value in selected format
+ let colorValueString = r + ',' + g + ',' + b ;
+
+ if (hexValueCheck){
+ const [red, green, blue] = [r, g, b];
+ colorValueString = `${[red, green, blue].map(x => x.toString(16).padStart(2, '0')).join('')}`;
+ } else{
+ //do nothing, allready set
+ }
+
+ // Check if start and end is the same, in which case remove
+
+ JSONledString += segmentString + colorSeparatorStart + colorValueString + colorSeparatorEnd;
+ fileJSON = JSONledString + segmentString + colorSeparatorStart + colorValueString + colorSeparatorEnd;
+
+ curentColorIndex = curentColorIndex + 1; // We've just added a new color to the string so up the count with one
+
+ if (curentColorIndex % maxNoOfColorsInCommandSting === 0 || i == maxi - 1) {
+
+ //If we have accumulated the max number of colors to send in a single command or if this is the last pixel, we should write the current colorstring to the array
+ commandArray.push(JSONledString);
+ JSONledString = ''; //Start on an new command string
+ } else
+ {
+ //Add a comma to continue the command string
+ JSONledString = JSONledString + ','
+ }
+ //Reset segment values
+ segmentStart = - 1;
+ }
+ }
+
+ JSONledString = ''
+
+ //For every commandString in the array
+ for (let i = 0; i < commandArray.length; i++) {
+ let thisJSONledString = `{"on":true,"bri":${brgh.value},"seg":{"id":${segID},"i":[${commandArray[i]}]}}`;
+ httpArray.push(thisJSONledString);
+
+ let thiscurlString = `curl -X POST "http://${gurl.value}/json/state" -d \'${thisJSONledString}\' -H "Content-Type: application/json"`;
+
+ //Aggregated Strings That should be returned to the user
+ if (i > 0){
+ JSONledString = JSONledString + '\n';
+ curlString = curlString + ' && ';
+ }
+ JSONledString += thisJSONledString;
+ curlString += thiscurlString;
+ }
+
+
+ haString = `#Uncomment if you don\'t allready have these defined in your switch section of your configuration.yaml
+#- platform: command_line
+ #switches:
+ ${haIDe.value}
+ friendly_name: ${haNe.value}
+ unique_id: ${haUe.value}
+ command_on: >
+ ${curlString}
+ command_off: >
+ curl -X POST "http://${gurl.value}/json/state" -d \'{"on":false}\' -H "Content-Type: application/json"`;
+
+ if (formatSelection == 'wled'){
+ JLD.value = JSONledString;
+ } else if (formatSelection == 'curl'){
+ JLD.value = curlString;
+ } else if (formatSelection == 'ha'){
+ JLD.value = haString;
+ } else {
+ JLD.value = 'ERROR!/n' + formatSelection + ' is an unknown format.'
+ }
+
+ fileJSON += ']}}';
+
+ let infoDiv = imin;
+ let canvasDiv = imin;
+ if (hasTransparency){
+ imageInfo = imageInfo + '
WARNING! Transparency info detected in image. Transparency (alpha) has been ignored. To ensure you get the result you desire, use only solid colors in your image.