MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches

◆ reconstruct()

StateDecoder::ReconstructionResult MayaFlux::Nexus::StateDecoder::reconstruct ( Fabric fabric,
const std::string &  base_path 
)

Patch existing entities and construct missing ones from schema.

Entities whose id exists in the fabric are patched in place. Missing entities are constructed, their callables resolved via the fabric's function registry (no-op fallback with warning if absent), and wired. Supported wiring kinds for v2: every, move_to, commit_driven. Unsupported kinds (on, use, bind) fall back to commit_driven + warning. Hard failure (bad schema, unreadable EXR, dimension mismatch) sets last_error() and returns a zeroed result.

Parameters
fabricTarget fabric. May be empty, partial, or full.
base_pathPath stem without extension.
Returns
Counts and per-entity warnings.

Definition at line 390 of file StateDecoder.cpp.

391{
392 ReconstructionResult result;
393 m_last_error.clear();
394
395 const std::string json_path = base_path + ".json";
396 const std::string exr_path = base_path + ".exr";
397
398 IO::JSONSerializer ser;
399 auto schema_opt = ser.read<FabricSchema>(json_path);
400 if (!schema_opt) {
401 m_last_error = "Failed to load schema: " + ser.last_error();
403 return result;
404 }
405 const auto& schema = *schema_opt;
406
407 if (schema.version < 2 || schema.version > 4) {
408 m_last_error = "Unsupported schema version: " + std::to_string(schema.version);
410 return result;
411 }
412
413 if (schema.entities.empty()) {
414 m_last_error = "Schema contains no entities: " + json_path;
416 return result;
417 }
418
419 const uint32_t expected_rows = schema.version >= 4 ? 5 : k_exr_rows;
420 auto pv_opt = load_exr(exr_path, static_cast<uint32_t>(schema.entities.size()), expected_rows, m_last_error);
421 if (!pv_opt) {
423 return result;
424 }
425 auto& pv = *pv_opt;
426 const auto* pixels = pv.image.as_float();
427 const uint32_t width = pv.width;
428 const auto& r = schema.ranges;
429
430 const auto existing_ids = fabric.all_ids();
431 const std::unordered_set<uint32_t> existing(existing_ids.begin(), existing_ids.end());
432
433 for (size_t i = 0; i < schema.entities.size(); ++i) {
434 const auto& entry = schema.entities[i];
435
436 if (!kind_known(entry.kind)) {
437 result.warnings.push_back("Unknown kind '" + entry.kind
438 + "' for id " + std::to_string(entry.id) + ", skipping");
439 ++result.skipped;
440 continue;
441 }
442
443 const size_t row0 = (static_cast<size_t>(0) * width + i) * k_channels;
444 const size_t row1 = (static_cast<size_t>(1) * width + i) * k_channels;
445 const size_t row2 = (static_cast<size_t>(2) * width + i) * k_channels;
446
447 const glm::vec3 position {
448 denormalize((*pixels)[row0 + 0], r.pos_x),
449 denormalize((*pixels)[row0 + 1], r.pos_y),
450 denormalize((*pixels)[row0 + 2], r.pos_z),
451 };
452 const float intensity = denormalize((*pixels)[row0 + 3], r.intensity);
453 const float radius = denormalize((*pixels)[row2 + 0], r.radius);
454 const float query_radius = denormalize((*pixels)[row2 + 1], r.query_radius);
455
456 auto read_color = [&]() -> glm::vec3 {
457 return {
458 denormalize((*pixels)[row1 + 0], r.color_r),
459 denormalize((*pixels)[row1 + 1], r.color_g),
460 denormalize((*pixels)[row1 + 2], r.color_b),
461 };
462 };
463 auto read_size = [&]() {
464 return denormalize((*pixels)[row1 + 3], r.size);
465 };
466
467 if (existing.count(entry.id)) {
468 // -----------------------------------------------------------------
469 // Patch existing entity.
470 // -----------------------------------------------------------------
471 switch (parse_kind(entry.kind)) {
473 auto e = fabric.get_emitter(entry.id);
474 if (!e) {
475 ++result.skipped;
476 continue;
477 }
478 if (!entry.influence_fn_name.empty() && e->fn_name() != entry.influence_fn_name) {
479 result.warnings.push_back("Emitter " + std::to_string(entry.id)
480 + " fn_name mismatch: schema='" + entry.influence_fn_name
481 + "' live='" + e->fn_name() + "'");
482 }
483 e->set_position(position);
484 e->set_intensity(intensity);
485 if (entry.color) {
486 e->set_color(read_color());
487 }
488 if (entry.size) {
489 e->set_size(read_size());
490 }
491 e->set_radius(radius);
492 break;
493 }
495 auto s = fabric.get_sensor(entry.id);
496 if (!s) {
497 ++result.skipped;
498 continue;
499 }
500 if (!entry.perception_fn_name.empty() && s->fn_name() != entry.perception_fn_name) {
501 result.warnings.push_back("Sensor " + std::to_string(entry.id)
502 + " fn_name mismatch: schema='" + entry.perception_fn_name
503 + "' live='" + s->fn_name() + "'");
504 }
505 s->set_position(position);
506 s->set_query_radius(query_radius);
507 break;
508 }
509 case Fabric::Kind::Agent: {
510 auto a = fabric.get_agent(entry.id);
511 if (!a) {
512 ++result.skipped;
513 continue;
514 }
515 if (!entry.perception_fn_name.empty() && a->perception_fn_name() != entry.perception_fn_name) {
516 result.warnings.push_back("Agent " + std::to_string(entry.id)
517 + " perception_fn mismatch: schema='" + entry.perception_fn_name + "'");
518 }
519 if (!entry.influence_fn_name.empty() && a->influence_fn_name() != entry.influence_fn_name) {
520 result.warnings.push_back("Agent " + std::to_string(entry.id)
521 + " influence_fn mismatch: schema='" + entry.influence_fn_name + "'");
522 }
523 a->set_position(position);
524 a->set_intensity(intensity);
525 if (entry.color) {
526 a->set_color(read_color());
527 }
528 if (entry.size) {
529 a->set_size(read_size());
530 }
531 a->set_radius(radius);
532 a->set_query_radius(query_radius);
533 break;
534 }
535 }
536 ++result.patched;
537
538 } else {
539 // -----------------------------------------------------------------
540 // Construct missing entity.
541 // -----------------------------------------------------------------
542 switch (parse_kind(entry.kind)) {
544 auto fn_ptr = fabric.resolve_influence_fn(entry.influence_fn_name);
546 if (!fn_ptr || !*fn_ptr) {
547 result.warnings.push_back("Emitter: unknown influence_fn '"
548 + entry.influence_fn_name + "', using no-op");
549 fn = [](const InfluenceContext&) { };
550 } else {
551 fn = *fn_ptr;
552 }
553 auto emitter = std::make_shared<Emitter>(entry.influence_fn_name, std::move(fn));
554 emitter->set_position(position);
555 emitter->set_intensity(intensity);
556 emitter->set_radius(radius);
557 if (entry.color) {
558 emitter->set_color(read_color());
559 }
560 if (entry.size) {
561 emitter->set_size(read_size());
562 }
563 auto wiring = fabric.wire(emitter);
564 if (emitter->id() != entry.id) {
565 result.warnings.push_back("Emitter schema_id=" + std::to_string(entry.id)
566 + " reconstructed as runtime_id=" + std::to_string(emitter->id()));
567 }
568 apply_wiring(std::move(wiring), entry.wiring, result.warnings);
569 break;
570 }
572 auto fn_ptr = fabric.resolve_perception_fn(entry.perception_fn_name);
574 if (!fn_ptr || !*fn_ptr) {
575 result.warnings.push_back("Sensor: unknown perception_fn '"
576 + entry.perception_fn_name + "', using no-op");
577 fn = [](const PerceptionContext&) { };
578 } else {
579 fn = *fn_ptr;
580 }
581 auto sensor = std::make_shared<Sensor>(query_radius,
582 entry.perception_fn_name, std::move(fn));
583 sensor->set_position(position);
584 auto wiring = fabric.wire(sensor);
585 if (sensor->id() != entry.id) {
586 result.warnings.push_back("Sensor schema_id=" + std::to_string(entry.id)
587 + " reconstructed as runtime_id=" + std::to_string(sensor->id()));
588 }
589 apply_wiring(std::move(wiring), entry.wiring, result.warnings);
590 break;
591 }
592 case Fabric::Kind::Agent: {
593 auto pfn_ptr = fabric.resolve_perception_fn(entry.perception_fn_name);
595 if (!pfn_ptr || !*pfn_ptr) {
596 result.warnings.push_back("Agent: unknown perception_fn '"
597 + entry.perception_fn_name + "', using no-op");
598 pfn = [](const PerceptionContext&) { };
599 } else {
600 pfn = *pfn_ptr;
601 }
602 auto ifn_ptr = fabric.resolve_influence_fn(entry.influence_fn_name);
604 if (!ifn_ptr || !*ifn_ptr) {
605 result.warnings.push_back("Agent: unknown influence_fn '"
606 + entry.influence_fn_name + "', using no-op");
607 ifn = [](const InfluenceContext&) { };
608 } else {
609 ifn = *ifn_ptr;
610 }
611 auto agent = std::make_shared<Agent>(query_radius,
612 entry.perception_fn_name, std::move(pfn),
613 entry.influence_fn_name, std::move(ifn));
614 agent->set_position(position);
615 agent->set_intensity(intensity);
616 agent->set_radius(radius);
617 agent->set_query_radius(query_radius);
618 if (entry.color) {
619 agent->set_color(read_color());
620 }
621 if (entry.size) {
622 agent->set_size(read_size());
623 }
624 auto wiring = fabric.wire(agent);
625 if (agent->id() != entry.id) {
626 result.warnings.push_back("Agent schema_id=" + std::to_string(entry.id)
627 + " reconstructed as runtime_id=" + std::to_string(agent->id()));
628 }
629 apply_wiring(std::move(wiring), entry.wiring, result.warnings);
630 break;
631 }
632 }
633 ++result.constructed;
634 }
635 }
636
638 "StateDecoder::reconstruct: constructed={} patched={} skipped={} warnings={}",
639 result.constructed, result.patched, result.skipped, result.warnings.size());
640
641 return result;
642}
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
size_t a
Range intensity
uint32_t width
Range radius
Range query_radius
const std::vector< float > * pixels
WiringRecord wiring
glm::vec3 position
Cycle Behavior: The for_cycles(N) configuration controls how many times the capture operation execute...
std::function< void(const PerceptionContext &)> PerceptionFn
Definition Agent.hpp:33
std::function< void(const InfluenceContext &)> InfluenceFn
Definition Agent.hpp:32
std::function< void(const InfluenceContext &)> InfluenceFn
Definition Emitter.hpp:28
std::function< void(const PerceptionContext &)> PerceptionFn
Definition Sensor.hpp:21
@ FileIO
Filesystem I/O operations.
@ Nexus
Spatial indexing and scheduling for user-defined behaviour.

References a, MayaFlux::Nexus::Fabric::Agent, MayaFlux::Nexus::Fabric::all_ids(), MayaFlux::Nexus::StateDecoder::ReconstructionResult::constructed, MayaFlux::Nexus::Fabric::Emitter, MayaFlux::Journal::FileIO, MayaFlux::Nexus::Fabric::get_agent(), MayaFlux::Nexus::Fabric::get_emitter(), MayaFlux::Nexus::Fabric::get_sensor(), intensity, MayaFlux::IO::JSONSerializer::last_error(), m_last_error, MF_ERROR, MF_INFO, MayaFlux::Journal::Nexus, MayaFlux::Nexus::StateDecoder::ReconstructionResult::patched, pixels, position, query_radius, radius, MayaFlux::IO::JSONSerializer::read(), MayaFlux::Nexus::Fabric::resolve_influence_fn(), MayaFlux::Nexus::Fabric::resolve_perception_fn(), MayaFlux::Nexus::Fabric::Sensor, MayaFlux::Nexus::StateDecoder::ReconstructionResult::skipped, MayaFlux::Nexus::StateDecoder::ReconstructionResult::warnings, width, MayaFlux::Nexus::Fabric::wire(), and wiring.

+ Here is the call graph for this function: