mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-04 03:11:43 +00:00
[wpilib] Allow LED pattern gradients to be discontinuous (#7174)
This commit is contained in:
@@ -263,7 +263,8 @@ LEDPattern LEDPattern::Steps(
|
||||
return Steps(std::span{steps.begin(), steps.end()});
|
||||
}
|
||||
|
||||
LEDPattern LEDPattern::Gradient(std::span<const Color> colors) {
|
||||
LEDPattern LEDPattern::Gradient(GradientType type,
|
||||
std::span<const Color> colors) {
|
||||
if (colors.size() == 0) {
|
||||
// no colors specified
|
||||
return LEDPattern::Off();
|
||||
@@ -273,11 +274,19 @@ LEDPattern LEDPattern::Gradient(std::span<const Color> colors) {
|
||||
return LEDPattern::Solid(colors[0]);
|
||||
}
|
||||
|
||||
return LEDPattern{[colors = std::vector(colors.begin(), colors.end())](
|
||||
return LEDPattern{[type, colors = std::vector(colors.begin(), colors.end())](
|
||||
auto data, auto writer) {
|
||||
size_t numSegments = colors.size();
|
||||
auto bufLen = data.size();
|
||||
int ledsPerSegment = bufLen / numSegments;
|
||||
int ledsPerSegment = 0;
|
||||
switch (type) {
|
||||
case kContinuous:
|
||||
ledsPerSegment = bufLen / numSegments;
|
||||
break;
|
||||
case kDiscontinuous:
|
||||
ledsPerSegment = (bufLen - 1) / (numSegments - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t led = 0; led < bufLen; led++) {
|
||||
int colorIndex = (led / ledsPerSegment) % numSegments;
|
||||
@@ -295,8 +304,9 @@ LEDPattern LEDPattern::Gradient(std::span<const Color> colors) {
|
||||
}};
|
||||
}
|
||||
|
||||
LEDPattern LEDPattern::Gradient(std::initializer_list<Color> colors) {
|
||||
return Gradient(std::span{colors.begin(), colors.end()});
|
||||
LEDPattern LEDPattern::Gradient(GradientType type,
|
||||
std::initializer_list<Color> colors) {
|
||||
return Gradient(type, std::span{colors.begin(), colors.end()});
|
||||
}
|
||||
|
||||
LEDPattern LEDPattern::Rainbow(int saturation, int value) {
|
||||
|
||||
@@ -315,29 +315,52 @@ class LEDPattern {
|
||||
static LEDPattern Steps(
|
||||
std::initializer_list<std::pair<double, Color>> steps);
|
||||
|
||||
/**
|
||||
* Creates a pattern that displays a non-animated gradient of colors across
|
||||
* the entire length of the LED strip. The gradient wraps around so the start
|
||||
* and end of the strip are the same color, which allows the gradient to be
|
||||
* modified with a scrolling effect with no discontinuities. Colors are evenly
|
||||
* distributed along the full length of the LED strip.
|
||||
*
|
||||
* @param colors the colors to display in the gradient
|
||||
* @return a motionless gradient pattern
|
||||
*/
|
||||
static LEDPattern Gradient(std::span<const Color> colors);
|
||||
/** Types of gradients. */
|
||||
enum GradientType {
|
||||
/**
|
||||
* A continuous gradient, where the gradient wraps around to allow for
|
||||
* seamless scrolling effects.
|
||||
*/
|
||||
kContinuous,
|
||||
/**
|
||||
* A discontinuous gradient, where the first pixel is set to the first color
|
||||
* of the gradient and the final pixel is set to the last color of the
|
||||
* gradient. There is no wrapping effect, so scrolling effects will display
|
||||
* an obvious seam.
|
||||
*/
|
||||
kDiscontinuous
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a pattern that displays a non-animated gradient of colors across
|
||||
* the entire length of the LED strip. The gradient wraps around so the start
|
||||
* and end of the strip are the same color, which allows the gradient to be
|
||||
* modified with a scrolling effect with no discontinuities. Colors are evenly
|
||||
* distributed along the full length of the LED strip.
|
||||
* the entire length of the LED strip. Colors are evenly distributed along the
|
||||
* full length of the LED strip. The gradient type is configured with the
|
||||
* {@code type} parameter, allowing the gradient to be either continuous (no
|
||||
* seams, good for scrolling effects) or discontinuous (a clear seam is
|
||||
* visible, but the gradient applies to the full length of the LED strip
|
||||
* without needing to use some space for wrapping).
|
||||
*
|
||||
* @param type the type of gradient (continuous or discontinuous)
|
||||
* @param colors the colors to display in the gradient
|
||||
* @return a motionless gradient pattern
|
||||
*/
|
||||
static LEDPattern Gradient(std::initializer_list<Color> colors);
|
||||
static LEDPattern Gradient(GradientType type, std::span<const Color> colors);
|
||||
|
||||
/**
|
||||
* Creates a pattern that displays a non-animated gradient of colors across
|
||||
* the entire length of the LED strip. Colors are evenly distributed along the
|
||||
* full length of the LED strip. The gradient type is configured with the
|
||||
* {@code type} parameter, allowing the gradient to be either continuous (no
|
||||
* seams, good for scrolling effects) or discontinuous (a clear seam is
|
||||
* visible, but the gradient applies to the full length of the LED strip
|
||||
* without needing to use some space for wrapping).
|
||||
*
|
||||
* @param type the type of gradient (continuous or discontinuous)
|
||||
* @param colors the colors to display in the gradient
|
||||
* @return a motionless gradient pattern
|
||||
*/
|
||||
static LEDPattern Gradient(GradientType type,
|
||||
std::initializer_list<Color> colors);
|
||||
|
||||
/**
|
||||
* Creates an LED pattern that displays a rainbow across the color wheel. The
|
||||
|
||||
@@ -48,7 +48,8 @@ TEST(LEDPatternTest, SolidColor) {
|
||||
|
||||
TEST(LEDPatternTest, EmptyGradientSetsToBlack) {
|
||||
std::array<Color, 0> colors;
|
||||
LEDPattern pattern = LEDPattern::Gradient(colors);
|
||||
LEDPattern pattern =
|
||||
LEDPattern::Gradient(LEDPattern::GradientType::kContinuous, colors);
|
||||
std::array<AddressableLED::LEDData, 5> buffer;
|
||||
pattern.ApplyTo(buffer);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
@@ -58,7 +59,8 @@ TEST(LEDPatternTest, EmptyGradientSetsToBlack) {
|
||||
|
||||
TEST(LEDPatternTest, SingleColorGradientSetsSolid) {
|
||||
std::array<Color, 1> colors{Color::kYellow};
|
||||
LEDPattern pattern = LEDPattern::Gradient(colors);
|
||||
LEDPattern pattern =
|
||||
LEDPattern::Gradient(LEDPattern::GradientType::kContinuous, colors);
|
||||
std::array<AddressableLED::LEDData, 5> buffer;
|
||||
pattern.ApplyTo(buffer);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
@@ -68,7 +70,8 @@ TEST(LEDPatternTest, SingleColorGradientSetsSolid) {
|
||||
|
||||
TEST(LEDPatternTest, Gradient2Colors) {
|
||||
std::array<Color, 2> colors{Color::kYellow, Color::kPurple};
|
||||
LEDPattern pattern = LEDPattern::Gradient(colors);
|
||||
LEDPattern pattern =
|
||||
LEDPattern::Gradient(LEDPattern::GradientType::kContinuous, colors);
|
||||
std::array<AddressableLED::LEDData, 99> buffer;
|
||||
pattern.ApplyTo(buffer);
|
||||
AssertIndexColor(buffer, 0, Color::kYellow);
|
||||
@@ -80,9 +83,21 @@ TEST(LEDPatternTest, Gradient2Colors) {
|
||||
AssertIndexColor(buffer, 98, Color::kYellow);
|
||||
}
|
||||
|
||||
TEST(LEDPatternTest, DiscontinuousGradient2Colors) {
|
||||
std::array<Color, 2> colors{Color::kYellow, Color::kPurple};
|
||||
LEDPattern pattern =
|
||||
LEDPattern::Gradient(LEDPattern::GradientType::kDiscontinuous, colors);
|
||||
std::array<AddressableLED::LEDData, 99> buffer;
|
||||
pattern.ApplyTo(buffer);
|
||||
AssertIndexColor(buffer, 0, Color::kYellow);
|
||||
AssertIndexColor(buffer, 49, LerpColors(Color::kYellow, Color::kPurple, 0.5));
|
||||
AssertIndexColor(buffer, 98, Color::kPurple);
|
||||
}
|
||||
|
||||
TEST(LEDPatternTest, Gradient3Colors) {
|
||||
std::array<Color, 3> colors{Color::kYellow, Color::kPurple, Color::kWhite};
|
||||
LEDPattern pattern = LEDPattern::Gradient(colors);
|
||||
LEDPattern pattern =
|
||||
LEDPattern::Gradient(LEDPattern::GradientType::kContinuous, colors);
|
||||
std::array<AddressableLED::LEDData, 99> buffer;
|
||||
pattern.ApplyTo(buffer);
|
||||
|
||||
@@ -99,6 +114,20 @@ TEST(LEDPatternTest, Gradient3Colors) {
|
||||
LerpColors(Color::kWhite, Color::kYellow, 32 / 33.0));
|
||||
}
|
||||
|
||||
TEST(LEDPatternTest, DiscontinuousGradient3Colors) {
|
||||
std::array<Color, 3> colors{Color::kYellow, Color::kPurple, Color::kWhite};
|
||||
LEDPattern pattern =
|
||||
LEDPattern::Gradient(LEDPattern::GradientType::kDiscontinuous, colors);
|
||||
std::array<AddressableLED::LEDData, 101> buffer;
|
||||
pattern.ApplyTo(buffer);
|
||||
|
||||
AssertIndexColor(buffer, 0, Color::kYellow);
|
||||
AssertIndexColor(buffer, 25, LerpColors(Color::kYellow, Color::kPurple, 0.5));
|
||||
AssertIndexColor(buffer, 50, Color::kPurple);
|
||||
AssertIndexColor(buffer, 75, LerpColors(Color::kPurple, Color::kWhite, 0.5));
|
||||
AssertIndexColor(buffer, 100, Color::kWhite);
|
||||
}
|
||||
|
||||
TEST(LEDPatternTest, EmptyStepsSetsToBlack) {
|
||||
std::array<std::pair<double, Color>, 0> steps;
|
||||
LEDPattern pattern = LEDPattern::Steps(steps);
|
||||
|
||||
@@ -569,16 +569,35 @@ public interface LEDPattern {
|
||||
};
|
||||
}
|
||||
|
||||
/** Types of gradients. */
|
||||
enum GradientType {
|
||||
/**
|
||||
* A continuous gradient, where the gradient wraps around to allow for seamless scrolling
|
||||
* effects.
|
||||
*/
|
||||
kContinuous,
|
||||
|
||||
/**
|
||||
* A discontinuous gradient, where the first pixel is set to the first color of the gradient and
|
||||
* the final pixel is set to the last color of the gradient. There is no wrapping effect, so
|
||||
* scrolling effects will display an obvious seam.
|
||||
*/
|
||||
kDiscontinuous
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a pattern that displays a non-animated gradient of colors across the entire length of
|
||||
* the LED strip. The gradient wraps around so the start and end of the strip are the same color,
|
||||
* which allows the gradient to be modified with a scrolling effect with no discontinuities.
|
||||
* Colors are evenly distributed along the full length of the LED strip.
|
||||
* the LED strip. Colors are evenly distributed along the full length of the LED strip. The
|
||||
* gradient type is configured with the {@code type} parameter, allowing the gradient to be either
|
||||
* continuous (no seams, good for scrolling effects) or discontinuous (a clear seam is visible,
|
||||
* but the gradient applies to the full length of the LED strip without needing to use some space
|
||||
* for wrapping).
|
||||
*
|
||||
* @param type the type of gradient (continuous or discontinuous)
|
||||
* @param colors the colors to display in the gradient
|
||||
* @return a motionless gradient pattern
|
||||
*/
|
||||
static LEDPattern gradient(Color... colors) {
|
||||
static LEDPattern gradient(GradientType type, Color... colors) {
|
||||
if (colors.length == 0) {
|
||||
// Nothing to display
|
||||
DriverStation.reportWarning("Creating a gradient with no colors!", false);
|
||||
@@ -595,7 +614,11 @@ public interface LEDPattern {
|
||||
|
||||
return (reader, writer) -> {
|
||||
int bufLen = reader.getLength();
|
||||
int ledsPerSegment = bufLen / numSegments;
|
||||
int ledsPerSegment =
|
||||
switch (type) {
|
||||
case kContinuous -> bufLen / numSegments;
|
||||
case kDiscontinuous -> (bufLen - 1) / (numSegments - 1);
|
||||
};
|
||||
|
||||
for (int led = 0; led < bufLen; led++) {
|
||||
int colorIndex = (led / ledsPerSegment) % numSegments;
|
||||
|
||||
@@ -11,6 +11,8 @@ import static edu.wpi.first.units.Units.Microseconds;
|
||||
import static edu.wpi.first.units.Units.Percent;
|
||||
import static edu.wpi.first.units.Units.Seconds;
|
||||
import static edu.wpi.first.units.Units.Value;
|
||||
import static edu.wpi.first.wpilibj.LEDPattern.GradientType.kContinuous;
|
||||
import static edu.wpi.first.wpilibj.LEDPattern.GradientType.kDiscontinuous;
|
||||
import static edu.wpi.first.wpilibj.util.Color.kBlack;
|
||||
import static edu.wpi.first.wpilibj.util.Color.kBlue;
|
||||
import static edu.wpi.first.wpilibj.util.Color.kLime;
|
||||
@@ -80,7 +82,7 @@ class LEDPatternTest {
|
||||
|
||||
@Test
|
||||
void gradient0SetsToBlack() {
|
||||
LEDPattern pattern = LEDPattern.gradient();
|
||||
LEDPattern pattern = LEDPattern.gradient(kContinuous);
|
||||
AddressableLEDBuffer buffer = new AddressableLEDBuffer(99);
|
||||
for (int i = 0; i < buffer.getLength(); i++) {
|
||||
buffer.setRGB(i, 127, 128, 129);
|
||||
@@ -95,7 +97,7 @@ class LEDPatternTest {
|
||||
|
||||
@Test
|
||||
void gradient1SetsToSolid() {
|
||||
LEDPattern pattern = LEDPattern.gradient(kYellow);
|
||||
LEDPattern pattern = LEDPattern.gradient(kContinuous, kYellow);
|
||||
|
||||
AddressableLEDBuffer buffer = new AddressableLEDBuffer(99);
|
||||
pattern.applyTo(buffer);
|
||||
@@ -106,8 +108,8 @@ class LEDPatternTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void gradient2Colors() {
|
||||
LEDPattern pattern = LEDPattern.gradient(kYellow, kPurple);
|
||||
void continuousGradient2Colors() {
|
||||
LEDPattern pattern = LEDPattern.gradient(kContinuous, kYellow, kPurple);
|
||||
|
||||
AddressableLEDBuffer buffer = new AddressableLEDBuffer(99);
|
||||
pattern.applyTo(buffer);
|
||||
@@ -119,9 +121,21 @@ class LEDPatternTest {
|
||||
assertColorEquals(kYellow, buffer.getLED(98));
|
||||
}
|
||||
|
||||
@Test
|
||||
void discontinuousGradient2Colors() {
|
||||
LEDPattern pattern = LEDPattern.gradient(kDiscontinuous, kYellow, kPurple);
|
||||
|
||||
AddressableLEDBuffer buffer = new AddressableLEDBuffer(99);
|
||||
pattern.applyTo(buffer);
|
||||
|
||||
assertColorEquals(kYellow, buffer.getLED(0));
|
||||
assertColorEquals(Color.lerpRGB(kYellow, kPurple, 0.5), buffer.getLED(49));
|
||||
assertColorEquals(kPurple, buffer.getLED(98));
|
||||
}
|
||||
|
||||
@Test
|
||||
void gradient3Colors() {
|
||||
LEDPattern pattern = LEDPattern.gradient(kYellow, kPurple, kWhite);
|
||||
LEDPattern pattern = LEDPattern.gradient(kContinuous, kYellow, kPurple, kWhite);
|
||||
AddressableLEDBuffer buffer = new AddressableLEDBuffer(99);
|
||||
pattern.applyTo(buffer);
|
||||
|
||||
@@ -134,6 +148,19 @@ class LEDPatternTest {
|
||||
assertColorEquals(Color.lerpRGB(kWhite, kYellow, 32.0 / 33.0), buffer.getLED(98));
|
||||
}
|
||||
|
||||
@Test
|
||||
void discontinuousGradient3Colors() {
|
||||
LEDPattern pattern = LEDPattern.gradient(kDiscontinuous, kYellow, kPurple, kWhite);
|
||||
AddressableLEDBuffer buffer = new AddressableLEDBuffer(101);
|
||||
pattern.applyTo(buffer);
|
||||
|
||||
assertColorEquals(kYellow, buffer.getLED(0));
|
||||
assertColorEquals(Color.lerpRGB(kYellow, kPurple, 0.5), buffer.getLED(25));
|
||||
assertColorEquals(kPurple, buffer.getLED(50));
|
||||
assertColorEquals(Color.lerpRGB(kPurple, kWhite, 0.5), buffer.getLED(75));
|
||||
assertColorEquals(kWhite, buffer.getLED(100));
|
||||
}
|
||||
|
||||
@Test
|
||||
void step0SetsToBlack() {
|
||||
LEDPattern pattern = LEDPattern.steps(Map.of());
|
||||
|
||||
Reference in New Issue
Block a user