256 {
257
258#if FASTLED_NO_JSON
262 FL_WARN(
"ScreenMap::ParseJson called with FASTLED_NO_JSON");
263 if (err) {
264 *err = "JSON is not supported in this build";
265 }
266 return false;
267#else
268
269
270 string _err;
271 if (!err) {
272 err = &_err;
273 }
274
276 if (!jsonDoc.has_value()) {
277 *err = "Failed to parse JSON";
278 FL_WARN(
"Failed to parse JSON");
279 return false;
280 }
281
282 if (!jsonDoc.is_object()) {
283 *err = "JSON root is not an object";
284 FL_WARN(
"JSON root is not an object");
285 return false;
286 }
287
288
289
290
291 bool explicitV2 = false;
292 bool explicitV1 = false;
293 if (jsonDoc.contains("version") && jsonDoc["version"].has_value()) {
294 auto versionOpt = jsonDoc["version"].as_int();
295 if (versionOpt) {
296 int v = static_cast<int>(*versionOpt);
297 if (v == 2) explicitV2 = true;
298 else if (v == 1) explicitV1 = true;
299 }
300 }
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();
305
306 if (explicitV2 || (!explicitV1 && hasSegments && !hasMap)) {
308 }
309
310
311
312 if (!jsonDoc.contains("map")) {
313 *err = "Missing 'map' key in JSON";
314 FL_WARN(
"Missing 'map' key in JSON");
315 return false;
316 }
317
318
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");
323 return false;
324 }
325
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");
330 return false;
331 }
332
333 auto& jsonMap = *jsonMapPtr;
334
335
336 for (const auto& kv : jsonMap) {
337 auto name = kv.first;
338
339
340
341 if (!kv.second) {
342 *err = "Null value for segment " + name;
343 return false;
344 }
345
346
347 fl::json val(kv.second);
348 if (!val.has_value()) {
349 *err = "Invalid value for segment " + name;
350 return false;
351 }
352
353 if (!val.is_object()) {
354 *err = "Segment value for " + name + " is not an object";
355 return false;
356 }
357
358
359 if (!val.contains("x")) {
360 *err = "Missing x array for " + name;
361 return false;
362 }
363
364 if (!val["x"].has_value() || !val["x"].is_array()) {
365 *err = "Invalid x array for " + name;
366 return false;
367 }
368
369
371
372
373 if (!val.contains("y")) {
374 *err = "Missing y array for " + name;
375 return false;
376 }
377
378 if (!val["y"].has_value() || !val["y"].is_array()) {
379 *err = "Invalid y array for " + name;
380 return false;
381 }
382
383
385
386
387 float diameter = -1.0f;
388 if (val.contains("diameter") && val["diameter"].has_value()) {
389 auto diameterOpt = val["diameter"].as_float();
390 if (diameterOpt) {
391 diameter = static_cast<float>(*diameterOpt);
392 }
393 }
394
396 if (n != x_array.
size() || n != y_array.
size()) {
397 if (n != x_array.
size()) {
398 }
399 if (n != y_array.
size()) {
400 }
401 }
402
404 for (size_t i = 0; i < n; i++) {
405 segment_map.set(i,
vec2f{x_array[i], y_array[i]});
406 }
407 (*segmentMaps)[name] =
fl::move(segment_map);
408 }
409 return true;
410#endif
411}
static json parse(const fl::string &txt) FL_NO_EXCEPT
fl::size size() const FL_NO_EXCEPT
constexpr remove_reference< T >::type && move(T &&t) FL_NO_EXCEPT
FL_DISABLE_WARNING_PUSH U constexpr common_type_t< T, U > min(T a, U b) FL_NO_EXCEPT
fl::vector< float > jsonArrayToFloatVector(const fl::json &jsonArray)
static bool parseV2SegmentArray(const fl::json &segmentsArr, fl::flat_map< string, ScreenMap > *segmentMaps, string *err) FL_NO_EXCEPT