diff --git a/Navigator/src/Navigator/Editor/SpectrumPanel.cpp b/Navigator/src/Navigator/Editor/SpectrumPanel.cpp index 4adcca3..520c9a5 100644 --- a/Navigator/src/Navigator/Editor/SpectrumPanel.cpp +++ b/Navigator/src/Navigator/Editor/SpectrumPanel.cpp @@ -1,12 +1,23 @@ #include "SpectrumPanel.h" -#include "implot.h" #include "misc/cpp/imgui_stdlib.h" #include "IconsFontAwesome5.h" namespace Navigator { + std::string GenerateStatString(const std::string& name, const StatResults& results, bool is2D = true) + { + std::stringstream stream; + stream << "Region: " << name << "\n" << "Integral: " << results.integral << "\n"; + if (results.integral == 0) + return stream.str(); + stream << "Centroid X: " << results.cent_x << " Std. Dev. X: " << results.sigma_x << " FWHM X: " << 2.355 * results.sigma_x << "\n"; + if(is2D) + stream << "Centroid Y: " << results.cent_y << " Std. Dev. Y: " << results.sigma_y << " FWHM Y: " << 2.355 * results.sigma_y << "\n"; + return stream.str(); + } + SpectrumPanel::SpectrumPanel() : - m_zoomedFlag(false), m_cutModeFlag(false), m_zoomedGram(), m_totalSlots(1) + m_zoomedFlag(false), m_cutModeFlag(false), m_zoomedGram(), m_nRegions(0), m_totalSlots(1) { m_tableSizes[0] = 1; m_tableSizes[1] = 1; } @@ -20,43 +31,21 @@ namespace Navigator { //CutMap& cutMap = CutMap::GetInstance(); static bool acceptCutFlag = false; bool result = false; + //static std::string selectedRegion = ""; if (ImGui::Begin("Active View")) { if (histoList.size() > 0) { if (m_zoomedFlag && m_zoomedGram.name != "") { - if(ImGui::Button(ICON_FA_CUT " Draw Cut")) - { - m_newCutParams = CutParams(); - m_newCutX.resize(0); - m_newCutY.resize(0); - ImGui::OpenPopup(ICON_FA_CUT " New Cut Dialog"); - } - if(ImGui::BeginPopupModal(ICON_FA_CUT " New Cut Dialog")) - { - m_newCutParams.x_par = m_zoomedGram.x_par; - m_newCutParams.y_par = m_zoomedGram.y_par; - ImGui::InputText("Cut Name", &m_newCutParams.name); - ImGui::BulletText("%s", ("X Parameter: "+m_newCutParams.x_par).c_str()); - ImGui::BulletText("%s", ("Y Parameter: "+m_newCutParams.y_par).c_str()); - if(ImGui::Button("Accept & Draw")) - { - m_cutModeFlag = true; - ImGui::CloseCurrentPopup(); - } - ImGui::SameLine(); - if(ImGui::Button("Cancel")) - { - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } + RenderCutButton(); ImGui::SameLine(); if(ImGui::Button("Clear")) { HistogramMap::GetInstance().ClearHistogram(m_zoomedGram.name); } + ImGui::SameLine(); + RenderRemoveRegionButton(); if (ImPlot::BeginPlot(m_zoomedGram.name.c_str(), ImVec2(-1, -1))) { @@ -96,6 +85,26 @@ namespace Navigator { } ImPlot::PlotLine(m_newCutParams.name.c_str(), m_newCutX.data(), m_newCutY.data(), m_newCutX.size()); } + + if (ImPlot::IsPlotSelected()) { + auto& select = ImPlot::GetPlotSelection(); + if (ImGui::IsMouseClicked(ImPlot::GetInputMap().SelectCancel)) { + ImPlot::CancelPlotSelection(); + m_integralRegions.emplace_back(select, "integralRegion_"+std::to_string(m_nRegions), m_zoomedGram.name); + m_nRegions++; + } + } + for (size_t i = 0; i < m_integralRegions.size(); i++) + { + auto& region = m_integralRegions[i]; + if (m_zoomedGram.name == region.histogram_name) + { + ImPlot::DragRect(i, ®ion.region.X.Min, ®ion.region.Y.Min, ®ion.region.X.Max, ®ion.region.Y.Max, ImVec4(1, 0, 1, 1)); + StatResults results = HistogramMap::GetInstance().AnalyzeHistogramRegion(m_zoomedGram.name, region.region); + ImPlot::PlotText(GenerateStatString(region.name, results, m_zoomedGram.y_par != "None").c_str(), (region.region.X.Max + region.region.X.Min) * 0.5, + (region.region.Y.Min + region.region.Y.Max) * 0.5); + } + } ImPlot::EndPlot(); } if (acceptCutFlag) @@ -181,6 +190,15 @@ namespace Navigator { m_zoomedFlag = true; m_zoomedGram = spec; } + + for (size_t i = 0; i < m_integralRegions.size(); i++) + { + auto& region = m_integralRegions[i]; + if (spec.name == region.histogram_name) + { + ImPlot::DragRect(i, ®ion.region.X.Min, ®ion.region.Y.Min, ®ion.region.X.Max, ®ion.region.Y.Max, ImVec4(1, 0, 1, 1)); + } + } ImPlot::EndPlot(); } i++; @@ -193,4 +211,82 @@ namespace Navigator { } return result; } + + void SpectrumPanel::RenderCutButton() + { + if (ImGui::Button(ICON_FA_CUT " Draw Cut")) + { + m_newCutParams = CutParams(); + m_newCutX.resize(0); + m_newCutY.resize(0); + ImGui::OpenPopup(ICON_FA_CUT " New Cut Dialog"); + } + if (ImGui::BeginPopupModal(ICON_FA_CUT " New Cut Dialog")) + { + m_newCutParams.x_par = m_zoomedGram.x_par; + m_newCutParams.y_par = m_zoomedGram.y_par; + ImGui::InputText("Cut Name", &m_newCutParams.name); + ImGui::BulletText("%s", ("X Parameter: " + m_newCutParams.x_par).c_str()); + ImGui::BulletText("%s", ("Y Parameter: " + m_newCutParams.y_par).c_str()); + if (ImGui::Button("Accept & Draw")) + { + m_cutModeFlag = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + } + + void SpectrumPanel::RenderRemoveRegionButton() + { + static std::string selectedRegion = ""; + if (ImGui::Button("Delete Region")) + { + selectedRegion = ""; + ImGui::OpenPopup("Remove Integral Region"); + } + if (ImGui::BeginPopupModal("Remove Integral Region")) + { + if (ImGui::BeginCombo("Region", selectedRegion.c_str())) + { + for (auto& region : m_integralRegions) + { + if (region.histogram_name == m_zoomedGram.name) + { + if (ImGui::Selectable(region.name.c_str(), region.name == selectedRegion, ImGuiSelectableFlags_DontClosePopups)) + selectedRegion = region.name; + } + } + ImGui::EndCombo(); + } + if (ImGui::Button("Ok")) + { + RemoveSelectedRegion(selectedRegion); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + } + + void SpectrumPanel::RemoveSelectedRegion(const std::string& region) + { + for (size_t i=0; i m_selectedGrams; + std::vector m_integralRegions; bool m_zoomedFlag; bool m_cutModeFlag; HistogramParameters m_zoomedGram; int m_tableSizes[2]; int m_totalSlots; + int m_nRegions; CutParams m_newCutParams; std::vector m_newCutX; std::vector m_newCutY; diff --git a/Navigator/src/Navigator/Histogram.cpp b/Navigator/src/Navigator/Histogram.cpp index d42a348..e922d76 100644 --- a/Navigator/src/Navigator/Histogram.cpp +++ b/Navigator/src/Navigator/Histogram.cpp @@ -79,6 +79,35 @@ namespace Navigator { m_binCounts[i] = 0; } + StatResults Histogram1D::AnalyzeRegion(double x_min, double x_max, double y_min, double y_max) + { + int bin_min, bin_max; + StatResults results; + + if (x_min <= m_params.min_x) + bin_min = 0; + else + bin_min = int((x_min - m_params.min_x) / (m_binWidth)); + + if (x_max >= m_params.max_x) + bin_max = m_params.nbins_x - 1; + else + bin_max = int((x_max - m_params.min_x) / (m_binWidth)); + + for (int i = bin_min; i <= bin_max; i++) + { + results.integral += m_binCounts[i]; + results.cent_x += m_binCounts[i] * (m_params.min_x + m_binWidth * i); + } + if (results.integral == 0) + return results; + + results.cent_x /= double(results.integral); + for (int i = bin_min; i <= bin_max; i++) + results.sigma_x += m_binCounts[i] * ((m_params.min_x + m_binWidth * i) - results.cent_x) * ((m_params.min_x + m_binWidth * i) - results.cent_x); + results.sigma_x = std::sqrt(results.sigma_x / (results.integral - 1)); + return results; + } /* 2D Histogram class @@ -158,4 +187,64 @@ namespace Navigator { m_binCounts[i] = 0; m_maxBinContent = 0; } + + StatResults Histogram2D::AnalyzeRegion(double x_min, double x_max, double y_min, double y_max) + { + int xbin_min, xbin_max, ybin_min, ybin_max; + int curbin; + + StatResults results; + + if (x_min <= m_params.min_x) + xbin_min = 0; + else + xbin_min = int((x_min - m_params.min_x) / (m_binWidthX)); + + if (x_max >= m_params.max_x) + xbin_max = m_params.nbins_x - 1; + else + xbin_max = int((x_max - m_params.min_x) / (m_binWidthX)); + + if (y_min <= m_params.min_y) + ybin_max = m_params.nbins_y - 1; + else + ybin_max = int((m_params.max_y - y_min)) / m_binWidthY; + + if (y_max >= m_params.max_y) + ybin_min = 0; + else + ybin_min = int((m_params.max_y - y_max)) / m_binWidthY; + + for (int y = ybin_min; y <= ybin_max; y++) + { + for (int x = xbin_min; x <= xbin_max; x++) + { + curbin = y * m_params.nbins_x + x; + results.integral += m_binCounts[curbin]; + results.cent_x += m_binCounts[curbin] * (m_params.min_x + m_binWidthX * x); + results.cent_y += m_binCounts[curbin] * (m_params.max_y - m_binWidthY * y); + } + } + + if (results.integral == 0) + return results; + + results.cent_x /= results.integral; + results.cent_y /= results.integral; + for (int y = ybin_min; y <= ybin_max; y++) + { + for (int x = xbin_min; x <= xbin_max; x++) + { + curbin = y * m_params.nbins_x + x; + results.sigma_x += m_binCounts[curbin] * ((m_params.min_x + m_binWidthX * x) - results.cent_x) * ((m_params.min_x + m_binWidthX * x) - results.cent_x); + results.sigma_y += m_binCounts[curbin] * ((m_params.max_y - m_binWidthY * y) - results.cent_y) * ((m_params.max_y - m_binWidthY * y) - results.cent_y); + } + } + + results.sigma_x = std::sqrt(results.sigma_x / (results.integral - 1)); + results.sigma_y = std::sqrt(results.sigma_y / (results.integral - 1)); + + return results; + } + } diff --git a/Navigator/src/Navigator/Histogram.h b/Navigator/src/Navigator/Histogram.h index ee9bf5b..1a9f7fb 100644 --- a/Navigator/src/Navigator/Histogram.h +++ b/Navigator/src/Navigator/Histogram.h @@ -5,6 +5,15 @@ namespace Navigator { + struct NAV_API StatResults + { + int integral = 0.0; + double cent_x = 0.0; + double cent_y = 0.0; + double sigma_x = 0.0; + double sigma_y = 0.0; + }; + struct NAV_API HistogramParameters { HistogramParameters() {} @@ -48,6 +57,7 @@ namespace Navigator { virtual void FillData() { NAV_WARN("Trying to fill a default histogram!"); } virtual void Draw() {} virtual void ClearData() {} + virtual StatResults AnalyzeRegion(double x_min, double x_max, double y_min = 0.0, double y_max = 0.0) { return StatResults(); } inline virtual bool Is1D() const { return false; } inline virtual bool Is2D() const { return false; } inline HistogramParameters& GetParameters() { return m_params; } @@ -70,6 +80,7 @@ namespace Navigator { virtual void FillData() override; virtual void Draw() override; virtual void ClearData() override; + virtual StatResults AnalyzeRegion(double x_min, double x_max, double y_min = 0.0, double y_max = 0.0) override; inline virtual bool Is1D() const override { return true; } inline virtual bool Is2D() const override { return false; } @@ -90,6 +101,7 @@ namespace Navigator { virtual void FillData() override; virtual void Draw() override; virtual void ClearData() override; + virtual StatResults AnalyzeRegion(double x_min, double x_max, double y_min = 0.0, double y_max = 0.0) override; inline virtual bool Is1D() const override { return false; } inline virtual bool Is2D() const override { return true; } diff --git a/Navigator/src/Navigator/HistogramMap.cpp b/Navigator/src/Navigator/HistogramMap.cpp index 0a775bf..8447afe 100644 --- a/Navigator/src/Navigator/HistogramMap.cpp +++ b/Navigator/src/Navigator/HistogramMap.cpp @@ -1,6 +1,8 @@ #include "HistogramMap.h" #include "ParameterMap.h" +#include "implot.h" + namespace Navigator { HistogramMap* HistogramMap::s_instance = new HistogramMap(); @@ -100,6 +102,16 @@ namespace Navigator { return m_nullResult; } + StatResults HistogramMap::AnalyzeHistogramRegion(const std::string& name, const ImPlotRect& region) + { + std::lock_guard guard(m_histoMutex); + auto iter = m_map.find(name); + if (iter != m_map.end()) + return iter->second->AnalyzeRegion(region.X.Min, region.X.Max, region.Y.Min, region.Y.Max); + else + return StatResults(); + } + std::vector HistogramMap::GetListOfHistograms() { std::lock_guard guard(m_histoMutex); diff --git a/Navigator/src/Navigator/HistogramMap.h b/Navigator/src/Navigator/HistogramMap.h index ad3dffb..04da155 100644 --- a/Navigator/src/Navigator/HistogramMap.h +++ b/Navigator/src/Navigator/HistogramMap.h @@ -6,6 +6,8 @@ #include +struct ImPlotRect; + namespace Navigator { class NAV_API HistogramMap @@ -19,7 +21,6 @@ namespace Navigator { void AddHistogram(const HistogramParameters& params); void RemoveHistogram(const std::string& name); - void AddCutToHistogramDraw(const std::string& cutname, const std::string& histoname); void AddCutToHistogramApplied(const std::string& cutname, const std::string& histoname); @@ -33,6 +34,7 @@ namespace Navigator { void DrawHistogram(const std::string& name); const HistogramParameters& GetHistogramParams(const std::string& name); + StatResults AnalyzeHistogramRegion(const std::string& name, const ImPlotRect& region); std::vector GetListOfHistograms(); diff --git a/Navigator/src/Navigator/ImGui/ImGuiLayer.cpp b/Navigator/src/Navigator/ImGui/ImGuiLayer.cpp index a9d9e2d..4fe9e52 100644 --- a/Navigator/src/Navigator/ImGui/ImGuiLayer.cpp +++ b/Navigator/src/Navigator/ImGui/ImGuiLayer.cpp @@ -34,7 +34,9 @@ namespace Navigator { ImGuiIO& io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; - io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; + //Viewports are real wonky on Linux and sometimes on Mac + // Can currently cause assertion failure on checking number of monitors in ImGui sanity checks. + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; ImGui::StyleColorsDark(); ImPlot::StyleColorsDark(); @@ -54,7 +56,7 @@ namespace Navigator { config.GlyphMinAdvanceX = 13.0f; // Use if you want to make the icon monospaced config.PixelSnapH = true; static const ImWchar icon_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; - io.Fonts->AddFontFromFileTTF("fonts/fa-solid-900.ttf", 15.0f, &config, icon_ranges); + io.Fonts->AddFontFromFileTTF("fonts/fa-solid-900.ttf", 13.0f, &config, icon_ranges); Application& app = Application::Get(); GLFWwindow* window = static_cast(app.GetWindow().GetNativeWindow());