FastLED 3.9.15
Loading...
Searching...
No Matches
screenmap.cpp.hpp
Go to the documentation of this file.
1/* Screenmap maps strip indexes to x,y coordinates. This is used for FastLED Web
2 * to map the 1D strip to a 2D grid. Note that the strip can have arbitrary
3 * size. this was first motivated during the (attempted? Oct. 19th 2024) port of
4 * the Chromancer project to FastLED Web.
5 */
6
7#include "fl/math/screenmap.h"
8
9// Heavy includes moved from header to reduce compilation time
10#include "fl/math/lut.h" // Full LUT definitions needed for implementation
11#include "fl/stl/json.h" // 61.1ms - only needed for ParseJson/toJson implementations
12#include "fl/stl/string.h" // 129.4ms - only needed for string parameters in implementations
13#include "fl/stl/flat_map.h" // 12.4ms - only needed for flat_map parameters in implementations
14// IWYU pragma: begin_keep
15#include "fl/stl/function.h"
16// IWYU pragma: end_keep // ~5ms - only needed for function<> constructor implementation
18
19// Other implementation dependencies
20#include "fl/math/math.h"
21#include "fl/math/math.h"
22#include "fl/stl/vector.h"
23#include "fl/log/log.h"
24#include "fl/stl/noexcept.h"
25
26
27namespace fl {
28
29// Default constructor and destructor - must be in .cpp for proper smart_ptr handling
30ScreenMap::ScreenMap() = default;
32
33// Helper function to extract a vector of floats from a JSON array
34fl::vector<float> jsonArrayToFloatVector(const fl::json& jsonArray) {
36
37 if (!jsonArray.has_value() || !jsonArray.is_array()) {
38 return result;
39 }
40 auto begin_float = jsonArray.begin_array<float>();
41 auto end_float = jsonArray.end_array<float>();
42
43 using T = decltype(*begin_float);
44 FL_STATIC_ASSERT(fl::is_same<T, fl::parse_result<float>>::value, "Value type must be parse_result<float>");
45
46 // Use explicit array iterator style as demonstrated in FEATURE.md
47 // DO NOT CHANGE THIS CODE. FIX THE IMPLIMENTATION IF NECESSARY.
48 for (auto it = begin_float; it != end_float; ++it) {
49 // assert that the value type is parse_result<float>
50
51 // get the name of the type
52 auto parseResult = *it;
53 if (!parseResult.has_error()) {
54 result.push_back(parseResult.get_value());
55 } else {
56 FL_WARN("jsonArrayToFloatVector: parse_result<float> has error: " << parseResult.get_error().message);
57 }
58 }
59
60 return result;
61}
62
63ScreenMap ScreenMap::Circle(int numLeds, float cm_between_leds,
64 float cm_led_diameter, float completion) {
65 ScreenMap screenMap(numLeds);
66
67 // radius from LED spacing
68 float circumference = numLeds * cm_between_leds;
69 float radius = circumference / (2 * FL_PI);
70
71 // how big an arc we light vs leave dark
72 float totalAngle = completion * 2 * FL_PI;
73 float gapAngle = 2 * FL_PI - totalAngle;
74
75 // shift so the dark gap is centered at the bottom (–π/2)
76 float startAngle = -FL_PI / 2 + gapAngle / 2.0f;
77
78 // if partial, land last LED exactly at startAngle+totalAngle
79 float divisor =
80 (completion < 1.0f && numLeds > 1) ? (numLeds - 1) : numLeds;
81
82 for (int i = 0; i < numLeds; ++i) {
83 float angle = startAngle + (i * totalAngle) / divisor;
84 float x = radius * cos(angle) * 2;
85 float y = radius * sin(angle) * 2;
86 screenMap[i] = {x, y};
87 }
88
89 screenMap.setDiameter(cm_led_diameter);
90 return screenMap;
91}
92
93ScreenMap ScreenMap::DefaultStrip(int numLeds, float cm_between_leds,
94 float cm_led_diameter, float completion) {
95 return Circle(numLeds, cm_between_leds, cm_led_diameter, completion);
96}
97
98// Helper: parse a v2 screenmap document into the flat segmentMaps.
99// v2 shape (see ledmapper #92):
100// {
101// "version": 2, // optional, explicit
102// "groups": { "<name>": { "color": "...", ... } }, // optional, ignored by firmware
103// "segments": [
104// { "id": "<unique>", "pin": <int|str>, "group": "<name>",
105// "x": [...], "y": [...], "z": [...], // z optional
106// "parent": "<id>", "offset": <int|null> } // parent+offset on forks only
107// ]
108// }
109//
110// Flatten policy: each segment becomes one ScreenMap entry keyed by `id`.
111// `pin`, `group`, `parent`, `offset` are not represented in the firmware-side
112// flat_map; they're UI/wiring metadata the editor and video tools care about.
113// `z` is dropped (firmware-side ScreenMap is 2D today).
114static bool parseV2SegmentArray(const fl::json& segmentsArr,
116 string *err) FL_NOEXCEPT {
117 if (!segmentsArr.has_value() || !segmentsArr.is_array()) {
118 *err = "v2 'segments' is not an array";
119 return false;
120 }
121
122 auto arrPtr = segmentsArr.as_array();
123 if (!arrPtr) {
124 *err = "v2 'segments' array could not be read";
125 return false;
126 }
127
128 for (const auto& elem : *arrPtr) {
129 if (!elem) {
130 *err = "v2 segment is null";
131 return false;
132 }
133 fl::json segVal(elem);
134 if (!segVal.has_value() || !segVal.is_object()) {
135 *err = "v2 segment is not an object";
136 return false;
137 }
138
139 // Required: id
140 if (!segVal.contains("id") || !segVal["id"].has_value()) {
141 *err = "v2 segment missing 'id'";
142 return false;
143 }
144 auto idOpt = segVal["id"].as_string();
145 if (!idOpt) {
146 *err = "v2 segment 'id' is not a string";
147 return false;
148 }
149 string id = *idOpt;
150
151 // Required: x
152 if (!segVal.contains("x") || !segVal["x"].has_value() || !segVal["x"].is_array()) {
153 *err = "v2 segment '" + id + "' missing or invalid 'x' array";
154 return false;
155 }
156 fl::vector<float> x_array = jsonArrayToFloatVector(segVal["x"]);
157
158 // Required: y
159 if (!segVal.contains("y") || !segVal["y"].has_value() || !segVal["y"].is_array()) {
160 *err = "v2 segment '" + id + "' missing or invalid 'y' array";
161 return false;
162 }
163 fl::vector<float> y_array = jsonArrayToFloatVector(segVal["y"]);
164
165 // Optional: diameter (not in canonical v2 but accepted as a backward-compat
166 // hint when present; otherwise the per-group `diameter` could be wired here
167 // in a future iteration).
168 float diameter = -1.0f;
169 if (segVal.contains("diameter") && segVal["diameter"].has_value()) {
170 auto diameterOpt = segVal["diameter"].as_float();
171 if (diameterOpt) {
172 diameter = static_cast<float>(*diameterOpt);
173 }
174 }
175
176 auto n = fl::min(x_array.size(), y_array.size());
177 ScreenMap segment_map(n, diameter);
178 for (size_t i = 0; i < n; i++) {
179 segment_map.set(i, vec2f{x_array[i], y_array[i]});
180 }
181 (*segmentMaps)[id] = fl::move(segment_map);
182 }
183 return true;
184}
185
186bool ScreenMap::ParseJson(const char *jsonStrScreenMap,
187 fl::flat_map<string, ScreenMap> *segmentMaps, string *err) {
188
189#if FASTLED_NO_JSON
190 FL_UNUSED(jsonStrScreenMap);
191 FL_UNUSED(segmentMaps);
192 FL_UNUSED(err);
193 FL_WARN("ScreenMap::ParseJson called with FASTLED_NO_JSON");
194 if (err) {
195 *err = "JSON is not supported in this build";
196 }
197 return false;
198#else
199 //FL_WARN_SCREENMAP("ParseJson called with JSON: " << jsonStrScreenMap);
200
201 string _err;
202 if (!err) {
203 err = &_err;
204 }
205
206 auto jsonDoc = fl::json::parse(jsonStrScreenMap);
207 if (!jsonDoc.has_value()) {
208 *err = "Failed to parse JSON";
209 FL_WARN("Failed to parse JSON");
210 return false;
211 }
212
213 if (!jsonDoc.is_object()) {
214 *err = "JSON root is not an object";
215 FL_WARN("JSON root is not an object");
216 return false;
217 }
218
219 // ── v2 dispatch ──────────────────────────────────────────────────────
220 // v2 if: explicit "version": 2 OR has top-level "segments" array.
221 // v1 if: explicit "version": 1 OR has top-level "map" object.
222 bool explicitV2 = false;
223 bool explicitV1 = false;
224 if (jsonDoc.contains("version") && jsonDoc["version"].has_value()) {
225 auto versionOpt = jsonDoc["version"].as_int();
226 if (versionOpt) {
227 int v = static_cast<int>(*versionOpt);
228 if (v == 2) explicitV2 = true;
229 else if (v == 1) explicitV1 = true;
230 }
231 }
232 bool hasSegments = jsonDoc.contains("segments") && jsonDoc["segments"].has_value()
233 && jsonDoc["segments"].is_array();
234 bool hasMap = jsonDoc.contains("map") && jsonDoc["map"].has_value()
235 && jsonDoc["map"].is_object();
236
237 if (explicitV2 || (!explicitV1 && hasSegments && !hasMap)) {
238 return parseV2SegmentArray(jsonDoc["segments"], segmentMaps, err);
239 }
240
241 // Fall through to v1 path.
242 // Check if "map" key exists and is an object
243 if (!jsonDoc.contains("map")) {
244 *err = "Missing 'map' key in JSON";
245 FL_WARN("Missing 'map' key in JSON");
246 return false;
247 }
248
249 // Get the map object
250 auto mapObj = jsonDoc["map"];
251 if (!mapObj.has_value() || !mapObj.is_object()) {
252 *err = "Invalid 'map' object in JSON";
253 FL_WARN("Invalid 'map' object in JSON");
254 return false;
255 }
256
257 auto jsonMapPtr = mapObj.as_object();
258 if (!jsonMapPtr || jsonMapPtr->empty()) {
259 *err = "Failed to parse map from JSON or map is empty";
260 FL_WARN("Failed to parse map from JSON or map is empty");
261 return false;
262 }
263
264 auto& jsonMap = *jsonMapPtr;
265
266
267 for (const auto& kv : jsonMap) {
268 auto name = kv.first;
269
270
271 // Check that the value is not null before creating json object
272 if (!kv.second) {
273 *err = "Null value for segment " + name;
274 return false;
275 }
276
277 // Create json object directly from shared_ptr
278 fl::json val(kv.second);
279 if (!val.has_value()) {
280 *err = "Invalid value for segment " + name;
281 return false;
282 }
283
284 if (!val.is_object()) {
285 *err = "Segment value for " + name + " is not an object";
286 return false;
287 }
288
289 // Check if x array exists and is actually an array
290 if (!val.contains("x")) {
291 *err = "Missing x array for " + name;
292 return false;
293 }
294
295 if (!val["x"].has_value() || !val["x"].is_array()) {
296 *err = "Invalid x array for " + name;
297 return false;
298 }
299
300 // Extract x array using our helper function
301 fl::vector<float> x_array = jsonArrayToFloatVector(val["x"]);
302
303 // Check if y array exists and is actually an array
304 if (!val.contains("y")) {
305 *err = "Missing y array for " + name;
306 return false;
307 }
308
309 if (!val["y"].has_value() || !val["y"].is_array()) {
310 *err = "Invalid y array for " + name;
311 return false;
312 }
313
314 // Extract y array using our helper function
315 fl::vector<float> y_array = jsonArrayToFloatVector(val["y"]);
316
317 // Get diameter (optional) with default value
318 float diameter = -1.0f; // default value
319 if (val.contains("diameter") && val["diameter"].has_value()) {
320 auto diameterOpt = val["diameter"].as_float();
321 if (diameterOpt) {
322 diameter = static_cast<float>(*diameterOpt);
323 }
324 }
325
326 auto n = fl::min(x_array.size(), y_array.size());
327 if (n != x_array.size() || n != y_array.size()) {
328 if (n != x_array.size()) {
329 }
330 if (n != y_array.size()) {
331 }
332 }
333
334 ScreenMap segment_map(n, diameter);
335 for (size_t i = 0; i < n; i++) {
336 segment_map.set(i, vec2f{x_array[i], y_array[i]});
337 }
338 (*segmentMaps)[name] = fl::move(segment_map);
339 }
340 return true;
341#endif
342}
343
344bool ScreenMap::ParseJson(const char *jsonStrScreenMap,
345 const char *screenMapName, ScreenMap *screenmap,
346 string *err) {
347
349 bool ok = ParseJson(jsonStrScreenMap, &segmentMaps, err);
350 if (!ok) {
351 return false;
352 }
353 if (segmentMaps.size() == 0) {
354 return false;
355 }
356 if (segmentMaps.contains(screenMapName)) {
357 *screenmap = segmentMaps[screenMapName];
358 return true;
359 }
360 string _err = "ScreenMap not found: ";
361 _err.append(screenMapName);
362 if (err) {
363 *err = _err;
364 }
365
366 return false;
367}
368
370 fl::json *doc) {
371
372#if FASTLED_NO_JSON
373 FL_WARN("ScreenMap::toJson called with FASTLED_NO_JSON");
374 return;
375#else
376 if (!doc) {
377 FL_WARN("ScreenMap::toJson called with nullptr doc");
378 return;
379 }
380
381 // Emits the v2 screenmap shape (issue ledmapper#143):
382 // { "version": 2,
383 // "groups": { "<name>": { "color": "#hex" } },
384 // "segments": [ { "id": "<name>", "pin": "pin1", "group": "<name>",
385 // "x": [...], "y": [...], "diameter": ... } ] }
386 // Bilingual readers (`ScreenMap::ParseJson`, ledmapper) accept both v1
387 // and v2, so any existing on-disk v1 JSON keeps loading. v1 emission
388 // is no longer supported.
389 *doc = fl::json::object();
390
391 fl::json groupsObj = fl::json::object();
392 fl::json segmentsArr = fl::json::array();
393
394 // Distinct palette so each strip lights up differently in the editor
395 // preview without the user having to pick a colour. Cycle on overflow.
396 static const char *const kPalette[] = {
397 "#3b82f6", "#10b981", "#f59e0b", "#ef4444",
398 "#a855f7", "#06b6d4", "#ec4899", "#84cc16",
399 };
400 constexpr size_t kPaletteSize = sizeof(kPalette) / sizeof(kPalette[0]);
401
402 size_t idx = 0;
403 for (const auto& kv : segmentMaps) {
404 if (kv.second.getLength() == 0) {
405 FL_WARN("ScreenMap::toJson called with empty segment: " << fl::string(kv.first));
406 continue;
407 }
408
409 const auto& name = kv.first;
410 const auto& segment = kv.second;
411 const float diameter = segment.getDiameter();
412
413 fl::json xArray = fl::json::array();
414 for (u16 i = 0; i < segment.getLength(); i++) {
415 xArray.push_back(fl::json(static_cast<double>(segment[i].x)));
416 }
417 fl::json yArray = fl::json::array();
418 for (u16 i = 0; i < segment.getLength(); i++) {
419 yArray.push_back(fl::json(static_cast<double>(segment[i].y)));
420 }
421
422 fl::json groupObj = fl::json::object();
423 groupObj.set("color", fl::json(fl::string(kPalette[idx % kPaletteSize])));
424 groupsObj.set(name, groupObj);
425
426 fl::json segmentObj = fl::json::object();
427 segmentObj.set("id", fl::json(fl::string(name)));
428 segmentObj.set("pin", fl::json(fl::string("pin1")));
429 segmentObj.set("group", fl::json(fl::string(name)));
430 segmentObj.set("x", xArray);
431 segmentObj.set("y", yArray);
432 segmentObj.set("diameter", fl::json(static_cast<double>(diameter)));
433 segmentsArr.push_back(segmentObj);
434
435 idx++;
436 }
437
438 doc->set("version", fl::json(static_cast<double>(2)));
439 doc->set("groups", groupsObj);
440 doc->set("segments", segmentsArr);
441
442 fl::string debugStr = doc->to_string();
443 FL_WARN("ScreenMap::toJson generated JSON: " << debugStr);
444#endif
445}
446
448 string *jsonBuffer) {
449 fl::json doc;
450 toJson(segmentMaps, &doc);
451 *jsonBuffer = doc.to_string();
452}
453
456 if (length > 0) {
458 LUTXYFLOAT &lut = *mLookUpTable.get();
459 vec2f *data = lut.getDataMutable();
460 for (u32 x = 0; x < length; x++) {
461 data[x] = {0, 0};
462 }
463 }
464}
465
466ScreenMap::ScreenMap(const vec2f *lut, u32 length, float diameter)
467 : length(length), mDiameter(diameter) {
469 LUTXYFLOAT &lut16xy = *mLookUpTable.get();
470 vec2f *data = lut16xy.getDataMutable();
471 for (u32 x = 0; x < length; x++) {
472 data[x] = lut[x];
473 }
474}
475
476ScreenMap::ScreenMap(int count, float diameter, fl::function<void(int, vec2f& pt_out)> func)
477 : length(count), mDiameter(diameter) {
478 if (count > 0) {
480 LUTXYFLOAT &lut = *mLookUpTable.get();
481 vec2f *data = lut.getDataMutable();
482 for (int i = 0; i < count; i++) {
483 func(i, data[i]);
484 }
485 }
486}
487
489 mDiameter = other.mDiameter;
490 length = other.length;
493}
494
496 mDiameter = other.mDiameter;
497 length = other.length;
498 fl::swap(mLookUpTable, other.mLookUpTable);
499 fl::swap(mSourceXYMap, other.mSourceXYMap);
500 other.mLookUpTable.reset();
501 other.mSourceXYMap.reset();
502}
503
504void ScreenMap::set(u16 index, const vec2f &p) {
505 if (mLookUpTable) {
506 LUTXYFLOAT &lut = *mLookUpTable.get();
507 auto *data = lut.getDataMutable();
508 data[index] = p;
509 }
510}
511
512void ScreenMap::setDiameter(float diameter) { mDiameter = diameter; }
513
515 if (x >= length || !mLookUpTable) {
516 return {0, 0};
517 }
518 LUTXYFLOAT &lut = *mLookUpTable.get();
519 vec2f screen_coords = lut[x];
520 return screen_coords;
521}
522
523u32 ScreenMap::getLength() const { return length; }
524
525float ScreenMap::getDiameter() const { return mDiameter; }
526
528
529 if (length == 0 || !mLookUpTable) {
530 return {0, 0};
531 }
532
533 LUTXYFLOAT &lut = *mLookUpTable.get();
534
535 fl::vec2f *data = lut.getDataMutable();
536 // float minX = lut[0].x;
537 // float maxX = lut[0].x;
538 // float minY = lut[0].y;
539 // float maxY = lut[0].y;
540 float minX = data[0].x;
541 float maxX = data[0].x;
542 float minY = data[0].y;
543 float maxY = data[0].y;
544
545 for (u32 i = 1; i < length; i++) {
546 const vec2f &p = lut[i];
547 minX = fl::min(minX, p.x);
548 maxX = fl::max(maxX, p.x);
549 minY = fl::min(minY, p.y);
550 maxY = fl::max(maxY, p.y);
551 }
552
553 return {maxX - minX, maxY - minY};
554}
555
557 static const vec2f s_empty = vec2f(0, 0); // okay static in header
558 return s_empty;
559}
560
561const vec2f &ScreenMap::operator[](u32 x) const {
562 if (x >= length || !mLookUpTable) {
563 return empty(); // better than crashing.
564 }
565 LUTXYFLOAT &lut = *mLookUpTable.get();
566 return lut[x];
567}
568
570 if (x >= length || !mLookUpTable) {
571 return const_cast<vec2f &>(empty()); // better than crashing.
572 }
573 LUTXYFLOAT &lut = *mLookUpTable.get();
574 auto *data = lut.getDataMutable();
575 return data[x];
576}
577
579 if (this != &other) {
580 mDiameter = other.mDiameter;
581 length = other.length;
582 mLookUpTable = other.mLookUpTable;
583 mSourceXYMap = other.mSourceXYMap;
584 }
585 return *this;
586}
587
589 if (this != &other) {
590 mDiameter = other.mDiameter;
591 length = other.length;
592 mLookUpTable = fl::move(other.mLookUpTable);
593 mSourceXYMap = fl::move(other.mSourceXYMap);
594 other.length = 0;
595 other.mDiameter = -1.0f;
596 }
597 return *this;
598}
599
603
605 return mSourceXYMap;
606}
607
609 return mSourceXYMap.get();
610}
611
613 return mSourceXYMap != nullptr;
614}
615
617 vec2f *data = mLookUpTable->getDataMutable();
618 for (u32 i = 0; i < length; i++) {
619 vec2f &curr = data[i];
620 curr.x += p.x;
621 curr.y += p.y;
622 }
623}
624
625ScreenMap& ScreenMap::addOffsetX(float x) { addOffset({x, 0}); return *this; }
626ScreenMap& ScreenMap::addOffsetY(float y) { addOffset({0, y}); return *this; }
627
628} // namespace fl
XYMap xymap
fl::ScreenMap screenmap
T * getDataMutable() FL_NOEXCEPT
Definition lut.h:53
u32 getLength() const FL_NOEXCEPT
static void toJson(const fl::flat_map< string, ScreenMap > &, fl::json *doc) FL_NOEXCEPT
static ScreenMap DefaultStrip(int numLeds, float cm_between_leds=1.5f, float cm_led_diameter=0.2f, float completion=.9f) FL_NOEXCEPT
float mDiameter
Definition screenmap.h:125
void setSourceXYMap(const fl::shared_ptr< XYMap > &xymap) FL_NOEXCEPT
Set the source XYMap (used for pixel transformation during encoding)
static void toJsonStr(const fl::flat_map< string, ScreenMap > &, string *jsonBuffer) FL_NOEXCEPT
const XYMapPtr & getSourceXYMapPtr() const FL_NOEXCEPT
Get the source XYMap shared pointer if available.
static const vec2f & empty() FL_NOEXCEPT
ScreenMap() FL_NOEXCEPT
void setDiameter(float diameter) FL_NOEXCEPT
vec2f getBounds() const FL_NOEXCEPT
const XYMap * getXYMap() const FL_NOEXCEPT
Get the source XYMap as a raw const pointer.
LUTXYFLOATPtr mLookUpTable
Definition screenmap.h:126
static bool ParseJson(const char *jsonStrScreenMap, fl::flat_map< string, ScreenMap > *segmentMaps, string *err=nullptr) FL_NOEXCEPT
bool hasSourceXYMap() const FL_NOEXCEPT
Check if source XYMap is available.
~ScreenMap() FL_NOEXCEPT
void addOffset(const vec2f &p) FL_NOEXCEPT
ScreenMap & addOffsetY(float y) FL_NOEXCEPT
void set(u16 index, const vec2f &p) FL_NOEXCEPT
float getDiameter() const FL_NOEXCEPT
XYMapPtr mSourceXYMap
Definition screenmap.h:127
vec2f mapToIndex(u32 x) const FL_NOEXCEPT
ScreenMap & operator=(const ScreenMap &other) FL_NOEXCEPT
ScreenMap & addOffsetX(float x) FL_NOEXCEPT
static ScreenMap Circle(int numLeds, float cm_between_leds=1.5f, float cm_led_diameter=0.5f, float completion=1.0f) FL_NOEXCEPT
const vec2f & operator[](u32 x) const FL_NOEXCEPT
size_type size() const FL_NOEXCEPT
Definition flat_map.h:96
bool contains(const Key &key) const FL_NOEXCEPT
Definition flat_map.h:156
void push_back(const json &value) FL_NOEXCEPT
Definition json.h:745
bool is_array() const FL_NOEXCEPT
Definition json.h:246
fl::optional< float > as_float() const FL_NOEXCEPT
Definition json.h:266
bool has_value() const FL_NOEXCEPT
Definition json.h:654
bool is_object() const FL_NOEXCEPT
Definition json.h:248
fl::string to_string() const FL_NOEXCEPT
Definition json.h:671
bool contains(size_t idx) const FL_NOEXCEPT
Definition json.h:625
fl::optional< fl::string > as_string() const FL_NOEXCEPT
Definition json.h:282
void set(const fl::string &key, const json &value) FL_NOEXCEPT
Definition json.h:701
static json parse(const fl::string &txt) FL_NOEXCEPT
Definition json.h:677
static json object() FL_NOEXCEPT
Definition json.h:692
static json array() FL_NOEXCEPT
Definition json.h:688
string & append(const bitset_fixed< N > &bs) FL_NOEXCEPT
Definition string.h:284
fl::size size() const FL_NOEXCEPT
fl::ScreenMap screenMap
Definition Corkscrew.h:101
FastLED's Elegant JSON Library: fl::json
#define FL_WARN(X)
Definition log.h:276
Centralized logging categories for FastLED hardware interfaces and subsystems.
#define FL_PI
Definition math.h:26
constexpr remove_reference< T >::type && move(T &&t) FL_NOEXCEPT
Definition s16x16x4.h:28
void swap(T &a, T &b) FL_NOEXCEPT
Definition s16x16x4.h:877
FL_DISABLE_WARNING_PUSH U constexpr common_type_t< T, U > min(T a, U b) FL_NOEXCEPT
Definition math.h:71
constexpr int type_rank< T >::value
constexpr common_type_t< T, U > max(T a, U b) FL_NOEXCEPT
Definition math.h:75
vec2< float > vec2f
Definition geometry.h:333
shared_ptr< XYMap > XYMapPtr
Definition screenmap.h:33
fl::vector< float > jsonArrayToFloatVector(const fl::json &jsonArray)
shared_ptr< T > make_shared(Args &&... args) FL_NOEXCEPT
Definition shared_ptr.h:414
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
enable_if< is_fixed_point< T >::value, T >::type cos(T angle) FL_NOEXCEPT
LUT< vec2f > LUTXYFLOAT
Definition lut.h:26
static bool parseV2SegmentArray(const fl::json &segmentsArr, fl::flat_map< string, ScreenMap > *segmentMaps, string *err) FL_NOEXCEPT
FL_STATIC_ASSERT(static_cast< fl::u8 >(Bus::STUB)==14, "Bus changed: add the new value to busName() in this file")
enable_if< is_fixed_point< T >::value, T >::type sin(T angle) FL_NOEXCEPT
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_UNUSED(x)
#define FL_NOEXCEPT
Portable compile-time assertion wrapper.
value_type y
Definition geometry.h:191
value_type x
Definition geometry.h:190