1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <functional>
#include <span>
#include <utility>
#include <units/frequency.h>
#include <units/length.h>
#include <units/time.h>
#include <units/velocity.h>
#include "frc/AddressableLED.h"
#include "util/Color.h"
namespace frc {
class LEDPattern {
public:
/**
* A wrapper around a length and an arbitrary reader function that accepts an
* LED index and returns data for the LED at that index. This configuration
* allows us to abstract over different container types without templating.
*/
class LEDReader {
public:
LEDReader(std::function<frc::AddressableLED::LEDData(int)> impl,
size_t size)
: m_impl{std::move(impl)}, m_size{size} {}
frc::AddressableLED::LEDData operator[](size_t index) const {
return m_impl(index);
}
size_t size() const { return m_size; }
private:
std::function<frc::AddressableLED::LEDData(int)> m_impl;
size_t m_size;
};
explicit LEDPattern(std::function<void(frc::LEDPattern::LEDReader,
std::function<void(int, frc::Color)>)>
impl);
void ApplyTo(LEDReader reader,
std::function<void(int, frc::Color)> writer) const;
/**
* Writes the pattern to an LED buffer. Dynamic animations should be called
* periodically (such as with a command or with a periodic method) to refresh
* the buffer over time.
*
* This method is intentionally designed to use separate objects for reading
* and writing data. By splitting them up, we can easily modify the behavior
* of some base pattern to make it scroll, blink, or breathe by intercepting
* the data writes to transform their behavior to whatever we like.
*
* @param data the current data of the LED strip
* @param writer data writer for setting new LED colors on the LED strip
*/
void ApplyTo(std::span<frc::AddressableLED::LEDData> data,
std::function<void(int, frc::Color)> writer) const;
/**
* Writes the pattern to an LED buffer. Dynamic animations should be called
* periodically (such as with a command or with a periodic method) to refresh
* the buffer over time.
*
* This method is intentionally designed to use separate objects for reading
* and writing data. By splitting them up, we can easily modify the behavior
* of some base pattern to make it scroll, blink, or breathe by intercepting
* the data writes to transform their behavior to whatever we like.
*
* @param data the current data of the LED strip
*/
void ApplyTo(std::span<frc::AddressableLED::LEDData> data) const;
/**
* Creates a pattern with remapped indices.
*
* @param indexMapper the index mapper
* @return the mapped pattern
*/
[[nodiscard]]
LEDPattern MapIndex(std::function<size_t(size_t, size_t)> indexMapper);
/**
* Creates a pattern that displays this one in reverse. Scrolling patterns
* will scroll in the opposite direction (but at the same speed). It will
* treat the end of an LED strip as the start, and the start of the strip as
* the end. This can be useful for making ping-pong patterns that travel from
* one end of an LED strip to the other, then reverse direction and move back
* to the start. This can also be useful when working with LED strips
* connected in a serpentine pattern (where the start of one strip is
* connected to the end of the previous one).
*
* @return the reverse pattern
*/
[[nodiscard]]
LEDPattern Reversed();
/**
* Creates a pattern that displays this one, but offset by a certain number of
* LEDs. The offset pattern will wrap around, if necessary.
*
* @param offset how many LEDs to offset by
* @return the offset pattern
*/
[[nodiscard]]
LEDPattern OffsetBy(int offset);
/**
* Creates a pattern that plays this one scrolling up the buffer. The velocity
* controls how fast the pattern returns back to its original position, and is
* in terms of the length of the LED strip; scrolling across a segment that is
* 10 LEDs long will travel twice as fast as on a segment that's only 5 LEDs
* long (assuming equal LED density on both segments).
*/
[[nodiscard]]
LEDPattern ScrollAtRelativeSpeed(units::hertz_t velocity);
/**
* Creates a pattern that plays this one scrolling up an LED strip. A negative
* velocity makes the pattern play in reverse.
*
* <p>For example, scrolling a pattern at 4 inches per second along an LED
* strip with 60 LEDs per meter:
*
* <pre>
* // LEDs per meter, a known value taken from the spec sheet of our
* particular LED strip units::meter_t LED_SPACING = units::meter_t{1 /60.0};
*
* frc::LEDPattern rainbow = frc::LEDPattern::Rainbow();
* frc::LEDPattern scrollingRainbow =
* rainbow.ScrollAtAbsoluteSpeed(units::feet_per_second_t{1 / 3.0},
* LED_SPACING);
* </pre>
*
* <p>Note that this pattern will scroll <i>faster</i> if applied to a less
* dense LED strip (such as 30 LEDs per meter), or <i>slower</i> if applied to
* a denser LED strip (such as 120 or 144 LEDs per meter).
*
* @param velocity how fast the pattern should move along a physical LED strip
* @param ledSpacing the distance between adjacent LEDs on the physical LED
* strip
* @return the scrolling pattern
*/
[[nodiscard]]
LEDPattern ScrollAtAbsoluteSpeed(units::meters_per_second_t velocity,
units::meter_t ledSpacing);
/**
* Creates a pattern that switches between playing this pattern and turning
* the entire LED strip off.
*
* @param onTime how long the pattern should play for, per cycle
* @param offTime how long the pattern should be turned off for, per cycle
* @return the blinking pattern
*/
[[nodiscard]]
LEDPattern Blink(units::second_t onTime, units::second_t offTime);
/**
* Like {@link LEDPattern::Blink(units::second_t)}, but where the
* "off" time is exactly equal to the "on" time.
*
* @param onTime how long the pattern should play for (and be turned off for),
* per cycle
* @return the blinking pattern
*/
[[nodiscard]]
LEDPattern Blink(units::second_t onTime);
/**
* Creates a pattern that blinks this one on and off in sync with a true/false
* signal. The pattern will play while the signal outputs {@code true}, and
* will turn off while the signal outputs
* {@code false}.
*
* @param signal the signal to synchronize with
* @return the blinking pattern
*/
[[nodiscard]]
LEDPattern SynchronizedBlink(std::function<bool()> signal);
/**
* Creates a pattern that brightens and dims this one over time. Brightness
* follows a sinusoidal pattern.
*
* @param period how fast the breathing pattern should complete a single cycle
* @return the breathing pattern
*/
[[nodiscard]]
LEDPattern Breathe(units::second_t period);
/**
* Creates a pattern that plays this pattern overlaid on another. Anywhere
* this pattern sets an LED to off (or {@link frc::Color::kBlack}), the base
* pattern will be displayed instead.
*
* @param base the base pattern to overlay on top of
* @return the combined overlay pattern
*/
[[nodiscard]]
LEDPattern OverlayOn(const LEDPattern& base);
/**
* Creates a pattern that displays outputs as a combination of this pattern
* and another. Color values are calculated as the average color of both
* patterns; if both patterns set the same LED to the same color, then it is
* set to that color, but if one pattern sets to one color and the other
* pattern sets it to off, then it will show the color of the first pattern
* but at approximately half brightness. This is different from {@link
* LEDPattern::OverlayOn(const LEDPattern&)}, which will show the base pattern
* at full brightness if the overlay is set to off at that position.
*
* @param other the pattern to blend with
* @return the blended pattern
*/
[[nodiscard]]
LEDPattern Blend(const LEDPattern& other);
/**
* Similar to {@link LEDPattern::Blend(const LEDPattern&)}, but performs a
* bitwise mask on each color channel rather than averaging the colors for
* each LED. This can be helpful for displaying only a portion of the base
* pattern by applying a mask that sets the desired area to white, and all
* other areas to black. However, it can also be used to display only certain
* color channels or hues; for example, masking with {@code
* LEDPattern.color(Color.kRed)} will turn off the green and blue channels on
* the output pattern, leaving only the red LEDs to be illuminated.
*
* @param mask the mask to apply
* @return the masked pattern
*/
[[nodiscard]]
LEDPattern Mask(const LEDPattern& mask);
/**
* Creates a pattern that plays this one, but at a different brightness.
* Brightness multipliers are applied per-channel in the RGB space; no HSL or
* HSV conversions are applied. Multipliers are also uncapped, which may
* result in the original colors washing out and appearing less saturated or
* even just a bright white.
*
* <p>This method is predominantly intended for dimming LEDs to avoid
* painfully bright or distracting patterns from playing (apologies to the
* 2024 NE Greater Boston field staff).
*
* <p>For example, dimming can be done simply by adding a call to
* `atBrightness` at the end of a pattern:
*
* <pre>
* // Solid red, but at 50% brightness
* frc::LEDPattern::Solid(frc::Color::kRed).AtBrightness(0.5);
*
* // Solid white, but at only 10% (i.e. ~0.5V)
* frc::LEDPattern::Solid(frc:Color::kWhite).AtBrightness(0.1);
* </pre>
*
* @param relativeBrightness the multiplier to apply to all channels to modify
* brightness
* @return the input pattern, displayed at
*/
[[nodiscard]]
LEDPattern AtBrightness(double relativeBrightness);
/** A pattern that turns off all LEDs. */
static LEDPattern Off();
/**
* Creates a pattern that displays a single static color along the entire
* length of the LED strip.
*
* @param color the color to display
* @return the pattern
*/
static LEDPattern Solid(const Color color);
/**
* Creates a pattern that works as a mask layer for {@link
* LEDPattern::Mask(const LEDPattern&)} that illuminates only the portion of
* the LED strip corresponding with some progress. The mask pattern will start
* from the base and set LEDs to white at a proportion equal to the progress
* returned by the function. Some usages for this could be for displaying
* progress of a flywheel to its target velocity, progress of a complex
* autonomous sequence, or the height of an elevator.
*
* <p>For example, creating a mask for displaying a red-to-blue gradient,
* starting from the red end, based on where an elevator is in its range of
* travel.
*
* <pre>
* frc::LEDPattern basePattern =
* frc::LEDPattern::Gradient(frc::Color::kRed, frc::Color::kBlue);
* frc::LEDPattern progressPattern =
* basePattern.Mask(frc::LEDPattern::ProgressMaskLayer([&]() {
* return elevator.GetHeight() / elevator.MaxHeight();
* });
* </pre>
*
* @param progressFunction the function to call to determine the progress.
* This should return values in the range [0, 1]; any values outside that
* range will be clamped.
* @return the mask pattern
*/
static LEDPattern ProgressMaskLayer(std::function<double()> progressFunction);
/**
* Display a set of colors in steps across the length of the LED strip. No
* interpolation is done between colors. Colors are specified by the first LED
* on the strip to show that color. The last color in the map will be
* displayed all the way to the end of the strip. LEDs positioned before the
* first specified step will be turned off (you can think of this as if
* there's a 0 -> black step by default).
*
* @param steps a map of progress to the color to start displaying at that
* position along the LED strip
* @return a motionless step pattern
*/
static LEDPattern Steps(std::span<const std::pair<double, Color>> steps);
/**
* Display a set of colors in steps across the length of the LED strip. No
* interpolation is done between colors. Colors are specified by the first LED
* on the strip to show that color. The last color in the map will be
* displayed all the way to the end of the strip. LEDs positioned before the
* first specified step will be turned off (you can think of this as if
* there's a 0 -> black step by default).
*
* @param steps a map of progress to the color to start displaying at that
* position along the LED strip
* @return a motionless step pattern
*/
static LEDPattern Steps(
std::initializer_list<std::pair<double, Color>> steps);
/** 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. 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::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
* rainbow pattern will stretch across the entire length of the LED strip.
*
* @param saturation the saturation of the HSV colors, in [0, 255]
* @param value the value of the HSV colors, in [0, 255]
* @return the rainbow pattern
*/
static LEDPattern Rainbow(int saturation, int value);
private:
std::function<void(frc::LEDPattern::LEDReader,
std::function<void(int, frc::Color)>)>
m_impl;
};
} // namespace frc