From ab590518642fbed4f8925b3f156a329a55374df1 Mon Sep 17 00:00:00 2001 From: epezent Date: Thu, 3 Dec 2020 22:57:20 -0600 Subject: [PATCH] prototyping tooltip support --- implot.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++++++++ implot.h | 11 +++++++ implot_demo.cpp | 12 ++++++-- 3 files changed, 97 insertions(+), 3 deletions(-) diff --git a/implot.cpp b/implot.cpp index fce72b3..bbd640d 100644 --- a/implot.cpp +++ b/implot.cpp @@ -2842,6 +2842,83 @@ bool DragPoint(const char* id, double* x, double* y, bool show_label, const ImVe return dragging; } +//----------------------------------------------------------------------------- +// TOOLTIPS +//----------------------------------------------------------------------------- + +template +T GetClosest(T val1, T val2, T target) { + if (target - val1 >= val2 - target) + return val2; + else + return val1; +} + +template +int FindClosestIdx(const T* arr, int n, T target) { + if (target <= arr[0]) + return 0; + if (target >= arr[n - 1]) + return n - 1; + int i = 0, j = n, mid = 0; + while (i < j) { + mid = (i + j) / 2; + if (arr[mid] == target) + return mid; + if (target < arr[mid]) { + if (mid > 0 && target > arr[mid - 1]) + return (target - arr[mid - 1] >= arr[mid] - target) ? mid : mid - 1; + j = mid; + } + else { + if (mid < n - 1 && target < arr[mid + 1]) + return (target - arr[mid] >= arr[mid+1] - target) ? mid + 1 : mid; + i = mid + 1; + } + } + return mid; +} + +// bool BeginTooltip(const double* xs_mono, const double* ys, int* idx_out = NULL); + + +bool BeginTooltipX(const double* xs_mono, const double* ys, int count, int* idx_out) { + if (ImPlot::IsPlotHovered()) { + double mouse_x = ImPlot::GetPlotMousePos().x; + if (mouse_x < (double)xs_mono[0] || mouse_x > (double)xs_mono[count-1]) + return false; + const int idx = FindClosestIdx(xs_mono, count, mouse_x); + PushPlotClipRect(); + ImVec2 pos = PlotToPixels(xs_mono[idx], ys[idx]); + ImVec4 col = GetLastItemColor(); + GetPlotDrawList()->AddCircleFilled(pos, 6, ImGui::ColorConvertFloat4ToU32(col)); + PopPlotClipRect(); + if (idx_out == NULL) { + ImGui::BeginTooltip(); + ImGui::PushStyleColor(ImGuiCol_Text, col); + ImGui::Text("%.3f,%.3f",xs_mono[idx], ys[idx]); + ImGui::PopStyleColor(); + ImGui::EndTooltip(); + } + else { + *idx_out = idx; + } + return true; + } + return false; +} + +void EndTooltip() { + +} + +// bool BeginTooltipY(const double* xs, const double* ys_mono, int* idx_out = NULL); + + +//----------------------------------------------------------------------------- +// LEGEND UTILS +//----------------------------------------------------------------------------- + void SetLegendLocation(ImPlotLocation location, ImPlotOrientation orientation, bool outside) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetLegendLocation() needs to be called between BeginPlot() and EndPlot()!"); diff --git a/implot.h b/implot.h index 6efd30b..d905846 100644 --- a/implot.h +++ b/implot.h @@ -531,6 +531,17 @@ IMPLOT_API bool DragLineY(const char* id, double* y_value, bool show_label = tru // Shows a draggable point at x,y. #col defaults to ImGuiCol_Text. IMPLOT_API bool DragPoint(const char* id, double* x, double* y, bool show_label = true, const ImVec4& col = IMPLOT_AUTO_COL, float radius = 4); +// Facilitates adding hover tooltips to plots. If #idx_out is NULL, a default tooltip will be rendered. Otherwise, the value will be set to the hovered idx, +// and it becomes YOUR responsibility to render tooltip information using regular ImGui functions. You MUST make a matching call to EndTooltip if these +// functions return true! +IMPLOT_API bool BeginTooltip(const double* xs_mono, const double* ys, int count, int* idx_out = NULL); +// This variant is optimized to search along monotonically increasing x values (this is the one you want for time-series plots). +IMPLOT_API bool BeginTooltipX(const double* xs_mono, const double* ys, int count, int* idx_out = NULL); +// This variant is optimized to search along monotonically increasing y values. +IMPLOT_API bool BeginTooltipY(const double* xs, const double* ys_mono, int count, int* idx_out = NULL); +// Only call EndTooltip if BeginTooltip returns true! e.g. if (BeginTooltipX(...)) { ... EndTooltip(); } +IMPLOT_API void EndTooltip(); + //----------------------------------------------------------------------------- // Legend Utils and Tools //----------------------------------------------------------------------------- diff --git a/implot_demo.cpp b/implot_demo.cpp index 042dabc..23bbfb6 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -233,10 +233,10 @@ void ShowDemoWindow(bool* p_open) { } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Line Plots")) { - static float xs1[1001], ys1[1001]; + static double xs1[1001], ys1[1001]; for (int i = 0; i < 1001; ++i) { xs1[i] = i * 0.001f; - ys1[i] = 0.5f + 0.5f * sinf(50 * (xs1[i] + (float)DEMO_TIME / 10)); + ys1[i] = 0.5 + 0.5 * sin(50 * (xs1[i] + (double)DEMO_TIME / 10)); } static double xs2[11], ys2[11]; for (int i = 0; i < 11; ++i) { @@ -246,8 +246,12 @@ void ShowDemoWindow(bool* p_open) { ImGui::BulletText("Anti-aliasing can be enabled from the plot's context menu (see Help)."); if (ImPlot::BeginPlot("Line Plot", "x", "f(x)")) { ImPlot::PlotLine("sin(x)", xs1, ys1, 1001); + if (ImPlot::BeginTooltipX(xs1,ys1,1001)) + ImPlot::EndTooltip(); ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); ImPlot::PlotLine("x^2", xs2, ys2, 11); + if (ImPlot::BeginTooltipX(xs2,ys2,11)) + ImPlot::EndTooltip(); ImPlot::EndPlot(); } } @@ -314,7 +318,7 @@ void ShowDemoWindow(bool* p_open) { //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Scatter Plots")) { srand(0); - static float xs1[100], ys1[100]; + static double xs1[100], ys1[100]; for (int i = 0; i < 100; ++i) { xs1[i] = i * 0.01f; ys1[i] = xs1[i] + 0.1f * ((float)rand() / (float)RAND_MAX); @@ -327,6 +331,8 @@ void ShowDemoWindow(bool* p_open) { if (ImPlot::BeginPlot("Scatter Plot", NULL, NULL)) { ImPlot::PlotScatter("Data 1", xs1, ys1, 100); + if (ImPlot::BeginTooltipX(xs1,ys1,100)) + ImPlot::EndTooltip(); ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); ImPlot::SetNextMarkerStyle(ImPlotMarker_Square, 6, ImVec4(0,1,0,0.5f), IMPLOT_AUTO, ImVec4(0,1,0,1)); ImPlot::PlotScatter("Data 2", xs2, ys2, 50);