Skip to content

Commit 5595794

Browse files
committed
Use generic index mapping function
1 parent 768a054 commit 5595794

File tree

4 files changed

+86
-174
lines changed

4 files changed

+86
-174
lines changed

wpilibc/src/main/native/cpp/LEDPattern.cpp

Lines changed: 23 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -38,30 +38,26 @@ void LEDPattern::ApplyTo(std::span<AddressableLED::LEDData> data) const {
3838
ApplyTo(data, [&](int index, Color color) { data[index].SetLED(color); });
3939
}
4040

41-
LEDPattern LEDPattern::Reversed() {
42-
return LEDPattern{[self = *this](auto data, auto writer) {
41+
LEDPattern LEDPattern::MapIndex(
42+
std::function<size_t(size_t, size_t)> indexMapper) {
43+
return LEDPattern{[self = *this, indexMapper](auto data, auto writer) {
44+
size_t bufLen = data.size();
4345
self.ApplyTo(
44-
LEDPattern::LEDReader{[=](auto i) { return data[data.size() - 1 - i]; },
45-
data.size()},
46-
[&](int i, Color color) { writer((data.size() - 1) - i, color); });
46+
LEDPattern::LEDReader{
47+
[=](auto i) { return data[indexMapper(bufLen, i)]; }, bufLen},
48+
[&](int i, Color color) { writer(indexMapper(bufLen, i), color); });
4749
}};
4850
}
4951

52+
LEDPattern LEDPattern::Reversed() {
53+
return MapIndex([](size_t bufLen, size_t i) { return bufLen - 1 - i; });
54+
}
55+
5056
LEDPattern LEDPattern::OffsetBy(int offset) {
51-
return LEDPattern{[=, self = *this](LEDPattern::LEDReader data, auto writer) {
52-
self.ApplyTo(LEDPattern::LEDReader{[=, d = std::move(data)](auto i) {
53-
int shiftedIndex = frc::FloorMod(
54-
i + offset,
55-
static_cast<int>(d.size()));
56-
return d[shiftedIndex];
57-
},
58-
data.size()},
59-
[&data, &writer, offset](int i, Color color) {
60-
int shiftedIndex =
61-
frc::FloorMod(i + offset, static_cast<int>(data.size()));
62-
writer(shiftedIndex, color);
63-
});
64-
}};
57+
return MapIndex([offset](size_t bufLen, size_t i) {
58+
return frc::FloorMod(static_cast<int>(i) + offset,
59+
static_cast<int>(bufLen));
60+
});
6561
}
6662

6763
LEDPattern LEDPattern::ScrollAtRelativeSpeed(units::hertz_t velocity) {
@@ -70,30 +66,17 @@ LEDPattern LEDPattern::ScrollAtRelativeSpeed(units::hertz_t velocity) {
7066
// Invert and multiply by 1,000,000 to get microseconds
7167
double periodMicros = 1e6 / velocity.value();
7268

73-
return LEDPattern{[=, self = *this](auto data, auto writer) {
74-
auto bufLen = data.size();
69+
return MapIndex([=](size_t bufLen, size_t i) {
7570
auto now = wpi::Now();
7671

7772
// index should move by (bufLen) / (period)
7873
double t =
7974
(now % static_cast<int64_t>(std::floor(periodMicros))) / periodMicros;
8075
int offset = static_cast<int>(std::floor(t * bufLen));
8176

82-
self.ApplyTo(LEDPattern::LEDReader{[=, d = std::move(data)](auto i) {
83-
int shiftedIndex = frc::FloorMod(
84-
i + offset,
85-
static_cast<int>(d.size()));
86-
return d[shiftedIndex];
87-
},
88-
data.size()},
89-
[=](int i, Color color) {
90-
// floorMod so if the offset is negative, we still get
91-
// positive outputs
92-
int shiftedIndex =
93-
frc::FloorMod(i + offset, static_cast<int>(bufLen));
94-
writer(shiftedIndex, color);
95-
});
96-
}};
77+
return frc::FloorMod(static_cast<int>(i) + offset,
78+
static_cast<int>(bufLen));
79+
});
9780
}
9881

9982
LEDPattern LEDPattern::ScrollAtAbsoluteSpeed(
@@ -103,8 +86,7 @@ LEDPattern LEDPattern::ScrollAtAbsoluteSpeed(
10386
auto microsPerLed =
10487
static_cast<int64_t>(std::floor((ledSpacing / velocity).value() * 1e6));
10588

106-
return LEDPattern{[=, self = *this](auto data, auto writer) {
107-
auto bufLen = data.size();
89+
return MapIndex([=](size_t bufLen, size_t i) {
10890
auto now = wpi::Now();
10991

11092
// every step in time that's a multiple of microsPerLED will increment
@@ -113,22 +95,9 @@ LEDPattern LEDPattern::ScrollAtAbsoluteSpeed(
11395
// offset values for negative velocities
11496
auto offset = static_cast<int64_t>(now) / microsPerLed;
11597

116-
self.ApplyTo(LEDPattern::LEDReader{[=, d = std::move(data)](auto i) {
117-
int shiftedIndex = frc::FloorMod(
118-
i + offset,
119-
static_cast<int>(d.size()));
120-
return d[shiftedIndex];
121-
},
122-
data.size()},
123-
[=, &writer](int i, Color color) {
124-
// FloorMod so if the offset is negative, we still get
125-
// positive outputs
126-
int shiftedIndex =
127-
frc::FloorMod(i + offset, static_cast<int>(bufLen));
128-
129-
writer(shiftedIndex, color);
130-
});
131-
}};
98+
return frc::FloorMod(static_cast<int>(i) + offset,
99+
static_cast<int>(bufLen));
100+
});
132101
}
133102

134103
LEDPattern LEDPattern::Blink(units::second_t onTime, units::second_t offTime) {

wpilibc/src/main/native/include/frc/LEDPattern.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,15 @@ class LEDPattern {
7979
*/
8080
void ApplyTo(std::span<frc::AddressableLED::LEDData> data) const;
8181

82+
/**
83+
* Creates a pattern with remapped indices.
84+
*
85+
* @param indexMapper the index mapper
86+
* @return the mapped pattern
87+
*/
88+
[[nodiscard]]
89+
LEDPattern MapIndex(std::function<size_t(size_t, size_t)> indexMapper);
90+
8291
/**
8392
* Creates a pattern that displays this one in reverse. Scrolling patterns
8493
* will scroll in the opposite direction (but at the same speed). It will

wpilibj/src/main/java/edu/wpi/first/wpilibj/LEDPattern.java

Lines changed: 54 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,18 @@
9393
*/
9494
@FunctionalInterface
9595
public interface LEDPattern {
96+
/** A functional interface for index mapping functions. */
97+
@FunctionalInterface
98+
interface IndexMapper {
99+
/**
100+
* Maps the index.
101+
*
102+
* @param bufLen Length of the buffer
103+
* @param index The index to map
104+
*/
105+
int apply(int bufLen, int index);
106+
}
107+
96108
/**
97109
* Writes the pattern to an LED buffer. Dynamic animations should be called periodically (such as
98110
* with a command or with a periodic method) to refresh the buffer over time.
@@ -130,19 +142,12 @@ default <T extends LEDReader & LEDWriter> void applyTo(T readWriter) {
130142
}
131143

132144
/**
133-
* Creates a pattern that displays this one in reverse. Scrolling patterns will scroll in the
134-
* opposite direction (but at the same speed). It will treat the end of an LED strip as the start,
135-
* and the start of the strip as the end. This can be useful for making ping-pong patterns that
136-
* travel from one end of an LED strip to the other, then reverse direction and move back to the
137-
* start. This can also be useful when working with LED strips connected in a serpentine pattern
138-
* (where the start of one strip is connected to the end of the previous one); however, consider
139-
* using a {@link AddressableLEDBufferView#reversed() reversed view} of the overall buffer for
140-
* that segment rather than reversing patterns.
145+
* Creates a pattern with remapped indices.
141146
*
142-
* @return the reverse pattern
143-
* @see AddressableLEDBufferView#reversed()
147+
* @param indexMapper the index mapper
148+
* @return the mapped pattern
144149
*/
145-
default LEDPattern reversed() {
150+
default LEDPattern mapIndex(IndexMapper indexMapper) {
146151
return (reader, writer) -> {
147152
int bufLen = reader.getLength();
148153
applyTo(
@@ -154,23 +159,40 @@ public int getLength() {
154159

155160
@Override
156161
public int getRed(int index) {
157-
return reader.getRed(getLength() - 1 - index);
162+
return reader.getRed(indexMapper.apply(bufLen, index));
158163
}
159164

160165
@Override
161166
public int getGreen(int index) {
162-
return reader.getGreen(getLength() - 1 - index);
167+
return reader.getGreen(indexMapper.apply(bufLen, index));
163168
}
164169

165170
@Override
166171
public int getBlue(int index) {
167-
return reader.getBlue(getLength() - 1 - index);
172+
return reader.getBlue(indexMapper.apply(bufLen, index));
168173
}
169174
},
170-
(i, r, g, b) -> writer.setRGB((bufLen - 1) - i, r, g, b));
175+
(i, r, g, b) -> writer.setRGB(indexMapper.apply(bufLen, i), r, g, b));
171176
};
172177
}
173178

179+
/**
180+
* Creates a pattern that displays this one in reverse. Scrolling patterns will scroll in the
181+
* opposite direction (but at the same speed). It will treat the end of an LED strip as the start,
182+
* and the start of the strip as the end. This can be useful for making ping-pong patterns that
183+
* travel from one end of an LED strip to the other, then reverse direction and move back to the
184+
* start. This can also be useful when working with LED strips connected in a serpentine pattern
185+
* (where the start of one strip is connected to the end of the previous one); however, consider
186+
* using a {@link AddressableLEDBufferView#reversed() reversed view} of the overall buffer for
187+
* that segment rather than reversing patterns.
188+
*
189+
* @return the reverse pattern
190+
* @see AddressableLEDBufferView#reversed()
191+
*/
192+
default LEDPattern reversed() {
193+
return mapIndex((length, index) -> length - 1 - index);
194+
}
195+
174196
/**
175197
* Creates a pattern that plays this one, but offset by a certain number of LEDs. The offset
176198
* pattern will wrap around, if necessary.
@@ -179,15 +201,7 @@ public int getBlue(int index) {
179201
* @return the offset pattern
180202
*/
181203
default LEDPattern offsetBy(int offset) {
182-
return (reader, writer) -> {
183-
int bufLen = reader.getLength();
184-
applyTo(
185-
LEDReader.RemappedReader.offset(reader, offset),
186-
(i, r, g, b) -> {
187-
int shiftedIndex = Math.floorMod(i + offset, bufLen);
188-
writer.setRGB(shiftedIndex, r, g, b);
189-
});
190-
};
204+
return mapIndex((length, index) -> Math.floorMod(index + offset, length));
191205
}
192206

193207
/**
@@ -211,23 +225,16 @@ default LEDPattern offsetBy(int offset) {
211225
default LEDPattern scrollAtRelativeSpeed(Frequency velocity) {
212226
final double periodMicros = velocity.asPeriod().in(Microseconds);
213227

214-
return (reader, writer) -> {
215-
int bufLen = reader.getLength();
216-
long now = WPIUtilJNI.now();
228+
return mapIndex(
229+
(bufLen, index) -> {
230+
long now = WPIUtilJNI.now();
217231

218-
// index should move by (buf.length) / (period)
219-
double t = (now % (long) periodMicros) / periodMicros;
220-
int offset = (int) (t * bufLen);
232+
// index should move by (buf.length) / (period)
233+
double t = (now % (long) periodMicros) / periodMicros;
234+
int offset = (int) (t * bufLen);
221235

222-
applyTo(
223-
LEDReader.RemappedReader.offset(reader, offset),
224-
(i, r, g, b) -> {
225-
// floorMod so if the offset is negative, we still get positive outputs
226-
int shiftedIndex = Math.floorMod(i + offset, bufLen);
227-
228-
writer.setRGB(shiftedIndex, r, g, b);
229-
});
230-
};
236+
return Math.floorMod(index + offset, bufLen);
237+
});
231238
}
232239

233240
/**
@@ -262,22 +269,16 @@ default LEDPattern scrollAtAbsoluteSpeed(LinearVelocity velocity, Distance ledSp
262269
var metersPerMicro = velocity.in(Meters.per(Microsecond));
263270
var microsPerLED = (int) (ledSpacing.in(Meters) / metersPerMicro);
264271

265-
return (reader, writer) -> {
266-
int bufLen = reader.getLength();
267-
long now = WPIUtilJNI.now();
268-
269-
// every step in time that's a multiple of microsPerLED will increment the offset by 1
270-
var offset = (int) (now / microsPerLED);
272+
return mapIndex(
273+
(bufLen, index) -> {
274+
long now = WPIUtilJNI.now();
271275

272-
applyTo(
273-
LEDReader.RemappedReader.offset(reader, offset),
274-
(i, r, g, b) -> {
275-
// floorMod so if the offset is negative, we still get positive outputs
276-
int shiftedIndex = Math.floorMod(i + offset, bufLen);
276+
// every step in time that's a multiple of microsPerLED will increment the offset by 1
277+
var offset = (int) (now / microsPerLED);
277278

278-
writer.setRGB(shiftedIndex, r, g, b);
279-
});
280-
};
279+
// floorMod so if the offset is negative, we still get positive outputs
280+
return Math.floorMod(index + offset, bufLen);
281+
});
281282
}
282283

283284
/**

wpilibj/src/main/java/edu/wpi/first/wpilibj/LEDReader.java

Lines changed: 0 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44

55
package edu.wpi.first.wpilibj;
66

7-
import edu.wpi.first.util.ErrorMessages;
87
import edu.wpi.first.wpilibj.util.Color;
98
import edu.wpi.first.wpilibj.util.Color8Bit;
10-
import java.util.function.IntUnaryOperator;
119

1210
/** Generic interface for reading data from an LED buffer. */
1311
public interface LEDReader {
@@ -98,69 +96,4 @@ default void forEach(IndexedColorIterator iterator) {
9896
iterator.accept(i, getRed(i), getGreen(i), getBlue(i));
9997
}
10098
}
101-
102-
/**
103-
* An LED reader implementation that operates on remapped indices. Remapping can be done using
104-
* arbitrary mapping functions; a static factory function is also provided for the common use case
105-
* of offset readers for things like scrolling animations.
106-
*/
107-
class RemappedReader implements LEDReader {
108-
private final LEDReader m_reader;
109-
private final IntUnaryOperator m_mapping;
110-
111-
/**
112-
* Creates a new remapping reader for a backing reader and an arbitrary index remapping
113-
* function.
114-
*
115-
* @param reader the backing reader to use
116-
* @param mapping the mapping function to use
117-
*/
118-
public RemappedReader(LEDReader reader, IntUnaryOperator mapping) {
119-
m_reader = ErrorMessages.requireNonNullParam(reader, "reader", "RemappedReader");
120-
m_mapping = ErrorMessages.requireNonNullParam(mapping, "mapping", "RemappedReader");
121-
}
122-
123-
/**
124-
* Creates a new offset LED reader based on an existing reader and integer offset. The offset
125-
* reader will be circular and wrap around.
126-
*
127-
* @param reader the backing reader to use
128-
* @param offset the offset to use when reading data
129-
* @return the offset reader
130-
*/
131-
public static RemappedReader offset(LEDReader reader, int offset) {
132-
return new RemappedReader(
133-
reader, original -> Math.floorMod(original + offset, reader.getLength()));
134-
}
135-
136-
@Override
137-
public int getLength() {
138-
return m_reader.getLength();
139-
}
140-
141-
@Override
142-
public int getRed(int index) {
143-
return m_reader.getRed(remapIndex(index));
144-
}
145-
146-
@Override
147-
public int getGreen(int index) {
148-
return m_reader.getGreen(remapIndex(index));
149-
}
150-
151-
@Override
152-
public int getBlue(int index) {
153-
return m_reader.getBlue(remapIndex(index));
154-
}
155-
156-
/**
157-
* Remaps an input index to the corresponding offset index.
158-
*
159-
* @param index the input index to remap
160-
* @return the remapped index
161-
*/
162-
public int remapIndex(int index) {
163-
return m_mapping.applyAsInt(index);
164-
}
165-
}
16699
}

0 commit comments

Comments
 (0)