Lamp-Da 0.1
A compact lantern project
Loading...
Searching...
No Matches
grid_rule.hpp
1
5#ifndef MODES_DRAW_GRID_RULE_HPP
6#define MODES_DRAW_GRID_RULE_HPP
7
8#include <array>
9
10namespace lampda::modes::draw::grid {
11
12using LampTy = hardware::LampTy;
13
16{
17 static constexpr uint8_t dstBufIdx = 0;
18 static constexpr uint8_t srcBufIdx = 1;
19 static constexpr uint8_t tempBufIdx = 2;
20 static constexpr uint8_t scrollAmount = 1;
21 static constexpr uint8_t renderBlurAmount = 32;
22 static constexpr bool scrollSkewed = false;
23 static constexpr bool loadFullOnReset =
24 true;
25};
26
48template<typename ConfigTy = LineRuleConfig> struct LineRule
49{
50 static constexpr uint8_t dstBufIdx = ConfigTy::dstBufIdx;
51 static constexpr uint8_t srcBufIdx = ConfigTy::srcBufIdx;
52 static constexpr uint8_t tempBufIdx = ConfigTy::tempBufIdx;
53
54 static constexpr bool loadFullOnReset = ConfigTy::loadFullOnReset;
55
56 static constexpr float fwidth = LampTy::maxWidthFloat;
57 static constexpr uint16_t width = LampTy::maxWidth;
58 using LineTy = std::array<uint32_t, width>;
59
61 static constexpr uint16_t nbLines = LampTy::maxHeight;
62
64 void reset(LampTy& lamp, LineTy& firstLine)
65 {
66 lastLine = 0;
67 currentLine = firstLine;
68 isResetted = true;
69
70 lamp.template fillTempBuffer(0);
71 auto& buffer = lamp.template getTempBuffer<dstBufIdx>();
72 std::copy(currentLine.begin(), currentLine.end(), buffer.begin());
73 }
74
76 void LMBD_INLINE reset(LampTy& lamp)
77 {
78 LineTy first {};
79 reset(lamp, first);
80 }
81
83 void update(LampTy& lamp, auto& callback)
84 {
85 auto& buffer = lamp.template getTempBuffer<dstBufIdx>();
86
87 uint16_t nextLine = (lastLine + 1) % (nbLines + 1);
88 uint16_t nextStart = nextLine * fwidth;
89 bool endReached = (nextStart + width) > buffer.size();
90
91 // if scrollAmount is 0, then we prefer wrapping the grid
92 if (endReached && ConfigTy::scrollAmount == 0)
93 {
94 nextStart = 0;
95 lastLine = (1 + lastLine) % (nbLines + 1);
96 }
97
98 // if not, scroll grid by scrollAmount (skewed or not)
99 else if (endReached)
100 {
101 scrollBy(lamp, ConfigTy::scrollAmount, ConfigTy::scrollSkewed);
102
103 lastLine -= ConfigTy::scrollAmount;
104 nextLine -= ConfigTy::scrollAmount;
105 nextStart = nextLine * fwidth;
106 }
107
108 // call callback
109 LineTy newLine {};
110 callback(currentLine, newLine);
111 currentLine = newLine;
112
113 // save result to buffer & move to next line
114 if (assertBool(nextStart + width <= buffer.size()))
115 std::copy(newLine.begin(), newLine.end(), buffer.begin() + nextStart);
116
117 lastLine = (1 + lastLine) % (nbLines + 1);
118 }
119
121 void display(LampTy& lamp, bool reversed = false)
122 {
123 if (reversed)
124 {
125 lamp.template setColorsFromBufferReversed<dstBufIdx>(true);
126 }
127 else
128 {
129 lamp.template setColorsFromBuffer<dstBufIdx>();
130 }
131 }
132
134 void LMBD_INLINE smoothUpdate(LampTy& lamp, auto& callback)
135 {
136 lamp.template copyBuffer<tempBufIdx, dstBufIdx>();
137 update(lamp, callback);
138 lamp.template copyBuffer<srcBufIdx, tempBufIdx>();
139 }
140
142 void LMBD_INLINE smoothDisplay(LampTy& lamp, float phasis)
143 {
144 lamp.setColorsFromMixedBuffers<dstBufIdx, srcBufIdx>(phasis);
145 }
146
148 void LMBD_INLINE smoothDisplay(LampTy& lamp, uint16_t counter, uint16_t maxCounter, uint16_t maxValue = 256)
149 {
150 uint16_t increment = maxValue / maxCounter;
151 float phase = counter * increment + increment / 2.0;
152 smoothDisplay(lamp, phase / maxValue);
153 }
154
156 LineTy LMBD_INLINE lineAtIndex(LampTy& lamp, uint16_t lineIndex)
157 {
158 const auto& buffer = lamp.template getTempBuffer<dstBufIdx>();
159 LineTy newLine {};
160
161 if (lineIndex < nbLines)
162 {
163 uint16_t lineStart = lineIndex * fwidth;
164 if (assertBool(lineStart + width <= buffer.size()))
165 std::copy(buffer.cbegin() + lineStart, buffer.cbegin() + lineStart + width, newLine.begin());
166 }
167
168 return newLine;
169 }
170
172 void scrollBy(LampTy& lamp, uint8_t amount, bool skewed)
173 {
174 auto& buffer = lamp.template getTempBuffer<dstBufIdx>();
175
176 for (uint16_t I = 0; I < nbLines; ++I)
177 {
178 auto line = lineAtIndex(lamp, I + amount);
179 uint16_t shiftStart = I * fwidth;
180 uint16_t shiftTweak = I * fwidth + 0.5;
181
182 // unholy coordinate system w/ LEDs 0.041151pt too long
183 if (skewed && shiftStart == shiftTweak && (I > 2 && I != lamp.shiftResidue))
184 {
185 auto last = line[0];
186 std::copy(line.begin() + 1, line.end(), line.begin());
187 line[line.size() - 1] = last;
188 }
189
190 // (at that point I gave up finding why)
191 if (skewed && I == 2)
192 shiftStart -= 1;
193
194 if (assertBool(shiftStart + line.size() <= buffer.size()))
195 std::copy(line.begin() + ((I || !skewed) ? 0 : 1), line.end(), buffer.begin() + shiftStart);
196 }
197 }
198
212 template<bool hasCustomRamp, uint8_t rampSubstitute = 70, uint8_t baseSmooth = 4>
213 void LMBD_INLINE loop(auto& ctx, auto& callback)
214 {
215 auto& lamp = ctx.lamp;
216
217 // first call since reset
218 if (isResetted)
219 {
220 isResetted = false;
221
222 // if the animation should load the full state on startup
223 if constexpr (loadFullOnReset)
224 {
225 // update the whole grid
226 for (size_t i = 0; i < nbLines; ++i)
227 {
228 update(lamp, callback);
229 display(lamp);
230 }
231 if constexpr (ConfigTy::renderBlurAmount)
232 lamp.blur(ConfigTy::renderBlurAmount);
233
234 return;
235 }
236 }
237 uint8_t rampValue = (hasCustomRamp ? ctx.get_active_custom_ramp() : rampSubstitute);
238
239 // by default, quickly run smoothly the grid scrolling
240 uint16_t smoothFrame = baseSmooth + rampValue / 16;
241 if (smoothFrame <= 10 && lamp.getBrightness() > 32)
242 {
243 uint32_t counter = lamp.raw_frame_count % smoothFrame;
244 if (!counter)
245 smoothUpdate(lamp, callback);
246
247 smoothDisplay(lamp, counter, smoothFrame);
248 if constexpr (ConfigTy::renderBlurAmount)
249 lamp.blur(ConfigTy::renderBlurAmount);
250
251 // coarser display for slow scrolling (smoothing too visible)
252 }
253 else
254 {
255 int now = lamp.now;
256 int frameDuration = rampValue + lamp.frameDurationMs;
257
258 if (abs(int(now - lastUpdate)) > frameDuration)
259 {
260 lastUpdate = now;
261 update(lamp, callback);
262
263 display(lamp);
264 if constexpr (ConfigTy::renderBlurAmount)
265 lamp.blur(ConfigTy::renderBlurAmount);
266 }
267 }
268 }
269
271 uint16_t lastLine = 0;
272 uint32_t lastUpdate = 0;
273 bool isResetted = false;
274};
275
286template<uint8_t ruleNo, bool straight = false, bool leftBound = false, bool rightBound = false, uint8_t nbBits = 24>
287void wolframRule(const auto& before, auto& after)
288{
289 constexpr uint8_t pattern[8] = {0b1 & (ruleNo >> 7),
290 0b1 & (ruleNo >> 6),
291 0b1 & (ruleNo >> 5),
292 0b1 & (ruleNo >> 4),
293 0b1 & (ruleNo >> 3),
294 0b1 & (ruleNo >> 2),
295 0b1 & (ruleNo >> 1),
296 0b1 & (ruleNo >> 0)};
297
298 uint16_t start = leftBound ? 1 : 0;
299 uint16_t end = rightBound ? after.size() - 1 : after.size();
300
301 for (uint16_t I = start; I < end; ++I)
302 {
303 uint16_t J = (I + (straight ? 1 : 0)) % end;
304 after[J] = 0;
305
306 for (uint32_t P = 0; P < nbBits; ++P)
307 {
308 uint32_t mask = 1 << P;
309 uint32_t l = before[(I + before.size() - 1) % before.size()] & mask;
310 uint32_t m = before[I] & mask;
311 uint32_t r = before[(I + 1) % before.size()] & mask;
312
313 uint8_t lmr = 0;
314 if (l)
315 lmr |= 0b100;
316 if (m)
317 lmr |= 0b010;
318 if (r)
319 lmr |= 0b001;
320
321 if (pattern[lmr])
322 {
323 after[J] |= mask;
324 }
325 }
326 }
327}
328
329} // namespace lampda::modes::draw::grid
330
331#endif
static constexpr N abs(const N a)
absolute of a number
Definition: utils.h:46
Define the rules for a line.
Definition: grid_rule.hpp:16
static constexpr uint8_t srcBufIdx
Sec. buf. used for smooth grid.
Definition: grid_rule.hpp:18
static constexpr uint8_t renderBlurAmount
Blur before display.
Definition: grid_rule.hpp:21
static constexpr uint8_t scrollAmount
Scroll each update.
Definition: grid_rule.hpp:20
static constexpr uint8_t tempBufIdx
Temporary buffer for copies.
Definition: grid_rule.hpp:19
static constexpr bool scrollSkewed
Scroll skewed to the left.
Definition: grid_rule.hpp:22
static constexpr uint8_t dstBufIdx
Main buf. used for the grid.
Definition: grid_rule.hpp:17
static constexpr bool loadFullOnReset
If true, the first call of loop will load the whole grid. False will load gradually.
Definition: grid_rule.hpp:23
Implement a line-based grid pattern, update line per line.
Definition: grid_rule.hpp:49
void display(LampTy &lamp, bool reversed=false)
Display the line-rule grid onto screen (if reversed reverse it)
Definition: grid_rule.hpp:121
std::array< uint32_t, width > LineTy
Type of a line array.
Definition: grid_rule.hpp:58
void LMBD_INLINE smoothDisplay(LampTy &lamp, uint16_t counter, uint16_t maxCounter, uint16_t maxValue=256)
For a counter between 0 and maxCounter display a smooth frame.
Definition: grid_rule.hpp:148
void update(LampTy &lamp, auto &callback)
When called, pass before and after line to callback for processing.
Definition: grid_rule.hpp:83
static constexpr uint16_t nbLines
Number of lines in grid.
Definition: grid_rule.hpp:61
void LMBD_INLINE loop(auto &ctx, auto &callback)
Default loop function, update and display, smooth display if fast.
Definition: grid_rule.hpp:213
void LMBD_INLINE smoothUpdate(LampTy &lamp, auto &callback)
Same as .update() but uses two buffers, required for smoothDisplay()
Definition: grid_rule.hpp:134
void LMBD_INLINE reset(LampTy &lamp)
Reset grip with an empty (black) first line.
Definition: grid_rule.hpp:76
bool isResetted
flag to signal that the grid just been resetted
Definition: grid_rule.hpp:273
uint16_t lastLine
latest treated line
Definition: grid_rule.hpp:271
void scrollBy(LampTy &lamp, uint8_t amount, bool skewed)
Scroll grid by amount upward (optionnaly skewed to the left)
Definition: grid_rule.hpp:172
void reset(LampTy &lamp, LineTy &firstLine)
To be called in the parent .reset() to set firstLine as first line.
Definition: grid_rule.hpp:64
uint32_t lastUpdate
latest update time, in milliseconds
Definition: grid_rule.hpp:272
LineTy LMBD_INLINE lineAtIndex(LampTy &lamp, uint16_t lineIndex)
Return a copy of line at lineAtIndex (may be empty if out of screen)
Definition: grid_rule.hpp:156
LineTy currentLine
Current line being processed.
Definition: grid_rule.hpp:270
void LMBD_INLINE smoothDisplay(LampTy &lamp, float phasis)
Display grid buffers as an in-between frame, at phasis (from 0 to 1)
Definition: grid_rule.hpp:142
Main interface between the user and the hardware of the lamp.
Definition: lamp_type.hpp:114
void setColorsFromMixedBuffers(float phase)
Mix two colors from a buffer.
Definition: lamp_type.hpp:989
void LMBD_INLINE blur(uint8_t blurBy)
Blur currently displayed content by blurBy units /!\ Only applied along the strip,...
Definition: lamp_type.hpp:753
static constexpr uint16_t maxHeight
(indexable) Height of "pixel space" w/ lamp taken as a LED matrix
Definition: lamp_type.hpp:387
static constexpr float maxWidthFloat
Width as a precise floating point number, equal to stripXCoordinates.
Definition: lamp_type.hpp:369
volatile const uint32_t now
(physical) The "now" on milliseconds, updated just before loop.
Definition: lamp_type.hpp:1075
static constexpr uint32_t frameDurationMs
Hardward try to call .loop() every frameDurationMs (12ms for 83.3fps)
Definition: lamp_type.hpp:339
static constexpr uint16_t maxWidth
(indexable) Width of "pixel space" w/ lamp taken as a LED matrix
Definition: lamp_type.hpp:366
brightness_t LMBD_INLINE getBrightness(const bool readPreviousBrightness=false)
Get brightness of the lamp.
Definition: lamp_type.hpp:694
volatile const uint32_t raw_frame_count
(physical) Raw frame count, incremented at an undefined rate
Definition: lamp_type.hpp:1092