From 71e38ad7704ae6925f3bbcdd08e8681c747c2dd0 Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Mon, 1 Jun 2020 17:33:01 -0500 Subject: [PATCH 01/13] start heatmap dev --- implot.cpp | 10 ++++++++++ implot.h | 3 +++ 2 files changed, 13 insertions(+) diff --git a/implot.cpp b/implot.cpp index 2b042ff..645de36 100644 --- a/implot.cpp +++ b/implot.cpp @@ -2822,6 +2822,16 @@ void PlotPieChart(const char** label_ids, double* values, int count, double x, d return PlotPieChartEx(label_ids, values, count, x, y, radius, show_percents, angle0); } +//----------------------------------------------------------------------------- +// PLOT HEATMAP +//----------------------------------------------------------------------------- + +void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, double scale_min, double scale_max, bool show_labels) { + ImPlotItem* item = RegisterItem(label_id); + if (!item->Show) + return; +} + //----------------------------------------------------------------------------- // PLOT DIGITAL //----------------------------------------------------------------------------- diff --git a/implot.h b/implot.h index 0e48723..c4abdbf 100644 --- a/implot.h +++ b/implot.h @@ -226,6 +226,9 @@ void PlotErrorBars(const char* label_id, const double* xs, const double* ys, con void PlotPieChart(const char** label_ids, float* values, int count, float x, float y, float radius, bool show_percents = true, float angle0 = 90); void PlotPieChart(const char** label_ids, double* values, int count, double x, double y, double radius, bool show_percents = true, double angle0 = 90); +// Plots a 2D heatmap chart. +void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, double scale_min, double scale_max, bool show_labels = true); + // Plots digital data. void PlotDigital(const char* label_id, const float* xs, const float* ys, int count, int offset = 0, int stride = sizeof(float)); void PlotDigital(const char* label_id, const double* xs, const double* ys, int count, int offset = 0, int stride = sizeof(double)); From 468ffe9ead7382150c98e34537db30cf94986b05 Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Mon, 1 Jun 2020 22:14:22 -0500 Subject: [PATCH 02/13] initial heatmap prototype working --- implot.cpp | 167 +++++++++++++++++++++++++++++++++++++++--------- implot.h | 53 ++++++++++----- implot_demo.cpp | 43 +++++++++++-- 3 files changed, 213 insertions(+), 50 deletions(-) diff --git a/implot.cpp b/implot.cpp index 484e97e..6e21eb3 100644 --- a/implot.cpp +++ b/implot.cpp @@ -31,6 +31,7 @@ Below is a change-log of API breaking changes only. If you are using one of the When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot files. You can read releases logs https://github.com/epezent/implot/releases for more details. +- 2020/06/01 (0.3) - SetPalette/RestorePalette were changed to SetColormap/RestoreColormap for consistency with other plotting libraries. - 2020/05/31 (0.3) - Plot functions taking custom ImVec2* getters were removed. Use the ImPlotPoint* getter versions instead. - 2020/05/29 (0.3) - The signature of ImPlotLimits::Contains was changed to take two doubles instead of ImVec2 - 2020/05/16 (0.2) - All plotting functions were reverted to being prefixed with "Plot" to maintain a consistent VerbNoun style. `Plot` was split into `PlotLine` @@ -152,8 +153,9 @@ inline void FlipFlag(TSet& set, TFlag flag) { HasFlag(set, flag) ? set &= ~flag : set |= flag; } -/// Linearly remaps float x from [x0 x1] to [y0 y1]. -inline float Remap(float x, float x0, float x1, float y0, float y1) { +/// Linearly remaps x from [x0 x1] to [y0 y1]. +template +inline T Remap(T x, T x0, T x1, T y0, T y1) { return y0 + (x - x0) * (y1 - y0) / (x1 - x0); } @@ -334,7 +336,7 @@ struct ImPlotContext { ImPlotContext() : RenderX(), RenderY() { CurrentPlot = NULL; FitThisFrame = FitX = false; - RestorePalette(); + RestoreColormap(); } /// ALl Plots @@ -385,7 +387,7 @@ struct ImPlotContext { // Mouse pos ImPlotPoint LastMousePos[MAX_Y_AXES]; // Style - ImVector ColorMap; + ImVector Colormap; ImPlotStyle Style; ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() @@ -404,7 +406,7 @@ static ImPlotContext gp; /// Returns the next unused default plot color ImVec4 NextColor() { - ImVec4 col = gp.ColorMap[gp.CurrentPlot->ColorIdx % gp.ColorMap.size()]; + ImVec4 col = gp.Colormap[gp.CurrentPlot->ColorIdx % gp.Colormap.size()]; gp.CurrentPlot->ColorIdx++; return col; } @@ -1078,8 +1080,8 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons float zoom_rate = 0.1f; if (IO.MouseWheel > 0) zoom_rate = (-zoom_rate) / (1.0f + (2.0f * zoom_rate)); - float tx = Remap(IO.MousePos.x, gp.BB_Grid.Min.x, gp.BB_Grid.Max.x, 0, 1); - float ty = Remap(IO.MousePos.y, gp.BB_Grid.Min.y, gp.BB_Grid.Max.y, 0, 1); + float tx = Remap(IO.MousePos.x, gp.BB_Grid.Min.x, gp.BB_Grid.Max.x, 0.0f, 1.0f); + float ty = Remap(IO.MousePos.y, gp.BB_Grid.Min.y, gp.BB_Grid.Max.y, 0.0f, 1.0f); if (hov_x_axis_region && !x.lock) { ImPlotAxisScale axis_scale(0, tx, ty, zoom_rate); const ImPlotPoint& plot_tl = axis_scale.Min; @@ -1866,29 +1868,105 @@ ImPlotStyle& GetStyle() { return gp.Style; } -void SetPalette(const ImVec4* colors, int num_colors) { - gp.ColorMap.shrink(0); - gp.ColorMap.reserve(num_colors); +void SetColormap(const ImVec4* colors, int num_colors) { + gp.Colormap.shrink(0); + gp.Colormap.reserve(num_colors); for (int i = 0; i < num_colors; ++i) { - gp.ColorMap.push_back(colors[i]); + gp.Colormap.push_back(colors[i]); } } -/// Returns the next unused default plot color -void RestorePalette() { - static ImVec4 default_colors[10] = { - ImVec4((0.0F), (0.7490196228F), (1.0F), (1.0F)), // Blues::DeepSkyBlue, - ImVec4((1.0F), (0.0F), (0.0F), (1.0F)), // Reds::Red, - ImVec4((0.4980392158F), (1.0F), (0.0F), (1.0F)), // Greens::Chartreuse, - ImVec4((1.0F), (1.0F), (0.0F), (1.0F)), // Yellows::Yellow, - ImVec4((0.0F), (1.0F), (1.0F), (1.0F)), // Cyans::Cyan, - ImVec4((1.0F), (0.6470588446F), (0.0F), (1.0F)), // Oranges::Orange, - ImVec4((1.0F), (0.0F), (1.0F), (1.0F)), // Purples::Magenta, - ImVec4((0.5411764979F), (0.1686274558F), (0.8862745166F), (1.0F)), // Purples::BlueViolet, - ImVec4((0.5f), (0.5f), (0.5f), (1.0F)), // Grays::Gray50, - ImVec4((0.8235294223F), (0.7058823705F), (0.5490196347F), (1.0F)) // Browns::Tan +void SetColormap(ImPlotColormap colormap) { + static ImVec4 maps[ImPlotColormap_COUNT][10] { + // ImPlotColormap_Default + { + ImVec4((0.0F), (0.7490196228F), (1.0F), (1.0F)), // Blues::DeepSkyBlue, + ImVec4((1.0F), (0.0F), (0.0F), (1.0F)), // Reds::Red, + ImVec4((0.4980392158F), (1.0F), (0.0F), (1.0F)), // Greens::Chartreuse, + ImVec4((1.0F), (1.0F), (0.0F), (1.0F)), // Yellows::Yellow, + ImVec4((0.0F), (1.0F), (1.0F), (1.0F)), // Cyans::Cyan, + ImVec4((1.0F), (0.6470588446F), (0.0F), (1.0F)), // Oranges::Orange, + ImVec4((1.0F), (0.0F), (1.0F), (1.0F)), // Purples::Magenta, + ImVec4((0.5411764979F), (0.1686274558F), (0.8862745166F), (1.0F)), // Purples::BlueViolet, + ImVec4((0.5f), (0.5f), (0.5f), (1.0F)), // Grays::Gray50, + ImVec4((0.8235294223F), (0.7058823705F), (0.5490196347F), (1.0F)) // Browns::Tan + }, + // ImPlotColormap_Viridis + { + ImVec4(0.267004f, 0.004874f, 0.329415f, 1.f), + ImVec4(0.281412f, 0.155834f, 0.469201f, 1.f), + ImVec4(0.244972f, 0.287675f, 0.53726f, 1.f), + ImVec4(0.190631f, 0.407061f, 0.556089f, 1.f), + ImVec4(0.147607f, 0.511733f, 0.557049f, 1.f), + ImVec4(0.119699f, 0.618490f, 0.536347f, 1.f), + ImVec4(0.208030f, 0.718701f, 0.472873f, 1.f), + ImVec4(0.430983f, 0.808473f, 0.346476f, 1.f), + ImVec4(0.709898f, 0.868751f, 0.169257f, 1.f), + ImVec4(0.993248f, 0.906157f, 0.143936f, 1.f ) + }, + // ImPlotColormap_Plasma + { + ImVec4(0.050383f, 0.029803f, 0.527975f, 1.f), + ImVec4(0.274191f, 0.012109f, 0.622722f, 1.f), + ImVec4(0.447714f, 0.002080f, 0.660240f, 1.f), + ImVec4(0.610667f, 0.090204f, 0.619951f, 1.f), + ImVec4(0.740143f, 0.213864f, 0.524216f, 1.f), + ImVec4(0.846788f, 0.342551f, 0.420579f, 1.f), + ImVec4(0.928329f, 0.472975f, 0.326067f, 1.f), + ImVec4(0.983041f, 0.624131f, 0.227937f, 1.f), + ImVec4(0.991209f, 0.790537f, 0.149377f, 1.f), + ImVec4(0.940015f, 0.975158f, 0.131326f, 1.f) + }, + // ImPlotColormap_Hot + { + ImVec4(0.3333f, 0.0f, 0.0f, 1.0f), + ImVec4(0.6667f, 0.0f, 0.0f, 1.0f), + ImVec4(1.0000f, 0.0f, 0.0f, 1.0f), + ImVec4(1.0000f, 0.3333f, 0.0f, 1.0f), + ImVec4(1.0000f, 0.6667f, 0.0f, 1.0f), + ImVec4(1.0000f, 1.0000f, 0.0f, 1.0f), + ImVec4(1.0000f, 1.0000f, 0.2500f, 1.0f), + ImVec4(1.0000f, 1.0000f, 0.5000f, 1.0f), + ImVec4(1.0000f, 1.0000f, 0.7500f, 1.0f), + ImVec4(1.0000f, 1.0000f, 1.0000f, 1.0f) + }, + // ImPlotColormap_Cool + { + ImVec4( 0.0f, 1.0000f, 1.0000f, 1.0f), + ImVec4(0.1111f, 0.8889f, 1.0000f, 1.0f), + ImVec4(0.2222f, 0.7778f, 1.0000f, 1.0f), + ImVec4(0.3333f, 0.6667f, 1.0000f, 1.0f), + ImVec4(0.4444f, 0.5556f, 1.0000f, 1.0f), + ImVec4(0.5556f, 0.4444f, 1.0000f, 1.0f), + ImVec4(0.6667f, 0.3333f, 1.0000f, 1.0f), + ImVec4(0.7778f, 0.2222f, 1.0000f, 1.0f), + ImVec4(0.8889f, 0.1111f, 1.0000f, 1.0f), + ImVec4(1.0000f, 0.0f, 1.0000f, 1.0f) + } + // ImPlotColormap_Spring + // ImPlotColormap_Summer + // ImPlotColormap_Autumn + // ImPlotColormap_Winter }; - SetPalette(default_colors, 10); + SetColormap(maps[colormap], 10); +} + +/// Returns the next unused default plot color +void RestoreColormap() { + SetColormap(ImPlotColormap_Default); +} + +ImVec4 SampleColormap(float t) { + t = ImClamp(t,0.0f,1.0f); + int i1 = (int)((gp.Colormap.Size-1) * t); + int i2 = i1 + 1; + + if (i2 == gp.Colormap.Size) + return gp.Colormap[i1]; + float t1 = (float)i1 / (float)gp.Colormap.Size; + float t2 = (float)i2 / (float)gp.Colormap.Size; + t = Remap(t, t1, t2, 0.0f, 1.0f); + return ImLerp(gp.Colormap[i1],gp.Colormap[i2],t); } void PushStyleColor(ImPlotCol idx, ImU32 col) { @@ -2515,8 +2593,7 @@ void PlotBarsEx(const char* label_id, Getter getter, int count, TWidth width, in int idx = offset; for (int i = 0; i < count; ++i) { - ImPlotPoint p; - p = getter(idx); + ImPlotPoint p = getter(idx); idx = (idx + 1) % count; if (p.y == 0) continue; @@ -2606,8 +2683,7 @@ void PlotBarsHEx(const char* label_id, Getter getter, int count, THeight height, int idx = offset; for (int i = 0; i < count; ++i) { - ImPlotPoint p; - p = getter(idx); + ImPlotPoint p = getter(idx); idx = (idx + 1) % count; if (p.x == 0) continue; @@ -2828,9 +2904,42 @@ void PlotPieChart(const char** label_ids, double* values, int count, double x, d //----------------------------------------------------------------------------- void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, double scale_min, double scale_max, bool show_labels) { + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotHeatmap() Needs to be called between BeginPlot() and EndPlot()!"); ImPlotItem* item = RegisterItem(label_id); if (!item->Show) return; + if (gp.FitThisFrame) { + FitPoint(ImPlotPoint(0, 0)); + FitPoint(ImPlotPoint(1, 1)); + } + + ImDrawList& DrawList = *ImGui::GetWindowDrawList(); + double w = 1.0 / cols; + double h = 1.0 / rows; + const ImPlotPoint half_size(w*0.5,h*0.5); + ImGui::PushClipRect(gp.BB_Grid.Min, gp.BB_Grid.Max, true); + int i = 0; + for (int r = 0; r < rows; ++r) { + for (int c = 0; c < cols; ++c) { + ImPlotPoint p; + p.x = 0.5*w + c*w; + p.y = 1 - (0.5*h + r*h); + ImVec2 px = PlotToPixels(p); + ImVec2 a = PlotToPixels(p.x - half_size.x, p.y - half_size.y); + ImVec2 b = PlotToPixels(p.x + half_size.x, p.y + half_size.y); + float t = (float)Remap(values[i], scale_min, scale_max, 0.0, 1.0); + ImVec4 color = SampleColormap(t); + DrawList.AddRectFilled(a, b, ImGui::GetColorU32(color)); + if (show_labels) { + char buff[32]; + sprintf(buff, "%g", values[i]); + ImVec2 size = ImGui::CalcTextSize(buff); + DrawList.AddText(px - size * 0.5f, ImGui::GetColorU32(ImGuiCol_Text), buff); + } + i++; + } + } + ImGui::PopClipRect(); } //----------------------------------------------------------------------------- diff --git a/implot.h b/implot.h index c4abdbf..f0154a7 100644 --- a/implot.h +++ b/implot.h @@ -34,6 +34,7 @@ typedef int ImPlotAxisFlags; typedef int ImPlotCol; typedef int ImPlotStyleVar; typedef int ImPlotMarker; +typedef int ImPlotColormap; // Options for plots. enum ImPlotFlags_ { @@ -114,7 +115,25 @@ enum ImPlotMarker_ { ImPlotMarker_Asterisk = 1 << 10, // a asterisk marker will be rendered at each point (not filled) }; -/// Double precision version of ImVec2 used by ImPlot. Extensible by end users. +// Built-in colormaps +enum ImPlotColormap_ { + // Qualitative + ImPlotColormap_Default, + // Sequential + ImPlotColormap_Viridis, + ImPlotColormap_Plasma, + ImPlotColormap_Hot, + ImPlotColormap_Cool, + // ImPlotColormap_Spring, + // ImPlotColormap_Summer, + // ImPlotColormap_Autumn, + // ImPlotColormap_Winter, + // Diverging + // ... + ImPlotColormap_COUNT +}; + +// Double precision version of ImVec2 used by ImPlot. Extensible by end users. struct ImPlotPoint { double x, y; ImPlotPoint() { x = y = 0.0; } @@ -226,7 +245,7 @@ void PlotErrorBars(const char* label_id, const double* xs, const double* ys, con void PlotPieChart(const char** label_ids, float* values, int count, float x, float y, float radius, bool show_percents = true, float angle0 = 90); void PlotPieChart(const char** label_ids, double* values, int count, double x, double y, double radius, bool show_percents = true, double angle0 = 90); -// Plots a 2D heatmap chart. +// Plots a 2D heatmap chart. Values are expected to be in row-major order. void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, double scale_min, double scale_max, bool show_labels = true); // Plots digital data. @@ -242,15 +261,15 @@ void PlotText(const char* text, double x, double y, bool vertical = false, const // Plot Queries //----------------------------------------------------------------------------- -/// Returns true if the plot area in the current or most recent plot is hovered. +// Returns true if the plot area in the current or most recent plot is hovered. bool IsPlotHovered(); -/// Returns the mouse position in x,y coordinates of the current or most recent plot. A negative y_axis uses the current value of SetPlotYAxis (0 initially). +// Returns the mouse position in x,y coordinates of the current or most recent plot. A negative y_axis uses the current value of SetPlotYAxis (0 initially). ImPlotPoint GetPlotMousePos(int y_axis = -1); -/// Returns the current or most recent plot axis range. A negative y_axis uses the current value of SetPlotYAxis (0 initially). +// Returns the current or most recent plot axis range. A negative y_axis uses the current value of SetPlotYAxis (0 initially). ImPlotLimits GetPlotLimits(int y_axis = -1); -/// Returns true if the current or most recent plot is being queried. +// Returns true if the current or most recent plot is being queried. bool IsPlotQueried(); -/// Returns the current or most recent plot query bounds. +// Returns the current or most recent plot query bounds. ImPlotLimits GetPlotQuery(int y_axis = -1); //----------------------------------------------------------------------------- @@ -260,10 +279,14 @@ ImPlotLimits GetPlotQuery(int y_axis = -1); // Provides access to plot style structure for permanant modifications to colors, sizes, etc. ImPlotStyle& GetStyle(); -// Sets the color palette to be used for plot items. -void SetPalette(const ImVec4* colors, int num_colors); -// Restores the default ImPlot color map. -void RestorePalette(); +// Switch to one of the built-in colormaps for plot items. +void SetColormap(ImPlotColormap colormap); +// Sets a custom colormap to be used for plot items. +void SetColormap(const ImVec4* colors, int num_colors); +// Restores the default ImPlot colormap. +void RestoreColormap(); +// Linearly interpolates a color from the current colormap/ +ImVec4 SampleColormap(float t); // Temporarily modify a plot color. Don't forget to call PopStyleColor! void PushStyleColor(ImPlotCol idx, ImU32 col); @@ -283,14 +306,14 @@ void PopStyleVar(int count = 1); // Plot Utils //----------------------------------------------------------------------------- -/// Set the axes range limits of the next plot. Call right before BeginPlot(). If ImGuiCond_Always is used, the axes limits will be locked. +// Set the axes range limits of the next plot. Call right before BeginPlot(). If ImGuiCond_Always is used, the axes limits will be locked. void SetNextPlotLimits(double x_min, double x_max, double y_min, double y_max, ImGuiCond cond = ImGuiCond_Once); -/// Set the X axis range limits of the next plot. Call right before BeginPlot(). If ImGuiCond_Always is used, the X axis limits will be locked. +// Set the X axis range limits of the next plot. Call right before BeginPlot(). If ImGuiCond_Always is used, the X axis limits will be locked. void SetNextPlotLimitsX(double x_min, double x_max, ImGuiCond cond = ImGuiCond_Once); -/// Set the Y axis range limits of the next plot. Call right before BeginPlot(). If ImGuiCond_Always is used, the Y axis limits will be locked. +// Set the Y axis range limits of the next plot. Call right before BeginPlot(). If ImGuiCond_Always is used, the Y axis limits will be locked. void SetNextPlotLimitsY(double y_min, double y_max, ImGuiCond cond = ImGuiCond_Once, int y_axis = 0); -/// Select which Y axis will be used for subsequent plot elements. The default is '0', or the first (left) Y axis. +// Select which Y axis will be used for subsequent plot elements. The default is '0', or the first (left) Y axis. void SetPlotYAxis(int y_axis); // Get the current Plot position (top-left) in pixels. diff --git a/implot_demo.cpp b/implot_demo.cpp index bdc08e9..c06151e 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -129,7 +129,7 @@ void ShowDemoWindow(bool* p_open) { if (show_app_metrics) { ImGui::ShowMetricsWindow(&show_app_metrics); } if (show_app_style_editor) { ImGui::Begin("Style Editor", &show_app_style_editor); ImGui::ShowStyleEditor(); ImGui::End(); } ImGui::SetNextWindowPos(ImVec2(50, 50), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(520, 750), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize(ImVec2(530, 750), ImGuiCond_FirstUseEver); ImGui::Begin("ImPlot Demo", p_open, ImGuiWindowFlags_MenuBar); if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("Tools")) { @@ -307,7 +307,7 @@ void ShowDemoWindow(bool* p_open) { ImVec4(0.9882f, 0.3059f, 0.1647f, 1.0f), ImVec4(0.7412f, 0.0f, 0.1490f, 1.0f), }; - ImPlot::SetPalette(YlOrRd, 5); + ImPlot::SetColormap(YlOrRd, 5); SetNextPlotLimits(0,1,0,1,ImGuiCond_Always); static const char* labels2[] = {"One","Two","Three","Four","Five"}; static t_float not_normalized[] = {1,2,3,4,5}; @@ -315,7 +315,38 @@ void ShowDemoWindow(bool* p_open) { ImPlot::PlotPieChart(labels2, not_normalized, 5, 0.5f, 0.5f, 0.4f); ImPlot::EndPlot(); } - ImPlot::RestorePalette(); + ImPlot::RestoreColormap(); + } + //------------------------------------------------------------------------- + if (ImGui::CollapsingHeader("Heatmaps")) { + static double values1[7][7] = {{0.8, 2.4, 2.5, 3.9, 0.0, 4.0, 0.0}, + {2.4, 0.0, 4.0, 1.0, 2.7, 0.0, 0.0}, + {1.1, 2.4, 0.8, 4.3, 1.9, 4.4, 0.0}, + {0.6, 0.0, 0.3, 0.0, 3.1, 0.0, 0.0}, + {0.7, 1.7, 0.6, 2.6, 2.2, 6.2, 0.0}, + {1.3, 1.2, 0.0, 0.0, 0.0, 3.2, 5.1}, + {0.1, 2.0, 0.0, 1.4, 0.0, 1.9, 6.3}}; + static double values2[100*100]; + for (int i = 0; i < 100*100; ++i) { + values2[i] = RandomRange(0,1); + } + static ImPlotColormap map = ImPlotColormap_Viridis; + if (ImGui::Button("Cycle Colormaps")) + map = (map + 1) % ImPlotColormap_COUNT; + static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax; + if (ImPlot::BeginPlot("##Heatmap1",NULL,NULL,ImVec2(250,250),0,axes_flags,axes_flags)) { + ImPlot::SetColormap(map); + ImPlot::PlotHeatmap("heat",values1[0],7,7,0,6.3); + ImPlot::EndPlot(); + ImPlot::RestoreColormap(); + } + ImGui::SameLine(); + if (ImPlot::BeginPlot("##Heatmap2",NULL,NULL,ImVec2(250,250),0,axes_flags,axes_flags)) { + ImPlot::SetColormap(map); + ImPlot::PlotHeatmap("heat",values2,100,100,0,1,false); + ImPlot::EndPlot(); + ImPlot::RestoreColormap(); + } } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Realtime Plots")) { @@ -783,12 +814,12 @@ void ShowDemoWindow(bool* p_open) { } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Custom Styles")) { - static ImVec4 my_palette[3] = { + static ImVec4 my_map[3] = { ImVec4(0.000f, 0.980f, 0.604f, 1.0f), ImVec4(0.996f, 0.278f, 0.380f, 1.0f), ImVec4(0.1176470593f, 0.5647059083f, 1.0f, 1.0f), }; - ImPlot::SetPalette(my_palette, 3); + ImPlot::SetColormap(my_map, 3); ImPlot::PushStyleColor(ImPlotCol_FrameBg, IM_COL32(32,51,77,255)); ImPlot::PushStyleColor(ImPlotCol_PlotBg, ImVec4(0,0,0,0)); ImPlot::PushStyleColor(ImPlotCol_PlotBorder, ImVec4(0,0,0,0)); @@ -810,7 +841,7 @@ void ShowDemoWindow(bool* p_open) { } ImPlot::PopStyleColor(5); ImPlot::PopStyleVar(); - ImPlot::RestorePalette(); + ImPlot::RestoreColormap(); } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Custom Rendering")) { From 356e604bd2b4491d65dd4393867a1b08be27425e Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Tue, 2 Jun 2020 12:34:14 -0500 Subject: [PATCH 03/13] colormaps --- implot.cpp | 406 +++++++++++++++++++++++++++++++----------------- implot.h | 45 +++--- implot_demo.cpp | 50 ++++-- 3 files changed, 317 insertions(+), 184 deletions(-) diff --git a/implot.cpp b/implot.cpp index 6e21eb3..94c3658 100644 --- a/implot.cpp +++ b/implot.cpp @@ -31,7 +31,7 @@ Below is a change-log of API breaking changes only. If you are using one of the When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot files. You can read releases logs https://github.com/epezent/implot/releases for more details. -- 2020/06/01 (0.3) - SetPalette/RestorePalette were changed to SetColormap/RestoreColormap for consistency with other plotting libraries. +- 2020/06/01 (0.3) - SetPalette was changed to `SetColormap` for consistency with other plotting libraries. `RestorePalette` was removed. Use `SetColormap(ImPlotColormap_Default)`. - 2020/05/31 (0.3) - Plot functions taking custom ImVec2* getters were removed. Use the ImPlotPoint* getter versions instead. - 2020/05/29 (0.3) - The signature of ImPlotLimits::Contains was changed to take two doubles instead of ImVec2 - 2020/05/16 (0.2) - All plotting functions were reverted to being prefixed with "Plot" to maintain a consistent VerbNoun style. `Plot` was split into `PlotLine` @@ -141,6 +141,16 @@ namespace { // Private Utils //----------------------------------------------------------------------------- +template +struct OffsetCalculator { + OffsetCalculator(int* sizes) { + Offsets[0] = 0; + for (int i = 1; i < Count; ++i) + Offsets[i] = Offsets[i-1] + sizes[i-1]; + } + int Offsets[Count]; +}; + /// Returns true if a flag is set template inline bool HasFlag(TSet set, TFlag flag) { @@ -179,33 +189,6 @@ inline bool NanOrInf(double val) { return val == HUGE_VAL || val == -HUGE_VAL || isnan(val); } -/// Utility function to that rounds x to powers of 2,5 and 10 for generating axis labels -/// Taken from Graphics Gems 1 Chapter 11.2, "Nice Numbers for Graph Labels" -inline double NiceNum(double x, bool round) { - double f; /* fractional part of x */ - double nf; /* nice, rounded fraction */ - int expv = (int)floor(ImLog10(x)); - f = x / ImPow(10.0, (double)expv); /* between 1 and 10 */ - if (round) - if (f < 1.5) - nf = 1; - else if (f < 3) - nf = 2; - else if (f < 7) - nf = 5; - else - nf = 10; - else if (f <= 1) - nf = 1; - else if (f <= 2) - nf = 2; - else if (f <= 5) - nf = 5; - else - nf = 10; - return nf * ImPow(10.0, expv); -} - /// Draws vertical text. The position is the bottom left of the text rect. inline void AddTextVertical(ImDrawList *DrawList, const char *text, ImVec2 pos, ImU32 text_color) { pos.x = IM_ROUND(pos.x); @@ -336,7 +319,7 @@ struct ImPlotContext { ImPlotContext() : RenderX(), RenderY() { CurrentPlot = NULL; FitThisFrame = FitX = false; - RestoreColormap(); + SetColormap(ImPlotColormap_Default); } /// ALl Plots @@ -387,7 +370,8 @@ struct ImPlotContext { // Mouse pos ImPlotPoint LastMousePos[MAX_Y_AXES]; // Style - ImVector Colormap; + ImVec4* Colormap; + int ColormapSize; ImPlotStyle Style; ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() @@ -406,7 +390,7 @@ static ImPlotContext gp; /// Returns the next unused default plot color ImVec4 NextColor() { - ImVec4 col = gp.Colormap[gp.CurrentPlot->ColorIdx % gp.Colormap.size()]; + ImVec4 col = gp.Colormap[gp.CurrentPlot->ColorIdx % gp.ColormapSize]; gp.CurrentPlot->ColorIdx++; return col; } @@ -553,6 +537,33 @@ struct TransformerLogLog { // Legend Utils //----------------------------------------------------------------------------- +/// Utility function to that rounds x to powers of 2,5 and 10 for generating axis labels +/// Taken from Graphics Gems 1 Chapter 11.2, "Nice Numbers for Graph Labels" +inline double NiceNum(double x, bool round) { + double f; /* fractional part of x */ + double nf; /* nice, rounded fraction */ + int expv = (int)floor(ImLog10(x)); + f = x / ImPow(10.0, (double)expv); /* between 1 and 10 */ + if (round) + if (f < 1.5) + nf = 1; + else if (f < 3) + nf = 2; + else if (f < 7) + nf = 5; + else + nf = 10; + else if (f <= 1) + nf = 1; + else if (f <= 2) + nf = 2; + else if (f <= 5) + nf = 5; + else + nf = 10; + return nf * ImPow(10.0, expv); +} + ImPlotItem* RegisterItem(const char* label_id) { ImGuiID id = ImGui::GetID(label_id); ImPlotItem* item = gp.CurrentPlot->Items.GetOrAddByKey(id); @@ -1868,107 +1879,6 @@ ImPlotStyle& GetStyle() { return gp.Style; } -void SetColormap(const ImVec4* colors, int num_colors) { - gp.Colormap.shrink(0); - gp.Colormap.reserve(num_colors); - for (int i = 0; i < num_colors; ++i) { - gp.Colormap.push_back(colors[i]); - } -} - -void SetColormap(ImPlotColormap colormap) { - static ImVec4 maps[ImPlotColormap_COUNT][10] { - // ImPlotColormap_Default - { - ImVec4((0.0F), (0.7490196228F), (1.0F), (1.0F)), // Blues::DeepSkyBlue, - ImVec4((1.0F), (0.0F), (0.0F), (1.0F)), // Reds::Red, - ImVec4((0.4980392158F), (1.0F), (0.0F), (1.0F)), // Greens::Chartreuse, - ImVec4((1.0F), (1.0F), (0.0F), (1.0F)), // Yellows::Yellow, - ImVec4((0.0F), (1.0F), (1.0F), (1.0F)), // Cyans::Cyan, - ImVec4((1.0F), (0.6470588446F), (0.0F), (1.0F)), // Oranges::Orange, - ImVec4((1.0F), (0.0F), (1.0F), (1.0F)), // Purples::Magenta, - ImVec4((0.5411764979F), (0.1686274558F), (0.8862745166F), (1.0F)), // Purples::BlueViolet, - ImVec4((0.5f), (0.5f), (0.5f), (1.0F)), // Grays::Gray50, - ImVec4((0.8235294223F), (0.7058823705F), (0.5490196347F), (1.0F)) // Browns::Tan - }, - // ImPlotColormap_Viridis - { - ImVec4(0.267004f, 0.004874f, 0.329415f, 1.f), - ImVec4(0.281412f, 0.155834f, 0.469201f, 1.f), - ImVec4(0.244972f, 0.287675f, 0.53726f, 1.f), - ImVec4(0.190631f, 0.407061f, 0.556089f, 1.f), - ImVec4(0.147607f, 0.511733f, 0.557049f, 1.f), - ImVec4(0.119699f, 0.618490f, 0.536347f, 1.f), - ImVec4(0.208030f, 0.718701f, 0.472873f, 1.f), - ImVec4(0.430983f, 0.808473f, 0.346476f, 1.f), - ImVec4(0.709898f, 0.868751f, 0.169257f, 1.f), - ImVec4(0.993248f, 0.906157f, 0.143936f, 1.f ) - }, - // ImPlotColormap_Plasma - { - ImVec4(0.050383f, 0.029803f, 0.527975f, 1.f), - ImVec4(0.274191f, 0.012109f, 0.622722f, 1.f), - ImVec4(0.447714f, 0.002080f, 0.660240f, 1.f), - ImVec4(0.610667f, 0.090204f, 0.619951f, 1.f), - ImVec4(0.740143f, 0.213864f, 0.524216f, 1.f), - ImVec4(0.846788f, 0.342551f, 0.420579f, 1.f), - ImVec4(0.928329f, 0.472975f, 0.326067f, 1.f), - ImVec4(0.983041f, 0.624131f, 0.227937f, 1.f), - ImVec4(0.991209f, 0.790537f, 0.149377f, 1.f), - ImVec4(0.940015f, 0.975158f, 0.131326f, 1.f) - }, - // ImPlotColormap_Hot - { - ImVec4(0.3333f, 0.0f, 0.0f, 1.0f), - ImVec4(0.6667f, 0.0f, 0.0f, 1.0f), - ImVec4(1.0000f, 0.0f, 0.0f, 1.0f), - ImVec4(1.0000f, 0.3333f, 0.0f, 1.0f), - ImVec4(1.0000f, 0.6667f, 0.0f, 1.0f), - ImVec4(1.0000f, 1.0000f, 0.0f, 1.0f), - ImVec4(1.0000f, 1.0000f, 0.2500f, 1.0f), - ImVec4(1.0000f, 1.0000f, 0.5000f, 1.0f), - ImVec4(1.0000f, 1.0000f, 0.7500f, 1.0f), - ImVec4(1.0000f, 1.0000f, 1.0000f, 1.0f) - }, - // ImPlotColormap_Cool - { - ImVec4( 0.0f, 1.0000f, 1.0000f, 1.0f), - ImVec4(0.1111f, 0.8889f, 1.0000f, 1.0f), - ImVec4(0.2222f, 0.7778f, 1.0000f, 1.0f), - ImVec4(0.3333f, 0.6667f, 1.0000f, 1.0f), - ImVec4(0.4444f, 0.5556f, 1.0000f, 1.0f), - ImVec4(0.5556f, 0.4444f, 1.0000f, 1.0f), - ImVec4(0.6667f, 0.3333f, 1.0000f, 1.0f), - ImVec4(0.7778f, 0.2222f, 1.0000f, 1.0f), - ImVec4(0.8889f, 0.1111f, 1.0000f, 1.0f), - ImVec4(1.0000f, 0.0f, 1.0000f, 1.0f) - } - // ImPlotColormap_Spring - // ImPlotColormap_Summer - // ImPlotColormap_Autumn - // ImPlotColormap_Winter - }; - SetColormap(maps[colormap], 10); -} - -/// Returns the next unused default plot color -void RestoreColormap() { - SetColormap(ImPlotColormap_Default); -} - -ImVec4 SampleColormap(float t) { - t = ImClamp(t,0.0f,1.0f); - int i1 = (int)((gp.Colormap.Size-1) * t); - int i2 = i1 + 1; - - if (i2 == gp.Colormap.Size) - return gp.Colormap[i1]; - float t1 = (float)i1 / (float)gp.Colormap.Size; - float t2 = (float)i2 / (float)gp.Colormap.Size; - t = Remap(t, t1, t2, 0.0f, 1.0f); - return ImLerp(gp.Colormap[i1],gp.Colormap[i2],t); -} - void PushStyleColor(ImPlotCol idx, ImU32 col) { ImGuiColorMod backup; backup.Col = idx; @@ -2200,6 +2110,34 @@ inline void RenderLine(ImDrawList& DrawList, const ImVec2& p1, const ImVec2& p2, DrawList._VtxCurrentIdx += 4; } +inline void RenderRect(ImDrawList& DrawList, const ImVec2& p1, const ImVec2& p2, ImU32 col, ImVec2 uv) { + DrawList._VtxWritePtr[0].pos.x = p1.x; + DrawList._VtxWritePtr[0].pos.y = p1.y; + DrawList._VtxWritePtr[0].uv = uv; + DrawList._VtxWritePtr[0].col = col; + DrawList._VtxWritePtr[1].pos.x = p2.x; + DrawList._VtxWritePtr[1].pos.y = p1.y; + DrawList._VtxWritePtr[1].uv = uv; + DrawList._VtxWritePtr[1].col = col; + DrawList._VtxWritePtr[2].pos.x = p2.x; + DrawList._VtxWritePtr[2].pos.y = p2.y; + DrawList._VtxWritePtr[2].uv = uv; + DrawList._VtxWritePtr[2].col = col; + DrawList._VtxWritePtr[3].pos.x = p1.x; + DrawList._VtxWritePtr[3].pos.y = p2.y; + DrawList._VtxWritePtr[3].uv = uv; + DrawList._VtxWritePtr[3].col = col; + DrawList._VtxWritePtr += 4; + DrawList._IdxWritePtr[0] = (ImDrawIdx)(DrawList._VtxCurrentIdx); + DrawList._IdxWritePtr[1] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1); + DrawList._IdxWritePtr[2] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3); + DrawList._IdxWritePtr[3] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1); + DrawList._IdxWritePtr[4] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 2); + DrawList._IdxWritePtr[5] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3); + DrawList._IdxWritePtr += 6; + DrawList._VtxCurrentIdx += 4; +} + inline void RenderFill(ImDrawList& DrawList, const ImVec2& p1, const ImVec2& p2, float zero, ImU32 col_fill, ImVec2 uv) { const int crosses_zero = (p1.y > zero && p2.y < zero) || (p1.y < zero && p2.y > zero); // could do y*y < 0 earlier on const float xmid = p1.x + (p2.x - p1.x) / (p2.y-p1.y) * (zero - p1.y); @@ -2903,40 +2841,54 @@ void PlotPieChart(const char** label_ids, double* values, int count, double x, d // PLOT HEATMAP //----------------------------------------------------------------------------- -void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, double scale_min, double scale_max, bool show_labels) { +void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, double scale_min, double scale_max, bool show_labels, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) { IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotHeatmap() Needs to be called between BeginPlot() and EndPlot()!"); ImPlotItem* item = RegisterItem(label_id); if (!item->Show) return; if (gp.FitThisFrame) { - FitPoint(ImPlotPoint(0, 0)); - FitPoint(ImPlotPoint(1, 1)); + FitPoint(bounds_min); + FitPoint(bounds_max); } - ImDrawList& DrawList = *ImGui::GetWindowDrawList(); - double w = 1.0 / cols; - double h = 1.0 / rows; + const double w = (bounds_max.x - bounds_min.x) / cols; + const double h = (bounds_max.y - bounds_min.y) / rows; const ImPlotPoint half_size(w*0.5,h*0.5); - ImGui::PushClipRect(gp.BB_Grid.Min, gp.BB_Grid.Max, true); + const int n = rows * cols; int i = 0; + ImGui::PushClipRect(gp.BB_Grid.Min, gp.BB_Grid.Max, true); + DrawList.PrimReserve(6*n, 4*n); + const ImVec2 uv = DrawList._Data->TexUvWhitePixel; for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { ImPlotPoint p; - p.x = 0.5*w + c*w; - p.y = 1 - (0.5*h + r*h); + p.x = bounds_min.x + 0.5*w + c*w; + p.y = bounds_min.y + 1 - (0.5*h + r*h); ImVec2 px = PlotToPixels(p); ImVec2 a = PlotToPixels(p.x - half_size.x, p.y - half_size.y); ImVec2 b = PlotToPixels(p.x + half_size.x, p.y + half_size.y); float t = (float)Remap(values[i], scale_min, scale_max, 0.0, 1.0); - ImVec4 color = SampleColormap(t); - DrawList.AddRectFilled(a, b, ImGui::GetColorU32(color)); - if (show_labels) { + ImVec4 color = LerpColormap(t); + // DrawList.AddRectFilled(a, b, ImGui::GetColorU32(color)); + RenderRect(DrawList, a, b, ImGui::GetColorU32(color), uv); + i++; + } + } + if (show_labels) { + // this has to go in its own loop due to PrimReserve above + i = 0; + for (int r = 0; r < rows; ++r) { + for (int c = 0; c < cols; ++c) { + ImPlotPoint p; + p.x = bounds_min.x + 0.5*w + c*w; + p.y = bounds_min.y + 1 - (0.5*h + r*h); + ImVec2 px = PlotToPixels(p); char buff[32]; sprintf(buff, "%g", values[i]); ImVec2 size = ImGui::CalcTextSize(buff); DrawList.AddText(px - size * 0.5f, ImGui::GetColorU32(ImGuiCol_Text), buff); + i++; } - i++; } } ImGui::PopClipRect(); @@ -3066,4 +3018,168 @@ void PlotText(const char* text, double x, double y, bool vertical, const ImVec2& PopPlotClipRect(); } +//------------------------------------------------------------------------------ +// COLORMAPS +//------------------------------------------------------------------------------ + +void SetColormap(const ImVec4* colors, int num_colors) { + IM_ASSERT_USER_ERROR(num_colors > 0, "The number of colors cannot be 0!"); + static ImVector user_colormap; + user_colormap.shrink(0); + user_colormap.reserve(num_colors); + for (int i = 0; i < num_colors; ++i) + user_colormap.push_back(colors[i]); + gp.Colormap = &user_colormap[0]; + gp.ColormapSize = num_colors; +} + +// Returns the size of the current colormap +int GetColormapSize() { + return gp.ColormapSize; +} + +/// Returns a color from the Color map given an index > 0 +ImVec4 GetColormapColor(int index) { + IM_ASSERT_USER_ERROR(index >= 0, "The Colormap index must be greater than zero!"); + return gp.Colormap[index % gp.ColormapSize]; +} + +ImVec4 LerpColormap(float t) { + t = ImClamp(t,0.0f,1.0f); + int i1 = (int)((gp.ColormapSize-1) * t); + int i2 = i1 + 1; + if (i2 == gp.ColormapSize) + return gp.Colormap[i1]; + float t1 = (float)i1 / (float)gp.ColormapSize; + float t2 = (float)i2 / (float)gp.ColormapSize; + t = Remap(t, t1, t2, 0.0f, 1.0f); + return ImLerp(gp.Colormap[i1], gp.Colormap[i2], t); +} + +void SetColormap(ImPlotColormap colormap) { + static int csizes[ImPlotColormap_COUNT] = {10,9,9,12,11,11,11,11,11,11}; + static OffsetCalculator coffs(csizes); + static ImVec4 cdata[] { + // ImPlotColormap_Default // X11 Named Colors + ImVec4(0.0f, 0.7490196228f, 1.0f, 1.0f), // Blues::DeepSkyBlue, + ImVec4(1.0f, 0.0f, 0.0f, 1.0f), // Reds::Red, + ImVec4(0.4980392158f, 1.0f, 0.0f, 1.0f), // Greens::Chartreuse, + ImVec4(1.0f, 1.0f, 0.0f, 1.0f), // Yellows::Yellow, + ImVec4(0.0f, 1.0f, 1.0f, 1.0f), // Cyans::Cyan, + ImVec4(1.0f, 0.6470588446f, 0.0f, 1.0f), // Oranges::Orange, + ImVec4(1.0f, 0.0f, 1.0f, 1.0f), // Purples::Magenta, + ImVec4(0.5411764979f, 0.1686274558f, 0.8862745166f, 1.0f), // Purples::BlueViolet, + ImVec4(0.5f, 0.5f, 0.5f, 1.0f), // Grays::Gray50, + ImVec4(0.8235294223f, 0.7058823705f, 0.5490196347f, 1.0f), // Browns::Tan + // ImPlotColormap_Dark + ImVec4(0.894118f, 0.101961f, 0.109804f, 1.0f), + ImVec4(0.215686f, 0.494118f, 0.721569f, 1.0f), + ImVec4(0.301961f, 0.686275f, 0.290196f, 1.0f), + ImVec4(0.596078f, 0.305882f, 0.639216f, 1.0f), + ImVec4(1.000000f, 0.498039f, 0.000000f, 1.0f), + ImVec4(1.000000f, 1.000000f, 0.200000f, 1.0f), + ImVec4(0.650980f, 0.337255f, 0.156863f, 1.0f), + ImVec4(0.968627f, 0.505882f, 0.749020f, 1.0f), + ImVec4(0.600000f, 0.600000f, 0.600000f, 1.0f), + // ImPlotColormap_Pastel + ImVec4(0.984314f, 0.705882f, 0.682353f, 1.0f), + ImVec4(0.701961f, 0.803922f, 0.890196f, 1.0f), + ImVec4(0.800000f, 0.921569f, 0.772549f, 1.0f), + ImVec4(0.870588f, 0.796078f, 0.894118f, 1.0f), + ImVec4(0.996078f, 0.850980f, 0.650980f, 1.0f), + ImVec4(1.000000f, 1.000000f, 0.800000f, 1.0f), + ImVec4(0.898039f, 0.847059f, 0.741176f, 1.0f), + ImVec4(0.992157f, 0.854902f, 0.925490f, 1.0f), + ImVec4(0.949020f, 0.949020f, 0.949020f, 1.0f), + // ImPlotColormap_Paired + ImVec4(0.258824f, 0.807843f, 0.890196f, 1.0f), + ImVec4(0.121569f, 0.470588f, 0.705882f, 1.0f), + ImVec4(0.698039f, 0.874510f, 0.541176f, 1.0f), + ImVec4(0.200000f, 0.627451f, 0.172549f, 1.0f), + ImVec4(0.984314f, 0.603922f, 0.600000f, 1.0f), + ImVec4(0.890196f, 0.101961f, 0.109804f, 1.0f), + ImVec4(0.992157f, 0.749020f, 0.435294f, 1.0f), + ImVec4(1.000000f, 0.498039f, 0.000000f, 1.0f), + ImVec4(0.792157f, 0.698039f, 0.839216f, 1.0f), + ImVec4(0.415686f, 0.239216f, 0.603922f, 1.0f), + ImVec4(1.000000f, 1.000000f, 0.600000f, 1.0f), + ImVec4(0.694118f, 0.349020f, 0.156863f, 1.0f), + // ImPlotColormap_Viridis + ImVec4(0.267004f, 0.004874f, 0.329415f, 1.0f), + ImVec4(0.282623f, 0.140926f, 0.457517f, 1.0f), + ImVec4(0.253935f, 0.265254f, 0.529983f, 1.0f), + ImVec4(0.206756f, 0.371758f, 0.553117f, 1.0f), + ImVec4(0.163625f, 0.471133f, 0.558148f, 1.0f), + ImVec4(0.127568f, 0.566949f, 0.550556f, 1.0f), + ImVec4(0.134692f, 0.658636f, 0.517649f, 1.0f), + ImVec4(0.266941f, 0.748751f, 0.440573f, 1.0f), + ImVec4(0.477504f, 0.821444f, 0.318195f, 1.0f), + ImVec4(0.741388f, 0.873449f, 0.149561f, 1.0f), + ImVec4(0.993248f, 0.906157f, 0.143936f, 1.0f), + // ImPlotColormap_Plasma + ImVec4(5.03830e-02f, 2.98030e-02f, 5.27975e-01f, 1.00000e+00f), + ImVec4(2.54627e-01f, 1.38820e-02f, 6.15419e-01f, 1.00000e+00f), + ImVec4(4.17642e-01f, 5.64000e-04f, 6.58390e-01f, 1.00000e+00f), + ImVec4(5.62738e-01f, 5.15450e-02f, 6.41509e-01f, 1.00000e+00f), + ImVec4(6.92840e-01f, 1.65141e-01f, 5.64522e-01f, 1.00000e+00f), + ImVec4(7.98216e-01f, 2.80197e-01f, 4.69538e-01f, 1.00000e+00f), + ImVec4(8.81443e-01f, 3.92529e-01f, 3.83229e-01f, 1.00000e+00f), + ImVec4(9.49217e-01f, 5.17763e-01f, 2.95662e-01f, 1.00000e+00f), + ImVec4(9.88260e-01f, 6.52325e-01f, 2.11364e-01f, 1.00000e+00f), + ImVec4(9.88648e-01f, 8.09579e-01f, 1.45357e-01f, 1.00000e+00f), + ImVec4(9.40015e-01f, 9.75158e-01f, 1.31326e-01f, 1.00000e+00f), + // ImPlotColormap_Hot + ImVec4(0.2500f, 0.f, 0.f, 1.0f), + ImVec4(0.5000f, 0.f, 0.f, 1.0f), + ImVec4(0.7500f, 0.f, 0.f, 1.0f), + ImVec4(1.0000f, 0.f, 0.f, 1.0f), + ImVec4(1.0000f, 0.2500f, 0.f, 1.0f), + ImVec4(1.0000f, 0.5000f, 0.f, 1.0f), + ImVec4(1.0000f, 0.7500f, 0.f, 1.0f), + ImVec4(1.0000f, 1.0000f, 0.f, 1.0f), + ImVec4(1.0000f, 1.0000f, 0.3333f, 1.0f), + ImVec4(1.0000f, 1.0000f, 0.6667f, 1.0f), + ImVec4(1.0000f, 1.0000f, 1.0000f, 1.0f), + // ImPlotColormap_Cool + ImVec4( 0.f, 1.0000f, 1.0000f, 1.0f), + ImVec4(0.1000f, 0.9000f, 1.0000f, 1.0f), + ImVec4(0.2000f, 0.8000f, 1.0000f, 1.0f), + ImVec4(0.3000f, 0.7000f, 1.0000f, 1.0f), + ImVec4(0.4000f, 0.6000f, 1.0000f, 1.0f), + ImVec4(0.5000f, 0.5000f, 1.0000f, 1.0f), + ImVec4(0.6000f, 0.4000f, 1.0000f, 1.0f), + ImVec4(0.7000f, 0.3000f, 1.0000f, 1.0f), + ImVec4(0.8000f, 0.2000f, 1.0000f, 1.0f), + ImVec4(0.9000f, 0.1000f, 1.0000f, 1.0f), + ImVec4(1.0000f, 0.f, 1.0000f, 1.0f), + // ImPlotColormap_Pink + ImVec4(0.2887f, 0.f, 0.f, 1.0f), + ImVec4(0.4830f, 0.2582f, 0.2582f, 1.0f), + ImVec4(0.6191f, 0.3651f, 0.3651f, 1.0f), + ImVec4(0.7303f, 0.4472f, 0.4472f, 1.0f), + ImVec4(0.7746f, 0.5916f, 0.5164f, 1.0f), + ImVec4(0.8165f, 0.7071f, 0.5774f, 1.0f), + ImVec4(0.8563f, 0.8062f, 0.6325f, 1.0f), + ImVec4(0.8944f, 0.8944f, 0.6831f, 1.0f), + ImVec4(0.9309f, 0.9309f, 0.8028f, 1.0f), + ImVec4(0.9661f, 0.9661f, 0.9068f, 1.0f), + ImVec4(1.0000f, 1.0000f, 1.0000f, 1.0f), + // ImPlotColormap_Jet + ImVec4( 0.f, 0.f, 0.6667f, 1.0f), + ImVec4( 0.f, 0.f, 1.0000f, 1.0f), + ImVec4( 0.f, 0.3333f, 1.0000f, 1.0f), + ImVec4( 0.f, 0.6667f, 1.0000f, 1.0f), + ImVec4( 0.f, 1.0000f, 1.0000f, 1.0f), + ImVec4(0.3333f, 1.0000f, 0.6667f, 1.0f), + ImVec4(0.6667f, 1.0000f, 0.3333f, 1.0f), + ImVec4(1.0000f, 1.0000f, 0.f, 1.0f), + ImVec4(1.0000f, 0.6667f, 0.f, 1.0f), + ImVec4(1.0000f, 0.3333f, 0.f, 1.0f), + ImVec4(1.0000f, 0.f, 0.f, 1.0f) + }; + // TODO: Calculate offsets at compile time + gp.Colormap = &cdata[coffs.Offsets[colormap]]; + gp.ColormapSize = csizes[colormap]; +} + } // namespace ImPlot diff --git a/implot.h b/implot.h index f0154a7..c18b4a1 100644 --- a/implot.h +++ b/implot.h @@ -117,19 +117,16 @@ enum ImPlotMarker_ { // Built-in colormaps enum ImPlotColormap_ { - // Qualitative - ImPlotColormap_Default, - // Sequential - ImPlotColormap_Viridis, - ImPlotColormap_Plasma, - ImPlotColormap_Hot, - ImPlotColormap_Cool, - // ImPlotColormap_Spring, - // ImPlotColormap_Summer, - // ImPlotColormap_Autumn, - // ImPlotColormap_Winter, - // Diverging - // ... + ImPlotColormap_Default = 0, // ImPlot default colormap (n=10) + ImPlotColormap_Dark = 1, // a.k.a. matplotlib "Set1" (n=9) + ImPlotColormap_Pastel = 2, // a.k.a. matplotlib "Pastel1" (n=9) + ImPlotColormap_Paired = 3, // a.k.a. matplotlib "Paired" (n=12) + ImPlotColormap_Viridis = 4, // a.k.a. matplotlib "viridis" (n=11) + ImPlotColormap_Plasma = 5, // a.k.a. matplotlib "plasma" (n=11) + ImPlotColormap_Hot = 6, // a.k.a. matplotlib/MATLAB "hot" (n=11) + ImPlotColormap_Cool = 7, // a.k.a. matplotlib/MATLAB "cool" (n=11) + ImPlotColormap_Pink = 8, // a.k.a. matplotlib/MATLAB "pink" (n=11) + ImPlotColormap_Jet = 9, // a.k.a. MATLAB "jet" (n=11) ImPlotColormap_COUNT }; @@ -246,7 +243,7 @@ void PlotPieChart(const char** label_ids, float* values, int count, float x, flo void PlotPieChart(const char** label_ids, double* values, int count, double x, double y, double radius, bool show_percents = true, double angle0 = 90); // Plots a 2D heatmap chart. Values are expected to be in row-major order. -void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, double scale_min, double scale_max, bool show_labels = true); +void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, double scale_min, double scale_max, bool show_labels = true, const ImPlotPoint& bounds_min = ImPlotPoint(0,0), const ImPlotPoint& bounds_max = ImPlotPoint(1,1)); // Plots digital data. void PlotDigital(const char* label_id, const float* xs, const float* ys, int count, int offset = 0, int stride = sizeof(float)); @@ -279,15 +276,6 @@ ImPlotLimits GetPlotQuery(int y_axis = -1); // Provides access to plot style structure for permanant modifications to colors, sizes, etc. ImPlotStyle& GetStyle(); -// Switch to one of the built-in colormaps for plot items. -void SetColormap(ImPlotColormap colormap); -// Sets a custom colormap to be used for plot items. -void SetColormap(const ImVec4* colors, int num_colors); -// Restores the default ImPlot colormap. -void RestoreColormap(); -// Linearly interpolates a color from the current colormap/ -ImVec4 SampleColormap(float t); - // Temporarily modify a plot color. Don't forget to call PopStyleColor! void PushStyleColor(ImPlotCol idx, ImU32 col); // Temporarily modify a plot color. Don't forget to call PopStyleColor! @@ -302,6 +290,17 @@ void PushStyleVar(ImPlotStyleVar idx, int val); // Undo temporary style modification. void PopStyleVar(int count = 1); +// Switch to one of the built-in colormaps. +void SetColormap(ImPlotColormap colormap); +// Sets a custom colormap. +void SetColormap(const ImVec4* colors, int num_colors); +// Returns the size of the current colormap +int GetColormapSize(); +/// Returns a color from the Color map given an index > 0 (modulo will be performed) +ImVec4 GetColormapColor(int index); +// Linearly interpolates a color from the current colormap given t between 0 and 1. +ImVec4 LerpColormap(float t); + //----------------------------------------------------------------------------- // Plot Utils //----------------------------------------------------------------------------- diff --git a/implot_demo.cpp b/implot_demo.cpp index c06151e..671c360 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -124,6 +124,7 @@ struct BenchmarkItem { namespace ImPlot { void ShowDemoWindow(bool* p_open) { + static const char* cmap_names[] = {"Default","Dark","Pastel","Paired","Viridis","Plasma","Hot","Cool","Pink","Jet"}; static bool show_app_metrics = false; static bool show_app_style_editor = false; if (show_app_metrics) { ImGui::ShowMetricsWindow(&show_app_metrics); } @@ -140,7 +141,7 @@ void ShowDemoWindow(bool* p_open) { ImGui::EndMenuBar(); } //------------------------------------------------------------------------- - ImGui::Text("ImPlot says hello. (0.2 WIP)"); + ImGui::Text("ImPlot says hello. (0.3 WIP)"); if (ImGui::CollapsingHeader("Help")) { ImGui::Text("USER GUIDE:"); ImGui::BulletText("Left click and drag within the plot area to pan X and Y axes."); @@ -264,6 +265,7 @@ void ShowDemoWindow(bool* p_open) { ImPlot::PlotBars("Final Exam", final, 10, 0.2f, 0); ImPlot::PlotBars("Course Grade", grade, 10, 0.2f, 0.2f); } + ImPlot::SetColormap(ImPlotColormap_Default); ImPlot::EndPlot(); } } @@ -312,40 +314,46 @@ void ShowDemoWindow(bool* p_open) { static const char* labels2[] = {"One","Two","Three","Four","Five"}; static t_float not_normalized[] = {1,2,3,4,5}; if (ImPlot::BeginPlot("##Pie2", NULL, NULL, ImVec2(250,250), ImPlotFlags_Legend, 0, 0)) { - ImPlot::PlotPieChart(labels2, not_normalized, 5, 0.5f, 0.5f, 0.4f); + ImPlot::PlotPieChart(labels2, not_normalized, 5, 0.5f, 0.5f, 0.4f, false, 0); ImPlot::EndPlot(); } - ImPlot::RestoreColormap(); + ImPlot::SetColormap(ImPlotColormap_Default); } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Heatmaps")) { static double values1[7][7] = {{0.8, 2.4, 2.5, 3.9, 0.0, 4.0, 0.0}, - {2.4, 0.0, 4.0, 1.0, 2.7, 0.0, 0.0}, - {1.1, 2.4, 0.8, 4.3, 1.9, 4.4, 0.0}, - {0.6, 0.0, 0.3, 0.0, 3.1, 0.0, 0.0}, - {0.7, 1.7, 0.6, 2.6, 2.2, 6.2, 0.0}, - {1.3, 1.2, 0.0, 0.0, 0.0, 3.2, 5.1}, - {0.1, 2.0, 0.0, 1.4, 0.0, 1.9, 6.3}}; + {2.4, 0.0, 4.0, 1.0, 2.7, 0.0, 0.0}, + {1.1, 2.4, 0.8, 4.3, 1.9, 4.4, 0.0}, + {0.6, 0.0, 0.3, 0.0, 3.1, 0.0, 0.0}, + {0.7, 1.7, 0.6, 2.6, 2.2, 6.2, 0.0}, + {1.3, 1.2, 0.0, 0.0, 0.0, 3.2, 5.1}, + {0.1, 2.0, 0.0, 1.4, 0.0, 1.9, 6.3}}; + static float scale_min = 0; + static float scale_max = 6.3f; static double values2[100*100]; for (int i = 0; i < 100*100; ++i) { values2[i] = RandomRange(0,1); } static ImPlotColormap map = ImPlotColormap_Viridis; - if (ImGui::Button("Cycle Colormaps")) + if (ImGui::Button("Cycle Colormap")) map = (map + 1) % ImPlotColormap_COUNT; + ImGui::SameLine(); + ImGui::LabelText("##Colormap Index", cmap_names[map]); + ImGui::DragFloatRange2("Scale (Left Only)",&scale_min,&scale_max,0.01f); static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax; if (ImPlot::BeginPlot("##Heatmap1",NULL,NULL,ImVec2(250,250),0,axes_flags,axes_flags)) { ImPlot::SetColormap(map); - ImPlot::PlotHeatmap("heat",values1[0],7,7,0,6.3); + ImPlot::PlotHeatmap("heat",values1[0],7,7,scale_min,scale_max); ImPlot::EndPlot(); - ImPlot::RestoreColormap(); + ImPlot::SetColormap(ImPlotColormap_Default); } ImGui::SameLine(); + SetNextPlotLimits(1,2,1,2); if (ImPlot::BeginPlot("##Heatmap2",NULL,NULL,ImVec2(250,250),0,axes_flags,axes_flags)) { ImPlot::SetColormap(map); - ImPlot::PlotHeatmap("heat",values2,100,100,0,1,false); + ImPlot::PlotHeatmap("heat",values2,100,100,0,1,false,{1,1},{2,2}); ImPlot::EndPlot(); - ImPlot::RestoreColormap(); + ImPlot::SetColormap(ImPlotColormap_Default); } } //------------------------------------------------------------------------- @@ -379,9 +387,16 @@ void ShowDemoWindow(bool* p_open) { } } //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Markers and Text")) { + if (ImGui::CollapsingHeader("Colormaps, Markers, and Text")) { + static ImPlotColormap map = ImPlotColormap_Default; + if (ImGui::Button("Cycle Colormap")) + map = (map + 1) % ImPlotColormap_COUNT; + ImGui::SameLine(); + ImGui::LabelText("##Colormap Index", cmap_names[map]); + ImGui::PushID(map); // NB: The merely a workaround so that the demo can cycle color maps. You wouldn't need to do this in your own code! ImPlot::SetNextPlotLimits(0, 10, 0, 12); if (ImPlot::BeginPlot("##MarkerStyles", NULL, NULL, ImVec2(-1,0), 0, 0, 0)) { + ImPlot::SetColormap(map); t_float xs[2] = {1,4}; t_float ys[2] = {10,11}; // filled @@ -451,8 +466,11 @@ void ShowDemoWindow(bool* p_open) { ImPlot::PlotText("Open Markers", 6.75, 11.75); ImPlot::PlotText("Fancy Markers", 4.5, 4.25, true); + ImPlot::SetColormap(ImPlotColormap_Default); + ImPlot::EndPlot(); } + ImGui::PopID(); } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Log Scale")) { @@ -841,7 +859,7 @@ void ShowDemoWindow(bool* p_open) { } ImPlot::PopStyleColor(5); ImPlot::PopStyleVar(); - ImPlot::RestoreColormap(); + ImPlot::SetColormap(ImPlotColormap_Default); } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Custom Rendering")) { From 663738452feb4034a4a474a7886487d7952351dd Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Tue, 2 Jun 2020 12:35:20 -0500 Subject: [PATCH 04/13] colormaps --- implot_demo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index 671c360..68707c1 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -341,7 +341,7 @@ void ShowDemoWindow(bool* p_open) { ImGui::LabelText("##Colormap Index", cmap_names[map]); ImGui::DragFloatRange2("Scale (Left Only)",&scale_min,&scale_max,0.01f); static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax; - if (ImPlot::BeginPlot("##Heatmap1",NULL,NULL,ImVec2(250,250),0,axes_flags,axes_flags)) { + if (ImPlot::BeginPlot("##Heatmap1",NULL,NULL,ImVec2(250,250))) { ImPlot::SetColormap(map); ImPlot::PlotHeatmap("heat",values1[0],7,7,scale_min,scale_max); ImPlot::EndPlot(); From ea51c41b4ae381ba809d15daccb730741977102f Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Tue, 2 Jun 2020 22:07:27 -0500 Subject: [PATCH 05/13] finish heatmaps --- implot.cpp | 213 +++++++++++++++++++++++++++++++++--------------- implot.h | 4 + implot_demo.cpp | 51 ++++++------ 3 files changed, 176 insertions(+), 92 deletions(-) diff --git a/implot.cpp b/implot.cpp index 94c3658..de4df52 100644 --- a/implot.cpp +++ b/implot.cpp @@ -537,33 +537,6 @@ struct TransformerLogLog { // Legend Utils //----------------------------------------------------------------------------- -/// Utility function to that rounds x to powers of 2,5 and 10 for generating axis labels -/// Taken from Graphics Gems 1 Chapter 11.2, "Nice Numbers for Graph Labels" -inline double NiceNum(double x, bool round) { - double f; /* fractional part of x */ - double nf; /* nice, rounded fraction */ - int expv = (int)floor(ImLog10(x)); - f = x / ImPow(10.0, (double)expv); /* between 1 and 10 */ - if (round) - if (f < 1.5) - nf = 1; - else if (f < 3) - nf = 2; - else if (f < 7) - nf = 5; - else - nf = 10; - else if (f <= 1) - nf = 1; - else if (f <= 2) - nf = 2; - else if (f <= 5) - nf = 5; - else - nf = 10; - return nf * ImPow(10.0, expv); -} - ImPlotItem* RegisterItem(const char* label_id) { ImGuiID id = ImGui::GetID(label_id); ImPlotItem* item = gp.CurrentPlot->Items.GetOrAddByKey(id); @@ -595,6 +568,33 @@ const char* GetLegendLabel(int i) { // Tick Utils //----------------------------------------------------------------------------- +/// Utility function to that rounds x to powers of 2,5 and 10 for generating axis labels +/// Taken from Graphics Gems 1 Chapter 11.2, "Nice Numbers for Graph Labels" +inline double NiceNum(double x, bool round) { + double f; /* fractional part of x */ + double nf; /* nice, rounded fraction */ + int expv = (int)floor(ImLog10(x)); + f = x / ImPow(10.0, (double)expv); /* between 1 and 10 */ + if (round) + if (f < 1.5) + nf = 1; + else if (f < 3) + nf = 2; + else if (f < 7) + nf = 5; + else + nf = 10; + else if (f <= 1) + nf = 1; + else if (f <= 2) + nf = 2; + else if (f <= 5) + nf = 5; + else + nf = 10; + return nf * ImPow(10.0, expv); +} + inline void GetTicks(const ImPlotRange& range, int nMajor, int nMinor, bool logscale, ImVector &out) { out.shrink(0); if (logscale) { @@ -616,7 +616,6 @@ inline void GetTicks(const ImPlotRange& range, int nMajor, int nMinor, bool logs } } else { - const double nice_range = NiceNum(range.Size() * 0.99, 0); const double interval = NiceNum(nice_range / (nMajor - 1), 1); const double graphmin = floor(range.Min / interval) * interval; @@ -1340,17 +1339,17 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons //----------------------------------------------------------------------------- template -bool Dragdouble(const char* label, F* v, float v_speed, F v_min, F v_max) { +bool DragFloat(const char* label, F* v, float v_speed, F v_min, F v_max) { return false; } template <> -bool Dragdouble(const char* label, double* v, float v_speed, double v_min, double v_max) { +bool DragFloat(const char* label, double* v, float v_speed, double v_min, double v_max) { return ImGui::DragScalar(label, ImGuiDataType_Double, v, v_speed, &v_min, &v_max, "%.3f", 1); } template <> -bool Dragdouble(const char* label, float* v, float v_speed, float v_min, float v_max) { +bool DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max) { return ImGui::DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, "%.3f", 1); } @@ -1370,7 +1369,7 @@ inline void AxisMenu(ImPlotAxis& Axis) { ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.25f); } - Dragdouble("Min", &Axis.Range.Min, 0.01f + 0.01f * (float)Axis.Range.Size(), -HUGE_VAL, Axis.Range.Max - DBL_EPSILON); + DragFloat("Min", &Axis.Range.Min, 0.01f + 0.01f * (float)Axis.Range.Size(), -HUGE_VAL, Axis.Range.Max - DBL_EPSILON); if (lock_min) { ImGui::PopItemFlag(); ImGui::PopStyleVar(); } @@ -1381,7 +1380,7 @@ inline void AxisMenu(ImPlotAxis& Axis) { if (lock_max) { ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.25f); } - Dragdouble("Max", &Axis.Range.Max, 0.01f + 0.01f * (float)Axis.Range.Size(), Axis.Range.Min + DBL_EPSILON, HUGE_VAL); + DragFloat("Max", &Axis.Range.Max, 0.01f + 0.01f * (float)Axis.Range.Size(), Axis.Range.Min + DBL_EPSILON, HUGE_VAL); if (lock_max) { ImGui::PopItemFlag(); ImGui::PopStyleVar(); @@ -1561,7 +1560,6 @@ void EndPlot() { gp.Col_Border, 1); } } - ImGui::PopClipRect(); PushPlotClipRect(); @@ -2110,23 +2108,23 @@ inline void RenderLine(ImDrawList& DrawList, const ImVec2& p1, const ImVec2& p2, DrawList._VtxCurrentIdx += 4; } -inline void RenderRect(ImDrawList& DrawList, const ImVec2& p1, const ImVec2& p2, ImU32 col, ImVec2 uv) { +inline void RenderRect(ImDrawList& DrawList, const ImVec2& p1, const ImVec2& p2, ImU32 col_ul, ImU32 col_ur, ImU32 col_bl, ImU32 col_br, ImVec2 uv) { DrawList._VtxWritePtr[0].pos.x = p1.x; DrawList._VtxWritePtr[0].pos.y = p1.y; DrawList._VtxWritePtr[0].uv = uv; - DrawList._VtxWritePtr[0].col = col; + DrawList._VtxWritePtr[0].col = col_ul; DrawList._VtxWritePtr[1].pos.x = p2.x; DrawList._VtxWritePtr[1].pos.y = p1.y; DrawList._VtxWritePtr[1].uv = uv; - DrawList._VtxWritePtr[1].col = col; + DrawList._VtxWritePtr[1].col = col_ur; DrawList._VtxWritePtr[2].pos.x = p2.x; DrawList._VtxWritePtr[2].pos.y = p2.y; DrawList._VtxWritePtr[2].uv = uv; - DrawList._VtxWritePtr[2].col = col; + DrawList._VtxWritePtr[2].col = col_br; DrawList._VtxWritePtr[3].pos.x = p1.x; DrawList._VtxWritePtr[3].pos.y = p2.y; DrawList._VtxWritePtr[3].uv = uv; - DrawList._VtxWritePtr[3].col = col; + DrawList._VtxWritePtr[3].col = col_bl; DrawList._VtxWritePtr += 4; DrawList._IdxWritePtr[0] = (ImDrawIdx)(DrawList._VtxCurrentIdx); DrawList._IdxWritePtr[1] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1); @@ -2841,22 +2839,13 @@ void PlotPieChart(const char** label_ids, double* values, int count, double x, d // PLOT HEATMAP //----------------------------------------------------------------------------- -void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, double scale_min, double scale_max, bool show_labels, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotHeatmap() Needs to be called between BeginPlot() and EndPlot()!"); - ImPlotItem* item = RegisterItem(label_id); - if (!item->Show) - return; - if (gp.FitThisFrame) { - FitPoint(bounds_min); - FitPoint(bounds_max); - } - ImDrawList& DrawList = *ImGui::GetWindowDrawList(); +template +void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* values, int rows, int cols, T scale_min, T scale_max, bool show_labels, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) { const double w = (bounds_max.x - bounds_min.x) / cols; const double h = (bounds_max.y - bounds_min.y) / rows; const ImPlotPoint half_size(w*0.5,h*0.5); const int n = rows * cols; int i = 0; - ImGui::PushClipRect(gp.BB_Grid.Min, gp.BB_Grid.Max, true); DrawList.PrimReserve(6*n, 4*n); const ImVec2 uv = DrawList._Data->TexUvWhitePixel; for (int r = 0; r < rows; ++r) { @@ -2864,13 +2853,13 @@ void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, ImPlotPoint p; p.x = bounds_min.x + 0.5*w + c*w; p.y = bounds_min.y + 1 - (0.5*h + r*h); - ImVec2 px = PlotToPixels(p); - ImVec2 a = PlotToPixels(p.x - half_size.x, p.y - half_size.y); - ImVec2 b = PlotToPixels(p.x + half_size.x, p.y + half_size.y); - float t = (float)Remap(values[i], scale_min, scale_max, 0.0, 1.0); + ImVec2 px = transformer(p); + ImVec2 a = transformer(p.x - half_size.x, p.y - half_size.y); + ImVec2 b = transformer(p.x + half_size.x, p.y + half_size.y); + float t = (float)Remap(values[i], scale_min, scale_max, T(0), T(1)); ImVec4 color = LerpColormap(t); - // DrawList.AddRectFilled(a, b, ImGui::GetColorU32(color)); - RenderRect(DrawList, a, b, ImGui::GetColorU32(color), uv); + ImU32 col = ImGui::GetColorU32(color); + RenderRect(DrawList, a, b, col, col, col, col, uv); i++; } } @@ -2882,7 +2871,7 @@ void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, ImPlotPoint p; p.x = bounds_min.x + 0.5*w + c*w; p.y = bounds_min.y + 1 - (0.5*h + r*h); - ImVec2 px = PlotToPixels(p); + ImVec2 px = transformer(p); char buff[32]; sprintf(buff, "%g", values[i]); ImVec2 size = ImGui::CalcTextSize(buff); @@ -2891,9 +2880,48 @@ void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, } } } +} + +template +void PlotHeatmapEx(const char* label_id, const T* values, int rows, int cols, T scale_min, T scale_max, bool show_labels, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) { + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotHeatmap() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(scale_min != scale_max, "Scale values must be different!"); + ImPlotItem* item = RegisterItem(label_id); + if (!item->Show) + return; + if (gp.FitThisFrame) { + FitPoint(bounds_min); + FitPoint(bounds_max); + } + ImDrawList& DrawList = *ImGui::GetWindowDrawList(); + ImGui::PushClipRect(gp.BB_Grid.Min, gp.BB_Grid.Max, true); + ImPlotState* plot = gp.CurrentPlot; + int y_axis = plot->CurrentYAxis; + if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale) && HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) + RenderHeatmap(TransformerLogLog(y_axis), DrawList, values, rows, cols, scale_min, scale_max, show_labels, bounds_min, bounds_max); + else if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale)) + RenderHeatmap(TransformerLogLin(y_axis), DrawList, values, rows, cols, scale_min, scale_max, show_labels, bounds_min, bounds_max); + else if (HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) + RenderHeatmap(TransformerLinLog(y_axis), DrawList, values, rows, cols, scale_min, scale_max, show_labels, bounds_min, bounds_max); + else + RenderHeatmap(TransformerLinLin(y_axis), DrawList, values, rows, cols, scale_min, scale_max, show_labels, bounds_min, bounds_max); ImGui::PopClipRect(); } +//----------------------------------------------------------------------------- +// float + +void PlotHeatmap(const char* label_id, const float* values, int rows, int cols, float scale_min, float scale_max, bool show_labels, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) { + return PlotHeatmapEx(label_id, values, rows, cols, scale_min, scale_max, show_labels, bounds_min, bounds_max); +} + +//----------------------------------------------------------------------------- +// double + +void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, double scale_min, double scale_max, bool show_labels, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) { + return PlotHeatmapEx(label_id, values, rows, cols, scale_min, scale_max, show_labels, bounds_min, bounds_max); +} + //----------------------------------------------------------------------------- // PLOT DIGITAL //----------------------------------------------------------------------------- @@ -3023,12 +3051,12 @@ void PlotText(const char* text, double x, double y, bool vertical, const ImVec2& //------------------------------------------------------------------------------ void SetColormap(const ImVec4* colors, int num_colors) { - IM_ASSERT_USER_ERROR(num_colors > 0, "The number of colors cannot be 0!"); + IM_ASSERT_USER_ERROR(num_colors > 1, "The number of colors must be greater than 1!"); static ImVector user_colormap; user_colormap.shrink(0); user_colormap.reserve(num_colors); - for (int i = 0; i < num_colors; ++i) - user_colormap.push_back(colors[i]); + for (int i = 0; i < num_colors; ++i) + user_colormap.push_back(colors[i]); gp.Colormap = &user_colormap[0]; gp.ColormapSize = num_colors; } @@ -3045,15 +3073,68 @@ ImVec4 GetColormapColor(int index) { } ImVec4 LerpColormap(float t) { - t = ImClamp(t,0.0f,1.0f); - int i1 = (int)((gp.ColormapSize-1) * t); + float tc = ImClamp(t,0.0f,1.0f); + int i1 = (int)((gp.ColormapSize -1 ) * tc); int i2 = i1 + 1; if (i2 == gp.ColormapSize) return gp.Colormap[i1]; - float t1 = (float)i1 / (float)gp.ColormapSize; - float t2 = (float)i2 / (float)gp.ColormapSize; - t = Remap(t, t1, t2, 0.0f, 1.0f); - return ImLerp(gp.Colormap[i1], gp.Colormap[i2], t); + float t1 = (float)i1 / (float)(gp.ColormapSize - 1); + float t2 = (float)i2 / (float)(gp.ColormapSize - 1); + float tr = Remap(t, t1, t2, 0.0f, 1.0f); + return ImLerp(gp.Colormap[i1], gp.Colormap[i2], tr); +} + +void ShowColormapScale(double scale_min, double scale_max, float height) { + static ImVector ticks; + static ImGuiTextBuffer txt_buff; + ImPlotRange range; + range.Min = scale_min; + range.Max = scale_max; + GetTicks(range, 10, 0, false, ticks); + LabelTicks(ticks, false, txt_buff); + float max_width = 0; + for (int i = 0; i < ticks.Size; ++i) + max_width = ticks[i].Size.x > max_width ? ticks[i].Size.x : max_width; + + ImGuiContext &G = *GImGui; + ImGuiWindow * Window = G.CurrentWindow; + if (Window->SkipItems) + return; + const ImGuiStyle &Style = G.Style; + const float txt_off = 5; + const float bar_w = 20; + + ImDrawList &DrawList = *Window->DrawList; + ImVec2 size(bar_w + txt_off + max_width + 2 * Style.WindowPadding.x, height); + ImRect bb_frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + size); + ImGui::ItemSize(bb_frame); + if (!ImGui::ItemAdd(bb_frame, 0, &bb_frame)) + return; + ImGui::RenderFrame(bb_frame.Min, bb_frame.Max, ImGui::GetColorU32(ImGuiCol_FrameBg)); + ImRect bb_grad(bb_frame.Min + Style.WindowPadding, bb_frame.Min + ImVec2(bar_w + Style.WindowPadding.x, height - Style.WindowPadding.y)); + + int num_cols = GetColormapSize(); + float h_step = (height - 2 * Style.WindowPadding.y) / (num_cols - 1); + const ImVec2 uv = DrawList._Data->TexUvWhitePixel; + for (int i = 0; i < num_cols-1; ++i) { + ImRect rect(bb_grad.Min.x, bb_grad.Min.y + h_step * i, bb_grad.Max.x, bb_grad.Min.y + h_step * (i + 1)); + ImU32 col1 = ImGui::GetColorU32(GetColormapColor(num_cols - 1 - i)); + ImU32 col2 = ImGui::GetColorU32(GetColormapColor(num_cols - 1 - (i+1))); + DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col1, col2, col2); + } + ImU32 col_border = gp.Style.Colors[ImPlotCol_PlotBorder].w == -1 ? ImGui::GetColorU32(ImGuiCol_Text, 0.5f) : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_PlotBorder]); + + ImGui::PushClipRect(bb_frame.Min, bb_frame.Max, true); + for (int i = 0; i < ticks.Size; ++i) { + float ypos = Remap((float)ticks[i].PlotPos, (float)range.Max, (float)range.Min, bb_grad.Min.y, bb_grad.Max.y); + if (ypos < bb_grad.Max.y - 2 && ypos > bb_grad.Min.y + 2) + DrawList.AddLine(ImVec2(bb_grad.Max.x-1, ypos), ImVec2(bb_grad.Max.x - (ticks[i].Major ? 10.0f : 5.0f), ypos), col_border, 1.0f); + DrawList.AddText(ImVec2(bb_grad.Max.x-1, ypos) + ImVec2(txt_off, -ticks[i].Size.y * 0.5f), ImGui::GetColorU32(ImGuiCol_Text), txt_buff.Buf.Data + ticks[i].TextOffset); + } + ImGui::PopClipRect(); + + DrawList.AddRect(bb_grad.Min, bb_grad.Max, col_border); + } void SetColormap(ImPlotColormap colormap) { diff --git a/implot.h b/implot.h index c18b4a1..cdbb4c6 100644 --- a/implot.h +++ b/implot.h @@ -243,6 +243,7 @@ void PlotPieChart(const char** label_ids, float* values, int count, float x, flo void PlotPieChart(const char** label_ids, double* values, int count, double x, double y, double radius, bool show_percents = true, double angle0 = 90); // Plots a 2D heatmap chart. Values are expected to be in row-major order. +void PlotHeatmap(const char* label_id, const float* values, int rows, int cols, float scale_min, float scale_max, bool show_labels = true, const ImPlotPoint& bounds_min = ImPlotPoint(0,0), const ImPlotPoint& bounds_max = ImPlotPoint(1,1)); void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, double scale_min, double scale_max, bool show_labels = true, const ImPlotPoint& bounds_min = ImPlotPoint(0,0), const ImPlotPoint& bounds_max = ImPlotPoint(1,1)); // Plots digital data. @@ -325,6 +326,9 @@ ImPlotPoint PixelsToPlot(const ImVec2& pix, int y_axis = -1); // Convert a position in the current plot's coordinate system to pixels. A negative y_axis uses the current value of SetPlotYAxis (0 initially). ImVec2 PlotToPixels(const ImPlotPoint& plt, int y_axis = -1); +// Renders a vertical color scale using the current color map +void ShowColormapScale(double scale_min, double scale_max, float height); + // Push clip rect for rendering to current plot area. void PushPlotClipRect(); // Pop plot clip rect. diff --git a/implot_demo.cpp b/implot_demo.cpp index 68707c1..fd5431c 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -54,7 +54,7 @@ typedef ImVec2 t_float2; #define Fmod fmodf #endif -namespace { +namespace ImPlot { t_float RandomRange(t_float min, t_float max) { t_float scale = rand() / (t_float) RAND_MAX; @@ -119,10 +119,6 @@ struct BenchmarkItem { ImVec4 Col; }; -} // private namespace - -namespace ImPlot { - void ShowDemoWindow(bool* p_open) { static const char* cmap_names[] = {"Default","Dark","Pastel","Paired","Viridis","Plasma","Hot","Cool","Pink","Jet"}; static bool show_app_metrics = false; @@ -321,40 +317,43 @@ void ShowDemoWindow(bool* p_open) { } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Heatmaps")) { - static double values1[7][7] = {{0.8, 2.4, 2.5, 3.9, 0.0, 4.0, 0.0}, - {2.4, 0.0, 4.0, 1.0, 2.7, 0.0, 0.0}, - {1.1, 2.4, 0.8, 4.3, 1.9, 4.4, 0.0}, - {0.6, 0.0, 0.3, 0.0, 3.1, 0.0, 0.0}, - {0.7, 1.7, 0.6, 2.6, 2.2, 6.2, 0.0}, - {1.3, 1.2, 0.0, 0.0, 0.0, 3.2, 5.1}, - {0.1, 2.0, 0.0, 1.4, 0.0, 1.9, 6.3}}; + static t_float values1[7][7] = {{0.8f, 2.4f, 2.5f, 3.9f, 0.0f, 4.0f, 0.0f}, + {2.4f, 0.0f, 4.0f, 1.0f, 2.7f, 0.0f, 0.0f}, + {1.1f, 2.4f, 0.8f, 4.3f, 1.9f, 4.4f, 0.0f}, + {0.6f, 0.0f, 0.3f, 0.0f, 3.1f, 0.0f, 0.0f}, + {0.7f, 1.7f, 0.6f, 2.6f, 2.2f, 6.2f, 0.0f}, + {1.3f, 1.2f, 0.0f, 0.0f, 0.0f, 3.2f, 5.1f}, + {0.1f, 2.0f, 0.0f, 1.4f, 0.0f, 1.9f, 6.3f}}; static float scale_min = 0; static float scale_max = 6.3f; - static double values2[100*100]; + static t_float values2[100*100]; for (int i = 0; i < 100*100; ++i) { values2[i] = RandomRange(0,1); } static ImPlotColormap map = ImPlotColormap_Viridis; - if (ImGui::Button("Cycle Colormap")) + if (ImGui::Button("Cycle Colormap",ImVec2(225,0))) map = (map + 1) % ImPlotColormap_COUNT; + ImPlot::SetColormap(map); ImGui::SameLine(); ImGui::LabelText("##Colormap Index", cmap_names[map]); - ImGui::DragFloatRange2("Scale (Left Only)",&scale_min,&scale_max,0.01f); + ImGui::SetNextItemWidth(225); + ImGui::DragFloat("Max",&scale_max,0.01f,0.1f,20); static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax; - if (ImPlot::BeginPlot("##Heatmap1",NULL,NULL,ImVec2(250,250))) { - ImPlot::SetColormap(map); + if (ImPlot::BeginPlot("##Heatmap1",NULL,NULL,ImVec2(225,225),ImPlotFlags_ContextMenu,axes_flags,axes_flags)) { ImPlot::PlotHeatmap("heat",values1[0],7,7,scale_min,scale_max); ImPlot::EndPlot(); - ImPlot::SetColormap(ImPlotColormap_Default); } ImGui::SameLine(); - SetNextPlotLimits(1,2,1,2); - if (ImPlot::BeginPlot("##Heatmap2",NULL,NULL,ImVec2(250,250),0,axes_flags,axes_flags)) { - ImPlot::SetColormap(map); - ImPlot::PlotHeatmap("heat",values2,100,100,0,1,false,{1,1},{2,2}); + ImPlot::ShowColormapScale(scale_min, scale_max, 225); + ImPlot::SetColormap(ImPlotColormap_Default); + ImGui::SameLine(); + static ImVec4 gray[2] = {ImVec4(0,0,0,1), ImVec4(1,1,1,1)}; + ImPlot::SetColormap(&gray[0], 2); + if (ImPlot::BeginPlot("##Heatmap2",NULL,NULL,ImVec2(225,225),ImPlotFlags_ContextMenu,0,0)) { + ImPlot::PlotHeatmap("heat",values2,100,100,0,1,false); ImPlot::EndPlot(); - ImPlot::SetColormap(ImPlotColormap_Default); } + ImPlot::SetColormap(ImPlotColormap_Default); } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Realtime Plots")) { @@ -387,13 +386,13 @@ void ShowDemoWindow(bool* p_open) { } } //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Colormaps, Markers, and Text")) { + if (ImGui::CollapsingHeader("Markers and Text")) { static ImPlotColormap map = ImPlotColormap_Default; - if (ImGui::Button("Cycle Colormap")) + if (ImGui::Button("Cycle Colormap##2")) map = (map + 1) % ImPlotColormap_COUNT; ImGui::SameLine(); ImGui::LabelText("##Colormap Index", cmap_names[map]); - ImGui::PushID(map); // NB: The merely a workaround so that the demo can cycle color maps. You wouldn't need to do this in your own code! + ImGui::PushID(map); // NB: The merely a workaround so that the demo can cycle color maps. You wouldn't need to do this in your own code! ImPlot::SetNextPlotLimits(0, 10, 0, 12); if (ImPlot::BeginPlot("##MarkerStyles", NULL, NULL, ImVec2(-1,0), 0, 0, 0)) { ImPlot::SetColormap(map); From 75441ef918d1a58df597991d751688b5c3081cff Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Tue, 2 Jun 2020 22:26:45 -0500 Subject: [PATCH 06/13] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3d8d836..3aae865 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ ImPlot is an immediate mode plotting widget for [Dear ImGui](https://github.com/ - vertical/horizontal bars - error bars - pie charts + - heatmaps - and more likely to come - mix/match multiple plot items on a single plot - configurable axes ranges and scaling (linear/log) @@ -29,6 +30,7 @@ ImPlot is an immediate mode plotting widget for [Dear ImGui](https://github.com/ - optional legend with toggle buttons to quickly show/hide items - size-aware grid with smart labels that are always power-of-ten multiples of 1, 2, and 5 - default styling based on current ImGui theme, but most elements can be customized independently +- several built-in colormaps - mouse cursor location display and optional crosshairs cursor - customizable data getters and data striding (just like ImGui:PlotLine) - relatively good performance for high density plots From 46d1b2f0f80de972faf031b09ee71e683079faf1 Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Tue, 2 Jun 2020 22:27:34 -0500 Subject: [PATCH 07/13] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3aae865..c572ed9 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,11 @@ ImPlot is an immediate mode plotting widget for [Dear ImGui](https://github.com/ - controls for zooming, panning, box selection, and auto-fitting data - controls for creating persistent query ranges (see demo) - several plot styling options: 10 marker types, adjustable marker sizes, line weights, outline colors, fill colors, etc. +- built-in and user definable colormaps - optional plot titles, axis labels, and grid labels - optional legend with toggle buttons to quickly show/hide items - size-aware grid with smart labels that are always power-of-ten multiples of 1, 2, and 5 - default styling based on current ImGui theme, but most elements can be customized independently -- several built-in colormaps - mouse cursor location display and optional crosshairs cursor - customizable data getters and data striding (just like ImGui:PlotLine) - relatively good performance for high density plots From ea1cfad9b38e2e51dbb0469c06434a5a5451c2f2 Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Tue, 2 Jun 2020 22:30:03 -0500 Subject: [PATCH 08/13] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c572ed9..f2aaf20 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ImPlot is an immediate mode plotting widget for [Dear ImGui](https://github.com/ - error bars - pie charts - heatmaps - - and more likely to come + - and more likely to come (check [Announcements](https://github.com/epezent/implot/issues/48)) - mix/match multiple plot items on a single plot - configurable axes ranges and scaling (linear/log) - reversible and lockable axes @@ -79,7 +79,7 @@ A: Yes to y-axes (up to three), "not yet" to x-axes. **Q: Does ImPlot support [insert plot type]?** -A: Maybe. Check the gallery and demo to see if your desired plot type is shown. If not, consider submitting an issue or better yet, a PR! +A: Maybe. Check the demo, gallery, or [Announcements](https://github.com/epezent/implot/issues/48) to see if your desired plot type is shown. If not, consider submitting an issue or better yet, a PR! **Q: Does ImPlot support 3D plots?** From bf72f342a72cae8ff3a3891d6cd38996b4279c56 Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Tue, 2 Jun 2020 22:30:25 -0500 Subject: [PATCH 09/13] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f2aaf20..6f2c176 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ImPlot is an immediate mode plotting widget for [Dear ImGui](https://github.com/ - error bars - pie charts - heatmaps - - and more likely to come (check [Announcements](https://github.com/epezent/implot/issues/48)) + - and more likely to come - mix/match multiple plot items on a single plot - configurable axes ranges and scaling (linear/log) - reversible and lockable axes From 6ea8b45d75b273699c67dfe777b98b7bf6973875 Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Wed, 3 Jun 2020 09:06:16 -0500 Subject: [PATCH 10/13] remove --- implot.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/implot.cpp b/implot.cpp index de4df52..36c3a3a 100644 --- a/implot.cpp +++ b/implot.cpp @@ -60,7 +60,6 @@ You can read releases logs https://github.com/epezent/implot/releases for more d #include "implot.h" #include "imgui_internal.h" -#include #ifdef _MSC_VER #define sprintf sprintf_s From 6c9a720d2ccfdf6c76ca3463771cacef98e4eca2 Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Wed, 3 Jun 2020 09:50:06 -0500 Subject: [PATCH 11/13] ensure next plot data is initialized --- implot.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/implot.cpp b/implot.cpp index 36c3a3a..8dcee5c 100644 --- a/implot.cpp +++ b/implot.cpp @@ -304,13 +304,23 @@ struct ImPlotState { int CurrentYAxis; }; -struct ImNextPlotData { +struct ImPlotNextPlotData { + ImPlotNextPlotData() { + HasXRange = false; + HasUserXTickLabels = false; + for (int i = 0; i < MAX_Y_AXES; ++i) { + HasYRange[i] = false; + HasUserYTickLabels[i] = false; + } + } ImGuiCond XRangeCond; ImGuiCond YRangeCond[MAX_Y_AXES]; bool HasXRange; bool HasYRange[MAX_Y_AXES]; ImPlotRange X; ImPlotRange Y[MAX_Y_AXES]; + bool HasUserXTickLabels; + bool HasUserYTickLabels[MAX_Y_AXES]; }; /// Holds Plot state information that must persist only between calls to BeginPlot()/EndPlot() @@ -374,7 +384,7 @@ struct ImPlotContext { ImPlotStyle Style; ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() - ImNextPlotData NextPlotData; + ImPlotNextPlotData NextPlotData; // Digital plot item count int DigitalPlotItemCnt; int DigitalPlotOffset; @@ -740,7 +750,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons ImGuiContext &G = *GImGui; ImGuiWindow * Window = G.CurrentWindow; if (Window->SkipItems) { - gp.NextPlotData = ImNextPlotData(); + gp.NextPlotData = ImPlotNextPlotData(); return false; } @@ -886,7 +896,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons gp.BB_Frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); ImGui::ItemSize(gp.BB_Frame); if (!ImGui::ItemAdd(gp.BB_Frame, 0, &gp.BB_Frame)) { - gp.NextPlotData = ImNextPlotData(); + gp.NextPlotData = ImPlotNextPlotData(); gp.CurrentPlot = NULL; if (!HasFlag(plot.Flags, ImPlotFlags_NoChild)) ImGui::EndChild(); @@ -1736,7 +1746,7 @@ void EndPlot() { // Null current plot/data gp.CurrentPlot = NULL; // Reset next plot data - gp.NextPlotData = ImNextPlotData(); + gp.NextPlotData = ImPlotNextPlotData(); // Pop ImGui::PushID at the end of BeginPlot ImGui::PopID(); // End child window From b497e7f72de317daf8f3fe6dfe939b9b4fd6b6c0 Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Wed, 3 Jun 2020 09:54:25 -0500 Subject: [PATCH 12/13] rename ImTick to ImPlotTick --- implot.cpp | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/implot.cpp b/implot.cpp index 8dcee5c..26c749b 100644 --- a/implot.cpp +++ b/implot.cpp @@ -229,8 +229,8 @@ ImVec4 NextColor(); //----------------------------------------------------------------------------- /// Tick mark info -struct ImTick { - ImTick(double value, bool major, bool render_label = true) { +struct ImPlotTick { + ImPlotTick(double value, bool major, bool render_label = true) { PlotPos = value; Major = major; RenderLabel = render_label; @@ -354,7 +354,7 @@ struct ImPlotContext { AxisColor Col_X; AxisColor Col_Y[MAX_Y_AXES]; // Tick marks - ImVector XTicks, YTicks[MAX_Y_AXES]; + ImVector XTicks, YTicks[MAX_Y_AXES]; ImGuiTextBuffer XTickLabels, YTickLabels[MAX_Y_AXES]; float AxisLabelReference[MAX_Y_AXES]; // Transformation cache @@ -604,7 +604,7 @@ inline double NiceNum(double x, bool round) { return nf * ImPow(10.0, expv); } -inline void GetTicks(const ImPlotRange& range, int nMajor, int nMinor, bool logscale, ImVector &out) { +inline void GetTicks(const ImPlotRange& range, int nMajor, int nMinor, bool logscale, ImVector &out) { out.shrink(0); if (logscale) { if (range.Min <= 0 || range.Max <= 0) @@ -616,11 +616,11 @@ inline void GetTicks(const ImPlotRange& range, int nMajor, int nMinor, bool logs double major2 = ImPow(10, (double)(e + 1)); double interval = (major2 - major1) / 9; if (major1 >= (range.Min - DBL_EPSILON) && major1 <= (range.Max + DBL_EPSILON)) - out.push_back(ImTick(major1, true)); + out.push_back(ImPlotTick(major1, true)); for (int i = 1; i < 9; ++i) { double minor = major1 + i * interval; if (minor >= (range.Min - DBL_EPSILON) && minor <= (range.Max + DBL_EPSILON)) - out.push_back(ImTick(minor, false, false)); + out.push_back(ImPlotTick(minor, false, false)); } } } @@ -631,21 +631,21 @@ inline void GetTicks(const ImPlotRange& range, int nMajor, int nMinor, bool logs const double graphmax = ceil(range.Max / interval) * interval; for (double major = graphmin; major < graphmax + 0.5 * interval; major += interval) { if (major >= range.Min && major <= range.Max) - out.push_back(ImTick(major, true)); + out.push_back(ImPlotTick(major, true)); for (int i = 1; i < nMinor; ++i) { double minor = major + i * interval / nMinor; if (minor >= range.Min && minor <= range.Max) - out.push_back(ImTick(minor, false)); + out.push_back(ImPlotTick(minor, false)); } } } } -inline void LabelTicks(ImVector &ticks, bool scientific, ImGuiTextBuffer& buffer) { +inline void LabelTicks(ImVector &ticks, bool scientific, ImGuiTextBuffer& buffer) { buffer.Buf.resize(0); char temp[32]; for (int t = 0; t < ticks.Size; t++) { - ImTick *tk = &ticks[t]; + ImPlotTick *tk = &ticks[t]; if (tk->RenderLabel) { tk->TextOffset = buffer.size(); if (scientific) @@ -937,7 +937,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons if (y[i].present && HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_TickLabels)) { LabelTicks(gp.YTicks[i], HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_Scientific), gp.YTickLabels[i]); for (int t = 0; t < gp.YTicks[i].Size; t++) { - ImTick *yt = &gp.YTicks[i][t]; + ImPlotTick *yt = &gp.YTicks[i][t]; max_label_width[i] = yt->Size.x > max_label_width[i] ? yt->Size.x : max_label_width[i]; } } @@ -1245,14 +1245,14 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // transform ticks if (gp.RenderX) { for (int t = 0; t < gp.XTicks.Size; t++) { - ImTick *xt = &gp.XTicks[t]; + ImPlotTick *xt = &gp.XTicks[t]; xt->PixelPos = PlotToPixels(xt->PlotPos, 0, 0).x; } } for (int i = 0; i < MAX_Y_AXES; i++) { if (gp.RenderY[i]) { for (int t = 0; t < gp.YTicks[i].Size; t++) { - ImTick *yt = &gp.YTicks[i][t]; + ImPlotTick *yt = &gp.YTicks[i][t]; yt->PixelPos = PlotToPixels(0, yt->PlotPos, i).y; } } @@ -1261,7 +1261,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // render grid if (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_GridLines)) { for (int t = 0; t < gp.XTicks.Size; t++) { - ImTick *xt = &gp.XTicks[t]; + ImPlotTick *xt = &gp.XTicks[t]; DrawList.AddLine(ImVec2(xt->PixelPos, gp.BB_Grid.Min.y), ImVec2(xt->PixelPos, gp.BB_Grid.Max.y), xt->Major ? gp.Col_X.Major : gp.Col_X.Minor, 1); } } @@ -1269,7 +1269,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons for (int i = 0; i < MAX_Y_AXES; i++) { if (y[i].present && HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_GridLines)) { for (int t = 0; t < gp.YTicks[i].Size; t++) { - ImTick *yt = &gp.YTicks[i][t]; + ImPlotTick *yt = &gp.YTicks[i][t]; DrawList.AddLine(ImVec2(gp.BB_Grid.Min.x, yt->PixelPos), ImVec2(gp.BB_Grid.Max.x, yt->PixelPos), yt->Major ? gp.Col_Y[i].Major : gp.Col_Y[i].Minor, 1); } } @@ -1286,7 +1286,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons if (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickLabels)) { ImGui::PushClipRect(gp.BB_Frame.Min, gp.BB_Frame.Max, true); for (int t = 0; t < gp.XTicks.Size; t++) { - ImTick *xt = &gp.XTicks[t]; + ImPlotTick *xt = &gp.XTicks[t]; if (xt->RenderLabel && xt->PixelPos >= gp.BB_Grid.Min.x - 1 && xt->PixelPos <= gp.BB_Grid.Max.x + 1) DrawList.AddText(ImVec2(xt->PixelPos - xt->Size.x * 0.5f, gp.BB_Grid.Max.y + txt_off), gp.Col_X.Txt, gp.XTickLabels.Buf.Data + xt->TextOffset); } @@ -1307,7 +1307,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons (-txt_off - max_label_width[0]) : txt_off); for (int t = 0; t < gp.YTicks[i].Size; t++) { - ImTick *yt = &gp.YTicks[i][t]; + ImPlotTick *yt = &gp.YTicks[i][t]; if (yt->RenderLabel && yt->PixelPos >= gp.BB_Grid.Min.y - 1 && yt->PixelPos <= gp.BB_Grid.Max.y + 1) { ImVec2 start(x_start, yt->PixelPos - 0.5f * yt->Size.y); DrawList.AddText(start, gp.Col_Y[i].Txt, gp.YTickLabels[i].Buf.Data + yt->TextOffset); @@ -1533,7 +1533,7 @@ void EndPlot() { PushPlotClipRect(); if (HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_TickMarks)) { for (int t = 0; t < gp.XTicks.Size; t++) { - ImTick *xt = &gp.XTicks[t]; + ImPlotTick *xt = &gp.XTicks[t]; DrawList.AddLine(ImVec2(xt->PixelPos, gp.BB_Grid.Max.y),ImVec2(xt->PixelPos, gp.BB_Grid.Max.y - (xt->Major ? 10.0f : 5.0f)), gp.Col_Border, 1); } } @@ -1552,7 +1552,7 @@ void EndPlot() { bool no_major = axis_count >= 3; for (int t = 0; t < gp.YTicks[i].Size; t++) { - ImTick *yt = &gp.YTicks[i][t]; + ImPlotTick *yt = &gp.YTicks[i][t]; ImVec2 start = ImVec2(x_start, yt->PixelPos); DrawList.AddLine( @@ -3094,7 +3094,7 @@ ImVec4 LerpColormap(float t) { } void ShowColormapScale(double scale_min, double scale_max, float height) { - static ImVector ticks; + static ImVector ticks; static ImGuiTextBuffer txt_buff; ImPlotRange range; range.Min = scale_min; From f59913b8e0dcfe20bc5d9b8c318b028726da44a2 Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Wed, 3 Jun 2020 14:37:01 -0500 Subject: [PATCH 13/13] finish custom ticks --- implot.cpp | 229 ++++++++++++++++++++++++++++++++---------------- implot.h | 7 ++ implot_demo.cpp | 53 +++++++++-- 3 files changed, 206 insertions(+), 83 deletions(-) diff --git a/implot.cpp b/implot.cpp index 26c749b..bb15520 100644 --- a/implot.cpp +++ b/implot.cpp @@ -150,6 +150,15 @@ struct OffsetCalculator { int Offsets[Count]; }; +template +void FillRange(ImVector& buffer, int n, T vmin, T vmax) { + buffer.resize(n); + T step = (vmax - vmin) / (n - 1); + for (int i = 0; i < n; ++i) { + buffer[i] = vmin + i * step; + } +} + /// Returns true if a flag is set template inline bool HasFlag(TSet set, TFlag flag) { @@ -234,6 +243,7 @@ struct ImPlotTick { PlotPos = value; Major = major; RenderLabel = render_label; + Labeled = false; } double PlotPos; float PixelPos; @@ -241,6 +251,7 @@ struct ImPlotTick { int TextOffset; bool Major; bool RenderLabel; + bool Labeled; }; struct ImPlotItem { @@ -307,10 +318,10 @@ struct ImPlotState { struct ImPlotNextPlotData { ImPlotNextPlotData() { HasXRange = false; - HasUserXTickLabels = false; + ShowDefaultTicksX = true; for (int i = 0; i < MAX_Y_AXES; ++i) { HasYRange[i] = false; - HasUserYTickLabels[i] = false; + ShowDefaultTicksY[i] = true; } } ImGuiCond XRangeCond; @@ -319,18 +330,53 @@ struct ImPlotNextPlotData { bool HasYRange[MAX_Y_AXES]; ImPlotRange X; ImPlotRange Y[MAX_Y_AXES]; - bool HasUserXTickLabels; - bool HasUserYTickLabels[MAX_Y_AXES]; + bool ShowDefaultTicksX; + bool ShowDefaultTicksY[MAX_Y_AXES]; }; /// Holds Plot state information that must persist only between calls to BeginPlot()/EndPlot() struct ImPlotContext { ImPlotContext() : RenderX(), RenderY() { - CurrentPlot = NULL; - FitThisFrame = FitX = false; + ChildWindowMade = false; + Reset(); SetColormap(ImPlotColormap_Default); } + void Reset() { + // end child window if it was made + if (ChildWindowMade) + ImGui::EndChild(); + ChildWindowMade = false; + // reset the next plot data + NextPlotData = ImPlotNextPlotData(); + // reset items count + VisibleItemCount = 0; + // reset legend items + LegendIndices.shrink(0); + LegendLabels.Buf.shrink(0); + // reset ticks/labels + XTicks.shrink(0); + XTickLabels.Buf.shrink(0); + for (int i = 0; i < 3; ++i) { + YTicks[i].shrink(0); + YTickLabels[i].Buf.shrink(0); + } + // reset extents + FitX = false; + ExtentsX.Min = HUGE_VAL; + ExtentsX.Max = -HUGE_VAL; + for (int i = 0; i < MAX_Y_AXES; i++) { + ExtentsY[i].Min = HUGE_VAL; + ExtentsY[i].Max = -HUGE_VAL; + FitY[i] = false; + } + // reset digital plot items count + DigitalPlotItemCnt = 0; + DigitalPlotOffset = 0; + // nullify plot + CurrentPlot = NULL; + } + /// ALl Plots ImPool Plots; /// Current Plot @@ -370,12 +416,15 @@ struct ImPlotContext { ImPlotRange ExtentsY[MAX_Y_AXES]; int VisibleItemCount; bool FitThisFrame; bool FitX; - bool FitY[MAX_Y_AXES] = {}; + bool FitY[MAX_Y_AXES]; // Hover states bool Hov_Frame; bool Hov_Grid; // Render flags bool RenderX, RenderY[MAX_Y_AXES]; + // Lock info + bool LockPlot; + bool ChildWindowMade; // Mouse pos ImPlotPoint LastMousePos[MAX_Y_AXES]; // Style @@ -394,7 +443,7 @@ struct ImPlotContext { static ImPlotContext gp; //----------------------------------------------------------------------------- -// Utils +// Context Utils //----------------------------------------------------------------------------- /// Returns the next unused default plot color @@ -440,7 +489,7 @@ inline void UpdateTransformCache() { } inline ImPlotPoint PixelsToPlot(float x, float y, int y_axis_in = -1) { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PixelsToPlot() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PixelsToPlot() needs to be called between BeginPlot() and EndPlot()!"); const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; ImPlotPoint plt; plt.x = (x - gp.PixelRange[y_axis].Min.x) / gp.Mx + gp.CurrentPlot->XAxis.Range.Min; @@ -462,7 +511,7 @@ ImPlotPoint PixelsToPlot(const ImVec2& pix, int y_axis) { // This function is convenient but should not be used to process a high volume of points. Use the Transformer structs below instead. inline ImVec2 PlotToPixels(double x, double y, int y_axis_in = -1) { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotToPixels() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotToPixels() needs to be called between BeginPlot() and EndPlot()!"); const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; ImVec2 pix; if (HasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale)) { @@ -604,8 +653,7 @@ inline double NiceNum(double x, bool round) { return nf * ImPow(10.0, expv); } -inline void GetTicks(const ImPlotRange& range, int nMajor, int nMinor, bool logscale, ImVector &out) { - out.shrink(0); +inline void AddDefaultTicks(const ImPlotRange& range, int nMajor, int nMinor, bool logscale, ImVector &out) { if (logscale) { if (range.Min <= 0 || range.Max <= 0) return; @@ -642,11 +690,10 @@ inline void GetTicks(const ImPlotRange& range, int nMajor, int nMinor, bool logs } inline void LabelTicks(ImVector &ticks, bool scientific, ImGuiTextBuffer& buffer) { - buffer.Buf.resize(0); char temp[32]; for (int t = 0; t < ticks.Size; t++) { ImPlotTick *tk = &ticks[t]; - if (tk->RenderLabel) { + if (tk->RenderLabel && !tk->Labeled) { tk->TextOffset = buffer.size(); if (scientific) sprintf(temp, "%.0e", tk->PlotPos); @@ -654,10 +701,24 @@ inline void LabelTicks(ImVector &ticks, bool scientific, ImGuiTextBu sprintf(temp, "%g", tk->PlotPos); buffer.append(temp, temp + strlen(temp) + 1); tk->Size = ImGui::CalcTextSize(buffer.Buf.Data + tk->TextOffset); + tk->Labeled = true; } } } +inline void AddCustomTicks(const double* values, const char** labels, int n, ImVector& ticks, ImGuiTextBuffer& buffer) { + for (int i = 0; i < n; ++i) { + ImPlotTick tick(values[i],false); + tick.TextOffset = buffer.size(); + if (labels != NULL) { + buffer.append(labels[i], labels[i] + strlen(labels[i]) + 1); + tick.Size = ImGui::CalcTextSize(labels[i]); + tick.Labeled = true; + } + ticks.push_back(tick); + } +} + namespace { struct AxisState { ImPlotAxis* axis; @@ -750,7 +811,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons ImGuiContext &G = *GImGui; ImGuiWindow * Window = G.CurrentWindow; if (Window->SkipItems) { - gp.NextPlotData = ImPlotNextPlotData(); + gp.Reset(); return false; } @@ -797,6 +858,10 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons ImGui::BeginChild(title, ImVec2(size.x == 0 ? default_w : size.x, size.y == 0 ? default_h : size.y)); Window = ImGui::GetCurrentWindow(); Window->ScrollMax.y = 1.0f; + gp.ChildWindowMade = true; + } + else { + gp.ChildWindowMade = false; } ImDrawList &DrawList = *Window->DrawList; @@ -828,7 +893,9 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons y[2] = AxisState(plot.YAxis[2], gp.NextPlotData.HasYRange[2], gp.NextPlotData.YRangeCond[2], HasFlag(plot.Flags, ImPlotFlags_YAxis3), y[1].present_so_far); - const bool lock_plot = x.lock && y[0].lock && y[1].lock && y[2].lock; + gp.LockPlot = x.lock && y[0].lock && + ( HasFlag(plot.Flags, ImPlotFlags_YAxis2) ? y[1].lock : true ) && + ( HasFlag(plot.Flags, ImPlotFlags_YAxis3) ? y[2].lock : true ); // CONSTRAINTS ------------------------------------------------------------ @@ -896,10 +963,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons gp.BB_Frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); ImGui::ItemSize(gp.BB_Frame); if (!ImGui::ItemAdd(gp.BB_Frame, 0, &gp.BB_Frame)) { - gp.NextPlotData = ImPlotNextPlotData(); - gp.CurrentPlot = NULL; - if (!HasFlag(plot.Flags, ImPlotFlags_NoChild)) - ImGui::EndChild(); + gp.Reset(); return false; } gp.Hov_Frame = ImGui::ItemHoverable(gp.BB_Frame, ID); @@ -920,11 +984,11 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } // get ticks - if (gp.RenderX) - GetTicks(plot.XAxis.Range, plot.XAxis.Divisions, plot.XAxis.Subdivisions, HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale), gp.XTicks); + if (gp.RenderX && gp.NextPlotData.ShowDefaultTicksX) + AddDefaultTicks(plot.XAxis.Range, plot.XAxis.Divisions, plot.XAxis.Subdivisions, HasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale), gp.XTicks); for (int i = 0; i < MAX_Y_AXES; i++) { - if (gp.RenderY[i]) { - GetTicks(plot.YAxis[i].Range, plot.YAxis[i].Divisions, plot.YAxis[i].Subdivisions, HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale), gp.YTicks[i]); + if (gp.RenderY[i] && gp.NextPlotData.ShowDefaultTicksY[i]) { + AddDefaultTicks(plot.YAxis[i].Range, plot.YAxis[i].Divisions, plot.YAxis[i].Subdivisions, HasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale), gp.YTicks[i]); } } @@ -1149,7 +1213,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons plot.Selecting = false; } // bad selection - if (plot.Selecting && (!HasFlag(plot.Flags, ImPlotFlags_BoxSelect) || lock_plot) && ImLengthSqr(plot.SelectStart - IO.MousePos) > 4) { + if (plot.Selecting && (!HasFlag(plot.Flags, ImPlotFlags_BoxSelect) || gp.LockPlot) && ImLengthSqr(plot.SelectStart - IO.MousePos) > 4) { ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); } // cancel selection @@ -1157,7 +1221,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons plot.Selecting = false; } // begin selection or query - if (gp.Hov_Frame && gp.Hov_Grid && IO.MouseClicked[1]) { + if (!gp.LockPlot && gp.Hov_Frame && gp.Hov_Grid && IO.MouseClicked[1]) { plot.SelectStart = IO.MousePos; plot.Selecting = true; } @@ -1326,20 +1390,6 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // push plot ID into stack ImGui::PushID(ID); - // reset items count - gp.VisibleItemCount = 0; - // reset extents - gp.ExtentsX.Min = HUGE_VAL; - gp.ExtentsX.Max = -HUGE_VAL; - for (int i = 0; i < MAX_Y_AXES; i++) { - gp.ExtentsY[i].Min = HUGE_VAL; - gp.ExtentsY[i].Max = -HUGE_VAL; - } - // clear item names - gp.LegendLabels.Buf.resize(0); - // reset digital plot items count - gp.DigitalPlotItemCnt = 0; - gp.DigitalPlotOffset = 0; return true; } @@ -1515,6 +1565,7 @@ void EndPlot() { // AXIS STATES ------------------------------------------------------------ + // TODO: Move this into gp to avoid repetition AxisState x(plot.XAxis, gp.NextPlotData.HasXRange, gp.NextPlotData.XRangeCond, true, 0); AxisState y[MAX_Y_AXES]; y[0] = AxisState(plot.YAxis[0], gp.NextPlotData.HasYRange[0], gp.NextPlotData.YRangeCond[0], true, 0); @@ -1523,10 +1574,10 @@ void EndPlot() { y[2] = AxisState(plot.YAxis[2], gp.NextPlotData.HasYRange[2], gp.NextPlotData.YRangeCond[2], HasFlag(plot.Flags, ImPlotFlags_YAxis3), y[1].present_so_far); - const bool lock_plot = x.lock && y[0].lock && y[1].lock && y[2].lock; - const bool any_y_locked = y[0].lock || y[1].lock || y[2].lock; + const bool any_y_locked = y[0].lock || y[1].lock || y[2].lock; const bool any_y_dragging = plot.YAxis[0].Dragging || plot.YAxis[1].Dragging || plot.YAxis[2].Dragging; + // FINAL RENDER ----------------------------------------------------------- // render ticks @@ -1575,20 +1626,21 @@ void EndPlot() { // render selection/query if (plot.Selecting) { ImRect select_bb(ImMin(IO.MousePos, plot.SelectStart), ImMax(IO.MousePos, plot.SelectStart)); - if (plot.Selecting && !lock_plot && HasFlag(plot.Flags, ImPlotFlags_BoxSelect)) { - if (IO.KeyAlt && IO.KeyShift && select_bb.GetWidth() > 2 && select_bb.GetHeight() > 2) { + bool select_big_enough = ImLengthSqr(select_bb.GetSize()) > 4; + if (plot.Selecting && !gp.LockPlot && HasFlag(plot.Flags, ImPlotFlags_BoxSelect) && select_big_enough) { + if (IO.KeyAlt && IO.KeyShift) { DrawList.AddRectFilled(gp.BB_Grid.Min, gp.BB_Grid.Max, gp.Col_SlctBg); DrawList.AddRect( gp.BB_Grid.Min, gp.BB_Grid.Max, gp.Col_SlctBd); } - else if ((x.lock || IO.KeyAlt) && select_bb.GetHeight() > 2) { + else if ((x.lock || IO.KeyAlt)) { DrawList.AddRectFilled(ImVec2(gp.BB_Grid.Min.x, select_bb.Min.y), ImVec2(gp.BB_Grid.Max.x, select_bb.Max.y), gp.Col_SlctBg); DrawList.AddRect( ImVec2(gp.BB_Grid.Min.x, select_bb.Min.y), ImVec2(gp.BB_Grid.Max.x, select_bb.Max.y), gp.Col_SlctBd); } - else if ((any_y_locked || IO.KeyShift) && select_bb.GetWidth() > 2) { + else if ((any_y_locked || IO.KeyShift)) { DrawList.AddRectFilled(ImVec2(select_bb.Min.x, gp.BB_Grid.Min.y), ImVec2(select_bb.Max.x, gp.BB_Grid.Max.y), gp.Col_SlctBg); DrawList.AddRect( ImVec2(select_bb.Min.x, gp.BB_Grid.Min.y), ImVec2(select_bb.Max.x, gp.BB_Grid.Max.y), gp.Col_SlctBd); } - else if (select_bb.GetWidth() > 2 && select_bb.GetHeight() > 2) { + else { DrawList.AddRectFilled(select_bb.Min, select_bb.Max, gp.Col_SlctBg); DrawList.AddRect( select_bb.Min, select_bb.Max, gp.Col_SlctBd); } @@ -1741,17 +1793,10 @@ void EndPlot() { } // CLEANUP ---------------------------------------------------------------- - // Reset legend items - gp.LegendIndices.shrink(0); - // Null current plot/data - gp.CurrentPlot = NULL; - // Reset next plot data - gp.NextPlotData = ImPlotNextPlotData(); // Pop ImGui::PushID at the end of BeginPlot ImGui::PopID(); - // End child window - if (!HasFlag(plot.Flags, ImPlotFlags_NoChild)) - ImGui::EndChild(); + // Reset context for next plot + gp.Reset(); } //----------------------------------------------------------------------------- @@ -1759,11 +1804,13 @@ void EndPlot() { //----------------------------------------------------------------------------- void SetNextPlotLimits(double x_min, double x_max, double y_min, double y_max, ImGuiCond cond) { + IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotLimits() needs to be called before BeginPlot()!"); SetNextPlotLimitsX(x_min, x_max, cond); SetNextPlotLimitsY(y_min, y_max, cond); } void SetNextPlotLimitsX(double x_min, double x_max, ImGuiCond cond) { + IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotLSetNextPlotLimitsXimitsY() needs to be called before BeginPlot()!"); IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. gp.NextPlotData.HasXRange = true; gp.NextPlotData.XRangeCond = cond; @@ -1772,7 +1819,8 @@ void SetNextPlotLimitsX(double x_min, double x_max, ImGuiCond cond) { } void SetNextPlotLimitsY(double y_min, double y_max, ImGuiCond cond, int y_axis) { - IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < MAX_Y_AXES, "y_axis Needs to be between 0 and MAX_Y_AXES"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotLimitsY() needs to be called before BeginPlot()!"); + IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < MAX_Y_AXES, "y_axis needs to be between 0 and MAX_Y_AXES"); IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. gp.NextPlotData.HasYRange[y_axis] = true; gp.NextPlotData.YRangeCond[y_axis] = cond; @@ -1780,24 +1828,51 @@ void SetNextPlotLimitsY(double y_min, double y_max, ImGuiCond cond, int y_axis) gp.NextPlotData.Y[y_axis].Max = y_max; } +void SetNextPlotTicksX(const double* values, int n_ticks, const char** labels, bool show_default) { + IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotTicksX() needs to be called before BeginPlot()!"); + gp.NextPlotData.ShowDefaultTicksX = show_default; + AddCustomTicks(values, labels, n_ticks, gp.XTicks, gp.XTickLabels); +} + +void SetNextPlotTicksX(double x_min, double x_max, int n_ticks, const char** labels, bool show_default) { + IM_ASSERT_USER_ERROR(n_ticks > 1, "The number of ticks must be greater than 1"); + static ImVector buffer; + FillRange(buffer, n_ticks, x_min, x_max); + SetNextPlotTicksX(&buffer[0], n_ticks, labels, show_default); +} + +void SetNextPlotTicksY(const double* values, int n_ticks, const char** labels, bool show_default, int y_axis) { + IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "SetNextPlotTicksY() needs to be called before BeginPlot()!"); + IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < MAX_Y_AXES, "y_axis needs to be between 0 and MAX_Y_AXES"); + gp.NextPlotData.ShowDefaultTicksY[y_axis] = show_default; + AddCustomTicks(values, labels, n_ticks, gp.YTicks[y_axis], gp.YTickLabels[y_axis]); +} + +void SetNextPlotTicksY(double y_min, double y_max, int n_ticks, const char** labels, bool show_default, int y_axis) { + IM_ASSERT_USER_ERROR(n_ticks > 1, "The number of ticks must be greater than 1"); + static ImVector buffer; + FillRange(buffer, n_ticks, y_min, y_max); + SetNextPlotTicksY(&buffer[0], n_ticks, labels, show_default,y_axis); +} + void SetPlotYAxis(int y_axis) { - IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < MAX_Y_AXES, "y_axis Needs to be between 0 and MAX_Y_AXES"); - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetPlotYAxis() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "SetPlotYAxis() needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(y_axis >= 0 && y_axis < MAX_Y_AXES, "y_axis needs to be between 0 and MAX_Y_AXES"); gp.CurrentPlot->CurrentYAxis = y_axis; } ImVec2 GetPlotPos() { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotPos() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotPos() needs to be called between BeginPlot() and EndPlot()!"); return gp.BB_Grid.Min; } ImVec2 GetPlotSize() { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotSize() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotSize() needs to be called between BeginPlot() and EndPlot()!"); return gp.BB_Grid.GetSize(); } void PushPlotClipRect() { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PushPlotClipRect() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PushPlotClipRect() needs to be called between BeginPlot() and EndPlot()!"); ImGui::PushClipRect(gp.BB_Grid.Min, gp.BB_Grid.Max, true); } @@ -1806,12 +1881,12 @@ void PopPlotClipRect() { } bool IsPlotHovered() { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotHovered() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotHovered() needs to be called between BeginPlot() and EndPlot()!"); return gp.Hov_Grid; } ImPlotPoint GetPlotMousePos(int y_axis_in) { IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < MAX_Y_AXES, "y_axis needs to between -1 and MAX_Y_AXES"); - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotMousePos() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotMousePos() needs to be called between BeginPlot() and EndPlot()!"); const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; return gp.LastMousePos[y_axis]; } @@ -1819,7 +1894,7 @@ ImPlotPoint GetPlotMousePos(int y_axis_in) { ImPlotLimits GetPlotLimits(int y_axis_in) { IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < MAX_Y_AXES, "y_axis needs to between -1 and MAX_Y_AXES"); - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotLimits() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotLimits() needs to be called between BeginPlot() and EndPlot()!"); const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; ImPlotState& plot = *gp.CurrentPlot; @@ -1830,13 +1905,13 @@ ImPlotLimits GetPlotLimits(int y_axis_in) { } bool IsPlotQueried() { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotQueried() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotQueried() needs to be called between BeginPlot() and EndPlot()!"); return gp.CurrentPlot->Queried; } ImPlotLimits GetPlotQuery(int y_axis_in) { IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < MAX_Y_AXES, "y_axis needs to between -1 and MAX_Y_AXES"); - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotQuery() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotQuery() needs to be called between BeginPlot() and EndPlot()!"); ImPlotState& plot = *gp.CurrentPlot; const int y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; @@ -2301,7 +2376,7 @@ struct GetterFuncPtrImPlotPoint { template inline void PlotEx(const char* label_id, Getter getter, int count, int offset) { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "Plot() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "Plot() needs to be called between BeginPlot() and EndPlot()!"); ImPlotState* plot = gp.CurrentPlot; const int y_axis = plot->CurrentYAxis; @@ -2503,7 +2578,7 @@ struct GetterBarH { template void PlotBarsEx(const char* label_id, Getter getter, int count, TWidth width, int offset) { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotBars() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotBars() needs to be called between BeginPlot() and EndPlot()!"); ImPlotItem* item = RegisterItem(label_id); if (!item->Show) @@ -2593,7 +2668,7 @@ void PlotBars(const char* label_id, ImPlotPoint (*getter_func)(void* data, int i template void PlotBarsHEx(const char* label_id, Getter getter, int count, THeight height, int offset) { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotBarsH() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotBarsH() needs to be called between BeginPlot() and EndPlot()!"); ImPlotItem* item = RegisterItem(label_id); if (!item->Show) @@ -2703,7 +2778,7 @@ struct GetterError { template void PlotErrorBarsEx(const char* label_id, Getter getter, int count, int offset) { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotErrorBars() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotErrorBars() needs to be called between BeginPlot() and EndPlot()!"); ImGuiID id = ImGui::GetID(label_id); ImPlotItem* item = gp.CurrentPlot->Items.GetByKey(id); @@ -2788,7 +2863,7 @@ inline void DrawPieSlice(ImDrawList& DrawList, const ImPlotPoint& center, double template void PlotPieChartEx(const char** label_ids, T* values, int count, T x, T y, T radius, bool show_percents, T angle0) { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotPieChart() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotPieChart() needs to be called between BeginPlot() and EndPlot()!"); ImDrawList & DrawList = *ImGui::GetWindowDrawList(); T sum = 0; @@ -2893,7 +2968,7 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value template void PlotHeatmapEx(const char* label_id, const T* values, int rows, int cols, T scale_min, T scale_max, bool show_labels, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotHeatmap() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotHeatmap() needs to be called between BeginPlot() and EndPlot()!"); IM_ASSERT_USER_ERROR(scale_min != scale_max, "Scale values must be different!"); ImPlotItem* item = RegisterItem(label_id); if (!item->Show) @@ -2938,7 +3013,7 @@ void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, template inline void PlotDigitalEx(const char* label_id, Getter getter, int count, int offset) { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotDigital() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotDigital() needs to be called between BeginPlot() and EndPlot()!"); ImPlotItem* item = RegisterItem(label_id); if (!item->Show) @@ -3044,7 +3119,7 @@ void PlotText(const char* text, float x, float y, bool vertical, const ImVec2& p //----------------------------------------------------------------------------- // double void PlotText(const char* text, double x, double y, bool vertical, const ImVec2& pixel_offset) { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotText() Needs to be called between BeginPlot() and EndPlot()!"); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotText() needs to be called between BeginPlot() and EndPlot()!"); ImDrawList & DrawList = *ImGui::GetWindowDrawList(); PushPlotClipRect(); ImVec2 pos = PlotToPixels(ImPlotPoint(x,y)) + pixel_offset; @@ -3099,7 +3174,9 @@ void ShowColormapScale(double scale_min, double scale_max, float height) { ImPlotRange range; range.Min = scale_min; range.Max = scale_max; - GetTicks(range, 10, 0, false, ticks); + ticks.shrink(0); + txt_buff.Buf.shrink(0); + AddDefaultTicks(range, 10, 0, false, ticks); LabelTicks(ticks, false, txt_buff); float max_width = 0; for (int i = 0; i < ticks.Size; ++i) diff --git a/implot.h b/implot.h index cdbb4c6..8f2ef9a 100644 --- a/implot.h +++ b/implot.h @@ -313,6 +313,13 @@ void SetNextPlotLimitsX(double x_min, double x_max, ImGuiCond cond = ImGuiCond_O // Set the Y axis range limits of the next plot. Call right before BeginPlot(). If ImGuiCond_Always is used, the Y axis limits will be locked. void SetNextPlotLimitsY(double y_min, double y_max, ImGuiCond cond = ImGuiCond_Once, int y_axis = 0); +// Set the X axis ticks and optionally the labels for the next plot. +void SetNextPlotTicksX(const double* values, int n_ticks, const char** labels = NULL, bool show_default = false); +void SetNextPlotTicksX(double x_min, double x_max, int n_ticks, const char** labels = NULL, bool show_default = false); +// Set the Y axis ticks and optionally the labels for the next plot. +void SetNextPlotTicksY(const double* values, int n_ticks, const char** labels = NULL, bool show_default = false, int y_axis = 0); +void SetNextPlotTicksY(double y_min, double y_max, int n_ticks, const char** labels = NULL, bool show_default = false, int y_axis = 0); + // Select which Y axis will be used for subsequent plot elements. The default is '0', or the first (left) Y axis. void SetPlotYAxis(int y_axis); diff --git a/implot_demo.cpp b/implot_demo.cpp index fd5431c..a7a0d02 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -241,12 +241,18 @@ void ShowDemoWindow(bool* p_open) { } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Bar Plots")) { + static const char* labels[] = {"S1","S2","S3","S4","S5","S6","S7","S8","S9","S10"}; + static const double positions[] = {0,1,2,3,4,5,6,7,8,9}; static bool horz = false; ImGui::Checkbox("Horizontal",&horz); - if (horz) + if (horz) { ImPlot::SetNextPlotLimits(0, 110, -0.5, 9.5, ImGuiCond_Always); - else + ImPlot::SetNextPlotTicksY(positions, 10, labels); + } + else { ImPlot::SetNextPlotLimits(-0.5, 9.5, 0, 110, ImGuiCond_Always); + ImPlot::SetNextPlotTicksX(positions, 10, labels); + } if (ImPlot::BeginPlot("Bar Plot", horz ? "Score": "Student", horz ? "Student" : "Score")) { static t_float midtm[10] = {83, 67, 23, 89, 83, 78, 91, 82, 85, 90}; static t_float final[10] = {80, 62, 56, 99, 55, 78, 88, 78, 90, 100}; @@ -331,15 +337,21 @@ void ShowDemoWindow(bool* p_open) { values2[i] = RandomRange(0,1); } static ImPlotColormap map = ImPlotColormap_Viridis; - if (ImGui::Button("Cycle Colormap",ImVec2(225,0))) + if (ImGui::Button("Change Colormap",ImVec2(225,0))) map = (map + 1) % ImPlotColormap_COUNT; ImPlot::SetColormap(map); ImGui::SameLine(); ImGui::LabelText("##Colormap Index", cmap_names[map]); ImGui::SetNextItemWidth(225); ImGui::DragFloat("Max",&scale_max,0.01f,0.1f,20); - static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax; - if (ImPlot::BeginPlot("##Heatmap1",NULL,NULL,ImVec2(225,225),ImPlotFlags_ContextMenu,axes_flags,axes_flags)) { + static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax | ImPlotAxisFlags_TickLabels; + static const char* xlabels[] = {"C1","C2","C3","C4","C5","C6","C7"}; + static const char* ylabels[] = {"R1","R2","R3","R4","R5","R6","R7"}; + + SetNextPlotTicksX(0 + 1.0/14.0, 1 - 1.0/14.0, 7, xlabels); + SetNextPlotTicksY(1- 1.0/14.0, 0 + 1.0/14.0, 7, ylabels); + + if (ImPlot::BeginPlot("##Heatmap1",NULL,NULL,ImVec2(225,225),0,axes_flags,axes_flags)) { ImPlot::PlotHeatmap("heat",values1[0],7,7,scale_min,scale_max); ImPlot::EndPlot(); } @@ -388,11 +400,11 @@ void ShowDemoWindow(bool* p_open) { //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Markers and Text")) { static ImPlotColormap map = ImPlotColormap_Default; - if (ImGui::Button("Cycle Colormap##2")) + if (ImGui::Button("Change Colormap##2")) map = (map + 1) % ImPlotColormap_COUNT; ImGui::SameLine(); ImGui::LabelText("##Colormap Index", cmap_names[map]); - ImGui::PushID(map); // NB: The merely a workaround so that the demo can cycle color maps. You wouldn't need to do this in your own code! + ImGui::PushID(map); // NB: This is merely a workaround so that the demo can cycle color maps. You wouldn't need to do this in your own code! ImPlot::SetNextPlotLimits(0, 10, 0, 12); if (ImPlot::BeginPlot("##MarkerStyles", NULL, NULL, ImVec2(-1,0), 0, 0, 0)) { ImPlot::SetColormap(map); @@ -830,6 +842,33 @@ void ShowDemoWindow(bool* p_open) { } } //------------------------------------------------------------------------- + if (ImGui::CollapsingHeader("Custom Ticks")) { + static bool custom_ticks = true; + static bool custom_labels = true; + ImGui::Checkbox("Show Custom Ticks", &custom_ticks); + if (custom_ticks) { + ImGui::SameLine(); + ImGui::Checkbox("Show Custom Labels", &custom_labels); + } + double pi = 3.14; + const char* pi_str[] = {"PI"}; + static double yticks[] = {1,3,7,9}; + static const char* ylabels[] = {"One","Three","Seven","Nine"}; + static double yticks_aux[] = {0.2,0.4,0.6}; + static const char* ylabels_aux[] = {"A","B","C","D","E","F"}; + if (custom_ticks) { + ImPlot::SetNextPlotTicksX(&pi,1,custom_labels ? pi_str : NULL, true); + ImPlot::SetNextPlotTicksY(yticks, 4, custom_labels ? ylabels : NULL); + ImPlot::SetNextPlotTicksY(yticks_aux, 3, custom_labels ? ylabels_aux : NULL, false, 1); + ImPlot::SetNextPlotTicksY(0, 1, 6, custom_labels ? ylabels_aux : NULL, false, 2); + } + ImPlot::SetNextPlotLimits(2.5,5,0,10); + if (ImPlot::BeginPlot("Custom Ticks", NULL, NULL, ImVec2(-1,0), ImPlotFlags_Default | ImPlotFlags_YAxis2 | ImPlotFlags_YAxis3)) { + + ImPlot::EndPlot(); + } + } + //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Custom Styles")) { static ImVec4 my_map[3] = { ImVec4(0.000f, 0.980f, 0.604f, 1.0f),