47 return diameter > 0.0f ? diameter : -1.0f;
60 if (!jsonArray.has_value() || !jsonArray.is_array()) {
63 auto begin_float = jsonArray.begin_array<
float>();
64 auto end_float = jsonArray.end_array<
float>();
66 using T =
decltype(*begin_float);
71 for (
auto it = begin_float; it != end_float; ++it) {
75 auto parseResult = *it;
76 if (!parseResult.has_error()) {
77 result.push_back(parseResult.get_value());
79 FL_WARN(
"jsonArrayToFloatVector: parse_result<float> has error: " << parseResult.get_error().message);
91 float circumference = numLeds * cm_between_leds;
92 float radius = circumference / (2 *
FL_PI);
95 float totalAngle = completion * 2 *
FL_PI;
96 float gapAngle = 2 *
FL_PI - totalAngle;
99 float startAngle = -
FL_PI / 2 + gapAngle / 2.0f;
103 (completion < 1.0f && numLeds > 1) ? (numLeds - 1) : numLeds;
105 for (
int i = 0; i < numLeds; ++i) {
106 float angle = startAngle + (i * totalAngle) / divisor;
107 float x = radius *
cos(angle) * 2;
108 float y = radius *
sin(angle) * 2;
118 return Circle(numLeds, cm_between_leds, cm_led_diameter, completion);
140 if (!segmentsArr.has_value() || !segmentsArr.is_array()) {
141 *err =
"v2 'segments' is not an array";
145 auto arrPtr = segmentsArr.as_array();
147 *err =
"v2 'segments' array could not be read";
151 for (
const auto& elem : *arrPtr) {
153 *err =
"v2 segment is null";
158 *err =
"v2 segment is not an object";
164 *err =
"v2 segment missing 'id'";
169 *err =
"v2 segment 'id' is not a string";
176 *err =
"v2 segment '" +
id +
"' missing or invalid 'x' array";
183 *err =
"v2 segment '" +
id +
"' missing or invalid 'y' array";
188 string type =
"led_strip";
190 auto typeOpt = segVal[
"type"].
as_string();
191 if (!typeOpt) { *err =
"v2 segment '" +
id +
"' has invalid 'type'";
return false; }
194 const bool isWire =
type ==
"el_wire";
195 const bool isPanel =
type ==
"el_panel";
196 if (
type !=
"led_strip" && !isWire && !isPanel) {
197 *err =
"v2 segment '" +
id +
"' has unsupported type '" +
type +
"'";
200 if (x_array.
size() != y_array.
size()) {
201 *err =
"v2 segment '" +
id +
"' has mismatched x/y lengths";
204 const size_t minVertices = isWire ? 2 : isPanel ? 3 : 0;
205 if ((isWire || isPanel) && x_array.
size() < minVertices) {
206 *err =
"v2 segment '" +
id +
"' has too few vertices";
209 float thickness = 0.0f;
212 *err =
"v2 el_wire segment '" +
id +
"' missing thickness";
215 auto thicknessOpt = segVal[
"thickness"].
as_float();
216 if (!thicknessOpt || *thicknessOpt <= 0.0f) {
217 *err =
"v2 el_wire segment '" +
id +
"' requires positive thickness";
220 thickness =
static_cast<float>(*thicknessOpt);
221 }
else if (segVal.
contains(
"thickness")) {
222 *err =
"v2 segment '" +
id +
"' thickness is only valid for el_wire";
229 float diameter = -1.0f;
231 auto diameterOpt = segVal[
"diameter"].
as_float();
233 diameter =
static_cast<float>(*diameterOpt);
238 ScreenMap segment_map(isWire || isPanel ? 1 :
static_cast<u32
>(n), diameter);
239 if (isWire || isPanel) {
242 for (
size_t i = 0; i < n; i++) vertices.
push_back({x_array[i], y_array[i]});
245 if (!isWire && !isPanel) {
246 for (
size_t i = 0; i < n; i++) {
247 segment_map.
set(
static_cast<u16
>(i),
vec2f{x_array[i], y_array[i]});
250 (*segmentMaps)[id] =
fl::move(segment_map);
262 FL_WARN(
"ScreenMap::ParseJson called with FASTLED_NO_JSON");
264 *err =
"JSON is not supported in this build";
276 if (!jsonDoc.has_value()) {
277 *err =
"Failed to parse JSON";
278 FL_WARN(
"Failed to parse JSON");
282 if (!jsonDoc.is_object()) {
283 *err =
"JSON root is not an object";
284 FL_WARN(
"JSON root is not an object");
291 bool explicitV2 =
false;
292 bool explicitV1 =
false;
293 if (jsonDoc.contains(
"version") && jsonDoc[
"version"].has_value()) {
294 auto versionOpt = jsonDoc[
"version"].as_int();
296 int v =
static_cast<int>(*versionOpt);
297 if (v == 2) explicitV2 =
true;
298 else if (v == 1) explicitV1 =
true;
301 bool hasSegments = jsonDoc.contains(
"segments") && jsonDoc[
"segments"].has_value()
302 && jsonDoc[
"segments"].is_array();
303 bool hasMap = jsonDoc.contains(
"map") && jsonDoc[
"map"].has_value()
304 && jsonDoc[
"map"].is_object();
306 if (explicitV2 || (!explicitV1 && hasSegments && !hasMap)) {
312 if (!jsonDoc.contains(
"map")) {
313 *err =
"Missing 'map' key in JSON";
314 FL_WARN(
"Missing 'map' key in JSON");
319 auto mapObj = jsonDoc[
"map"];
320 if (!mapObj.has_value() || !mapObj.is_object()) {
321 *err =
"Invalid 'map' object in JSON";
322 FL_WARN(
"Invalid 'map' object in JSON");
326 auto jsonMapPtr = mapObj.as_object();
327 if (!jsonMapPtr || jsonMapPtr->empty()) {
328 *err =
"Failed to parse map from JSON or map is empty";
329 FL_WARN(
"Failed to parse map from JSON or map is empty");
333 auto& jsonMap = *jsonMapPtr;
336 for (
const auto& kv : jsonMap) {
337 auto name = kv.first;
342 *err =
"Null value for segment " + name;
349 *err =
"Invalid value for segment " + name;
354 *err =
"Segment value for " + name +
" is not an object";
360 *err =
"Missing x array for " + name;
364 if (!val[
"x"].has_value() || !val[
"x"].
is_array()) {
365 *err =
"Invalid x array for " + name;
374 *err =
"Missing y array for " + name;
378 if (!val[
"y"].has_value() || !val[
"y"].
is_array()) {
379 *err =
"Invalid y array for " + name;
387 float diameter = -1.0f;
389 auto diameterOpt = val[
"diameter"].
as_float();
391 diameter =
static_cast<float>(*diameterOpt);
396 if (n != x_array.
size() || n != y_array.
size()) {
397 if (n != x_array.
size()) {
399 if (n != y_array.
size()) {
404 for (
size_t i = 0; i < n; i++) {
405 segment_map.
set(i,
vec2f{x_array[i], y_array[i]});
407 (*segmentMaps)[name] =
fl::move(segment_map);
418 bool ok =
ParseJson(jsonStrScreenMap, &segmentMaps, err);
422 if (segmentMaps.
size() == 0) {
425 if (segmentMaps.
contains(screenMapName)) {
429 string _err =
"ScreenMap not found: ";
430 _err.
append(screenMapName);
442 FL_WARN(
"ScreenMap::toJson called with FASTLED_NO_JSON");
446 FL_WARN(
"ScreenMap::toJson called with nullptr doc");
469 static const char *
const kPalette[] = {
470 "#3b82f6",
"#10b981",
"#f59e0b",
"#ef4444",
471 "#a855f7",
"#06b6d4",
"#ec4899",
"#84cc16",
473 constexpr size_t kPaletteSize =
sizeof(kPalette) /
sizeof(kPalette[0]);
476 for (
const auto& kv : segmentMaps) {
477 if (kv.second.getLength() == 0) {
478 FL_WARN(
"ScreenMap::toJson called with empty segment: " << (
fl::string(kv.first)));
482 const auto& name = kv.first;
483 const auto& segment = kv.second;
484 const float diameter = segment.getDiameter();
487 for (u16 i = 0; i < segment.getLength(); i++) {
491 for (u16 i = 0; i < segment.getLength(); i++) {
497 groupsObj.
set(name, groupObj);
519 segmentObj.
set(
"x", xArray);
520 segmentObj.
set(
"y", yArray);
531 if (diameter > 0.0f) {
540 doc->set(
"groups", groupsObj);
541 doc->set(
"segments", segmentsArr);
544 FL_WARN(
"ScreenMap::toJson generated JSON: " << debugStr);
551 toJson(segmentMaps, &doc);
583 for (
int i = 0; i < count; i++) {
603 other.mLookUpTable.reset();
604 other.mSourceXYMap.reset();
616 mDiameter = normalizedDiameter(diameter);
624 vec2f screen_coords = lut[
x];
625 return screen_coords;
645 float minX = data[0].
x;
646 float maxX = data[0].
x;
647 float minY = data[0].
y;
648 float maxY = data[0].
y;
650 for (u32 i = 1; i <
length; i++) {
651 const vec2f &p = lut[i];
658 return {maxX - minX, maxY - minY};
663 if (!vertices || count == 0)
return;
670 for (u32 i = 0; i < count; ++i) shape.
vertices.push_back(vertices[i]);
677 static const Shape emptyShape = [] {
708 if (
this != &other) {
719 if (
this != &other) {
726 other.mDiameter = -1.0f;
749 for (u32 i = 0; i <
length; i++) {
750 vec2f &curr = data[i];
T * getDataMutable() FL_NO_EXCEPT
void addOffset(const vec2f &p) FL_NO_EXCEPT
static void toJsonStr(const fl::flat_map< string, ScreenMap > &, string *jsonBuffer) FL_NO_EXCEPT
static ScreenMap DefaultStrip(int numLeds, float cm_between_leds=1.5f, float cm_led_diameter=0.2f, float completion=.9f) FL_NO_EXCEPT
static ScreenMap Circle(int numLeds, float cm_between_leds=1.5f, float cm_led_diameter=0.5f, float completion=1.0f) FL_NO_EXCEPT
bool hasShapes() const FL_NO_EXCEPT
void setSourceXYMap(const fl::shared_ptr< XYMap > &xymap) FL_NO_EXCEPT
Set the source XYMap (used for pixel transformation during encoding)
const XYMap * getXYMap() const FL_NO_EXCEPT
Get the source XYMap as a raw const pointer.
ScreenMap & addOffsetY(float y) FL_NO_EXCEPT
ScreenMap & addOffsetX(float x) FL_NO_EXCEPT
fl::vector< Shape > mShapes
const vec2f & operator[](u32 x) const FL_NO_EXCEPT
void setShape(u32 index, Shape::Type type, const vec2f *vertices, u32 count, float thickness=0.0f) FL_NO_EXCEPT
static bool ParseJson(const char *jsonStrScreenMap, fl::flat_map< string, ScreenMap > *segmentMaps, string *err=nullptr) FL_NO_EXCEPT
ScreenMap & operator=(const ScreenMap &other) FL_NO_EXCEPT
float getDiameter() const FL_NO_EXCEPT
LUTXYFLOATPtr mLookUpTable
vec2f mapToIndex(u32 x) const FL_NO_EXCEPT
static const vec2f & empty() FL_NO_EXCEPT
u32 getShapeCount() const FL_NO_EXCEPT
void setDiameter(float diameter) FL_NO_EXCEPT
bool hasSourceXYMap() const FL_NO_EXCEPT
Check if source XYMap is available.
void set(u16 index, const vec2f &p) FL_NO_EXCEPT
u32 getLength() const FL_NO_EXCEPT
const Shape & getShape(u32 index) const FL_NO_EXCEPT
vec2f getBounds() const FL_NO_EXCEPT
static void toJson(const fl::flat_map< string, ScreenMap > &, fl::json *doc) FL_NO_EXCEPT
const XYMapPtr & getSourceXYMapPtr() const FL_NO_EXCEPT
Get the source XYMap shared pointer if available.
bool contains(const Key &key) const FL_NO_EXCEPT
size_type size() const FL_NO_EXCEPT
bool is_object() const FL_NO_EXCEPT
fl::string to_string() const FL_NO_EXCEPT
fl::optional< fl::string > as_string() const FL_NO_EXCEPT
void set(const fl::string &key, const json &value) FL_NO_EXCEPT
bool has_value() const FL_NO_EXCEPT
fl::optional< float > as_float() const FL_NO_EXCEPT
bool is_array() const FL_NO_EXCEPT
void push_back(const json &value) FL_NO_EXCEPT
bool contains(size_t idx) const FL_NO_EXCEPT
static json parse(const fl::string &txt) FL_NO_EXCEPT
static json object() FL_NO_EXCEPT
static json array() FL_NO_EXCEPT
string & append(const bitset_fixed< N > &bs) FL_NO_EXCEPT
fl::size size() const FL_NO_EXCEPT
void reserve(fl::size n) FL_NO_EXCEPT
void push_back(const T &value) FL_NO_EXCEPT
fl::UISlider length("Length", 1.0f, 0.0f, 1.0f, 0.01f)
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_NO_EXCEPT
shared_ptr< T > make_shared(Args &&... args) FL_NO_EXCEPT
float normalizedDiameter(float diameter) FL_NO_EXCEPT
constexpr common_type_t< T, U > max(T a, U b) FL_NO_EXCEPT
enable_if< is_fixed_point< T >::value, T >::type sin(T angle) FL_NO_EXCEPT
void swap(array< T, N > &lhs, array< T, N > &rhs) FL_NO_EXCEPT
u8 u8 fract8 blur_amount FL_NO_EXCEPT
constexpr int type_rank< T >::value
enable_if< is_fixed_point< T >::value, T >::type cos(T angle) FL_NO_EXCEPT
FL_DISABLE_WARNING_PUSH U constexpr common_type_t< T, U > min(T a, U b) FL_NO_EXCEPT
shared_ptr< XYMap > XYMapPtr
fl::vector< float > jsonArrayToFloatVector(const fl::json &jsonArray) FL_NO_EXCEPT
expected< T, E > result
Alias for expected (Rust-style naming)
FL_STATIC_ASSERT(static_cast< fl::u8 >(Bus::FLEX_IO)==8, "Bus changed: update busName() and busDriverName()")
static bool parseV2SegmentArray(const fl::json &segmentsArr, fl::flat_map< string, ScreenMap > *segmentMaps, string *err) FL_NO_EXCEPT
Base definition for an LED controller.
Portable compile-time assertion wrapper.
enum fl::ScreenMap::Shape::Type type
fl::vector< vec2f > vertices