// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // Associated header // #include "k4aimugraph.h" // System headers // #include #include #include // Library headers // #include "k4aimgui_all.h" // Project headers // #include "k4aimguiextensions.h" #include "k4aviewererrormanager.h" #include "k4aviewersettingsmanager.h" #include "k4awindowsizehelpers.h" using namespace k4aviewer; namespace { std::string GetScaleTitle(const std::string &title) { // We need to have different widget names for each instance or we'll // get two copies of the same widget that modify the same variable. // // ImGui hides everything in the title past the ##, so this isn't user-visible, // but it lets us disambiguate the slider widgets. // return std::string("##") + title; } constexpr float MinHeight = 50.f; } // namespace K4AImuGraph::K4AImuGraph(std::string &&title, std::string &&xLabel, std::string &&yLabel, std::string &&zLabel, std::string &&units, const float minRange, const float maxRange, const float defaultRange) : m_title(std::move(title)), m_xLabel(std::move(xLabel)), m_yLabel(std::move(yLabel)), m_zLabel(std::move(zLabel)), m_units(std::move(units)), m_minRange(minRange), m_maxRange(maxRange), m_currentRange(-defaultRange), m_scaleTitle(GetScaleTitle(m_title)) { } void K4AImuGraph::Show(ImVec2 maxSize, const K4AImuGraphData::AccumulatorArray &graphData, int graphFrontIdx, uint64_t timestamp) { // One line for the graph type (accelerometer/gyro), one for the timestamp // const float textHeight = 2 * ImGui::GetTextLineHeightWithSpacing(); ImGuiStyle &style = ImGui::GetStyle(); ImVec2 sliderSize; sliderSize.x = GetStandardVerticalSliderWidth(); sliderSize.y = maxSize.y - textHeight; sliderSize.y = std::max(MinHeight, sliderSize.y); ImVec2 graphSize; graphSize.x = maxSize.x; graphSize.x -= sliderSize.x; graphSize.x -= 2 * style.ItemSpacing.x; constexpr int graphCount = 3; graphSize.y = sliderSize.y; graphSize.y -= (graphCount - 1) * style.ItemSpacing.y; graphSize.y /= graphCount; ImGui::BeginGroup(); ImGui::Text("%s", m_title.c_str()); ImGui::Text("Time (us): %llu", static_cast(timestamp)); // We use negative min/max ranges to reverse the direction of the slider, which makes it // grow when you drag up, which is a bit more intuitive. // ImGuiExtensions::K4AVSliderFloat(m_scaleTitle.c_str(), sliderSize, &m_currentRange, -m_maxRange, -m_minRange, "Scale"); ImGui::SameLine(); ImGui::BeginGroup(); PlotGraph(m_xLabel.c_str(), graphSize, graphData, graphFrontIdx, 0); PlotGraph(m_yLabel.c_str(), graphSize, graphData, graphFrontIdx, 1); PlotGraph(m_zLabel.c_str(), graphSize, graphData, graphFrontIdx, 2); ImGui::EndGroup(); ImGui::EndGroup(); } void K4AImuGraph::PlotGraph(const char *name, ImVec2 graphSize, const K4AImuGraphData::AccumulatorArray &graphData, int graphFrontIdx, int offset) { std::stringstream nameBuilder; nameBuilder << "##" << name; const float currentData = graphData[(static_cast(graphFrontIdx) + graphData.size() - 1) % graphData.size()].v[offset]; std::string label; if (K4AViewerSettingsManager::Instance().GetViewerOption(ViewerOption::ShowInfoPane)) { std::stringstream labelBuilder; labelBuilder << name << ": "; // We want to keep the same width for the number so it doesn't // jump around and the decimal point aligns across graphs // // Add padding if there's no negative sign // if (currentData >= 0) { labelBuilder << " "; } // Pad assuming a max of 3 digits over the decimal point // if (std::log10(std::abs(currentData)) < 1) { labelBuilder << " "; } if (std::log10(std::abs(currentData)) < 2) { labelBuilder << " "; } labelBuilder << std::fixed << std::setfill('0') << std::setw(5) << currentData << " " << m_units; label = labelBuilder.str(); } struct SelectorData { const k4a_float3_t *data; int offset; } selectorData; selectorData.data = &graphData[0]; selectorData.offset = offset; ImGui::PlotLines(nameBuilder.str().c_str(), [](void *data, int idx) { auto *sd = reinterpret_cast(data); return sd->data[idx].v[sd->offset]; }, &selectorData, static_cast(graphData.size()), graphFrontIdx, label.c_str(), m_currentRange, -m_currentRange, graphSize); }