/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #include "ImGuiPlotTwoLines.hpp" #include #include namespace ImGui { void PlotTwoLines(const char* label, float (*getter1)(void* data, int idx), void* data1, const char *tooltipPrefix1, float (*getter2)(void* data, int idx), void* data2, const char *tooltipPrefix2, int values_count, float scale_min, float scale_max, ImVec2 graph_size) { ImGuiContext& g = *ImGui::GetCurrentContext(); ImGuiWindow* window = ImGui::GetCurrentWindow(); if (window->SkipItems) return; const ImGuiStyle& style = g.Style; ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); if (graph_size.x == 0.0f) graph_size.x = ImGui::CalcItemWidth(); if (graph_size.y == 0.0f) graph_size.y = label_size.y + (style.FramePadding.y * 2); ImRect frame_bb(window->DC.CursorPos, ImVec2(window->DC.CursorPos.x + graph_size.x, window->DC.CursorPos.y + graph_size.y)); ImRect inner_bb(ImVec2(frame_bb.Min.x + style.FramePadding.x, frame_bb.Min.y + style.FramePadding.y), ImVec2(frame_bb.Max.x - style.FramePadding.x, frame_bb.Max.y - style.FramePadding.y)); ImRect total_bb(frame_bb.Min, ImVec2(frame_bb.Max.x + (label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f), frame_bb.Max.y)); ImGui::ItemSize(total_bb, style.FramePadding.y); if (!ImGui::ItemAdd(total_bb, 0)) return; // Determine scale from inputs if not specified if (scale_min == FLT_MAX || scale_max == FLT_MAX) { float v1_min = FLT_MAX, v1_max = -FLT_MAX; for (int i = 0; i < values_count; i++) { float v = getter1(data1, i); if (v != v) // Ignore NaN values continue; v1_min = ImMin(v1_min, v); v1_max = ImMax(v1_max, v); } float v2_min = FLT_MAX, v2_max = -FLT_MAX; for (int i = 0; i < values_count; i++) { float v = getter2(data2, i); if (v != v) // Ignore NaN values continue; v2_min = ImMin(v2_min, v); v2_max = ImMax(v2_max, v); } if (scale_min == FLT_MAX) scale_min = ImMin(v1_min, v2_min); if (scale_max == FLT_MAX) scale_max = ImMax(v1_max, v2_max); } ImGui::RenderFrame(frame_bb.Min, frame_bb.Max, ImGui::GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); int res_w = ImMin((int)graph_size.x, values_count); // Number of samples if (res_w <= 0) return; // Tooltip on hover int v_hovered = -1; if (ImGui::IsItemHovered()) { const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f); const int v_idx = (int)(t * values_count); IM_ASSERT(v_idx >= 0 && v_idx < values_count); const float v0 = getter1(data1, v_idx); const float v1 = getter2(data2, v_idx); ImGui::SetTooltip("%d: (%s: %8.4g, %s: %8.4g)", v_idx, tooltipPrefix1, v0, tooltipPrefix2, v1); v_hovered = v_idx; } const float t_step = 1.0f / res_w; ImDrawList* draw_list = ImGui::GetWindowDrawList(); draw_list->PushClipRect(inner_bb.Min, inner_bb.Max, true); ImColor col1 = ImColor(255, 100, 100); ImColor col2 = ImColor(100, 255, 100); // Plot first line for (int n = 0; n < res_w; n++) { const float t0 = t_step * (n + 0); const float t1 = t_step * (n + 1); const int v_idx0 = (int)(t0 * values_count); int v_idx1 = (int)(t1 * values_count); if(v_idx1 >= values_count) v_idx1 = values_count - 1; IM_ASSERT(v_idx0 >= 0 && v_idx0 < values_count); IM_ASSERT(v_idx1 >= 0 && v_idx1 < values_count); const float v0 = getter1(data1, v_idx0); const float v1 = getter1(data1, v_idx1); const float x0 = ImLerp(inner_bb.Min.x, inner_bb.Max.x, t0); const float x1 = ImLerp(inner_bb.Min.x, inner_bb.Max.x, t1); const float y0 = ImLerp(inner_bb.Max.y, inner_bb.Min.y, (v0 - scale_min) / (scale_max - scale_min)); const float y1 = ImLerp(inner_bb.Max.y, inner_bb.Min.y, (v1 - scale_min) / (scale_max - scale_min)); draw_list->AddLine(ImVec2(x0, y0), ImVec2(x1, y1), col1); } // Plot second line for (int n = 0; n < res_w; n++) { const float t0 = t_step * (n + 0); const float t1 = t_step * (n + 1); const int v_idx0 = (int)(t0 * values_count); int v_idx1 = (int)(t1 * values_count); if(v_idx1 >= values_count) v_idx1 = values_count - 1; IM_ASSERT(v_idx0 >= 0 && v_idx0 < values_count); IM_ASSERT(v_idx1 >= 0 && v_idx1 < values_count); const float v0 = getter2(data2, v_idx0); const float v1 = getter2(data2, v_idx1); const float x0 = ImLerp(inner_bb.Min.x, inner_bb.Max.x, t0); const float x1 = ImLerp(inner_bb.Min.x, inner_bb.Max.x, t1); const float y0 = ImLerp(inner_bb.Max.y, inner_bb.Min.y, (v0 - scale_min) / (scale_max - scale_min)); const float y1 = ImLerp(inner_bb.Max.y, inner_bb.Min.y, (v1 - scale_min) / (scale_max - scale_min)); draw_list->AddLine(ImVec2(x0, y0), ImVec2(x1, y1), col2); } draw_list->PopClipRect(); if (label_size.x > 0.0f) ImGui::RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); } }