37 if (!jsonArray.has_value() || !jsonArray.is_array()) {
40 auto begin_float = jsonArray.begin_array<
float>();
41 auto end_float = jsonArray.end_array<
float>();
43 using T =
decltype(*begin_float);
48 for (
auto it = begin_float; it != end_float; ++it) {
52 auto parseResult = *it;
53 if (!parseResult.has_error()) {
54 result.push_back(parseResult.get_value());
56 FL_WARN(
"jsonArrayToFloatVector: parse_result<float> has error: " << parseResult.get_error().message);
64 float cm_led_diameter,
float completion) {
68 float circumference = numLeds * cm_between_leds;
69 float radius = circumference / (2 *
FL_PI);
72 float totalAngle = completion * 2 *
FL_PI;
73 float gapAngle = 2 *
FL_PI - totalAngle;
76 float startAngle = -
FL_PI / 2 + gapAngle / 2.0f;
80 (completion < 1.0f && numLeds > 1) ? (numLeds - 1) : numLeds;
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;
94 float cm_led_diameter,
float completion) {
95 return Circle(numLeds, cm_between_leds, cm_led_diameter, completion);
117 if (!segmentsArr.has_value() || !segmentsArr.is_array()) {
118 *err =
"v2 'segments' is not an array";
122 auto arrPtr = segmentsArr.as_array();
124 *err =
"v2 'segments' array could not be read";
128 for (
const auto& elem : *arrPtr) {
130 *err =
"v2 segment is null";
135 *err =
"v2 segment is not an object";
141 *err =
"v2 segment missing 'id'";
146 *err =
"v2 segment 'id' is not a string";
153 *err =
"v2 segment '" +
id +
"' missing or invalid 'x' array";
160 *err =
"v2 segment '" +
id +
"' missing or invalid 'y' array";
168 float diameter = -1.0f;
170 auto diameterOpt = segVal[
"diameter"].
as_float();
172 diameter =
static_cast<float>(*diameterOpt);
178 for (
size_t i = 0; i < n; i++) {
179 segment_map.
set(i,
vec2f{x_array[i], y_array[i]});
181 (*segmentMaps)[id] =
fl::move(segment_map);
193 FL_WARN(
"ScreenMap::ParseJson called with FASTLED_NO_JSON");
195 *err =
"JSON is not supported in this build";
207 if (!jsonDoc.has_value()) {
208 *err =
"Failed to parse JSON";
209 FL_WARN(
"Failed to parse JSON");
213 if (!jsonDoc.is_object()) {
214 *err =
"JSON root is not an object";
215 FL_WARN(
"JSON root is not an 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();
227 int v =
static_cast<int>(*versionOpt);
228 if (v == 2) explicitV2 =
true;
229 else if (v == 1) explicitV1 =
true;
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();
237 if (explicitV2 || (!explicitV1 && hasSegments && !hasMap)) {
243 if (!jsonDoc.contains(
"map")) {
244 *err =
"Missing 'map' key in JSON";
245 FL_WARN(
"Missing 'map' key in JSON");
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");
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");
264 auto& jsonMap = *jsonMapPtr;
267 for (
const auto& kv : jsonMap) {
268 auto name = kv.first;
273 *err =
"Null value for segment " + name;
280 *err =
"Invalid value for segment " + name;
285 *err =
"Segment value for " + name +
" is not an object";
291 *err =
"Missing x array for " + name;
295 if (!val[
"x"].has_value() || !val[
"x"].
is_array()) {
296 *err =
"Invalid x array for " + name;
305 *err =
"Missing y array for " + name;
309 if (!val[
"y"].has_value() || !val[
"y"].
is_array()) {
310 *err =
"Invalid y array for " + name;
318 float diameter = -1.0f;
320 auto diameterOpt = val[
"diameter"].
as_float();
322 diameter =
static_cast<float>(*diameterOpt);
327 if (n != x_array.
size() || n != y_array.
size()) {
328 if (n != x_array.
size()) {
330 if (n != y_array.
size()) {
335 for (
size_t i = 0; i < n; i++) {
336 segment_map.
set(i,
vec2f{x_array[i], y_array[i]});
338 (*segmentMaps)[name] =
fl::move(segment_map);
349 bool ok =
ParseJson(jsonStrScreenMap, &segmentMaps, err);
353 if (segmentMaps.
size() == 0) {
356 if (segmentMaps.
contains(screenMapName)) {
360 string _err =
"ScreenMap not found: ";
361 _err.
append(screenMapName);
373 FL_WARN(
"ScreenMap::toJson called with FASTLED_NO_JSON");
377 FL_WARN(
"ScreenMap::toJson called with nullptr doc");
396 static const char *
const kPalette[] = {
397 "#3b82f6",
"#10b981",
"#f59e0b",
"#ef4444",
398 "#a855f7",
"#06b6d4",
"#ec4899",
"#84cc16",
400 constexpr size_t kPaletteSize =
sizeof(kPalette) /
sizeof(kPalette[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));
409 const auto& name = kv.first;
410 const auto& segment = kv.second;
411 const float diameter = segment.getDiameter();
414 for (u16 i = 0; i < segment.getLength(); i++) {
418 for (u16 i = 0; i < segment.getLength(); i++) {
424 groupsObj.
set(name, groupObj);
430 segmentObj.
set(
"x", xArray);
431 segmentObj.
set(
"y", yArray);
432 segmentObj.
set(
"diameter",
fl::json(
static_cast<double>(diameter)));
438 doc->
set(
"version",
fl::json(
static_cast<double>(2)));
439 doc->
set(
"groups", groupsObj);
440 doc->
set(
"segments", segmentsArr);
443 FL_WARN(
"ScreenMap::toJson generated JSON: " << debugStr);
448 string *jsonBuffer) {
450 toJson(segmentMaps, &doc);
482 for (
int i = 0; i < count; i++) {
500 other.mLookUpTable.reset();
501 other.mSourceXYMap.reset();
519 vec2f screen_coords = lut[
x];
520 return screen_coords;
540 float minX = data[0].
x;
541 float maxX = data[0].
x;
542 float minY = data[0].
y;
543 float maxY = data[0].
y;
545 for (u32 i = 1; i <
length; i++) {
546 const vec2f &p = lut[i];
553 return {maxX - minX, maxY - minY};
579 if (
this != &other) {
589 if (
this != &other) {
595 other.mDiameter = -1.0f;
618 for (u32 i = 0; i <
length; i++) {
619 vec2f &curr = data[i];
T * getDataMutable() FL_NOEXCEPT
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
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
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
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.
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
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
bool contains(const Key &key) const FL_NOEXCEPT
void push_back(const json &value) FL_NOEXCEPT
bool is_array() const FL_NOEXCEPT
fl::optional< float > as_float() const FL_NOEXCEPT
bool has_value() const FL_NOEXCEPT
bool is_object() const FL_NOEXCEPT
fl::string to_string() const FL_NOEXCEPT
bool contains(size_t idx) const FL_NOEXCEPT
fl::optional< fl::string > as_string() const FL_NOEXCEPT
void set(const fl::string &key, const json &value) FL_NOEXCEPT
static json parse(const fl::string &txt) FL_NOEXCEPT
static json object() FL_NOEXCEPT
static json array() FL_NOEXCEPT
string & append(const bitset_fixed< N > &bs) FL_NOEXCEPT
fl::size size() const FL_NOEXCEPT
FastLED's Elegant JSON Library: fl::json
Centralized logging categories for FastLED hardware interfaces and subsystems.
constexpr remove_reference< T >::type && move(T &&t) FL_NOEXCEPT
void swap(T &a, T &b) FL_NOEXCEPT
FL_DISABLE_WARNING_PUSH U constexpr common_type_t< T, U > min(T a, U b) FL_NOEXCEPT
constexpr int type_rank< T >::value
constexpr common_type_t< T, U > max(T a, U b) FL_NOEXCEPT
shared_ptr< XYMap > XYMapPtr
fl::vector< float > jsonArrayToFloatVector(const fl::json &jsonArray)
shared_ptr< T > make_shared(Args &&... args) FL_NOEXCEPT
expected< T, E > result
Alias for expected (Rust-style naming)
enable_if< is_fixed_point< T >::value, T >::type cos(T angle) FL_NOEXCEPT
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.
Portable compile-time assertion wrapper.