Skip to content

File LayerUI.cpp

File List > AIAC > LayerUI.cpp

Go to the documentation of this file

// #####################################################################
// >>>>>>>>>>>>>>>>>>>>> BEGINNING OF LEGAL NOTICE >>>>>>>>>>>>>>>>>>>>>
//######################################################################
//
// This source file, along with its associated content, was authored by
// Andrea Settimi, Hong-Bin Yang, Naravich Chutisilp, and numerous other
// contributors. The code was originally developed at the Laboratory for
// Timber Construction (IBOIS, director: Prof. Yves Weinand) at the School of 
// Architecture, Civil and Environmental Engineering (ENAC) at the Swiss
// Federal Institute of Technology in Lausanne (EPFL) for the Doctoral
// Research "Augmented Carpentry" (PhD researcher: Andrea Settimi,
// co-director: Dr. Julien Gamerro, director: Prof. Yves Weinand).
//
// Although the entire repository is distributed under the GPL license,
// these particular source files may also be used under the terms of the
// MIT license. By accessing or using this file, you agree to the following:
//
// 1. You may reproduce, modify, and distribute this file in accordance
//    with the terms of the MIT license.
// 2. You must retain this legal notice in all copies or substantial
//    portions of this file.
// 3. This file is provided "AS IS," without any express or implied
//    warranties, including but not limited to the implied warranties of
//    merchantability and fitness for a particular purpose.
//
// If you cannot or will not comply with the above conditions, you are
// not permitted to use this file. By proceeding, you acknowledge and
// accept all terms and conditions herein.
//
//######################################################################
// <<<<<<<<<<<<<<<<<<<<<<< END OF LEGAL NOTICE <<<<<<<<<<<<<<<<<<<<<<<<
// #####################################################################
//
#include "aiacpch.h"

#include "AIAC/LayerUI.h"
#include "AIAC/Application.h"

#include "AIAC/Image.h"
#include "AIAC/Render/Renderer.h"

#include "AIAC/UI/ImGuiFileDialog.h"

#include "AIAC/UI/ClrPalette.h"
#include "AIAC/UI/CustomLogos.h"
#include "LayerCameraCalib.h"
#include "LayerLogRecorder.h"

#include "ttool.hh"

#include "utils/utils.h"
#include "LayerUtils.h"

#include <filesystem>
#include <optional>
#include <string>

std::optional<std::string> GetLatestFilePath(const std::string& directoryPath, const std::string& extension);
std::optional<std::string> GetLatestFilePath(const std::string& directoryPath, const std::string& extension) {
    std::optional<std::filesystem::path> latestFilePath;
    std::filesystem::file_time_type latestModificationTime;

    for (const auto& entry : std::filesystem::directory_iterator(directoryPath)) {
        if (entry.is_regular_file() && entry.path().extension() == extension) {
            auto currentFileModificationTime = std::filesystem::last_write_time(entry);
            if (!latestFilePath || currentFileModificationTime > latestModificationTime) {
                latestFilePath = entry.path();
                latestModificationTime = currentFileModificationTime;
            }
        }
    }

    if (latestFilePath) {
        return latestFilePath->string();
    } else {
        return std::nullopt;
    }
}


namespace AIAC
{
    void LayerUI::OnAttach()
    {
        IMGUI_CHECKVERSION();
        ImGui::CreateContext();
        ImGuiIO& io = ImGui::GetIO(); (void)io;

        //---------------------------------------------------------------------------------
        // styling of the main window
        ImGui::StyleColorsDark();
        ImGuiStyle& style = ImGui::GetStyle();

        style.Colors[ImGuiCol_MenuBarBg]             = ImVec4(0.00f, 0.00f, 0.00f, 0.20f);
        style.Colors[ImGuiCol_WindowBg]              = ImVec4(0.00f, 0.00f, 0.00f, 0.50f);
        style.Colors[ImGuiCol_Border]                = AIAC_UI_DARK_GREY;
        style.Colors[ImGuiCol_BorderShadow]          = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);

        style.Colors[ImGuiCol_FrameBg]               = AIAC_UI_LIGHT_GREY;
        style.Colors[ImGuiCol_FrameBgHovered]        = AIAC_UI_DARK_GREY;
        style.Colors[ImGuiCol_FrameBgActive]         = AIAC_UI_SPARK_ORANGE;

        style.Colors[ImGuiCol_CheckMark]             = AIAC_UI_SPARK_ORANGE;
        style.Colors[ImGuiCol_SliderGrab]            = AIAC_UI_SPARK_ORANGE;
        style.Colors[ImGuiCol_SliderGrabActive]      = AIAC_UI_SPARK_ORANGE;

        style.Colors[ImGuiCol_ResizeGrip]            = AIAC_UI_DARK_GREY;
        style.Colors[ImGuiCol_ResizeGripHovered]     = AIAC_UI_LIGHT_GREY;
        style.Colors[ImGuiCol_ResizeGripActive]      = AIAC_UI_SPARK_ORANGE;

        style.Colors[ImGuiCol_PlotHistogramHovered]  = AIAC_UI_DARK_GREY;
        style.Colors[ImGuiCol_TextSelectedBg]        = AIAC_UI_SPARK_ORANGE;

        style.Colors[ImGuiCol_TitleBg]               = AIAC_UI_DARK_GREY;
        style.Colors[ImGuiCol_TitleBgCollapsed]      = AIAC_UI_DARK_GREY;
        style.Colors[ImGuiCol_TitleBgActive]         = AIAC_UI_SPARK_ORANGE;

        style.Colors[ImGuiCol_Header]                = AIAC_UI_DARK_GREY;
        style.Colors[ImGuiCol_HeaderHovered]         = AIAC_UI_SPARK_ORANGE;
        style.Colors[ImGuiCol_HeaderActive]          = AIAC_UI_SPARK_ORANGE;

        style.Colors[ImGuiCol_Button]                = AIAC_UI_DARK_GREY;
        style.Colors[ImGuiCol_ButtonHovered]         = AIAC_UI_SPARK_ORANGE;
        style.Colors[ImGuiCol_ButtonActive]          = AIAC_UI_SPARK_ORANGE;
        style.Colors[ImGuiCol_FrameBg]               = AIAC_UI_DARK_GREY;

        style.Colors[ImGuiCol_ScrollbarBg]           = AIAC_UI_LIGHT_GREY;
        style.Colors[ImGuiCol_ScrollbarGrab]         = AIAC_UI_DARK_GREY;
        style.Colors[ImGuiCol_ScrollbarGrabHovered]  = AIAC_UI_SPARK_ORANGE;
        style.Colors[ImGuiCol_ScrollbarGrabActive]   = AIAC_UI_SPARK_ORANGE;

        style.ScrollbarSize = 30.0f;
        style.WindowRounding = 4.0f;
        style.ChildRounding = 4.0f;
        style.FrameRounding = 4.0f;
        style.PopupRounding = 4.0f;
        style.ScrollbarRounding = 4.0f;
        style.GrabRounding = 4.0f;
        style.TabRounding = 4.0f;
        style.FramePadding = ImVec2(6, 6);

        //---------------------------------------------------------------------------------

        ImGui_ImplGlfw_InitForOpenGL(AIAC_APP.GetWindow()->GetGLFWWindow(), true);
        ImGui_ImplOpenGL3_Init(AIAC_APP.GetWindow()->GetGlslVersion());

        io.Fonts->AddFontFromFileTTF("assets/fonts/UbuntuMono-R.ttf", 14.0f);  // default

        // Load images from memory
        m_LogoLightClr = AIAC::Image(AIAC_LOGO_COLOR);

        // Set panes UI for layers
        //                Label                    Collapse  PaneContent
        StackPane(PaneUI("Camera",                 false,    AIAC_BIND_EVENT_FN(SetPaneUICamera)    ));
        StackPane(PaneUI("Mapping",                false,    AIAC_BIND_EVENT_FN(SetPaneUISlam)      ));
        StackPane(PaneUI("ACIM (Execution model)", false,    AIAC_BIND_EVENT_FN(SetPaneUIACIM)      ));
        StackPane(PaneUI("Toolhead",               false,    AIAC_BIND_EVENT_FN(SetPaneUIToolhead), AIAC_BIND_EVENT_FN(OnCollapsingPaneUIToolhead)));
        StackPane(PaneUI("Utils",                  false,    AIAC_BIND_EVENT_FN(SetPaneUIUtils)     ));

        m_IsOpen = new bool(true);
    }

    void LayerUI::OnFrameStart()
    {
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();
    }

    void LayerUI::OnUIRender()
    {
        IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing dear imgui context. Refer to examples app!");

        if (AIAC_APP.GetLayer<LayerSlam>()->IsMapping()) {
            ShowMappingPopup();
        } else if (AIAC_APP.GetLayer<LayerCameraCalib>()->IsCalibrating()){
            ShowCamCalibPopup();
            if (m_IsChoosingCamCalibFileSavePath){
                ShowSaveCamCalibFileDialog();
            }
        } else {
            ShowMenuBar();
            ShowMainUI();
            ShowSceneViewport();
            ShowLogRecorderUI();

            if(m_IsCombiningMap){
                ShowCombineMapPopup();
            }

            if(m_IsReconstructing3D){
                ShowReconstruct3DPopup();
            }
        }
        ShowFileSelectDialog();

        ImGui::Render();
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
    }

    void LayerUI::OnDetach()
    {
        ImGui_ImplOpenGL3_Shutdown();
        ImGui_ImplGlfw_Shutdown();
        ImGui::DestroyContext();
    }

    template<typename... Args>
    void PaneUI::Show(Args &&... args) {
        bool isOpened = ImGui::CollapsingHeader(m_Label, m_IsCollapsed ? ImGuiTreeNodeFlags_DefaultOpen : 0);
        if (isOpened) {
            // if this one is not opened yet, close the previously opened one
            if (m_CollapseState != CollapseState::OPEN) {
                auto lastOpenedPaneUI = AIAC_APP.GetLayer<LayerUI>()->GetOpenedPaneUI();
                if (lastOpenedPaneUI != nullptr && lastOpenedPaneUI->m_Label != m_Label) {
                    ImGui::GetStateStorage()->SetInt(ImGui::GetID(lastOpenedPaneUI->m_Label), 0);
                    lastOpenedPaneUI->Show();
                }
                AIAC_APP.GetLayer<LayerUI>()->SetOpenedPaneUI(this);
            }
            m_CollapseState = CollapseState::OPEN;
            m_func(std::forward<Args>(args)...);
        }

        // check if it's just collapsed
        if (!isOpened) {
            if (m_CollapseState == CollapseState::OPEN) {
                m_CollapseState = CollapseState::ON_COLLAPSING;
            }
        }
    }

    template<typename... Args>
    void PaneUI::CheckOnCollapsing(Args &&... args) {
        if (m_CollapseState == CollapseState::ON_COLLAPSING) {
            m_onCollapseCallback(std::forward<Args>(args)...);
            m_CollapseState = CollapseState::COLLAPSE;
        }
    }

    void LayerUI::ShowMenuBar()
    {
        if (ImGui::BeginMainMenuBar())
        {
            ImGui::Image(m_LogoLightClr.GetImTexture().ID, ImVec2(18, 18), ImVec2(0, 1), ImVec2(1, 0));
            ImGui::SameLine();

            if (ImGui::BeginMenu("App"))
            {
                if (ImGui::MenuItem("Close"))
                {
                    *m_IsOpen = false;
                    AIAC_EBUS->EnqueueEvent(std::make_shared<AppCloseEvent>());
                }
                ImGui::EndMenu();
            }
            ImGui::EndMainMenuBar();
        }
    }

    void LayerUI::ShowMainUI()
    {
        ImGui::Begin("augmented_carpentry", m_IsOpen);
#ifdef ENABLE_DEV_UI
        ImGui::Image(m_LogoLightClr.GetImTexture().ID, ImVec2(60, 60), ImVec2(0, 1), ImVec2(1, 0));
        ImGui::SameLine();
        ImGui::Text("This is a prototype for augmented_carpentry \n Version 01.00.00 \n Build 2021-01-01 00:00:00 \n IBOIS, EPFL");
#endif

        for (auto& pane : m_PaneUIStack) {
            pane.Show();
            pane.CheckOnCollapsing();
        }

        ImGui::End();
    }

    void LayerUI::ShowSceneViewport()
    {
        ImGui::Begin("Scene Viewport", m_IsOpen);

        ImVec2 viewportSize = ImGui::GetContentRegionAvail();
        viewportSize.y -= (ImGui::GetTextLineHeight() + 16);
        AIAC_APP.GetRenderer()->SetGlobalViewSize(viewportSize.x, viewportSize.y);

        SetGlobalViewUI(viewportSize);

        ImGui::End();
    }

    void LayerUI::SetGlobalViewUI(ImVec2 viewportSize)
    {
        if(m_IsMouseLDown && ImGui::IsMouseDragging(0, 0.0f)) {
            ImVec2 mousePos = ImGui::GetMousePos();
            switch(m_AdjustTarget){
                case AdjustTarget::SCALE:
                    AIAC_APP.GetRenderer()->UpdateGlobalViewCameraScale(mousePos.x - m_LastMouseLPos.x - mousePos.y + m_LastMouseLPos.y);
                    break;
                case AdjustTarget::ROTATION:
                    AIAC_APP.GetRenderer()->UpdateGlobalViewCameraRotation(mousePos.x - m_LastMouseLPos.x, mousePos.y - m_LastMouseLPos.y);
                    break;
                case AdjustTarget::TRANSLATION:
                    AIAC_APP.GetRenderer()->UpdateGlobalViewCameraTranslation(mousePos.x - m_LastMouseLPos.x, mousePos.y - m_LastMouseLPos.y);
                    break;
            }
            m_LastMouseLPos = mousePos;
        }
        if(m_IsMouseRDown && ImGui::IsMouseDragging(1, 0.0f)) {
            ImVec2 mousePos = ImGui::GetMousePos();
            AIAC_APP.GetRenderer()->UpdateGlobalViewCameraTranslation(mousePos.x - m_LastMouseRPos.x, mousePos.y - m_LastMouseRPos.y);
            m_LastMouseRPos = mousePos;
        }

        CvtGlTextureObj2ImTexture(AIAC_APP.GetRenderer()->GetGlobalView(), m_SceneViewportImTexture, viewportSize);

        ImGui::ImageButton(m_SceneViewportImTexture.ID, viewportSize, ImVec2(0, 1), ImVec2(1, 0), 0, ImColor(255, 255, 255, 128));
        if(ImGui::IsItemHovered()) {
            if(ImGui::IsMouseDown(0) && !m_IsMouseLDown) {
                m_IsMouseLDown = true;
                m_AdjustTarget = AdjustTarget::ROTATION;
                m_LastMouseLPos = ImGui::GetMousePos();
            }
            if(ImGui::IsMouseDown(1) && !m_IsMouseRDown) {
                m_IsMouseRDown = true;
                m_AdjustTarget = AdjustTarget::TRANSLATION;
                m_LastMouseRPos = ImGui::GetMousePos();
            }
        }
        if(ImGui::IsMouseReleased(0)) { m_IsMouseLDown = false; }
        if(ImGui::IsMouseReleased(1)) { m_IsMouseRDown = false; }

        ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_LIGHT_GREY);
        ImGui::Button("Scale");
        if(ImGui::IsItemHovered() && ImGui::IsMouseDown(0) && !m_IsMouseLDown) {
            m_AdjustTarget = AdjustTarget::SCALE;
            m_IsMouseLDown = true;
            m_LastMouseLPos = ImGui::GetMousePos();
        }
        ImGui::SameLine();
        ImGui::Button("Move");
        if(ImGui::IsItemHovered() && ImGui::IsMouseDown(0) && !m_IsMouseLDown) {
            m_AdjustTarget = AdjustTarget::TRANSLATION;
            m_IsMouseLDown = true;
            m_LastMouseLPos = ImGui::GetMousePos();
        }
        ImGui::SameLine();
        if(ImGui::Button("NW")){
            AIAC_APP.GetRenderer()->SetGlobalViewToActivatedComponent(Renderer::StandardView::NW);
        }
        ImGui::SameLine();
        if(ImGui::Button("NE")){
            AIAC_APP.GetRenderer()->SetGlobalViewToActivatedComponent(Renderer::StandardView::NE);
        }
        ImGui::SameLine();
        if(ImGui::Button("SW")){
            AIAC_APP.GetRenderer()->SetGlobalViewToActivatedComponent(Renderer::StandardView::SW);
        }
        ImGui::SameLine();
        if(ImGui::Button("SE")){
            AIAC_APP.GetRenderer()->SetGlobalViewToActivatedComponent(Renderer::StandardView::SE);
        }
        ImGui::PopStyleColor();
    }


    void LayerUI::SetPaneUICamera()
    {
        string currentItem = AIAC_APP.GetLayer<LayerCamera>()->GetCurrentDevice();
        static const char* previewValue = currentItem.c_str();
        if(ImGui::BeginCombo("##AvailableDevices", previewValue)){
            for (auto& devicePath : AIAC_APP.GetLayer<LayerCamera>()->AvailableDevices) {
                bool isSelected = (currentItem == devicePath);
                if (ImGui::Selectable(devicePath.c_str(), isSelected)){
                    AIAC_APP.GetLayer<LayerCamera>()->SetCurrentDevice(devicePath);
                }
                if (isSelected)
                    ImGui::SetItemDefaultFocus();
            }
            ImGui::EndCombo();
        }
        ImGui::SameLine();
        if(ImGui::Button("Refresh")){
            AIAC_APP.GetLayer<LayerCamera>()->UpdateAvailableDevices();
        }

        ImGui::Text("Flip ");
        ImGui::SameLine();
        if(ImGui::Checkbox("Horizontal", &AIAC_APP.GetLayer<LayerCamera>()->MainCamera.FlipHorizontal)){
            AIAC::Config::UpdateEntry(AIAC::Config::SEC_AIAC, AIAC::Config::CAM_FLIP_HORIZONTAL, AIAC_APP.GetLayer<LayerCamera>()->MainCamera.FlipHorizontal);
        }
        ImGui::SameLine();
        if(ImGui::Checkbox("Vertical", &AIAC_APP.GetLayer<LayerCamera>()->MainCamera.FlipVertical)){
            AIAC::Config::UpdateEntry(AIAC::Config::SEC_AIAC, AIAC::Config::CAM_FLIP_VERTICAL, AIAC_APP.GetLayer<LayerCamera>()->MainCamera.FlipVertical);
        };

        AIAC::Camera& camera = AIAC_APP.GetLayer<AIAC::LayerCamera>()->MainCamera;
        ImGui::Text("Resolution: (%d x %d) > (%d x %d)", camera.GetRawWidth(), camera.GetRawHeight(), camera.GetWidth(), camera.GetHeight());

        ImGui::BeginChild("camera_function_child", ImVec2(0, 50), true, ImGuiWindowFlags_HorizontalScrollbar);
            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_LIGHT_GREY);
            if(ImGui::Button("Start Calibration")){
                AIAC_APP.GetLayer<LayerCameraCalib>()->StartCalibration();
            }
            ImGui::SameLine();
            if (ImGui::Button("Open Calib File")) {
                ImGuiFileDialog::Instance()->OpenDialog("ChooseCameraCalib", "Open calib", ".yml", ".");
            }
            if (ImGuiFileDialog::Instance()->Display("ChooseCameraCalib"))
            {
                if (ImGuiFileDialog::Instance()->IsOk())
                {
                    std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
                    AIAC_EBUS->EnqueueEvent(std::make_shared<CameraCalibrationLoadedEvent>(filePathName));
                }
                ImGuiFileDialog::Instance()->Close();
            }

            ImGui::PopStyleColor();
        ImGui::EndChild();
    }

    void LayerUI::SetPaneUISlam()
    {
        ImVec2 sizeButtons = ImVec2(110, 50);
        ImGui::Text("Import files:");
        ImGui::BeginChild("slam_info_child", ImVec2(0, 95), true, ImGuiWindowFlags_HorizontalScrollbar);
            std::string currentSLAMMap = AIAC::Config::Get<std::string>(AIAC::Config::SEC_TSLAM, AIAC::Config::MAP_FILE, ".");
            size_t lastSlashPos = currentSLAMMap.find_last_of("/\\");
            if (lastSlashPos != std::string::npos) {
                currentSLAMMap = currentSLAMMap.substr(lastSlashPos + 1);
            }
            ImGui::Text("Current SLAM Map: %s", currentSLAMMap.c_str());

            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_BRONZE_ORANGE);
            if (ImGui::Button("Open SLAM map", sizeButtons))
            {
                std::string mapFolderDefault = AIAC::Config::Get<std::string>(AIAC::Config::SEC_TSLAM, AIAC::Config::SAVE_DIR_MAPS, ".");
                ImGuiFileDialog::Instance()->OpenDialog("ChooseSLAMmap", "Open SLAM map", ".map", mapFolderDefault.c_str());
            }
            if (ImGuiFileDialog::Instance()->Display("ChooseSLAMmap"))
            {
                if (ImGuiFileDialog::Instance()->IsOk())
                {
                    std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
                    AIAC_EBUS->EnqueueEvent(std::make_shared<SLAMMapLoadedEvent>(filePathName));
                }
                ImGuiFileDialog::Instance()->Close();
            }
            ImGui::SameLine();
            ImGui::PopStyleColor();

            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_GRAPE_PURPLE);
            if (ImGui::Button("Open Vocab", sizeButtons))
                ImGuiFileDialog::Instance()->OpenDialog("ChooseVocab", "Open Vocab", ".fbow", ".");

            if (ImGuiFileDialog::Instance()->Display("ChooseVocab"))
            {
                if (ImGuiFileDialog::Instance()->IsOk())
                {
                std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
                AIAC_EBUS->EnqueueEvent(std::make_shared<SLAMVocabularyLoadedEvent>(filePathName));
                }
                ImGuiFileDialog::Instance()->Close();
            }
            ImGui::PopStyleColor();

            ImGui::SameLine();
            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_PLUM_PURPLE);
            if (ImGui::Button("Open latest Map", sizeButtons))
            {
                std::string mapFolderDefault = AIAC::Config::Get<std::string>(AIAC::Config::SEC_TSLAM, AIAC::Config::SAVE_DIR_MAPS, ".");
                auto latestMapFilePathOpt = GetLatestFilePath(mapFolderDefault, ".map");

                if (latestMapFilePathOpt.has_value())
                {
                    std::string latestMapFilePath = latestMapFilePathOpt.value();
                    AIAC_EBUS->EnqueueEvent(std::make_shared<SLAMMapLoadedEvent>(latestMapFilePath));
                } else {
                    AIAC_INFO("No latest map file found in the directory: {0}", mapFolderDefault);
                }
            }
            ImGui::PopStyleColor();

        ImGui::EndChild();

        ImGui::Text("Mapping Functions:");
        ImGui::BeginChild("mapping_function_child", ImVec2(0, 80), true, ImGuiWindowFlags_HorizontalScrollbar);
            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_BRONZE_ORANGE);
            if(ImGui::Button("Start Mapping", sizeButtons)){
                std::string defaultPath = "";
                defaultPath += "./scanned_map/map-";
                defaultPath += GetCurrentDateTime();
                defaultPath += ".map";
                strncpy(m_MappingParams.MapSavingPath, defaultPath.c_str(), defaultPath.size());
                AIAC_EBUS->EnqueueEvent(std::make_shared<SLAMStartMappingEvent>());
            }
            ImGui::SameLine();
            ImGui::PopStyleColor();

            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_PRUSSIAN_BLUE);
            if(ImGui::Button("Combine Map", sizeButtons)){
                m_IsCombiningMap = true;
            }
            ImGui::SameLine();
            if(ImGui::Button("Reconstruct 3D", sizeButtons)){
                m_IsReconstructing3D = true;
            }
            ImGui::PopStyleColor();
        ImGui::EndChild();

#ifdef ENABLE_DEV_UI
        ImGui::Checkbox("Show Tag", &AIAC_APP.GetLayer<AIAC::LayerSlam>()->ToShowTag);
        ImGui::Checkbox("Process Frames", &AIAC_APP.GetLayer<AIAC::LayerSlam>()->ToProcess);
        ImGui::Checkbox("Enhance Photo", &AIAC_APP.GetLayer<AIAC::LayerSlam>()->ToEnhance);

        ImGui::Text("Tracked: %s", AIAC_APP.GetLayer<AIAC::LayerSlam>()->IsTracked() ? "Yes" : "No");
#endif

#ifdef ENABLE_DEV_UI
        std::string camPoseStr; camPoseStr << AIAC_APP.GetLayer<AIAC::LayerSlam>()->GetCamPoseCv();
        ImGui::Text("Estimated Camera Pose: \n%s", camPoseStr.c_str());
#endif
    }

    void LayerUI::SetPaneUIACIM()
    {
        // ACIM file manager (ACIM Loader, Scanned model re/loader)
        ImGui::Text("ACIM File Manager:");
        ImGui::BeginChild("ACIM File Manager", ImVec2(0, 60), true, ImGuiWindowFlags_HorizontalScrollbar);
            std::string currentACIMFileEntry = "Current ACIM:  " + AIAC_APP.GetLayer<LayerModel>()->GetACInfoModelName();
            ImGui::Text("%s", currentACIMFileEntry.c_str());
            if(ImGui::Button("Load ACIM")){
                OpenFileSelectDialog("Open ACIM file", ".acim", m_TmpPathBuf, [&]{
                    AIAC_APP.GetLayer<AIAC::LayerModel>()->LoadACInfoModel(m_TmpPathBuf);
                });
            }
            ImGui::SameLine();
            if(ImGui::Button("Reload ACIM")){
                AIAC_APP.GetLayer<AIAC::LayerModel>()->ReloadACInfoModel();
            }
            ImGui::SameLine();
            if(ImGui::Button("Load Scanned Model")){
                std::string saveDirMap = AIAC::Config::Get<std::string>(AIAC::Config::SEC_TSLAM, AIAC::Config::SAVE_DIR_MAPS, ".");
                const char* saveDirMapCStr = saveDirMap.c_str();
                this->m_TmpPathBuf[0] = '\0';
                strcpy(this->m_TmpPathBuf, saveDirMapCStr);
                OpenFileSelectDialog("Open Scanned Model", ".ply", this->m_TmpPathBuf, [&]{
                    AIAC_APP.GetLayer<AIAC::LayerModel>()->LoadScannedModel(this->m_TmpPathBuf);
                });
            }
        ImGui::EndChild();

        // Re-position the ACIM model
        ImGui::Text("ACIM position:");
        ImGui::BeginChild("adjust_model_alignment", ImVec2(0, 110), true, ImGuiWindowFlags_HorizontalScrollbar);
            float sliderVal = 0.f;
            ImGui::SliderFloat("## Model Offset", &sliderVal, -1.0f, 1.0f, "Model Offset", ImGuiSliderFlags_AlwaysClamp);
                if (sliderVal != 0.f) AIAC_APP.GetLayer<AIAC::LayerModel>()->AddAlignOffset(sliderVal);
                sliderVal = 0.f;
            ImGui::SameLine();
            ImGui::Text("%s", std::to_string(AIAC_APP.GetLayer<AIAC::LayerModel>()->GetAlignOffset()).c_str());

            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_ROYAL_PURPLE);
            if(ImGui::Button("Align Center")){
                AIAC_APP.GetLayer<AIAC::LayerModel>()->ResetAlignOffset();
            }
            ImGui::PopStyleColor();
            ImGui::SameLine();
            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_LOLLIPOP_PURPLE);
            if(ImGui::Button("Align End 1")){
                AIAC_APP.GetLayer<AIAC::LayerModel>()->ForceAlignToEnd(1);
            }
            ImGui::PopStyleColor();
            ImGui::SameLine();
            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_PLUM_PURPLE);
            if(ImGui::Button("Align End 2")){
                AIAC_APP.GetLayer<AIAC::LayerModel>()->ForceAlignToEnd(0);
            }
            ImGui::PopStyleColor();

            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_EGYPTIAN_BLUE);
            if(ImGui::Button("Rotate +")){
                AIAC_APP.GetLayer<AIAC::LayerModel>()->ChangeAlignRotation(1);
            }
            ImGui::PopStyleColor();
            ImGui::SameLine();
            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_EGYPTIAN_BLUE);
            if(ImGui::Button("Rotate -")){
                AIAC_APP.GetLayer<AIAC::LayerModel>()->ChangeAlignRotation(-1);
            }
            ImGui::PopStyleColor();
            ImGui::SameLine();
            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_SAPPHIRE_BLUE);
            if(ImGui::Button("Reset Rotation")){
                AIAC_APP.GetLayer<AIAC::LayerModel>()->ResetAlignRotation();
            }
            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_CINNAMON_RED);
            ImGui::PopStyleColor();
            ImGui::SameLine();
            if(ImGui::Button("Flip")){
                AIAC_APP.GetLayer<AIAC::LayerModel>()->FlipAlign();
            }
            ImGui::PopStyleColor();
        ImGui::EndChild();

        // current component to execute
        std::string isCurrentDone = AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().GetCurrentComponent()->IsMarkedDone ? "Done" : "Not done";
        if (AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().GetCurrentComponent()->IsMarkedDone)
            ImGui::PushStyleColor(ImGuiCol_Text, AIAC_UI_GREEN);
        else
            ImGui::PushStyleColor(ImGuiCol_Text, AIAC_UI_RED);
        ImGui::Text("Current Execution: %s", isCurrentDone.c_str());
        ImGui::PopStyleColor();
        ImGui::BeginChild("components_control_panel", ImVec2(0, 216), true, ImGuiWindowFlags_HorizontalScrollbar);
            // print the progress
            ImGui::Text("Progress: %d / %d ( %.2f%% )", AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().GetFabricatedComponents(),
                                                        AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().GetTotalComponents(),
                                                        AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().GetFabricationProgress());

            // control the component to fabricate
            string currentCompoID = AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().GetCurrentComponentID();
            static const char* previewValue = currentCompoID.c_str();
            ImGui::SetNextItemWidth(300);
            ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(7, 7));
            if(ImGui::BeginCombo("##ACIMComponents", previewValue))
            {
                for (auto& componentID : AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().GetAllComponentsIDs()) {
                    bool isSelected = (currentCompoID == componentID);
                    if (AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().GetComponent(componentID)->IsMarkedDone)
                        ImGui::PushStyleColor(ImGuiCol_Text, AIAC_UI_GREEN);
                    else
                        ImGui::PushStyleColor(ImGuiCol_Text, AIAC_UI_RED);
                    if (ImGui::Selectable(componentID.c_str(), isSelected)){
                        AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().SetCurrentComponentTo(componentID.c_str());
                        AIAC_APP.GetLayer<LayerFeedback>()->ActivateCurrentFeedbackVisibility();
                    }
                    if (isSelected)
                        ImGui::SetItemDefaultFocus();
                    ImGui::PopStyleColor();
                }
                ImGui::EndCombo();
            }

            // go to next or previous component (<,>)
            float halfWidth = ImGui::GetWindowWidth() / 2;
            if(ImGui::Button("<", ImVec2(halfWidth-30, 0)))
            {
                AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().SetPrevComponentAsCurrent();
                AIAC_APP.GetLayer<LayerFeedback>()->ActivateCurrentFeedbackVisibility();
            }
            ImGui::SameLine();
            if (ImGui::Button(">", ImVec2(halfWidth-30, 0)))
            {
                AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().SetNextComponentAsCurrent();
                AIAC_APP.GetLayer<LayerFeedback>()->ActivateCurrentFeedbackVisibility();
            }

            // swap ends for holes only
            auto currentComp = AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().GetCurrentComponent();
            if (auto hole = dynamic_cast<TimberInfo::Hole*>(currentComp)){
                if(ImGui::Button("Swap S/E")) hole->SwapStartEnd();
                ImGui::SameLine();
                ImGui::Text("Diameter: %.f mm", hole->GetRadius()*2000);
            }
            ImGui::PopStyleVar();

            // this is a button for enable the plane visualizer
            // for cutting tools show the red plane for cutting guidance
            if (auto cut = dynamic_cast<TimberInfo::Cut*>(currentComp)){
                if(ImGui::Checkbox("Show Cut Plane", &AIAC_APP.GetLayer<AIAC::LayerFeedback>()->ToShowCutPlane)){
                    if(AIAC_APP.GetLayer<AIAC::LayerFeedback>()->ToShowCutPlane){
                        AIAC_APP.GetLayer<AIAC::LayerFeedback>()->EnableCutPlane(true);
                    } else {
                        AIAC_APP.GetLayer<AIAC::LayerFeedback>()->EnableCutPlane(false);
                    }
                };
            }
            ImGui::SameLine();
            if(ImGui::Checkbox("Show Cotas", &AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().IsShowingCotas)){
                if(AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().IsShowingCotas){
                    AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().UpdateCotasVisibility(true);
                } else {
                    AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().UpdateCotasVisibility(false);
                }
            }

            if (auto feedback = dynamic_cast<CutCircularSawFeedback*>(AIAC_APP.GetLayer<LayerFeedback>()->GetCurrentFabFeedback())){
                ImGui::Checkbox("Manually Select Face", &feedback->IsRefFacesSelectedManually);
                if (feedback->IsRefFacesSelectedManually) {
                    if(ImGui::Button("<##FaceSelect", ImVec2(halfWidth-30, 0)))
                    {
                        feedback->ManuallyScrollRefFace(-1);
                    }
                    ImGui::SameLine();
                    if (ImGui::Button(">##FaceSelect", ImVec2(halfWidth-30, 0)))
                    {
                        feedback->ManuallyScrollRefFace(1);
                    }
                }
            }

            if (auto feedback = dynamic_cast<CutChainSawFeedback*>(AIAC_APP.GetLayer<LayerFeedback>()->GetCurrentFabFeedback())){
                ImGui::Checkbox("Manually Select Face", &feedback->IsRefFacesSelectedManually);
                if (feedback->IsRefFacesSelectedManually) {
                    if(ImGui::Button("<##FaceSelect", ImVec2(halfWidth-30, 0)))
                    {
                        feedback->ManuallyScrollRefFace(-1);
                    }
                    ImGui::SameLine();
                    if (ImGui::Button(">##FaceSelect", ImVec2(halfWidth-30, 0)))
                    {
                        feedback->ManuallyScrollRefFace(1);
                    }
                }
            }


            ImGui::PushStyleColor(ImGuiCol_CheckMark, AIAC_UI_LIGHT_GREEN);
            if(ImGui::Checkbox("Mark as Done", &AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().GetCurrentComponent()->IsMarkedDone));
            ImGui::PopStyleColor();
            ImGui::SameLine();
            if(ImGui::Checkbox("Show All Components", &AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().IsShowingAllComponents)){
                if(AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().IsShowingAllComponents){
                    AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().ShowAllComponents();
                    AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().SetBboxVisibility(true);
                    AIAC_APP.GetLayer<LayerFeedback>()->DeactivateCurrentFeedbackVisibility();
                } else {
                    AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetTimberInfo().HideAllComponentsExceptCurrent();
                    AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().SetBboxVisibility(false);
                    AIAC_APP.GetLayer<LayerFeedback>()->ActivateCurrentFeedbackVisibility();
                }
            }
        ImGui::EndChild();

        ImGui::Text("Scaling Factor Adjustment:");
        ImGui::BeginChild("adjust_model_scale_factor", ImVec2(0, 90), true, ImGuiWindowFlags_HorizontalScrollbar);
            ImGui::Text("ACIM BBox Length: %.2f", AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetRealWorldLength() * 1000);
            ImGui::SameLine();
            ImGui::Text("/ Measured Length: %.2f", AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().GetMeasuredBboxLength() * 1000);
            sliderVal = 0.f;
            ImGui::SliderFloat("## Adjust measured length", &sliderVal, -1e-3f, 1e-3f, "Adjust measured length", ImGuiSliderFlags_AlwaysClamp);
            if (sliderVal != 0.f) AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().AddMeasuredBboxLength(sliderVal);

            if(ImGui::Button("Confirm & Close APP")){
                // restart APP
                AIAC_APP.GetLayer<LayerModel>()->GetACInfoModel().AdjustScale();
                *m_IsOpen = false;
                AIAC_EBUS->EnqueueEvent(std::make_shared<AppCloseEvent>());
            }
        ImGui::EndChild();
    }

    void LayerUI::ShowSaveVideoRecorderFileDialog(){
         ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x * 0.8, ImGui::GetIO().DisplaySize.y * 0.75));
         if (ImGui::Button("Select Save Directory")) {
             ImGuiFileDialog::Instance()->OpenDialog("ChooseDirectoryDialog", "Choose Directory", nullptr, ".");
         }

         if (ImGuiFileDialog::Instance()->Display("ChooseDirectoryDialog")) {
             if (ImGuiFileDialog::Instance()->IsOk()) {
                 std::string filePathName  = ImGuiFileDialog::Instance()->GetFilePathName();
                 std::string filePath  = ImGuiFileDialog::Instance()->GetCurrentPath();
                 AIAC_APP.GetLayer<AIAC::LayerUtils>()->SetSaveFolderPath(filePathName);
             }
             ImGuiFileDialog::Instance()->Close();
         }
    }
    void LayerUI::SetPaneUIUtils(){
         ImGui::Text("Set Utils Folder:");
         ImGui::BeginChild("Set Utils Folder", ImVec2(0, 60), true);
             ShowSaveVideoRecorderFileDialog();
             std::string displayedPath = AIAC_APP.GetLayer<AIAC::LayerUtils>()->GetSaveFolderPath();
             ImGui::Text("Selected Path: %s", displayedPath.c_str());
         ImGui::EndChild();

         ImGui::Text("Video Recorder:");
         ImGui::BeginChild("Video Recorder", ImVec2(0, 100), true);
             static bool isRecordingInProgress = false;
             ImGui::Text("Recording: ");
             ImGui::SameLine();
             if(isRecordingInProgress){
                 ImGui::TextColored(AIAC_UI_GREEN, "Yes");
             } else {
                 ImGui::TextColored(AIAC_UI_RED, "No");
             }

             ImGui::Text("Video is being processed: ");
             ImGui::SameLine();
             if(AIAC_APP.GetLayer<LayerUtils>()->IsProcessing()){
                 ImGui::TextColored(AIAC_UI_GREEN, "Yes");
             } else {
                 ImGui::TextColored(AIAC_UI_RED, "No");
             }

             ImGui::Text("Video Recorder Controls: ");
             if (ImGui::Button("Start Recording"))
             {
                 if (!isRecordingInProgress && !AIAC_APP.GetLayer<LayerUtils>()->IsProcessing())
                 {
                     isRecordingInProgress = true;
                     AIAC_APP.GetLayer<LayerUtils>()->StartRecording();
                 }
             }
             ImGui::SameLine();
             if (ImGui::Button("Stop Recording"))
             {
                 if (isRecordingInProgress && !AIAC_APP.GetLayer<LayerUtils>()->IsProcessing())
                 {
                     isRecordingInProgress = false;
                     AIAC_APP.GetLayer<LayerUtils>()->StopRecording();
                 }
             };
         ImGui::EndChild();

         ImGui::Text("Hole Toolhead Axis Exporter:");
         ImGui::BeginChild("Hole Toolhead Axis Exporter", ImVec2(0, 42), true);
             if (ImGui::Button("Export Hole Toolhead Coordinates"))
             {
                 AIAC_APP.GetLayer<LayerUtils>()->ExportHoleToolheadAxis();
             }
         ImGui::EndChild();

         ImGui::Text("Window Photo:");
         ImGui::BeginChild("Window Photo", ImVec2(0, 42), true);
         if (ImGui::Button("Take a window photo"))
         {
             AIAC_APP.GetLayer<LayerUtils>()->SetWindowScreenshot(true);
         }
         ImGui::SameLine();
         if (ImGui::Button("Take a colored buffer photo"))
         {
             AIAC_APP.GetLayer<LayerUtils>()->TakeBufferScreenshot();
         }
         ImGui::EndChild();

    }

    void LayerUI::SetPaneUIToolhead()
    {
        ImGui::Text("Toolhead control:");
        ImGui::BeginChild("toolhead_pose_inputs", ImVec2(0, 470), true, ImGuiWindowFlags_HorizontalScrollbar);

            ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.5, 0.5));

            ImVec2 sizeShowToolHeadVisualsButton = ImVec2(-1, 60);
            static bool isButtonVisualsPress = false;
            std::string buttonVisualLabel = isButtonVisualsPress ? "Show Silhouette" : "Hide Silhouette";
            if (!isButtonVisualsPress) 
            {
                ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_HIBISCUS_RED);
                ImGui::PushStyleColor(ImGuiCol_HeaderHovered, AIAC_UI_HIBISCUS_RED);
                AIAC_APP.GetLayer<AIAC::LayerToolhead>()->IsShowSilouhette = true;
            }
            else
            {
                ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_YALE_BLUE);
                ImGui::PushStyleColor(ImGuiCol_HeaderHovered, AIAC_UI_YALE_BLUE);
                AIAC_APP.GetLayer<AIAC::LayerToolhead>()->IsShowSilouhette = false;
            }
            if (ImGui::Button(buttonVisualLabel.c_str(), sizeShowToolHeadVisualsButton))
                isButtonVisualsPress = !isButtonVisualsPress;
            ImGui::PopStyleColor(2);

            ImVec2 sizeSquareButton = ImVec2(70, 70);

            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_SEA_GREEN);
            ImGui::PushStyleColor(ImGuiCol_HeaderHovered, AIAC_UI_GREEN);
            if(ImGui::Button("save \npose", sizeSquareButton))
                AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->ManipulateModel('y');
            ImGui::SameLine();
            ImGui::PopStyleColor(2);

            static bool isButtonPressed = false;
            std::string buttonLabel = isButtonPressed ? "Stop \nrefine" : "Start \nrefine";
            if (!isButtonPressed) 
            {
                ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_GRAPE_PURPLE);
                ImGui::PushStyleColor(ImGuiCol_HeaderHovered, AIAC_UI_GRAPE_PURPLE);
                AIAC_APP.GetLayer<AIAC::LayerToolhead>()->ToolheadStateUI = -1;
            }
            else
            {
                ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_CARMINE_RED);
                ImGui::PushStyleColor(ImGuiCol_HeaderHovered, AIAC_UI_CARMINE_RED);
                AIAC_APP.GetLayer<AIAC::LayerToolhead>()->ToolheadStateUI = 0;
            }
            if (ImGui::Button(buttonLabel.c_str(), sizeSquareButton))
                isButtonPressed = !isButtonPressed;
            ImGui::SameLine();
            ImGui::PopStyleColor(2);

            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_LIGHT_GREY);
            ImGui::PushStyleColor(ImGuiCol_HeaderHovered, AIAC_UI_LIGHT_GREY);
            if(ImGui::Button("reset \nlast \npose", sizeSquareButton))
                AIAC_APP.GetLayer<AIAC::LayerToolhead>()->ResetToLastSavedPose();
            ImGui::SameLine();
            ImGui::PopStyleColor(2);

            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_LIGHT_GREY);
            ImGui::PushStyleColor(ImGuiCol_HeaderHovered, AIAC_UI_LIGHT_GREY);
            if(ImGui::Button("reset \nconfig \npose", sizeSquareButton))
                AIAC_APP.GetLayer<AIAC::LayerToolhead>()->ResetPoseFromConfig();
            ImGui::PopStyleColor(2);

            ImGui::PopStyleVar();

            ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(200, 15));
            ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 100);
            ImGui::PushStyleVar(ImGuiStyleVar_GrabRounding, 5);
            ImGui::PushItemWidth(-30);
            float sliderF = 0.f;
            ImGui::SliderFloat("Teq", &sliderF, -0.01f, 0.01f, "<backward T forward>", ImGuiSliderFlags_AlwaysClamp);
            if (sliderF != 0.f)
            {
                AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->SetModelManipulationTranslationScale(abs(sliderF));
                if (sliderF > 0.f)
                    AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->ManipulateModel('e');
                else
                    AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->ManipulateModel('q');
            }
            sliderF = 0.f;
            ImGui::SliderFloat("Tda", &sliderF, -0.01f, 0.01f, "<left T right>", ImGuiSliderFlags_AlwaysClamp);
            if (sliderF != 0.f)
            {
                AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->SetModelManipulationTranslationScale(abs(sliderF));
                if (sliderF > 0.f)
                    AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->ManipulateModel('d');
                else
                    AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->ManipulateModel('a');
            }
            sliderF = 0.f;
            ImGui::SliderFloat("Tws", &sliderF, -0.01f, 0.01f, "<down T up>", ImGuiSliderFlags_AlwaysClamp);
            if (sliderF != 0.f)
            {
                AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->SetModelManipulationTranslationScale(abs(sliderF));
                if (sliderF > 0.f)
                    AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->ManipulateModel('w');
                else
                    AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->ManipulateModel('s');
            }
            sliderF = 0.f;
            ImGui::SliderFloat("Rjl", &sliderF, -3.0f, 3.0f, "<left R right>", ImGuiSliderFlags_AlwaysClamp);
            if (sliderF != 0.f)
            {
                AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->SetModelManipulationRotationScale(abs(sliderF));
                if (sliderF > 0.f)
                    AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->ManipulateModel('j');
                else
                    AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->ManipulateModel('l');
            }
            sliderF = 0.f;
            ImGui::SliderFloat("Rik", &sliderF, -3.0f, 3.0f, "<down R up>", ImGuiSliderFlags_AlwaysClamp);
            if (sliderF != 0.f)
            {
                AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->SetModelManipulationRotationScale(abs(sliderF));
                if (sliderF > 0.f)
                    AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->ManipulateModel('i');
                else
                    AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->ManipulateModel('k');
            }
            sliderF = 0.f;
            ImGui::SliderFloat("Ruo", &sliderF, -3.0f, 3.0f, "<backward R forward>", ImGuiSliderFlags_AlwaysClamp);
            if (sliderF != 0.f)
            {
                AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->SetModelManipulationRotationScale(abs(sliderF));
                if (sliderF > 0.f)
                    AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->ManipulateModel('u');
                else
                    AIAC_APP.GetLayer<AIAC::LayerToolhead>()->TTool->ManipulateModel('o');
            }
            ImGui::PopStyleVar(3);
            ImGui::PopItemWidth();
        ImGui::EndChild();

        ImGui::Text("Tools graphics:");
            ImGui::BeginChild("toolhead_graphics", ImVec2(-1, 40));

#ifdef ENABLE_DEV_UI
            if(ImGui::Checkbox("Draw Shaded", &AIAC_APP.GetLayer<AIAC::LayerToolhead>()->IsShowShaded));
            if(ImGui::Checkbox("Draw Toolhead GOData", &AIAC_APP.GetLayer<AIAC::LayerToolhead>()->IsShowToolheadGOInfo))
                AIAC_APP.GetLayer<AIAC::LayerToolhead>()->ACInfoToolheadManager->GetActiveToolhead()->SetVisibility(AIAC_APP.GetLayer<AIAC::LayerToolhead>()->IsShowToolheadGOInfo);
#endif
        ImGui::EndChild();

#ifdef ENABLE_DEV_UI
        ImGui::Text("Toolhead Classifier:");
        ImGui::BeginChild("toolhead_classifier", ImVec2(0, 300), true, ImGuiWindowFlags_HorizontalScrollbar);
            if(ImGui::Button("Detect Toolhead", ImVec2(-1, 40)))
                AIAC_APP.GetLayer<AIAC::LayerToolhead>()->DetectToolhead();
            std::string classifierLog = AIAC_APP.GetLayer<AIAC::LayerToolhead>()->GetClassifierLog();
            ImGui::BeginChild("toolhead_selection_ml", ImVec2(0, 150), true, ImGuiWindowFlags_HorizontalScrollbar);
                std::vector<std::string> toolheadNamesML;
                for (auto& toolhead : AIAC_APP.GetLayer<AIAC::LayerToolhead>()->GetClassifierToolheadList())
                    toolheadNamesML.emplace_back(toolhead);
                for (int i = 0; i < toolheadNamesML.size(); i++)
                {
                    auto toolNameButton = ImGui::Button(toolheadNamesML[i].c_str());
                    if (toolNameButton)
                    {
                        AIAC_APP.GetLayer<AIAC::LayerToolhead>()->SetCurrentObject(toolheadNamesML[i]);
                    }
                }
            ImGui::EndChild();
            ImGui::Text("Classifier Log: \n%s", classifierLog.c_str());
        ImGui::EndChild();
#endif

#ifdef ENABLE_DEV_UI
        std::string poseLogButtonText = "Log TTool Pose";
        bool deferPoseLogUI = false;
        if(AIAC_APP.GetLayer<AIAC::LayerToolhead>()->IsSavePoseLog)
        {
            deferPoseLogUI = true;
            poseLogButtonText = "Stop Log TTool Pose";
            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_LIGHT_GREEN);
            ImGui::PushStyleColor(ImGuiCol_ButtonHovered, AIAC_UI_GREEN);
            ImGui::PushStyleColor(ImGuiCol_ButtonActive, AIAC_UI_GREEN);
        }
        if (ImGui::Button(poseLogButtonText.c_str(), ImVec2(-1, 40)))
            AIAC_APP.GetLayer<AIAC::LayerToolhead>()->IsSavePoseLog = !AIAC_APP.GetLayer<AIAC::LayerToolhead>()->IsSavePoseLog;
        if (deferPoseLogUI)
        {
            ImGui::PopStyleColor(3);
        }
#endif

        // selection of toolhead
        float windowHeigh = ImGui::GetWindowHeight();
        float availableHeight = ImGui::GetContentRegionAvail().y;
        std::string toolheadNameActive = AIAC_APP.GetLayer<AIAC::LayerToolhead>()->ACInfoToolheadManager->GetActiveToolheadName();
        std::vector<std::string> toolheadNames;
        for (auto& toolhead : AIAC_APP.GetLayer<AIAC::LayerToolhead>()->ACInfoToolheadManager->GetToolheadNames())
            toolheadNames.emplace_back(toolhead);
        int numbButtonPerRow = 4;
        ImVec2 sizeButton = ImVec2(80, 80);
        int totalHeight = toolheadNames.size() / numbButtonPerRow * sizeButton.y;

        ImGui::Text("Active toolhead: %s", toolheadNameActive.c_str());
        ImGui::BeginChild(
            "toolhead_selection",
            ImVec2(0, totalHeight+sizeButton.y+30),
            false,
            ImGuiWindowFlags_NoScrollbar
        );
            for (int i = 0; i < toolheadNames.size(); i++)
            {
                if (i % numbButtonPerRow != 0)
                    ImGui::SameLine();

                std::string label = toolheadNames[i];
                int charPerLine = sizeButton.x / 10;
                for (int j = label.length() - 1; j > 0; j--)
                {
                    if (j % charPerLine == 0)
                    {
                        label.insert(j, "\n");
                    }
                }

                ImVec4 buttonClr = AIAC_UI_DARK_GREY;
                ACToolHeadType toolheadType = AIAC_APP.GetLayer<AIAC::LayerToolhead>()->ACInfoToolheadManager->GetToolheadType(toolheadNames[i]);
                if (toolheadType == ACToolHeadType::DRILLBIT)
                    ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_PURPLE);
                else if (toolheadType == ACToolHeadType::CIRCULARSAW)
                    ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_DARK_GREEN);
                else if (toolheadType == ACToolHeadType::SABERSAW)
                    ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_LIGHT_GREY);
                else if (toolheadType == ACToolHeadType::CHAINSAW)
                    ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_LIGHT_BLUE);
                else if (label == toolheadNameActive)
                    ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_SPARK_ORANGE);
                else
                    ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_DARK_GREY);

                if (ImGui::Button(label.c_str(), sizeButton))
                    AIAC_APP.GetLayer<AIAC::LayerToolhead>()->SetCurrentObject(toolheadNames[i]);

                ImGui::PopStyleColor();
            }
        ImGui::EndChild();
    }

    void LayerUI::OnCollapsingPaneUIToolhead(){
        AIAC_APP.GetLayer<AIAC::LayerToolhead>()->IsShowSilouhette = false;
    }

    void LayerUI::ShowMappingPopup()
    {
        ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y));
        ImGui::SetNextWindowPos(ImVec2(0, 0));
        ImGui::Begin("Mapping", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize);

            CvtGlTextureObj2ImTexture(AIAC_APP.GetRenderer()->GetMappingView(), m_MappingViewImTexture);
            // TODO: use config to store the size, and set glViewPort before rendering
            ImGui::ImageButton(m_MappingViewImTexture.ID, ImVec2(600, 442), ImVec2(0, 1), ImVec2(1, 0), 0, ImColor(255, 255, 255, 128));

            ImGui::SameLine();
            ImGui::BeginChild("mapping_info_child", ImVec2(0, 0), false);
                // First part - global view
                ImVec2 sideBarViewportSize = ImGui::GetContentRegionAvail();
                ImGui::BeginChild("global_view", ImVec2(sideBarViewportSize.x, sideBarViewportSize.x * 3 / 4 + 20), true);
                    ImVec2 viewportSize = ImGui::GetContentRegionAvail();
                    viewportSize = ImVec2(viewportSize.x, viewportSize.y - 24);
                    AIAC_APP.GetRenderer()->SetGlobalViewSize(viewportSize.x, viewportSize.y);
                    SetGlobalViewUI(viewportSize);
                ImGui::EndChild();

                // Second part - mapping info
                // sideBarViewportSize = ImGui::GetContentRegionAvail();
                // ImGui::BeginChild("mapping_info", ImVec2(sideBarViewportSize.x, 0), true);
                //     ImGui::Text("Map Points: %u", AIAC_APP.GetLayer<LayerSlam>()->Slam.getMap()->map_points.size());
                //     ImGui::Text("Map Markers: %lu", AIAC_APP.GetLayer<LayerSlam>()->Slam.getMap()->map_markers.size());
                // ImGui::EndChild();

                // Third part - mapping menu for adjusting params
                sideBarViewportSize = ImGui::GetContentRegionAvail();
                ImGui::BeginChild("mapping_menu", ImVec2(sideBarViewportSize.x, sideBarViewportSize.y - 48), true);
                    ImGui::Text("Save Path:");
                    ImGui::InputText("## Map Saving Path", m_MappingParams.MapSavingPath, PATH_BUF_SIZE, ImGuiInputTextFlags_AutoSelectAll);
                    ImGui::SameLine();
                    if(ImGui::Button(".", ImVec2(0, 0))){
                        // m_IsSavingMap = true;
                        OpenFileSelectDialog("Choose Save Map Path", ".map", m_MappingParams.MapSavingPath);
                    }
                    ImGui::Checkbox("Optimize Map", &m_MappingParams.ToOptimizeMap);
                    ImGui::Checkbox("Save Map", &m_MappingParams.ToSaveMap);

                    ImGui::Text("--------------------");
                    ImGui::Text("Reconstruct Params:");
                    float sliderValFloat = 0.f;
                    int sliderValInt = 0;

                    sliderValFloat = 0.f;
                    ImGui::SliderFloat("## Radius Search", &sliderValFloat, -1.0f, 1.0f, "Radius Search", ImGuiSliderFlags_AlwaysClamp);
                    if (sliderValFloat != 0.f) m_ReconstructParams.RadiusSearch += sliderValFloat;
                    ImGui::SameLine();
                    ImGui::Text("%.1f", m_ReconstructParams.RadiusSearch);

                    sliderValFloat = 0.f;
                    ImGui::SliderFloat("## Crease Angle Threshold", &sliderValFloat, -1.0f, 1.0f, "Crease Angle Threshold", ImGuiSliderFlags_AlwaysClamp);
                    if (sliderValFloat != 0.f) m_ReconstructParams.CreaseAngleThreshold += sliderValFloat;
                    ImGui::SameLine();
                    ImGui::Text("%.1f", m_ReconstructParams.CreaseAngleThreshold);

                    sliderValInt = 0;
                    ImGui::SliderInt("## Min Cluster Size", &sliderValInt, -3, 3, "Min Cluster Size", ImGuiSliderFlags_AlwaysClamp);
                    if (sliderValInt != 0) m_ReconstructParams.MinClusterSize += sliderValInt;
                    ImGui::SameLine();
                    ImGui::Text("%d", m_ReconstructParams.MinClusterSize);

                    sliderValFloat = 0.f;
                    ImGui::SliderFloat("## Max Pln Dist", &sliderValFloat, -1.0f, 1.0f, "Max Pln Dist", ImGuiSliderFlags_AlwaysClamp);
                    if (sliderValFloat != 0.f) m_ReconstructParams.MaxPlnDist += sliderValFloat;
                    ImGui::SameLine();
                    ImGui::Text("%.1f", m_ReconstructParams.MaxPlnDist);

                    sliderValFloat = 0.f;
                    ImGui::SliderFloat("## Max Pln Angle", &sliderValFloat, -1.0f, 1.0f, "Max Pln Angle", ImGuiSliderFlags_AlwaysClamp);
                    if (sliderValFloat != 0.f) m_ReconstructParams.MaxPlnAngle += sliderValFloat;
                    ImGui::SameLine();
                    ImGui::Text("%.1f", m_ReconstructParams.MaxPlnAngle);

                    sliderValFloat = 0.f;
                    ImGui::SliderFloat("## AABB Scale Factor", &sliderValFloat, -1.0f, 1.0f, "AABB Scale Factor", ImGuiSliderFlags_AlwaysClamp);
                    if (sliderValFloat != 0.f) m_ReconstructParams.AABBScaleFactor += sliderValFloat;
                    ImGui::SameLine();
                    ImGui::Text("%.1f", m_ReconstructParams.AABBScaleFactor);

                    sliderValFloat = 0.f;
                    ImGui::SliderFloat("## Max Poly Dist", &sliderValFloat, -1.0f, 1.0f, "Max Poly Dist", ImGuiSliderFlags_AlwaysClamp);
                    if (sliderValFloat != 0.f) m_ReconstructParams.MaxPolyDist += sliderValFloat;
                    ImGui::SameLine();
                    ImGui::Text("%.1f", m_ReconstructParams.MaxPolyDist);

                    sliderValFloat = 0.f;
                    ImGui::PushItemWidth(-54);
                    ImGui::SliderFloat("## Eps", &sliderValFloat, -1.0f, 1.0f, "Eps", ImGuiSliderFlags_AlwaysClamp);
                    ImGui::PopItemWidth();
                    if (sliderValFloat > 0.f) m_ReconstructParams.Eps *= 2;
                    else if (sliderValFloat < 0.f) m_ReconstructParams.Eps /= 2;
                    ImGui::SameLine();
                    ImGui::Text("%.1e", m_ReconstructParams.Eps);

                ImGui::EndChild();
                if(ImGui::Button("Discard", ImVec2(ImGui::GetContentRegionAvail().x, 40))){
                    AIAC_EBUS->EnqueueEvent(std::make_shared<SLAMStopMappingEvent>(
                        false,
                        m_MappingParams.MapSavingPath,
                        false,
                        m_ReconstructParams.RadiusSearch,
                        m_ReconstructParams.CreaseAngleThreshold,
                        m_ReconstructParams.MinClusterSize,
                        m_ReconstructParams.AABBScaleFactor,
                        m_ReconstructParams.MaxPolyDist,
                        m_ReconstructParams.MaxPlnDist,
                        m_ReconstructParams.MaxPlnAngle,
                        m_ReconstructParams.Eps
                    ));
                }
                if(ImGui::Button("Save & Exit", ImVec2(ImGui::GetContentRegionAvail().x, 40))){
                    AIAC_EBUS->EnqueueEvent(std::make_shared<SLAMStopMappingEvent>(
                        m_MappingParams.ToSaveMap,
                        m_MappingParams.MapSavingPath,
                        m_MappingParams.ToOptimizeMap,
                        m_ReconstructParams.RadiusSearch,
                        m_ReconstructParams.CreaseAngleThreshold,
                        m_ReconstructParams.MinClusterSize,
                        m_ReconstructParams.AABBScaleFactor,
                        m_ReconstructParams.MaxPolyDist,
                        m_ReconstructParams.MaxPlnDist,
                        m_ReconstructParams.MaxPlnAngle,
                        m_ReconstructParams.Eps
                    ));
                }
            ImGui::EndChild();

            if (ImGui::BeginPopupContextWindow())
            {
                if (ImGui::Selectable("Clear"))
                {
                }
                ImGui::EndPopup();
            }
        ImGui::End();
    }

    void LayerUI::ShowCamCalibPopup()
    {
        ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y));
        ImGui::SetNextWindowPos(ImVec2(0, 0));
        ImGui::Begin("Camera Calibration", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize);

        CvtGlTextureObj2ImTexture(AIAC_APP.GetRenderer()->GetCamCalibView(), m_CamCalibViewImTexture);
        ImGui::ImageButton(m_CamCalibViewImTexture.ID, ImVec2(600, 442), ImVec2(0, 1), ImVec2(1, 0), 0, ImColor(255, 255, 255, 128));

        ImGui::SameLine();
        ImGui::BeginChild("cam_calib_info_child", ImVec2(0, 0), false); {
            ImVec2 sideBarViewportSize = ImGui::GetContentRegionAvail();
            ImGui::BeginChild("calib_param", ImVec2(sideBarViewportSize.x, 132), true);
            {
                ImGui::Text("Board Size:");

                ImGui::Text("Width");
                ImGui::SameLine();
                ImGui::InputInt("Board Width",
                                &AIAC_APP.GetLayer<AIAC::LayerCameraCalib>()->GetCameraCalibrator().boardSize.width);

                ImGui::Text("Height");
                ImGui::SameLine();
                ImGui::InputInt("Board Height",
                                &AIAC_APP.GetLayer<AIAC::LayerCameraCalib>()->GetCameraCalibrator().boardSize.height);

                ImGui::Text("Square Size");
                ImGui::SameLine();
                ImGui::InputFloat(" Square Size",
                                  &AIAC_APP.GetLayer<AIAC::LayerCameraCalib>()->GetCameraCalibrator().squareSize);

                ImGui::Checkbox("Use Fisheye",
                                &AIAC_APP.GetLayer<AIAC::LayerCameraCalib>()->GetCameraCalibrator().useFisheye);

            } ImGui::EndChild();
            ImGui::BeginChild("capture_setting", ImVec2(sideBarViewportSize.x, 108), true);
            {
                ImGui::Checkbox("Auto Capture", &AIAC_APP.GetLayer<AIAC::LayerCameraCalib>()->AutoCapture);

                ImGui::Text("Delay");
                ImGui::SameLine();
                ImGui::InputInt("Delay", &AIAC_APP.GetLayer<AIAC::LayerCameraCalib>()->Delay);

                ImGui::Text("Num of Frames");
                ImGui::SameLine();
                ImGui::InputInt("Num Of Frame", &AIAC_APP.GetLayer<AIAC::LayerCameraCalib>()->NumOfFrame);

                stringstream ss;
                if (AIAC_APP.GetLayer<AIAC::LayerCameraCalib>()->IsCapturing()) {
                    ss << "Capturing: "
                        << AIAC_APP.GetLayer<AIAC::LayerCameraCalib>()->GetCameraCalibrator().GetImageAmount() << "/"
                        << AIAC_APP.GetLayer<AIAC::LayerCameraCalib>()->NumOfFrame;
                } else {
                    ss << "Click Start to Capture";
                }
                ImGui::Text("%s", ss.str().c_str());

            }ImGui::EndChild();
            sideBarViewportSize = ImGui::GetContentRegionAvail();
            ImGui::BeginChild("menu", sideBarViewportSize, true);
            {
                // Save Path
                if(ImGui::Button(">")){
                    m_IsChoosingCamCalibFileSavePath = true;
                }
                ImGui::SameLine();
                char tmpSavePath[PATH_BUF_SIZE];
                strcpy(tmpSavePath, AIAC_APP.GetLayer<AIAC::LayerCameraCalib>()->SaveFilename.c_str());
                ImGui::InputText("Save Path", tmpSavePath, PATH_BUF_SIZE);
                AIAC_APP.GetLayer<AIAC::LayerCameraCalib>()->SaveFilename = tmpSavePath;

                if(ImGui::Button("Start")){
                    AIAC_APP.GetLayer<AIAC::LayerCameraCalib>()->StartCapturing();
                }
                ImGui::SameLine();
                if(ImGui::Button("Exit")){
                    AIAC_APP.GetLayer<AIAC::LayerCameraCalib>()->StopCalibration();
                }
            } ImGui::EndChild();
        } ImGui::EndChild();

        ImGui::End();
    }

    void LayerUI::ShowCombineMapPopup()
    {
//        ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x * 0.3, ImGui::GetIO().DisplaySize.y * 0.3));

        ImGui::Begin("Combine map", nullptr);
            ImGui::PushItemWidth(-1);

            ImGui::Text("Select File:");
            if(ImGui::Button("Map 1", ImVec2(60, 0))){
                OpenFileSelectDialog("Select input map 1 path", ".map", m_CombMapParams.MapPathA);
            }
            ImGui::SameLine();
            ImGui::InputText("In Map 1", m_CombMapParams.MapPathA, PATH_BUF_SIZE, ImGuiInputTextFlags_AutoSelectAll);

            if(ImGui::Button("Map 2", ImVec2(60, 0))){
                OpenFileSelectDialog("Select input map 2 path", ".map", m_CombMapParams.MapPathB);
            }
            ImGui::SameLine();
            ImGui::InputText("In Map 2", m_CombMapParams.MapPathB, PATH_BUF_SIZE, ImGuiInputTextFlags_AutoSelectAll);

            if(ImGui::Button("Output", ImVec2(60, 0))){
                OpenFileSelectDialog("Select export map path", ".map", m_CombMapParams.OutputPath);
            }
            ImGui::SameLine();
            ImGui::InputText("In Output Path", m_CombMapParams.OutputPath, PATH_BUF_SIZE, ImGuiInputTextFlags_AutoSelectAll);
            ImGui::PopItemWidth();

            ImGui::Text(" ");
            ImGui::Text(" ");
            ImGui::SameLine(ImGui::GetWindowWidth() - 176);
            if(ImGui::Button("Cancel", ImVec2(80, 0))){ m_IsCombiningMap = false; }
            ImGui::SameLine();
            if(ImGui::Button("Confirm", ImVec2(80, 0))){
                if(strlen(m_CombMapParams.MapPathA) == 0 || strlen(m_CombMapParams.MapPathB) == 0){
                    AIAC_ERROR("Path not selected.");
                } else {
                    AIAC_EBUS->EnqueueEvent(std::make_shared<SLAMCombineMapEvent>(
                            m_CombMapParams.MapPathA,
                            m_CombMapParams.MapPathB,
                            m_CombMapParams.OutputPath,
                            50,
                            m_ReconstructParams.RadiusSearch,
                            m_ReconstructParams.CreaseAngleThreshold,
                            m_ReconstructParams.MinClusterSize,
                            m_ReconstructParams.AABBScaleFactor,
                            m_ReconstructParams.MaxPolyDist,
                            m_ReconstructParams.MaxPlnDist,
                            m_ReconstructParams.MaxPlnAngle,
                            m_ReconstructParams.Eps
                    ));

                    memset(m_CombMapParams.MapPathA, 0, PATH_BUF_SIZE);
                    memset(m_CombMapParams.MapPathB, 0, PATH_BUF_SIZE);
                    memset(m_CombMapParams.OutputPath, 0, PATH_BUF_SIZE);
                    m_IsCombiningMap = false;
                }
            }

        ImGui::End();
    }

    void LayerUI::ShowReconstruct3DPopup()
    {
        ImGui::Begin("Reconstruct 3D", nullptr);
            ImGui::Text("Select Path:");
            if(ImGui::Button("Import .yml", ImVec2(84, 0))){
                OpenFileSelectDialog("Select map path", ".yml", m_ReconstructParams.TagMapPath);
            }
            ImGui::SameLine();
            ImGui::InputText("## Tag Map Path", m_ReconstructParams.TagMapPath, PATH_BUF_SIZE, ImGuiInputTextFlags_AutoSelectAll);
            if(ImGui::Button("Export .ply", ImVec2(84, 0))){
                OpenFileSelectDialog("Select export .ply path", ".ply", m_ReconstructParams.ExportPath);
            }
            ImGui::SameLine();
            ImGui::InputText("## Export Path", m_ReconstructParams.ExportPath, PATH_BUF_SIZE, ImGuiInputTextFlags_AutoSelectAll);

            ImGui::PushItemWidth(-54);

            ImGui::Text("Reconstruct Params");
            ImGui::SameLine();
            if(ImGui::Button("Load from file", ImVec2(106, 0)))
            {
                std::string defaultConfigDir = AIAC::Config::Get<std::string>(
                    AIAC::Config::SEC_TSLAM,
                    AIAC::Config::RECONSTRUCT_CONFIG_DEFAULT_FILE,
                    "assets/tslam/reconstruct_default.ini");
                if (!defaultConfigDir.empty()) {
                    AIAC_INFO("Default config for reconstruction dir: ");
                    AIAC_INFO(defaultConfigDir);
                    const char* defaultConfigDirCStr = defaultConfigDir.c_str();
                    this->m_TmpPathBuf[0] = '\0';
                    strcpy(this->m_TmpPathBuf, defaultConfigDirCStr);
                }
                else
                    AIAC_WARN("Default config for reconstruction dir is empty.");

                AIAC_INFO("TmpPathBuf1: ");
                AIAC_INFO(this->m_TmpPathBuf);

                OpenFileSelectDialog("Load Reconstruction Parameter", ".ini", m_TmpPathBuf, [&]
                {
                    AIAC_INFO("TmpPathBuf2: ");
                    AIAC_INFO(this->m_TmpPathBuf);

                    LoadReconstructParamsFromFile(m_TmpPathBuf);
                });
            }
            float sliderValFloat = 0.f;
            int sliderValInt = 0;

            sliderValFloat = 0.f;
            ImGui::Text(" (2.0)");
            ImGui::SameLine();
            ImGui::SliderFloat("## Radius Search", &sliderValFloat, -1.0f, 1.0f, "Radius Search", ImGuiSliderFlags_AlwaysClamp);
            if (sliderValFloat != 0.f) m_ReconstructParams.RadiusSearch += sliderValFloat;
            ImGui::SameLine();
            ImGui::Text("%.1f", m_ReconstructParams.RadiusSearch);

            sliderValFloat = 0.f;
            ImGui::Text(" (5.0)");
            ImGui::SameLine();
            ImGui::SliderFloat("## Crease Angle Threshold", &sliderValFloat, -1.0f, 1.0f, "Crease Angle Threshold", ImGuiSliderFlags_AlwaysClamp);
            if (sliderValFloat != 0.f) m_ReconstructParams.CreaseAngleThreshold += sliderValFloat;
            ImGui::SameLine();
            ImGui::Text("%.1f", m_ReconstructParams.CreaseAngleThreshold);

            sliderValInt = 0;
            ImGui::Text("  (1) ");
            ImGui::SameLine();
            ImGui::SliderInt("## Min Cluster Size", &sliderValInt, -3, 3, "Min Cluster Size", ImGuiSliderFlags_AlwaysClamp);
            if (sliderValInt != 0) m_ReconstructParams.MinClusterSize += sliderValInt;
            ImGui::SameLine();
            ImGui::Text("%d", m_ReconstructParams.MinClusterSize);

            sliderValFloat = 0.f;
            ImGui::Text(" (1.0)");
            ImGui::SameLine();
            ImGui::SliderFloat("## Max Pln Dist", &sliderValFloat, -1.0f, 1.0f, "Max Pln Dist", ImGuiSliderFlags_AlwaysClamp);
            if (sliderValFloat != 0.f) m_ReconstructParams.MaxPlnDist += sliderValFloat;
            ImGui::SameLine();
            ImGui::Text("%.1f", m_ReconstructParams.MaxPlnDist);

            sliderValFloat = 0.f;
            ImGui::Text(" (5.0)");
            ImGui::SameLine();
            ImGui::SliderFloat("## Max Pln Angle", &sliderValFloat, -1.0f, 1.0f, "Max Pln Angle", ImGuiSliderFlags_AlwaysClamp);
            if (sliderValFloat != 0.f) m_ReconstructParams.MaxPlnAngle += sliderValFloat;
            ImGui::SameLine();
            ImGui::Text("%.1f", m_ReconstructParams.MaxPlnAngle);

            sliderValFloat = 0.f;
            ImGui::Text(" (1.1)");
            ImGui::SameLine();
            ImGui::SliderFloat("## AABB Scale Factor", &sliderValFloat, -1.0f, 1.0f, "AABB Scale Factor", ImGuiSliderFlags_AlwaysClamp);
            if (sliderValFloat != 0.f) m_ReconstructParams.AABBScaleFactor += sliderValFloat;
            ImGui::SameLine();
            ImGui::Text("%.1f", m_ReconstructParams.AABBScaleFactor);

            sliderValFloat = 0.f;
            ImGui::Text(" (1.0)");
            ImGui::SameLine();
            ImGui::SliderFloat("## Max Poly Dist", &sliderValFloat, -1.0f, 1.0f, "Max Poly Dist", ImGuiSliderFlags_AlwaysClamp);
            if (sliderValFloat != 0.f) m_ReconstructParams.MaxPolyDist += sliderValFloat;
            ImGui::SameLine();
            ImGui::Text("%.1f", m_ReconstructParams.MaxPolyDist);

            sliderValFloat = 0.f;
            ImGui::Text("(1e-5)");
            ImGui::SameLine();
            ImGui::SliderFloat("## Eps", &sliderValFloat, -1.0f, 1.0f, "Eps", ImGuiSliderFlags_AlwaysClamp);
            ImGui::PopItemWidth();
            if (sliderValFloat > 0.f) m_ReconstructParams.Eps *= 2;
            else if (sliderValFloat < 0.f) m_ReconstructParams.Eps /= 2;
            ImGui::SameLine();
            ImGui::Text("%.1e", m_ReconstructParams.Eps);


            ImGui::Text(" ");
            ImGui::Text(" ");
            ImGui::SameLine(ImGui::GetWindowWidth() - 180);
            if(ImGui::Button("Cancel", ImVec2(80, 0))){ m_IsReconstructing3D = false; }
            ImGui::SameLine();
            if(ImGui::Button("Confirm", ImVec2(80, 0))){
                if(strlen(m_ReconstructParams.TagMapPath) == 0 || strlen(m_ReconstructParams.ExportPath) == 0){
                    AIAC_ERROR("Path not selected.");
                } else {                    
                    // Reconstruct 3D
                    bool isReconstructed = AIAC_APP.GetLayer<AIAC::LayerSlam>()->Slam.Reconstruct3DModelAndExportPly(
                        m_ReconstructParams.TagMapPath,
                        m_ReconstructParams.ExportPath,
                        m_ReconstructParams.RadiusSearch,
                        m_ReconstructParams.CreaseAngleThreshold,
                        m_ReconstructParams.MinClusterSize,
                        m_ReconstructParams.AABBScaleFactor,
                        m_ReconstructParams.MaxPolyDist,
                        m_ReconstructParams.MaxPlnDist,
                        m_ReconstructParams.MaxPlnAngle,
                        m_ReconstructParams.Eps
                    );

                    // Load reconstructed 3D model
                    if(isReconstructed) AIAC_APP.GetLayer<AIAC::LayerModel>()->LoadScannedModel(m_ReconstructParams.ExportPath);
                    else AIAC_WARN("Reconstruction failed, skip loading the model");

                    // memset(m_ReconstructParams.TagMapPath, 0, PATH_BUF_SIZE);
                    // memset(m_ReconstructParams.ExportPath, 0, PATH_BUF_SIZE);
                    m_IsReconstructing3D = false;
                }
            }

        ImGui::End();
    }

    // FIXME: This should not be here, the LayerUI should only handle visualization, not logic
    void LayerUI::LoadReconstructParamsFromFile(const char *filePath){
        inih::INIReader reader(filePath);
        auto section = "Reconstruction Parameters";
        m_ReconstructParams.RadiusSearch = reader.Get<float>(section, "RadiusSearch");
        m_ReconstructParams.CreaseAngleThreshold = reader.Get<float>(section, "CreaseAngleThreshold");
        m_ReconstructParams.MinClusterSize = reader.Get<int>(section, "MinClusterSize");
        m_ReconstructParams.AABBScaleFactor = reader.Get<float>(section, "AABBScaleFactor");
        m_ReconstructParams.MaxPolyDist = reader.Get<float>(section, "MaxPolyDist");
        m_ReconstructParams.MaxPlnDist = reader.Get<float>(section, "MaxPlnDist");
        m_ReconstructParams.MaxPlnAngle = reader.Get<float>(section, "MaxPlnAngle");
        m_ReconstructParams.Eps = reader.Get<float>(section, "Eps");
    }

    void LayerUI::ShowSaveCamCalibFileDialog()
    {
        ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x * 0.8, ImGui::GetIO().DisplaySize.y * 0.75));
        ImGuiFileDialog::Instance()->OpenDialog("ChooseSaveCalibFilePath", "Choose calib file path", ".yml", ".");
        if (ImGuiFileDialog::Instance()->Display("ChooseSaveCalibFilePath"))
        {
            if (ImGuiFileDialog::Instance()->IsOk())
            {
                std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
                std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath();

                AIAC_INFO("Saving calib file to {0}", filePathName.c_str());
                AIAC_APP.GetLayer<AIAC::LayerCameraCalib>()->SetSaveFilename(filePathName);
            }
            ImGuiFileDialog::Instance()->Close();
            m_IsChoosingCamCalibFileSavePath = false;
        }
    }

    void LayerUI::OpenFileSelectDialog(const char* title, const char* fileExt, char *path, std::function<void()> callback) {
        ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x * 0.8, ImGui::GetIO().DisplaySize.y * 0.75));
        if (strlen(path) == 0) {
            ImGuiFileDialog::Instance()->OpenDialog("SelectFileDialog", title, fileExt, m_FileSelectDefaultPath);
        }
        else {
            char* pathBuf = new char[PATH_BUF_SIZE];
            memset(pathBuf, 0, PATH_BUF_SIZE);
            memcpy(pathBuf, path, strlen(path));
            ImGuiFileDialog::Instance()->OpenDialog("SelectFileDialog", title, fileExt, pathBuf);
            ImGuiFileDialog::Instance()->OpenDialog("SelectFileDialog", title, fileExt, path);
        }
        m_FileSelectionTargetBuf = path;
        m_FileSelectionCallback = callback;
    }

    void LayerUI::ShowFileSelectDialog(){
        if (ImGuiFileDialog::Instance()->Display("SelectFileDialog")) {
            if (ImGuiFileDialog::Instance()->IsOk()) {
                std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
                AIAC_INFO("Select: {0}", filePathName.c_str());
                memset(m_FileSelectionTargetBuf, 0, PATH_BUF_SIZE);
                memcpy(m_FileSelectionTargetBuf, filePathName.c_str(), filePathName.length());

                if(m_FileSelectionCallback) m_FileSelectionCallback();
            }
            ImGuiFileDialog::Instance()->Close();
        }
        m_FileSelectDefaultPath = ImGuiFileDialog::Instance()->GetCurrentPath();
        if (m_FileSelectDefaultPath[m_FileSelectDefaultPath.length()-1] != '/') {
            m_FileSelectDefaultPath += "/";
        };
    }

    void LayerUI::ShowLogRecorderUI() {
        ImGui::Begin("Log Recorder", m_IsOpen);
        if (!AIAC_APP.GetLayer<AIAC::LayerLogRecorder>()->IsRecording()){
//            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_GREEN);
            if(ImGui::Button("Start", ImVec2(-1, 40))){
                AIAC_APP.GetLayer<AIAC::LayerLogRecorder>()->StartRecording();
            }
//            ImGui::PopStyleColor();
        } else {
            ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_RED);
            if(ImGui::Button("Stop", ImVec2(-1, 40))){
                AIAC_APP.GetLayer<AIAC::LayerLogRecorder>()->StopRecording();
            }
            ImGui::PopStyleColor();

            if(AIAC_APP.GetLayer<AIAC::LayerLogRecorder>()->IsPaused()) {
                ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_YELLOW);
                if(ImGui::Button("Resume", ImVec2(-1, 40))){
                    AIAC_APP.GetLayer<AIAC::LayerLogRecorder>()->ResumeRecording();
                }
                ImGui::PopStyleColor();
            } else {
                ImGui::PushStyleColor(ImGuiCol_Button, AIAC_UI_GREY);
                if(ImGui::Button("Pause", ImVec2(-1, 40))) {
                    AIAC_APP.GetLayer<AIAC::LayerLogRecorder>()->PauseRecording();
                }
                ImGui::PopStyleColor();
            }
        }
        ImGui::End();
    }
}