From 0631f3a2c14cc707873e4c0d8b4a6b0aaa8ef7f4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20=C5=A0toura=C4=8D?= <525032@mail.muni.cz>
Date: Tue, 21 May 2024 12:42:30 +0200
Subject: [PATCH] add functional import of rofiworld

---
 src/edit/include/edit/editor.hpp              |   5 +
 src/edit/src/editor.cpp                       |  34 +++++
 .../include/filein/rofiworld_loader.hpp       |   2 +
 src/filein/src/rofiworld_loader.cpp           | 121 ++++++++++++------
 src/gui/include/gui/ui.hpp                    |   2 +
 src/gui/src/ui.cpp                            |  76 +++++++++--
 src/rofi/include/rofi/rofiworld.hpp           |   3 +
 7 files changed, 193 insertions(+), 50 deletions(-)

diff --git a/src/edit/include/edit/editor.hpp b/src/edit/include/edit/editor.hpp
index 539793f..7d84ebc 100644
--- a/src/edit/include/edit/editor.hpp
+++ b/src/edit/include/edit/editor.hpp
@@ -49,6 +49,9 @@ public:
     bool export_file_prompt = false;
     bool export_file_visible = false;
     bool export_file_error = false;
+    bool import_file_prompt = false;
+    bool import_file_visible = false;
+    bool import_file_error = false;
     bool save_module_prompt = false;
     bool save_module_visible = false;
     bool save_module_error = false;
@@ -229,6 +232,7 @@ class Editor
     std::vector<module_ptr> modules = import_saved_configurations_module();
 
     rofiworld_ptr rofiworld = std::make_shared<rofi::RofiWorld>();
+    std::map<std::string, rofiworld_ptr> rofiworld_selection;
 
     module_ptr selected_add_module;
     module_ptr prev_selected_add_module;
@@ -351,6 +355,7 @@ public:
     void manageUI();
     void manageUISaveModule();
     void manageUISaveRofiWorld();
+    void manageUIImportRofiWorld();
 
     bool checkModuleExport(const voxel_graph_ptr &voxel_graph, const build_mode_I build_mode);
 
diff --git a/src/edit/src/editor.cpp b/src/edit/src/editor.cpp
index 663483d..6f14a98 100644
--- a/src/edit/src/editor.cpp
+++ b/src/edit/src/editor.cpp
@@ -51,6 +51,8 @@ void Editor::manageUI()
 
     manageUISaveModule();
     manageUISaveRofiWorld();
+
+    manageUIImportRofiWorld();
 }
 
 void Editor::manageUISaveModule()
@@ -157,6 +159,38 @@ void Editor::manageUISaveRofiWorld()
     }
 }
 
+void Editor::manageUIImportRofiWorld()
+{
+    if (editor_phase != phase::two)
+        return;
+
+    if (ui.import_file_prompt)
+    {
+        const std::string rofiworlds_directory = "./data/rofi/rofiworlds/";
+        auto imported_rofiworld = import_rofiworld(std::filesystem::path(rofiworlds_directory + std::string(ui.name_buffer) + std::string(".json")));
+        if (imported_rofiworld)
+        {
+            for (const auto &module : imported_rofiworld->getModulesSet())
+                setRoFIMesh(module->getParts());
+
+            setRofiWorldNodeTreeToScene(rofiworld->getNodeTree(), imported_rofiworld->getNodeTree());
+            rofiworld = imported_rofiworld;
+            voxel_graph = imported_rofiworld->getVoxelGraph();
+
+            ui.import_file_error = false;
+            ui.name_buffer[0] = '\0';
+        }
+        else
+        {
+            setError("Error: Cannot Import Configuration");
+            ui.import_file_visible = true;
+            ui.import_file_error = true;
+        }
+
+        ui.import_file_prompt = false;
+    }
+}
+
 bool Editor::checkModuleExport(const voxel_graph_ptr &voxel_graph, const build_mode_I build_mode)
 {
     using enum build_mode_I;
diff --git a/src/filein/include/filein/rofiworld_loader.hpp b/src/filein/include/filein/rofiworld_loader.hpp
index be78aa7..bfbb578 100644
--- a/src/filein/include/filein/rofiworld_loader.hpp
+++ b/src/filein/include/filein/rofiworld_loader.hpp
@@ -52,6 +52,8 @@ void get_rofiworld_data(const boost::json::object &json_object,
                         boost::json::array &moduleJoints_array);
 
 bool import_modules(rofiworld_ptr rofiworld, const boost::json::array &modules_array);
+void rotate_universal_joints(const boost::json::object &module_object, rofiworld_ptr rofiworld, module_ptr um_module);
+module_ptr import_pad(const uint64_t width, const uint64_t height);
 
 bool import_spaceJoints(rofiworld_ptr rofiworld, const boost::json::array &spaceJoints_array);
 bool import_spaceJoint(rofiworld_ptr rofiworld, const boost::json::object &spaceJoint_object);
diff --git a/src/filein/src/rofiworld_loader.cpp b/src/filein/src/rofiworld_loader.cpp
index 0cddeee..060e3c8 100644
--- a/src/filein/src/rofiworld_loader.cpp
+++ b/src/filein/src/rofiworld_loader.cpp
@@ -343,16 +343,16 @@ void get_rofiworld_data(const boost::json::object &json_object,
 
 bool import_modules(rofiworld_ptr rofiworld, const boost::json::array &modules_array)
 {
-    const std::string modules_directory = "./data/rofi/modules";
-    const auto universal = import_module(modules_directory + std::string("UM.json"));
-    const auto cube = import_module(modules_directory + std::string("cube_full.json"));
+    const std::string modules_directory = "./data/rofi/modules/";
+    // const auto universal = import_module(modules_directory + std::string("UM.json"));
+    // const auto cube = import_module(modules_directory + std::string("cube_full.json"));
 
     for (const auto &module_value : modules_array)
     {
         const auto module_object = module_value.as_object();
         const auto module_type = module_object.at("type");
         const auto module_type_string = module_type.if_string();
-        const auto module_id = module_object.at("id").as_uint64();
+        const auto module_id = static_cast<uint64_t>(module_object.at("id").as_int64());
 
         module_ptr module_copy = nullptr;
 
@@ -361,49 +361,89 @@ bool import_modules(rofiworld_ptr rofiworld, const boost::json::array &modules_a
 
         if (*module_type_string == "universal")
         {
-            module_copy = rofi::Module::copy(universal);
+            module_copy = import_module(modules_directory + std::string("UM.json"));
 
-            const auto alpha = module_object.at("alpha").is_int64() ? module_object.at("alpha").as_int64() : module_object.at("alpha").as_double();
-            const auto shoe_a = module_copy->getConnectorByDock("A-Z");
-            rofiworld->rotateConnector(shoe_a, static_cast<float>(alpha));
+            rofi::VoxelGraph::linkImportVoxelGraphToNodes(module_copy->getParts());
+            rofi::Module::setModuleNodeHierarchy(module_copy->getParts(), std::string(*module_type_string));
 
-            const auto beta = module_object.at("beta").is_int64() ? module_object.at("beta").as_int64() : module_object.at("beta").as_double();
-            const auto shoe_b = module_copy->getConnectorByDock("B-Z");
-            rofiworld->rotateConnector(shoe_b, static_cast<float>(beta));
-
-            const auto gamma = module_object.at("gamma").is_int64() ? module_object.at("gamma").as_int64() : module_object.at("gamma").as_double();
-            const auto body_b = module_copy->getParts()->getVoxels().front()->getNode()->getPositionLocal() == glm::vec3(0.0f) 
-                                ? module_copy->getParts()->getVoxels().back()
-                                : module_copy->getParts()->getVoxels().front();
-            rofiworld->rotateVoxel(body_b, static_cast<float>(gamma));
-
-            rofiworld->addModule(rofi::Module::copy(universal));
+            rotate_universal_joints(module_object, rofiworld, module_copy);
         }
         else if (*module_type_string == "cube")
         {
-            module_copy = rofi::Module::copy(cube);
-            rofiworld->addModule(module_copy);
+            module_copy = import_module(modules_directory + std::string("cube_full.json"));
+
+            rofi::VoxelGraph::linkImportVoxelGraphToNodes(module_copy->getParts());
+            rofi::Module::setModuleNodeHierarchy(module_copy->getParts(), std::string(*module_type_string));
         }
         else if (*module_type_string == "pad")
         {
-            const auto width = module_object.at("width").as_int64();
-            const auto height = module_object.at("height").as_int64();
+            const auto width = static_cast<uint64_t>(module_object.at("width").as_int64());
+            const auto height = static_cast<uint64_t>(module_object.at("height").as_int64());
+
+            module_copy = import_pad(width, height);
 
-            // TO DO
-            // module_copy = std::make_shared<rofi::Module>()
-            // rofiworld->addModule()
+            rofi::VoxelGraph::linkImportVoxelGraphToNodes(module_copy->getParts());
+            rofi::Module::setModuleNodeHierarchy(module_copy->getParts(), std::string(*module_type_string));
         }
         else
             return false;
 
+        rofiworld->addModule(module_copy);
         rofiworld->changeModuleID(module_copy, module_id);
-
-        rofi::VoxelGraph::linkImportVoxelGraphToNodes(module_copy->getParts());
-        rofi::Module::setModuleNodeHierarchy(module_copy->getParts(), std::string(*module_type_string));
     }
 
     return true;
+}
+
+void rotate_universal_joints(const boost::json::object &module_object, rofiworld_ptr rofiworld, module_ptr um_module)
+{
+    const auto alpha = module_object.at("alpha").is_int64() ? module_object.at("alpha").as_int64() : module_object.at("alpha").as_double();
+    const auto shoe_a = um_module->getConnectorByDock("A-Z");
+    rofiworld->rotateConnector(shoe_a, static_cast<float>(alpha));
+
+    const auto beta = module_object.at("beta").is_int64() ? module_object.at("beta").as_int64() : module_object.at("beta").as_double();
+    const auto shoe_b = um_module->getConnectorByDock("B-Z");
+    rofiworld->rotateConnector(shoe_b, static_cast<float>(beta));
+
+    const auto gamma = module_object.at("gamma").is_int64() ? module_object.at("gamma").as_int64() : module_object.at("gamma").as_double();
+    const auto body_b = um_module->getParts()->getVoxels().front()->getNode()->getPositionLocal() == glm::vec3(0.0f) 
+                        ? um_module->getParts()->getVoxels().back()
+                        : um_module->getParts()->getVoxels().front();
+    rofiworld->rotateVoxel(body_b, static_cast<float>(gamma));
+}
+
+module_ptr import_pad(const uint64_t width, const uint64_t height)
+{
+    using namespace rofi;
+
+    voxel_graph_ptr vg = std::make_shared<VoxelGraph>();
+    std::map<objectbase_ptr, uint64_t> component_ids;
+    uint64_t id_counter;
+
+    for ( int x = 0; x < width; ++x ) 
+    {
+        for ( int y = 0; y < height; ++y ) 
+        {
+            float x_pos = static_cast<float>(x);
+            float y_pos = static_cast<float>(y);
+
+            auto open_con_node = Node::create(glm::vec3(x_pos, y_pos, 0.0f), get_connector_face_rotation(glm::vec3(0.0f, 0.0f, -1.0f)));
+            open_con_node->rotateRotationQuat(glm::rotation(open_con_node->getRotationAxisXLocal(), glm::vec3(0.0f, 1.0f, 0.0f)));
+            auto open_connector = vg->addConnector(open_con_node, connector_type::open);
+            component_ids.emplace(open_connector, id_counter);
+
+            auto pad_board_node = Node::create(glm::vec3(x_pos, y_pos, 0.5f), glm::angleAxis(glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f)));
+            auto pad_board = vg->addPadBoard(pad_board_node);
+
+            vg->join(open_connector, pad_board, enum_to_vec(side::y_pos));
+
+            ++id_counter;
+        }
+    }
 
+    std::string name = std::string("pad") + std::to_string(width) + std::to_string(height);
+    std::string type = "pad";
+    return std::make_shared<Module>(vg, name, type, component_ids);
 }
 
 bool import_spaceJoints(rofiworld_ptr rofiworld, const boost::json::array &spaceJoints_array)
@@ -434,10 +474,10 @@ bool import_spaceJoint(rofiworld_ptr rofiworld, const boost::json::object &space
 
 module_ptr import_spaceJoint_to(rofiworld_ptr rofiworld, const boost::json::object &to_object)
 {
-    if (to_object.at("component").as_uint64() != 0)
+    if (to_object.at("component").as_int64() != 0)
         return nullptr;
 
-    const auto id = to_object.at("id").as_uint64();
+    const auto id = static_cast<uint64_t>(to_object.at("id").as_int64());
     ASSUMPTION(rofiworld->containsID(id));
     return rofiworld->getModuleByID(id);
 }
@@ -454,7 +494,7 @@ void import_spaceJoint_point(const module_ptr module, const boost::json::array &
             position[i] = static_cast<float>(value.as_double());
     }
 
-    module->getNode()->setPosVec(position);
+    module->getNode()->setPosVec(convert_to_editor_coordinates(position));
 }
 
 void import_spaceJoint_joint(rofiworld_ptr rofiworld, const module_ptr module, const boost::json::object &joint)
@@ -464,6 +504,12 @@ void import_spaceJoint_joint(rofiworld_ptr rofiworld, const module_ptr module, c
     const auto editor_rotation_mat = convert_to_editor_import_coordinates(rofi_rotation_mat);
 
     // TO DO rotate somehow
+
+    // const auto root_connector = std::dynamic_pointer_cast<rofi::Connector>(module->getComponentByID(0));
+    // ASSUMPTION(root_connector);
+    // rofiworld->rebuildNodeTree(module, root_connector->getNode());
+    // root_connector->getNode()->setRotationQuat(editor_rotation_mat);
+
 }
 
 void import_moduleJoints(rofiworld_ptr rofiworld, const boost::json::array &moduleJoints_array)
@@ -487,18 +533,11 @@ void import_moduleJoint(rofiworld_ptr rofiworld, const boost::json::object &modu
     rofiworld->rebuildNodeTree(from_module, from_connector->getNode());
     rofiworld->getVoxelGraph()->joinConnectors(from_connector, to_connector);
     rofiworld->teleportModulesByConnectors(from_connector, to_connector, mutual_orientation);
-
-    // rebuildRofiWorldNodeTree(rofiworld->getModuleWithNode(source_node), source_node, rofiworld);
-
-    // voxel_graph->joinConnectors(selected_connectors.first, selected_connectors.second);
-
-    // rofiworld->teleportModulesByConnectors(selected_connectors.first, selected_connectors.second, selected_cardinality);
-    
 }
 
 std::pair<module_ptr, connector_ptr> get_moduleJoint_module_connector(rofiworld_ptr rofiworld, const boost::json::object &where_object)
 {
-    const auto id = where_object.at("id").as_uint64();
+    const auto id = static_cast<uint64_t>(where_object.at("id").as_int64());
     const auto module = rofiworld->getModuleByID(id);
 
     const auto dock = where_object.at("connector");
@@ -506,7 +545,7 @@ std::pair<module_ptr, connector_ptr> get_moduleJoint_module_connector(rofiworld_
     if (dock.is_string())
         return {module, module->getConnectorByDock(std::string(dock.as_string()))};
     else // number
-        return {module, std::dynamic_pointer_cast<rofi::Connector>(module->getComponentByID(dock.as_uint64()))};
+        return {module, std::dynamic_pointer_cast<rofi::Connector>(module->getComponentByID(dock.as_int64()))};
 }
 
 rofi::cardinal get_moduleJoint_orientation(const boost::json::string &orientation_string)
diff --git a/src/gui/include/gui/ui.hpp b/src/gui/include/gui/ui.hpp
index 193d172..9f83c4e 100644
--- a/src/gui/include/gui/ui.hpp
+++ b/src/gui/include/gui/ui.hpp
@@ -57,6 +57,8 @@ void save_module_ui(const osi::Window &window, edit::UIData &data);
 void export_file_ui(const osi::Window &window, edit::UIData &data);
 void export_module_file_ui(const osi::Window &window, edit::UIData &data);
 void export_rofiworld_file_ui(const osi::Window &window, edit::UIData &data);
+void import_file_ui(const osi::Window &window, edit::UIData &data);
+void import_rofiworld_file_ui(const osi::Window &window, edit::UIData &data);
 void rotation_settings(const osi::Window &window, edit::UIData &data);
 void experimental_warning(const osi::Window &window, edit::UIData &data);
 
diff --git a/src/gui/src/ui.cpp b/src/gui/src/ui.cpp
index 6b61ec4..0ae207b 100644
--- a/src/gui/src/ui.cpp
+++ b/src/gui/src/ui.cpp
@@ -180,14 +180,21 @@ void rofiworld_menu_ui(const osi::Window &window, edit::UIData &data)
 
 void file_menu_ui(const osi::Window &window, edit::UIData &data)
 {
-    if (ImGui::MenuItem("Export Configuration", NULL, false, data.editor_mode_I == edit::mode_I::build)) 
+    using namespace edit;
+    bool available = (data.editor_mode_I == mode_I::build && data.editor_phase == phase::one)
+                     || (data.editor_mode_II == mode_II::build && data.editor_phase == phase::two);
+    if (ImGui::MenuItem("Export Configuration", NULL, false, available)) 
     {
         data.export_file_visible = true;
     }
-    // if (ImGui::MenuItem("Import Configuration", NULL, false, data.editor_mode_I == edit::mode_I::build)) 
-    // {
-    //     // TO DO
-    // }
+
+    if (data.editor_phase == phase::one)
+        return;
+    
+    if (ImGui::MenuItem("Import Configuration", NULL, false, available)) 
+    {
+        data.import_file_visible = true;
+    }
 }
 
 void help_menu_ui(const osi::Window &window, edit::UIData &data)
@@ -238,6 +245,7 @@ void mode_I_build_ui(const osi::Window &window, edit::UIData &data)
     build_toolbar_ui(window, data);
     save_module_ui(window, data);
     export_file_ui(window, data);
+    import_file_ui(window, data);
 
     create_pad_ui(window, data);
     rotation_settings(window, data);
@@ -255,6 +263,7 @@ void mode_II_build_ui(const osi::Window &window, edit::UIData &data)
     module_rotation_ui(window, data);
     component_rotation_ui(window, data);
     export_file_ui(window, data);
+    import_file_ui(window, data);
 }
 
 int build_mode_I_to_int(edit::build_mode_I mode)
@@ -927,7 +936,7 @@ void save_module_ui(const osi::Window &window, edit::UIData &data)
         ImGui::NewLine();
     }
     
-    if (ImGui::Button("Submit")) {
+    if (ImGui::Button("Submit##SaveModuleButton")) {
         data.save_module_prompt = true;
         data.export_file_prompt = true;
         data.save_module_visible = false;
@@ -979,7 +988,7 @@ void export_module_file_ui(const osi::Window &window, edit::UIData &data)
         ImGui::NewLine();
     }
 
-    if (ImGui::Button("Submit")) {
+    if (ImGui::Button("Submit##ExportModuleButton")) {
         data.export_file_prompt = true;
         data.export_file_visible = false;
     }
@@ -999,14 +1008,21 @@ void export_rofiworld_file_ui(const osi::Window &window, edit::UIData &data)
     ImGui::Text("Enter Text:");
     
     // InputText takes a pointer to a buffer, a maximum buffer size, and optional flags
-    ImGui::InputText("File Name##RofiWorldFileNameInput", data.name_buffer, sizeof(data.name_buffer), 
+    ImGui::InputText("File Name##RofiWorldExportFileNameInput", data.name_buffer, sizeof(data.name_buffer), 
                      ImGuiInputTextFlags_AutoSelectAll
                      | ImGuiInputTextFlags_AlwaysOverwrite);
 
+    if (data.export_file_error && data.current_second - data.error_start_second < 2)
+        ImGui::Text(data.error_msg.c_str());
+    else
+    {
+        data.export_file_error = false;
+        ImGui::NewLine();
+    }
 
     ImGui::Checkbox("Selected Module as spaceJoint##spaceJointCheckBox", &data.export_rofiworld_use_selected_space_joint);
 
-    if (ImGui::Button("Submit")) {
+    if (ImGui::Button("Submit##ExportRofiWorldButton")) {
         data.export_file_prompt = true;
         data.export_file_visible = false;
     }
@@ -1014,6 +1030,48 @@ void export_rofiworld_file_ui(const osi::Window &window, edit::UIData &data)
     ImGui::End();
 }
 
+void import_file_ui(const osi::Window &window, edit::UIData &data)
+{
+    if (!data.import_file_visible)
+        return;
+
+    ImGui::SetNextWindowPos(ImVec2(0, mode_toolbar_height * 3), ImGuiCond_Appearing, ImVec2(0.0f, 0.0f));
+
+    import_rofiworld_file_ui(window, data);
+}
+
+void import_rofiworld_file_ui(const osi::Window &window, edit::UIData &data)
+{
+    if (!ImGui::Begin("Import RofiWorld Configuration", &data.import_file_visible, 
+                      ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize))
+    {
+        ImGui::End();
+        return;
+    }
+
+    ImGui::Text("Enter Text:");
+    
+    // InputText takes a pointer to a buffer, a maximum buffer size, and optional flags
+    ImGui::InputText("File Name##RofiWorldImportFileNameInput", data.name_buffer, sizeof(data.name_buffer), 
+                     ImGuiInputTextFlags_AutoSelectAll
+                     | ImGuiInputTextFlags_AlwaysOverwrite);
+
+    if (data.import_file_error && data.current_second - data.error_start_second < 2)
+        ImGui::Text(data.error_msg.c_str());
+    else
+    {
+        data.import_file_error = false;
+        ImGui::NewLine();
+    }
+
+    if (ImGui::Button("Submit##ImportRofiWorldButton")) {
+        data.import_file_prompt = true;
+        data.import_file_visible = false;
+    }
+
+    ImGui::End();
+}
+
 void rotation_settings(const osi::Window &window, edit::UIData &data)
 {
     if (!data.connector_selected 
diff --git a/src/rofi/include/rofi/rofiworld.hpp b/src/rofi/include/rofi/rofiworld.hpp
index 0c8fb2d..80ab2db 100644
--- a/src/rofi/include/rofi/rofiworld.hpp
+++ b/src/rofi/include/rofi/rofiworld.hpp
@@ -32,10 +32,13 @@ class RofiWorld
 
 public:
 
+    bool empty() const { return voxel_graph->empty() && node_tree.empty(); }
+
     const std::vector<node_ptr> &getNodeTree() const { return node_tree; }
     voxel_graph_ptr getVoxelGraph() const { return voxel_graph; }
 
     const std::map<module_ptr, uint64_t> &getModulesMap() const { return modules_map; }
+    const std::set<module_ptr> &getModulesSet() const { return modules_set; }
     const uint64_t getModuleID(const module_ptr module) const { return modules_map.at(module); }
     const module_ptr getModuleByID(const uint64_t id) const;
     bool containsID(uint64_t id) const { return ids_set.contains(id); }
-- 
GitLab