From 3dbfae7fc423330334773f6af28703fc08e1df3c Mon Sep 17 00:00:00 2001 From: Makuna Date: Sun, 20 Mar 2016 10:06:58 -0700 Subject: [PATCH] Topology, Curves, Gamma, and Samples Topology, Tiles, and Mosaic layout Animation Curves Color Gamma correct change timescale dynamically on NeoPixelAnimator bilinear color blend methods new samples to support new features --- ReadMe.md | 14 +- examples/NeoPixelCylon/NeoPixelCylon.ino | 121 +++++ examples/NeoPixelGamma/NeoPixelGamma.ino | 93 ++++ .../NeoPixelMosaicDump/NeoPixelMosaicDump.ino | 98 ++++ .../NeoPixelMosaicTest/NeoPixelMosaicTest.ino | 97 ++++ .../NeoPixelTilesDump/NeoPixelTilesDump.ino | 102 +++++ .../NeoPixelTilesTest/NeoPixelTilesTest.ino | 101 +++++ .../NeoPixelTopologyDump.ino | 78 ++++ .../NeoPixelTopologyTest.ino | 90 ++++ keywords.txt | 35 +- library.properties | 4 +- src/NeoPixelAnimator.h | 7 +- src/NeoPixelBus.h | 11 +- src/internal/HsbColor.cpp | 2 +- src/internal/HsbColor.h | 3 +- src/internal/HslColor.cpp | 3 +- src/internal/HslColor.h | 2 +- src/internal/HtmlColor.h | 20 +- src/internal/Layouts.h | 425 ++++++++++++++++++ src/internal/NeoColorFeatures.h | 7 +- src/internal/NeoEase.h | 156 ++++--- src/internal/NeoGamma.h | 90 ++++ src/internal/NeoMosaic.h | 157 +++++++ src/internal/NeoPixelAnimator.cpp | 5 +- src/internal/NeoTiles.h | 124 +++++ src/internal/NeoTopology.h | 73 +++ src/internal/RgbColor.cpp | 20 +- src/internal/RgbColor.h | 30 +- src/internal/RgbwColor.cpp | 25 +- src/internal/RgbwColor.h | 34 +- 30 files changed, 1917 insertions(+), 110 deletions(-) create mode 100644 examples/NeoPixelCylon/NeoPixelCylon.ino create mode 100644 examples/NeoPixelGamma/NeoPixelGamma.ino create mode 100644 examples/NeoPixelMosaicDump/NeoPixelMosaicDump.ino create mode 100644 examples/NeoPixelMosaicTest/NeoPixelMosaicTest.ino create mode 100644 examples/NeoPixelTilesDump/NeoPixelTilesDump.ino create mode 100644 examples/NeoPixelTilesTest/NeoPixelTilesTest.ino create mode 100644 examples/NeoPixelTopologyDump/NeoPixelTopologyDump.ino create mode 100644 examples/NeoPixelTopologyTest/NeoPixelTopologyTest.ino create mode 100644 src/internal/Layouts.h create mode 100644 src/internal/NeoGamma.h create mode 100644 src/internal/NeoMosaic.h create mode 100644 src/internal/NeoTiles.h create mode 100644 src/internal/NeoTopology.h diff --git a/ReadMe.md b/ReadMe.md index fdaae9a1..8e4dfb77 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -8,6 +8,7 @@ A library to control one wire protocol RGB and RGBW leds like SK6812, WS2811, an Supports most Arduino platforms. This is the most funtional library for the Esp8266 as it provides solutions for all Esp8266 module types even when WiFi is used. + Please read this best practices link before connecting your NeoPixels, it will save you alot of time and effort. [Adafruit NeoPixel Best Practices](https://learn.adafruit.com/adafruit-neopixel-uberguide/best-practices) @@ -16,21 +17,18 @@ For quick questions jump on Gitter and ask away. For bugs, make sure there isn't an active issue and then create one. -This new library supports a templatized model of defining which method gets used to send data and what order and size the pixel data is sent in. This new design creates the smallest code for each definition of features used. Further it allows for picking which method to send the data on the Esp8266 in an easy to change way. -Please see examples to become familiar with the new design. -Due to this design you will often realize over 500 bytes of more program storage for your sketch. Important for the smallest Arduinos project. - +## Documentation +[See Wiki](https://github.com/Makuna/NeoPixelBus/wiki) -## Installing This Library (prefered) +## Installing This Library (prefered, you just want to use it) Open the Library Manager and search for "NeoPixelBus by Makuna" and install -## Installing This Library From GitHub +## Installing This Library From GitHub (advanced, you want to contribute) Create a directory in your Arduino\Library folder named "NeoPixelBus" Clone (Git) this project into that folder. It should now show up in the import list when you restart Arduino IDE. -## Documentation -[See Wiki](https://github.com/Makuna/NeoPixelBus/wiki) + diff --git a/examples/NeoPixelCylon/NeoPixelCylon.ino b/examples/NeoPixelCylon/NeoPixelCylon.ino new file mode 100644 index 00000000..4ffd027d --- /dev/null +++ b/examples/NeoPixelCylon/NeoPixelCylon.ino @@ -0,0 +1,121 @@ +// NeoPixelCylon +// This example will move a Cylong Red Eye back and forth across the +// the full collection of pixels on the strip. +// +// This will demonstrate the use of the NeoEase animation ease methods; that provide +// simulated acceleration to the animations. +// +// + +#include +#include + +const uint16_t PixelCount = 16; // make sure to set this to the number of pixels in your strip +const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 +const RgbColor CylonEyeColor(HtmlColor(0x7f0000)); + +NeoPixelBus strip(PixelCount, PixelPin); + +NeoPixelAnimator animations(2); // only ever need 2 animations + +uint16_t lastPixel = 0; // track the eye position +int8_t moveDir = 1; // track the direction of movement + +// uncomment one of the lines below to see the effects of +// changing the ease function on the movement animation +AnimEaseFunction moveEase = +// NeoEase::Linear; +// NeoEase::QuadraticInOut; +// NeoEase::CubicInOut; + NeoEase::QuarticInOut; +// NeoEase::QuinticInOut; +// NeoEase::SinusoidalInOut; +// NeoEase::ExponentialInOut; +// NeoEase::CircularInOut; + +void FadeAll(uint8_t darkenBy) +{ + RgbColor color; + for (uint16_t indexPixel = 0; indexPixel < strip.PixelCount(); indexPixel++) + { + color = strip.GetPixelColor(indexPixel); + color.Darken(darkenBy); + strip.SetPixelColor(indexPixel, color); + } +} + +void FadeAnimUpdate(const AnimationParam& param) +{ + if (param.state == AnimationState_Completed) + { + FadeAll(10); + animations.RestartAnimation(param.index); + } +} + +void MoveAnimUpdate(const AnimationParam& param) +{ + // apply the movement animation curve + float progress = moveEase(param.progress); + + // use the curved progress to calculate the pixel to effect + uint16_t nextPixel; + if (moveDir > 0) + { + nextPixel = progress * PixelCount; + } + else + { + nextPixel = (1.0f - progress) * PixelCount; + } + + // if progress moves fast enough, we may move more than + // one pixel, so we update all between the calculated and + // the last + if (lastPixel != nextPixel) + { + for (uint16_t i = lastPixel + moveDir; i != nextPixel; i += moveDir) + { + strip.SetPixelColor(i, CylonEyeColor); + } + } + strip.SetPixelColor(nextPixel, CylonEyeColor); + + lastPixel = nextPixel; + + if (param.state == AnimationState_Completed) + { + // reverse direction of movement + moveDir *= -1; + + // done, time to restart this position tracking animation/timer + animations.RestartAnimation(param.index); + } +} + +void SetupAnimations() +{ + // fade all pixels providing a tail that is longer the faster + // the pixel moves. + animations.StartAnimation(0, 5, FadeAnimUpdate); + + // take several seconds to move eye fron one side to the other + animations.StartAnimation(1, 2000, MoveAnimUpdate); +} + +void setup() +{ + strip.Begin(); + strip.Show(); + + SetupAnimations(); +} + +void loop() +{ + // this is all that is needed to keep it running + // and avoiding using delay() is always a good thing for + // any timing related routines + animations.UpdateAnimations(); + strip.Show(); +} diff --git a/examples/NeoPixelGamma/NeoPixelGamma.ino b/examples/NeoPixelGamma/NeoPixelGamma.ino new file mode 100644 index 00000000..02c8f88e --- /dev/null +++ b/examples/NeoPixelGamma/NeoPixelGamma.ino @@ -0,0 +1,93 @@ +// NeoPixelGamma +// This example will display a timed series of color gradiants with gamma correction +// and then without. +// If the last pixel is on, then the colors being shown are color corrected. +// It will show Red grandiant, Green grandiant, Blue grandiant, a White grandiant, and +// then repeat. +// +// This will demonstrate the use of the NeoGamma class +// +// + +#include +#include + +const uint16_t PixelCount = 16; // make sure to set this to the number of pixels in your strip +const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 + +NeoPixelBus strip(PixelCount, PixelPin); + +// uncomment only one of these to compare memory use or speed +// +// NeoGamma gamma; +NeoGamma gamma; + +void DrawPixels(bool corrected, HslColor startColor, HslColor stopColor) +{ + for (uint16_t index = 0; index < strip.PixelCount() - 1; index++) + { + float progress = index / static_cast(strip.PixelCount() - 2); + RgbColor color = HslColor::LinearBlend(startColor, stopColor, progress); + if (corrected) + { + color = gamma.Correct(color); + } + strip.SetPixelColor(index, color); + } + + // use the last pixel to indicate if we are showing corrected colors or not + if (corrected) + { + strip.SetPixelColor(strip.PixelCount() - 1, RgbColor(64)); + } + else + { + strip.SetPixelColor(strip.PixelCount() - 1, RgbColor(0)); + } + + strip.Show(); +} + +void setup() +{ + strip.Begin(); + strip.Show(); +} + +void loop() +{ + HslColor startColor; + HslColor stopColor; + + // red color + startColor = HslColor(0.0f, 1.0f, 0.0f); + stopColor = HslColor(0.0f, 1.0f, 0.5f); + DrawPixels(true, startColor, stopColor); + delay(5000); + DrawPixels(false, startColor, stopColor); + delay(5000); + + // green color + startColor = HslColor(0.33f, 1.0f, 0.0f); + stopColor = HslColor(0.33f, 1.0f, 0.5f); + DrawPixels(true, startColor, stopColor); + delay(5000); + DrawPixels(false, startColor, stopColor); + delay(5000); + + // blue color + startColor = HslColor(0.66f, 1.0f, 0.0f); + stopColor = HslColor(0.66f, 1.0f, 0.5f); + DrawPixels(true, startColor, stopColor); + delay(5000); + DrawPixels(false, startColor, stopColor); + delay(5000); + + // white color + startColor = HslColor(0.0f, 0.0f, 0.0f); + stopColor = HslColor(0.0f, 0.0f, 0.5f); + DrawPixels(true, startColor, stopColor); + delay(5000); + DrawPixels(false, startColor, stopColor); + delay(5000); +} \ No newline at end of file diff --git a/examples/NeoPixelMosaicDump/NeoPixelMosaicDump.ino b/examples/NeoPixelMosaicDump/NeoPixelMosaicDump.ino new file mode 100644 index 00000000..6b70b6f4 --- /dev/null +++ b/examples/NeoPixelMosaicDump/NeoPixelMosaicDump.ino @@ -0,0 +1,98 @@ +//---------------------------------------------------------------------- +// NeoPixelMosaicDump +// This will dump to the serial output a grid map of the defined mosaic +// The output is displayed as row column labeled grid with the NeoPixelBus +// index of the pixel at the intersection of the row and column. +// +// To help with physical layout, there maybe included a symbol following the index +// < means the index is the input index for the panel, the first on the panel +// > means the index is the output index for the panel, the last on the panel +// +// This is useful in visualising the mosaic layout of your panels to +// confirm you have them correctly wired together for this mosaic pattern +// +// It does not require that you have the actual panel connected +//---------------------------------------------------------------------- + +#include +#include + +// uncomment one of these that matches your panel pixel layouts +// rotation is ignored for mosaic as it applies a rotation for you +// that is specific to the location of the panel within the mosaic +// to reduce connection lengths + +typedef ColumnMajorAlternatingLayout MyPanelLayout; +// typedef ColumnMajorLayout MyPanelLayout; +// typedef RowMajorAlternatingLayout MyPanelLayout; +// typedef RowMajorLayout MyPanelLayout; + +// make sure to set these panel and tile layout to match your sizes +const uint8_t PanelWidth = 8; // a 8 pixel x 8 pixel matrix of leds on the panel +const uint8_t PanelHeight = 8; +const uint8_t TileWidth = 4; // laid out in 4 panels x 2 panels mosaic +const uint8_t TileHeight = 2; + +NeoMosaic mosaic( + PanelWidth, + PanelHeight, + TileWidth, + TileHeight); + +void DumpMosaic() +{ + Serial.println(); + + Serial.print("\t\t"); + for (int x = 0; x < mosaic.getWidth(); x++) + { + Serial.print(x); + Serial.print("\t"); + } + Serial.println(); + + Serial.print("\t---"); + for (int x = 0; x < mosaic.getWidth(); x++) + { + Serial.print("--------"); + } + Serial.println(); + + for (int y = 0; y < mosaic.getHeight(); y++) + { + Serial.print(" "); + Serial.print(y); + Serial.print("\t|\t"); + + for (int x = 0; x < mosaic.getWidth(); x++) + { + NeoTopologyHint hint = mosaic.TopologyHint(x, y); + + Serial.print(mosaic.Map(x, y)); + if (hint == NeoTopologyHint_FirstOnPanel) + { + Serial.print("<"); + } + else if (hint == NeoTopologyHint_LastOnPanel) + { + Serial.print(">"); + } + Serial.print("\t"); + } + Serial.println(); + } +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + DumpMosaic(); +} + +void loop() +{ + +} + diff --git a/examples/NeoPixelMosaicTest/NeoPixelMosaicTest.ino b/examples/NeoPixelMosaicTest/NeoPixelMosaicTest.ino new file mode 100644 index 00000000..3c21c330 --- /dev/null +++ b/examples/NeoPixelMosaicTest/NeoPixelMosaicTest.ino @@ -0,0 +1,97 @@ +//---------------------------------------------------------------------- +// NeoPixelTopologyTest +// This will display specific colors in specific locations on the led panels +// +// This is useful in confirming the layout of your panels +// +// It does require that you have the actual panels connected +//---------------------------------------------------------------------- + +#include +#include + +// uncomment one of these that matches your panel pixel layouts +// rotation is ignored for mosaic as it applies a rotation for you +// that is specific to the location of the panel within the mosaic +// to reduce connection lengths + +typedef ColumnMajorAlternatingLayout MyPanelLayout; +// typedef ColumnMajorLayout MyPanelLayout; +// typedef RowMajorAlternatingLayout MyPanelLayout; +// typedef RowMajorLayout MyPanelLayout; + +// make sure to set these panel values to the sizes of yours +const uint8_t PanelWidth = 8; // 8 pixel x 8 pixel matrix of leds +const uint8_t PanelHeight = 8; +const uint8_t TileWidth = 4; // laid out in 4 panels x 2 panels mosaic +const uint8_t TileHeight = 2; + +const uint16_t PixelCount = PanelWidth * PanelHeight * TileWidth * TileHeight; +const uint8_t PixelPin = 2; + +NeoMosaic mosaic( + PanelWidth, + PanelHeight, + TileWidth, + TileHeight); + +NeoPixelBus strip(PixelCount, PixelPin); +//NeoPixelBus strip(PixelCount, PixelPin); +//NeoPixelBus strip(PixelCount, PixelPin); + +RgbColor red(128, 0, 0); +RgbColor green(0, 128, 0); +RgbColor blue(0, 0, 128); +RgbColor white(128); +// if using NeoRgbwFeature above, use this white instead to use +// the correct white element of the LED +//RgbwColor white(128); +RgbColor black(0); + +const uint16_t left = 0; +const uint16_t right = PanelWidth - 1; +const uint16_t top = 0; +const uint16_t bottom = PanelHeight - 1; + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + Serial.println(); + Serial.println("Initializing..."); + + strip.Begin(); + strip.Show(); + + Serial.println(); + Serial.println("Running..."); +} + +void loop() +{ + delay(2500); + + Serial.println(); + Serial.println("If your panel is correctly defined, you should see ..."); + Serial.println("Upper left is white."); + Serial.println("Upper right is Red."); + Serial.println("Lower right is Green"); + Serial.println("Lower Left is Blue"); + + // use the topo to map the 2d cordinate to the pixel + // and use that to SetPixelColor + strip.SetPixelColor(mosaic.Map(left, top), white); + strip.SetPixelColor(mosaic.Map(right, top), red); + strip.SetPixelColor(mosaic.Map(right, bottom), green); + strip.SetPixelColor(mosaic.Map(left, bottom), blue); + strip.Show(); + + delay(5000); + + Serial.println(); + Serial.println("Cleared to black ..."); + strip.ClearTo(black); + strip.Show(); +} + diff --git a/examples/NeoPixelTilesDump/NeoPixelTilesDump.ino b/examples/NeoPixelTilesDump/NeoPixelTilesDump.ino new file mode 100644 index 00000000..2849c561 --- /dev/null +++ b/examples/NeoPixelTilesDump/NeoPixelTilesDump.ino @@ -0,0 +1,102 @@ +//---------------------------------------------------------------------- +// NeoPixelTileDump +// This will dump to the serial output a grid map of the defined tiles +// The output is displayed as row column labeled grid with the NeoPixelBus +// index of the pixel at the intersection of the row and column +// +// To help with physical layout, there maybe included a symbol following the index +// < means the index is the input index for the panel, the first on the panel +// > means the index is the output index for the panel, the last on the panel +// +// This is useful in visualising the tile layout of your panels to +// confirm you have them correctly wired together for the defined pattern +// +// It does not require that you have the actual panel connected +//---------------------------------------------------------------------- + +#include +#include + +// uncomment one of these that matches your panel pixel layouts and +// how you want them rotated. Not all the rotations are listed here +// but you can modifiy the name to include the rotation of 90,180, or 270. + +typedef ColumnMajorAlternatingLayout MyPanelLayout; +// typedef ColumnMajorLayout MyPanelLayout; +// typedef RowMajorAlternatingLayout MyPanelLayout; +// typedef RowMajorLayout MyPanelLayout; +// typedef RowMajor90Layout MyPanelLayout; // note rotation 90 was used + +// change this to be one of the layouts which will define the layout +// of the panels themselves +typedef ColumnMajorLayout MyTilesLayout; + +// make sure to set these panel and tile layout to match your sizes +const uint8_t PanelWidth = 8; // 8 pixel x 8 pixel matrix of leds +const uint8_t PanelHeight = 8; +const uint8_t TileWidth = 4; // laid out in 4 panels x 2 panels mosaic +const uint8_t TileHeight = 2; + +NeoTiles tiles( + PanelWidth, + PanelHeight, + TileWidth, + TileHeight); + +void DumpTopo() +{ + Serial.println(); + + Serial.print("\t\t"); + for (int x = 0; x < tiles.getWidth(); x++) + { + Serial.print(x); + Serial.print("\t"); + } + Serial.println(); + + Serial.print("\t---"); + for (int x = 0; x < tiles.getWidth(); x++) + { + Serial.print("--------"); + } + Serial.println(); + + for (int y = 0; y < tiles.getHeight(); y++) + { + Serial.print(" "); + Serial.print(y); + Serial.print("\t|\t"); + + for (int x = 0; x < tiles.getWidth(); x++) + { + NeoTopologyHint hint = tiles.TopologyHint(x, y); + + Serial.print(tiles.Map(x, y)); + if (hint == NeoTopologyHint_FirstOnPanel) + { + Serial.print("<"); + } + else if (hint == NeoTopologyHint_LastOnPanel) + { + Serial.print(">"); + } + Serial.print("\t"); + } + Serial.println(); + } +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + DumpTopo(); +} + +void loop() +{ + +} + diff --git a/examples/NeoPixelTilesTest/NeoPixelTilesTest.ino b/examples/NeoPixelTilesTest/NeoPixelTilesTest.ino new file mode 100644 index 00000000..41d8208b --- /dev/null +++ b/examples/NeoPixelTilesTest/NeoPixelTilesTest.ino @@ -0,0 +1,101 @@ +//---------------------------------------------------------------------- +// NeoPixelTilesTest +// This will display specific colors in specific locations on the led panels +// +// This is useful in confirming the layout of your panels +// +// It does require that you have the actual panels connected +//---------------------------------------------------------------------- + +#include +#include + +// uncomment one of these that matches your panel pixel layouts and +// how you want them rotated. Not all the rotations are listed here +// but you can modifiy the name to include the rotation of 90,180, or 270. + +typedef ColumnMajorAlternatingLayout MyPanelLayout; +// typedef ColumnMajorLayout MyPanelLayout; +// typedef RowMajorAlternatingLayout MyPanelLayout; +// typedef RowMajorLayout MyPanelLayout; +// typedef RowMajor90Layout MyPanelLayout; // note rotation 90 was used + +// change this to be one of the layouts which will define the layout +// of the panels themselves +typedef ColumnMajorLayout MyTilesLayout; + +// make sure to set these panel values to the sizes of yours +const uint8_t PanelWidth = 8; // 8 pixel x 8 pixel matrix of leds +const uint8_t PanelHeight = 8; +const uint8_t TileWidth = 4; // laid out in 4 panels x 2 panels mosaic +const uint8_t TileHeight = 2; + +const uint16_t PixelCount = PanelWidth * PanelHeight * TileWidth * TileHeight; +const uint8_t PixelPin = 2; + +NeoTiles tiles( + PanelWidth, + PanelHeight, + TileWidth, + TileHeight); + +NeoPixelBus strip(PixelCount, PixelPin); +//NeoPixelBus strip(PixelCount, PixelPin); +//NeoPixelBus strip(PixelCount, PixelPin); + +RgbColor red(128, 0, 0); +RgbColor green(0, 128, 0); +RgbColor blue(0, 0, 128); +RgbColor white(128); +// if using NeoRgbwFeature above, use this white instead to use +// the correct white element of the LED +//RgbwColor white(128); +RgbColor black(0); + +const uint16_t left = 0; +const uint16_t right = PanelWidth - 1; +const uint16_t top = 0; +const uint16_t bottom = PanelHeight - 1; + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + Serial.println(); + Serial.println("Initializing..."); + + strip.Begin(); + strip.Show(); + + Serial.println(); + Serial.println("Running..."); +} + +void loop() +{ + delay(2500); + + Serial.println(); + Serial.println("If your panel is correctly defined, you should see ..."); + Serial.println("Upper left is white."); + Serial.println("Upper right is Red."); + Serial.println("Lower right is Green"); + Serial.println("Lower Left is Blue"); + + // use the topo to map the 2d cordinate to the pixel + // and use that to SetPixelColor + strip.SetPixelColor(tiles.Map(left, top), white); + strip.SetPixelColor(tiles.Map(right, top), red); + strip.SetPixelColor(tiles.Map(right, bottom), green); + strip.SetPixelColor(tiles.Map(left, bottom), blue); + strip.Show(); + + delay(5000); + + Serial.println(); + Serial.println("Cleared to black ..."); + strip.ClearTo(black); + strip.Show(); +} + diff --git a/examples/NeoPixelTopologyDump/NeoPixelTopologyDump.ino b/examples/NeoPixelTopologyDump/NeoPixelTopologyDump.ino new file mode 100644 index 00000000..aa78a253 --- /dev/null +++ b/examples/NeoPixelTopologyDump/NeoPixelTopologyDump.ino @@ -0,0 +1,78 @@ +//---------------------------------------------------------------------- +// NeoPixelTopologyDump +// This will dump to the serial output a grid map of the defined topology +// The output is displayed as row column labeled grid with the NeoPixelBus +// index of the pixel at the intersection of the row and column +// +// This is useful in visualising the layout of your panel so you can +// confirm you have the correct pattern +// +// It does not require that you have the actual panel connected +//---------------------------------------------------------------------- + +#include +#include + +// uncomment one of these that matches your panel pixel layouts and +// how you want them rotated. Not all the rotations are listed here +// but you can modifiy the name to include the rotation of 90,180, or 270. + +typedef ColumnMajorAlternatingLayout MyPanelLayout; +// typedef ColumnMajorLayout MyPanelLayout; +// typedef RowMajorAlternatingLayout MyPanelLayout; +// typedef RowMajorLayout MyPanelLayout; +// typedef RowMajor90Layout MyPanelLayout; // note rotation 90 was used + +// make sure to set these panel values to the sizes of yours +const uint8_t PanelWidth = 8; // 8 pixel x 8 pixel matrix of leds +const uint8_t PanelHeight = 8; + +NeoTopology topo(PanelWidth, PanelHeight); + +void DumpTopo() +{ + Serial.println(); + + Serial.print("\t\t"); + for (int x = 0; x < topo.getWidth(); x++) + { + Serial.print(x); + Serial.print("\t"); + } + Serial.println(); + + Serial.print("\t--"); + for (int x = 0; x < topo.getWidth(); x++) + { + Serial.print("--------"); + } + Serial.println(); + + for (int y = 0; y < topo.getHeight(); y++) + { + Serial.print(" "); + Serial.print(y); + Serial.print("\t|\t"); + + for (int x = 0; x < topo.getWidth(); x++) + { + Serial.print(topo.Map(x, y)); + Serial.print("\t"); + } + Serial.println(); + } +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + DumpTopo(); +} + +void loop() +{ + +} + diff --git a/examples/NeoPixelTopologyTest/NeoPixelTopologyTest.ino b/examples/NeoPixelTopologyTest/NeoPixelTopologyTest.ino new file mode 100644 index 00000000..cd200551 --- /dev/null +++ b/examples/NeoPixelTopologyTest/NeoPixelTopologyTest.ino @@ -0,0 +1,90 @@ +//---------------------------------------------------------------------- +// NeoPixelTopologyTest +// This will display specific colors in specific locations on the led panel +// +// This is useful in confirming the layout of your panel +// +// It does require that you have the actual panel connected +//---------------------------------------------------------------------- + +#include +#include + +// uncomment one of these that matches your panel pixel layouts and +// how you want them rotated. Not all the rotations are listed here +// but you can modifiy the name to include the rotation of 90,180, or 270. + +typedef ColumnMajorAlternatingLayout MyPanelLayout; +// typedef ColumnMajorLayout MyPanelLayout; +// typedef RowMajorAlternatingLayout MyPanelLayout; +// typedef RowMajorLayout MyPanelLayout; +// typedef RowMajor90Layout MyPanelLayout; // note rotation 90 was used + +// make sure to set these panel values to the sizes of yours +const uint8_t PanelWidth = 8; // 8 pixel x 8 pixel matrix of leds +const uint8_t PanelHeight = 8; +const uint16_t PixelCount = PanelWidth * PanelHeight; +const uint8_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 + +NeoTopology topo(PanelWidth, PanelHeight); + +NeoPixelBus strip(PixelCount, PixelPin); +//NeoPixelBus strip(PixelCount, PixelPin); +//NeoPixelBus strip(PixelCount, PixelPin); + +RgbColor red(128, 0, 0); +RgbColor green(0, 128, 0); +RgbColor blue(0, 0, 128); +RgbColor white(128); +// if using NeoRgbwFeature above, use this white instead to use +// the correct white element of the LED +//RgbwColor white(128); +RgbColor black(0); + +const uint16_t left = 0; +const uint16_t right = PanelWidth - 1; +const uint16_t top = 0; +const uint16_t bottom = PanelHeight - 1; + +void setup() +{ + Serial.begin(115200); + while (!Serial); // wait for serial attach + + Serial.println(); + Serial.println("Initializing..."); + + strip.Begin(); + strip.Show(); + + Serial.println(); + Serial.println("Running..."); +} + +void loop() +{ + delay(2500); + + Serial.println(); + Serial.println("If your panel is correctly defined, you should see ..."); + Serial.println("Upper left is white."); + Serial.println("Upper right is Red."); + Serial.println("Lower right is Green"); + Serial.println("Lower Left is Blue"); + + // use the topo to map the 2d cordinate to the pixel + // and use that to SetPixelColor + strip.SetPixelColor(topo.Map(left, top), white); + strip.SetPixelColor(topo.Map(right, top), red); + strip.SetPixelColor(topo.Map(right, bottom), green); + strip.SetPixelColor(topo.Map(left, bottom), blue); + strip.Show(); + + delay(5000); + + Serial.println(); + Serial.println("Cleared to black ..."); + strip.ClearTo(black); + strip.Show(); +} + diff --git a/keywords.txt b/keywords.txt index bddec9b1..30b15006 100644 --- a/keywords.txt +++ b/keywords.txt @@ -31,6 +31,28 @@ AnimUpdateCallback KEYWORD1 AnimationParam KEYWORD1 NeoEase KEYWORD1 AnimEaseFunction KEYWORD1 +RowMajorLayout KEYWORD1 +RowMajor90Layout KEYWORD1 +RowMajor180Layout KEYWORD1 +RowMajor270Layout KEYWORD1 +RowMajorAlternatingLayout KEYWORD1 +RowMajorAlternating90Layout KEYWORD1 +RowMajorAlternating180Layout KEYWORD1 +RowMajorAlternating270Layout KEYWORD1 +ColumnMajorLayout KEYWORD1 +ColumnMajor90Layout KEYWORD1 +ColumnMajor180Layout KEYWORD1 +ColumnMajor270Layout KEYWORD1 +ColumnMajorAlternatingLayout KEYWORD1 +ColumnMajorAlternating90Layout KEYWORD1 +ColumnMajorAlternating180Layout KEYWORD1 +ColumnMajorAlternating270Layout KEYWORD1 +NeoTopology KEYWORD1 +NeoTiles KEYWORD1 +NeoMosaic KEYWORD1 +NeoGammaEquationMethod KEYWORD1 +NeoGammaTableMethod KEYWORD1 +NeoGamma KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -52,6 +74,7 @@ CalculateBrightness KEYWORD2 Darken KEYWORD2 Lighten KEYWORD2 LinearBlend KEYWORD2 +BilinearBlend KEYWORD2 IsAnimating KEYWORD2 NextAvailableAnimation KEYWORD2 StartAnimation KEYWORD2 @@ -63,7 +86,8 @@ UpdateAnimations KEYWORD2 IsPaused KEYWORD2 Pause KEYWORD2 Resume KEYWORD2 -TimeScale KEYWORD2 +getTimeScale KEYWORD2 +setTimeScale KEYWORD2 QuadraticIn KEYWORD2 QuadraticOut KEYWORD2 QuadraticInOut KEYWORD2 @@ -85,6 +109,12 @@ ExponentialInOut KEYWORD2 CircularIn KEYWORD2 CircularOut KEYWORD2 CircularInOut KEYWORD2 +Gamma KEYWORD2 +Map KEYWORD2 +getWidth KEYWORD2 +getHeight KEYWORD2 +TopologyHint KEYWORD2 +Correct KEYWORD2 ####################################### # Constants (LITERAL1) @@ -98,3 +128,6 @@ NEO_DECASECONDS LITERAL1 AnimationState_Started LITERAL1 AnimationState_Progress LITERAL1 AnimationState_Completed LITERAL1 +NeoTopologyHint_FirstOnPanel LITERAL1 +NeoTopologyHint_InPanel LITERAL1 +NeoTopologyHint_LastOnPanel LITERAL1 diff --git a/library.properties b/library.properties index b0bd90d4..4bcae0bc 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=NeoPixelBus by Makuna -version=2.0.3 +version=2.0.4 author=Michael C. Miller (makuna@live.com) maintainer=Michael C. Miller (makuna@live.com) sentence=A library that makes controlling NeoPixels (WS2811, WS2812 & SK6812) easy. paragraph=Supports most Arduino platforms, and especially Esp8266. Support for RGBW pixels. Includes seperate RgbColor, RgbwColor, HslColor, and HsbColor objects. Includes an animator class that helps create asyncronous animations. For Esp8266 it has three methods of sending data, DMA, UART, and Bit Bang. category=Display -url=https://github.com/Makuna/NeoPixelBus +url=https://github.com/Makuna/NeoPixelBus/wiki architectures=* \ No newline at end of file diff --git a/src/NeoPixelAnimator.h b/src/NeoPixelAnimator.h index 612cda39..62e52d52 100644 --- a/src/NeoPixelAnimator.h +++ b/src/NeoPixelAnimator.h @@ -125,11 +125,16 @@ class NeoPixelAnimator _animationLastTick = millis(); } - uint16_t TimeScale() + uint16_t getTimeScale() { return _timeScale; } + void setTimeScale(uint16_t timeScale) + { + _timeScale = (timeScale < 1) ? (1) : (timeScale > 32768) ? 32768 : timeScale; + } + private: struct AnimationContext { diff --git a/src/NeoPixelBus.h b/src/NeoPixelBus.h index 158dcdc6..b748a5f1 100644 --- a/src/NeoPixelBus.h +++ b/src/NeoPixelBus.h @@ -32,6 +32,15 @@ License along with NeoPixel. If not, see #include "internal/HsbColor.h" #include "internal/HtmlColor.h" #include "internal/RgbwColor.h" + +#include "internal/Layouts.h" +#include "internal/NeoTopology.h" +#include "internal/NeoTiles.h" +#include "internal/NeoMosaic.h" + +#include "internal/NeoEase.h" +#include "internal/NeoGamma.h" + #include "internal/NeoColorFeatures.h" #if defined(ARDUINO_ARCH_ESP8266) @@ -124,7 +133,7 @@ template class NeoPixelBus return _countPixels; }; - void SetPixelColor(uint16_t indexPixel, typename T_COLOR_FEATURE::ColorObject color) + void SetPixelColor(uint16_t indexPixel, const typename T_COLOR_FEATURE::ColorObject& color) { if (indexPixel < _countPixels) { diff --git a/src/internal/HsbColor.cpp b/src/internal/HsbColor.cpp index 1ca06cc4..31f1b325 100644 --- a/src/internal/HsbColor.cpp +++ b/src/internal/HsbColor.cpp @@ -67,7 +67,7 @@ HsbColor::HsbColor(const RgbColor& color) B = v; } -HsbColor HsbColor::LinearBlend(HsbColor left, HsbColor right, float progress) +HsbColor HsbColor::LinearBlend(const HsbColor& left, const HsbColor& right, float progress) { return HsbColor(left.H + ((right.H - left.H) * progress), left.S + ((right.S - left.S) * progress), diff --git a/src/internal/HsbColor.h b/src/internal/HsbColor.h index bcc929ed..ef1639c4 100644 --- a/src/internal/HsbColor.h +++ b/src/internal/HsbColor.h @@ -1,3 +1,4 @@ + /*------------------------------------------------------------------------- HsbColor provides a color object that can be directly consumed by NeoPixelBus @@ -62,7 +63,7 @@ struct HsbColor // progress - (0.0 - 1.0) value where 0.0 will return left and 1.0 will return right // and a value between will blend the color weighted linearly between them // ------------------------------------------------------------------------ - static HsbColor LinearBlend(HsbColor left, HsbColor right, float progress); + static HsbColor LinearBlend(const HsbColor& left, const HsbColor& right, float progress); // ------------------------------------------------------------------------ // Hue, Saturation, Brightness color members diff --git a/src/internal/HslColor.cpp b/src/internal/HslColor.cpp index 9e0f39a0..59c418de 100644 --- a/src/internal/HslColor.cpp +++ b/src/internal/HslColor.cpp @@ -1,6 +1,7 @@ /*------------------------------------------------------------------------- HslColor provides a color object that can be directly consumed by NeoPixelBus + Written by Michael C. Miller. I invest time and resources providing this open source code, @@ -70,7 +71,7 @@ HslColor::HslColor(const RgbColor& color) L = l; } -HslColor HslColor::LinearBlend(HslColor left, HslColor right, float progress) +HslColor HslColor::LinearBlend(const HslColor& left, const HslColor& right, float progress) { return HslColor(left.H + ((right.H - left.H) * progress), left.S + ((right.S - left.S) * progress), diff --git a/src/internal/HslColor.h b/src/internal/HslColor.h index 5e7cd87c..6b41d540 100644 --- a/src/internal/HslColor.h +++ b/src/internal/HslColor.h @@ -64,7 +64,7 @@ struct HslColor // progress - (0.0 - 1.0) value where 0.0 will return left and 1.0 will return right // and a value between will blend the color weighted linearly between them // ------------------------------------------------------------------------ - static HslColor LinearBlend(HslColor left, HslColor right, float progress); + static HslColor LinearBlend(const HslColor& left, const HslColor& right, float progress); // ------------------------------------------------------------------------ // Hue, Saturation, Lightness color members diff --git a/src/internal/HtmlColor.h b/src/internal/HtmlColor.h index 286ed7c1..80974bd1 100644 --- a/src/internal/HtmlColor.h +++ b/src/internal/HtmlColor.h @@ -62,7 +62,25 @@ struct HtmlColor { }; - + // ------------------------------------------------------------------------ + // BilinearBlend between four colors by the amount defined by 2d variable + // c00 - upper left quadrant color + // c01 - upper right quadrant color + // c10 - lower left quadrant color + // c11 - lower right quadrant color + // x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space + // y - unit value (0.0 - 1.0) that defines the blend progress in vertical space + // ------------------------------------------------------------------------ + static HtmlColor BilinearBlend(const HtmlColor& c00, + const HtmlColor& c01, + const HtmlColor& c10, + const HtmlColor& c11, + float x, + float y) + { + return RgbColor::BilinearBlend(c00, c01, c10, c11, x, y); + } + // ------------------------------------------------------------------------ // Color member (0-0xffffff) where // 0xff0000 is red diff --git a/src/internal/Layouts.h b/src/internal/Layouts.h new file mode 100644 index 00000000..9a3184af --- /dev/null +++ b/src/internal/Layouts.h @@ -0,0 +1,425 @@ +#pragma once +/*------------------------------------------------------------------------- +Layout provides a collection of class objects that are used with NeoTopology +object. +They define the specific layout of pixels and do the math to change the 2d +cordinate space to 1d cordinate space + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + + +//----------------------------------------------------------------------------- +// RowMajor +//----------------------------------------------------------------------------- + +class RowMajorLayout; +class RowMajor90Layout; +class RowMajor180Layout; +class RowMajor270Layout; + +class RowMajorTilePreference +{ +public: + typedef RowMajorLayout EvenRowEvenColumnLayout; + typedef RowMajor270Layout EvenRowOddColumnLayout; + typedef RowMajor90Layout OddRowEvenColumnLayout; + typedef RowMajor180Layout OddRowOddColumnLayout; +}; + +// layout example of 4x4 +// 00 01 02 03 +// 04 05 06 07 +// 08 09 10 11 +// 12 13 14 15 +// +class RowMajorLayout : public RowMajorTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + return x + y * width; + } +}; + +// layout example of 4x4 +// 12 08 04 00 +// 13 09 05 01 +// 14 10 06 02 +// 15 11 07 03 +// +class RowMajor90Layout : public RowMajorTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + return (width - 1 - x) * height + y; + } +}; + +// layout example of 4x4 +// 15 14 13 12 +// 11 10 09 08 +// 07 06 05 04 +// 03 02 01 00 +// +class RowMajor180Layout : public RowMajorTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + return (width - 1 - x) + (height - 1 - y) * width; + } +}; + +// layout example of 4x4 +// 03 07 11 15 +// 02 06 10 14 +// 01 05 09 13 +// 00 04 08 12 +// +class RowMajor270Layout : public RowMajorTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + return x * height + (height - 1 - y); + } +}; + + +//----------------------------------------------------------------------------- +// ColumnMajor +//----------------------------------------------------------------------------- + +class ColumnMajorLayout; +class ColumnMajor90Layout; +class ColumnMajor180Layout; +class ColumnMajor270Layout; + +class ColumnMajorTilePreference +{ +public: + typedef ColumnMajorLayout EvenRowEvenColumnLayout; + typedef ColumnMajor270Layout EvenRowOddColumnLayout; + typedef ColumnMajor90Layout OddRowEvenColumnLayout; + typedef ColumnMajor180Layout OddRowOddColumnLayout; +}; + +// layout example of 4x4 +// 00 04 08 12 +// 01 05 09 13 +// 02 06 10 14 +// 03 07 11 15 +// +class ColumnMajorLayout : public ColumnMajorTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + return x * height + y; + } +}; + +// layout example of 4x4 +// 03 02 01 00 +// 07 06 05 04 +// 11 10 09 08 +// 15 14 13 12 +// +class ColumnMajor90Layout : public ColumnMajorTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + return (width - 1 - x) + y * width; + } +}; + +// layout example of 4x4 +// 15 11 07 03 +// 14 10 06 02 +// 13 09 05 01 +// 12 08 04 00 +// +class ColumnMajor180Layout : public ColumnMajorTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + return (width - 1 - x) * height + (height - 1 - y); + } +}; + +// layout example of 4x4 +// 12 13 14 15 +// 08 09 10 11 +// 04 05 06 07 +// 00 01 02 03 +// +class ColumnMajor270Layout : public ColumnMajorTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + return x + (height - 1 - y) * width; + } +}; + + +//----------------------------------------------------------------------------- +// RowMajorAlternating +//----------------------------------------------------------------------------- + +class RowMajorAlternating270Layout; +class RowMajorAlternating90Layout; + +class RowMajorAlternatingTilePreference +{ +public: + typedef RowMajorAlternating270Layout EvenRowEvenColumnLayout; + typedef RowMajorAlternating270Layout EvenRowOddColumnLayout; + typedef RowMajorAlternating90Layout OddRowEvenColumnLayout; + typedef RowMajorAlternating90Layout OddRowOddColumnLayout; +}; + +// layout example of 4x4 +// 00 01 02 03 +// 07 06 05 04 +// 08 09 10 11 +// 15 14 13 12 +// +class RowMajorAlternatingLayout : public RowMajorAlternatingTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + uint16_t index = y * width; + + if (y & 0x0001) + { + index += ((width - 1) - x); + } + else + { + index += x; + } + return index; + } +}; + +// layout example of 4x4 +// 15 08 07 00 +// 14 09 06 01 +// 13 10 05 02 +// 12 11 04 03 +// +class RowMajorAlternating90Layout : public RowMajorAlternatingTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + uint16_t mx = ((width - 1) - x); + uint16_t index = mx * height; + + if (mx & 0x0001) + { + index += ((height - 1) - y); + } + else + { + index += y; + } + return index; + } +}; + +// layout example of 4x4 +// 12 13 14 15 +// 11 10 09 08 +// 04 05 06 07 +// 03 02 01 00 +// +class RowMajorAlternating180Layout : public RowMajorAlternatingTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + uint16_t my = ((height - 1) - y); + uint16_t index = my * width; + + if (my & 0x0001) + { + index += x; + } + else + { + index += ((width - 1) - x); + } + return index; + } +}; + +// layout example of 4x4 +// 03 04 11 12 +// 02 05 10 13 +// 01 06 09 14 +// 00 07 08 15 +// +class RowMajorAlternating270Layout : public RowMajorAlternatingTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + uint16_t index = x * height; + + if (x & 0x0001) + { + index += y; + } + else + { + index += ((height - 1) - y); + } + return index; + } +}; + + +//----------------------------------------------------------------------------- +// ColumnMajorAlternating +//----------------------------------------------------------------------------- + +class ColumnMajorAlternatingLayout; +class ColumnMajorAlternating180Layout; + +class ColumnMajorAlternatingTilePreference +{ +public: + typedef ColumnMajorAlternatingLayout EvenRowEvenColumnLayout; + typedef ColumnMajorAlternatingLayout EvenRowOddColumnLayout; + typedef ColumnMajorAlternating180Layout OddRowEvenColumnLayout; + typedef ColumnMajorAlternating180Layout OddRowOddColumnLayout; +}; + +// layout example of 4x4 +// 00 07 08 15 +// 01 06 09 14 +// 02 05 10 13 +// 03 04 11 12 +// +class ColumnMajorAlternatingLayout : public ColumnMajorAlternatingTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + uint16_t index = x * height; + + if (x & 0x0001) + { + index += ((height - 1) - y); + } + else + { + index += y; + } + return index; + } +}; + +// layout example of 4x4 +// 03 02 01 00 +// 04 05 06 07 +// 11 10 09 08 +// 12 13 14 15 +// +class ColumnMajorAlternating90Layout : public ColumnMajorAlternatingTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + uint16_t index = y * width; + + if (y & 0x0001) + { + index += x; + } + else + { + index += ((width - 1) - x); + } + return index; + } +}; + +// layout example of 4x4 +// 12 11 04 03 +// 13 10 05 02 +// 14 09 06 01 +// 15 08 07 00 +// +class ColumnMajorAlternating180Layout : public ColumnMajorAlternatingTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + uint16_t mx = ((width - 1) - x); + uint16_t index = mx * height; + + if (mx & 0x0001) + { + index += y; + } + else + { + index += ((height - 1) - y); + } + return index; + } +}; + +// layout example of 4x4 +// 15 14 13 12 +// 08 09 10 11 +// 07 06 05 04 +// 00 01 02 03 +// +class ColumnMajorAlternating270Layout : public ColumnMajorAlternatingTilePreference +{ +public: + static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + { + uint16_t my = ((height - 1) - y); + uint16_t index = my * width; + + if (my & 0x0001) + { + index += ((width - 1) - x); + } + else + { + index += x; + } + return index; + } +}; diff --git a/src/internal/NeoColorFeatures.h b/src/internal/NeoColorFeatures.h index f5e637fe..41200dc3 100644 --- a/src/internal/NeoColorFeatures.h +++ b/src/internal/NeoColorFeatures.h @@ -30,26 +30,23 @@ class Neo3Elements { public: static const size_t PixelSize = 3; - static const RgbColor Empty(); static uint8_t* getPixelAddress(uint8_t* pPixels, uint16_t indexPixel) { - return &pPixels[indexPixel * PixelSize]; + return pPixels + indexPixel * PixelSize; } typedef RgbColor ColorObject; - }; class Neo4Elements { public: static const size_t PixelSize = 4; - static const RgbColor Empty(); static uint8_t* getPixelAddress(uint8_t* pPixels, uint16_t indexPixel) { - return &pPixels[indexPixel * PixelSize]; + return pPixels + indexPixel * PixelSize; } typedef RgbwColor ColorObject; diff --git a/src/internal/NeoEase.h b/src/internal/NeoEase.h index e52869cf..eaa50239 100644 --- a/src/internal/NeoEase.h +++ b/src/internal/NeoEase.h @@ -28,187 +28,197 @@ License along with NeoPixel. If not, see #ifdef ARDUINO_ARCH_AVR -typedef float(*AnimEaseFunction)(float linear); +typedef float(*AnimEaseFunction)(float unitValue); #else #undef max #undef min #include -typedef std::function AnimEaseFunction; +typedef std::function AnimEaseFunction; #endif class NeoEase { public: - static float QuadraticIn(float linear) + static float Linear(float unitValue) { - return linear * linear; + return unitValue; } - static float QuadraticOut(float linear) + static float QuadraticIn(float unitValue) { - return (-linear * (linear - 2.0f)); + return unitValue * unitValue; } - static float QuadraticInOut(float linear) + static float QuadraticOut(float unitValue) { - linear *= 2.0f; - if (linear < 1.0f) + return (-unitValue * (unitValue - 2.0f)); + } + + static float QuadraticInOut(float unitValue) + { + unitValue *= 2.0f; + if (unitValue < 1.0f) { - return (0.5f * linear * linear); + return (0.5f * unitValue * unitValue); } else { - linear -= 1.0f; - return (-0.5f * (linear * (linear - 2.0f) - 1.0f)); + unitValue -= 1.0f; + return (-0.5f * (unitValue * (unitValue - 2.0f) - 1.0f)); } } - static float CubicIn(float linear) + static float CubicIn(float unitValue) { - return (linear * linear * linear); + return (unitValue * unitValue * unitValue); } - static float CubicOut(float linear) + static float CubicOut(float unitValue) { - linear -= 1.0f; - return (linear * linear * linear + 1); + unitValue -= 1.0f; + return (unitValue * unitValue * unitValue + 1); } - static float CubicInOut(float linear) + static float CubicInOut(float unitValue) { - linear *= 2.0f; - if (linear < 1.0f) + unitValue *= 2.0f; + if (unitValue < 1.0f) { - return (0.5f * linear * linear * linear); + return (0.5f * unitValue * unitValue * unitValue); } else { - linear -= 2.0f; - return (0.5f * (linear * linear * linear + 2.0f)); + unitValue -= 2.0f; + return (0.5f * (unitValue * unitValue * unitValue + 2.0f)); } } - static float QuarticIn(float linear) + static float QuarticIn(float unitValue) { - return (linear * linear * linear * linear); + return (unitValue * unitValue * unitValue * unitValue); } - static float QuarticOut(float linear) + static float QuarticOut(float unitValue) { - linear -= 1.0f; - return -(linear * linear * linear * linear - 1); + unitValue -= 1.0f; + return -(unitValue * unitValue * unitValue * unitValue - 1); } - static float QuarticInOut(float linear) + static float QuarticInOut(float unitValue) { - linear *= 2.0f; - if (linear < 1.0f) + unitValue *= 2.0f; + if (unitValue < 1.0f) { - return (0.5f * linear * linear * linear * linear); + return (0.5f * unitValue * unitValue * unitValue * unitValue); } else { - linear -= 2.0f; - return (-0.5f * (linear * linear * linear * linear - 2.0f)); + unitValue -= 2.0f; + return (-0.5f * (unitValue * unitValue * unitValue * unitValue - 2.0f)); } } - static float QuinticIn(float linear) + static float QuinticIn(float unitValue) { - return (linear * linear * linear * linear * linear); + return (unitValue * unitValue * unitValue * unitValue * unitValue); } - static float QuinticOut(float linear) + static float QuinticOut(float unitValue) { - linear -= 1.0f; - return (linear * linear * linear * linear * linear + 1.0f); + unitValue -= 1.0f; + return (unitValue * unitValue * unitValue * unitValue * unitValue + 1.0f); } - static float QuinticInOut(float linear) + static float QuinticInOut(float unitValue) { - linear *= 2.0f; - if (linear < 1.0f) + unitValue *= 2.0f; + if (unitValue < 1.0f) { - return (0.5f * linear * linear * linear * linear * linear); + return (0.5f * unitValue * unitValue * unitValue * unitValue * unitValue); } else { - linear -= 2.0f; - return (0.5f * (linear * linear * linear * linear * linear + 2.0f)); + unitValue -= 2.0f; + return (0.5f * (unitValue * unitValue * unitValue * unitValue * unitValue + 2.0f)); } } - static float SinusoidalIn(float linear) + static float SinusoidalIn(float unitValue) { - return (-cos(linear * HALF_PI) + 1.0f); + return (-cos(unitValue * HALF_PI) + 1.0f); } - static float SinusoidalOut(float linear) + static float SinusoidalOut(float unitValue) { - return (sin(linear * HALF_PI)); + return (sin(unitValue * HALF_PI)); } - static float SinusoidalInOut(float linear) + static float SinusoidalInOut(float unitValue) { - return -0.5 * (cos(PI * linear) - 1.0f); + return -0.5 * (cos(PI * unitValue) - 1.0f); } - static float ExponentialIn(float linear) + static float ExponentialIn(float unitValue) { - return (pow(2, 10.0f * (linear - 1.0f))); + return (pow(2, 10.0f * (unitValue - 1.0f))); } - static float ExponentialOut(float linear) + static float ExponentialOut(float unitValue) { - return (-pow(2, -10.0f * linear) + 1.0f); + return (-pow(2, -10.0f * unitValue) + 1.0f); } - static float ExponentialInOut(float linear) + static float ExponentialInOut(float unitValue) { - linear *= 2.0f; - if (linear < 1.0f) + unitValue *= 2.0f; + if (unitValue < 1.0f) { - return (0.5f * pow(2, 10.0f * (linear - 1.0f))); + return (0.5f * pow(2, 10.0f * (unitValue - 1.0f))); } else { - linear -= 1.0f; - return (0.5f * (-pow(2, -10.0f * linear) + 2.0f)); + unitValue -= 1.0f; + return (0.5f * (-pow(2, -10.0f * unitValue) + 2.0f)); } } - static float CircularIn(float linear) + static float CircularIn(float unitValue) { - if (linear == 1.0f) + if (unitValue == 1.0f) { return 1.0f; } else { - return (-(sqrt(1.0f - linear * linear) - 1.0f)); + return (-(sqrt(1.0f - unitValue * unitValue) - 1.0f)); } } - static float CircularOut(float linear) + static float CircularOut(float unitValue) { - linear -= 1.0f; - return (sqrt(1.0f - linear * linear)); + unitValue -= 1.0f; + return (sqrt(1.0f - unitValue * unitValue)); } - static float CircularInOut(float linear) + static float CircularInOut(float unitValue) { - linear *= 2.0f; - if (linear < 1.0f) + unitValue *= 2.0f; + if (unitValue < 1.0f) { - return (-0.5f * (sqrt(1.0f - linear * linear) - 1)); + return (-0.5f * (sqrt(1.0f - unitValue * unitValue) - 1)); } else { - linear -= 2.0f; - return (0.5f * (sqrt(1.0f - linear * linear) + 1.0f)); + unitValue -= 2.0f; + return (0.5f * (sqrt(1.0f - unitValue * unitValue) + 1.0f)); } } + + static float Gamma(float unitValue) + { + return pow(unitValue, 1.0f / 0.45f); + } }; \ No newline at end of file diff --git a/src/internal/NeoGamma.h b/src/internal/NeoGamma.h new file mode 100644 index 00000000..68d1c7c1 --- /dev/null +++ b/src/internal/NeoGamma.h @@ -0,0 +1,90 @@ +/*------------------------------------------------------------------------- +NeoPixelGamma class is used to correct RGB colors for human eye gamma levels + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ +#pragma once + +// NeoGammaEquationMethod uses no memory but is slower than NeoGammaTableMethod +class NeoGammaEquationMethod +{ +public: + static uint8_t Correct(uint8_t value) + { + return static_cast(255.0f * NeoEase::Gamma(value / 255.0f) + 0.5f); + } +}; + +// NeoGammaTableMethod uses 256 bytes of memory, but is significantly faster +class NeoGammaTableMethod +{ +public: + static uint8_t Correct(uint8_t value) + { + return _table[value]; + } + +private: + static const uint8_t _table[256]; +}; + + +// use one of the method classes above as a converter for this template class +template class NeoGamma +{ +public: + RgbColor Correct(const RgbColor& original) + { + return RgbColor(T_METHOD::Correct(original.R), + T_METHOD::Correct(original.G), + T_METHOD::Correct(original.B)); + } + + RgbwColor Correct(const RgbwColor& original) + { + return RgbwColor(T_METHOD::Correct(original.R), + T_METHOD::Correct(original.G), + T_METHOD::Correct(original.B), + T_METHOD::Correct(original.W) ); + } +}; + + +const uint8_t NeoGammaTableMethod::_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, + 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, + 12, 12, 13, 13, 14, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, + 19, 20, 20, 21, 22, 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, + 29, 30, 30, 31, 32, 33, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, + 41, 42, 43, 43, 44, 45, 46, 47, 48, 49, 50, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 71, + 72, 73, 74, 75, 76, 77, 78, 80, 81, 82, 83, 84, 86, 87, 88, 89, + 91, 92, 93, 94, 96, 97, 98, 100, 101, 102, 104, 105, 106, 108, 109, 110, + 112, 113, 115, 116, 118, 119, 121, 122, 123, 125, 126, 128, 130, 131, 133, 134, + 136, 137, 139, 140, 142, 144, 145, 147, 149, 150, 152, 154, 155, 157, 159, 160, + 162, 164, 166, 167, 169, 171, 173, 175, 176, 178, 180, 182, 184, 186, 187, 189, + 191, 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221, + 223, 225, 227, 229, 231, 233, 235, 238, 240, 242, 244, 246, 248, 251, 253, 255 +}; diff --git a/src/internal/NeoMosaic.h b/src/internal/NeoMosaic.h new file mode 100644 index 00000000..f023d849 --- /dev/null +++ b/src/internal/NeoMosaic.h @@ -0,0 +1,157 @@ +#pragma once + +/*------------------------------------------------------------------------- +Mosiac provides a mapping feature of a 2d cordinate to linear 1d cordinate +It is used to map tiles of matricies of NeoPixels to a index on the NeoPixelBus +where the the matricies use a set of prefered topology and the tiles of +those matricies use the RowMajorAlternating layout + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + + +//----------------------------------------------------------------------------- +// class NeoMosaic +// Complex Tile layout class that reduces distance of the interconnects between +// the tiles by using different rotations of the layout at specific locations +// +// T_LAYOUT = the layout used for matrix panel (rotation is ignored) +// +// NOTE: The tiles in the mosaic are always laid out using RowMajorAlternating +// +//----------------------------------------------------------------------------- + +template class NeoMosaic +{ +public: + NeoMosaic(uint16_t topoWidth, uint16_t topoHeight, + uint16_t mosaicWidth, uint16_t mosaicHeight) : + _topoWidth(topoWidth), + _topoHeight(topoHeight), + _mosaicWidth(mosaicWidth), + _mosaicHeight(mosaicHeight) + { + } + + uint16_t Map(uint16_t x, uint16_t y) const + { + uint16_t localIndex; + uint16_t tileOffset; + + calculate(x, y, &localIndex, &tileOffset); + + return localIndex + tileOffset; + } + + NeoTopologyHint TopologyHint(uint16_t x, uint16_t y) const + { + uint16_t localIndex; + uint16_t tileOffset; + NeoTopologyHint result; + + calculate(x, y, &localIndex, &tileOffset); + + if (localIndex == 0) + { + result = NeoTopologyHint_FirstOnPanel; + } + else if (localIndex == (_topoWidth * _topoHeight - 1)) + { + result = NeoTopologyHint_LastOnPanel; + } + else + { + result = NeoTopologyHint_InPanel; + } + + return result; + } + + uint16_t getWidth() const + { + return _topoWidth * _mosaicWidth; + } + + uint16_t getHeight() const + { + return _topoHeight * _mosaicHeight; + } + +private: + const uint16_t _topoWidth; + const uint16_t _topoHeight; + const uint16_t _mosaicWidth; + const uint16_t _mosaicHeight; + + void calculate(uint16_t x, uint16_t y, uint16_t* pLocalIndex, uint16_t* pTileOffset) const + { + uint16_t totalWidth = getWidth(); + uint16_t totalHeight = getHeight(); + + if (x >= totalWidth) + { + x = totalWidth - 1; + } + + if (y >= totalHeight) + { + y = totalHeight - 1; + } + + uint16_t tileX = x / _topoWidth; + uint16_t topoX = x % _topoWidth; + + uint16_t tileY = y / _topoHeight; + uint16_t topoY = y % _topoHeight; + + *pTileOffset = RowMajorAlternatingLayout::Map(_mosaicWidth, + _mosaicHeight, + tileX, + tileY) * _topoWidth * _topoHeight; + + if (tileX & 0x0001) + { + // odd columns + if (tileY & 0x0001) + { + *pLocalIndex = T_LAYOUT::OddRowOddColumnLayout::Map(_topoWidth, _topoHeight, topoX, topoY); + } + else + { + *pLocalIndex = T_LAYOUT::EvenRowOddColumnLayout::Map(_topoWidth, _topoHeight, topoX, topoY); + } + } + else + { + // even columns + if (tileY & 0x0001) + { + *pLocalIndex = T_LAYOUT::OddRowEvenColumnLayout::Map(_topoWidth, _topoHeight, topoX, topoY); + } + else + { + *pLocalIndex = T_LAYOUT::EvenRowEvenColumnLayout::Map(_topoWidth, _topoHeight, topoX, topoY); + } + } + } +}; \ No newline at end of file diff --git a/src/internal/NeoPixelAnimator.cpp b/src/internal/NeoPixelAnimator.cpp index 74552c25..7f908b1a 100644 --- a/src/internal/NeoPixelAnimator.cpp +++ b/src/internal/NeoPixelAnimator.cpp @@ -32,10 +32,7 @@ NeoPixelAnimator::NeoPixelAnimator(uint16_t countAnimations, uint16_t timeScale) _activeAnimations(0), _isRunning(true) { - // due to strange esp include header issues, min and max aren't usable - _timeScale = (timeScale < 1) ? (1) : (timeScale > 32768) ? 32768 : timeScale; - //_timeScale = max(1, min(32768, timeScale)); - + setTimeScale(timeScale); _animations = new AnimationContext[_countAnimations]; } diff --git a/src/internal/NeoTiles.h b/src/internal/NeoTiles.h new file mode 100644 index 00000000..8babebdf --- /dev/null +++ b/src/internal/NeoTiles.h @@ -0,0 +1,124 @@ +#pragma once + +/*------------------------------------------------------------------------- +NeoTiles provides a mapping feature of a 2d cordinate to linear 1d cordinate +It is used to map tiles of matricies of NeoPixels to a index on the NeoPixelBus +where the the matricies use one topology and the tiles of those matricies can +use another + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +//----------------------------------------------------------------------------- +// class NeoTiles +// Simple template Tile layout class +// T_MATRIX_LAYOUT = the layout used on the pixel matrix panel (a tile) +// T_TILE_LAYOUT = the layout used for the tiles. +// +//----------------------------------------------------------------------------- +template class NeoTiles +{ +public: + NeoTiles(uint16_t topoWidth, uint16_t topoHeight, + uint16_t tilesWidth, uint16_t tilesHeight) : + _topo(topoWidth, topoHeight), + _width(tilesWidth), + _height(tilesHeight) + { + } + + uint16_t Map(uint16_t x, uint16_t y) const + { + uint16_t localIndex; + uint16_t tileOffset; + + calculate(x, y, &localIndex, &tileOffset); + + return localIndex + tileOffset; + } + + NeoTopologyHint TopologyHint(uint16_t x, uint16_t y) const + { + uint16_t localIndex; + uint16_t tileOffset; + NeoTopologyHint result; + + calculate(x, y, &localIndex, &tileOffset); + + if (localIndex == 0) + { + result = NeoTopologyHint_FirstOnPanel; + } + else if (localIndex == (_topo.getWidth() * _topo.getHeight() - 1)) + { + result = NeoTopologyHint_LastOnPanel; + } + else + { + result = NeoTopologyHint_InPanel; + } + + return result; + } + + uint16_t getWidth() const + { + return _width * _topo.getWidth(); + } + + uint16_t getHeight() const + { + return _height * _topo.getHeight(); + } + +private: + const NeoTopology _topo; + const uint16_t _width; + const uint16_t _height; + + void calculate(uint16_t x, uint16_t y, uint16_t* pLocalIndex, uint16_t* pTileOffset) const + { + uint16_t totalWidth = getWidth(); + uint16_t totalHeight = getHeight(); + + if (x >= totalWidth) + { + x = totalWidth - 1; + } + + if (y >= totalHeight) + { + y = totalHeight - 1; + } + + uint16_t tileX = x / _topo.getWidth(); + uint16_t topoX = x % _topo.getWidth(); + + uint16_t tileY = y / _topo.getHeight(); + uint16_t topoY = y % _topo.getHeight(); + + *pTileOffset = T_TILE_LAYOUT::Map(_width, _height, tileX, tileY) * _topo.getWidth() * _topo.getHeight(); + *pLocalIndex = _topo.Map(topoX, topoY); + } +}; + diff --git a/src/internal/NeoTopology.h b/src/internal/NeoTopology.h new file mode 100644 index 00000000..f4ec63af --- /dev/null +++ b/src/internal/NeoTopology.h @@ -0,0 +1,73 @@ +#pragma once + +/*------------------------------------------------------------------------- +NeoTopology provides a mapping feature of a 2d cordinate to linear 1d cordinate +It is used to map a matrix of NeoPixels to a index on the NeoPixelBus + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +enum NeoTopologyHint +{ + NeoTopologyHint_FirstOnPanel, + NeoTopologyHint_InPanel, + NeoTopologyHint_LastOnPanel, +}; + +template class NeoTopology +{ +public: + NeoTopology(uint16_t width, uint16_t height) : + _width(width), + _height(height) + { + + } + + uint16_t Map(uint16_t x, uint16_t y) const + { + if (x >= _width) + { + x = _width - 1; + } + if (y >= _height) + { + y = _height - 1; + } + return T_LAYOUT::Map(_width, _height, x, y); + } + + uint16_t getWidth() const + { + return _width; + } + + uint16_t getHeight() const + { + return _height; + } + +private: + const uint16_t _width; + const uint16_t _height; +}; diff --git a/src/internal/RgbColor.cpp b/src/internal/RgbColor.cpp index c8e1c758..261c9205 100644 --- a/src/internal/RgbColor.cpp +++ b/src/internal/RgbColor.cpp @@ -217,9 +217,27 @@ void RgbColor::Lighten(uint8_t delta) } } -RgbColor RgbColor::LinearBlend(RgbColor left, RgbColor right, float progress) +RgbColor RgbColor::LinearBlend(const RgbColor& left, const RgbColor& right, float progress) { return RgbColor( left.R + ((right.R - left.R) * progress), left.G + ((right.G - left.G) * progress), left.B + ((right.B - left.B) * progress)); +} + +RgbColor RgbColor::BilinearBlend(const RgbColor& c00, + const RgbColor& c01, + const RgbColor& c10, + const RgbColor& c11, + float x, + float y) +{ + float v00 = (1.0f - x) * (1.0f - y); + float v10 = x * (1.0f - y); + float v01 = (1.0f - x) * y; + float v11 = x * y; + + return RgbColor( + c00.R * v00 + c10.R * v10 + c01.R * v01 + c11.R * v11, + c00.G * v00 + c10.G * v10 + c01.G * v01 + c11.G * v11, + c00.B * v00 + c10.B * v10 + c01.B * v01 + c11.B * v11); } \ No newline at end of file diff --git a/src/internal/RgbColor.h b/src/internal/RgbColor.h index 5a7c8300..1ee9986a 100644 --- a/src/internal/RgbColor.h +++ b/src/internal/RgbColor.h @@ -79,6 +79,16 @@ struct RgbColor { }; + bool operator==(const RgbColor& other) const + { + return (R == other.R && G == other.G && B == other.B); + }; + + bool operator!=(const RgbColor& other) const + { + return !(*this == other); + }; + // ------------------------------------------------------------------------ // CalculateBrightness will calculate the overall brightness // NOTE: This is a simple linear brightness @@ -93,7 +103,7 @@ struct RgbColor void Darken(uint8_t delta); // ------------------------------------------------------------------------ - // Lighten will adjust the color by the given delta toward white + // Lighten will adjust the color by the given delta toward ite // NOTE: This is a simple linear change // delta - (0-255) the amount to lighten the color // ------------------------------------------------------------------------ @@ -106,8 +116,24 @@ struct RgbColor // progress - (0.0 - 1.0) value where 0 will return left and 1.0 will return right // and a value between will blend the color weighted linearly between them // ------------------------------------------------------------------------ - static RgbColor LinearBlend(RgbColor left, RgbColor right, float progress); + static RgbColor LinearBlend(const RgbColor& left, const RgbColor& right, float progress); + // ------------------------------------------------------------------------ + // BilinearBlend between four colors by the amount defined by 2d variable + // c00 - upper left quadrant color + // c01 - upper right quadrant color + // c10 - lower left quadrant color + // c11 - lower right quadrant color + // x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space + // y - unit value (0.0 - 1.0) that defines the blend progress in vertical space + // ------------------------------------------------------------------------ + static RgbColor BilinearBlend(const RgbColor& c00, + const RgbColor& c01, + const RgbColor& c10, + const RgbColor& c11, + float x, + float y); + // ------------------------------------------------------------------------ // Red, Green, Blue color members (0-255) where // (0,0,0) is black and (255,255,255) is white diff --git a/src/internal/RgbwColor.cpp b/src/internal/RgbwColor.cpp index e4abdae6..a1ee31ef 100644 --- a/src/internal/RgbwColor.cpp +++ b/src/internal/RgbwColor.cpp @@ -29,13 +29,13 @@ License along with NeoPixel. If not, see #include "HsbColor.h" #include "RgbwColor.h" -RgbwColor::RgbwColor(HslColor color) +RgbwColor::RgbwColor(const HslColor& color) { RgbColor rgbColor(color); *this = rgbColor; } -RgbwColor::RgbwColor(HsbColor color) +RgbwColor::RgbwColor(const HsbColor& color) { RgbColor rgbColor(color); *this = rgbColor; @@ -137,10 +137,29 @@ void RgbwColor::Lighten(uint8_t delta) } } -RgbwColor RgbwColor::LinearBlend(RgbwColor left, RgbwColor right, float progress) +RgbwColor RgbwColor::LinearBlend(const RgbwColor& left, const RgbwColor& right, float progress) { return RgbwColor( left.R + ((right.R - left.R) * progress), left.G + ((right.G - left.G) * progress), left.B + ((right.B - left.B) * progress), left.W + ((right.W - left.W) * progress) ); +} + +RgbwColor RgbwColor::BilinearBlend(const RgbwColor& c00, + const RgbwColor& c01, + const RgbwColor& c10, + const RgbwColor& c11, + float x, + float y) +{ + float v00 = (1.0f - x) * (1.0f - y); + float v10 = x * (1.0f - y); + float v01 = (1.0f - x) * y; + float v11 = x * y; + + return RgbwColor( + c00.R * v00 + c10.R * v10 + c01.R * v01 + c11.R * v11, + c00.G * v00 + c10.G * v10 + c01.G * v01 + c11.G * v11, + c00.B * v00 + c10.B * v10 + c01.B * v01 + c11.B * v11, + c00.W * v00 + c10.W * v10 + c01.W * v01 + c11.W * v11 ); } \ No newline at end of file diff --git a/src/internal/RgbwColor.h b/src/internal/RgbwColor.h index 795109bf..41f0d05e 100644 --- a/src/internal/RgbwColor.h +++ b/src/internal/RgbwColor.h @@ -59,7 +59,7 @@ struct RgbwColor // ------------------------------------------------------------------------ // Construct a RgbwColor using RgbColor // ------------------------------------------------------------------------ - RgbwColor(RgbColor color) : + RgbwColor(const RgbColor& color) : R(color.R), G(color.G), B(color.B), @@ -71,12 +71,12 @@ struct RgbwColor // ------------------------------------------------------------------------ // Construct a RgbwColor using HslColor // ------------------------------------------------------------------------ - RgbwColor(HslColor color); + RgbwColor(const HslColor& color); // ------------------------------------------------------------------------ // Construct a RgbwColor using HsbColor // ------------------------------------------------------------------------ - RgbwColor(HsbColor color); + RgbwColor(const HsbColor& color); // ------------------------------------------------------------------------ // Construct a RgbwColor that will have its values set in latter operations @@ -86,6 +86,16 @@ struct RgbwColor { }; + bool operator==(const RgbwColor& other) const + { + return (R == other.R && G == other.G && B == other.B && W == other.W); + }; + + bool operator!=(const RgbwColor& other) const + { + return !(*this == other); + }; + // ------------------------------------------------------------------------ // Returns if the color is grey, all values are equal other than white // ------------------------------------------------------------------------ @@ -130,8 +140,24 @@ struct RgbwColor // progress - (0.0 - 1.0) value where 0 will return left and 1.0 will return right // and a value between will blend the color weighted linearly between them // ------------------------------------------------------------------------ - static RgbwColor LinearBlend(RgbwColor left, RgbwColor right, float progress); + static RgbwColor LinearBlend(const RgbwColor& left, const RgbwColor& right, float progress); + // ------------------------------------------------------------------------ + // BilinearBlend between four colors by the amount defined by 2d variable + // c00 - upper left quadrant color + // c01 - upper right quadrant color + // c10 - lower left quadrant color + // c11 - lower right quadrant color + // x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space + // y - unit value (0.0 - 1.0) that defines the blend progress in vertical space + // ------------------------------------------------------------------------ + static RgbwColor BilinearBlend(const RgbwColor& c00, + const RgbwColor& c01, + const RgbwColor& c10, + const RgbwColor& c11, + float x, + float y); + // ------------------------------------------------------------------------ // Red, Green, Blue, White color members (0-255) where // (0,0,0,0) is black and (255,255,255, 0) and (0,0,0,255) is white