From 04dd49a2100996c230efbcf0a23a24a4f8c6196b Mon Sep 17 00:00:00 2001 From: epezent Date: Thu, 12 Nov 2020 23:06:03 -0600 Subject: [PATCH 01/19] prototyping optimized heatmaps --- implot_internal.h | 10 -- implot_items.cpp | 227 ++++++++++++++++++++++++---------------------- 2 files changed, 117 insertions(+), 120 deletions(-) diff --git a/implot_internal.h b/implot_internal.h index c7415f54..3fbcf6cc 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -968,14 +968,4 @@ IMPLOT_API bool ShowDatePicker(const char* id, int* level, ImPlotTime* t, const // #t will be set when a new hour, minute, or sec is selected or am/pm is toggled, and the function will return true. IMPLOT_API bool ShowTimePicker(const char* id, ImPlotTime* t); -//----------------------------------------------------------------------------- -// [SECTION] Internal / Experimental Plotters -// No guarantee of forward compatibility here! -//----------------------------------------------------------------------------- - -// Plots axis-aligned, filled rectangles. Every two consecutive points defines opposite corners of a single rectangle. -IMPLOT_API void PlotRects(const char* label_id, const float* xs, const float* ys, int count, int offset = 0, int stride = sizeof(float)); -IMPLOT_API void PlotRects(const char* label_id, const double* xs, const double* ys, int count, int offset = 0, int stride = sizeof(double)); -IMPLOT_API void PlotRects(const char* label_id, ImPlotPoint (*getter)(void* data, int idx), void* data, int count, int offset = 0); - } // namespace ImPlot diff --git a/implot_items.cpp b/implot_items.cpp index 9f247858..286870a9 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -634,51 +634,6 @@ struct ShadedRenderer { static const int VtxConsumed = 5; }; -template -struct RectRenderer { - inline RectRenderer(const TGetter& getter, const TTransformer& transformer, ImU32 col) : - Getter(getter), - Transformer(transformer), - Prims(Getter.Count / 2), - Col(col) - {} - inline bool operator()(ImDrawList& DrawList, const ImRect& /*cull_rect*/, const ImVec2& uv, int prim) const { - // TODO: Culling - ImVec2 P1 = Transformer(Getter(2*prim)); - ImVec2 P2 = Transformer(Getter(2*prim+1)); - DrawList._VtxWritePtr[0].pos = P1; - DrawList._VtxWritePtr[0].uv = uv; - DrawList._VtxWritePtr[0].col = Col; - DrawList._VtxWritePtr[1].pos.x = P1.x; - DrawList._VtxWritePtr[1].pos.y = P2.y; - DrawList._VtxWritePtr[1].uv = uv; - DrawList._VtxWritePtr[1].col = Col; - DrawList._VtxWritePtr[2].pos = P2; - DrawList._VtxWritePtr[2].uv = uv; - DrawList._VtxWritePtr[2].col = Col; - DrawList._VtxWritePtr[3].pos.x = P2.x; - DrawList._VtxWritePtr[3].pos.y = P1.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; - return true; - } - const TGetter& Getter; - const TTransformer& Transformer; - const int Prims; - const ImU32 Col; - static const int IdxConsumed = 6; - static const int VtxConsumed = 4; -}; - // Stupid way of calculating maximum index size of ImDrawIdx without integer overflow issues template struct MaxIdx { static const unsigned int Value; }; @@ -1656,30 +1611,127 @@ template IMPLOT_API void PlotPieChart(const char* const label_ids[], con // PLOT HEATMAP //----------------------------------------------------------------------------- +struct RectInfo { + ImPlotPoint Min, Max; + ImU32 Color; +}; + +template +struct RectRenderer { + inline RectRenderer(const TGetter& getter, const TTransformer& transformer) : + Getter(getter), + Transformer(transformer), + Prims(Getter.Count) + {} + inline bool operator()(ImDrawList& DrawList, const ImRect& /*cull_rect*/, const ImVec2& uv, int prim) const { + // TODO: Culling + RectInfo rect = Getter(prim); + ImVec2 P1 = Transformer(rect.Min); + ImVec2 P2 = Transformer(rect.Max); + DrawList._VtxWritePtr[0].pos = P1; + DrawList._VtxWritePtr[0].uv = uv; + DrawList._VtxWritePtr[0].col = rect.Color; + DrawList._VtxWritePtr[1].pos.x = P1.x; + DrawList._VtxWritePtr[1].pos.y = P2.y; + DrawList._VtxWritePtr[1].uv = uv; + DrawList._VtxWritePtr[1].col = rect.Color; + DrawList._VtxWritePtr[2].pos = P2; + DrawList._VtxWritePtr[2].uv = uv; + DrawList._VtxWritePtr[2].col = rect.Color; + DrawList._VtxWritePtr[3].pos.x = P2.x; + DrawList._VtxWritePtr[3].pos.y = P1.y; + DrawList._VtxWritePtr[3].uv = uv; + DrawList._VtxWritePtr[3].col = rect.Color; + 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; + return true; + } + const TGetter& Getter; + const TTransformer& Transformer; + const int Prims; + static const int IdxConsumed = 6; + static const int VtxConsumed = 4; +}; + +template +struct GetterHeatmap { + GetterHeatmap(const T* values, int rows, int cols, double scale_min, double scale_max, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) : + Values(values), + Count(rows*cols), + Rows(rows), + Cols(cols), + ScaleMin(scale_min), + ScaleMax(scale_max), + BoundsMin(bounds_min), + BoundsMax(bounds_max), + Width((bounds_max.x - bounds_min.x) / cols), + Height((bounds_max.y - bounds_min.y) / rows), + HalfSize(Width*0.5, Height*0.5) + { } + + inline RectInfo operator()(int idx) const { + const int r = (idx / Cols); + const int c = idx % Cols; + const ImPlotPoint p(BoundsMin.x + 0.5*Width + c*Width, BoundsMax.y - (0.5*Height + r*Height)); + const double t = ImRemap((double)Values[idx], ScaleMin, ScaleMax, 0.0, 1.0); + ImVec4 color = LerpColormap((float)t); + color.w *= GImPlot->Style.FillAlpha; + RectInfo rect; + rect.Min = ImPlotPoint(p.x - HalfSize.x, p.y - HalfSize.y); + rect.Max = ImPlotPoint(p.x + HalfSize.x, p.y + HalfSize.y); + rect.Color = ImGui::GetColorU32(color); + return rect; + } + + const T* const Values; + const int Count, Rows, Cols; + const double ScaleMin, ScaleMax, Width, Height; + const ImPlotPoint BoundsMin, BoundsMax, HalfSize; +}; + template void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* values, int rows, int cols, double scale_min, double scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) { - ImPlotContext& gp = *GImPlot; - 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); - int 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_max.y - (0.5*h + r*h); - ImVec2 a = transformer(ImPlotPoint(p.x - half_size.x, p.y - half_size.y)); - ImVec2 b = transformer(ImPlotPoint(p.x + half_size.x, p.y + half_size.y)); - double t = ImRemap((double)values[i], scale_min, scale_max, 0.0, 1.0); - ImVec4 color = LerpColormap((float)t); - color.w *= gp.Style.FillAlpha; - ImU32 col = ImGui::GetColorU32(color); - DrawList.AddRectFilled(a, b, col); - i++; - } + // ImPlotContext& gp = *GImPlot; + GetterHeatmap getter(values, rows, cols, scale_min, scale_max, bounds_min, bounds_max); + + switch (GetCurrentScale()) { + case ImPlotScale_LinLin: RenderPrimitives(RectRenderer, TransformerLinLin>(getter, TransformerLinLin()), DrawList, GImPlot->BB_Plot); break; + case ImPlotScale_LogLin: RenderPrimitives(RectRenderer, TransformerLogLin>(getter, TransformerLogLin()), DrawList, GImPlot->BB_Plot); break;; + case ImPlotScale_LinLog: RenderPrimitives(RectRenderer, TransformerLinLog>(getter, TransformerLinLog()), DrawList, GImPlot->BB_Plot); break;; + case ImPlotScale_LogLog: RenderPrimitives(RectRenderer, TransformerLogLog>(getter, TransformerLogLog()), DrawList, GImPlot->BB_Plot); break;; } + + // 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); + // int 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_max.y - (0.5*h + r*h); + // ImVec2 a = transformer(ImPlotPoint(p.x - half_size.x, p.y - half_size.y)); + // ImVec2 b = transformer(ImPlotPoint(p.x + half_size.x, p.y + half_size.y)); + // double t = ImRemap((double)values[i], scale_min, scale_max, 0.0, 1.0); + // ImVec4 color = LerpColormap((float)t); + // color.w *= gp.Style.FillAlpha; + // ImU32 col = ImGui::GetColorU32(color); + // DrawList.AddRectFilled(a, b, col); + // i++; + // } + // } if (fmt != NULL) { - i = 0; + 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); + int i = 0; for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { ImPlotPoint p; @@ -1815,51 +1867,6 @@ void PlotDigitalG(const char* label_id, ImPlotPoint (*getter_func)(void* data, i return PlotDigitalEx(label_id, getter); } -//----------------------------------------------------------------------------- -// PLOT RECTS -//----------------------------------------------------------------------------- -template -void PlotRectsEx(const char* label_id, const Getter& getter) { - if (BeginItem(label_id, ImPlotCol_Fill)) { - if (FitThisFrame()) { - for (int i = 0; i < getter.Count; ++i) { - ImPlotPoint p = getter(i); - FitPoint(p); - } - } - const ImPlotNextItemData& s = GetItemData(); - if (s.RenderFill) { - ImDrawList& DrawList = *GetPlotDrawList(); - ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]); - switch (GetCurrentScale()) { - case ImPlotScale_LinLin: RenderPrimitives(RectRenderer(getter, TransformerLinLin(), col), DrawList, GImPlot->BB_Plot); break; - case ImPlotScale_LogLin: RenderPrimitives(RectRenderer(getter, TransformerLogLin(), col), DrawList, GImPlot->BB_Plot); break; - case ImPlotScale_LinLog: RenderPrimitives(RectRenderer(getter, TransformerLinLog(), col), DrawList, GImPlot->BB_Plot); break; - case ImPlotScale_LogLog: RenderPrimitives(RectRenderer(getter, TransformerLogLog(), col), DrawList, GImPlot->BB_Plot); break; - } - } - EndItem(); - } -} - -// float -void PlotRects(const char* label_id, const float* xs, const float* ys, int count, int offset, int stride) { - GetterXsYs getter(xs,ys,count,offset,stride); - PlotRectsEx(label_id, getter); -} - -// double -void PlotRects(const char* label_id, const double* xs, const double* ys, int count, int offset, int stride) { - GetterXsYs getter(xs,ys,count,offset,stride); - PlotRectsEx(label_id, getter); -} - -// custom -void PlotRects(const char* label_id, ImPlotPoint (*getter_func)(void* data, int idx), void* data, int count, int offset) { - GetterFuncPtr getter(getter_func,data,count,offset); - return PlotRectsEx(label_id, getter); -} - //----------------------------------------------------------------------------- // PLOT IMAGE //----------------------------------------------------------------------------- From f708aee64c092dd24048a3869b150a49fda242a7 Mon Sep 17 00:00:00 2001 From: epezent Date: Fri, 13 Nov 2020 12:32:21 -0600 Subject: [PATCH 02/19] add basic histogram impl --- implot.h | 3 +++ implot_demo.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++--- implot_internal.h | 6 +++++ implot_items.cpp | 42 ++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 3 deletions(-) diff --git a/implot.h b/implot.h index 3015c79c..d0967a0b 100644 --- a/implot.h +++ b/implot.h @@ -416,6 +416,9 @@ template IMPLOT_API void PlotBarsH(const char* label_id, const T* v template IMPLOT_API void PlotBarsH(const char* label_id, const T* xs, const T* ys, int count, double height, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotBarsHG(const char* label_id, ImPlotPoint (*getter)(void* data, int idx), void* data, int count, double height, int offset=0); +// Plots a horizontal histogram. If pdf is true, data will be normalized to produce an estimation of the probability density function. +template IMPLOT_API void PlotHistogram(const char* label_id, const T* values, int count, int bins, bool pdf=false, double bar_scale=1.0); + // Plots vertical error bar. The label_id should be the same as the label_id of the associated line or bar plot. template IMPLOT_API void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* err, int count, int offset=0, int stride=sizeof(T)); template IMPLOT_API void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset=0, int stride=sizeof(T)); diff --git a/implot_demo.cpp b/implot_demo.cpp index ffed0bf6..5e20218c 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #ifdef _MSC_VER #define sprintf sprintf_s @@ -72,6 +71,36 @@ inline T RandomRange(T min, T max) { return min + scale * ( max - min ); } +double RandomGauss() { + static double V1, V2, S; + static int phase = 0; + double X; + if(phase == 0) { + do { + double U1 = (double)rand() / RAND_MAX; + double U2 = (double)rand() / RAND_MAX; + + V1 = 2 * U1 - 1; + V2 = 2 * U2 - 1; + S = V1 * V1 + V2 * V2; + } while(S >= 1 || S == 0); + + X = V1 * sqrt(-2 * log(S) / S); + } else + X = V2 * sqrt(-2 * log(S) / S); + phase = 1 - phase; + return X; +} + +template +struct NormalDistribution { + NormalDistribution(double mean, double sd) { + for (int i = 0; i < N; ++i) + Data[i] = RandomGauss()*sd + mean; + } + double Data[N]; +}; + // utility structure for realtime plot struct ScrollingBuffer { int MaxSize; @@ -384,6 +413,33 @@ void ShowDemoWindow(bool* p_open) { } } //------------------------------------------------------------------------- + if (ImGui::CollapsingHeader("Histograms")) { + static double mu1 = 3; + static double sigma1 = 2; + static double mu2 = 7; + static double sigma2 = 2.5; + static NormalDistribution<5000> dist1(mu1, sigma1); + static NormalDistribution<5000> dist2(mu2, sigma2); + static double pdfx[500]; + static double pdfy1[500]; + static double pdfy2[500]; + for (int i = 0; i < 500; ++i) { + pdfx[i] = -6 + 22 * (double)i/499.0; + pdfy1[i] = exp( - (pdfx[i]-mu1)*(pdfx[i]-mu1) / (2*sigma1*sigma1)) / (sigma1 * sqrt(2*3.141592653589793238)); + pdfy2[i] = exp( - (pdfx[i]-mu2)*(pdfx[i]-mu2) / (2*sigma2*sigma2)) / (sigma2 * sqrt(2*3.141592653589793238)); + } + ImPlot::SetNextPlotLimits(-6, 16, 0, 0.25); + if (ImPlot::BeginPlot("##Histograms")) { + ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.5f); + ImPlot::PlotHistogram("PDF1", dist1.Data, 1000, IMPLOT_AUTO, true); + ImPlot::PlotLine("PDF1",pdfx,pdfy1,500); + ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.5f); + ImPlot::PlotHistogram("PDF2", dist2.Data, 1000, IMPLOT_AUTO, true); + ImPlot::PlotLine("PDF2",pdfx,pdfy2,500); + ImPlot::EndPlot(); + } + } + //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Error Bars")) { static float xs[5] = {1,2,3,4,5}; static float bar[5] = {1,2,5,3,4}; @@ -862,8 +918,8 @@ void ShowDemoWindow(bool* p_open) { ImPlot::DragLineY("y2",&y2,show_labels); double xs[1000], ys[1000]; for (int i = 0; i < 1000; ++i) { - xs[i] = (x2+x1)/2+abs(x2-x1)*(i/1000.0f - 0.5f); - ys[i] = (y1+y2)/2+abs(y2-y1)/2*sin(f*i/10); + xs[i] = (x2+x1)/2+fabs(x2-x1)*(i/1000.0f - 0.5f); + ys[i] = (y1+y2)/2+fabs(y2-y1)/2*sin(f*i/10); } ImPlot::PlotLine("Interactive Data", xs, ys, 1000); ImPlot::SetPlotYAxis(ImPlotYAxis_2); diff --git a/implot_internal.h b/implot_internal.h index c7415f54..a0939f99 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -107,6 +107,12 @@ inline double ImConstrainInf(double val) { return val == HUGE_VAL ? DBL_MAX : v inline double ImConstrainLog(double val) { return val <= 0 ? 0.001f : val; } // Turns numbers less than 0 to zero inline double ImConstrainTime(double val) { return val < IMPLOT_MIN_TIME ? IMPLOT_MIN_TIME : (val > IMPLOT_MAX_TIME ? IMPLOT_MAX_TIME : val); } +// Finds min value in an unsorted array +template +inline T ImMinArray(const T* values, int count) { T m = values[0]; for (int i = 1; i < count; ++i) { if (values[i] < m) { m = values[i]; } } return m; } +// Finds the max value in an unsorted array +template +inline T ImMaxArray(const T* values, int count) { T m = values[0]; for (int i = 1; i < count; ++i) { if (values[i] > m) { m = values[i]; } } return m; } // Offset calculator helper template diff --git a/implot_items.cpp b/implot_items.cpp index 9f247858..af97287e 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1364,6 +1364,48 @@ void PlotBarsHG(const char* label_id, ImPlotPoint (*getter_func)(void* data, int PlotBarsHEx(label_id, getter, height); } +//----------------------------------------------------------------------------- +// PLOT HISTOGRAM +//----------------------------------------------------------------------------- + +template +void PlotHistogram(const char* label_id, const T* values, int count, int bins, bool pdf, double bar_scale) { + const T min_val = ImMinArray(values, count); + const T max_val = ImMaxArray(values, count); + const double width = (double)(max_val - min_val) / bins; + static ImVector bin_centers; + static ImVector bin_counts; + bin_centers.resize(bins); + bin_counts.resize(bins); + for (int b = 0; b < bins; ++b) { + bin_centers[b] = (double)min_val + b * width + width * 0.5; + bin_counts[b] = 0; + } + for (int i = 0; i < count; ++i) { + int b = (int)((double)(values[i] - min_val) / width); + b = ImClamp(b, 0, bins - 1); + bin_counts[b] += 1.0; + } + if (pdf) { + for (int b = 0; b < bins; ++b) + bin_counts[b] = bin_counts[b] / (count * width); + + } + PlotBars(label_id, &bin_centers.Data[0], &bin_counts.Data[0], bins, bar_scale*width); +} + +template IMPLOT_API void PlotHistogram(const char* label_id, const ImS8* values, int count, int bins, bool pdf, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImU8* values, int count, int bins, bool pdf, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImS16* values, int count, int bins, bool pdf, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImU16* values, int count, int bins, bool pdf, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImS32* values, int count, int bins, bool pdf, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImU32* values, int count, int bins, bool pdf, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImS64* values, int count, int bins, bool pdf, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImU64* values, int count, int bins, bool pdf, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const float* values, int count, int bins, bool pdf, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const double* values, int count, int bins, bool pdf, double bar_scale); + + //----------------------------------------------------------------------------- // PLOT ERROR BARS //----------------------------------------------------------------------------- From 77d048e9cd4b6acb21708e903989e2b1a5fbe4d1 Mon Sep 17 00:00:00 2001 From: epezent Date: Fri, 13 Nov 2020 22:10:54 -0600 Subject: [PATCH 03/19] heatmap opts --- implot_items.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implot_items.cpp b/implot_items.cpp index 286870a9..5189618a 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1677,7 +1677,7 @@ struct GetterHeatmap { { } inline RectInfo operator()(int idx) const { - const int r = (idx / Cols); + const int r = idx / Cols; const int c = idx % Cols; const ImPlotPoint p(BoundsMin.x + 0.5*Width + c*Width, BoundsMax.y - (0.5*Height + r*Height)); const double t = ImRemap((double)Values[idx], ScaleMin, ScaleMax, 0.0, 1.0); From 55a20ac7a4790eadd21b0c62a0bf54465c8f3142 Mon Sep 17 00:00:00 2001 From: epezent Date: Fri, 13 Nov 2020 23:57:26 -0600 Subject: [PATCH 04/19] finish up histograms --- implot.h | 6 +++--- implot_demo.cpp | 56 +++++++++++++++++++++++++++++------------------- implot_items.cpp | 42 +++++++++++++++++++++++------------- 3 files changed, 64 insertions(+), 40 deletions(-) diff --git a/implot.h b/implot.h index d0967a0b..a35abfae 100644 --- a/implot.h +++ b/implot.h @@ -416,8 +416,8 @@ template IMPLOT_API void PlotBarsH(const char* label_id, const T* v template IMPLOT_API void PlotBarsH(const char* label_id, const T* xs, const T* ys, int count, double height, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotBarsHG(const char* label_id, ImPlotPoint (*getter)(void* data, int idx), void* data, int count, double height, int offset=0); -// Plots a horizontal histogram. If pdf is true, data will be normalized to produce an estimation of the probability density function. -template IMPLOT_API void PlotHistogram(const char* label_id, const T* values, int count, int bins, bool pdf=false, double bar_scale=1.0); +// Plots a horizontal histogram. If cumulative is true, each bin contains its count plus the counts of all previous bins. If density is true, the PDF is visualized. If both are true, the CDF is visualized. +template IMPLOT_API void PlotHistogram(const char* label_id, const T* values, int count, int bins, bool cumulative=false, bool density=false, double bar_scale=1.0); // Plots vertical error bar. The label_id should be the same as the label_id of the associated line or bar plot. template IMPLOT_API void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* err, int count, int offset=0, int stride=sizeof(T)); @@ -447,7 +447,7 @@ IMPLOT_API void PlotImage(const char* label_id, ImTextureID user_texture_id, con // Plots a centered text label at point x,y with optional pixel offset. Text color can be changed with ImPlot::PushStyleColor(ImPlotCol_InlayText, ...). IMPLOT_API void PlotText(const char* text, double x, double y, bool vertical=false, const ImVec2& pix_offset=ImVec2(0,0)); -// Plots an dummy item (i.e. adds a legend entry colored by ImPlotCol_Line) +// Plots a dummy item (i.e. adds a legend entry colored by ImPlotCol_Line) IMPLOT_API void PlotDummy(const char* label_id); //----------------------------------------------------------------------------- diff --git a/implot_demo.cpp b/implot_demo.cpp index 5e20218c..b6315e68 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -95,8 +95,8 @@ double RandomGauss() { template struct NormalDistribution { NormalDistribution(double mean, double sd) { - for (int i = 0; i < N; ++i) - Data[i] = RandomGauss()*sd + mean; + for (int i = 0; i < N; ++i) + Data[i] = RandomGauss()*sd + mean; } double Data[N]; }; @@ -414,28 +414,40 @@ void ShowDemoWindow(bool* p_open) { } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Histograms")) { - static double mu1 = 3; - static double sigma1 = 2; - static double mu2 = 7; - static double sigma2 = 2.5; - static NormalDistribution<5000> dist1(mu1, sigma1); - static NormalDistribution<5000> dist2(mu2, sigma2); - static double pdfx[500]; - static double pdfy1[500]; - static double pdfy2[500]; - for (int i = 0; i < 500; ++i) { - pdfx[i] = -6 + 22 * (double)i/499.0; - pdfy1[i] = exp( - (pdfx[i]-mu1)*(pdfx[i]-mu1) / (2*sigma1*sigma1)) / (sigma1 * sqrt(2*3.141592653589793238)); - pdfy2[i] = exp( - (pdfx[i]-mu2)*(pdfx[i]-mu2) / (2*sigma2*sigma2)) / (sigma2 * sqrt(2*3.141592653589793238)); - } - ImPlot::SetNextPlotLimits(-6, 16, 0, 0.25); + static int bins = 30; + static bool cumulative = false; + static bool density = true; + static double mu = 5; + static double sigma = 2; + static NormalDistribution<5000> dist(mu, sigma); + static double x[100]; + static double y[100]; + if (density) { + for (int i = 0; i < 100; ++i) { + x[i] = -3 + 16 * (double)i/99.0; + y[i] = exp( - (x[i]-mu)*(x[i]-mu) / (2*sigma*sigma)) / (sigma * sqrt(2*3.141592653589793238)); + } + if (cumulative) { + for (int i = 1; i < 100; ++i) + y[i] += y[i-1]; + for (int i = 0; i < 100; ++i) + y[i] /= y[99]; + } + } + ImGui::SetNextItemWidth(100); + ImGui::SliderInt("Bins", &bins, 0, 100); + ImGui::SameLine(); + if (ImGui::Checkbox("Density", &density)) + ImPlot::FitNextPlotAxes(); + ImGui::SameLine(); + if (ImGui::Checkbox("Cumulative", &cumulative)) + ImPlot::FitNextPlotAxes(); + ImPlot::SetNextPlotLimits(-3, 13, 0, 0.25); if (ImPlot::BeginPlot("##Histograms")) { ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.5f); - ImPlot::PlotHistogram("PDF1", dist1.Data, 1000, IMPLOT_AUTO, true); - ImPlot::PlotLine("PDF1",pdfx,pdfy1,500); - ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.5f); - ImPlot::PlotHistogram("PDF2", dist2.Data, 1000, IMPLOT_AUTO, true); - ImPlot::PlotLine("PDF2",pdfx,pdfy2,500); + ImPlot::PlotHistogram("Empirical", dist.Data, 5000, bins, cumulative, density); + if (density) + ImPlot::PlotLine("Theoretical",x,y,100); ImPlot::EndPlot(); } } diff --git a/implot_items.cpp b/implot_items.cpp index af97287e..49b8946b 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1368,8 +1368,10 @@ void PlotBarsHG(const char* label_id, ImPlotPoint (*getter_func)(void* data, int // PLOT HISTOGRAM //----------------------------------------------------------------------------- -template -void PlotHistogram(const char* label_id, const T* values, int count, int bins, bool pdf, double bar_scale) { +template +void PlotHistogram(const char* label_id, const T* values, int count, int bins, bool cumulative, bool density, double bar_scale) { + if (count <= 0 || bins <= 0) + return; const T min_val = ImMinArray(values, count); const T max_val = ImMaxArray(values, count); const double width = (double)(max_val - min_val) / bins; @@ -1386,24 +1388,34 @@ void PlotHistogram(const char* label_id, const T* values, int count, int bins, b b = ImClamp(b, 0, bins - 1); bin_counts[b] += 1.0; } - if (pdf) { - for (int b = 0; b < bins; ++b) + if (cumulative && density) { + for (int b = 1; b < bins; ++b) + bin_counts[b] += bin_counts[b-1]; + for (int b = 0; b < bins; ++b) + bin_counts[b] /= count; + } + else if (cumulative) { + for (int b = 1; b < bins; ++b) + bin_counts[b] += bin_counts[b-1]; + } + else if (density) { + for (int b = 0; b < bins; ++b) bin_counts[b] = bin_counts[b] / (count * width); - + } PlotBars(label_id, &bin_centers.Data[0], &bin_counts.Data[0], bins, bar_scale*width); } -template IMPLOT_API void PlotHistogram(const char* label_id, const ImS8* values, int count, int bins, bool pdf, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImU8* values, int count, int bins, bool pdf, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImS16* values, int count, int bins, bool pdf, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImU16* values, int count, int bins, bool pdf, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImS32* values, int count, int bins, bool pdf, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImU32* values, int count, int bins, bool pdf, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImS64* values, int count, int bins, bool pdf, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImU64* values, int count, int bins, bool pdf, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const float* values, int count, int bins, bool pdf, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const double* values, int count, int bins, bool pdf, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImS8* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImU8* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImS16* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImU16* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImS32* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImU32* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImS64* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImU64* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const float* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const double* values, int count, int bins, bool cumulative, bool density, double bar_scale); //----------------------------------------------------------------------------- From 35fd6d5cce9a37f67ec2c3e861d9c8fd0b7d9805 Mon Sep 17 00:00:00 2001 From: epezent Date: Sat, 14 Nov 2020 00:04:15 -0600 Subject: [PATCH 05/19] not worth it --- implot_demo.cpp | 8 +++---- implot_items.cpp | 62 ++++++++++++++++++++++++------------------------ 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index ffed0bf6..5ff2f732 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -502,17 +502,17 @@ void ShowDemoWindow(bool* p_open) { ImGui::SameLine(); - static double values2[100*100]; + static double values2[500*500]; srand((unsigned int)(DEMO_TIME*1000000)); - for (int i = 0; i < 100*100; ++i) + for (int i = 0; i < 500*500; ++i) values2[i] = RandomRange(0.0,1.0); static ImVec4 gray[2] = {ImVec4(0,0,0,1), ImVec4(1,1,1,1)}; ImPlot::PushColormap(gray, 2); ImPlot::SetNextPlotLimits(-1,1,-1,1); if (ImPlot::BeginPlot("##Heatmap2",NULL,NULL,ImVec2(225,225),0,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations)) { - ImPlot::PlotHeatmap("heat1",values2,100,100,0,1,NULL); - ImPlot::PlotHeatmap("heat2",values2,100,100,0,1,NULL, ImPlotPoint(-1,-1), ImPlotPoint(0,0)); + ImPlot::PlotHeatmap("heat1",values2,500,500,0,1,NULL); + ImPlot::PlotHeatmap("heat2",values2,500,500,0,1,NULL, ImPlotPoint(-1,-1), ImPlotPoint(0,0)); ImPlot::EndPlot(); } ImPlot::PopColormap(); diff --git a/implot_items.cpp b/implot_items.cpp index 5189618a..33f72c3c 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1698,40 +1698,40 @@ struct GetterHeatmap { template void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* values, int rows, int cols, double scale_min, double scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) { - // ImPlotContext& gp = *GImPlot; - GetterHeatmap getter(values, rows, cols, scale_min, scale_max, bounds_min, bounds_max); - - switch (GetCurrentScale()) { - case ImPlotScale_LinLin: RenderPrimitives(RectRenderer, TransformerLinLin>(getter, TransformerLinLin()), DrawList, GImPlot->BB_Plot); break; - case ImPlotScale_LogLin: RenderPrimitives(RectRenderer, TransformerLogLin>(getter, TransformerLogLin()), DrawList, GImPlot->BB_Plot); break;; - case ImPlotScale_LinLog: RenderPrimitives(RectRenderer, TransformerLinLog>(getter, TransformerLinLog()), DrawList, GImPlot->BB_Plot); break;; - case ImPlotScale_LogLog: RenderPrimitives(RectRenderer, TransformerLogLog>(getter, TransformerLogLog()), DrawList, GImPlot->BB_Plot); break;; - } + ImPlotContext& gp = *GImPlot; + // GetterHeatmap getter(values, rows, cols, scale_min, scale_max, bounds_min, 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); - // int 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_max.y - (0.5*h + r*h); - // ImVec2 a = transformer(ImPlotPoint(p.x - half_size.x, p.y - half_size.y)); - // ImVec2 b = transformer(ImPlotPoint(p.x + half_size.x, p.y + half_size.y)); - // double t = ImRemap((double)values[i], scale_min, scale_max, 0.0, 1.0); - // ImVec4 color = LerpColormap((float)t); - // color.w *= gp.Style.FillAlpha; - // ImU32 col = ImGui::GetColorU32(color); - // DrawList.AddRectFilled(a, b, col); - // i++; - // } + // switch (GetCurrentScale()) { + // case ImPlotScale_LinLin: RenderPrimitives(RectRenderer, TransformerLinLin>(getter, TransformerLinLin()), DrawList, GImPlot->BB_Plot); break; + // case ImPlotScale_LogLin: RenderPrimitives(RectRenderer, TransformerLogLin>(getter, TransformerLogLin()), DrawList, GImPlot->BB_Plot); break;; + // case ImPlotScale_LinLog: RenderPrimitives(RectRenderer, TransformerLinLog>(getter, TransformerLinLog()), DrawList, GImPlot->BB_Plot); break;; + // case ImPlotScale_LogLog: RenderPrimitives(RectRenderer, TransformerLogLog>(getter, TransformerLogLog()), DrawList, GImPlot->BB_Plot); break;; // } + + 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); + int 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_max.y - (0.5*h + r*h); + ImVec2 a = transformer(ImPlotPoint(p.x - half_size.x, p.y - half_size.y)); + ImVec2 b = transformer(ImPlotPoint(p.x + half_size.x, p.y + half_size.y)); + double t = ImRemap((double)values[i], scale_min, scale_max, 0.0, 1.0); + ImVec4 color = LerpColormap((float)t); + color.w *= gp.Style.FillAlpha; + ImU32 col = ImGui::GetColorU32(color); + DrawList.AddRectFilled(a, b, col); + i++; + } + } if (fmt != NULL) { - 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); - int i = 0; + // 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); + i = 0; for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { ImPlotPoint p; From a32053bb2494a0f15dea783ff010f7c27eed99a0 Mon Sep 17 00:00:00 2001 From: epezent Date: Sat, 14 Nov 2020 08:07:37 -0600 Subject: [PATCH 06/19] proto hist 2d --- implot.h | 9 ++- implot_demo.cpp | 8 +++ implot_items.cpp | 154 ++++++++++++++++++++++++++++++----------------- 3 files changed, 114 insertions(+), 57 deletions(-) diff --git a/implot.h b/implot.h index a35abfae..5c2f458c 100644 --- a/implot.h +++ b/implot.h @@ -416,9 +416,6 @@ template IMPLOT_API void PlotBarsH(const char* label_id, const T* v template IMPLOT_API void PlotBarsH(const char* label_id, const T* xs, const T* ys, int count, double height, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotBarsHG(const char* label_id, ImPlotPoint (*getter)(void* data, int idx), void* data, int count, double height, int offset=0); -// Plots a horizontal histogram. If cumulative is true, each bin contains its count plus the counts of all previous bins. If density is true, the PDF is visualized. If both are true, the CDF is visualized. -template IMPLOT_API void PlotHistogram(const char* label_id, const T* values, int count, int bins, bool cumulative=false, bool density=false, double bar_scale=1.0); - // Plots vertical error bar. The label_id should be the same as the label_id of the associated line or bar plot. template IMPLOT_API void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* err, int count, int offset=0, int stride=sizeof(T)); template IMPLOT_API void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset=0, int stride=sizeof(T)); @@ -437,6 +434,12 @@ template IMPLOT_API void PlotPieChart(const char* const label_ids[] // Plots a 2D heatmap chart. Values are expected to be in row-major order. #label_fmt can be set to NULL for no labels. template IMPLOT_API void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1)); +// Plots a horizontal histogram. If cumulative is true, each bin contains its count plus the counts of all previous bins. If density is true, the PDF is visualized. If both are true, the CDF is visualized. +template IMPLOT_API void PlotHistogram(const char* label_id, const T* values, int count, int bins, bool cumulative=false, bool density=false, double bar_scale=1.0); + +// Plots two dimensional, bivariate histogram as a heatmap. If density is true, the PDF is visualized. +template IMPLOT_API void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins, int y_bins, bool density=false); + // Plots digital data. Digital plots do not respond to y drag or zoom, and are always referenced to the bottom of the plot. template IMPLOT_API void PlotDigital(const char* label_id, const T* xs, const T* ys, int count, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotDigitalG(const char* label_id, ImPlotPoint (*getter)(void* data, int idx), void* data, int count, int offset=0); diff --git a/implot_demo.cpp b/implot_demo.cpp index b6315e68..19961d25 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -450,6 +450,14 @@ void ShowDemoWindow(bool* p_open) { ImPlot::PlotLine("Theoretical",x,y,100); ImPlot::EndPlot(); } + + if (ImPlot::BeginPlot("Hist2D")) { + ImPlot::PushColormap(ImPlotColormap_Jet); + ImPlot::PlotHistogram2D("Hist2D",dist.Data,dist.Data,5000,100,100); + ImPlot::PopColormap(); + + ImPlot::EndPlot(); + } } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Error Bars")) { diff --git a/implot_items.cpp b/implot_items.cpp index 49b8946b..8a03392b 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1364,60 +1364,6 @@ void PlotBarsHG(const char* label_id, ImPlotPoint (*getter_func)(void* data, int PlotBarsHEx(label_id, getter, height); } -//----------------------------------------------------------------------------- -// PLOT HISTOGRAM -//----------------------------------------------------------------------------- - -template -void PlotHistogram(const char* label_id, const T* values, int count, int bins, bool cumulative, bool density, double bar_scale) { - if (count <= 0 || bins <= 0) - return; - const T min_val = ImMinArray(values, count); - const T max_val = ImMaxArray(values, count); - const double width = (double)(max_val - min_val) / bins; - static ImVector bin_centers; - static ImVector bin_counts; - bin_centers.resize(bins); - bin_counts.resize(bins); - for (int b = 0; b < bins; ++b) { - bin_centers[b] = (double)min_val + b * width + width * 0.5; - bin_counts[b] = 0; - } - for (int i = 0; i < count; ++i) { - int b = (int)((double)(values[i] - min_val) / width); - b = ImClamp(b, 0, bins - 1); - bin_counts[b] += 1.0; - } - if (cumulative && density) { - for (int b = 1; b < bins; ++b) - bin_counts[b] += bin_counts[b-1]; - for (int b = 0; b < bins; ++b) - bin_counts[b] /= count; - } - else if (cumulative) { - for (int b = 1; b < bins; ++b) - bin_counts[b] += bin_counts[b-1]; - } - else if (density) { - for (int b = 0; b < bins; ++b) - bin_counts[b] = bin_counts[b] / (count * width); - - } - PlotBars(label_id, &bin_centers.Data[0], &bin_counts.Data[0], bins, bar_scale*width); -} - -template IMPLOT_API void PlotHistogram(const char* label_id, const ImS8* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImU8* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImS16* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImU16* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImS32* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImU32* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImS64* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImU64* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const float* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const double* values, int count, int bins, bool cumulative, bool density, double bar_scale); - - //----------------------------------------------------------------------------- // PLOT ERROR BARS //----------------------------------------------------------------------------- @@ -1783,6 +1729,106 @@ template IMPLOT_API void PlotHeatmap(const char* label_id, const ImU64* v template IMPLOT_API void PlotHeatmap(const char* label_id, const float* values, int rows, int cols, double scale_min, double scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max); template IMPLOT_API void PlotHeatmap(const char* label_id, const double* values, int rows, int cols, double scale_min, double scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max); +//----------------------------------------------------------------------------- +// PLOT HISTOGRAM +//----------------------------------------------------------------------------- + +template +void PlotHistogram(const char* label_id, const T* values, int count, int bins, bool cumulative, bool density, double bar_scale) { + if (count <= 0 || bins <= 0) + return; + const T min_val = ImMinArray(values, count); + const T max_val = ImMaxArray(values, count); + const double width = (double)(max_val - min_val) / bins; + static ImVector bin_centers; + static ImVector bin_counts; + bin_centers.resize(bins); + bin_counts.resize(bins); + for (int b = 0; b < bins; ++b) { + bin_centers[b] = (double)min_val + b * width + width * 0.5; + bin_counts[b] = 0; + } + for (int i = 0; i < count; ++i) { + int b = (int)((double)(values[i] - min_val) / width); + b = ImClamp(b, 0, bins - 1); + bin_counts[b] += 1.0; + } + if (cumulative && density) { + for (int b = 1; b < bins; ++b) + bin_counts[b] += bin_counts[b-1]; + for (int b = 0; b < bins; ++b) + bin_counts[b] /= count; + } + else if (cumulative) { + for (int b = 1; b < bins; ++b) + bin_counts[b] += bin_counts[b-1]; + } + else if (density) { + for (int b = 0; b < bins; ++b) + bin_counts[b] = bin_counts[b] / (count * width); + + } + PlotBars(label_id, &bin_centers.Data[0], &bin_counts.Data[0], bins, bar_scale*width); +} + +template IMPLOT_API void PlotHistogram(const char* label_id, const ImS8* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImU8* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImS16* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImU16* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImS32* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImU32* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImS64* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImU64* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const float* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const double* values, int count, int bins, bool cumulative, bool density, double bar_scale); + +//----------------------------------------------------------------------------- +// PLOT HISTOGRAM 2D +//----------------------------------------------------------------------------- + +// Plots two dimensional, bivariate histogram as a heatmap. If density is true, the PDF is visualized. +template +void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins, int y_bins, bool density) { + if (count <= 0 || x_bins <= 0 || y_bins <= 0) + return; + const int bins = x_bins * y_bins; + const T min_x = ImMinArray(xs, count); + const T max_x = ImMaxArray(xs, count); + const T min_y = ImMinArray(ys, count); + const T max_y = ImMaxArray(ys, count); + const double width = (double)(max_x - min_x) / x_bins; + const double height = (double)(max_y - min_y) / y_bins; + + static ImVector bin_counts; + bin_counts.resize(bins); + + for (int i = 0; i < count; ++i) { + const int xb = ImClamp( (int)((double)(xs[i] - min_x) / width) , 0, x_bins - 1); + const int yb = ImClamp( (int)((double)(ys[i] - min_y) / height) , 0, y_bins - 1); + const int b = yb * x_bins + xb; + bin_counts[b] += 1.0; + } + if (density) { + for (int b = 0; b < bins; ++b) + bin_counts[b] = bin_counts[b] / (count * width * height); + } + const double max_count = ImMaxArray(&bin_counts.Data[0], bins); + PlotHeatmap(label_id, &bin_counts.Data[0], x_bins, y_bins, 0, max_count, NULL, ImPlotPoint((double)min_x, (double)min_y), ImPlotPoint((double)max_x, (double)max_y)); +} + +template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImS8* xs, const ImS8* ys, int count, int x_bins, int y_bins, bool density); +template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImU8* xs, const ImU8* ys, int count, int x_bins, int y_bins, bool density); +template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImS16* xs, const ImS16* ys, int count, int x_bins, int y_bins, bool density); +template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImU16* xs, const ImU16* ys, int count, int x_bins, int y_bins, bool density); +template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImS32* xs, const ImS32* ys, int count, int x_bins, int y_bins, bool density); +template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImU32* xs, const ImU32* ys, int count, int x_bins, int y_bins, bool density); +template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImS64* xs, const ImS64* ys, int count, int x_bins, int y_bins, bool density); +template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImU64* xs, const ImU64* ys, int count, int x_bins, int y_bins, bool density); +template IMPLOT_API void PlotHistogram2D(const char* label_id, const float* xs, const float* ys, int count, int x_bins, int y_bins, bool density); +template IMPLOT_API void PlotHistogram2D(const char* label_id, const double* xs, const double* ys, int count, int x_bins, int y_bins, bool density); + + + //----------------------------------------------------------------------------- // PLOT DIGITAL //----------------------------------------------------------------------------- From 277ef7d7cdfe0d744a114a2000f0e90241a817b3 Mon Sep 17 00:00:00 2001 From: epezent Date: Sat, 14 Nov 2020 14:27:40 -0600 Subject: [PATCH 07/19] hist2d --- implot_demo.cpp | 1 - implot_items.cpp | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index 19961d25..7400b0ab 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -455,7 +455,6 @@ void ShowDemoWindow(bool* p_open) { ImPlot::PushColormap(ImPlotColormap_Jet); ImPlot::PlotHistogram2D("Hist2D",dist.Data,dist.Data,5000,100,100); ImPlot::PopColormap(); - ImPlot::EndPlot(); } } diff --git a/implot_items.cpp b/implot_items.cpp index 8a03392b..c6222955 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1806,6 +1806,8 @@ void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, const int xb = ImClamp( (int)((double)(xs[i] - min_x) / width) , 0, x_bins - 1); const int yb = ImClamp( (int)((double)(ys[i] - min_y) / height) , 0, y_bins - 1); const int b = yb * x_bins + xb; + if (b >= bins) + printf("no!\n"); bin_counts[b] += 1.0; } if (density) { From 9ddc76d8973784cf470b90a3495ad8c9b1546d8c Mon Sep 17 00:00:00 2001 From: epezent Date: Sat, 14 Nov 2020 14:27:48 -0600 Subject: [PATCH 08/19] hist2d --- implot_items.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/implot_items.cpp b/implot_items.cpp index c6222955..8a03392b 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1806,8 +1806,6 @@ void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, const int xb = ImClamp( (int)((double)(xs[i] - min_x) / width) , 0, x_bins - 1); const int yb = ImClamp( (int)((double)(ys[i] - min_y) / height) , 0, y_bins - 1); const int b = yb * x_bins + xb; - if (b >= bins) - printf("no!\n"); bin_counts[b] += 1.0; } if (density) { From f0e28614d9a7fda7640035326402791e1ebeba8c Mon Sep 17 00:00:00 2001 From: epezent Date: Sat, 14 Nov 2020 17:07:08 -0600 Subject: [PATCH 09/19] more work on histograms --- implot.cpp | 8 +++---- implot.h | 7 +++--- implot_demo.cpp | 22 ++++++++++--------- implot_internal.h | 6 +++--- implot_items.cpp | 55 +++++++++++++++++++++++++++++------------------ 5 files changed, 57 insertions(+), 41 deletions(-) diff --git a/implot.cpp b/implot.cpp index 6e3573b7..eb12ff74 100644 --- a/implot.cpp +++ b/implot.cpp @@ -1701,13 +1701,13 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons for (int i = 0; i < IMPLOT_Y_AXES; i++) gp.FitY[i] = plot.YAxis[i].HoveredTot; } - // fit from FitNextPlotAxes - if (gp.NextPlotData.FitX) { + // fit from FitNextPlotAxes or auto fit + if (gp.NextPlotData.FitX || ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_AutoFit)) { gp.FitThisFrame = true; gp.FitX = true; } for (int i = 0; i < IMPLOT_Y_AXES; ++i) { - if (gp.NextPlotData.FitY[i]) { + if (gp.NextPlotData.FitY[i] || ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_AutoFit)) { gp.FitThisFrame = true; gp.FitY[i] = true; } @@ -1871,7 +1871,7 @@ void ShowAxisContextMenu(ImPlotAxisState& state, bool time_allowed) { ImGui::PushItemWidth(75); ImPlotAxis& axis = *state.Axis; - bool total_lock = state.HasRange && state.RangeCond == ImGuiCond_Always; + bool total_lock = state.Lock; bool logscale = ImHasFlag(axis.Flags, ImPlotAxisFlags_LogScale); bool timescale = ImHasFlag(axis.Flags, ImPlotAxisFlags_Time); bool grid = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoGridLines); diff --git a/implot.h b/implot.h index 5c2f458c..593c3846 100644 --- a/implot.h +++ b/implot.h @@ -89,8 +89,9 @@ enum ImPlotAxisFlags_ { ImPlotAxisFlags_LogScale = 1 << 3, // a logartithmic (base 10) axis scale will be used (mutually exclusive with ImPlotAxisFlags_Time) ImPlotAxisFlags_Time = 1 << 4, // axis will display date/time formatted labels (mutually exclusive with ImPlotAxisFlags_LogScale) ImPlotAxisFlags_Invert = 1 << 5, // the axis will be inverted - ImPlotAxisFlags_LockMin = 1 << 6, // the axis minimum value will be locked when panning/zooming - ImPlotAxisFlags_LockMax = 1 << 7, // the axis maximum value will be locked when panning/zooming + ImPlotAxisFlags_AutoFit = 1 << 6, // axis will be auto-fitting + ImPlotAxisFlags_LockMin = 1 << 7, // the axis minimum value will be locked when panning/zooming + ImPlotAxisFlags_LockMax = 1 << 8, // the axis maximum value will be locked when panning/zooming ImPlotAxisFlags_Lock = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax, ImPlotAxisFlags_NoDecorations = ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels }; @@ -435,7 +436,7 @@ template IMPLOT_API void PlotPieChart(const char* const label_ids[] template IMPLOT_API void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1)); // Plots a horizontal histogram. If cumulative is true, each bin contains its count plus the counts of all previous bins. If density is true, the PDF is visualized. If both are true, the CDF is visualized. -template IMPLOT_API void PlotHistogram(const char* label_id, const T* values, int count, int bins, bool cumulative=false, bool density=false, double bar_scale=1.0); +template IMPLOT_API void PlotHistogram(const char* label_id, const T* values, int count, int bins, bool cumulative=false, bool density=false, ImPlotRange range=ImPlotRange(), double bar_scale=1.0); // Plots two dimensional, bivariate histogram as a heatmap. If density is true, the PDF is visualized. template IMPLOT_API void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins, int y_bins, bool density=false); diff --git a/implot_demo.cpp b/implot_demo.cpp index 7400b0ab..b388860b 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -79,7 +79,6 @@ double RandomGauss() { do { double U1 = (double)rand() / RAND_MAX; double U2 = (double)rand() / RAND_MAX; - V1 = 2 * U1 - 1; V2 = 2 * U2 - 1; S = V1 * V1 + V2 * V2; @@ -437,26 +436,29 @@ void ShowDemoWindow(bool* p_open) { ImGui::SetNextItemWidth(100); ImGui::SliderInt("Bins", &bins, 0, 100); ImGui::SameLine(); - if (ImGui::Checkbox("Density", &density)) - ImPlot::FitNextPlotAxes(); + ImGui::Checkbox("Density", &density); ImGui::SameLine(); - if (ImGui::Checkbox("Cumulative", &cumulative)) - ImPlot::FitNextPlotAxes(); + ImGui::Checkbox("Cumulative", &cumulative); + static float rmin = 0; + static float rmax = 0; + ImGui::DragFloat2("Range",&rmin,0.1f,-3,13); ImPlot::SetNextPlotLimits(-3, 13, 0, 0.25); if (ImPlot::BeginPlot("##Histograms")) { ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.5f); - ImPlot::PlotHistogram("Empirical", dist.Data, 5000, bins, cumulative, density); + ImPlot::PlotHistogram("Empirical", dist.Data, 5000, bins, cumulative, density, ImPlotRange(rmin,rmax)); if (density) ImPlot::PlotLine("Theoretical",x,y,100); ImPlot::EndPlot(); } - if (ImPlot::BeginPlot("Hist2D")) { - ImPlot::PushColormap(ImPlotColormap_Jet); - ImPlot::PlotHistogram2D("Hist2D",dist.Data,dist.Data,5000,100,100); - ImPlot::PopColormap(); + static NormalDistribution<10000> dist1(5, 5); + static NormalDistribution<10000> dist2(4, 1); + ImPlot::PushColormap(ImPlotColormap_Jet); + if (ImPlot::BeginPlot("Hist2D",0,0,ImVec2(-1,0),0,ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit)) { + ImPlot::PlotHistogram2D("Hist2D",dist1.Data,dist2.Data,10000,100,100); ImPlot::EndPlot(); } + ImPlot::PopColormap(); } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Error Bars")) { diff --git a/implot_internal.h b/implot_internal.h index a0939f99..3edfb04e 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -483,9 +483,9 @@ struct ImPlotAxisState Present = present; HasLabels = !ImHasFlag(Axis->Flags, ImPlotAxisFlags_NoTickLabels); Invert = ImHasFlag(Axis->Flags, ImPlotAxisFlags_Invert); - LockMin = ImHasFlag(Axis->Flags, ImPlotAxisFlags_LockMin) || (HasRange && RangeCond == ImGuiCond_Always); - LockMax = ImHasFlag(Axis->Flags, ImPlotAxisFlags_LockMax) || (HasRange && RangeCond == ImGuiCond_Always); - Lock = !Present || ((LockMin && LockMax) || (HasRange && RangeCond == ImGuiCond_Always)); + LockMin = ImHasFlag(Axis->Flags, ImPlotAxisFlags_LockMin) || (HasRange && RangeCond == ImGuiCond_Always) || ImHasFlag(Axis->Flags, ImPlotAxisFlags_AutoFit); + LockMax = ImHasFlag(Axis->Flags, ImPlotAxisFlags_LockMax) || (HasRange && RangeCond == ImGuiCond_Always) || ImHasFlag(Axis->Flags, ImPlotAxisFlags_AutoFit); + Lock = !Present || ((LockMin && LockMax) || (HasRange && RangeCond == ImGuiCond_Always)) || ImHasFlag(Axis->Flags, ImPlotAxisFlags_AutoFit); IsTime = ImHasFlag(Axis->Flags, ImPlotAxisFlags_Time); } diff --git a/implot_items.cpp b/implot_items.cpp index 8a03392b..f2585eda 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1662,6 +1662,13 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value 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); + if (scale_min == scale_max) { + ImVec2 a = transformer(bounds_min); + ImVec2 b = transformer(bounds_max); + ImU32 col = ImGui::GetColorU32(LerpColormap(0)); + DrawList.AddRectFilled(a, b, col); + return; + } int i = 0; for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { @@ -1678,8 +1685,8 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value i++; } } + i = 0; if (fmt != NULL) { - i = 0; for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { ImPlotPoint p; @@ -1701,7 +1708,6 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value template void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) { - IM_ASSERT_USER_ERROR(scale_min != scale_max, "Scale values must be different!"); if (BeginItem(label_id)) { if (FitThisFrame()) { FitPoint(bounds_min); @@ -1734,24 +1740,28 @@ template IMPLOT_API void PlotHeatmap(const char* label_id, const double* //----------------------------------------------------------------------------- template -void PlotHistogram(const char* label_id, const T* values, int count, int bins, bool cumulative, bool density, double bar_scale) { +void PlotHistogram(const char* label_id, const T* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale) { if (count <= 0 || bins <= 0) return; - const T min_val = ImMinArray(values, count); - const T max_val = ImMaxArray(values, count); - const double width = (double)(max_val - min_val) / bins; + if (range.Min == 0 && range.Max == 0) { + range.Min = (double)ImMinArray(values, count); + range.Max = (double)ImMaxArray(values, count); + } + const double width = range.Size() / bins; static ImVector bin_centers; static ImVector bin_counts; bin_centers.resize(bins); bin_counts.resize(bins); for (int b = 0; b < bins; ++b) { - bin_centers[b] = (double)min_val + b * width + width * 0.5; + bin_centers[b] = range.Min + b * width + width * 0.5; bin_counts[b] = 0; } for (int i = 0; i < count; ++i) { - int b = (int)((double)(values[i] - min_val) / width); - b = ImClamp(b, 0, bins - 1); - bin_counts[b] += 1.0; + if (range.Contains((double)values[i])) { + int b = (int)((double)(values[i] - range.Min) / width); + b = ImClamp(b, 0, bins - 1); + bin_counts[b] += 1.0; + } } if (cumulative && density) { for (int b = 1; b < bins; ++b) @@ -1771,16 +1781,16 @@ void PlotHistogram(const char* label_id, const T* values, int count, int bins, b PlotBars(label_id, &bin_centers.Data[0], &bin_counts.Data[0], bins, bar_scale*width); } -template IMPLOT_API void PlotHistogram(const char* label_id, const ImS8* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImU8* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImS16* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImU16* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImS32* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImU32* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImS64* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImU64* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const float* values, int count, int bins, bool cumulative, bool density, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const double* values, int count, int bins, bool cumulative, bool density, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImS8* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImU8* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImS16* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImU16* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImS32* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImU32* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImS64* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const ImU64* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const float* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); +template IMPLOT_API void PlotHistogram(const char* label_id, const double* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); //----------------------------------------------------------------------------- // PLOT HISTOGRAM 2D @@ -1791,7 +1801,6 @@ template void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins, int y_bins, bool density) { if (count <= 0 || x_bins <= 0 || y_bins <= 0) return; - const int bins = x_bins * y_bins; const T min_x = ImMinArray(xs, count); const T max_x = ImMaxArray(xs, count); const T min_y = ImMinArray(ys, count); @@ -1800,7 +1809,11 @@ void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, const double height = (double)(max_y - min_y) / y_bins; static ImVector bin_counts; + + const int bins = x_bins * y_bins; bin_counts.resize(bins); + for (int b = 0; b < bins; ++b) + bin_counts[b] = 0; for (int i = 0; i < count; ++i) { const int xb = ImClamp( (int)((double)(xs[i] - min_x) / width) , 0, x_bins - 1); From 8ce97034791a39bc6371b75e82bdc2d764c80c16 Mon Sep 17 00:00:00 2001 From: epezent Date: Mon, 16 Nov 2020 08:38:29 -0600 Subject: [PATCH 10/19] add auto binning --- implot.h | 21 +++++++++++++------ implot_demo.cpp | 6 +++--- implot_internal.h | 23 +++++++++++++++++++-- implot_items.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 85 insertions(+), 16 deletions(-) diff --git a/implot.h b/implot.h index 9aa4f499..2744e61f 100644 --- a/implot.h +++ b/implot.h @@ -61,6 +61,7 @@ typedef int ImPlotColormap; // -> enum ImPlotColormap_ typedef int ImPlotLocation; // -> enum ImPlotLocation_ typedef int ImPlotOrientation; // -> enum ImPlotOrientation_ typedef int ImPlotYAxis; // -> enum ImPlotYAxis_; +typedef int ImPlotBinMethod; // -> enum ImPlotBinMethod_ // Options for plots. enum ImPlotFlags_ { @@ -219,6 +220,14 @@ enum ImPlotYAxis_ { ImPlotYAxis_3 = 2 // second on right side }; +// Enums for different automatic histogram binning methods (k = bin count or w = bin width) +enum ImPlotBinMethod_ { + ImPlotBinMethod_Sqrt = -1, // k = sqrt(n) + ImPlotBinMethod_Sturges = -2, // k = 1 + log2(n) + ImPlotBinMethod_Rice = -3, // k = 2 * cbrt(n) + ImPlotBinMethod_Scott = -4, // w = 3.49 * sigma / cbrt(n) +}; + // Double precision version of ImVec2 used by ImPlot. Extensible by end users. struct ImPlotPoint { double x, y; @@ -440,13 +449,13 @@ template IMPLOT_API void PlotPieChart(const char* const label_ids[] // Plots a 2D heatmap chart. Values are expected to be in row-major order. #label_fmt can be set to NULL for no labels. template IMPLOT_API void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1)); -// Plots a horizontal histogram. If cumulative is true, each bin contains its count plus the counts of all previous bins. If density is true, the PDF is visualized. If both are true, the CDF is visualized. -// If range is left unspecified, the min/max of values will be used as the range. Values outside of range are not binned and are ignored in the total count for density=true histograms. -template IMPLOT_API void PlotHistogram(const char* label_id, const T* values, int count, int bins, bool cumulative=false, bool density=false, ImPlotRange range=ImPlotRange(), double bar_scale=1.0); +// Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBinMethod. If #cumulative is true, each bin contains its count plus the counts of all previous bins. If #density is true, the PDF is visualized. +// If both are true, the CDF is visualized. If #range is left unspecified, the min/max of #values will be used as the range. Values outside of range are not binned and are ignored in the total count for #density=true histograms. +template IMPLOT_API void PlotHistogram(const char* label_id, const T* values, int count, int bins=ImPlotBinMethod_Sturges, bool cumulative=false, bool density=false, ImPlotRange range=ImPlotRange(), double bar_scale=1.0); -// Plots two dimensional, bivariate histogram as a heatmap. If density is true, the PDF is visualized. If range is left unspecified, -// the min/max of xs an ys will be used as the ranges. Values outside of range are not binned and are ignored in the total count for density=true histograms. -template IMPLOT_API void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins, int y_bins, bool density=false, ImPlotLimits range=ImPlotLimits()); +// Plots two dimensional, bivariate histogram as a heatmap. #x_bins and #y_bins can be a positive integer or an ImPlotBinMethod. If #density is true, the PDF is visualized. If #range is left unspecified, +// the min/max of #xs an #ys will be used as the ranges. Values outside of range are not binned and are ignored in the total count for #density=true histograms. +template IMPLOT_API void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins=ImPlotBinMethod_Sturges, int y_bins=ImPlotBinMethod_Sturges, bool density=false, ImPlotLimits range=ImPlotLimits()); // Plots digital data. Digital plots do not respond to y drag or zoom, and are always referenced to the bottom of the plot. template IMPLOT_API void PlotDigital(const char* label_id, const T* xs, const T* ys, int count, int offset=0, int stride=sizeof(T)); diff --git a/implot_demo.cpp b/implot_demo.cpp index f730b894..beea25cf 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -417,7 +417,7 @@ void ShowDemoWindow(bool* p_open) { } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Histograms")) { - static int bins = 50; + static int bins = ImPlotBinMethod_Sturges; static bool cumulative = false; static bool density = true; static double mu = 5; @@ -437,8 +437,8 @@ void ShowDemoWindow(bool* p_open) { y[i] /= y[99]; } } - ImGui::SetNextItemWidth(100); - ImGui::SliderInt("Bins", &bins, 0, 100); + ImGui::SetNextItemWidth(200); + ImGui::SliderInt("Bins", &bins, -4, 100); ImGui::SameLine(); ImGui::Checkbox("Density", &density); ImGui::SameLine(); diff --git a/implot_internal.h b/implot_internal.h index aa3535cf..c1a94da3 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -106,14 +106,33 @@ inline double ImConstrainInf(double val) { return val == HUGE_VAL ? DBL_MAX : v inline double ImConstrainLog(double val) { return val <= 0 ? 0.001f : val; } // Turns numbers less than 0 to zero inline double ImConstrainTime(double val) { return val < IMPLOT_MIN_TIME ? IMPLOT_MIN_TIME : (val > IMPLOT_MAX_TIME ? IMPLOT_MAX_TIME : val); } +// True if two numbers are approximately equal using units in the last place. +inline bool ImAlmostEqual(double v1, double v2, int ulp = 2) { return ImAbs(v1-v2) < DBL_EPSILON * ImAbs(v1+v2) * ulp || ImAbs(v1-v2) < DBL_MIN; } // Finds min value in an unsorted array template inline T ImMinArray(const T* values, int count) { T m = values[0]; for (int i = 1; i < count; ++i) { if (values[i] < m) { m = values[i]; } } return m; } // Finds the max value in an unsorted array template inline T ImMaxArray(const T* values, int count) { T m = values[0]; for (int i = 1; i < count; ++i) { if (values[i] > m) { m = values[i]; } } return m; } -// True if two numbers are approximately equal using units in the last place. -inline bool ImAlmostEqual(double v1, double v2, int ulp = 2) { return ImAbs(v1-v2) < DBL_EPSILON * ImAbs(v1+v2) * ulp || ImAbs(v1-v2) < DBL_MIN; } +// Finds the mean of an array +template +inline double ImMean(const T* values, int count) { + double den = 1.0 / count; + double mu = 0; + for (int i = 0; i < count; ++i) + mu += values[i] * den; + return mu; +} +// Finds the sample standard deviation of an array +template +inline double ImStdDev(const T* values, int count) { + double den = 1.0 / (count - 1.0); + double mu = ImMean(values, count); + double x = 0; + for (int i = 0; i < count; ++i) + x += (values[i] - mu) * (values[i] - mu) * den; + return sqrt(x); +} // Offset calculator helper template diff --git a/implot_items.cpp b/implot_items.cpp index 71f3923e..6959bc77 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1739,15 +1739,46 @@ template IMPLOT_API void PlotHeatmap(const char* label_id, const double* // PLOT HISTOGRAM //----------------------------------------------------------------------------- +template +void BinValues(const T* values, int count, ImPlotBinMethod meth, const ImPlotRange& range, int& bins, double& width) { + switch (meth) { + case ImPlotBinMethod_Sqrt: + bins = (int)ceil(sqrt(count)); + width = range.Size() / bins; + break; + case ImPlotBinMethod_Sturges: + bins = (int)ceil(1.0 + log2(count)); + width = range.Size() / bins; + break; + case ImPlotBinMethod_Rice: + bins = (int)ceil(2 * cbrt(count)); + width = range.Size() / bins; + break; + case ImPlotBinMethod_Scott: + width = 3.49 * ImStdDev(values, count) / cbrt(count); + bins = (int)round(range.Size() / width); + width = range.Size() / bins; + break; + } +} + template void PlotHistogram(const char* label_id, const T* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale) { - if (count <= 0 || bins <= 0) + + if (count <= 0 || bins == 0) return; + if (range.Min == 0 && range.Max == 0) { range.Min = (double)ImMinArray(values, count); range.Max = (double)ImMaxArray(values, count); } - const double width = range.Size() / bins; + + double width; + if (bins < 0) + BinValues(values, count, bins, range, bins, width); + else + width = range.Size() / bins; + static ImVector bin_centers; static ImVector bin_counts; bin_centers.resize(bins); @@ -1800,8 +1831,10 @@ template IMPLOT_API void PlotHistogram(const char* label_id, const doubl // Plots two dimensional, bivariate histogram as a heatmap. If density is true, the PDF is visualized. template void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range) { - if (count <= 0 || x_bins <= 0 || y_bins <= 0) + + if (count <= 0 || x_bins == 0 || y_bins == 0) return; + if (range.X.Min == 0 && range.X.Max == 0) { range.X.Min = (double)ImMinArray(xs, count); range.X.Max = (double)ImMaxArray(xs, count); @@ -1810,8 +1843,16 @@ void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, range.Y.Min = (double)ImMinArray(ys, count); range.Y.Max = (double)ImMaxArray(ys, count); } - const double width = range.X.Size() / x_bins; - const double height = range.Y.Size() / y_bins; + + double width, height; + if (x_bins < 0) + BinValues(xs, count, x_bins, range.X, x_bins, width); + else + width = range.X.Size() / x_bins; + if (y_bins < 0) + BinValues(ys, count, y_bins, range.Y, y_bins, height); + else + height = range.Y.Size() / y_bins; static ImVector bin_counts; const int bins = x_bins * y_bins; From 9c182f6157068394f8ae91788224db146178b097 Mon Sep 17 00:00:00 2001 From: epezent Date: Tue, 17 Nov 2020 22:57:39 -0600 Subject: [PATCH 11/19] fix upside down hist2d and merge min/max calculation --- implot_demo.cpp | 4 ++-- implot_internal.h | 20 ++++++++++++++++++ implot_items.cpp | 53 +++++++++++++++++++++++++++++++++-------------- 3 files changed, 59 insertions(+), 18 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index beea25cf..5456af9a 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -455,8 +455,8 @@ void ShowDemoWindow(bool* p_open) { ImPlot::EndPlot(); } - static NormalDistribution<10000> dist1(0, 2); - static NormalDistribution<10000> dist2(0, 1); + static NormalDistribution<10000> dist1(1, 2); + static NormalDistribution<10000> dist2(1, 1); ImPlot::PushColormap(ImPlotColormap_Jet); if (ImPlot::BeginPlot("Hist2D",0,0,ImVec2(-1,0),0,ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit)) { ImPlot::PlotHistogram2D("Hist2D",dist1.Data,dist2.Data,10000,100,100,false,ImPlotLimits(-6,6,-6,6)); diff --git a/implot_internal.h b/implot_internal.h index c1a94da3..c0f6882c 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -114,6 +114,16 @@ inline T ImMinArray(const T* values, int count) { T m = values[0]; for (int i = // Finds the max value in an unsorted array template inline T ImMaxArray(const T* values, int count) { T m = values[0]; for (int i = 1; i < count; ++i) { if (values[i] > m) { m = values[i]; } } return m; } +// Finds the min and max value in an unsorted array +template +inline void ImMinMaxArray(const T* values, int count, T* min_out, T* max_out) { + T Min = values[0]; T Max = values[0]; + for (int i = 1; i < count; ++i) { + if (values[i] < Min) { Min = values[i]; } + if (values[i] > Max) { Max = values[i]; } + } + *min_out = Min; *max_out = Max; +} // Finds the mean of an array template inline double ImMean(const T* values, int count) { @@ -133,6 +143,16 @@ inline double ImStdDev(const T* values, int count) { x += (values[i] - mu) * (values[i] - mu) * den; return sqrt(x); } +// Mix color a and b by factor t [0 256] +inline ImU32 ImMixColor32(ImU32 a, ImU32 b, ImU32 t) { + ImU32 af = 256 - t; + ImU32 bf = t; + ImU64 al = (a & 0x00ff00ff) | (((ImU64)(a & 0xff00ff00)) << 24); + ImU64 bl = (b & 0x00ff00ff) | (((ImU64)(b & 0xff00ff00)) << 24); + ImU64 mix = (al * af + bl * bf); + ImU32 result = ((mix >> 32) & 0xff00ff00) | ((mix & 0xff00ff00) >> 8); + return result; +} // Offset calculator helper template diff --git a/implot_items.cpp b/implot_items.cpp index 6959bc77..3d1b5b21 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1657,7 +1657,7 @@ template IMPLOT_API void PlotPieChart(const char* const label_ids[], con //----------------------------------------------------------------------------- template -void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* values, int rows, int cols, double scale_min, double scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) { +void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* values, int rows, int cols, double scale_min, double scale_max, const char* fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, bool reverse_y) { ImPlotContext& gp = *GImPlot; const double w = (bounds_max.x - bounds_min.x) / cols; const double h = (bounds_max.y - bounds_min.y) / rows; @@ -1670,11 +1670,13 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value return; } int i = 0; + const double yref = reverse_y ? bounds_max.y : bounds_min.y; + const double ydir = reverse_y ? -1 : 1; 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_max.y - (0.5*h + r*h); + p.y = yref + ydir * (0.5*h + r*h); ImVec2 a = transformer(ImPlotPoint(p.x - half_size.x, p.y - half_size.y)); ImVec2 b = transformer(ImPlotPoint(p.x + half_size.x, p.y + half_size.y)); double t = ImRemap((double)values[i], scale_min, scale_max, 0.0, 1.0); @@ -1691,7 +1693,7 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value for (int c = 0; c < cols; ++c) { ImPlotPoint p; p.x = bounds_min.x + 0.5*w + c*w; - p.y = bounds_max.y - (0.5*h + r*h); + p.y = yref + ydir * (0.5*h + r*h); ImVec2 px = transformer(p); char buff[32]; sprintf(buff, fmt, values[i]); @@ -1715,10 +1717,10 @@ void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, doub } ImDrawList& DrawList = *GetPlotDrawList(); switch (GetCurrentScale()) { - case ImPlotScale_LinLin: RenderHeatmap(TransformerLinLin(), DrawList, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max); break; - case ImPlotScale_LogLin: RenderHeatmap(TransformerLogLin(), DrawList, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max); break; - case ImPlotScale_LinLog: RenderHeatmap(TransformerLinLog(), DrawList, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max); break; - case ImPlotScale_LogLog: RenderHeatmap(TransformerLogLog(), DrawList, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max); break; + case ImPlotScale_LinLin: RenderHeatmap(TransformerLinLin(), DrawList, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true); break; + case ImPlotScale_LogLin: RenderHeatmap(TransformerLogLin(), DrawList, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true); break; + case ImPlotScale_LinLog: RenderHeatmap(TransformerLinLog(), DrawList, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true); break; + case ImPlotScale_LogLog: RenderHeatmap(TransformerLogLog(), DrawList, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true); break; } EndItem(); } @@ -1769,8 +1771,10 @@ void PlotHistogram(const char* label_id, const T* values, int count, int bins, b return; if (range.Min == 0 && range.Max == 0) { - range.Min = (double)ImMinArray(values, count); - range.Max = (double)ImMaxArray(values, count); + T Min, Max; + ImMinMaxArray(values, count, &Min, &Max); + range.Min = (double)Min; + range.Max = (double)Max; } double width; @@ -1836,12 +1840,16 @@ void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, return; if (range.X.Min == 0 && range.X.Max == 0) { - range.X.Min = (double)ImMinArray(xs, count); - range.X.Max = (double)ImMaxArray(xs, count); + T Min, Max; + ImMinMaxArray(xs, count, &Min, &Max); + range.X.Min = (double)Min; + range.X.Max = (double)Max; } if (range.Y.Min == 0 && range.Y.Max == 0) { - range.Y.Min = (double)ImMinArray(ys, count); - range.Y.Max = (double)ImMaxArray(ys, count); + T Min, Max; + ImMinMaxArray(ys, count, &Min, &Max); + range.Y.Min = (double)Min; + range.Y.Max = (double)Max; } double width, height; @@ -1875,7 +1883,22 @@ void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, bin_counts[b] = bin_counts[b] / (counted * width * height); } const double max_count = ImMaxArray(&bin_counts.Data[0], bins); - PlotHeatmap(label_id, &bin_counts.Data[0], x_bins, y_bins, 0, max_count, NULL, range.Min(), range.Max()); + + if (BeginItem(label_id)) { + if (FitThisFrame()) { + FitPoint(range.Min()); + FitPoint(range.Max()); + } + ImDrawList& DrawList = *GetPlotDrawList(); + switch (GetCurrentScale()) { + case ImPlotScale_LinLin: RenderHeatmap(TransformerLinLin(), DrawList, &bin_counts.Data[0], x_bins, y_bins, 0, max_count, NULL, range.Min(), range.Max(), false); break; + case ImPlotScale_LogLin: RenderHeatmap(TransformerLogLin(), DrawList, &bin_counts.Data[0], x_bins, y_bins, 0, max_count, NULL, range.Min(), range.Max(), false); break; + case ImPlotScale_LinLog: RenderHeatmap(TransformerLinLog(), DrawList, &bin_counts.Data[0], x_bins, y_bins, 0, max_count, NULL, range.Min(), range.Max(), false); break; + case ImPlotScale_LogLog: RenderHeatmap(TransformerLogLog(), DrawList, &bin_counts.Data[0], x_bins, y_bins, 0, max_count, NULL, range.Min(), range.Max(), false); break; + } + EndItem(); + } + // PlotHeatmap(label_id, &bin_counts.Data[0], x_bins, y_bins, 0, max_count, NULL, range.Min(), range.Max()); } template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImS8* xs, const ImS8* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range); @@ -1889,8 +1912,6 @@ template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImU template IMPLOT_API void PlotHistogram2D(const char* label_id, const float* xs, const float* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range); template IMPLOT_API void PlotHistogram2D(const char* label_id, const double* xs, const double* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range); - - //----------------------------------------------------------------------------- // PLOT DIGITAL //----------------------------------------------------------------------------- From 1def36b1737593ea8c5f0c53de182f45941f08c2 Mon Sep 17 00:00:00 2001 From: epezent Date: Sun, 22 Nov 2020 09:22:42 -0600 Subject: [PATCH 12/19] add color32 lerp --- implot.cpp | 33 +++++++++++++++++++++++++++++++++ implot_internal.h | 3 +++ implot_items.cpp | 3 --- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/implot.cpp b/implot.cpp index 994399bf..7c148920 100644 --- a/implot.cpp +++ b/implot.cpp @@ -3145,6 +3145,26 @@ void SetColormap(const ImVec4* colors, int size) { gp.ColormapSize = size; } +const ImU32* GetColormap32(ImPlotColormap colormap, int* size_out) { + static const int csizes[ImPlotColormap_COUNT] = {10,10,9,9,12,11,11,11,11,11,11}; + static const ImOffsetCalculator coffs(csizes); + static ImU32 cdata32[] = { + 4294950656, 4278190335, 4278255487, 4278255615, 4294967040, 4278232575, 4294902015, 4293012362, 4286611584, 4287411410, + 4289753676, 4283598045, 4285048917, 4283584196, 4289950337, 4284512403, 4291005402, 4287401100, 4285839820, 4291671396, + 4280031972, 4290281015, 4283084621, 4288892568, 4278222847, 4281597951, 4280833702, 4290740727, 4288256409, + 4289639675, 4293119411, 4291161036, 4293184478, 4289124862, 4291624959, 4290631909, 4293712637, 4294111986, + 4293119554, 4290017311, 4287291314, 4281114675, 4288256763, 4280031971, 4285513725, 4278222847, 4292260554, 4288298346, 4288282623, 4280834481, + 4283695428, 4285867080, 4287054913, 4287455029, 4287526954, 4287402273, 4286883874, 4285579076, 4283552122, 4280737725, 4280674301, + 4287039501, 4288480321, 4289200234, 4288941455, 4287638193, 4286072780, 4284638433, 4283139314, 4281771772, 4280667900, 4280416752, + 4278190144, 4278190208, 4278190271, 4278190335, 4278206719, 4278223103, 4278239231, 4278255615, 4283826175, 4289396735, 4294967295, + 4294967040, 4294960666, 4294954035, 4294947661, 4294941030, 4294934656, 4294928025, 4294921651, 4294915020, 4294908646, 4294902015, + 4278190154, 4282532475, 4284308894, 4285690554, 4286879686, 4287870160, 4288794330, 4289651940, 4291685869, 4293392118, 4294967295, + 4289331200, 4294901760, 4294923520, 4294945280, 4294967040, 4289396565, 4283826090, 4278255615, 4278233855, 4278212095, 4278190335 + }; + *size_out = csizes[colormap]; + return &cdata32[coffs.Offsets[colormap]]; +} + const ImVec4* GetColormap(ImPlotColormap colormap, int* size_out) { static const int csizes[ImPlotColormap_COUNT] = {10,10,9,9,12,11,11,11,11,11,11}; static const ImOffsetCalculator coffs(csizes); @@ -3304,6 +3324,19 @@ ImVec4 GetColormapColor(int index) { return gp.Colormap[index % gp.ColormapSize]; } +ImU32 LerpColormap32(const ImU32* colormap, int size, float t) { + float tc = ImClamp(t,0.0f,1.0f); + int i1 = (int)((size -1 ) * tc); + int i2 = i1 + 1; + if (i2 == size || size == 1) + return colormap[i1]; + float den = 1.0f / (size - 1); + float t1 = i1 * den; + float t2 = i2 * den; + float tr = ImRemap01(t, t1, t2); + return ImMixColor32(colormap[i1], colormap[i2], (ImU32)(tr*256)); +} + ImVec4 LerpColormap(const ImVec4* colormap, int size, float t) { float tc = ImClamp(t,0.0f,1.0f); int i1 = (int)((size -1 ) * tc); diff --git a/implot_internal.h b/implot_internal.h index c0f6882c..525f6d6b 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -94,6 +94,9 @@ inline void ImFlipFlag(TSet& set, TFlag flag) { ImHasFlag(set, flag) ? set &= ~f // Linearly remaps x from [x0 x1] to [y0 y1]. template inline T ImRemap(T x, T x0, T x1, T y0, T y1) { return y0 + (x - x0) * (y1 - y0) / (x1 - x0); } +// Linear rempas x from [x0 x1] to [0 1] +template +inline T ImRemap01(T x, T x0, T x1) { return (x - x0) / (x1 - x0); } // Returns always positive modulo (assumes r != 0) inline int ImPosMod(int l, int r) { return (l % r + r) % r; } // Returns true if val is NAN or INFINITY diff --git a/implot_items.cpp b/implot_items.cpp index 3d1b5b21..c153046a 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1812,7 +1812,6 @@ void PlotHistogram(const char* label_id, const T* values, int count, int bins, b else if (density) { for (int b = 0; b < bins; ++b) bin_counts[b] = bin_counts[b] / (counted * width); - } PlotBars(label_id, &bin_centers.Data[0], &bin_counts.Data[0], bins, bar_scale*width); } @@ -1832,7 +1831,6 @@ template IMPLOT_API void PlotHistogram(const char* label_id, const doubl // PLOT HISTOGRAM 2D //----------------------------------------------------------------------------- -// Plots two dimensional, bivariate histogram as a heatmap. If density is true, the PDF is visualized. template void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range) { @@ -1898,7 +1896,6 @@ void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, } EndItem(); } - // PlotHeatmap(label_id, &bin_counts.Data[0], x_bins, y_bins, 0, max_count, NULL, range.Min(), range.Max()); } template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImS8* xs, const ImS8* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range); From 1868f26338f9228dcdac2f088ec2d5e3ddb240db Mon Sep 17 00:00:00 2001 From: epezent Date: Mon, 8 Mar 2021 10:58:27 -0600 Subject: [PATCH 13/19] colormap overhaul --- implot.cpp | 397 +++++++++++++++------------------------------- implot.h | 35 ++-- implot_demo.cpp | 9 +- implot_internal.h | 137 ++++++++++------ implot_items.cpp | 13 +- 5 files changed, 240 insertions(+), 351 deletions(-) diff --git a/implot.cpp b/implot.cpp index 321d2cbe..8323d495 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. +- 2021/03/08 (0.9) - - 2021/03/07 (0.9) - The signature of ShowColormapScale was modified to accept a ImVec2 size. - 2021/02/28 (0.9) - BeginLegendDragDropSource was changed to BeginDragDropSourceItem with a number of other drag and drop improvements. - 2021/01/18 (0.9) - The default behavior for opening context menus was change from double right-click to single right-click. ImPlotInputMap and related functions were moved @@ -131,6 +132,8 @@ ImPlotStyle::ImPlotStyle() { ImPlot::StyleColorsAuto(this); + Colormap = ImPlotColormap_Deep; + AntiAliasedLines = false; UseLocalTime = false; Use24HourClock = false; @@ -383,9 +386,34 @@ void SetCurrentContext(ImPlotContext* ctx) { GImPlot = ctx; } +#define IMPLOT_APPEND_CMAP(name) ctx->ColormapData.Append(#name, name, sizeof(name)/sizeof(ImU32)) + void Initialize(ImPlotContext* ctx) { Reset(ctx); - ctx->Colormap = GetColormap(ImPlotColormap_Default, &ctx->ColormapSize); + + const ImU32 Default[] = {4294950656, 4278190335, 4278255487, 4278255615, 4294967040, 4278232575, 4294902015, 4293012362, 4286611584, 4287411410 }; + const ImU32 Deep[] = {4289753676, 4283598045, 4285048917, 4283584196, 4289950337, 4284512403, 4291005402, 4287401100, 4285839820, 4291671396 }; + const ImU32 Dark[] = {4280031972, 4290281015, 4283084621, 4288892568, 4278222847, 4281597951, 4280833702, 4290740727, 4288256409 }; + const ImU32 Pastel[] = {4289639675, 4293119411, 4291161036, 4293184478, 4289124862, 4291624959, 4290631909, 4293712637, 4294111986 }; + const ImU32 Paired[] = {4293119554, 4290017311, 4287291314, 4281114675, 4288256763, 4280031971, 4285513725, 4278222847, 4292260554, 4288298346, 4288282623, 4280834481}; + const ImU32 Viridis[] = {4283695428, 4285867080, 4287054913, 4287455029, 4287526954, 4287402273, 4286883874, 4285579076, 4283552122, 4280737725, 4280674301 }; + const ImU32 Plasma[] = {4287039501, 4288480321, 4289200234, 4288941455, 4287638193, 4286072780, 4284638433, 4283139314, 4281771772, 4280667900, 4280416752 }; + const ImU32 Hot[] = {4278190144, 4278190208, 4278190271, 4278190335, 4278206719, 4278223103, 4278239231, 4278255615, 4283826175, 4289396735, 4294967295 }; + const ImU32 Cool[] = {4294967040, 4294960666, 4294954035, 4294947661, 4294941030, 4294934656, 4294928025, 4294921651, 4294915020, 4294908646, 4294902015 }; + const ImU32 Pink[] = {4278190154, 4282532475, 4284308894, 4285690554, 4286879686, 4287870160, 4288794330, 4289651940, 4291685869, 4293392118, 4294967295 }; + const ImU32 Jet[] = {4289331200, 4294901760, 4294923520, 4294945280, 4294967040, 4289396565, 4283826090, 4278255615, 4278233855, 4278212095, 4278190335 }; + + IMPLOT_APPEND_CMAP(Default); + IMPLOT_APPEND_CMAP(Deep); + IMPLOT_APPEND_CMAP(Dark); + IMPLOT_APPEND_CMAP(Pastel); + IMPLOT_APPEND_CMAP(Paired); + IMPLOT_APPEND_CMAP(Viridis); + IMPLOT_APPEND_CMAP(Plasma); + IMPLOT_APPEND_CMAP(Hot); + IMPLOT_APPEND_CMAP(Cool); + IMPLOT_APPEND_CMAP(Pink); + IMPLOT_APPEND_CMAP(Jet); } void Reset(ImPlotContext* ctx) { @@ -3238,257 +3266,86 @@ void PopStyleVar(int count) { // COLORMAPS //------------------------------------------------------------------------------ +ImPlotColormap AddColormap(const char* name, const ImVec4* colormap, int size) { + ImPlotContext& gp = *GImPlot; + IM_ASSERT_USER_ERROR(size > 1, "The colormap size must be greater than 1!"); + IM_ASSERT_USER_ERROR(gp.ColormapData.Lookup(name) == -1, "The colormap name has already been used!"); + return gp.ColormapData.Append(name, colormap, size); +} + +ImPlotColormap AddColormap(const char* name, const ImU32* colormap, int size) { + ImPlotContext& gp = *GImPlot; + IM_ASSERT_USER_ERROR(size > 1, "The colormap size must be greater than 1!"); + IM_ASSERT_USER_ERROR(gp.ColormapData.Lookup(name) == -1, "The colormap name has already be used!"); + return gp.ColormapData.Append(name, colormap, size); +} void PushColormap(ImPlotColormap colormap) { ImPlotContext& gp = *GImPlot; - gp.ColormapModifiers.push_back(ImPlotColormapMod(gp.Colormap, gp.ColormapSize)); - gp.Colormap = GetColormap(colormap, &gp.ColormapSize); + IM_ASSERT_USER_ERROR(colormap >= 0 && colormap < gp.ColormapData.Count, "The colormap index is invalid!"); + gp.ColormapModifiers.push_back(gp.Style.Colormap); + gp.Style.Colormap = colormap; } -void PushColormap(const ImVec4* colormap, int size) { +void PushColormap(const char* name) { ImPlotContext& gp = *GImPlot; - gp.ColormapModifiers.push_back(ImPlotColormapMod(gp.Colormap, gp.ColormapSize)); - gp.Colormap = colormap; - gp.ColormapSize = size; + PushColormap(gp.ColormapData.Lookup(name)); } void PopColormap(int count) { ImPlotContext& gp = *GImPlot; while (count > 0) { - const ImPlotColormapMod& backup = gp.ColormapModifiers.back(); - gp.Colormap = backup.Colormap; - gp.ColormapSize = backup.ColormapSize; + const ImPlotColormap& backup = gp.ColormapModifiers.back(); + gp.Style.Colormap = backup; gp.ColormapModifiers.pop_back(); count--; } } -void SetColormap(ImPlotColormap colormap, int samples) { - ImPlotContext& gp = *GImPlot; - gp.Colormap = GetColormap(colormap, &gp.ColormapSize); - if (samples > 1) { - static ImVector resampled; - resampled.resize(samples); - ResampleColormap(gp.Colormap, gp.ColormapSize, &resampled[0], samples); - SetColormap(&resampled[0], samples); - } -} - -void SetColormap(const ImVec4* colors, int size) { +int GetColormapCount() { ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(colors != NULL, "You can't set the colors to NULL!"); - IM_ASSERT_USER_ERROR(size > 0, "The number of colors must be greater than 0!"); - static ImVector user_colormap; - user_colormap.shrink(0); - user_colormap.reserve(size); - for (int i = 0; i < size; ++i) - user_colormap.push_back(colors[i]); - gp.Colormap = &user_colormap[0]; - gp.ColormapSize = size; -} - -const ImU32* GetColormap32(ImPlotColormap colormap, int* size_out) { - static const int csizes[ImPlotColormap_COUNT] = {10,10,9,9,12,11,11,11,11,11,11}; - static const ImOffsetCalculator coffs(csizes); - static ImU32 cdata32[] = { - 4294950656, 4278190335, 4278255487, 4278255615, 4294967040, 4278232575, 4294902015, 4293012362, 4286611584, 4287411410, - 4289753676, 4283598045, 4285048917, 4283584196, 4289950337, 4284512403, 4291005402, 4287401100, 4285839820, 4291671396, - 4280031972, 4290281015, 4283084621, 4288892568, 4278222847, 4281597951, 4280833702, 4290740727, 4288256409, - 4289639675, 4293119411, 4291161036, 4293184478, 4289124862, 4291624959, 4290631909, 4293712637, 4294111986, - 4293119554, 4290017311, 4287291314, 4281114675, 4288256763, 4280031971, 4285513725, 4278222847, 4292260554, 4288298346, 4288282623, 4280834481, - 4283695428, 4285867080, 4287054913, 4287455029, 4287526954, 4287402273, 4286883874, 4285579076, 4283552122, 4280737725, 4280674301, - 4287039501, 4288480321, 4289200234, 4288941455, 4287638193, 4286072780, 4284638433, 4283139314, 4281771772, 4280667900, 4280416752, - 4278190144, 4278190208, 4278190271, 4278190335, 4278206719, 4278223103, 4278239231, 4278255615, 4283826175, 4289396735, 4294967295, - 4294967040, 4294960666, 4294954035, 4294947661, 4294941030, 4294934656, 4294928025, 4294921651, 4294915020, 4294908646, 4294902015, - 4278190154, 4282532475, 4284308894, 4285690554, 4286879686, 4287870160, 4288794330, 4289651940, 4291685869, 4293392118, 4294967295, - 4289331200, 4294901760, 4294923520, 4294945280, 4294967040, 4289396565, 4283826090, 4278255615, 4278233855, 4278212095, 4278190335 - }; - *size_out = csizes[colormap]; - return &cdata32[coffs.Offsets[colormap]]; -} - -const ImVec4* GetColormap(ImPlotColormap colormap, int* size_out) { - static const int csizes[ImPlotColormap_COUNT] = {10,10,9,9,12,11,11,11,11,11,11}; - static const ImOffsetCalculator 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_Deep - ImVec4(0.298f, 0.447f, 0.690f, 1.000f), - ImVec4(0.867f, 0.518f, 0.322f, 1.000f), - ImVec4(0.333f, 0.659f, 0.408f, 1.000f), - ImVec4(0.769f, 0.306f, 0.322f, 1.000f), - ImVec4(0.506f, 0.446f, 0.702f, 1.000f), - ImVec4(0.576f, 0.471f, 0.376f, 1.000f), - ImVec4(0.855f, 0.545f, 0.765f, 1.000f), - ImVec4(0.549f, 0.549f, 0.549f, 1.000f), - ImVec4(0.800f, 0.725f, 0.455f, 1.000f), - ImVec4(0.392f, 0.710f, 0.804f, 1.000f), - // 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) - }; - *size_out = csizes[colormap]; - return &cdata[coffs.Offsets[colormap]]; + return gp.ColormapData.Count; } const char* GetColormapName(ImPlotColormap colormap) { - static const char* cmap_names[] = {"Default","Deep","Dark","Pastel","Paired","Viridis","Plasma","Hot","Cool","Pink","Jet"}; - return cmap_names[colormap]; -} - -void ResampleColormap(const ImVec4* colormap_in, int size_in, ImVec4* colormap_out, int size_out) { - for (int i = 0; i < size_out; ++i) { - float t = i * 1.0f / (size_out - 1); - colormap_out[i] = LerpColormap(colormap_in, size_in, t); - } + ImPlotContext& gp = *GImPlot; + return gp.ColormapData.GetName(colormap); } int GetColormapSize() { ImPlotContext& gp = *GImPlot; - return gp.ColormapSize; + return gp.ColormapData.GetSize(gp.Style.Colormap); } -ImVec4 GetColormapColor(int index) { +ImU32 GetColormapColorU32(int idx) { ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(index >= 0, "The Colormap index must be greater than zero!"); - return gp.Colormap[index % gp.ColormapSize]; + return gp.ColormapData.GetColor(gp.Style.Colormap, idx); } -ImVec4 LerpColormap(const ImVec4* colormap, int size, float t) { - float tc = ImClamp(t,0.0f,1.0f); - int i1 = (int)((size -1 ) * tc); - int i2 = i1 + 1; - if (i2 == size || size == 1) - return colormap[i1]; - float t1 = (float)i1 / (float)(size - 1); - float t2 = (float)i2 / (float)(size - 1); - float tr = ImRemap(t, t1, t2, 0.0f, 1.0f); - return ImLerp(colormap[i1], colormap[i2], tr); +ImVec4 GetColormapColor(int idx) { + return ImGui::ColorConvertU32ToFloat4(GetColormapColorU32(idx)); } -ImVec4 LerpColormap(float t) { +ImU32 NextColormapColorU32() { ImPlotContext& gp = *GImPlot; - return LerpColormap(gp.Colormap, gp.ColormapSize, t); + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "NextColormapColor() needs to be called between BeginPlot() and EndPlot()!"); + int idx = gp.CurrentPlot->ColormapIdx % gp.ColormapData.GetSize(gp.Style.Colormap); + ImU32 col = gp.ColormapData.GetColor(gp.Style.Colormap, idx); + gp.CurrentPlot->ColormapIdx++; + return col; } ImVec4 NextColormapColor() { + return ImGui::ColorConvertU32ToFloat4(NextColormapColorU32()); +} + +ImU32 LerpColormapU32(float t) { ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "NextColormapColor() needs to be called between BeginPlot() and EndPlot()!"); - ImVec4 col = gp.Colormap[gp.CurrentPlot->ColormapIdx % gp.ColormapSize]; - gp.CurrentPlot->ColormapIdx++; - return col; + return gp.ColormapData.Lerp(gp.Style.Colormap,t); +} + +ImVec4 LerpColormap(float t) { + return ImGui::ColorConvertU32ToFloat4(LerpColormapU32(t)); } void ShowColormapScale(double scale_min, double scale_max, const ImVec2& size) { @@ -3534,8 +3391,8 @@ void ShowColormapScale(double scale_min, double scale_max, const ImVec2& size) { float h_step = (frame_size.y - 2 * gp.Style.PlotPadding.y) / (num_cols - 1); 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 - i - 1)); - ImU32 col2 = ImGui::GetColorU32(GetColormapColor(num_cols - i - 2)); + ImU32 col1 = GetColormapColorU32(num_cols - i - 1); + ImU32 col2 = GetColormapColorU32(num_cols - i - 2); DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col1, col2, col2); } const ImU32 col_tick = GetStyleColorU32(ImPlotCol_TitleText); @@ -3585,14 +3442,13 @@ bool ShowStyleSelector(const char* label) } bool ShowColormapSelector(const char* label) { + ImPlotContext& gp = *GImPlot; bool set = false; - static const char* map = ImPlot::GetColormapName(ImPlotColormap_Default); - if (ImGui::BeginCombo(label, map)) { - for (int i = 0; i < ImPlotColormap_COUNT; ++i) { - const char* name = GetColormapName(i); - if (ImGui::Selectable(name, map == name)) { - map = name; - ImPlot::SetColormap(i); + if (ImGui::BeginCombo(label, gp.ColormapData.GetName(gp.Style.Colormap))) { + for (int i = 0; i < gp.ColormapData.Count; ++i) { + const char* name = gp.ColormapData.GetName(i); + if (ImGui::Selectable(name, gp.Style.Colormap == i)) { + gp.Style.Colormap = i; ImPlot::BustItemCache(); set = true; } @@ -3750,92 +3606,89 @@ void ShowStyleEditor(ImPlotStyle* ref) { ImGui::LogToClipboard(); else ImGui::LogToTTY(); - ImGui::LogText("static const ImVec4 colormap[%d] = {\n", gp.ColormapSize); - for (int i = 0; i < gp.ColormapSize; ++i) { - const ImVec4& col = gp.Colormap[i]; - ImGui::LogText(" ImVec4(%.2ff, %.2ff, %.2ff, %.2ff)%s\n", col.x, col.y, col.z, col.w, i == gp.ColormapSize - 1 ? "" : ","); + int size = GetColormapSize(); + const char* name = GetColormapName(gp.Style.Colormap); + ImGui::LogText("static const ImU32 %s_Data[%d] = {\n", name, size); + for (int i = 0; i < size; ++i) { + ImU32 col = GetColormapColorU32(i); + ImGui::LogText(" %u%s\n", col, i == size - 1 ? "" : ","); } - ImGui::LogText("};"); + ImGui::LogText("};\nImPlotColormap %s = ImPlot::AddColormap(\"%s\", %s_Data, %d);", name, name, name, size); ImGui::LogFinish(); } ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); - ImGui::SameLine(); HelpMarker("Export code for selected Colormap\n(built in or custom)."); + ImGui::SameLine(); HelpMarker("Export code for current Colormap."); + + + // built-in/added ImGui::Separator(); - static ImVector custom; - static bool custom_set = false; - for (int i = 0; i < ImPlotColormap_COUNT; ++i) { + for (int i = 0; i < gp.ColormapData.Count; ++i) { ImGui::PushID(i); - int size; - const ImVec4* cmap = GetColormap(i, &size); - bool selected = cmap == gp.Colormap; - if (selected) { - custom_set = false; - } + int size = gp.ColormapData.GetSize(i); + bool selected = i == gp.Style.Colormap; + if (!selected) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f); if (ImGui::Button(GetColormapName(i), ImVec2(75,0))) { - SetColormap(i); + gp.Style.Colormap = i; BustItemCache(); - custom_set = false; } if (!selected) ImGui::PopStyleVar(); ImGui::SameLine(); + ImGui::BeginGroup(); for (int c = 0; c < size; ++c) { ImGui::PushID(c); - ImGui::ColorButton("",cmap[c]); - if (c != size -1) + ImVec4 col4 = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetColor(i,c)); + if (ImGui::ColorEdit4("",&col4.x,ImGuiColorEditFlags_NoInputs)) { + ImU32 col32 = ImGui::ColorConvertFloat4ToU32(col4); + gp.ColormapData.SetColor(i,c,col32); + BustItemCache(); + } + if ((c + 1) % 12 != 0 && c != size -1) ImGui::SameLine(); ImGui::PopID(); } + ImGui::EndGroup(); ImGui::PopID(); } + + + static ImVector custom; if (custom.Size == 0) { custom.push_back(ImVec4(1,1,1,1)); custom.push_back(ImVec4(0.5f,0.5f,0.5f,1)); - } + } ImGui::Separator(); ImGui::BeginGroup(); - bool custom_set_now = custom_set; - if (!custom_set_now) - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f); - if (ImGui::Button("Custom", ImVec2(75, 0))) { - SetColormap(&custom[0], custom.Size); - BustItemCache(); - custom_set = true; - } - if (!custom_set_now) - ImGui::PopStyleVar(); - if (ImGui::Button("+", ImVec2((75 - ImGui::GetStyle().ItemSpacing.x)/2,0))) { - custom.push_back(ImVec4(0,0,0,1)); - if (custom_set) { - SetColormap(&custom[0], custom.Size); - BustItemCache(); - } - } + static char name[16] = "MyColormap"; + ImGui::SetNextItemWidth(75); + ImGui::InputText("##Name",name,16,ImGuiInputTextFlags_CharsNoBlank); + + if (ImGui::Button("+", ImVec2((75 - ImGui::GetStyle().ItemSpacing.x)/2,0))) + custom.push_back(ImVec4(0,0,0,1)); ImGui::SameLine(); - if (ImGui::Button("-", ImVec2((75 - ImGui::GetStyle().ItemSpacing.x)/2,0)) && custom.Size > 1) { - custom.pop_back(); - if (custom_set) { - SetColormap(&custom[0], custom.Size); - BustItemCache(); - } - } + if (ImGui::Button("-", ImVec2((75 - ImGui::GetStyle().ItemSpacing.x)/2,0)) && custom.Size > 2) + custom.pop_back(); + if (ImGui::Button("Add", ImVec2(75, 0))) + AddColormap(name,custom.Data,custom.Size); + ImGui::EndGroup(); ImGui::SameLine(); ImGui::BeginGroup(); for (int c = 0; c < custom.Size; ++c) { ImGui::PushID(c); - if (ImGui::ColorEdit4("##Col1", &custom[c].x, ImGuiColorEditFlags_NoInputs) && custom_set) { - SetColormap(&custom[0], custom.Size); - BustItemCache(); + if (ImGui::ColorEdit4("##Col1", &custom[c].x, ImGuiColorEditFlags_NoInputs)) { + } if ((c + 1) % 12 != 0) ImGui::SameLine(); ImGui::PopID(); } ImGui::EndGroup(); + + ImGui::EndTabItem(); } ImGui::EndTabBar(); diff --git a/implot.h b/implot.h index af429fc7..b8e278a6 100644 --- a/implot.h +++ b/implot.h @@ -192,8 +192,7 @@ enum ImPlotColormap_ { ImPlotColormap_Hot = 7, // a.k.a. matplotlib/MATLAB "hot" (n=11) ImPlotColormap_Cool = 8, // a.k.a. matplotlib/MATLAB "cool" (n=11) ImPlotColormap_Pink = 9, // a.k.a. matplotlib/MATLAB "pink" (n=11) - ImPlotColormap_Jet = 10, // a.k.a. MATLAB "jet" (n=11) - ImPlotColormap_COUNT + ImPlotColormap_Jet = 10 // a.k.a. MATLAB "jet" (n=11) }; // Used to position items on a plot (e.g. legends, labels, etc.) @@ -295,8 +294,10 @@ struct ImPlotStyle { ImVec2 FitPadding; // = 0,0 additional fit padding as a percentage of the fit extents (e.g. ImVec2(0.1f,0.1f) adds 10% to the fit extents of X and Y) ImVec2 PlotDefaultSize; // = 400,300 default size used when ImVec2(0,0) is passed to BeginPlot ImVec2 PlotMinSize; // = 300,225 minimum size plot frame can be when shrunk - // colors + // style colors ImVec4 Colors[ImPlotCol_COUNT]; // array of plot specific colors + // colormap + ImPlotColormap Colormap; // index of current colormap // settings/flags bool AntiAliasedLines; // = false, enable global anti-aliasing on plot lines (overrides ImPlotFlags_AntiAliased) bool UseLocalTime; // = false, axis labels will be formatted for your timezone when ImPlotAxisFlag_Time is enabled @@ -664,33 +665,35 @@ IMPLOT_API const char* GetMarkerName(ImPlotMarker idx); // 2) Pushed an item style color using PushStyleColor(). // 3) Set the next item style with a SetNextXStyle function. -// Temporarily switch to one of the built-in colormaps. -IMPLOT_API void PushColormap(ImPlotColormap colormap); -// Temporarily switch to your custom colormap. The pointer data must persist until the matching call to PopColormap! -IMPLOT_API void PushColormap(const ImVec4* colormap, int size); +// Add a new colormap. The colormap can be used by pushing either the returned index or the string name with PushColormap. +// The colormap name must be unique and the size must be greater than 1. +IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImVec4* colormap, int size); +IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImU32* colormap, int size); + +// Temporarily switch to one of the built-in (i.e. ImPlotColormap_XXX) or user-added colormaps (i.e. a return value of AddColormap). Don't forget to call PopColormap! +IMPLOT_API void PushColormap(ImPlotColormap cmap); +// Push a colormap by string name. Use the string you provided to AddColormap, or use built-in names such as "Default", "Deep", etc. Don't forget to call PopColormap! +IMPLOT_API void PushColormap(const char* name); // Undo temporary colormap modification. IMPLOT_API void PopColormap(int count = 1); -// Permanently sets a custom colormap. The colors will be copied to internal memory. Typically used on startup. Prefer PushColormap instead of calling this each frame. -IMPLOT_API void SetColormap(const ImVec4* colormap, int size); -// Permanently switch to one of the built-in colormaps. If samples is greater than 1, the map will be linearly resampled. Typically used on startup. Don't call this each frame. -IMPLOT_API void SetColormap(ImPlotColormap colormap, int samples = 0); - // Returns the size of the current colormap. IMPLOT_API int GetColormapSize(); // Returns a color from the Color map given an index >= 0 (modulo will be performed). -IMPLOT_API ImVec4 GetColormapColor(int index); +IMPLOT_API ImVec4 GetColormapColor(int idx); // Linearly interpolates a color from the current colormap given t between 0 and 1. IMPLOT_API ImVec4 LerpColormap(float t); // Returns the next unused colormap color and advances the colormap. Can be used to skip colors if desired. IMPLOT_API ImVec4 NextColormapColor(); +// Returns the number of available colormaps. +IMPLOT_API int GetColormapCount(); +// Returns a null terminated string name for a built-in colormap. +IMPLOT_API const char* GetColormapName(ImPlotColormap cmap); + // Renders a vertical color scale using the current color map. Call this before or after Begin/EndPlot. IMPLOT_API void ShowColormapScale(double scale_min, double scale_max, const ImVec2& size = ImVec2(0,0)); -// Returns a null terminated string name for a built-in colormap. -IMPLOT_API const char* GetColormapName(ImPlotColormap colormap); - //----------------------------------------------------------------------------- // Miscellaneous //----------------------------------------------------------------------------- diff --git a/implot_demo.cpp b/implot_demo.cpp index 6fed362d..c49b94d3 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -472,9 +472,10 @@ void ShowDemoWindow(bool* p_open) { ImPlot::EndPlot(); } + srand(0); static NormalDistribution<10000> dist1(1, 2); static NormalDistribution<10000> dist2(1, 1); - ImPlot::PushColormap(ImPlotColormap_Jet); + ImPlot::PushColormap("Cool"); if (ImPlot::BeginPlot("Hist2D",0,0,ImVec2(-1,0),0,ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit)) { ImPlot::PlotHistogram2D("Hist2D",dist1.Data,dist2.Data,10000,100,100,false,ImPlotLimits(-6,6,-6,6)); ImPlot::EndPlot(); @@ -589,7 +590,7 @@ void ShowDemoWindow(bool* p_open) { static ImPlotColormap map = ImPlotColormap_Viridis; if (ImGui::Button("Change Colormap",ImVec2(225,0))) - map = (map + 1) % ImPlotColormap_COUNT; + map = (map + 1) % ImPlot::GetColormapCount(); ImGui::SameLine(); ImGui::LabelText("##Colormap Index", "%s", ImPlot::GetColormapName(map)); @@ -606,7 +607,6 @@ void ShowDemoWindow(bool* p_open) { } ImGui::SameLine(); ImPlot::ShowColormapScale(scale_min, scale_max, ImVec2(60,225)); - ImPlot::PopColormap(); ImGui::SameLine(); @@ -615,8 +615,6 @@ void ShowDemoWindow(bool* p_open) { for (int i = 0; i < 500*500; ++i) values2[i] = RandomRange(0.0,1.0); - static ImVec4 gray[2] = {ImVec4(0,0,0,1), ImVec4(1,1,1,1)}; - ImPlot::PushColormap(gray, 2); ImPlot::SetNextPlotLimits(-1,1,-1,1); if (ImPlot::BeginPlot("##Heatmap2",NULL,NULL,ImVec2(225,225),0,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations)) { ImPlot::PlotHeatmap("heat1",values2,500,500,0,1,NULL); @@ -624,6 +622,7 @@ void ShowDemoWindow(bool* p_open) { ImPlot::EndPlot(); } ImPlot::PopColormap(); + } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Images")) { diff --git a/implot_internal.h b/implot_internal.h index 29e9e844..9dc0b1cd 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -148,25 +148,29 @@ inline double ImStdDev(const T* values, int count) { } // Mix color a and b by factor t [0 256] inline ImU32 ImMixColor32(ImU32 a, ImU32 b, ImU32 t) { - ImU32 af = 256 - t; +#ifdef IMPLOT_MIX64 + ImU32 af = 256-t; ImU32 bf = t; ImU64 al = (a & 0x00ff00ff) | (((ImU64)(a & 0xff00ff00)) << 24); ImU64 bl = (b & 0x00ff00ff) | (((ImU64)(b & 0xff00ff00)) << 24); ImU64 mix = (al * af + bl * bf); - ImU32 result = ((mix >> 32) & 0xff00ff00) | ((mix & 0xff00ff00) >> 8); - return result; + return ((mix >> 32) & 0xff00ff00) | ((mix & 0xff00ff00) >> 8); +#else + ImU32 af = 256-t; + ImU32 bf = t; + ImU32 al = (a & 0x00ff00ff); + ImU32 ah = (a & 0xff00ff00) >> 8; + ImU32 bl = (b & 0x00ff00ff); + ImU32 bh = (b & 0xff00ff00) >> 8; + ImU32 ml = (al * af + bl * bf); + ImU32 mh = (ah * af + bh * bf); + return (mh & 0xff00ff00) | ((ml & 0xff00ff00) >> 8); +#endif +} +// Set alpha channel of 32-bit color from float in range [0 1] +inline ImU32 ImAlphaColor32(ImU32 col, float alpha) { + return col & ~((ImU32)((1.0f-alpha)*255)< -struct ImOffsetCalculator { - ImOffsetCalculator(const int* sizes) { - Offsets[0] = 0; - for (int i = 1; i < Count; ++i) - Offsets[i] = Offsets[i-1] + sizes[i-1]; - } - int Offsets[Count]; -}; // Character buffer writer helper (FIXME: Can't we replace this with ImGuiTextBuffer?) struct ImBufferWriter @@ -315,19 +319,72 @@ static inline bool operator<=(const ImPlotTime& lhs, const ImPlotTime& rhs) static inline bool operator>=(const ImPlotTime& lhs, const ImPlotTime& rhs) { return lhs > rhs || lhs == rhs; } -// Storage for colormap modifiers -struct ImPlotColormapMod { - ImPlotColormapMod(const ImVec4* colormap, int colormap_size) { - Colormap = colormap; - ColormapSize = colormap_size; +// Colormap data storage +struct ImPlotColormapData { + ImVector Data; + ImVector DataSizes; + ImVector DataOffsets; + ImGuiTextBuffer Text; + ImVector TextOffsets; + ImGuiStorage Map; + int Count; + + ImPlotColormapData() { Count = 0; } + + int Append(const char* name, const ImU32* data, int size) { + DataOffsets.push_back(Data.size()); + DataSizes.push_back(size); + Data.reserve(Data.size()+size); + for (int i = 0; i < size; ++i) + Data.push_back(data[i]); + TextOffsets.push_back(Text.size()); + Text.append(name, name + strlen(name) + 1); + ImGuiID key = ImHashStr(name); + int idx = Count; + Map.SetInt(key,idx); + Count++; + return idx; + } + + ImPlotColormap Append(const char* name, const ImVec4* data, int size) { + DataOffsets.push_back(Data.size()); + DataSizes.push_back(size); + Data.reserve(Data.size()+size); + for (int i = 0; i < size; ++i) + Data.push_back(ImGui::ColorConvertFloat4ToU32(data[i])); + TextOffsets.push_back(Text.size()); + Text.append(name, name + strlen(name) + 1); + ImGuiID key = ImHashStr(name); + int idx = Count; + Map.SetInt(key,idx); + Count++; + return idx; + } + + inline const ImU32* GetData(ImPlotColormap cmap) { return &Data[DataOffsets[cmap]]; } + inline int GetSize(ImPlotColormap cmap) { return DataSizes[cmap]; } + inline const char* GetName(ImPlotColormap cmap) { return Text.Buf.Data + TextOffsets[cmap]; } + inline ImU32 GetColor(ImPlotColormap cmap, int idx) { return Data[DataOffsets[cmap]+idx]; } + inline void SetColor(ImPlotColormap cmap, int idx, ImU32 value) { Data[DataOffsets[cmap]+idx] = value; } + inline ImPlotColormap Lookup(const char* name) { ImGuiID key = ImHashStr(name); return Map.GetInt(key,-1); } + + inline ImU32 Lerp(ImPlotColormap cmap, float t) { + int size = GetSize(cmap); + int i1 = (int)((size -1 ) * t); + int i2 = i1 + 1; + if (i2 == size || size == 1) + return GetColor(cmap, i1); + float den = 1.0f / (size - 1); + float t1 = i1 * den; + float t2 = i2 * den; + float tr = ImRemap01(t, t1, t2); + return ImMixColor32(GetColor(cmap, i1), GetColor(cmap, i2), (ImU32)(tr*256)); } - const ImVec4* Colormap; - int ColormapSize; + }; // ImPlotPoint with positive/negative error values -struct ImPlotPointError -{ +struct ImPlotPointError { double X, Y, Neg, Pos; ImPlotPointError(double x, double y, double neg, double pos) { X = x; Y = y; Neg = neg; Pos = pos; @@ -750,9 +807,8 @@ struct ImPlotContext { ImPlotStyle Style; ImVector ColorModifiers; ImVector StyleModifiers; - const ImVec4* Colormap; - int ColormapSize; - ImVector ColormapModifiers; + ImPlotColormapData ColormapData; + ImVector ColormapModifiers; // Time tm Tm; @@ -911,28 +967,6 @@ IMPLOT_API ImVec4 GetAutoColor(ImPlotCol idx); inline ImVec4 GetStyleColorVec4(ImPlotCol idx) { return IsColorAuto(idx) ? GetAutoColor(idx) : GImPlot->Style.Colors[idx]; } inline ImU32 GetStyleColorU32(ImPlotCol idx) { return ImGui::ColorConvertFloat4ToU32(GetStyleColorVec4(idx)); } -// Get built-in colormap data and size -IMPLOT_API const ImVec4* GetColormap(ImPlotColormap colormap, int* size_out); -// Linearly interpolates a color from the current colormap given t between 0 and 1. -IMPLOT_API ImVec4 LerpColormap(const ImVec4* colormap, int size, float t); -// Resamples a colormap. #size_out must be greater than 1. -IMPLOT_API void ResampleColormap(const ImVec4* colormap_in, int size_in, ImVec4* colormap_out, int size_out); - -IMPLOT_API const ImU32* GetColormap32(ImPlotColormap colormap, int* size_out); - -// Linearly interpolates a color from the current colormap given t between 0 and 1 (t must be clamped!) -inline ImU32 LerpColormap32(const ImU32* colormap, int size, float t) { - int i1 = (int)((size -1 ) * t); - int i2 = i1 + 1; - if (i2 == size || size == 1) - return colormap[i1]; - float den = 1.0f / (size - 1); - float t1 = i1 * den; - float t2 = i2 * den; - float tr = ImRemap01(t, t1, t2); - return ImMixColor32(colormap[i1], colormap[i2], (ImU32)(tr*256)); -} - // Draws vertical text. The position is the bottom left of the text rect. IMPLOT_API void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char* text_begin, const char* text_end = NULL); // Calculates the size of vertical text @@ -949,6 +983,13 @@ inline ImVec2 ClampLabelPos(ImVec2 pos, const ImVec2& size, const ImVec2& Min, c return pos; } +// Returns a color from the Color map given an index >= 0 (modulo will be performed). +IMPLOT_API ImU32 GetColormapColorU32(int idx); +// Returns the next unused colormap color and advances the colormap. Can be used to skip colors if desired. +IMPLOT_API ImU32 NextColormapColorU32(); +// Linearly interpolates a color from the current colormap given t between 0 and 1. +IMPLOT_API ImU32 LerpColormapU32(float t); + //----------------------------------------------------------------------------- // [SECTION] Math and Misc Utils //----------------------------------------------------------------------------- diff --git a/implot_items.cpp b/implot_items.cpp index cd809080..91b4d908 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1780,7 +1780,6 @@ struct RectRenderer { template struct GetterHeatmap { GetterHeatmap(const T* values, int rows, int cols, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : - Colormap(GetColormap32(ImPlotColormap_Jet,&ColormapSize)), Values(values), Count(rows*cols), Rows(rows), @@ -1799,21 +1798,15 @@ struct GetterHeatmap { const int r = idx / Cols; const int c = idx % Cols; const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height)); - const double t = ImRemap01((double)Values[idx], ScaleMin, ScaleMax); - // ImVec4 color = LerpColormap((float)t); - // color.w *= GImPlot->Style.FillAlpha; + const float t = (float)ImRemap01((double)Values[idx], ScaleMin, ScaleMax); RectInfo rect; rect.Min.x = p.x - HalfSize.x; rect.Min.y = p.y - HalfSize.y; rect.Max.x = p.x + HalfSize.x; rect.Max.y = p.y + HalfSize.y; - // rect.Color = ImGui::GetColorU32(color); - rect.Color = LerpColormap32(Colormap, ColormapSize, (float)t); + rect.Color = GImPlot->ColormapData.Lerp(GImPlot->Style.Colormap,t); return rect; } - - int ColormapSize; - const ImU32* Colormap; const T* const Values; const int Count, Rows, Cols; const double ScaleMin, ScaleMax, Width, Height, XRef, YRef, YDir; @@ -1826,7 +1819,7 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value if (scale_min == scale_max) { ImVec2 a = transformer(bounds_min); ImVec2 b = transformer(bounds_max); - ImU32 col = ImGui::GetColorU32(LerpColormap(0)); + ImU32 col = GetColormapColorU32(0); DrawList.AddRectFilled(a, b, col); return; } From 443e8c5964d6fa57acf169f53be8304ad9b33d1b Mon Sep 17 00:00:00 2001 From: epezent Date: Tue, 9 Mar 2021 09:21:36 -0600 Subject: [PATCH 14/19] tidying up histograms --- implot.cpp | 37 ++++++++----- implot.h | 47 ++++++++-------- implot_demo.cpp | 131 ++++++++++++++++++++++++++------------------ implot_internal.h | 30 +++++++++- implot_items.cpp | 137 ++++++++++++++++++++++++---------------------- 5 files changed, 226 insertions(+), 156 deletions(-) diff --git a/implot.cpp b/implot.cpp index 8323d495..710e0006 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. -- 2021/03/08 (0.9) - +- 2021/03/08 (0.9) - SetColormap and PushColormap(ImVec4*) were removed. Use AddColormap for custom colormap support. - 2021/03/07 (0.9) - The signature of ShowColormapScale was modified to accept a ImVec2 size. - 2021/02/28 (0.9) - BeginLegendDragDropSource was changed to BeginDragDropSourceItem with a number of other drag and drop improvements. - 2021/01/18 (0.9) - The default behavior for opening context menus was change from double right-click to single right-click. ImPlotInputMap and related functions were moved @@ -1733,21 +1733,25 @@ bool BeginPlot(const char* title, const char* x_label, const char* y1_label, con UpdateTransformCache(); ImVec2 select_size = plot.SelectStart - IO.MousePos; if (!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect)) { - ImPlotPoint p1 = PixelsToPlot(plot.SelectStart); - ImPlotPoint p2 = PixelsToPlot(IO.MousePos); const bool x_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.HorizontalMod) && ImFabs(select_size.x) > 2; const bool y_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.VerticalMod) && ImFabs(select_size.y) > 2; - if (!plot.XAxis.IsLockedMin() && x_can_change) - plot.XAxis.SetMin(ImMin(p1.x, p2.x)); - if (!plot.XAxis.IsLockedMax() && x_can_change) - plot.XAxis.SetMax(ImMax(p1.x, p2.x)); + if (!plot.XAxis.IsInputLocked()) { + ImPlotPoint p1 = PixelsToPlot(plot.SelectStart); + ImPlotPoint p2 = PixelsToPlot(IO.MousePos); + if (!plot.XAxis.IsLockedMin() && x_can_change) + plot.XAxis.SetMin(ImMin(p1.x, p2.x)); + if (!plot.XAxis.IsLockedMax() && x_can_change) + plot.XAxis.SetMax(ImMax(p1.x, p2.x)); + } for (int i = 0; i < IMPLOT_Y_AXES; i++) { - p1 = PixelsToPlot(plot.SelectStart, i); - p2 = PixelsToPlot(IO.MousePos, i); - if (!plot.YAxis[i].IsLockedMin() && y_can_change) - plot.YAxis[i].SetMin(ImMin(p1.y, p2.y)); - if (!plot.YAxis[i].IsLockedMax() && y_can_change) - plot.YAxis[i].SetMax(ImMax(p1.y, p2.y)); + if (!plot.YAxis->IsInputLocked()) { + ImPlotPoint p1 = PixelsToPlot(plot.SelectStart, i); + ImPlotPoint p2 = PixelsToPlot(IO.MousePos, i); + if (!plot.YAxis[i].IsLockedMin() && y_can_change) + plot.YAxis[i].SetMin(ImMin(p1.y, p2.y)); + if (!plot.YAxis[i].IsLockedMax() && y_can_change) + plot.YAxis[i].SetMax(ImMax(p1.y, p2.y)); + } } if (x_can_change || y_can_change || (ImHasFlag(IO.KeyMods,gp.InputMap.HorizontalMod) && ImHasFlag(IO.KeyMods,gp.InputMap.VerticalMod))) plot.ContextLocked = gp.InputMap.BoxSelectButton == gp.InputMap.ContextMenuButton; @@ -3755,6 +3759,11 @@ void ShowMetricsWindow(bool* p_popen) { ImGui::Separator(); int n_plots = gp.Plots.GetSize(); if (ImGui::TreeNode("Tools")) { + if (ImGui::Button("Bust Plot Cache")) + BustPlotCache(); + ImGui::SameLine(); + if (ImGui::Button("Bust Item Cache")) + BustItemCache(); ImGui::Checkbox("Show Plot Rects", &show_plot_rects); ImGui::Checkbox("Show Axes Rects", &show_axes_rects); ImGui::TreePop(); @@ -3775,7 +3784,7 @@ void ShowMetricsWindow(bool* p_popen) { ImGui::Bullet(); ImGui::ColorEdit4("Color",&item->Color.x, ImGuiColorEditFlags_NoInputs); ImGui::Bullet(); ImGui::Text("NameOffset: %d",item->NameOffset); ImGui::Bullet(); ImGui::Text("Name: %s", item->NameOffset != -1 ? plot->LegendData.Labels.Buf.Data + item->NameOffset : "N/A"); - ImGui::Bullet(); ImGui::Value("Hovered: %s",item->LegendHovered ? "true" : "false"); + ImGui::Bullet(); ImGui::Text("Hovered: %s",item->LegendHovered ? "true" : "false"); ImGui::TreePop(); } ImGui::PopID(); diff --git a/implot.h b/implot.h index b8e278a6..e8409898 100644 --- a/implot.h +++ b/implot.h @@ -61,7 +61,7 @@ typedef int ImPlotColormap; // -> enum ImPlotColormap_ typedef int ImPlotLocation; // -> enum ImPlotLocation_ typedef int ImPlotOrientation; // -> enum ImPlotOrientation_ typedef int ImPlotYAxis; // -> enum ImPlotYAxis_; -typedef int ImPlotBinMethod; // -> enum ImPlotBinMethod_ +typedef int ImPlotBin; // -> enum ImPlotBin_ // Options for plots. enum ImPlotFlags_ { @@ -222,11 +222,11 @@ enum ImPlotYAxis_ { }; // Enums for different automatic histogram binning methods (k = bin count or w = bin width) -enum ImPlotBinMethod_ { - ImPlotBinMethod_Sqrt = -1, // k = sqrt(n) - ImPlotBinMethod_Sturges = -2, // k = 1 + log2(n) - ImPlotBinMethod_Rice = -3, // k = 2 * cbrt(n) - ImPlotBinMethod_Scott = -4, // w = 3.49 * sigma / cbrt(n) +enum ImPlotBin_ { + ImPlotBin_Sqrt = -1, // k = sqrt(n) + ImPlotBin_Sturges = -2, // k = 1 + log2(n) + ImPlotBin_Rice = -3, // k = 2 * cbrt(n) + ImPlotBin_Scott = -4, // w = 3.49 * sigma / cbrt(n) }; // Double precision version of ImVec2 used by ImPlot. Extensible by end users. @@ -442,13 +442,15 @@ template IMPLOT_API void PlotPieChart(const char* const label_ids[] // Plots a 2D heatmap chart. Values are expected to be in row-major order. #label_fmt can be set to NULL for no labels. template IMPLOT_API void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1)); -// Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBinMethod. If #cumulative is true, each bin contains its count plus the counts of all previous bins. If #density is true, the PDF is visualized. -// If both are true, the CDF is visualized. If #range is left unspecified, the min/max of #values will be used as the range. Values outside of range are not binned and are ignored in the total count for #density=true histograms. -template IMPLOT_API void PlotHistogram(const char* label_id, const T* values, int count, int bins=ImPlotBinMethod_Sturges, bool cumulative=false, bool density=false, ImPlotRange range=ImPlotRange(), double bar_scale=1.0); +// Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #cumulative is true, each bin contains its count plus the counts of all previous bins. +// If #density is true, the PDF is visualized. If both are true, the CDF is visualized. If #range is left unspecified, the min/max of #values will be used as the range. +// If #range is specified, outlier values outside of the range are not binned. However, outliers still count toward normalizing and cumulative counts unless #outliers is false. The largest bin count or density is returned. +template IMPLOT_API double PlotHistogram(const char* label_id, const T* values, int count, int bins=ImPlotBin_Sturges, bool cumulative=false, bool density=false, ImPlotRange range=ImPlotRange(), bool outliers=true, double bar_scale=1.0); -// Plots two dimensional, bivariate histogram as a heatmap. #x_bins and #y_bins can be a positive integer or an ImPlotBinMethod. If #density is true, the PDF is visualized. If #range is left unspecified, -// the min/max of #xs an #ys will be used as the ranges. Values outside of range are not binned and are ignored in the total count for #density=true histograms. -template IMPLOT_API void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins=ImPlotBinMethod_Sturges, int y_bins=ImPlotBinMethod_Sturges, bool density=false, ImPlotLimits range=ImPlotLimits()); +// Plots two dimensional, bivariate histogram as a heatmap. #x_bins and #y_bins can be a positive integer or an ImPlotBin. If #density is true, the PDF is visualized. +// If #range is left unspecified, the min/max of #xs an #ys will be used as the ranges. If #range is specified, outlier values outside of range are not binned. +// However, outliers still count toward the normalizing count for density plots unless #outliers is false. The largest bin count or density is returned. +template IMPLOT_API double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins=ImPlotBin_Sturges, int y_bins=ImPlotBin_Sturges, bool density=false, ImPlotLimits range=ImPlotLimits(), bool outliers=true); // Plots digital data. Digital plots do not respond to y drag or zoom, and are always referenced to the bottom of the plot. template IMPLOT_API void PlotDigital(const char* label_id, const T* xs, const T* ys, int count, int offset=0, int stride=sizeof(T)); @@ -656,25 +658,26 @@ IMPLOT_API const char* GetMarkerName(ImPlotMarker idx); // Colormaps //----------------------------------------------------------------------------- -// Item styling is based on Colormaps when the relevant ImPlotCol_ is set to -// IMPLOT_AUTO_COL (default). Several built in colormaps are available and can be -// toggled in the demo. You can push/pop or set your own colormaps as well. +// Item styling is based on colormaps when the relevant ImPlotCol_ is set to +// IMPLOT_AUTO_COL (default). Several built-in colormaps are available. You can +// add and then push/pop or set your own colormaps as well. To permanently set +// a colormap, modify the Colormap member of your ImPlotStyle. -// The Colormap data will be ignored and a custom color will be used if you have done one of the following: -// 1) Modified an item style color in your ImPlotStyle to anything other than IMPLOT_AUTO_COL. +// Colormap data will be ignored and a custom color will be used if you have done one of the following: +// 1) Modified an item style color in your ImPlotStyle to anything other than IMPLOT_AUTO_COL. // 2) Pushed an item style color using PushStyleColor(). // 3) Set the next item style with a SetNextXStyle function. // Add a new colormap. The colormap can be used by pushing either the returned index or the string name with PushColormap. -// The colormap name must be unique and the size must be greater than 1. +// The colormap name must be unique and the size must be greater than 1. IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImVec4* colormap, int size); IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImU32* colormap, int size); // Temporarily switch to one of the built-in (i.e. ImPlotColormap_XXX) or user-added colormaps (i.e. a return value of AddColormap). Don't forget to call PopColormap! IMPLOT_API void PushColormap(ImPlotColormap cmap); -// Push a colormap by string name. Use the string you provided to AddColormap, or use built-in names such as "Default", "Deep", etc. Don't forget to call PopColormap! +// Push a colormap by string name. Use the string you provided to AddColormap, or use built-in names such as "Default", "Deep", "Jet", etc. Don't forget to call PopColormap! IMPLOT_API void PushColormap(const char* name); -// Undo temporary colormap modification. +// Undo temporary colormap modification(s). IMPLOT_API void PopColormap(int count = 1); // Returns the size of the current colormap. @@ -688,7 +691,7 @@ IMPLOT_API ImVec4 NextColormapColor(); // Returns the number of available colormaps. IMPLOT_API int GetColormapCount(); -// Returns a null terminated string name for a built-in colormap. +// Returns a null terminated string name for a colormap by index (built-in or user-added). IMPLOT_API const char* GetColormapName(ImPlotColormap cmap); // Renders a vertical color scale using the current color map. Call this before or after Begin/EndPlot. @@ -702,7 +705,7 @@ IMPLOT_API void ShowColormapScale(double scale_min, double scale_max, const ImVe IMPLOT_API void ItemIcon(const ImVec4& col); IMPLOT_API void ItemIcon(ImU32 col); -// Get the plot draw list for rendering to the current plot area. +// Get the plot draw list for custom rendering to the current plot area. IMPLOT_API ImDrawList* GetPlotDrawList(); // Push clip rect for rendering to current plot area. IMPLOT_API void PushPlotClipRect(); diff --git a/implot_demo.cpp b/implot_demo.cpp index c49b94d3..b3b1fdd5 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -431,57 +431,7 @@ void ShowDemoWindow(bool* p_open) { } ImPlot::EndPlot(); } - } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Histograms")) { - static int bins = ImPlotBinMethod_Sturges; - static bool cumulative = false; - static bool density = true; - static double mu = 5; - static double sigma = 2; - static NormalDistribution<10000> dist(mu, sigma); - static double x[100]; - static double y[100]; - if (density) { - for (int i = 0; i < 100; ++i) { - x[i] = -3 + 16 * (double)i/99.0; - y[i] = exp( - (x[i]-mu)*(x[i]-mu) / (2*sigma*sigma)) / (sigma * sqrt(2*3.141592653589793238)); - } - if (cumulative) { - for (int i = 1; i < 100; ++i) - y[i] += y[i-1]; - for (int i = 0; i < 100; ++i) - y[i] /= y[99]; - } - } - ImGui::SetNextItemWidth(200); - ImGui::SliderInt("Bins", &bins, -4, 100); - ImGui::SameLine(); - ImGui::Checkbox("Density", &density); - ImGui::SameLine(); - ImGui::Checkbox("Cumulative", &cumulative); - static float rmin = 0; - static float rmax = 0; - ImGui::DragFloat2("Range",&rmin,0.1f,-3,13); - ImPlot::SetNextPlotLimits(-3, 13, 0, 0.25); - if (ImPlot::BeginPlot("##Histograms")) { - ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.5f); - ImPlot::PlotHistogram("Empirical", dist.Data, 10000, bins, cumulative, density, ImPlotRange(rmin,rmax)); - if (density) - ImPlot::PlotLine("Theoretical",x,y,100); - ImPlot::EndPlot(); - } - - srand(0); - static NormalDistribution<10000> dist1(1, 2); - static NormalDistribution<10000> dist2(1, 1); - ImPlot::PushColormap("Cool"); - if (ImPlot::BeginPlot("Hist2D",0,0,ImVec2(-1,0),0,ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit)) { - ImPlot::PlotHistogram2D("Hist2D",dist1.Data,dist2.Data,10000,100,100,false,ImPlotLimits(-6,6,-6,6)); - ImPlot::EndPlot(); - } - ImPlot::PopColormap(); - } + } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Error Bars")) { static float xs[5] = {1,2,3,4,5}; @@ -625,6 +575,83 @@ void ShowDemoWindow(bool* p_open) { } //------------------------------------------------------------------------- + if (ImGui::CollapsingHeader("Histograms")) { + static int bins = 50; + static bool cumulative = false; + static bool density = true; + static bool outliers = true; + static double mu = 5; + static double sigma = 2; + static NormalDistribution<10000> dist(mu, sigma); + static double x[100]; + static double y[100]; + if (density) { + for (int i = 0; i < 100; ++i) { + x[i] = -3 + 16 * (double)i/99.0; + y[i] = exp( - (x[i]-mu)*(x[i]-mu) / (2*sigma*sigma)) / (sigma * sqrt(2*3.141592653589793238)); + } + if (cumulative) { + for (int i = 1; i < 100; ++i) + y[i] += y[i-1]; + for (int i = 0; i < 100; ++i) + y[i] /= y[99]; + } + } + ImGui::SetNextItemWidth(200); + if (ImGui::RadioButton("Sqrt",bins==ImPlotBin_Sqrt)) bins = ImPlotBin_Sqrt; ImGui::SameLine(); + if (ImGui::RadioButton("Sturges",bins==ImPlotBin_Sturges)) bins = ImPlotBin_Sturges; ImGui::SameLine(); + if (ImGui::RadioButton("Rice",bins==ImPlotBin_Rice)) bins = ImPlotBin_Rice; ImGui::SameLine(); + if (ImGui::RadioButton("Scott",bins==ImPlotBin_Scott)) bins = ImPlotBin_Scott; ImGui::SameLine(); + if (ImGui::RadioButton("N Bins",bins>=0)) bins = 50; + if (bins>=0) { + ImGui::SameLine(); + ImGui::SetNextItemWidth(200); + ImGui::SliderInt("##Bins", &bins, 1, 100); + } + ImGui::Checkbox("Density", &density); + ImGui::SameLine(); + ImGui::Checkbox("Cumulative", &cumulative); + ImGui::SameLine(); + static bool range = false; + ImGui::Checkbox("Range", &range); + static float rmin = -3; + static float rmax = 13; + if (range) { + ImGui::SameLine(); + ImGui::SetNextItemWidth(200); + ImGui::DragFloat2("##Range",&rmin,0.1f,-3,13); + ImGui::SameLine(); + ImGui::Checkbox("Outliers",&outliers); + } + ImPlot::SetNextPlotLimits(-3, 13, 0, 0.25); + if (ImPlot::BeginPlot("##Histograms")) { + ImPlot::SetNextFillStyle(IMPLOT_AUTO_COL, 0.5f); + ImPlot::PlotHistogram("Empirical", dist.Data, 10000, bins, cumulative, density, range ? ImPlotRange(rmin,rmax) : ImPlotRange(), outliers); + if (density && outliers) + ImPlot::PlotLine("Theoretical",x,y,100); + ImPlot::EndPlot(); + } + + static int count = 500000; + static int xybins[2] = {200,200}; + static bool density2 = false; + ImGui::SliderInt("Count",&count,100,500000); + ImGui::SliderInt2("Bins",xybins,1,500); + ImGui::SameLine(); + ImGui::Checkbox("Density##2",&density2); + static NormalDistribution<500000> dist1(1, 2); + static NormalDistribution<500000> dist2(1, 1); + double max_count = 0; + ImPlot::PushColormap("Viridis"); + if (ImPlot::BeginPlot("##Hist2D",0,0,ImVec2(ImGui::GetContentRegionAvail().x-75-ImGui::GetStyle().ItemSpacing.x,0),0,ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit)) { + max_count = ImPlot::PlotHistogram2D("Hist2D",dist1.Data,dist2.Data,count,xybins[0],xybins[1],density2,ImPlotLimits(-6,6,-6,6)); + ImPlot::EndPlot(); + } + ImGui::SameLine(); + ImPlot::ShowColormapScale(0,max_count,ImVec2(75,0)); + ImPlot::PopColormap(); + } + //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Images")) { ImGui::BulletText("Below we are displaying the font texture, which is the only texture we have\naccess to in this demo."); ImGui::BulletText("Use the 'ImTextureID' type as storage to pass pointers or identifiers to your\nown texture data."); @@ -726,7 +753,7 @@ void ShowDemoWindow(bool* p_open) { ys2[i] = log(xs[i]); ys3[i] = pow(10.0, xs[i]); } - ImGui::BulletText("Open the plot context menu (double right click) to change scales."); + ImGui::BulletText("Open the plot context menu (right click) to change scales."); ImPlot::SetNextPlotLimits(0.1, 100, 0, 10); if (ImPlot::BeginPlot("Log Plot", NULL, NULL, ImVec2(-1,0), 0, ImPlotAxisFlags_LogScale )) { diff --git a/implot_internal.h b/implot_internal.h index 9dc0b1cd..37511676 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -370,10 +370,10 @@ struct ImPlotColormapData { inline ImU32 Lerp(ImPlotColormap cmap, float t) { int size = GetSize(cmap); - int i1 = (int)((size -1 ) * t); + int i1 = (int)((size - 1 ) * t); int i2 = i1 + 1; - if (i2 == size || size == 1) - return GetColor(cmap, i1); + if (i2 == size || size == 1) + return GetColor(cmap, i1); float den = 1.0f / (size - 1); float t1 = i1 * den; float t2 = i2 * den; @@ -813,6 +813,9 @@ struct ImPlotContext { // Time tm Tm; + // Temp data for general use + ImVector Temp1, Temp2; + // Misc int VisibleItemCount; int DigitalPlotItemCnt; @@ -1027,6 +1030,27 @@ inline T OffsetAndStride(const T* data, int idx, int count, int offset, int stri return *(const T*)(const void*)((const unsigned char*)data + (size_t)idx * stride); } +// Calculate histogram bin counts and widths +template +void CalculateBins(const T* values, int count, ImPlotBin meth, const ImPlotRange& range, int& bins_out, double& width_out) { + switch (meth) { + case ImPlotBin_Sqrt: + bins_out = (int)ceil(sqrt(count)); + break; + case ImPlotBin_Sturges: + bins_out = (int)ceil(1.0 + log2(count)); + break; + case ImPlotBin_Rice: + bins_out = (int)ceil(2 * cbrt(count)); + break; + case ImPlotBin_Scott: + width_out = 3.49 * ImStdDev(values, count) / cbrt(count); + bins_out = (int)round(range.Size() / width_out); + break; + } + width_out = range.Size() / bins_out; +} + //----------------------------------------------------------------------------- // Time Utils //----------------------------------------------------------------------------- diff --git a/implot_items.cpp b/implot_items.cpp index 91b4d908..1f0dfe6d 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1412,6 +1412,7 @@ void PlotErrorBars(const char* label_id, const T* xs, const T* ys, const T* neg, GetterError getter(xs, ys, neg, pos, count, offset, stride); PlotErrorBarsEx(label_id, getter); } + template IMPLOT_API void PlotErrorBars(const char* label_id, const ImS8* xs, const ImS8* ys, const ImS8* neg, const ImS8* pos, int count, int offset, int stride); template IMPLOT_API void PlotErrorBars(const char* label_id, const ImU8* xs, const ImU8* ys, const ImU8* neg, const ImU8* pos, int count, int offset, int stride); template IMPLOT_API void PlotErrorBars(const char* label_id, const ImS16* xs, const ImS16* ys, const ImS16* neg, const ImS16* pos, int count, int offset, int stride); @@ -1740,11 +1741,14 @@ struct RectRenderer { Transformer(transformer), Prims(Getter.Count) {} - inline bool operator()(ImDrawList& DrawList, const ImRect& /*cull_rect*/, const ImVec2& uv, int prim) const { - // TODO: Culling + inline bool operator()(ImDrawList& DrawList, const ImRect& cull_rect, const ImVec2& uv, int prim) const { RectInfo rect = Getter(prim); ImVec2 P1 = Transformer(rect.Min); ImVec2 P2 = Transformer(rect.Max); + + if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) + return false; + DrawList._VtxWritePtr[0].pos = P1; DrawList._VtxWritePtr[0].uv = uv; DrawList._VtxWritePtr[0].col = rect.Color; @@ -1890,33 +1894,10 @@ template IMPLOT_API void PlotHeatmap(const char* label_id, const double* //----------------------------------------------------------------------------- template -void BinValues(const T* values, int count, ImPlotBinMethod meth, const ImPlotRange& range, int& bins, double& width) { - switch (meth) { - case ImPlotBinMethod_Sqrt: - bins = (int)ceil(sqrt(count)); - width = range.Size() / bins; - break; - case ImPlotBinMethod_Sturges: - bins = (int)ceil(1.0 + log2(count)); - width = range.Size() / bins; - break; - case ImPlotBinMethod_Rice: - bins = (int)ceil(2 * cbrt(count)); - width = range.Size() / bins; - break; - case ImPlotBinMethod_Scott: - width = 3.49 * ImStdDev(values, count) / cbrt(count); - bins = (int)round(range.Size() / width); - width = range.Size() / bins; - break; - } -} - -template -void PlotHistogram(const char* label_id, const T* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale) { +double PlotHistogram(const char* label_id, const T* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, bool outliers, double bar_scale) { if (count <= 0 || bins == 0) - return; + return 0; if (range.Min == 0 && range.Max == 0) { T Min, Max; @@ -1927,63 +1908,82 @@ void PlotHistogram(const char* label_id, const T* values, int count, int bins, b double width; if (bins < 0) - BinValues(values, count, bins, range, bins, width); + CalculateBins(values, count, bins, range, bins, width); else width = range.Size() / bins; - static ImVector bin_centers; - static ImVector bin_counts; + ImVector& bin_centers = GImPlot->Temp1; + ImVector& bin_counts = GImPlot->Temp2; bin_centers.resize(bins); bin_counts.resize(bins); + int below = 0; + for (int b = 0; b < bins; ++b) { bin_centers[b] = range.Min + b * width + width * 0.5; bin_counts[b] = 0; } int counted = 0; + double max_count = 0; for (int i = 0; i < count; ++i) { - if (range.Contains((double)values[i])) { - const int b = ImClamp((int)((double)(values[i] - range.Min) / width), 0, bins - 1); + double val = (double)values[i]; + if (range.Contains(val)) { + const int b = ImClamp((int)((val - range.Min) / width), 0, bins - 1); bin_counts[b] += 1.0; + if (bin_counts[b] > max_count) + max_count = bin_counts[b]; counted++; } + else if (val < range.Min) { + below++; + } } if (cumulative && density) { + if (outliers) + bin_counts[0] += below; for (int b = 1; b < bins; ++b) bin_counts[b] += bin_counts[b-1]; + double scale = 1.0 / (outliers ? count : counted); for (int b = 0; b < bins; ++b) - bin_counts[b] /= counted; + bin_counts[b] *= scale; + max_count = bin_counts[bins-1]; } else if (cumulative) { + if (outliers) + bin_counts[0] += below; for (int b = 1; b < bins; ++b) bin_counts[b] += bin_counts[b-1]; + max_count = bin_counts[bins-1]; } else if (density) { + double scale = 1.0 / ((outliers ? count : counted) * width); for (int b = 0; b < bins; ++b) - bin_counts[b] = bin_counts[b] / (counted * width); + bin_counts[b] *= scale; + max_count *= scale; } PlotBars(label_id, &bin_centers.Data[0], &bin_counts.Data[0], bins, bar_scale*width); + return max_count; } -template IMPLOT_API void PlotHistogram(const char* label_id, const ImS8* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImU8* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImS16* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImU16* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImS32* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImU32* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImS64* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const ImU64* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const float* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); -template IMPLOT_API void PlotHistogram(const char* label_id, const double* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, double bar_scale); +template IMPLOT_API double PlotHistogram(const char* label_id, const ImS8* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, bool outliers, double bar_scale); +template IMPLOT_API double PlotHistogram(const char* label_id, const ImU8* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, bool outliers, double bar_scale); +template IMPLOT_API double PlotHistogram(const char* label_id, const ImS16* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, bool outliers, double bar_scale); +template IMPLOT_API double PlotHistogram(const char* label_id, const ImU16* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, bool outliers, double bar_scale); +template IMPLOT_API double PlotHistogram(const char* label_id, const ImS32* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, bool outliers, double bar_scale); +template IMPLOT_API double PlotHistogram(const char* label_id, const ImU32* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, bool outliers, double bar_scale); +template IMPLOT_API double PlotHistogram(const char* label_id, const ImS64* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, bool outliers, double bar_scale); +template IMPLOT_API double PlotHistogram(const char* label_id, const ImU64* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, bool outliers, double bar_scale); +template IMPLOT_API double PlotHistogram(const char* label_id, const float* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, bool outliers, double bar_scale); +template IMPLOT_API double PlotHistogram(const char* label_id, const double* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, bool outliers, double bar_scale); //----------------------------------------------------------------------------- // PLOT HISTOGRAM 2D //----------------------------------------------------------------------------- template -void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range) { +double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers) { if (count <= 0 || x_bins == 0 || y_bins == 0) - return; + return 0; if (range.X.Min == 0 && range.X.Max == 0) { T Min, Max; @@ -2000,35 +2000,41 @@ void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, double width, height; if (x_bins < 0) - BinValues(xs, count, x_bins, range.X, x_bins, width); + CalculateBins(xs, count, x_bins, range.X, x_bins, width); else width = range.X.Size() / x_bins; if (y_bins < 0) - BinValues(ys, count, y_bins, range.Y, y_bins, height); + CalculateBins(ys, count, y_bins, range.Y, y_bins, height); else height = range.Y.Size() / y_bins; - static ImVector bin_counts; const int bins = x_bins * y_bins; + + ImVector& bin_counts = GImPlot->Temp1; bin_counts.resize(bins); + for (int b = 0; b < bins; ++b) bin_counts[b] = 0; int counted = 0; + double max_count = 0; for (int i = 0; i < count; ++i) { if (range.Contains((double)xs[i], (double)ys[i])) { const int xb = ImClamp( (int)((double)(xs[i] - range.X.Min) / width) , 0, x_bins - 1); const int yb = ImClamp( (int)((double)(ys[i] - range.Y.Min) / height) , 0, y_bins - 1); const int b = yb * x_bins + xb; bin_counts[b] += 1.0; + if (bin_counts[b] > max_count) + max_count = bin_counts[b]; counted++; } } if (density) { + double scale = 1.0 / ((outliers ? count : counted) * width * height); for (int b = 0; b < bins; ++b) - bin_counts[b] = bin_counts[b] / (counted * width * height); + bin_counts[b] *= scale; + max_count *= scale; } - const double max_count = ImMaxArray(&bin_counts.Data[0], bins); if (BeginItem(label_id)) { if (FitThisFrame()) { @@ -2037,25 +2043,26 @@ void PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, } ImDrawList& DrawList = *GetPlotDrawList(); switch (GetCurrentScale()) { - case ImPlotScale_LinLin: RenderHeatmap(TransformerLinLin(), DrawList, &bin_counts.Data[0], x_bins, y_bins, 0, max_count, NULL, range.Min(), range.Max(), false); break; - case ImPlotScale_LogLin: RenderHeatmap(TransformerLogLin(), DrawList, &bin_counts.Data[0], x_bins, y_bins, 0, max_count, NULL, range.Min(), range.Max(), false); break; - case ImPlotScale_LinLog: RenderHeatmap(TransformerLinLog(), DrawList, &bin_counts.Data[0], x_bins, y_bins, 0, max_count, NULL, range.Min(), range.Max(), false); break; - case ImPlotScale_LogLog: RenderHeatmap(TransformerLogLog(), DrawList, &bin_counts.Data[0], x_bins, y_bins, 0, max_count, NULL, range.Min(), range.Max(), false); break; + case ImPlotScale_LinLin: RenderHeatmap(TransformerLinLin(), DrawList, &bin_counts.Data[0], y_bins, x_bins, 0, max_count, NULL, range.Min(), range.Max(), false); break; + case ImPlotScale_LogLin: RenderHeatmap(TransformerLogLin(), DrawList, &bin_counts.Data[0], y_bins, x_bins, 0, max_count, NULL, range.Min(), range.Max(), false); break; + case ImPlotScale_LinLog: RenderHeatmap(TransformerLinLog(), DrawList, &bin_counts.Data[0], y_bins, x_bins, 0, max_count, NULL, range.Min(), range.Max(), false); break; + case ImPlotScale_LogLog: RenderHeatmap(TransformerLogLog(), DrawList, &bin_counts.Data[0], y_bins, x_bins, 0, max_count, NULL, range.Min(), range.Max(), false); break; } EndItem(); } + return max_count; } -template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImS8* xs, const ImS8* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range); -template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImU8* xs, const ImU8* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range); -template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImS16* xs, const ImS16* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range); -template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImU16* xs, const ImU16* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range); -template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImS32* xs, const ImS32* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range); -template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImU32* xs, const ImU32* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range); -template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImS64* xs, const ImS64* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range); -template IMPLOT_API void PlotHistogram2D(const char* label_id, const ImU64* xs, const ImU64* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range); -template IMPLOT_API void PlotHistogram2D(const char* label_id, const float* xs, const float* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range); -template IMPLOT_API void PlotHistogram2D(const char* label_id, const double* xs, const double* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range); +template IMPLOT_API double PlotHistogram2D(const char* label_id, const ImS8* xs, const ImS8* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); +template IMPLOT_API double PlotHistogram2D(const char* label_id, const ImU8* xs, const ImU8* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); +template IMPLOT_API double PlotHistogram2D(const char* label_id, const ImS16* xs, const ImS16* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); +template IMPLOT_API double PlotHistogram2D(const char* label_id, const ImU16* xs, const ImU16* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); +template IMPLOT_API double PlotHistogram2D(const char* label_id, const ImS32* xs, const ImS32* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); +template IMPLOT_API double PlotHistogram2D(const char* label_id, const ImU32* xs, const ImU32* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); +template IMPLOT_API double PlotHistogram2D(const char* label_id, const ImS64* xs, const ImS64* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); +template IMPLOT_API double PlotHistogram2D(const char* label_id, const ImU64* xs, const ImU64* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); +template IMPLOT_API double PlotHistogram2D(const char* label_id, const float* xs, const float* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); +template IMPLOT_API double PlotHistogram2D(const char* label_id, const double* xs, const double* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers); //----------------------------------------------------------------------------- // PLOT DIGITAL From 31c89b2e91bdb9d875a4b82315f736c30df3ec6b Mon Sep 17 00:00:00 2001 From: epezent Date: Tue, 9 Mar 2021 10:49:53 -0600 Subject: [PATCH 15/19] bug fixes --- implot.cpp | 4 ++-- implot_demo.cpp | 8 ++++---- implot_internal.h | 29 +++++++++++++++-------------- implot_items.cpp | 4 ++-- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/implot.cpp b/implot.cpp index 710e0006..cf596698 100644 --- a/implot.cpp +++ b/implot.cpp @@ -3735,8 +3735,8 @@ void ShowAxisMetrics(ImPlotAxis* axis, bool show_axis_rects) { ImGui::Bullet(); ImGui::Text("AllHovered: %s", axis->AllHovered ? "true" : "false"); ImGui::Bullet(); ImGui::Text("Present: %s", axis->Present ? "true" : "false"); ImGui::Bullet(); ImGui::Text("HasRange: %s", axis->HasRange ? "true" : "false"); - ImGui::Bullet(); ImGui::Text("LinkedMin: %p", axis->LinkedMin); - ImGui::Bullet(); ImGui::Text("LinkedMax: %p", axis->LinkedMax); + ImGui::Bullet(); ImGui::Text("LinkedMin: %p", (void*)axis->LinkedMin); + ImGui::Bullet(); ImGui::Text("LinkedMax: %p", (void*)axis->LinkedMax); if (show_axis_rects) { ImDrawList& fg = *ImGui::GetForegroundDrawList(); fg.AddRect(axis->HoverRect.Min, axis->HoverRect.Max, IM_COL32(0,255,0,255)); diff --git a/implot_demo.cpp b/implot_demo.cpp index b3b1fdd5..4862cc0b 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -598,10 +598,10 @@ void ShowDemoWindow(bool* p_open) { } } ImGui::SetNextItemWidth(200); - if (ImGui::RadioButton("Sqrt",bins==ImPlotBin_Sqrt)) bins = ImPlotBin_Sqrt; ImGui::SameLine(); - if (ImGui::RadioButton("Sturges",bins==ImPlotBin_Sturges)) bins = ImPlotBin_Sturges; ImGui::SameLine(); - if (ImGui::RadioButton("Rice",bins==ImPlotBin_Rice)) bins = ImPlotBin_Rice; ImGui::SameLine(); - if (ImGui::RadioButton("Scott",bins==ImPlotBin_Scott)) bins = ImPlotBin_Scott; ImGui::SameLine(); + if (ImGui::RadioButton("Sqrt",bins==ImPlotBin_Sqrt)) { bins = ImPlotBin_Sqrt; } ImGui::SameLine(); + if (ImGui::RadioButton("Sturges",bins==ImPlotBin_Sturges)) { bins = ImPlotBin_Sturges; } ImGui::SameLine(); + if (ImGui::RadioButton("Rice",bins==ImPlotBin_Rice)) { bins = ImPlotBin_Rice; } ImGui::SameLine(); + if (ImGui::RadioButton("Scott",bins==ImPlotBin_Scott)) { bins = ImPlotBin_Scott; } ImGui::SameLine(); if (ImGui::RadioButton("N Bins",bins>=0)) bins = 50; if (bins>=0) { ImGui::SameLine(); diff --git a/implot_internal.h b/implot_internal.h index 37511676..0858caa3 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -148,25 +148,26 @@ inline double ImStdDev(const T* values, int count) { } // Mix color a and b by factor t [0 256] inline ImU32 ImMixColor32(ImU32 a, ImU32 b, ImU32 t) { -#ifdef IMPLOT_MIX64 - ImU32 af = 256-t; - ImU32 bf = t; - ImU64 al = (a & 0x00ff00ff) | (((ImU64)(a & 0xff00ff00)) << 24); - ImU64 bl = (b & 0x00ff00ff) | (((ImU64)(b & 0xff00ff00)) << 24); - ImU64 mix = (al * af + bl * bf); +#ifdef IMPLOT_MIX64 // may be slightly faster on 64-bit machines + const ImU32 af = 256-t; + const ImU32 bf = t; + const ImU64 al = (a & 0x00ff00ff) | (((ImU64)(a & 0xff00ff00)) << 24); + const ImU64 bl = (b & 0x00ff00ff) | (((ImU64)(b & 0xff00ff00)) << 24); + const ImU64 mix = (al * af + bl * bf); return ((mix >> 32) & 0xff00ff00) | ((mix & 0xff00ff00) >> 8); #else - ImU32 af = 256-t; - ImU32 bf = t; - ImU32 al = (a & 0x00ff00ff); - ImU32 ah = (a & 0xff00ff00) >> 8; - ImU32 bl = (b & 0x00ff00ff); - ImU32 bh = (b & 0xff00ff00) >> 8; - ImU32 ml = (al * af + bl * bf); - ImU32 mh = (ah * af + bh * bf); + const ImU32 af = 256-t; + const ImU32 bf = t; + const ImU32 al = (a & 0x00ff00ff); + const ImU32 ah = (a & 0xff00ff00) >> 8; + const ImU32 bl = (b & 0x00ff00ff); + const ImU32 bh = (b & 0xff00ff00) >> 8; + const ImU32 ml = (al * af + bl * bf); + const ImU32 mh = (ah * af + bh * bf); return (mh & 0xff00ff00) | ((ml & 0xff00ff00) >> 8); #endif } + // Set alpha channel of 32-bit color from float in range [0 1] inline ImU32 ImAlphaColor32(ImU32 col, float alpha) { return col & ~((ImU32)((1.0f-alpha)*255)< Date: Tue, 9 Mar 2021 17:11:30 -0600 Subject: [PATCH 16/19] add lookup tables for colormap --- implot.cpp | 53 +++++++++----- implot.h | 16 +++-- implot_demo.cpp | 23 ++++--- implot_internal.h | 172 +++++++++++++++++++++++++++++++++------------- implot_items.cpp | 24 ++++--- 5 files changed, 192 insertions(+), 96 deletions(-) diff --git a/implot.cpp b/implot.cpp index cf596698..aa9486ea 100644 --- a/implot.cpp +++ b/implot.cpp @@ -396,9 +396,9 @@ void Initialize(ImPlotContext* ctx) { const ImU32 Dark[] = {4280031972, 4290281015, 4283084621, 4288892568, 4278222847, 4281597951, 4280833702, 4290740727, 4288256409 }; const ImU32 Pastel[] = {4289639675, 4293119411, 4291161036, 4293184478, 4289124862, 4291624959, 4290631909, 4293712637, 4294111986 }; const ImU32 Paired[] = {4293119554, 4290017311, 4287291314, 4281114675, 4288256763, 4280031971, 4285513725, 4278222847, 4292260554, 4288298346, 4288282623, 4280834481}; - const ImU32 Viridis[] = {4283695428, 4285867080, 4287054913, 4287455029, 4287526954, 4287402273, 4286883874, 4285579076, 4283552122, 4280737725, 4280674301 }; + const ImU32 Viridis[] = {4283695428, 4285867080, 4287054913, 4287455029, 4287526954, 4287402273, 4286883874, 4285579076, 4283552122, 4280737725, 4280674301 }; const ImU32 Plasma[] = {4287039501, 4288480321, 4289200234, 4288941455, 4287638193, 4286072780, 4284638433, 4283139314, 4281771772, 4280667900, 4280416752 }; - const ImU32 Hot[] = {4278190144, 4278190208, 4278190271, 4278190335, 4278206719, 4278223103, 4278239231, 4278255615, 4283826175, 4289396735, 4294967295 }; + const ImU32 Hot[] = {4278190144, 4278190208, 4278190271, 4278190335, 4278206719, 4278223103, 4278239231, 4278255615, 4283826175, 4289396735, 4294967295 }; const ImU32 Cool[] = {4294967040, 4294960666, 4294954035, 4294947661, 4294941030, 4294934656, 4294928025, 4294921651, 4294915020, 4294908646, 4294902015 }; const ImU32 Pink[] = {4278190154, 4282532475, 4284308894, 4285690554, 4286879686, 4287870160, 4288794330, 4289651940, 4291685869, 4293392118, 4294967295 }; const ImU32 Jet[] = {4289331200, 4294901760, 4294923520, 4294945280, 4294967040, 4289396565, 4283826090, 4278255615, 4278233855, 4278212095, 4278190335 }; @@ -1735,9 +1735,9 @@ bool BeginPlot(const char* title, const char* x_label, const char* y1_label, con if (!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect)) { const bool x_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.HorizontalMod) && ImFabs(select_size.x) > 2; const bool y_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.VerticalMod) && ImFabs(select_size.y) > 2; - if (!plot.XAxis.IsInputLocked()) { + if (!plot.XAxis.IsInputLocked()) { ImPlotPoint p1 = PixelsToPlot(plot.SelectStart); - ImPlotPoint p2 = PixelsToPlot(IO.MousePos); + ImPlotPoint p2 = PixelsToPlot(IO.MousePos); if (!plot.XAxis.IsLockedMin() && x_can_change) plot.XAxis.SetMin(ImMin(p1.x, p2.x)); if (!plot.XAxis.IsLockedMax() && x_can_change) @@ -3293,7 +3293,7 @@ void PushColormap(ImPlotColormap colormap) { void PushColormap(const char* name) { ImPlotContext& gp = *GImPlot; - PushColormap(gp.ColormapData.Lookup(name)); + PushColormap(gp.ColormapData.Lookup(name)); } void PopColormap(int count) { @@ -3621,8 +3621,8 @@ void ShowStyleEditor(ImPlotStyle* ref) { ImGui::LogFinish(); } ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); - ImGui::SameLine(); HelpMarker("Export code for current Colormap."); - + ImGui::SameLine(); HelpMarker("Export code for current Colormap."); + // built-in/added ImGui::Separator(); @@ -3630,7 +3630,7 @@ void ShowStyleEditor(ImPlotStyle* ref) { ImGui::PushID(i); int size = gp.ColormapData.GetSize(i); bool selected = i == gp.Style.Colormap; - + if (!selected) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f); @@ -3663,21 +3663,22 @@ void ShowStyleEditor(ImPlotStyle* ref) { if (custom.Size == 0) { custom.push_back(ImVec4(1,1,1,1)); custom.push_back(ImVec4(0.5f,0.5f,0.5f,1)); - } + } ImGui::Separator(); ImGui::BeginGroup(); static char name[16] = "MyColormap"; - ImGui::SetNextItemWidth(75); - ImGui::InputText("##Name",name,16,ImGuiInputTextFlags_CharsNoBlank); - - if (ImGui::Button("+", ImVec2((75 - ImGui::GetStyle().ItemSpacing.x)/2,0))) - custom.push_back(ImVec4(0,0,0,1)); + + + if (ImGui::Button("+", ImVec2((75 - ImGui::GetStyle().ItemSpacing.x)/2,0))) + custom.push_back(ImVec4(0,0,0,1)); ImGui::SameLine(); - if (ImGui::Button("-", ImVec2((75 - ImGui::GetStyle().ItemSpacing.x)/2,0)) && custom.Size > 2) - custom.pop_back(); - if (ImGui::Button("Add", ImVec2(75, 0))) - AddColormap(name,custom.Data,custom.Size); - + if (ImGui::Button("-", ImVec2((75 - ImGui::GetStyle().ItemSpacing.x)/2,0)) && custom.Size > 2) + custom.pop_back(); + ImGui::SetNextItemWidth(75); + ImGui::InputText("##Name",name,16,ImGuiInputTextFlags_CharsNoBlank); + if (ImGui::Button("Add", ImVec2(75, 0)) && gp.ColormapData.Lookup(name)==-1) + AddColormap(name,custom.Data,custom.Size); + ImGui::EndGroup(); ImGui::SameLine(); ImGui::BeginGroup(); @@ -3768,6 +3769,20 @@ void ShowMetricsWindow(bool* p_popen) { ImGui::Checkbox("Show Axes Rects", &show_axes_rects); ImGui::TreePop(); } + if (ImGui::TreeNode("Colormaps")) { +#ifdef IMPLOT_USE_COLORMAP_TABLES + ImGui::Text("IMPLOT_USE_COLORMAP_TABLES: true"); +#else + ImGui::Text("IMPLOT_USE_COLORMAP_TABLES: false"); +#endif + ImGui::Separator(); + ImGui::Text("Colormaps: %d", gp.ColormapData.Count); + ImGui::Text("Data Size: %d", gp.ColormapData.Data.Size); +#ifdef IMPLOT_USE_COLORMAP_TABLES + ImGui::Text("Table Size: %d", gp.ColormapData.Table.Size); +#endif + ImGui::TreePop(); + } if (ImGui::TreeNode("Plots","Plots (%d)", n_plots)) { for (int p = 0; p < n_plots; ++p) { // plot diff --git a/implot.h b/implot.h index e8409898..6d964c9f 100644 --- a/implot.h +++ b/implot.h @@ -442,12 +442,12 @@ template IMPLOT_API void PlotPieChart(const char* const label_ids[] // Plots a 2D heatmap chart. Values are expected to be in row-major order. #label_fmt can be set to NULL for no labels. template IMPLOT_API void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1)); -// Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #cumulative is true, each bin contains its count plus the counts of all previous bins. -// If #density is true, the PDF is visualized. If both are true, the CDF is visualized. If #range is left unspecified, the min/max of #values will be used as the range. +// Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #cumulative is true, each bin contains its count plus the counts of all previous bins. +// If #density is true, the PDF is visualized. If both are true, the CDF is visualized. If #range is left unspecified, the min/max of #values will be used as the range. // If #range is specified, outlier values outside of the range are not binned. However, outliers still count toward normalizing and cumulative counts unless #outliers is false. The largest bin count or density is returned. template IMPLOT_API double PlotHistogram(const char* label_id, const T* values, int count, int bins=ImPlotBin_Sturges, bool cumulative=false, bool density=false, ImPlotRange range=ImPlotRange(), bool outliers=true, double bar_scale=1.0); -// Plots two dimensional, bivariate histogram as a heatmap. #x_bins and #y_bins can be a positive integer or an ImPlotBin. If #density is true, the PDF is visualized. +// Plots two dimensional, bivariate histogram as a heatmap. #x_bins and #y_bins can be a positive integer or an ImPlotBin. If #density is true, the PDF is visualized. // If #range is left unspecified, the min/max of #xs an #ys will be used as the ranges. If #range is specified, outlier values outside of range are not binned. // However, outliers still count toward the normalizing count for density plots unless #outliers is false. The largest bin count or density is returned. template IMPLOT_API double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins=ImPlotBin_Sturges, int y_bins=ImPlotBin_Sturges, bool density=false, ImPlotLimits range=ImPlotLimits(), bool outliers=true); @@ -659,17 +659,21 @@ IMPLOT_API const char* GetMarkerName(ImPlotMarker idx); //----------------------------------------------------------------------------- // Item styling is based on colormaps when the relevant ImPlotCol_ is set to -// IMPLOT_AUTO_COL (default). Several built-in colormaps are available. You can +// IMPLOT_AUTO_COL (default). Several built-in colormaps are available. You can // add and then push/pop or set your own colormaps as well. To permanently set // a colormap, modify the Colormap member of your ImPlotStyle. // Colormap data will be ignored and a custom color will be used if you have done one of the following: -// 1) Modified an item style color in your ImPlotStyle to anything other than IMPLOT_AUTO_COL. +// 1) Modified an item style color in your ImPlotStyle to anything other than IMPLOT_AUTO_COL. // 2) Pushed an item style color using PushStyleColor(). // 3) Set the next item style with a SetNextXStyle function. +// NB: If you make heavy use of plots where colormap interpolation is +// required (e.g. PlotHeatmap or PlotHistogram2D), consider defining the +// compile time option IMPLOT_USE_COLORMAP_TABLES (see implot_internal.h). + // Add a new colormap. The colormap can be used by pushing either the returned index or the string name with PushColormap. -// The colormap name must be unique and the size must be greater than 1. +// The colormap name must be unique and the size must be greater than 1. You will receive an assert otherwise! IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImVec4* colormap, int size); IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImU32* colormap, int size); diff --git a/implot_demo.cpp b/implot_demo.cpp index 4862cc0b..9a4b71f8 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -431,7 +431,7 @@ void ShowDemoWindow(bool* p_open) { } ImPlot::EndPlot(); } - } + } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Error Bars")) { static float xs[5] = {1,2,3,4,5}; @@ -560,15 +560,16 @@ void ShowDemoWindow(bool* p_open) { ImGui::SameLine(); - static double values2[500*500]; + const int size = 200; + static double values2[size*size]; srand((unsigned int)(DEMO_TIME*1000000)); - for (int i = 0; i < 500*500; ++i) + for (int i = 0; i < size*size; ++i) values2[i] = RandomRange(0.0,1.0); ImPlot::SetNextPlotLimits(-1,1,-1,1); if (ImPlot::BeginPlot("##Heatmap2",NULL,NULL,ImVec2(225,225),0,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations)) { - ImPlot::PlotHeatmap("heat1",values2,500,500,0,1,NULL); - ImPlot::PlotHeatmap("heat2",values2,500,500,0,1,NULL, ImPlotPoint(-1,-1), ImPlotPoint(0,0)); + ImPlot::PlotHeatmap("heat1",values2,size,size,0,1,NULL); + ImPlot::PlotHeatmap("heat2",values2,size,size,0,1,NULL, ImPlotPoint(-1,-1), ImPlotPoint(0,0)); ImPlot::EndPlot(); } ImPlot::PopColormap(); @@ -602,7 +603,7 @@ void ShowDemoWindow(bool* p_open) { if (ImGui::RadioButton("Sturges",bins==ImPlotBin_Sturges)) { bins = ImPlotBin_Sturges; } ImGui::SameLine(); if (ImGui::RadioButton("Rice",bins==ImPlotBin_Rice)) { bins = ImPlotBin_Rice; } ImGui::SameLine(); if (ImGui::RadioButton("Scott",bins==ImPlotBin_Scott)) { bins = ImPlotBin_Scott; } ImGui::SameLine(); - if (ImGui::RadioButton("N Bins",bins>=0)) bins = 50; + if (ImGui::RadioButton("N Bins",bins>=0)) bins = 50; if (bins>=0) { ImGui::SameLine(); ImGui::SetNextItemWidth(200); @@ -610,10 +611,10 @@ void ShowDemoWindow(bool* p_open) { } ImGui::Checkbox("Density", &density); ImGui::SameLine(); - ImGui::Checkbox("Cumulative", &cumulative); + ImGui::Checkbox("Cumulative", &cumulative); ImGui::SameLine(); - static bool range = false; - ImGui::Checkbox("Range", &range); + static bool range = false; + ImGui::Checkbox("Range", &range); static float rmin = -3; static float rmax = 13; if (range) { @@ -642,14 +643,14 @@ void ShowDemoWindow(bool* p_open) { static NormalDistribution<500000> dist1(1, 2); static NormalDistribution<500000> dist2(1, 1); double max_count = 0; - ImPlot::PushColormap("Viridis"); + // ImPlot::PushColormap("Viridis"); if (ImPlot::BeginPlot("##Hist2D",0,0,ImVec2(ImGui::GetContentRegionAvail().x-75-ImGui::GetStyle().ItemSpacing.x,0),0,ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit)) { max_count = ImPlot::PlotHistogram2D("Hist2D",dist1.Data,dist2.Data,count,xybins[0],xybins[1],density2,ImPlotLimits(-6,6,-6,6)); ImPlot::EndPlot(); } ImGui::SameLine(); ImPlot::ShowColormapScale(0,max_count,ImVec2(75,0)); - ImPlot::PopColormap(); + // ImPlot::PopColormap(); } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Images")) { diff --git a/implot_internal.h b/implot_internal.h index 0858caa3..483e2de4 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -43,22 +43,24 @@ #endif //----------------------------------------------------------------------------- -// [SECTION] Forward Declarations +// [SECTION] Compile Time Options //----------------------------------------------------------------------------- -struct ImPlotTick; -struct ImPlotAxis; -struct ImPlotAxisColor; -struct ImPlotItem; -struct ImPlotLegendData; -struct ImPlotPlot; -struct ImPlotNextPlotData; +// If this is defined, colormap interpolation will be based on lookup tables. +// The size of each table is exactly (N-1)*255+1 where N is the number of colors +// in the colormap. E.g., a 10 color colormap will consume 9,184 bytes. Tables +// are generated when a new colormap is added (or on startup for built-in tables). +// In some cases, the performance gain may outweigh the memory requirement. +// Experiment for yourself. -//----------------------------------------------------------------------------- -// [SECTION] Context Pointer -//----------------------------------------------------------------------------- +// #define IMPLOT_USE_COLORMAP_TABLES -extern IMPLOT_API ImPlotContext* GImPlot; // Current implicit context pointer +// If this is defined, 64-bit multiplications will be used to mix 32-bit colors. +// On 64-bit machines, this may yield a slight performance boost. It is not +// advisable to enable this on 32-bit machines. This options is mostly irrelevant +// if IMPLOT_USE_COLORMAP_TABLES is also defined. + +// #define IMPLOT_MIX64 //----------------------------------------------------------------------------- // [SECTION] Macros and Constants @@ -78,6 +80,24 @@ extern IMPLOT_API ImPlotContext* GImPlot; // Current implicit context pointer // Maximum allowable timestamp value 01/01/3000 @ 12:00am (UTC) (DO NOT INCREASE THIS) #define IMPLOT_MAX_TIME 32503680000 +//----------------------------------------------------------------------------- +// [SECTION] Forward Declarations +//----------------------------------------------------------------------------- + +struct ImPlotTick; +struct ImPlotAxis; +struct ImPlotAxisColor; +struct ImPlotItem; +struct ImPlotLegendData; +struct ImPlotPlot; +struct ImPlotNextPlotData; + +//----------------------------------------------------------------------------- +// [SECTION] Context Pointer +//----------------------------------------------------------------------------- + +extern IMPLOT_API ImPlotContext* GImPlot; // Current implicit context pointer + //----------------------------------------------------------------------------- // [SECTION] Generic Helpers //----------------------------------------------------------------------------- @@ -146,18 +166,18 @@ inline double ImStdDev(const T* values, int count) { x += (values[i] - mu) * (values[i] - mu) * den; return sqrt(x); } -// Mix color a and b by factor t [0 256] -inline ImU32 ImMixColor32(ImU32 a, ImU32 b, ImU32 t) { -#ifdef IMPLOT_MIX64 // may be slightly faster on 64-bit machines - const ImU32 af = 256-t; - const ImU32 bf = t; +// Mix color a and b by factor s in [0 256] +inline ImU32 ImMixU32(ImU32 a, ImU32 b, ImU32 s) { +#ifdef IMPLOT_MIX64 + const ImU32 af = 256-s; + const ImU32 bf = s; const ImU64 al = (a & 0x00ff00ff) | (((ImU64)(a & 0xff00ff00)) << 24); - const ImU64 bl = (b & 0x00ff00ff) | (((ImU64)(b & 0xff00ff00)) << 24); - const ImU64 mix = (al * af + bl * bf); - return ((mix >> 32) & 0xff00ff00) | ((mix & 0xff00ff00) >> 8); + const ImU64 bl = (b & 0x00ff00ff) | (((ImU64)(b & 0xff00ff00)) << 24); + const ImU64 mix = (al * af + bl * bf); + return ((mix >> 32) & 0xff00ff00) | ((mix & 0xff00ff00) >> 8); #else - const ImU32 af = 256-t; - const ImU32 bf = t; + const ImU32 af = 256-s; + const ImU32 bf = s; const ImU32 al = (a & 0x00ff00ff); const ImU32 ah = (a & 0xff00ff00) >> 8; const ImU32 bl = (b & 0x00ff00ff); @@ -168,8 +188,21 @@ inline ImU32 ImMixColor32(ImU32 a, ImU32 b, ImU32 t) { #endif } -// Set alpha channel of 32-bit color from float in range [0 1] -inline ImU32 ImAlphaColor32(ImU32 col, float alpha) { +// Lerp across an array of 32-bit collors given t in [0.0 1.0] +inline ImU32 ImLerpU32(const ImU32* colors, int size, float t) { + int i1 = (int)((size - 1 ) * t); + int i2 = i1 + 1; + if (i2 == size || size == 1) + return colors[i1]; + float den = 1.0f / (size - 1); + float t1 = i1 * den; + float t2 = i2 * den; + float tr = ImRemap01(t, t1, t2); + return ImMixU32(colors[i1], colors[i2], (ImU32)(tr*256)); +}; + +// Set alpha channel of 32-bit color from float in range [0.0 1.0] +inline ImU32 ImAlphaU32(ImU32 col, float alpha) { return col & ~((ImU32)((1.0f-alpha)*255)< Table; + ImVector TableSizes; + ImVector TableOffsets; + + void RegenTable(ImPlotColormap cmap) { + const ImU32* data = GetData(cmap); + const int size = GetSize(cmap); + const int off = TableOffsets[cmap]; + const int table_size = 255 * (size-1) + 1; + int j = 0; + for (int i = 0; i < size-1; ++i) { + for (int s = 0; s < 255; ++s) { + ImU32 a = data[i]; + ImU32 b = data[i+1]; + ImU32 c = ImMixU32(a,b,s); + Table[off + j++] = c; + } + } + Table[off+j] = data[size-1]; + } inline ImU32 Lerp(ImPlotColormap cmap, float t) { - int size = GetSize(cmap); - int i1 = (int)((size - 1 ) * t); - int i2 = i1 + 1; - if (i2 == size || size == 1) - return GetColor(cmap, i1); - float den = 1.0f / (size - 1); - float t1 = i1 * den; - float t2 = i2 * den; - float tr = ImRemap01(t, t1, t2); - return ImMixColor32(GetColor(cmap, i1), GetColor(cmap, i2), (ImU32)(tr*256)); + ImU32* table = &Table[TableOffsets[cmap]]; + int size = TableSizes[cmap]; + int idx = (int)((size-1)*t); + return table[idx]; + } + + inline void SetColor(ImPlotColormap cmap, int idx, ImU32 value) { + Data[DataOffsets[cmap]+idx] = value; + RegenTable(cmap); } +#else + inline ImU32 Lerp(ImPlotColormap cmap, float t) { return ImLerpU32(GetData(cmap),GetSize(cmap),t); } + inline void SetColor(ImPlotColormap cmap, int idx, ImU32 value) { Data[DataOffsets[cmap]+idx] = value; } +#endif + + inline const ImU32* GetData(ImPlotColormap cmap) const { return &Data[DataOffsets[cmap]]; } + inline ImU32* GetData(ImPlotColormap cmap) { return &Data[DataOffsets[cmap]]; } + inline int GetSize(ImPlotColormap cmap) { return DataSizes[cmap]; } + inline const char* GetName(ImPlotColormap cmap) { return Text.Buf.Data + TextOffsets[cmap]; } + inline ImU32 GetColor(ImPlotColormap cmap, int idx) { return Data[DataOffsets[cmap]+idx]; } + inline ImPlotColormap Lookup(const char* name) { ImGuiID key = ImHashStr(name); return Map.GetInt(key,-1); } + }; // ImPlotPoint with positive/negative error values @@ -1035,16 +1109,16 @@ inline T OffsetAndStride(const T* data, int idx, int count, int offset, int stri template void CalculateBins(const T* values, int count, ImPlotBin meth, const ImPlotRange& range, int& bins_out, double& width_out) { switch (meth) { - case ImPlotBin_Sqrt: - bins_out = (int)ceil(sqrt(count)); + case ImPlotBin_Sqrt: + bins_out = (int)ceil(sqrt(count)); break; - case ImPlotBin_Sturges: - bins_out = (int)ceil(1.0 + log2(count)); + case ImPlotBin_Sturges: + bins_out = (int)ceil(1.0 + log2(count)); break; - case ImPlotBin_Rice: - bins_out = (int)ceil(2 * cbrt(count)); + case ImPlotBin_Rice: + bins_out = (int)ceil(2 * cbrt(count)); break; - case ImPlotBin_Scott: + case ImPlotBin_Scott: width_out = 3.49 * ImStdDev(values, count) / cbrt(count); bins_out = (int)round(range.Size() / width_out); break; diff --git a/implot_items.cpp b/implot_items.cpp index 64c6c51f..6188ab56 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1746,8 +1746,8 @@ struct RectRenderer { ImVec2 P1 = Transformer(rect.Min); ImVec2 P2 = Transformer(rect.Max); - if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) - return false; + if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) + return false; DrawList._VtxWritePtr[0].pos = P1; DrawList._VtxWritePtr[0].uv = uv; @@ -1808,7 +1808,9 @@ struct GetterHeatmap { rect.Min.y = p.y - HalfSize.y; rect.Max.x = p.x + HalfSize.x; rect.Max.y = p.y + HalfSize.y; - rect.Color = GImPlot->ColormapData.Lerp(GImPlot->Style.Colormap,t); + + // rect.Color = ImLerpU32(Colormap,ColormapSize,t); + rect.Color = GImPlot->ColormapData.Lerp(GImPlot->Style.Colormap, t); // ImLerpU32(Colormap,ColormapSize,t); return rect; } const T* const Values; @@ -1895,7 +1897,7 @@ template IMPLOT_API void PlotHeatmap(const char* label_id, const double* template double PlotHistogram(const char* label_id, const T* values, int count, int bins, bool cumulative, bool density, ImPlotRange range, bool outliers, double bar_scale) { - + if (count <= 0 || bins == 0) return 0; @@ -1907,7 +1909,7 @@ double PlotHistogram(const char* label_id, const T* values, int count, int bins, } double width; - if (bins < 0) + if (bins < 0) CalculateBins(values, count, bins, range, bins, width); else width = range.Size() / bins; @@ -1979,12 +1981,12 @@ template IMPLOT_API double PlotHistogram(const char* label_id, const dou // PLOT HISTOGRAM 2D //----------------------------------------------------------------------------- -template +template double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count, int x_bins, int y_bins, bool density, ImPlotLimits range, bool outliers) { - + if (count <= 0 || x_bins == 0 || y_bins == 0) return 0; - + if (range.X.Min == 0 && range.X.Max == 0) { T Min, Max; ImMinMaxArray(xs, count, &Min, &Max); @@ -1999,11 +2001,11 @@ double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count } double width, height; - if (x_bins < 0) + if (x_bins < 0) CalculateBins(xs, count, x_bins, range.X, x_bins, width); else width = range.X.Size() / x_bins; - if (y_bins < 0) + if (y_bins < 0) CalculateBins(ys, count, y_bins, range.Y, y_bins, height); else height = range.Y.Size() / y_bins; @@ -2012,7 +2014,7 @@ double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count ImVector& bin_counts = GImPlot->Temp1; bin_counts.resize(bins); - + for (int b = 0; b < bins; ++b) bin_counts[b] = 0; From 10a11a07ab2a07b77f4092fa209b810d3259474c Mon Sep 17 00:00:00 2001 From: epezent Date: Wed, 10 Mar 2021 10:10:46 -0600 Subject: [PATCH 17/19] final tweaks to histograms and colormaps --- implot.cpp | 88 ++++++++++++++++++++++++++++++++++------------- implot.h | 7 ++-- implot_demo.cpp | 6 ++-- implot_internal.h | 28 ++++++++------- implot_items.cpp | 36 +++++++++++-------- 5 files changed, 108 insertions(+), 57 deletions(-) diff --git a/implot.cpp b/implot.cpp index aa9486ea..04d60448 100644 --- a/implot.cpp +++ b/implot.cpp @@ -622,8 +622,8 @@ void ShowLegendEntries(ImPlotPlot& plot, const ImRect& legend_bb, bool interacta const float txt_ht = ImGui::GetTextLineHeight(); const float icon_size = txt_ht; const float icon_shrink = 2; - ImVec4 col_txt = GetStyleColorVec4(ImPlotCol_LegendText); - ImU32 col_txt_dis = ImGui::GetColorU32(col_txt * ImVec4(1,1,1,0.25f)); + ImU32 col_txt = GetStyleColorU32(ImPlotCol_LegendText); + ImU32 col_txt_dis = ImAlphaU32(col_txt, 0.25f); // render each legend item float sum_label_width = 0; for (int i = 0; i < plot.GetLegendCount(); ++i) { @@ -641,26 +641,24 @@ void ShowLegendEntries(ImPlotPlot& plot, const ImRect& legend_bb, bool interacta label_bb.Min = top_left; label_bb.Max = top_left + ImVec2(label_width + icon_size, icon_size); ImU32 col_hl_txt; + ImU32 col_item = ImAlphaU32(item->Color,1); if (interactable && (icon_bb.Contains(IO.MousePos) || label_bb.Contains(IO.MousePos))) { item->LegendHovered = true; - col_hl_txt = ImGui::GetColorU32(ImLerp(col_txt, item->Color, 0.25f)); + col_hl_txt = ImMixU32(col_txt, col_item, 64); } else { // item->LegendHovered = false; col_hl_txt = ImGui::GetColorU32(col_txt); } ImU32 iconColor; - ImVec4 item_color = item->Color; - item_color.w = 1; if (interactable && icon_bb.Contains(IO.MousePos)) { - ImVec4 colAlpha = item_color; - colAlpha.w = 0.5f; - iconColor = item->Show ? ImGui::GetColorU32(colAlpha) : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.5f); + ImU32 col_alpha = ImAlphaU32(col_item,0.5f); + iconColor = item->Show ? col_alpha : ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.5f); if (IO.MouseClicked[0]) item->Show = !item->Show; } else { - iconColor = item->Show ? ImGui::GetColorU32(item_color) : col_txt_dis; + iconColor = item->Show ? col_item : col_txt_dis; } DrawList.AddRectFilled(icon_bb.Min, icon_bb.Max, iconColor, 1); const char* text_display_end = ImGui::FindRenderedTextEnd(label, NULL); @@ -3769,20 +3767,6 @@ void ShowMetricsWindow(bool* p_popen) { ImGui::Checkbox("Show Axes Rects", &show_axes_rects); ImGui::TreePop(); } - if (ImGui::TreeNode("Colormaps")) { -#ifdef IMPLOT_USE_COLORMAP_TABLES - ImGui::Text("IMPLOT_USE_COLORMAP_TABLES: true"); -#else - ImGui::Text("IMPLOT_USE_COLORMAP_TABLES: false"); -#endif - ImGui::Separator(); - ImGui::Text("Colormaps: %d", gp.ColormapData.Count); - ImGui::Text("Data Size: %d", gp.ColormapData.Data.Size); -#ifdef IMPLOT_USE_COLORMAP_TABLES - ImGui::Text("Table Size: %d", gp.ColormapData.Table.Size); -#endif - ImGui::TreePop(); - } if (ImGui::TreeNode("Plots","Plots (%d)", n_plots)) { for (int p = 0; p < n_plots; ++p) { // plot @@ -3796,7 +3780,11 @@ void ShowMetricsWindow(bool* p_popen) { ImGui::PushID(i); if (ImGui::TreeNode("Item", "Item [ID=%u]", item->ID)) { ImGui::Bullet(); ImGui::Checkbox("Show", &item->Show); - ImGui::Bullet(); ImGui::ColorEdit4("Color",&item->Color.x, ImGuiColorEditFlags_NoInputs); + ImGui::Bullet(); + ImVec4 temp = ImGui::ColorConvertU32ToFloat4(item->Color); + if (ImGui::ColorEdit4("Color",&temp.x, ImGuiColorEditFlags_NoInputs)) + item->Color = ImGui::ColorConvertFloat4ToU32(temp); + ImGui::Bullet(); ImGui::Text("NameOffset: %d",item->NameOffset); ImGui::Bullet(); ImGui::Text("Name: %s", item->NameOffset != -1 ? plot->LegendData.Labels.Buf.Data + item->NameOffset : "N/A"); ImGui::Bullet(); ImGui::Text("Hovered: %s",item->LegendHovered ? "true" : "false"); @@ -3837,6 +3825,58 @@ void ShowMetricsWindow(bool* p_popen) { } ImGui::TreePop(); } + if (ImGui::TreeNode("Colormaps")) { +#ifdef IMPLOT_USE_COLORMAP_TABLES + ImGui::BulletText("IMPLOT_USE_COLORMAP_TABLES: true"); +#else + ImGui::BulletText("IMPLOT_USE_COLORMAP_TABLES: false"); +#endif +#ifdef IMPLOT_MIX64 + ImGui::BulletText("IMPLOT_MIX64: true"); +#else + ImGui::BulletText("IMPLOT_MIX64: false"); +#endif + ImGui::BulletText("Colormaps: %d", gp.ColormapData.Count); + ImGui::BulletText("Data Size: %d", gp.ColormapData.Data.Size); +#ifdef IMPLOT_USE_COLORMAP_TABLES + ImGui::BulletText("Table Size: %d", gp.ColormapData.Table.Size); + if (ImGui::TreeNode("Table Data")) { + for (int m = 0; m < gp.ColormapData.Count; ++m) { + if (ImGui::TreeNode(gp.ColormapData.GetName(m))) { + int keys = gp.ColormapData.GetSize(m); + int size = gp.ColormapData.TableSizes[m]; + int off = gp.ColormapData.TableOffsets[m]; + static float t = 0.5; + ImGui::BulletText("Keys: %d", keys); + ImGui::BulletText("Size: %d", size); + ImGui::SetNextItemWidth(200); + ImGui::SliderFloat("##Sample",&t,0,1); + ImGui::SameLine(); + ImVec4 samp = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.Lerp(m,t)); + ImGui::ColorButton("Sampler",samp); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0,0,0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); + for (int c = 0; c < size; ++c) { + ImVec4 col = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.Table[off+c]); + ImGui::PushID(m*10000+c); + ImGui::ColorButton("",col,0,ImVec2(6,6)); + ImGui::PopID(); + if ((c + 1) % 255 != 0 && c != size - 1) + ImGui::SameLine(); + + } + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + ImGui::TreePop(); + } + } + ImGui::TreePop(); + } +#endif + + + ImGui::TreePop(); + } ImGui::End(); } diff --git a/implot.h b/implot.h index 6d964c9f..58e115c4 100644 --- a/implot.h +++ b/implot.h @@ -668,9 +668,10 @@ IMPLOT_API const char* GetMarkerName(ImPlotMarker idx); // 2) Pushed an item style color using PushStyleColor(). // 3) Set the next item style with a SetNextXStyle function. -// NB: If you make heavy use of plots where colormap interpolation is -// required (e.g. PlotHeatmap or PlotHistogram2D), consider defining the -// compile time option IMPLOT_USE_COLORMAP_TABLES (see implot_internal.h). +// NB: When colormaps are added, an 32-bit color interpolation table is built and stored. +// The size of this table is ((N-1)*255+1)*4 bytes, where N is the colormap size. If your +// application is memory limited, you can disable interpolation tables by undefining +// IMPLOT_USE_COLORMAP_TABLES in implot_internal.h. // Add a new colormap. The colormap can be used by pushing either the returned index or the string name with PushColormap. // The colormap name must be unique and the size must be greater than 1. You will receive an assert otherwise! diff --git a/implot_demo.cpp b/implot_demo.cpp index 9a4b71f8..161077e2 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -643,14 +643,14 @@ void ShowDemoWindow(bool* p_open) { static NormalDistribution<500000> dist1(1, 2); static NormalDistribution<500000> dist2(1, 1); double max_count = 0; - // ImPlot::PushColormap("Viridis"); + ImPlot::PushColormap("Hot"); if (ImPlot::BeginPlot("##Hist2D",0,0,ImVec2(ImGui::GetContentRegionAvail().x-75-ImGui::GetStyle().ItemSpacing.x,0),0,ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit)) { max_count = ImPlot::PlotHistogram2D("Hist2D",dist1.Data,dist2.Data,count,xybins[0],xybins[1],density2,ImPlotLimits(-6,6,-6,6)); ImPlot::EndPlot(); } ImGui::SameLine(); ImPlot::ShowColormapScale(0,max_count,ImVec2(75,0)); - // ImPlot::PopColormap(); + ImPlot::PopColormap(); } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Images")) { @@ -1694,7 +1694,7 @@ void PlotCandlestick(const char* label_id, const double* xs, const double* opens // begin plot item if (ImPlot::BeginItem(label_id)) { // override legend icon color - ImPlot::GetCurrentItem()->Color = ImVec4(0.25f,0.25f,0.25f,1); + ImPlot::GetCurrentItem()->Color = IM_COL32(64,64,64,255); // fit data if requested if (ImPlot::FitThisFrame()) { for (int i = 0; i < count; ++i) { diff --git a/implot_internal.h b/implot_internal.h index 483e2de4..d5127f80 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -47,17 +47,18 @@ //----------------------------------------------------------------------------- // If this is defined, colormap interpolation will be based on lookup tables. -// The size of each table is exactly (N-1)*255+1 where N is the number of colors -// in the colormap. E.g., a 10 color colormap will consume 9,184 bytes. Tables -// are generated when a new colormap is added (or on startup for built-in tables). -// In some cases, the performance gain may outweigh the memory requirement. -// Experiment for yourself. +// The size of each table is exactly ((N-1)*255+1)*4 bytes where N is the number of +// colors in the colormap. E.g., a 10 color colormap will consume 9,184 bytes. +// Tables are generated when a new colormap is added (or on startup for built-in +// tables). In most cases, the performance gain will outweigh the memory requirement. +// If your application is memory limited, you may disable this, and runtime +// interpolation will be performed. Expect ~20% performance hit for large heatmaps. -// #define IMPLOT_USE_COLORMAP_TABLES +#define IMPLOT_USE_COLORMAP_TABLES // If this is defined, 64-bit multiplications will be used to mix 32-bit colors. -// On 64-bit machines, this may yield a slight performance boost. It is not -// advisable to enable this on 32-bit machines. This options is mostly irrelevant +// On 64-bit machines, this may yield a slight (~5%) performance boost. It is not +// advisable to enable this on 32-bit machines. This option is mostly irrelevant // if IMPLOT_USE_COLORMAP_TABLES is also defined. // #define IMPLOT_MIX64 @@ -199,7 +200,7 @@ inline ImU32 ImLerpU32(const ImU32* colors, int size, float t) { float t2 = i2 * den; float tr = ImRemap01(t, t1, t2); return ImMixU32(colors[i1], colors[i2], (ImU32)(tr*256)); -}; +} // Set alpha channel of 32-bit color from float in range [0.0 1.0] inline ImU32 ImAlphaU32(ImU32 col, float alpha) { @@ -366,6 +367,8 @@ struct ImPlotColormapData { ImPlotColormapData() { Count = 0; } int Append(const char* name, const ImU32* data, int size) { + if (Lookup(name) != -1) + return -1; DataOffsets.push_back(Data.size()); DataSizes.push_back(size); Data.reserve(Data.size()+size); @@ -388,6 +391,8 @@ struct ImPlotColormapData { } ImPlotColormap Append(const char* name, const ImVec4* data, int size) { + if (Lookup(name) != -1) + return -1; DataOffsets.push_back(Data.size()); DataSizes.push_back(size); Data.reserve(Data.size()+size); @@ -419,7 +424,6 @@ struct ImPlotColormapData { const ImU32* data = GetData(cmap); const int size = GetSize(cmap); const int off = TableOffsets[cmap]; - const int table_size = 255 * (size-1) + 1; int j = 0; for (int i = 0; i < size-1; ++i) { for (int s = 0; s < 255; ++s) { @@ -695,7 +699,7 @@ struct ImPlotAxis struct ImPlotItem { ImGuiID ID; - ImVec4 Color; + ImU32 Color; int NameOffset; bool Show; bool LegendHovered; @@ -703,7 +707,7 @@ struct ImPlotItem ImPlotItem() { ID = 0; - Color = ImPlot::NextColormapColor(); + // Color = ImPlot::NextColormapColor(); NameOffset = -1; Show = true; SeenThisFrame = false; diff --git a/implot_items.cpp b/implot_items.cpp index 6188ab56..9303bc75 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -114,7 +114,7 @@ void SetNextErrorBarStyle(const ImVec4& col, float size, float weight) { ImVec4 GetLastItemColor() { ImPlotContext& gp = *GImPlot; if (gp.PreviousItem) - return gp.PreviousItem->Color; + return ImGui::ColorConvertU32ToFloat4(gp.PreviousItem->Color); return ImVec4(); } @@ -148,12 +148,17 @@ bool BeginItem(const char* label_id, ImPlotCol recolor_from) { // set current item gp.CurrentItem = item; ImPlotNextItemData& s = gp.NextItemData; - // override item color + // set/override item color if (recolor_from != -1) { if (!IsColorAuto(s.Colors[recolor_from])) - item->Color = s.Colors[recolor_from]; + item->Color = ImGui::ColorConvertFloat4ToU32(s.Colors[recolor_from]); else if (!IsColorAuto(gp.Style.Colors[recolor_from])) - item->Color = gp.Style.Colors[recolor_from]; + item->Color = ImGui::ColorConvertFloat4ToU32(gp.Style.Colors[recolor_from]); + else if (just_created) + item->Color = NextColormapColorU32(); + } + else if (just_created) { + item->Color = NextColormapColorU32(); } // hide/show item if (gp.NextItemData.HasHidden) { @@ -168,9 +173,10 @@ bool BeginItem(const char* label_id, ImPlotCol recolor_from) { return false; } else { + ImVec4 item_color = ImGui::ColorConvertU32ToFloat4(item->Color); // stage next item colors - s.Colors[ImPlotCol_Line] = IsColorAuto(s.Colors[ImPlotCol_Line]) ? ( IsColorAuto(ImPlotCol_Line) ? item->Color : gp.Style.Colors[ImPlotCol_Line] ) : s.Colors[ImPlotCol_Line]; - s.Colors[ImPlotCol_Fill] = IsColorAuto(s.Colors[ImPlotCol_Fill]) ? ( IsColorAuto(ImPlotCol_Fill) ? item->Color : gp.Style.Colors[ImPlotCol_Fill] ) : s.Colors[ImPlotCol_Fill]; + s.Colors[ImPlotCol_Line] = IsColorAuto(s.Colors[ImPlotCol_Line]) ? ( IsColorAuto(ImPlotCol_Line) ? item_color : gp.Style.Colors[ImPlotCol_Line] ) : s.Colors[ImPlotCol_Line]; + s.Colors[ImPlotCol_Fill] = IsColorAuto(s.Colors[ImPlotCol_Fill]) ? ( IsColorAuto(ImPlotCol_Fill) ? item_color : gp.Style.Colors[ImPlotCol_Fill] ) : s.Colors[ImPlotCol_Fill]; s.Colors[ImPlotCol_MarkerOutline] = IsColorAuto(s.Colors[ImPlotCol_MarkerOutline]) ? ( IsColorAuto(ImPlotCol_MarkerOutline) ? s.Colors[ImPlotCol_Line] : gp.Style.Colors[ImPlotCol_MarkerOutline] ) : s.Colors[ImPlotCol_MarkerOutline]; s.Colors[ImPlotCol_MarkerFill] = IsColorAuto(s.Colors[ImPlotCol_MarkerFill]) ? ( IsColorAuto(ImPlotCol_MarkerFill) ? s.Colors[ImPlotCol_Line] : gp.Style.Colors[ImPlotCol_MarkerFill] ) : s.Colors[ImPlotCol_MarkerFill]; s.Colors[ImPlotCol_ErrorBar] = IsColorAuto(s.Colors[ImPlotCol_ErrorBar]) ? ( GetStyleColorVec4(ImPlotCol_ErrorBar) ) : s.Colors[ImPlotCol_ErrorBar]; @@ -1680,7 +1686,7 @@ void PlotPieChart(const char* const label_ids[], const T* values, int count, dou double percent = normalize ? (double)values[i] / sum : (double)values[i]; a1 = a0 + 2 * IM_PI * percent; if (BeginItem(label_ids[i])) { - ImU32 col = ImGui::GetColorU32(GetCurrentItem()->Color); + ImU32 col = GetCurrentItem()->Color; if (percent < 0.5) { RenderPieSlice(DrawList, center, radius, a0, a1, col); } @@ -1705,7 +1711,7 @@ void PlotPieChart(const char* const label_ids[], const T* values, int count, dou ImVec2 size = ImGui::CalcTextSize(buffer); double angle = a0 + (a1 - a0) * 0.5; ImVec2 pos = PlotToPixels(center.x + 0.5 * radius * cos(angle), center.y + 0.5 * radius * sin(angle)); - ImU32 col = CalcTextColor(item->Color); + ImU32 col = CalcTextColor(ImGui::ColorConvertU32ToFloat4(item->Color)); DrawList.AddText(pos - size * 0.5f, col, buffer); } a0 = a1; @@ -1746,7 +1752,7 @@ struct RectRenderer { ImVec2 P1 = Transformer(rect.Min); ImVec2 P2 = Transformer(rect.Max); - if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) + if ((rect.Color & IM_COL32_A_MASK) == 0 || !cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) return false; DrawList._VtxWritePtr[0].pos = P1; @@ -1799,18 +1805,17 @@ struct GetterHeatmap { { } inline RectInfo operator()(int idx) const { + double val = (double)Values[idx]; const int r = idx / Cols; const int c = idx % Cols; const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height)); - const float t = ImClamp((float)ImRemap01((double)Values[idx], ScaleMin, ScaleMax),0.0f,1.0f); RectInfo rect; rect.Min.x = p.x - HalfSize.x; rect.Min.y = p.y - HalfSize.y; rect.Max.x = p.x + HalfSize.x; rect.Max.y = p.y + HalfSize.y; - - // rect.Color = ImLerpU32(Colormap,ColormapSize,t); - rect.Color = GImPlot->ColormapData.Lerp(GImPlot->Style.Colormap, t); // ImLerpU32(Colormap,ColormapSize,t); + const float t = ImClamp((float)ImRemap01(val, ScaleMin, ScaleMax),0.0f,1.0f); + rect.Color = GImPlot->ColormapData.Lerp(GImPlot->Style.Colormap, t); return rect; } const T* const Values; @@ -2162,12 +2167,13 @@ void PlotImage(const char* label_id, ImTextureID user_texture_id, const ImPlotPo FitPoint(bmin); FitPoint(bmax); } - GetCurrentItem()->Color = tint_col; + ImU32 tint_col32 = ImGui::ColorConvertFloat4ToU32(tint_col); + GetCurrentItem()->Color = tint_col32; ImDrawList& DrawList = *GetPlotDrawList(); ImVec2 p1 = PlotToPixels(bmin.x, bmax.y); ImVec2 p2 = PlotToPixels(bmax.x, bmin.y); PushPlotClipRect(); - DrawList.AddImage(user_texture_id, p1, p2, uv0, uv1, ImGui::ColorConvertFloat4ToU32(tint_col)); + DrawList.AddImage(user_texture_id, p1, p2, uv0, uv1, tint_col32); PopPlotClipRect(); EndItem(); } From ab81362ad39ae8c32a85af5f6ba99c5d942ca41d Mon Sep 17 00:00:00 2001 From: epezent Date: Thu, 11 Mar 2021 00:10:29 -0600 Subject: [PATCH 18/19] colormap memory opts and misc tools --- implot.cpp | 187 +++++++++++++++++++++++++++------------------ implot.h | 42 +++++----- implot_demo.cpp | 11 ++- implot_internal.h | 190 ++++++++++++++++++++-------------------------- implot_items.cpp | 4 +- 5 files changed, 227 insertions(+), 207 deletions(-) diff --git a/implot.cpp b/implot.cpp index 04d60448..e204b56b 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. -- 2021/03/08 (0.9) - SetColormap and PushColormap(ImVec4*) were removed. Use AddColormap for custom colormap support. +- 2021/03/08 (0.9) - SetColormap and PushColormap(ImVec4*) were removed. Use AddColormap for custom colormap support. LerpColormap was changed to SampleColormap. - 2021/03/07 (0.9) - The signature of ShowColormapScale was modified to accept a ImVec2 size. - 2021/02/28 (0.9) - BeginLegendDragDropSource was changed to BeginDragDropSourceItem with a number of other drag and drop improvements. - 2021/01/18 (0.9) - The default behavior for opening context menus was change from double right-click to single right-click. ImPlotInputMap and related functions were moved @@ -386,12 +386,11 @@ void SetCurrentContext(ImPlotContext* ctx) { GImPlot = ctx; } -#define IMPLOT_APPEND_CMAP(name) ctx->ColormapData.Append(#name, name, sizeof(name)/sizeof(ImU32)) +#define IMPLOT_APPEND_CMAP(name, qual) ctx->ColormapData.Append(#name, name, sizeof(name)/sizeof(ImU32), qual) void Initialize(ImPlotContext* ctx) { Reset(ctx); - const ImU32 Default[] = {4294950656, 4278190335, 4278255487, 4278255615, 4294967040, 4278232575, 4294902015, 4293012362, 4286611584, 4287411410 }; const ImU32 Deep[] = {4289753676, 4283598045, 4285048917, 4283584196, 4289950337, 4284512403, 4291005402, 4287401100, 4285839820, 4291671396 }; const ImU32 Dark[] = {4280031972, 4290281015, 4283084621, 4288892568, 4278222847, 4281597951, 4280833702, 4290740727, 4288256409 }; const ImU32 Pastel[] = {4289639675, 4293119411, 4291161036, 4293184478, 4289124862, 4291624959, 4290631909, 4293712637, 4294111986 }; @@ -403,17 +402,16 @@ void Initialize(ImPlotContext* ctx) { const ImU32 Pink[] = {4278190154, 4282532475, 4284308894, 4285690554, 4286879686, 4287870160, 4288794330, 4289651940, 4291685869, 4293392118, 4294967295 }; const ImU32 Jet[] = {4289331200, 4294901760, 4294923520, 4294945280, 4294967040, 4289396565, 4283826090, 4278255615, 4278233855, 4278212095, 4278190335 }; - IMPLOT_APPEND_CMAP(Default); - IMPLOT_APPEND_CMAP(Deep); - IMPLOT_APPEND_CMAP(Dark); - IMPLOT_APPEND_CMAP(Pastel); - IMPLOT_APPEND_CMAP(Paired); - IMPLOT_APPEND_CMAP(Viridis); - IMPLOT_APPEND_CMAP(Plasma); - IMPLOT_APPEND_CMAP(Hot); - IMPLOT_APPEND_CMAP(Cool); - IMPLOT_APPEND_CMAP(Pink); - IMPLOT_APPEND_CMAP(Jet); + IMPLOT_APPEND_CMAP(Deep, true); + IMPLOT_APPEND_CMAP(Dark, true); + IMPLOT_APPEND_CMAP(Pastel, true); + IMPLOT_APPEND_CMAP(Paired, true); + IMPLOT_APPEND_CMAP(Viridis, false); + IMPLOT_APPEND_CMAP(Plasma, false); + IMPLOT_APPEND_CMAP(Hot, false); + IMPLOT_APPEND_CMAP(Cool, false); + IMPLOT_APPEND_CMAP(Pink, false); + IMPLOT_APPEND_CMAP(Jet, false); } void Reset(ImPlotContext* ctx) { @@ -3268,18 +3266,22 @@ void PopStyleVar(int count) { // COLORMAPS //------------------------------------------------------------------------------ -ImPlotColormap AddColormap(const char* name, const ImVec4* colormap, int size) { +ImPlotColormap AddColormap(const char* name, const ImVec4* colormap, int size, bool qual) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(size > 1, "The colormap size must be greater than 1!"); - IM_ASSERT_USER_ERROR(gp.ColormapData.Lookup(name) == -1, "The colormap name has already been used!"); - return gp.ColormapData.Append(name, colormap, size); + IM_ASSERT_USER_ERROR(gp.ColormapData.GetIndex(name) == -1, "The colormap name has already been used!"); + ImVector buffer; + buffer.resize(size); + for (int i = 0; i < size; ++i) + buffer[i] = ImGui::ColorConvertFloat4ToU32(colormap[i]); + return gp.ColormapData.Append(name, buffer.Data, size, qual); } -ImPlotColormap AddColormap(const char* name, const ImU32* colormap, int size) { +ImPlotColormap AddColormap(const char* name, const ImU32* colormap, int size, bool qual) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(size > 1, "The colormap size must be greater than 1!"); - IM_ASSERT_USER_ERROR(gp.ColormapData.Lookup(name) == -1, "The colormap name has already be used!"); - return gp.ColormapData.Append(name, colormap, size); + IM_ASSERT_USER_ERROR(gp.ColormapData.GetIndex(name) == -1, "The colormap name has already be used!"); + return gp.ColormapData.Append(name, colormap, size, qual); } void PushColormap(ImPlotColormap colormap) { @@ -3291,7 +3293,9 @@ void PushColormap(ImPlotColormap colormap) { void PushColormap(const char* name) { ImPlotContext& gp = *GImPlot; - PushColormap(gp.ColormapData.Lookup(name)); + ImPlotColormap idx = gp.ColormapData.GetIndex(name); + IM_ASSERT_USER_ERROR(idx != -1, "The colormap name is invalid!"); + PushColormap(idx); } void PopColormap(int count) { @@ -3316,12 +3320,13 @@ const char* GetColormapName(ImPlotColormap colormap) { int GetColormapSize() { ImPlotContext& gp = *GImPlot; - return gp.ColormapData.GetSize(gp.Style.Colormap); + return gp.ColormapData.GetKeyCount(gp.Style.Colormap); } ImU32 GetColormapColorU32(int idx) { ImPlotContext& gp = *GImPlot; - return gp.ColormapData.GetColor(gp.Style.Colormap, idx); + idx = idx % gp.ColormapData.GetKeyCount(gp.Style.Colormap); + return gp.ColormapData.GetKeyColor(gp.Style.Colormap, idx); } ImVec4 GetColormapColor(int idx) { @@ -3331,8 +3336,8 @@ ImVec4 GetColormapColor(int idx) { ImU32 NextColormapColorU32() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "NextColormapColor() needs to be called between BeginPlot() and EndPlot()!"); - int idx = gp.CurrentPlot->ColormapIdx % gp.ColormapData.GetSize(gp.Style.Colormap); - ImU32 col = gp.ColormapData.GetColor(gp.Style.Colormap, idx); + int idx = gp.CurrentPlot->ColormapIdx % gp.ColormapData.GetKeyCount(gp.Style.Colormap); + ImU32 col = gp.ColormapData.GetKeyColor(gp.Style.Colormap, idx); gp.CurrentPlot->ColormapIdx++; return col; } @@ -3341,13 +3346,50 @@ ImVec4 NextColormapColor() { return ImGui::ColorConvertU32ToFloat4(NextColormapColorU32()); } -ImU32 LerpColormapU32(float t) { +ImU32 SampleColormapU32(float t) { ImPlotContext& gp = *GImPlot; - return gp.ColormapData.Lerp(gp.Style.Colormap,t); + return gp.ColormapData.LerpTable(gp.Style.Colormap,t); } -ImVec4 LerpColormap(float t) { - return ImGui::ColorConvertU32ToFloat4(LerpColormapU32(t)); +ImVec4 SampleColormap(float t) { + return ImGui::ColorConvertU32ToFloat4(SampleColormapU32(t)); +} + +void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed, bool continuous) { + const int n = continuous ? size - 1 : size; + ImU32 col1, col2; + if (vert) { + const float step = bounds.GetHeight() / n; + ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Max.x, bounds.Min.y + step); + for (int i = 0; i < n; ++i) { + if (reversed) { + col1 = colors[size-i-1]; + col2 = continuous ? colors[size-i-2] : col1; + } + else { + col1 = colors[i]; + col2 = continuous ? colors[i+1] : col1; + } + DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col1, col2, col2); + rect.TranslateY(step); + } + } + else { + const float step = bounds.GetWidth() / n; + ImRect rect(bounds.Min.x, bounds.Min.y, bounds.Min.x + step, bounds.Max.y); + for (int i = 0; i < n; ++i) { + if (reversed) { + col1 = colors[size-i-1]; + col2 = continuous ? colors[size-i-2] : col1; + } + else { + col1 = colors[i]; + col2 = continuous ? colors[i+1] : col1; + } + DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col2, col2, col1); + rect.TranslateX(step); + } + } } void ShowColormapScale(double scale_min, double scale_max, const ImVec2& size) { @@ -3389,14 +3431,10 @@ void ShowColormapScale(double scale_min, double scale_max, const ImVec2& size) { ImRect bb_grad(bb_frame.Min + gp.Style.PlotPadding, bb_frame.Min + ImVec2(bar_w + gp.Style.PlotPadding.x, frame_size.y - gp.Style.PlotPadding.y)); ImGui::PushClipRect(bb_frame.Min, bb_frame.Max, true); - int num_cols = GetColormapSize(); - float h_step = (frame_size.y - 2 * gp.Style.PlotPadding.y) / (num_cols - 1); - 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 = GetColormapColorU32(num_cols - i - 1); - ImU32 col2 = GetColormapColorU32(num_cols - i - 2); - DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col1, col2, col2); - } + + ImPlotColormap map = gp.Style.Colormap; + RenderColorBar(gp.ColormapData.GetKeys(map), gp.ColormapData.GetKeyCount(map), DrawList, bb_grad, true, true, !gp.ColormapData.IsQual(map)); + const ImU32 col_tick = GetStyleColorU32(ImPlotCol_TitleText); for (int i = 0; i < gp.CTicks.Size; ++i) { const float ypos = ImRemap((float)gp.CTicks.Ticks[i].PlotPos, (float)range.Max, (float)range.Min, bb_grad.Min.y, bb_grad.Max.y); @@ -3626,13 +3664,13 @@ void ShowStyleEditor(ImPlotStyle* ref) { ImGui::Separator(); for (int i = 0; i < gp.ColormapData.Count; ++i) { ImGui::PushID(i); - int size = gp.ColormapData.GetSize(i); + int size = gp.ColormapData.GetKeyCount(i); bool selected = i == gp.Style.Colormap; if (!selected) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f); - if (ImGui::Button(GetColormapName(i), ImVec2(75,0))) { + if (ImGui::Button(GetColormapName(i), ImVec2(100,0))) { gp.Style.Colormap = i; BustItemCache(); } @@ -3642,10 +3680,10 @@ void ShowStyleEditor(ImPlotStyle* ref) { ImGui::BeginGroup(); for (int c = 0; c < size; ++c) { ImGui::PushID(c); - ImVec4 col4 = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetColor(i,c)); + ImVec4 col4 = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetKeyColor(i,c)); if (ImGui::ColorEdit4("",&col4.x,ImGuiColorEditFlags_NoInputs)) { ImU32 col32 = ImGui::ColorConvertFloat4ToU32(col4); - gp.ColormapData.SetColor(i,c,col32); + gp.ColormapData.SetKeyColor(i,c,col32); BustItemCache(); } if ((c + 1) % 12 != 0 && c != size -1) @@ -3667,15 +3705,17 @@ void ShowStyleEditor(ImPlotStyle* ref) { static char name[16] = "MyColormap"; - if (ImGui::Button("+", ImVec2((75 - ImGui::GetStyle().ItemSpacing.x)/2,0))) + if (ImGui::Button("+", ImVec2((100 - ImGui::GetStyle().ItemSpacing.x)/2,0))) custom.push_back(ImVec4(0,0,0,1)); ImGui::SameLine(); - if (ImGui::Button("-", ImVec2((75 - ImGui::GetStyle().ItemSpacing.x)/2,0)) && custom.Size > 2) + if (ImGui::Button("-", ImVec2((100 - ImGui::GetStyle().ItemSpacing.x)/2,0)) && custom.Size > 2) custom.pop_back(); - ImGui::SetNextItemWidth(75); + ImGui::SetNextItemWidth(100); ImGui::InputText("##Name",name,16,ImGuiInputTextFlags_CharsNoBlank); - if (ImGui::Button("Add", ImVec2(75, 0)) && gp.ColormapData.Lookup(name)==-1) - AddColormap(name,custom.Data,custom.Size); + static bool qual = true; + ImGui::Checkbox("Qualitative",&qual); + if (ImGui::Button("Add", ImVec2(100, 0)) && gp.ColormapData.GetIndex(name)==-1) + AddColormap(name,custom.Data,custom.Size,qual); ImGui::EndGroup(); ImGui::SameLine(); @@ -3826,55 +3866,52 @@ void ShowMetricsWindow(bool* p_popen) { ImGui::TreePop(); } if (ImGui::TreeNode("Colormaps")) { -#ifdef IMPLOT_USE_COLORMAP_TABLES - ImGui::BulletText("IMPLOT_USE_COLORMAP_TABLES: true"); -#else - ImGui::BulletText("IMPLOT_USE_COLORMAP_TABLES: false"); -#endif -#ifdef IMPLOT_MIX64 - ImGui::BulletText("IMPLOT_MIX64: true"); -#else - ImGui::BulletText("IMPLOT_MIX64: false"); -#endif ImGui::BulletText("Colormaps: %d", gp.ColormapData.Count); - ImGui::BulletText("Data Size: %d", gp.ColormapData.Data.Size); -#ifdef IMPLOT_USE_COLORMAP_TABLES - ImGui::BulletText("Table Size: %d", gp.ColormapData.Table.Size); - if (ImGui::TreeNode("Table Data")) { + ImGui::BulletText("Memory: %d bytes", gp.ColormapData.Tables.Size * 4); + if (ImGui::TreeNode("Data")) { for (int m = 0; m < gp.ColormapData.Count; ++m) { if (ImGui::TreeNode(gp.ColormapData.GetName(m))) { - int keys = gp.ColormapData.GetSize(m); - int size = gp.ColormapData.TableSizes[m]; - int off = gp.ColormapData.TableOffsets[m]; + const ImU32* keys = gp.ColormapData.GetKeys(m); + int count = gp.ColormapData.GetKeyCount(m); + int size = gp.ColormapData.GetTableSize(m); + bool qual = gp.ColormapData.IsQual(m); static float t = 0.5; - ImGui::BulletText("Keys: %d", keys); - ImGui::BulletText("Size: %d", size); - ImGui::SetNextItemWidth(200); + ImGui::BulletText("Qualitative: %s", qual ? "true" : "false"); + ImGui::BulletText("Key Count: %d", count); + ImGui::BulletText("Table Size: %d", size); + ImGui::Indent(); + ImGui::SetNextItemWidth(370); + ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos; + RenderColorBar(keys,count,*ImGui::GetWindowDrawList(),ImRect(pos.x,pos.y,pos.x+370,pos.y+ImGui::GetFrameHeight()),false,false,!qual); + ImGui::PushStyleColor(ImGuiCol_FrameBg,IM_COL32_BLACK_TRANS); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive,IM_COL32_BLACK_TRANS); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered,IM_COL32_BLACK_TRANS); + ImGui::PushStyleColor(ImGuiCol_SliderGrab,IM_COL32_WHITE); + ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize,1); ImGui::SliderFloat("##Sample",&t,0,1); + ImGui::PopStyleColor(4); + ImGui::PopStyleVar(1); ImGui::SameLine(); - ImVec4 samp = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.Lerp(m,t)); + ImVec4 samp = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.LerpTable(m,t)); ImGui::ColorButton("Sampler",samp); ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0,0,0,0)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); for (int c = 0; c < size; ++c) { - ImVec4 col = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.Table[off+c]); - ImGui::PushID(m*10000+c); - ImGui::ColorButton("",col,0,ImVec2(6,6)); + ImVec4 col = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetTableColor(m,c)); + ImGui::PushID(m*1000+c); + ImGui::ColorButton("",col,0,ImVec2(10,10)); ImGui::PopID(); - if ((c + 1) % 255 != 0 && c != size - 1) + if ((c + 1) % 40 != 0 && c != size - 1) ImGui::SameLine(); - } ImGui::PopStyleVar(); ImGui::PopStyleColor(); + ImGui::Unindent(); ImGui::TreePop(); } } ImGui::TreePop(); } -#endif - - ImGui::TreePop(); } ImGui::End(); diff --git a/implot.h b/implot.h index 58e115c4..736e60b0 100644 --- a/implot.h +++ b/implot.h @@ -182,17 +182,16 @@ enum ImPlotMarker_ { // Built-in colormaps enum ImPlotColormap_ { - ImPlotColormap_Default = 0, // ImPlot default colormap (n=10) - ImPlotColormap_Deep = 1, // a.k.a. seaborn deep (n=10) - ImPlotColormap_Dark = 2, // a.k.a. matplotlib "Set1" (n=9) - ImPlotColormap_Pastel = 3, // a.k.a. matplotlib "Pastel1" (n=9) - ImPlotColormap_Paired = 4, // a.k.a. matplotlib "Paired" (n=12) - ImPlotColormap_Viridis = 5, // a.k.a. matplotlib "viridis" (n=11) - ImPlotColormap_Plasma = 6, // a.k.a. matplotlib "plasma" (n=11) - ImPlotColormap_Hot = 7, // a.k.a. matplotlib/MATLAB "hot" (n=11) - ImPlotColormap_Cool = 8, // a.k.a. matplotlib/MATLAB "cool" (n=11) - ImPlotColormap_Pink = 9, // a.k.a. matplotlib/MATLAB "pink" (n=11) - ImPlotColormap_Jet = 10 // a.k.a. MATLAB "jet" (n=11) + ImPlotColormap_Deep = 0, // a.k.a. seaborn deep (qual=true, n=10) (default) + ImPlotColormap_Dark = 1, // a.k.a. matplotlib "Set1" (qual=true, n=9 ) + ImPlotColormap_Pastel = 2, // a.k.a. matplotlib "Pastel1" (qual=true, n=9 ) + ImPlotColormap_Paired = 3, // a.k.a. matplotlib "Paired" (qual=true, n=12) + ImPlotColormap_Viridis = 4, // a.k.a. matplotlib "viridis" (qual=false, n=11) + ImPlotColormap_Plasma = 5, // a.k.a. matplotlib "plasma" (qual=false, n=11) + ImPlotColormap_Hot = 6, // a.k.a. matplotlib/MATLAB "hot" (qual=false, n=11) + ImPlotColormap_Cool = 7, // a.k.a. matplotlib/MATLAB "cool" (qual=false, n=11) + ImPlotColormap_Pink = 8, // a.k.a. matplotlib/MATLAB "pink" (qual=false, n=11) + ImPlotColormap_Jet = 9 // a.k.a. MATLAB "jet" (qual=false, n=11) }; // Used to position items on a plot (e.g. legends, labels, etc.) @@ -661,22 +660,21 @@ IMPLOT_API const char* GetMarkerName(ImPlotMarker idx); // Item styling is based on colormaps when the relevant ImPlotCol_ is set to // IMPLOT_AUTO_COL (default). Several built-in colormaps are available. You can // add and then push/pop or set your own colormaps as well. To permanently set -// a colormap, modify the Colormap member of your ImPlotStyle. +// a colormap, modify the Colormap index member of your ImPlotStyle. // Colormap data will be ignored and a custom color will be used if you have done one of the following: // 1) Modified an item style color in your ImPlotStyle to anything other than IMPLOT_AUTO_COL. // 2) Pushed an item style color using PushStyleColor(). // 3) Set the next item style with a SetNextXStyle function. -// NB: When colormaps are added, an 32-bit color interpolation table is built and stored. -// The size of this table is ((N-1)*255+1)*4 bytes, where N is the colormap size. If your -// application is memory limited, you can disable interpolation tables by undefining -// IMPLOT_USE_COLORMAP_TABLES in implot_internal.h. - // Add a new colormap. The colormap can be used by pushing either the returned index or the string name with PushColormap. -// The colormap name must be unique and the size must be greater than 1. You will receive an assert otherwise! -IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImVec4* colormap, int size); -IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImU32* colormap, int size); +// The colormap name must be unique and the size must be greater than 1. You will receive an assert otherwise! By default +// colormaps are considered to be qualitative. If you want to create a perceptually continuous colormap, set #qual=false. +// This will treat the colors you provide as keys, and ImPlot will build a linearly interpolated lookup table that fills +// in the gaps. The memory footprint of this table will be ((size-1)*255+1)*4 bytes at a maximum, but likely less depending +// upon how spatially separated your colors are in RGB space. +IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImVec4* cols, int size, bool qual=true); +IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImU32* cols, int size, bool qual=true); // Temporarily switch to one of the built-in (i.e. ImPlotColormap_XXX) or user-added colormaps (i.e. a return value of AddColormap). Don't forget to call PopColormap! IMPLOT_API void PushColormap(ImPlotColormap cmap); @@ -689,8 +687,8 @@ IMPLOT_API void PopColormap(int count = 1); IMPLOT_API int GetColormapSize(); // Returns a color from the Color map given an index >= 0 (modulo will be performed). IMPLOT_API ImVec4 GetColormapColor(int idx); -// Linearly interpolates a color from the current colormap given t between 0 and 1. -IMPLOT_API ImVec4 LerpColormap(float t); +// Sample a color from the current colormap given t between 0 and 1. +IMPLOT_API ImVec4 SampleColormap(float t); // Returns the next unused colormap color and advances the colormap. Can be used to skip colors if desired. IMPLOT_API ImVec4 NextColormapColor(); diff --git a/implot_demo.cpp b/implot_demo.cpp index 161077e2..d3e2e95e 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -75,6 +75,15 @@ inline T RandomRange(T min, T max) { return min + scale * ( max - min ); } +ImVec4 RandomColor() { + ImVec4 col; + col.x = RandomRange(0.0f,1.0f); + col.y = RandomRange(0.0f,1.0f); + col.z = RandomRange(0.0f,1.0f); + col.w = 1.0f; + return col; +} + double RandomGauss() { static double V1, V2, S; static int phase = 0; @@ -1096,7 +1105,7 @@ void ShowDemoWindow(bool* p_open) { Plt = 0; Yax = ImPlotYAxis_1; sprintf(Label, "%02d Hz", Idx+1); - Color = ImPlot::GetColormapColor(Idx); + Color = RandomColor(); Data.reserve(1001); for (int k = 0; k < 1001; ++k) { float t = k * 1.0f / 999; diff --git a/implot_internal.h b/implot_internal.h index d5127f80..bb4f99a0 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -42,27 +42,6 @@ #error Must include implot.h before implot_internal.h #endif -//----------------------------------------------------------------------------- -// [SECTION] Compile Time Options -//----------------------------------------------------------------------------- - -// If this is defined, colormap interpolation will be based on lookup tables. -// The size of each table is exactly ((N-1)*255+1)*4 bytes where N is the number of -// colors in the colormap. E.g., a 10 color colormap will consume 9,184 bytes. -// Tables are generated when a new colormap is added (or on startup for built-in -// tables). In most cases, the performance gain will outweigh the memory requirement. -// If your application is memory limited, you may disable this, and runtime -// interpolation will be performed. Expect ~20% performance hit for large heatmaps. - -#define IMPLOT_USE_COLORMAP_TABLES - -// If this is defined, 64-bit multiplications will be used to mix 32-bit colors. -// On 64-bit machines, this may yield a slight (~5%) performance boost. It is not -// advisable to enable this on 32-bit machines. This option is mostly irrelevant -// if IMPLOT_USE_COLORMAP_TABLES is also defined. - -// #define IMPLOT_MIX64 - //----------------------------------------------------------------------------- // [SECTION] Macros and Constants //----------------------------------------------------------------------------- @@ -356,110 +335,104 @@ static inline bool operator>=(const ImPlotTime& lhs, const ImPlotTime& rhs) // Colormap data storage struct ImPlotColormapData { - ImVector Data; - ImVector DataSizes; - ImVector DataOffsets; + ImVector Keys; + ImVector KeyCounts; + ImVector KeyOffsets; + ImVector Tables; + ImVector TableSizes; + ImVector TableOffsets; ImGuiTextBuffer Text; ImVector TextOffsets; + ImVector Quals; ImGuiStorage Map; int Count; ImPlotColormapData() { Count = 0; } - int Append(const char* name, const ImU32* data, int size) { - if (Lookup(name) != -1) + int Append(const char* name, const ImU32* keys, int count, bool qual) { + if (GetIndex(name) != -1) return -1; - DataOffsets.push_back(Data.size()); - DataSizes.push_back(size); - Data.reserve(Data.size()+size); - for (int i = 0; i < size; ++i) - Data.push_back(data[i]); + KeyOffsets.push_back(Keys.size()); + KeyCounts.push_back(count); + Keys.reserve(Keys.size()+count); + for (int i = 0; i < count; ++i) + Keys.push_back(keys[i]); TextOffsets.push_back(Text.size()); Text.append(name, name + strlen(name) + 1); - ImGuiID key = ImHashStr(name); - int idx = Count; - Map.SetInt(key,idx); - Count++; -#ifdef IMPLOT_USE_COLORMAP_TABLES - int table_size = 255 * (size-1) + 1; - TableOffsets.push_back(Table.size()); - TableSizes.push_back(table_size); - Table.resize(Table.size()+table_size); - RegenTable(idx); -#endif - return idx; + Quals.push_back(qual); + ImGuiID id = ImHashStr(name); + int idx = Count++; + Map.SetInt(id,idx); + _AppendTable(idx); + return idx; } - ImPlotColormap Append(const char* name, const ImVec4* data, int size) { - if (Lookup(name) != -1) - return -1; - DataOffsets.push_back(Data.size()); - DataSizes.push_back(size); - Data.reserve(Data.size()+size); - for (int i = 0; i < size; ++i) - Data.push_back(ImGui::ColorConvertFloat4ToU32(data[i])); - TextOffsets.push_back(Text.size()); - Text.append(name, name + strlen(name) + 1); - ImGuiID key = ImHashStr(name); - int idx = Count; - Map.SetInt(key,idx); - Count++; -#ifdef IMPLOT_USE_COLORMAP_TABLES - int table_size = 255 * (size-1) + 1; - TableOffsets.push_back(Table.size()); - TableSizes.push_back(table_size); - Table.resize(Table.size()+table_size); - RegenTable(idx); -#endif - return idx; - } - -#ifdef IMPLOT_USE_COLORMAP_TABLES - - ImVector Table; - ImVector TableSizes; - ImVector TableOffsets; - - void RegenTable(ImPlotColormap cmap) { - const ImU32* data = GetData(cmap); - const int size = GetSize(cmap); - const int off = TableOffsets[cmap]; - int j = 0; - for (int i = 0; i < size-1; ++i) { - for (int s = 0; s < 255; ++s) { - ImU32 a = data[i]; - ImU32 b = data[i+1]; - ImU32 c = ImMixU32(a,b,s); - Table[off + j++] = c; + void _AppendTable(ImPlotColormap cmap) { + int key_count = GetKeyCount(cmap); + const ImU32* keys = GetKeys(cmap); + int off = Tables.size(); + TableOffsets.push_back(off); + if (IsQual(cmap)) { + Tables.reserve(key_count); + for (int i = 0; i < key_count; ++i) + Tables.push_back(keys[i]); + TableSizes.push_back(key_count); + } + else { + int max_size = 255 * (key_count-1) + 1; + Tables.reserve(off + max_size); + ImU32 last = keys[0]; + Tables.push_back(last); + int n = 1; + for (int i = 0; i < key_count-1; ++i) { + for (int s = 0; s < 255; ++s) { + ImU32 a = keys[i]; + ImU32 b = keys[i+1]; + ImU32 c = ImMixU32(a,b,s); + if (c != last) { + Tables.push_back(c); + last = c; + n++; + } + } + } + ImU32 c = keys[key_count-1]; + if (c != last) { + Tables.push_back(c); + n++; } + TableSizes.push_back(n); } - Table[off+j] = data[size-1]; } - inline ImU32 Lerp(ImPlotColormap cmap, float t) { - ImU32* table = &Table[TableOffsets[cmap]]; - int size = TableSizes[cmap]; - int idx = (int)((size-1)*t); - return table[idx]; + void RebuildTables() { + Tables.resize(0); + TableSizes.resize(0); + TableOffsets.resize(0); + for (int i = 0; i < Count; ++i) + _AppendTable(i); } - inline void SetColor(ImPlotColormap cmap, int idx, ImU32 value) { - Data[DataOffsets[cmap]+idx] = value; - RegenTable(cmap); + inline bool IsQual(ImPlotColormap cmap) const { return Quals[cmap]; } + inline const char* GetName(ImPlotColormap cmap) const { return Text.Buf.Data + TextOffsets[cmap]; } + inline ImPlotColormap GetIndex(const char* name) const { ImGuiID key = ImHashStr(name); return Map.GetInt(key,-1); } + + inline const ImU32* GetKeys(ImPlotColormap cmap) const { return &Keys[KeyOffsets[cmap]]; } + inline int GetKeyCount(ImPlotColormap cmap) const { return KeyCounts[cmap]; } + inline ImU32 GetKeyColor(ImPlotColormap cmap, int idx) const { return Keys[KeyOffsets[cmap]+idx]; } + inline void SetKeyColor(ImPlotColormap cmap, int idx, ImU32 value) { Keys[KeyOffsets[cmap]+idx] = value; RebuildTables(); } + + inline const ImU32* GetTable(ImPlotColormap cmap) const { return &Tables[TableOffsets[cmap]]; } + inline int GetTableSize(ImPlotColormap cmap) const { return TableSizes[cmap]; } + inline ImU32 GetTableColor(ImPlotColormap cmap, int idx) const { return Tables[TableOffsets[cmap]+idx]; } + + inline ImU32 LerpTable(ImPlotColormap cmap, float t) const { + int off = TableOffsets[cmap]; + int siz = TableSizes[cmap]; + int idx = Quals[cmap] ? ImClamp((int)(siz*t),0,siz-1) : (int)((siz - 1) * t + 0.5f); + return Tables[off + idx]; } - -#else - inline ImU32 Lerp(ImPlotColormap cmap, float t) { return ImLerpU32(GetData(cmap),GetSize(cmap),t); } - inline void SetColor(ImPlotColormap cmap, int idx, ImU32 value) { Data[DataOffsets[cmap]+idx] = value; } -#endif - - inline const ImU32* GetData(ImPlotColormap cmap) const { return &Data[DataOffsets[cmap]]; } - inline ImU32* GetData(ImPlotColormap cmap) { return &Data[DataOffsets[cmap]]; } - inline int GetSize(ImPlotColormap cmap) { return DataSizes[cmap]; } - inline const char* GetName(ImPlotColormap cmap) { return Text.Buf.Data + TextOffsets[cmap]; } - inline ImU32 GetColor(ImPlotColormap cmap, int idx) { return Data[DataOffsets[cmap]+idx]; } - inline ImPlotColormap Lookup(const char* name) { ImGuiID key = ImHashStr(name); return Map.GetInt(key,-1); } - + }; // ImPlotPoint with positive/negative error values @@ -1070,7 +1043,10 @@ IMPLOT_API ImU32 GetColormapColorU32(int idx); // Returns the next unused colormap color and advances the colormap. Can be used to skip colors if desired. IMPLOT_API ImU32 NextColormapColorU32(); // Linearly interpolates a color from the current colormap given t between 0 and 1. -IMPLOT_API ImU32 LerpColormapU32(float t); +IMPLOT_API ImU32 SampleColormapU32(float t); + +// Render a colormap bar +IMPLOT_API void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed, bool continuous); //----------------------------------------------------------------------------- // [SECTION] Math and Misc Utils diff --git a/implot_items.cpp b/implot_items.cpp index 9303bc75..71160c52 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1815,7 +1815,7 @@ struct GetterHeatmap { rect.Max.x = p.x + HalfSize.x; rect.Max.y = p.y + HalfSize.y; const float t = ImClamp((float)ImRemap01(val, ScaleMin, ScaleMax),0.0f,1.0f); - rect.Color = GImPlot->ColormapData.Lerp(GImPlot->Style.Colormap, t); + rect.Color = GImPlot->ColormapData.LerpTable(GImPlot->Style.Colormap, t); return rect; } const T* const Values; @@ -1858,7 +1858,7 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value sprintf(buff, fmt, values[i]); ImVec2 size = ImGui::CalcTextSize(buff); double t = ImClamp(ImRemap01((double)values[i], scale_min, scale_max),0.0,1.0); - ImVec4 color = LerpColormap((float)t); + ImVec4 color = SampleColormap((float)t); ImU32 col = CalcTextColor(color); DrawList.AddText(px - size * 0.5f, col, buff); i++; From a44666c9ddc138d8fb655a3ade281d53318c1c56 Mon Sep 17 00:00:00 2001 From: epezent Date: Wed, 17 Mar 2021 06:43:09 -0500 Subject: [PATCH 19/19] finish PR --- implot.cpp | 316 +++++++++++++++++++++++++++++++--------------- implot.h | 211 +++++++++++++++++++++---------- implot_demo.cpp | 139 ++++++++++---------- implot_internal.h | 93 ++++++++------ implot_items.cpp | 17 ++- 5 files changed, 500 insertions(+), 276 deletions(-) diff --git a/implot.cpp b/implot.cpp index e204b56b..6fc47e54 100644 --- a/implot.cpp +++ b/implot.cpp @@ -1,6 +1,6 @@ // MIT License -// Copyright (c) 2020 Evan Pezent +// Copyright (c) 2021 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -32,6 +32,7 @@ When you are not sure about a old symbol or function name, try using the Search/ You can read releases logs https://github.com/epezent/implot/releases for more details. - 2021/03/08 (0.9) - SetColormap and PushColormap(ImVec4*) were removed. Use AddColormap for custom colormap support. LerpColormap was changed to SampleColormap. + ShowColormapScale was changed to ColormapScale and requires additional arguments. - 2021/03/07 (0.9) - The signature of ShowColormapScale was modified to accept a ImVec2 size. - 2021/02/28 (0.9) - BeginLegendDragDropSource was changed to BeginDragDropSourceItem with a number of other drag and drop improvements. - 2021/01/18 (0.9) - The default behavior for opening context menus was change from double right-click to single right-click. ImPlotInputMap and related functions were moved @@ -123,7 +124,7 @@ ImPlotStyle::ImPlotStyle() { LabelPadding = ImVec2(5,5); LegendPadding = ImVec2(10,10); LegendInnerPadding = ImVec2(5,5); - LegendSpacing = ImVec2(0,0); + LegendSpacing = ImVec2(5,0); MousePosPadding = ImVec2(10,10); AnnotationPadding = ImVec2(2,2); FitPadding = ImVec2(0,0); @@ -387,20 +388,27 @@ void SetCurrentContext(ImPlotContext* ctx) { } #define IMPLOT_APPEND_CMAP(name, qual) ctx->ColormapData.Append(#name, name, sizeof(name)/sizeof(ImU32), qual) +#define IM_RGB(r,g,b) IM_COL32(r,g,b,255) void Initialize(ImPlotContext* ctx) { Reset(ctx); - const ImU32 Deep[] = {4289753676, 4283598045, 4285048917, 4283584196, 4289950337, 4284512403, 4291005402, 4287401100, 4285839820, 4291671396 }; - const ImU32 Dark[] = {4280031972, 4290281015, 4283084621, 4288892568, 4278222847, 4281597951, 4280833702, 4290740727, 4288256409 }; - const ImU32 Pastel[] = {4289639675, 4293119411, 4291161036, 4293184478, 4289124862, 4291624959, 4290631909, 4293712637, 4294111986 }; - const ImU32 Paired[] = {4293119554, 4290017311, 4287291314, 4281114675, 4288256763, 4280031971, 4285513725, 4278222847, 4292260554, 4288298346, 4288282623, 4280834481}; - const ImU32 Viridis[] = {4283695428, 4285867080, 4287054913, 4287455029, 4287526954, 4287402273, 4286883874, 4285579076, 4283552122, 4280737725, 4280674301 }; - const ImU32 Plasma[] = {4287039501, 4288480321, 4289200234, 4288941455, 4287638193, 4286072780, 4284638433, 4283139314, 4281771772, 4280667900, 4280416752 }; - const ImU32 Hot[] = {4278190144, 4278190208, 4278190271, 4278190335, 4278206719, 4278223103, 4278239231, 4278255615, 4283826175, 4289396735, 4294967295 }; - const ImU32 Cool[] = {4294967040, 4294960666, 4294954035, 4294947661, 4294941030, 4294934656, 4294928025, 4294921651, 4294915020, 4294908646, 4294902015 }; - const ImU32 Pink[] = {4278190154, 4282532475, 4284308894, 4285690554, 4286879686, 4287870160, 4288794330, 4289651940, 4291685869, 4293392118, 4294967295 }; - const ImU32 Jet[] = {4289331200, 4294901760, 4294923520, 4294945280, 4294967040, 4289396565, 4283826090, 4278255615, 4278233855, 4278212095, 4278190335 }; + const ImU32 Deep[] = {4289753676, 4283598045, 4285048917, 4283584196, 4289950337, 4284512403, 4291005402, 4287401100, 4285839820, 4291671396 }; + const ImU32 Dark[] = {4280031972, 4290281015, 4283084621, 4288892568, 4278222847, 4281597951, 4280833702, 4290740727, 4288256409 }; + const ImU32 Pastel[] = {4289639675, 4293119411, 4291161036, 4293184478, 4289124862, 4291624959, 4290631909, 4293712637, 4294111986 }; + const ImU32 Paired[] = {4293119554, 4290017311, 4287291314, 4281114675, 4288256763, 4280031971, 4285513725, 4278222847, 4292260554, 4288298346, 4288282623, 4280834481}; + const ImU32 Viridis[] = {4283695428, 4285867080, 4287054913, 4287455029, 4287526954, 4287402273, 4286883874, 4285579076, 4283552122, 4280737725, 4280674301 }; + const ImU32 Plasma[] = {4287039501, 4288480321, 4289200234, 4288941455, 4287638193, 4286072780, 4284638433, 4283139314, 4281771772, 4280667900, 4280416752 }; + const ImU32 Hot[] = {4278190144, 4278190208, 4278190271, 4278190335, 4278206719, 4278223103, 4278239231, 4278255615, 4283826175, 4289396735, 4294967295 }; + const ImU32 Cool[] = {4294967040, 4294960666, 4294954035, 4294947661, 4294941030, 4294934656, 4294928025, 4294921651, 4294915020, 4294908646, 4294902015 }; + const ImU32 Pink[] = {4278190154, 4282532475, 4284308894, 4285690554, 4286879686, 4287870160, 4288794330, 4289651940, 4291685869, 4293392118, 4294967295 }; + const ImU32 Jet[] = {4289331200, 4294901760, 4294923520, 4294945280, 4294967040, 4289396565, 4283826090, 4278255615, 4278233855, 4278212095, 4278190335 }; + const ImU32 Twilight[] = {IM_RGB(226,217,226),IM_RGB(166,191,202),IM_RGB(109,144,192),IM_RGB(95,88,176),IM_RGB(83,30,124),IM_RGB(47,20,54),IM_RGB(100,25,75),IM_RGB(159,60,80),IM_RGB(192,117,94),IM_RGB(208,179,158),IM_RGB(226,217,226)}; + const ImU32 RdBu[] = {IM_RGB(103,0,31),IM_RGB(178,24,43),IM_RGB(214,96,77),IM_RGB(244,165,130),IM_RGB(253,219,199),IM_RGB(247,247,247),IM_RGB(209,229,240),IM_RGB(146,197,222),IM_RGB(67,147,195),IM_RGB(33,102,172),IM_RGB(5,48,97)}; + const ImU32 BrBG[] = {IM_RGB(84,48,5),IM_RGB(140,81,10),IM_RGB(191,129,45),IM_RGB(223,194,125),IM_RGB(246,232,195),IM_RGB(245,245,245),IM_RGB(199,234,229),IM_RGB(128,205,193),IM_RGB(53,151,143),IM_RGB(1,102,94),IM_RGB(0,60,48)}; + const ImU32 PiYG[] = {IM_RGB(142,1,82),IM_RGB(197,27,125),IM_RGB(222,119,174),IM_RGB(241,182,218),IM_RGB(253,224,239),IM_RGB(247,247,247),IM_RGB(230,245,208),IM_RGB(184,225,134),IM_RGB(127,188,65),IM_RGB(77,146,33),IM_RGB(39,100,25)}; + const ImU32 Spectral[] = {IM_RGB(158,1,66),IM_RGB(213,62,79),IM_RGB(244,109,67),IM_RGB(253,174,97),IM_RGB(254,224,139),IM_RGB(255,255,191),IM_RGB(230,245,152),IM_RGB(171,221,164),IM_RGB(102,194,165),IM_RGB(50,136,189),IM_RGB(94,79,162)}; + const ImU32 Greys[] = {IM_COL32_WHITE, IM_COL32_BLACK }; IMPLOT_APPEND_CMAP(Deep, true); IMPLOT_APPEND_CMAP(Dark, true); @@ -412,6 +420,13 @@ void Initialize(ImPlotContext* ctx) { IMPLOT_APPEND_CMAP(Cool, false); IMPLOT_APPEND_CMAP(Pink, false); IMPLOT_APPEND_CMAP(Jet, false); + IMPLOT_APPEND_CMAP(Twilight, false); + IMPLOT_APPEND_CMAP(RdBu, false); + IMPLOT_APPEND_CMAP(BrBG, false); + IMPLOT_APPEND_CMAP(PiYG, false); + IMPLOT_APPEND_CMAP(Spectral, false); + IMPLOT_APPEND_CMAP(Greys, false); + } void Reset(ImPlotContext* ctx) { @@ -1361,7 +1376,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y1_label, con } // AXIS STATES ------------------------------------------------------------ - plot.XAxis.HasRange = gp.NextPlotData.HasXRange; plot.XAxis.RangeCond = gp.NextPlotData.XRangeCond; plot.XAxis.Present = true; + plot.XAxis.HasRange = gp.NextPlotData.HasXRange; plot.XAxis.RangeCond = gp.NextPlotData.XRangeCond; plot.XAxis.Present = true; plot.YAxis[0].HasRange = gp.NextPlotData.HasYRange[0]; plot.YAxis[0].RangeCond = gp.NextPlotData.YRangeCond[0]; plot.YAxis[0].Present = true; plot.YAxis[1].HasRange = gp.NextPlotData.HasYRange[1]; plot.YAxis[1].RangeCond = gp.NextPlotData.YRangeCond[1]; plot.YAxis[1].Present = ImHasFlag(plot.Flags, ImPlotFlags_YAxis2); plot.YAxis[2].HasRange = gp.NextPlotData.HasYRange[2]; plot.YAxis[2].RangeCond = gp.NextPlotData.YRangeCond[2]; plot.YAxis[2].Present = ImHasFlag(plot.Flags, ImPlotFlags_YAxis3); @@ -1382,8 +1397,8 @@ bool BeginPlot(const char* title, const char* x_label, const char* y1_label, con for (int i = 0; i < IMPLOT_Y_AXES; ++i) plot.YAxis[i].Constrain(); - // constain equal axes for primary x and y if not approximately equal - // constains x to y since x pixel size depends on y labels width, and causes feedback loops in opposite case + // constrain equal axes for primary x and y if not approximately equal + // constrains x to y since x pixel size depends on y labels width, and causes feedback loops in opposite case if (ImHasFlag(plot.Flags, ImPlotFlags_Equal)) { double xar = plot.XAxis.GetAspect(); double yar = plot.YAxis[0].GetAspect(); @@ -1461,7 +1476,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y1_label, con // plot bb // (1) calc top/bot padding and plot height - ImVec2 title_size = ImVec2(0.0f, 0.0f); + ImVec2 title_size(0.0f, 0.0f); const float txt_height = ImGui::GetTextLineHeight(); if (!ImHasFlag(plot.Flags, ImPlotFlags_NoTitle)){ title_size = ImGui::CalcTextSize(title, NULL, true); @@ -1522,12 +1537,10 @@ bool BeginPlot(const char* title, const char* x_label, const char* y1_label, con // axis label reference gp.YAxisReference[0] = plot.PlotRect.Min.x; gp.YAxisReference[1] = plot.PlotRect.Max.x; - gp.YAxisReference[2] = !plot.YAxis[1].Present - ? plot.PlotRect.Max.x - : gp.YAxisReference[1] - + (plot.YAxis[1].IsLabeled() ? gp.Style.LabelPadding.x + gp.YTicks[1].MaxWidth : 0) - + (show_y2_label ? txt_height + gp.Style.LabelPadding.x : 0) - + gp.Style.LabelPadding.x + gp.Style.MinorTickLen.y; + gp.YAxisReference[2] = !plot.YAxis[1].Present ? plot.PlotRect.Max.x : gp.YAxisReference[1] + + (plot.YAxis[1].IsLabeled() ? gp.Style.LabelPadding.x + gp.YTicks[1].MaxWidth : 0) + + (show_y2_label ? txt_height + gp.Style.LabelPadding.x : 0) + + gp.Style.LabelPadding.x + gp.Style.MinorTickLen.y; // y axis regions bb and hover plot.YAxis[0].HoverRect = ImRect(ImVec2(plot.AxesRect.Min.x, plot.PlotRect.Min.y), ImVec2(plot.PlotRect.Min.x, plot.PlotRect.Max.y)); @@ -1914,7 +1927,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y1_label, con // render title if (title_size.x > 0.0f && !ImHasFlag(plot.Flags, ImPlotFlags_NoTitle)) { ImU32 col = GetStyleColorU32(ImPlotCol_TitleText); - const char* title_end = ImGui::FindRenderedTextEnd(title, NULL); + const char* title_end = ImGui::FindRenderedTextEnd(title); DrawList.AddText(ImVec2(plot.CanvasRect.GetCenter().x - title_size.x * 0.5f, plot.CanvasRect.Min.y),col,title,title_end); } @@ -2912,7 +2925,7 @@ bool DragPoint(const char* id, double* x, double* y, bool show_label, const ImVe ImGui::GetCurrentWindow()->DC.CursorPos = old_cursor_pos; if (ImGui::IsItemHovered() || ImGui::IsItemActive()) { gp.CurrentPlot->PlotHovered = false; - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); + // ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); if (show_label) { ImVec2 label_pos = pos + ImVec2(16 * GImGui->Style.MouseCursorScale, 8 * GImGui->Style.MouseCursorScale); char buff1[32]; @@ -3075,6 +3088,18 @@ void ItemIcon(ImU32 col) { ImGui::Dummy(size); } +void ColormapIcon(ImPlotColormap cmap) { + ImPlotContext& gp = *GImPlot; + const float txt_size = ImGui::GetTextLineHeight(); + ImVec2 size(txt_size-4,txt_size); + ImGuiWindow* window = ImGui::GetCurrentWindow(); + ImVec2 pos = window->DC.CursorPos; + ImRect rect(pos+ImVec2(0,2),pos+size-ImVec2(0,2)); + ImDrawList& DrawList = *ImGui::GetWindowDrawList(); + RenderColorBar(gp.ColormapData.GetKeys(cmap),gp.ColormapData.GetKeyCount(cmap),DrawList,rect,false,false,!gp.ColormapData.IsQual(cmap)); + ImGui::Dummy(size); +} + //----------------------------------------------------------------------------- void SetLegendLocation(ImPlotLocation location, ImPlotOrientation orientation, bool outside) { @@ -3188,6 +3213,7 @@ void PushStyleColor(ImPlotCol idx, const ImVec4& col) { void PopStyleColor(int count) { ImPlotContext& gp = *GImPlot; + IM_ASSERT_USER_ERROR(count <= gp.ColorModifiers.Size, "You can't pop more modifiers than have been pushed!"); while (count > 0) { ImGuiColorMod& backup = gp.ColorModifiers.back(); @@ -3243,6 +3269,7 @@ void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) void PopStyleVar(int count) { ImPlotContext& gp = *GImPlot; + IM_ASSERT_USER_ERROR(count <= gp.StyleModifiers.Size, "You can't pop more modifiers than have been pushed!"); while (count > 0) { ImGuiStyleMod& backup = gp.StyleModifiers.back(); const ImPlotStyleVarInfo* info = GetPlotStyleVarInfo(backup.VarIdx); @@ -3284,6 +3311,21 @@ ImPlotColormap AddColormap(const char* name, const ImU32* colormap, int size, b return gp.ColormapData.Append(name, colormap, size, qual); } +int GetColormapCount() { + ImPlotContext& gp = *GImPlot; + return gp.ColormapData.Count; +} + +const char* GetColormapName(ImPlotColormap colormap) { + ImPlotContext& gp = *GImPlot; + return gp.ColormapData.GetName(colormap); +} + +ImPlotColormap GetColormapIndex(const char* name) { + ImPlotContext& gp = *GImPlot; + return gp.ColormapData.GetIndex(name); +} + void PushColormap(ImPlotColormap colormap) { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(colormap >= 0 && colormap < gp.ColormapData.Count, "The colormap index is invalid!"); @@ -3300,6 +3342,7 @@ void PushColormap(const char* name) { void PopColormap(int count) { ImPlotContext& gp = *GImPlot; + IM_ASSERT_USER_ERROR(count <= gp.ColormapModifiers.Size, "You can't pop more modifiers than have been pushed!"); while (count > 0) { const ImPlotColormap& backup = gp.ColormapModifiers.back(); gp.Style.Colormap = backup; @@ -3308,51 +3351,47 @@ void PopColormap(int count) { } } -int GetColormapCount() { +ImU32 NextColormapColorU32() { ImPlotContext& gp = *GImPlot; - return gp.ColormapData.Count; + IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "NextColormapColor() needs to be called between BeginPlot() and EndPlot()!"); + int idx = gp.CurrentPlot->ColormapIdx % gp.ColormapData.GetKeyCount(gp.Style.Colormap); + ImU32 col = gp.ColormapData.GetKeyColor(gp.Style.Colormap, idx); + gp.CurrentPlot->ColormapIdx++; + return col; } -const char* GetColormapName(ImPlotColormap colormap) { - ImPlotContext& gp = *GImPlot; - return gp.ColormapData.GetName(colormap); +ImVec4 NextColormapColor() { + return ImGui::ColorConvertU32ToFloat4(NextColormapColorU32()); } -int GetColormapSize() { +int GetColormapSize(ImPlotColormap cmap) { ImPlotContext& gp = *GImPlot; + cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; + IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); return gp.ColormapData.GetKeyCount(gp.Style.Colormap); } -ImU32 GetColormapColorU32(int idx) { +ImU32 GetColormapColorU32(int idx, ImPlotColormap cmap) { ImPlotContext& gp = *GImPlot; - idx = idx % gp.ColormapData.GetKeyCount(gp.Style.Colormap); - return gp.ColormapData.GetKeyColor(gp.Style.Colormap, idx); -} - -ImVec4 GetColormapColor(int idx) { - return ImGui::ColorConvertU32ToFloat4(GetColormapColorU32(idx)); -} - -ImU32 NextColormapColorU32() { - ImPlotContext& gp = *GImPlot; - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "NextColormapColor() needs to be called between BeginPlot() and EndPlot()!"); - int idx = gp.CurrentPlot->ColormapIdx % gp.ColormapData.GetKeyCount(gp.Style.Colormap); - ImU32 col = gp.ColormapData.GetKeyColor(gp.Style.Colormap, idx); - gp.CurrentPlot->ColormapIdx++; - return col; + cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; + IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); + idx = idx % gp.ColormapData.GetKeyCount(cmap); + return gp.ColormapData.GetKeyColor(cmap, idx); } -ImVec4 NextColormapColor() { - return ImGui::ColorConvertU32ToFloat4(NextColormapColorU32()); +ImVec4 GetColormapColor(int idx, ImPlotColormap cmap) { + return ImGui::ColorConvertU32ToFloat4(GetColormapColorU32(idx,cmap)); } -ImU32 SampleColormapU32(float t) { +ImU32 SampleColormapU32(float t, ImPlotColormap cmap) { ImPlotContext& gp = *GImPlot; + cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; + IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); return gp.ColormapData.LerpTable(gp.Style.Colormap,t); } -ImVec4 SampleColormap(float t) { - return ImGui::ColorConvertU32ToFloat4(SampleColormapU32(t)); +ImVec4 SampleColormap(float t, ImPlotColormap cmap) { + return ImGui::ColorConvertU32ToFloat4(SampleColormapU32(t,cmap)); } void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed, bool continuous) { @@ -3372,7 +3411,7 @@ void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const I } DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col1, col2, col2); rect.TranslateY(step); - } + } } else { const float step = bounds.GetWidth() / n; @@ -3389,34 +3428,39 @@ void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const I DrawList.AddRectFilledMultiColor(rect.Min, rect.Max, col1, col2, col2, col1); rect.TranslateX(step); } - } + } } -void ShowColormapScale(double scale_min, double scale_max, const ImVec2& size) { +void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size, ImPlotColormap cmap) { ImGuiContext &G = *GImGui; ImGuiWindow * Window = G.CurrentWindow; if (Window->SkipItems) return; - ImPlotContext& gp = *GImPlot; - gp.CTicks.Reset(); - - ImPlotRange range; - range.Min = scale_min; - range.Max = scale_max; + const ImGuiID ID = Window->GetID(label); + ImVec2 label_size(0,0); + label_size = ImGui::CalcTextSize(label,NULL,true); - const float txt_off = gp.Style.LabelPadding.x; - float bar_w = 20; + ImPlotContext& gp = *GImPlot; + cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; + IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); ImVec2 frame_size = ImGui::CalcItemSize(size, 0, gp.Style.PlotDefaultSize.y); if (frame_size.y < gp.Style.PlotMinSize.y && size.y < 0.0f) frame_size.y = gp.Style.PlotMinSize.y; + ImPlotRange range(scale_min,scale_max); + gp.CTicks.Reset(); AddTicksDefault(range, ImMax(2, (int)IM_ROUND(0.0025 * frame_size.y)), IMPLOT_SUB_DIV, gp.CTicks); + + const float txt_off = gp.Style.LabelPadding.x; + const float pad_right = txt_off + gp.CTicks.MaxWidth + (label_size.x > 0 ? txt_off + label_size.y : 0); + float bar_w = 20; + if (frame_size.x == 0) - frame_size.x = bar_w + txt_off + gp.CTicks.MaxWidth + 2 * gp.Style.PlotPadding.x; + frame_size.x = bar_w + pad_right + 2 * gp.Style.PlotPadding.x; else { - bar_w = frame_size.x - (txt_off + gp.CTicks.MaxWidth + 2 * gp.Style.PlotPadding.x); + bar_w = frame_size.x - (pad_right + 2 * gp.Style.PlotPadding.x); if (bar_w < gp.Style.MajorTickLen.y) bar_w = gp.Style.MajorTickLen.y; } @@ -3424,30 +3468,96 @@ void ShowColormapScale(double scale_min, double scale_max, const ImVec2& size) { ImDrawList &DrawList = *Window->DrawList; ImRect bb_frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); ImGui::ItemSize(bb_frame); - if (!ImGui::ItemAdd(bb_frame, 0, &bb_frame)) + if (!ImGui::ItemAdd(bb_frame, ID, &bb_frame)) return; ImGui::RenderFrame(bb_frame.Min, bb_frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, G.Style.FrameRounding); ImRect bb_grad(bb_frame.Min + gp.Style.PlotPadding, bb_frame.Min + ImVec2(bar_w + gp.Style.PlotPadding.x, frame_size.y - gp.Style.PlotPadding.y)); ImGui::PushClipRect(bb_frame.Min, bb_frame.Max, true); - - ImPlotColormap map = gp.Style.Colormap; - RenderColorBar(gp.ColormapData.GetKeys(map), gp.ColormapData.GetKeyCount(map), DrawList, bb_grad, true, true, !gp.ColormapData.IsQual(map)); - - const ImU32 col_tick = GetStyleColorU32(ImPlotCol_TitleText); + RenderColorBar(gp.ColormapData.GetKeys(cmap), gp.ColormapData.GetKeyCount(cmap), DrawList, bb_grad, true, true, !gp.ColormapData.IsQual(cmap)); + const ImU32 col_tick = GetStyleColorU32(ImPlotCol_YAxis); + const ImU32 col_text = ImGui::GetColorU32(ImGuiCol_Text); for (int i = 0; i < gp.CTicks.Size; ++i) { const float ypos = ImRemap((float)gp.CTicks.Ticks[i].PlotPos, (float)range.Max, (float)range.Min, bb_grad.Min.y, bb_grad.Max.y); const float tick_width = gp.CTicks.Ticks[i].Major ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y; const float tick_thick = gp.CTicks.Ticks[i].Major ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.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 - tick_width, ypos), col_tick, tick_thick); - DrawList.AddText(ImVec2(bb_grad.Max.x-1, ypos) + ImVec2(txt_off, -gp.CTicks.Ticks[i].LabelSize.y * 0.5f), col_tick, gp.CTicks.GetText(i)); + DrawList.AddText(ImVec2(bb_grad.Max.x-1, ypos) + ImVec2(txt_off, -gp.CTicks.Ticks[i].LabelSize.y * 0.5f), col_text, gp.CTicks.GetText(i)); + } + if (label_size.x > 0) { + ImVec2 label_pos(bb_grad.Max.x - 1 + 2*txt_off + gp.CTicks.MaxWidth, bb_grad.GetCenter().y + label_size.x*0.5f ); + const char* label_end = ImGui::FindRenderedTextEnd(label); + AddTextVertical(&DrawList,label_pos,col_text,label,label_end); } DrawList.AddRect(bb_grad.Min, bb_grad.Max, GetStyleColorU32(ImPlotCol_PlotBorder)); ImGui::PopClipRect(); } +bool ColormapSlider(const char* label, float* t, ImVec4* out, const char* format, ImPlotColormap cmap) { + *t = ImClamp(*t,0.0f,1.0f); + ImGuiContext &G = *GImGui; + ImGuiWindow * Window = G.CurrentWindow; + if (Window->SkipItems) + return false; + ImPlotContext& gp = *GImPlot; + cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; + IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); + const ImU32* keys = GImPlot->ColormapData.GetKeys(cmap); + const int count = GImPlot->ColormapData.GetKeyCount(cmap); + const bool qual = GImPlot->ColormapData.IsQual(cmap); + const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos; + const float w = ImGui::CalcItemWidth(); + const float h = ImGui::GetFrameHeight(); + const ImRect rect = ImRect(pos.x,pos.y,pos.x+w,pos.y+h); + RenderColorBar(keys,count,*ImGui::GetWindowDrawList(),rect,false,false,!qual); + const ImU32 grab = CalcTextColor(GImPlot->ColormapData.LerpTable(cmap,*t)); + // const ImU32 text = CalcTextColor(GImPlot->ColormapData.LerpTable(cmap,0.5f)); + ImGui::PushStyleColor(ImGuiCol_FrameBg,IM_COL32_BLACK_TRANS); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive,IM_COL32_BLACK_TRANS); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered,ImVec4(1,1,1,0.1f)); + ImGui::PushStyleColor(ImGuiCol_SliderGrab,grab); + ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, grab); + ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize,2); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding,0); + const bool changed = ImGui::SliderFloat(label,t,0,1,format); + ImGui::PopStyleColor(5); + ImGui::PopStyleVar(2); + if (out != NULL) + *out = ImGui::ColorConvertU32ToFloat4(GImPlot->ColormapData.LerpTable(cmap,*t)); + return changed; +} + +bool ColormapButton(const char* label, const ImVec2& size_arg, ImPlotColormap cmap) { + ImGuiContext &G = *GImGui; + const ImGuiStyle& style = G.Style; + ImGuiWindow * Window = G.CurrentWindow; + if (Window->SkipItems) + return false; + ImPlotContext& gp = *GImPlot; + cmap = cmap == IMPLOT_AUTO ? gp.Style.Colormap : cmap; + IM_ASSERT_USER_ERROR(cmap >= 0 && cmap < gp.ColormapData.Count, "Invalid colormap index!"); + const ImU32* keys = GImPlot->ColormapData.GetKeys(cmap); + const int count = GImPlot->ColormapData.GetKeyCount(cmap); + const bool qual = GImPlot->ColormapData.IsQual(cmap); + const ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos; + const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true); + ImVec2 size = ImGui::CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); + const ImRect rect = ImRect(pos.x,pos.y,pos.x+size.x,pos.y+size.y); + RenderColorBar(keys,count,*ImGui::GetWindowDrawList(),rect,false,false,!qual); + const ImU32 text = CalcTextColor(GImPlot->ColormapData.LerpTable(cmap,G.Style.ButtonTextAlign.x)); + ImGui::PushStyleColor(ImGuiCol_Button,IM_COL32_BLACK_TRANS); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered,ImVec4(1,1,1,0.1f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive,ImVec4(1,1,1,0.2f)); + ImGui::PushStyleColor(ImGuiCol_Text,text); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding,0); + const bool pressed = ImGui::Button(label,size); + ImGui::PopStyleColor(4); + ImGui::PopStyleVar(1); + return pressed; +} + //----------------------------------------------------------------------------- // Style Editor etc. @@ -3650,15 +3760,16 @@ void ShowStyleEditor(ImPlotStyle* ref) { const char* name = GetColormapName(gp.Style.Colormap); ImGui::LogText("static const ImU32 %s_Data[%d] = {\n", name, size); for (int i = 0; i < size; ++i) { - ImU32 col = GetColormapColorU32(i); + ImU32 col = GetColormapColorU32(i,gp.Style.Colormap); ImGui::LogText(" %u%s\n", col, i == size - 1 ? "" : ","); } ImGui::LogText("};\nImPlotColormap %s = ImPlot::AddColormap(\"%s\", %s_Data, %d);", name, name, name, size); ImGui::LogFinish(); } ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); - ImGui::SameLine(); HelpMarker("Export code for current Colormap."); - + ImGui::SameLine(); + static bool edit = false; + ImGui::Checkbox("Edit Mode",&edit); // built-in/added ImGui::Separator(); @@ -3667,10 +3778,10 @@ void ShowStyleEditor(ImPlotStyle* ref) { int size = gp.ColormapData.GetKeyCount(i); bool selected = i == gp.Style.Colormap; - + const char* name = GetColormapName(i); if (!selected) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.25f); - if (ImGui::Button(GetColormapName(i), ImVec2(100,0))) { + if (ImGui::Button(name, ImVec2(100,0))) { gp.Style.Colormap = i; BustItemCache(); } @@ -3678,17 +3789,23 @@ void ShowStyleEditor(ImPlotStyle* ref) { ImGui::PopStyleVar(); ImGui::SameLine(); ImGui::BeginGroup(); - for (int c = 0; c < size; ++c) { - ImGui::PushID(c); - ImVec4 col4 = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetKeyColor(i,c)); - if (ImGui::ColorEdit4("",&col4.x,ImGuiColorEditFlags_NoInputs)) { - ImU32 col32 = ImGui::ColorConvertFloat4ToU32(col4); - gp.ColormapData.SetKeyColor(i,c,col32); - BustItemCache(); + if (edit) { + for (int c = 0; c < size; ++c) { + ImGui::PushID(c); + ImVec4 col4 = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetKeyColor(i,c)); + if (ImGui::ColorEdit4("",&col4.x,ImGuiColorEditFlags_NoInputs)) { + ImU32 col32 = ImGui::ColorConvertFloat4ToU32(col4); + gp.ColormapData.SetKeyColor(i,c,col32); + BustItemCache(); + } + if ((c + 1) % 12 != 0 && c != size -1) + ImGui::SameLine(); + ImGui::PopID(); } - if ((c + 1) % 12 != 0 && c != size -1) - ImGui::SameLine(); - ImGui::PopID(); + } + else { + if (ImPlot::ColormapButton("##",ImVec2(-1,0),i)) + edit = true; } ImGui::EndGroup(); ImGui::PopID(); @@ -3697,8 +3814,9 @@ void ShowStyleEditor(ImPlotStyle* ref) { static ImVector custom; if (custom.Size == 0) { - custom.push_back(ImVec4(1,1,1,1)); - custom.push_back(ImVec4(0.5f,0.5f,0.5f,1)); + custom.push_back(ImVec4(1,0,0,1)); + custom.push_back(ImVec4(0,1,0,1)); + custom.push_back(ImVec4(0,0,1,1)); } ImGui::Separator(); ImGui::BeginGroup(); @@ -3871,28 +3989,20 @@ void ShowMetricsWindow(bool* p_popen) { if (ImGui::TreeNode("Data")) { for (int m = 0; m < gp.ColormapData.Count; ++m) { if (ImGui::TreeNode(gp.ColormapData.GetName(m))) { - const ImU32* keys = gp.ColormapData.GetKeys(m); int count = gp.ColormapData.GetKeyCount(m); int size = gp.ColormapData.GetTableSize(m); bool qual = gp.ColormapData.IsQual(m); - static float t = 0.5; ImGui::BulletText("Qualitative: %s", qual ? "true" : "false"); ImGui::BulletText("Key Count: %d", count); ImGui::BulletText("Table Size: %d", size); ImGui::Indent(); - ImGui::SetNextItemWidth(370); - ImVec2 pos = ImGui::GetCurrentWindow()->DC.CursorPos; - RenderColorBar(keys,count,*ImGui::GetWindowDrawList(),ImRect(pos.x,pos.y,pos.x+370,pos.y+ImGui::GetFrameHeight()),false,false,!qual); - ImGui::PushStyleColor(ImGuiCol_FrameBg,IM_COL32_BLACK_TRANS); - ImGui::PushStyleColor(ImGuiCol_FrameBgActive,IM_COL32_BLACK_TRANS); - ImGui::PushStyleColor(ImGuiCol_FrameBgHovered,IM_COL32_BLACK_TRANS); - ImGui::PushStyleColor(ImGuiCol_SliderGrab,IM_COL32_WHITE); - ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize,1); - ImGui::SliderFloat("##Sample",&t,0,1); - ImGui::PopStyleColor(4); - ImGui::PopStyleVar(1); + + static float t = 0.5; + ImVec4 samp; + float wid = 32 * 10 - ImGui::GetFrameHeight() - ImGui::GetStyle().ItemSpacing.x; + ImGui::SetNextItemWidth(wid); + ImPlot::ColormapSlider("##Sample",&t,&samp,"%.3f",m); ImGui::SameLine(); - ImVec4 samp = ImGui::ColorConvertU32ToFloat4(gp.ColormapData.LerpTable(m,t)); ImGui::ColorButton("Sampler",samp); ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0,0,0,0)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); @@ -3901,7 +4011,7 @@ void ShowMetricsWindow(bool* p_popen) { ImGui::PushID(m*1000+c); ImGui::ColorButton("",col,0,ImVec2(10,10)); ImGui::PopID(); - if ((c + 1) % 40 != 0 && c != size - 1) + if ((c + 1) % 32 != 0 && c != size - 1) ImGui::SameLine(); } ImGui::PopStyleVar(); diff --git a/implot.h b/implot.h index 736e60b0..8503e813 100644 --- a/implot.h +++ b/implot.h @@ -1,6 +1,6 @@ // MIT License -// Copyright (c) 2020 Evan Pezent +// Copyright (c) 2021 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -32,7 +32,7 @@ // Define attributes of all API symbols declarations (e.g. for DLL under Windows) // Using ImPlot via a shared library is not recommended, because we don't guarantee // backward nor forward ABI compatibility and also function call overhead. If you -// do use ImPlot as a DLL, be sure to call SetImGuiContext (details below). +// do use ImPlot as a DLL, be sure to call SetImGuiContext (see Miscellanous section). #ifndef IMPLOT_API #define IMPLOT_API #endif @@ -182,16 +182,22 @@ enum ImPlotMarker_ { // Built-in colormaps enum ImPlotColormap_ { - ImPlotColormap_Deep = 0, // a.k.a. seaborn deep (qual=true, n=10) (default) - ImPlotColormap_Dark = 1, // a.k.a. matplotlib "Set1" (qual=true, n=9 ) - ImPlotColormap_Pastel = 2, // a.k.a. matplotlib "Pastel1" (qual=true, n=9 ) - ImPlotColormap_Paired = 3, // a.k.a. matplotlib "Paired" (qual=true, n=12) - ImPlotColormap_Viridis = 4, // a.k.a. matplotlib "viridis" (qual=false, n=11) - ImPlotColormap_Plasma = 5, // a.k.a. matplotlib "plasma" (qual=false, n=11) - ImPlotColormap_Hot = 6, // a.k.a. matplotlib/MATLAB "hot" (qual=false, n=11) - ImPlotColormap_Cool = 7, // a.k.a. matplotlib/MATLAB "cool" (qual=false, n=11) - ImPlotColormap_Pink = 8, // a.k.a. matplotlib/MATLAB "pink" (qual=false, n=11) - ImPlotColormap_Jet = 9 // a.k.a. MATLAB "jet" (qual=false, n=11) + ImPlotColormap_Deep = 0, // a.k.a. seaborn deep (qual=true, n=10) (default) + ImPlotColormap_Dark = 1, // a.k.a. matplotlib "Set1" (qual=true, n=9 ) + ImPlotColormap_Pastel = 2, // a.k.a. matplotlib "Pastel1" (qual=true, n=9 ) + ImPlotColormap_Paired = 3, // a.k.a. matplotlib "Paired" (qual=true, n=12) + ImPlotColormap_Viridis = 4, // a.k.a. matplotlib "viridis" (qual=false, n=11) + ImPlotColormap_Plasma = 5, // a.k.a. matplotlib "plasma" (qual=false, n=11) + ImPlotColormap_Hot = 6, // a.k.a. matplotlib/MATLAB "hot" (qual=false, n=11) + ImPlotColormap_Cool = 7, // a.k.a. matplotlib/MATLAB "cool" (qual=false, n=11) + ImPlotColormap_Pink = 8, // a.k.a. matplotlib/MATLAB "pink" (qual=false, n=11) + ImPlotColormap_Jet = 9, // a.k.a. MATLAB "jet" (qual=false, n=11) + ImPlotColormap_Twilight = 10, // a.k.a. matplotlib "twilight" (qual=false, n=11) + ImPlotColormap_RdBu = 11, // red/blue, Color Brewer (qual=false, n=11) + ImPlotColormap_BrBG = 12, // brown/blue-green, Color Brewer (qual=false, n=11) + ImPlotColormap_PiYG = 13, // pink/yellow-green, Color Brewer (qual=false, n=11) + ImPlotColormap_Spectral = 14, // color spectrum, Color Brewer (qual=false, n=11) + ImPlotColormap_Greys = 15, // white/black (qual=false, n=2 ) }; // Used to position items on a plot (e.g. legends, labels, etc.) @@ -287,16 +293,16 @@ struct ImPlotStyle { ImVec2 LabelPadding; // = 5,5 padding between axes labels, tick labels, and plot edge ImVec2 LegendPadding; // = 10,10 legend padding from plot edges ImVec2 LegendInnerPadding; // = 5,5 legend inner padding from legend edges - ImVec2 LegendSpacing; // = 0,0 spacing between legend entries + ImVec2 LegendSpacing; // = 5,0 spacing between legend entries ImVec2 MousePosPadding; // = 10,10 padding between plot edge and interior mouse location text ImVec2 AnnotationPadding; // = 2,2 text padding around annotation labels ImVec2 FitPadding; // = 0,0 additional fit padding as a percentage of the fit extents (e.g. ImVec2(0.1f,0.1f) adds 10% to the fit extents of X and Y) ImVec2 PlotDefaultSize; // = 400,300 default size used when ImVec2(0,0) is passed to BeginPlot ImVec2 PlotMinSize; // = 300,225 minimum size plot frame can be when shrunk // style colors - ImVec4 Colors[ImPlotCol_COUNT]; // array of plot specific colors + ImVec4 Colors[ImPlotCol_COUNT]; // Array of styling colors. Indexable with ImPlotCol_ enums. // colormap - ImPlotColormap Colormap; // index of current colormap + ImPlotColormap Colormap; // The current colormap. Set this to either an ImPlotColormap_ enum or an index returned by AddColormap. // settings/flags bool AntiAliasedLines; // = false, enable global anti-aliasing on plot lines (overrides ImPlotFlags_AntiAliased) bool UseLocalTime; // = false, axis labels will be formatted for your timezone when ImPlotAxisFlag_Time is enabled @@ -317,22 +323,43 @@ namespace ImPlot { // Creates a new ImPlot context. Call this after ImGui::CreateContext. IMPLOT_API ImPlotContext* CreateContext(); -// Destroys an ImPlot context. Call this before ImGui::DestroyContext. NULL = destroy current context +// Destroys an ImPlot context. Call this before ImGui::DestroyContext. NULL = destroy current context. IMPLOT_API void DestroyContext(ImPlotContext* ctx = NULL); // Returns the current ImPlot context. NULL if no context has ben set. IMPLOT_API ImPlotContext* GetCurrentContext(); // Sets the current ImPlot context. IMPLOT_API void SetCurrentContext(ImPlotContext* ctx); +// Sets the current **ImGui** context. This is ONLY necessary if you are compiling +// ImPlot as a DLL (not recommended) separate from your ImGui compilation. It +// sets the global variable GImGui, which is not shared across DLL boundaries. +// See GImGui documentation in imgui.cpp for more details. +IMPLOT_API void SetImGuiContext(ImGuiContext* ctx); + //----------------------------------------------------------------------------- // Begin/End Plot //----------------------------------------------------------------------------- // Starts a 2D plotting context. If this function returns true, EndPlot() must -// be called, e.g. "if (BeginPlot(...)) { ... EndPlot(); }". #title_id must -// be unique. If you need to avoid ID collisions or don't want to display a -// title in the plot, use double hashes (e.g. "MyPlot##Hidden" or "##NoTitle"). -// If #x_label and/or #y_label are provided, axes labels will be displayed. +// be called! You are encouraged to use the following call convention: +// +// if (BeginPlot(...)) { +// ImPlot::PlotLine(...); +// EndPlot(); +// } +// +// Important notes: +// +// - #title_id must be unique to the current ImGui window. If you need to avoid ID +// collisions or don't want to display a title in the plot, use double hashes +// (e.g. "MyPlot##Hidden" or "##NoTitle"). +// - If #x_label and/or #y_label are provided, axes labels will be displayed. +// - #size is the **frame** size of the plot widget, not the plot area. The default +// size of plots (i.e. when ImVec2(0,0)) can be modified in your ImPlotStyle +// (default is 400x300). +// - Auxiliary y-axes must be enabled with ImPlotFlags_YAxis2/3 to be displayed. +// - See ImPlotFlags and ImPlotAxisFlags for more available options. + IMPLOT_API bool BeginPlot(const char* title_id, const char* x_label = NULL, const char* y_label = NULL, @@ -346,7 +373,7 @@ IMPLOT_API bool BeginPlot(const char* title_id, const char* y3_label = NULL); // Only call EndPlot() if BeginPlot() returns true! Typically called at the end -// of an if statement conditioned on BeginPlot(). +// of an if statement conditioned on BeginPlot(). See above. IMPLOT_API void EndPlot(); //----------------------------------------------------------------------------- @@ -370,9 +397,9 @@ IMPLOT_API void EndPlot(); // Vector2f data[42]; // ImPlot::PlotLine("line", &data[0].x, &data[0].y, 42, 0, sizeof(Vector2f)); // or sizeof(float)*2 // -// 2. Write a custom getter function or C++ lambda and pass it and your data to +// 2. Write a custom getter C function or C++ lambda and pass it and your data to // an ImPlot function post-fixed with a G (e.g. PlotScatterG). This has a -// slight performance cost, but probably not enough to worry about. +// slight performance cost, but probably not enough to worry about. Example: // // ImPlotPoint MyDataGetter(void* data, int idx) { // MyData* my_data = (MyData*)data; @@ -382,8 +409,17 @@ IMPLOT_API void EndPlot(); // return p // } // ... -// MyData my_data; -// ImPlot::PlotScatterG("scatter", MyDataGetter, &my_data, my_data.Size()); +// auto my_lambda = [](void*, int idx) { +// double t = idx / 999.0; +// return ImPlotPoint(t, 0.5+0.5*std::sin(2*PI*10*t)); +// }; +// ... +// if (ImPlot::BeginPlot("MyPlot")) { +// MyData my_data; +// ImPlot::PlotScatterG("scatter", MyDataGetter, &my_data, my_data.Size()); +// ImPlot::PlotLineG("line", my_lambda, nullptr, 1000); +// ImPlot::EndPlot(); +// } // // NB: All types are converted to double before plotting. You may lose information // if you try plotting extremely large 64-bit integral types. Proceed with caution! @@ -553,7 +589,7 @@ IMPLOT_API bool DragPoint(const char* id, double* x, double* y, bool show_label // The following functions MUST be called BETWEEN Begin/EndPlot! -// Set the location of the current plot's legend. +// Set the location of the current plot's legend (default = North|West). IMPLOT_API void SetLegendLocation(ImPlotLocation location, ImPlotOrientation orientation = ImPlotOrientation_Vertical, bool outside = false); // Set the location of the current plot's mouse position text (default = South|East). IMPLOT_API void SetMousePosLocation(ImPlotLocation location); @@ -600,26 +636,55 @@ IMPLOT_API void EndDragDropSource(); // Plot and Item Styling //----------------------------------------------------------------------------- +// Styling colors in ImPlot works similarly to styling colors in ImGui, but +// with one important difference. Like ImGui, all style colors are stored in an +// indexable array in ImPlotStyle. You can permanently modify these values through +// GetStyle().Colors, or temporarily modify them with Push/Pop functions below. +// However, by default all style colors in ImPlot default to a special color +// IMPLOT_AUTO_COL. The behavior of this color depends upon the style color to +// which it as applied: +// +// 1) For style colors associated with plot items (e.g. ImPlotCol_Line), +// IMPLOT_AUTO_COL tells ImPlot to color the item with the next unused +// color in the current colormap. Thus, every item will have a different +// color up to the number of colors in the colormap, at which point the +// colormap will roll over. For most use cases, you should not need to +// modify these style colors to anything but IMPLOT_COL_AUTO. You are +// probably better off changing the current colormap. However, if you +// need to explicitly color a particular item you may either Push/Pop +// the style color around the item in question, or use the SetNextXXXStyle +// API below. If you permanently set one of these style colors to a specific +// color, or forget to call Pop, then all subsequent items will be styled +// with the color you set. +// +// 2) For style colors associated with plot styling (e.g. ImPlotCol_PlotBg), +// IMPLOT_AUTO_COL tells ImPlot to set that color from color data in your +// **ImGuiStyle**. The ImGuiCol_ that these style colors default to are +// detailed above, and in general have been mapped to produce plots visually +// consistent with your current ImGui style. Of course, you are free to +// manually set these colors to whatever you like, and further can Push/Pop +// them around individual plots. + // Provides access to plot style structure for permanant modifications to colors, sizes, etc. IMPLOT_API ImPlotStyle& GetStyle(); -// Style colors for current ImGui style (default). +// Style plot colors for current ImGui style (default). IMPLOT_API void StyleColorsAuto(ImPlotStyle* dst = NULL); -// Style colors for ImGui "Classic". +// Style plot colors for ImGui "Classic". IMPLOT_API void StyleColorsClassic(ImPlotStyle* dst = NULL); -// Style colors for ImGui "Dark". +// Style plot colors for ImGui "Dark". IMPLOT_API void StyleColorsDark(ImPlotStyle* dst = NULL); -// Style colors for ImGui "Light". +// Style plot colors for ImGui "Light". IMPLOT_API void StyleColorsLight(ImPlotStyle* dst = NULL); // Use PushStyleX to temporarily modify your ImPlotStyle. The modification // will last until the matching call to PopStyleX. You MUST call a pop for // every push, otherwise you will leak memory! This behaves just like ImGui. -// Temporarily modify a plot color. Don't forget to call PopStyleColor! +// Temporarily modify a style color. Don't forget to call PopStyleColor! IMPLOT_API void PushStyleColor(ImPlotCol idx, ImU32 col); IMPLOT_API void PushStyleColor(ImPlotCol idx, const ImVec4& col); -// Undo temporary color modification. Undo multiple pushes at once by increasing count. +// Undo temporary style color modification(s). Undo multiple pushes at once by increasing count. IMPLOT_API void PopStyleColor(int count = 1); // Temporarily modify a style variable of float type. Don't forget to call PopStyleVar! @@ -628,7 +693,7 @@ IMPLOT_API void PushStyleVar(ImPlotStyleVar idx, float val); IMPLOT_API void PushStyleVar(ImPlotStyleVar idx, int val); // Temporarily modify a style variable of ImVec2 type. Don't forget to call PopStyleVar! IMPLOT_API void PushStyleVar(ImPlotStyleVar idx, const ImVec2& val); -// Undo temporary style modification. Undo multiple pushes at once by increasing count. +// Undo temporary style variable modification(s). Undo multiple pushes at once by increasing count. IMPLOT_API void PopStyleVar(int count = 1); // The following can be used to modify the style of the next plot item ONLY. They do @@ -657,62 +722,82 @@ IMPLOT_API const char* GetMarkerName(ImPlotMarker idx); // Colormaps //----------------------------------------------------------------------------- -// Item styling is based on colormaps when the relevant ImPlotCol_ is set to +// Item styling is based on colormaps when the relevant ImPlotCol_XXX is set to // IMPLOT_AUTO_COL (default). Several built-in colormaps are available. You can -// add and then push/pop or set your own colormaps as well. To permanently set -// a colormap, modify the Colormap index member of your ImPlotStyle. +// add and then push/pop your own colormaps as well. To permanently set a colormap, +// modify the Colormap index member of your ImPlotStyle. // Colormap data will be ignored and a custom color will be used if you have done one of the following: // 1) Modified an item style color in your ImPlotStyle to anything other than IMPLOT_AUTO_COL. // 2) Pushed an item style color using PushStyleColor(). -// 3) Set the next item style with a SetNextXStyle function. +// 3) Set the next item style with a SetNextXXXStyle function. // Add a new colormap. The colormap can be used by pushing either the returned index or the string name with PushColormap. // The colormap name must be unique and the size must be greater than 1. You will receive an assert otherwise! By default -// colormaps are considered to be qualitative. If you want to create a perceptually continuous colormap, set #qual=false. +// colormaps are considered to be qualitative (i.e. discrete). If you want to create a continuous colormap, set #qual=false. // This will treat the colors you provide as keys, and ImPlot will build a linearly interpolated lookup table that fills -// in the gaps. The memory footprint of this table will be ((size-1)*255+1)*4 bytes at a maximum, but likely less depending -// upon how spatially separated your colors are in RGB space. +// in the gaps. The memory footprint of this table will be exactly ((size-1)*255+1)*4 bytes. IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImVec4* cols, int size, bool qual=true); IMPLOT_API ImPlotColormap AddColormap(const char* name, const ImU32* cols, int size, bool qual=true); +// Returns the number of available colormaps. +IMPLOT_API int GetColormapCount(); +// Returns a null terminated string name for a colormap given an index. Returns NULL if index is invalid. +IMPLOT_API const char* GetColormapName(ImPlotColormap cmap); +// Returns an index number for a colormap given a valid string name. Returns -1 if name is invalid. +IMPLOT_API ImPlotColormap GetColormapIndex(const char* name); + // Temporarily switch to one of the built-in (i.e. ImPlotColormap_XXX) or user-added colormaps (i.e. a return value of AddColormap). Don't forget to call PopColormap! IMPLOT_API void PushColormap(ImPlotColormap cmap); -// Push a colormap by string name. Use the string you provided to AddColormap, or use built-in names such as "Default", "Deep", "Jet", etc. Don't forget to call PopColormap! +// Push a colormap by string name. Use built-in names such as "Default", "Deep", "Jet", etc or a string you provided to AddColormap. Don't forget to call PopColormap! IMPLOT_API void PushColormap(const char* name); -// Undo temporary colormap modification(s). +// Undo temporary colormap modification(s). Undo multiple pushes at once by increasing count. IMPLOT_API void PopColormap(int count = 1); -// Returns the size of the current colormap. -IMPLOT_API int GetColormapSize(); -// Returns a color from the Color map given an index >= 0 (modulo will be performed). -IMPLOT_API ImVec4 GetColormapColor(int idx); -// Sample a color from the current colormap given t between 0 and 1. -IMPLOT_API ImVec4 SampleColormap(float t); -// Returns the next unused colormap color and advances the colormap. Can be used to skip colors if desired. +// Returns the next color from the current colormap and advances the colormap for the current plot. +// Can also be used with no return value to skip colors if desired. You need to call this between Begin/EndPlot! IMPLOT_API ImVec4 NextColormapColor(); -// Returns the number of available colormaps. -IMPLOT_API int GetColormapCount(); -// Returns a null terminated string name for a colormap by index (built-in or user-added). -IMPLOT_API const char* GetColormapName(ImPlotColormap cmap); +// Colormap utils. If cmap = IMPLOT_AUTO (default), the current colormap is assumed. +// Pass an explicit colormap index (built-in or user added) to specify otherwise. -// Renders a vertical color scale using the current color map. Call this before or after Begin/EndPlot. -IMPLOT_API void ShowColormapScale(double scale_min, double scale_max, const ImVec2& size = ImVec2(0,0)); +// Returns the size of a colormap. +IMPLOT_API int GetColormapSize(ImPlotColormap cmap = IMPLOT_AUTO); +// Returns a color from a colormap given an index >= 0 (modulo will be performed). +IMPLOT_API ImVec4 GetColormapColor(int idx, ImPlotColormap cmap = IMPLOT_AUTO); +// Sample a color from the current colormap given t between 0 and 1. +IMPLOT_API ImVec4 SampleColormap(float t, ImPlotColormap cmap = IMPLOT_AUTO); + +// Shows a vertical color scale with linear spaced ticks using the specified color map. Use double hashes to hide label (e.g. "##NoLabel"). +IMPLOT_API void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size = ImVec2(0,0), ImPlotColormap cmap = IMPLOT_AUTO); +// Shows a horizontal slider with a colormap gradient background. Optionally returns the color sampled at t in [0 1]. +IMPLOT_API bool ColormapSlider(const char* label, float* t, ImVec4* out = NULL, const char* format = "", ImPlotColormap cmap = IMPLOT_AUTO); +// Shows a button with a colormap gradient brackground. +IMPLOT_API bool ColormapButton(const char* label, const ImVec2& size = ImVec2(0,0), ImPlotColormap cmap = IMPLOT_AUTO); + +// When items in a plot sample their color from a colormap, the color is cached and does not change +// unless explicitly overriden. Therefore, if you change the colormap after the item has already been plotted, +// item colors will not update. If you need item colors to resample the new colormap, then use this +// function to bust the cached colors. If #plot_title_id is NULL, then every item in EVERY existing plot +// will be cache busted. Otherwise only the plot specified by #plot_title_id will be busted. For the +// latter, this function must be called in the ImGui window that the plot is in. You should rarely if ever +// need this function, but it is available for applications that require runtime swaps (see Heatmaps demo). +IMPLOT_API void BustColorCache(const char* plot_title_id = NULL); //----------------------------------------------------------------------------- // Miscellaneous //----------------------------------------------------------------------------- -// Render a icon similar to those that appear in legends (nifty for data lists). +// Render icons similar to those that appear in legends (nifty for data lists). IMPLOT_API void ItemIcon(const ImVec4& col); IMPLOT_API void ItemIcon(ImU32 col); +IMPLOT_API void ColormapIcon(ImPlotColormap cmap); -// Get the plot draw list for custom rendering to the current plot area. +// Get the plot draw list for custom rendering to the current plot area. Call between Begin/EndPlot. IMPLOT_API ImDrawList* GetPlotDrawList(); -// Push clip rect for rendering to current plot area. +// Push clip rect for rendering to current plot area. Call between Begin/EndPlot. IMPLOT_API void PushPlotClipRect(); -// Pop plot clip rect. +// Pop plot clip rect. Call between Begin/EndPlot. IMPLOT_API void PopPlotClipRect(); // Shows ImPlot style selector dropdown menu. @@ -721,17 +806,11 @@ IMPLOT_API bool ShowStyleSelector(const char* label); IMPLOT_API bool ShowColormapSelector(const char* label); // Shows ImPlot style editor block (not a window). IMPLOT_API void ShowStyleEditor(ImPlotStyle* ref = NULL); -// Add basic help/info block (not a window): how to manipulate ImPlot as an end-user. +// Add basic help/info block for end users (not a window). IMPLOT_API void ShowUserGuide(); // Shows ImPlot metrics/debug information. IMPLOT_API void ShowMetricsWindow(bool* p_popen = NULL); -// Sets the current _ImGui_ context. This is ONLY necessary if you are compiling -// ImPlot as a DLL (not recommended) separate from your ImGui compilation. It -// sets the global variable GImGui, which is not shared across DLL boundaries. -// See GImGui documentation in imgui.cpp for more details. -IMPLOT_API void SetImGuiContext(ImGuiContext* ctx); - //----------------------------------------------------------------------------- // Demo (add implot_demo.cpp to your sources!) //----------------------------------------------------------------------------- diff --git a/implot_demo.cpp b/implot_demo.cpp index d3e2e95e..d76316d6 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1,6 +1,6 @@ // MIT License -// Copyright (c) 2020 Evan Pezent +// Copyright (c) 2021 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -548,11 +548,17 @@ void ShowDemoWindow(bool* p_open) { static const char* ylabels[] = {"R1","R2","R3","R4","R5","R6","R7"}; static ImPlotColormap map = ImPlotColormap_Viridis; - if (ImGui::Button("Change Colormap",ImVec2(225,0))) + if (ImPlot::ColormapButton(ImPlot::GetColormapName(map),ImVec2(225,0),map)) { map = (map + 1) % ImPlot::GetColormapCount(); + // We bust the color cache of our plots so that item colors will + // resample the new colormap in the event that they have already + // been created. See documentation in implot.h. + BustColorCache("##Heatmap1"); + BustColorCache("##Heatmap2"); + } ImGui::SameLine(); - ImGui::LabelText("##Colormap Index", "%s", ImPlot::GetColormapName(map)); + ImGui::LabelText("##Colormap Index", "%s", "Change Colormap"); ImGui::SetNextItemWidth(225); ImGui::DragFloatRange2("Min / Max",&scale_min, &scale_max, 0.01f, -20, 20); static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks; @@ -565,7 +571,7 @@ void ShowDemoWindow(bool* p_open) { ImPlot::EndPlot(); } ImGui::SameLine(); - ImPlot::ShowColormapScale(scale_min, scale_max, ImVec2(60,225)); + ImPlot::ColormapScale("##HeatScale",scale_min, scale_max, ImVec2(60,225)); ImGui::SameLine(); @@ -652,16 +658,68 @@ void ShowDemoWindow(bool* p_open) { static NormalDistribution<500000> dist1(1, 2); static NormalDistribution<500000> dist2(1, 1); double max_count = 0; - ImPlot::PushColormap("Hot"); - if (ImPlot::BeginPlot("##Hist2D",0,0,ImVec2(ImGui::GetContentRegionAvail().x-75-ImGui::GetStyle().ItemSpacing.x,0),0,ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit)) { + ImPlot::PushColormap("Twilight"); + if (ImPlot::BeginPlot("##Hist2D",0,0,ImVec2(ImGui::GetContentRegionAvail().x-100-ImGui::GetStyle().ItemSpacing.x,0),0,ImPlotAxisFlags_AutoFit,ImPlotAxisFlags_AutoFit)) { max_count = ImPlot::PlotHistogram2D("Hist2D",dist1.Data,dist2.Data,count,xybins[0],xybins[1],density2,ImPlotLimits(-6,6,-6,6)); ImPlot::EndPlot(); } ImGui::SameLine(); - ImPlot::ShowColormapScale(0,max_count,ImVec2(75,0)); + ImPlot::ColormapScale(density2 ? "Density" : "Count",0,max_count,ImVec2(100,0)); ImPlot::PopColormap(); } //------------------------------------------------------------------------- + if (ImGui::CollapsingHeader("Digital Plots")) { + ImGui::BulletText("Digital plots do not respond to Y drag and zoom, so that"); + ImGui::Indent(); + ImGui::Text("you can drag analog plots over the rising/falling digital edge."); + ImGui::Unindent(); + + static bool paused = false; + static ScrollingBuffer dataDigital[2]; + static ScrollingBuffer dataAnalog[2]; + static bool showDigital[2] = {true, false}; + static bool showAnalog[2] = {true, false}; + + char label[32]; + ImGui::Checkbox("digital_0", &showDigital[0]); ImGui::SameLine(); + ImGui::Checkbox("digital_1", &showDigital[1]); ImGui::SameLine(); + ImGui::Checkbox("analog_0", &showAnalog[0]); ImGui::SameLine(); + ImGui::Checkbox("analog_1", &showAnalog[1]); + + static float t = 0; + if (!paused) { + t += ImGui::GetIO().DeltaTime; + //digital signal values + if (showDigital[0]) + dataDigital[0].AddPoint(t, sinf(2*t) > 0.45); + if (showDigital[1]) + dataDigital[1].AddPoint(t, sinf(2*t) < 0.45); + //Analog signal values + if (showAnalog[0]) + dataAnalog[0].AddPoint(t, sinf(2*t)); + if (showAnalog[1]) + dataAnalog[1].AddPoint(t, cosf(2*t)); + } + ImPlot::SetNextPlotLimitsY(-1, 1); + ImPlot::SetNextPlotLimitsX(t - 10.0, t, paused ? ImGuiCond_Once : ImGuiCond_Always); + if (ImPlot::BeginPlot("##Digital")) { + for (int i = 0; i < 2; ++i) { + if (showDigital[i] && dataDigital[i].Data.size() > 0) { + sprintf(label, "digital_%d", i); + ImPlot::PlotDigital(label, &dataDigital[i].Data[0].x, &dataDigital[i].Data[0].y, dataDigital[i].Data.size(), dataDigital[i].Offset, 2 * sizeof(float)); + } + } + for (int i = 0; i < 2; ++i) { + if (showAnalog[i]) { + sprintf(label, "analog_%d", i); + if (dataAnalog[i].Data.size() > 0) + ImPlot::PlotLine(label, &dataAnalog[i].Data[0].x, &dataAnalog[i].Data[0].y, dataAnalog[i].Data.size(), dataAnalog[i].Offset, 2 * sizeof(float)); + } + } + ImPlot::EndPlot(); + } + } + //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Images")) { ImGui::BulletText("Below we are displaying the font texture, which is the only texture we have\naccess to in this demo."); ImGui::BulletText("Use the 'ImTextureID' type as storage to pass pointers or identifiers to your\nown texture data."); @@ -939,9 +997,9 @@ void ShowDemoWindow(bool* p_open) { } ImGui::Text("The current plot limits are: [%g,%g,%g,%g]", range.X.Min, range.X.Max, range.Y.Min, range.Y.Max); ImGui::Text("The current query limits are: [%g,%g,%g,%g]", query.X.Min, query.X.Max, query.Y.Min, query.Y.Max); - } - //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Views")) { + + ImGui::Separator(); + // mimic's soulthread's imgui_plot demo static float x_data[512]; static float y_data1[512]; @@ -960,15 +1018,15 @@ void ShowDemoWindow(bool* p_open) { ImGui::BulletText("Query the first plot to render a subview in the second plot (see above for controls)."); ImPlot::SetNextPlotLimits(0,0.01,-1,1); ImPlotAxisFlags flags = ImPlotAxisFlags_NoTickLabels; - ImPlotLimits query; + ImPlotLimits query2; if (ImPlot::BeginPlot("##View1",NULL,NULL,ImVec2(-1,150), ImPlotFlags_Query, flags, flags)) { ImPlot::PlotLine("Signal 1", x_data, y_data1, 512); ImPlot::PlotLine("Signal 2", x_data, y_data2, 512); ImPlot::PlotLine("Signal 3", x_data, y_data3, 512); - query = ImPlot::GetPlotQuery(); + query2 = ImPlot::GetPlotQuery(); ImPlot::EndPlot(); } - ImPlot::SetNextPlotLimits(query.X.Min, query.X.Max, query.Y.Min, query.Y.Max, ImGuiCond_Always); + ImPlot::SetNextPlotLimits(query2.X.Min, query2.X.Max, query2.Y.Min, query2.Y.Max, ImGuiCond_Always); if (ImPlot::BeginPlot("##View2",NULL,NULL,ImVec2(-1,150), ImPlotFlags_CanvasOnly, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations)) { ImPlot::PlotLine("Signal 1", x_data, y_data1, 512); ImPlot::PlotLine("Signal 2", x_data, y_data2, 512); @@ -984,7 +1042,7 @@ void ShowDemoWindow(bool* p_open) { ImGui::CheckboxFlags("South", (unsigned int*)&loc, ImPlotLocation_South); ImGui::SameLine(); ImGui::CheckboxFlags("West", (unsigned int*)&loc, ImPlotLocation_West); ImGui::SameLine(); ImGui::CheckboxFlags("East", (unsigned int*)&loc, ImPlotLocation_East); ImGui::SameLine(); - ImGui::Checkbox("Horizontal", &h); ImGui::SameLine(); + ImGui::Checkbox("Horizontal##2", &h); ImGui::SameLine(); ImGui::Checkbox("Outside", &o); ImGui::SliderFloat2("LegendPadding", (float*)&GetStyle().LegendPadding, 0.0f, 20.0f, "%.0f"); @@ -1247,59 +1305,6 @@ void ShowDemoWindow(bool* p_open) { ImGui::EndChild(); } //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Digital and Analog Signals")) { - - ImGui::BulletText("You can plot digital and analog signals on the same plot."); - ImGui::BulletText("Digital signals do not respond to Y drag and zoom, so that"); - ImGui::Indent(); - ImGui::Text("you can drag analog signals over the rising/falling digital edge."); - ImGui::Unindent(); - - static bool paused = false; - static ScrollingBuffer dataDigital[2]; - static ScrollingBuffer dataAnalog[2]; - static bool showDigital[2] = {true, false}; - static bool showAnalog[2] = {true, false}; - - char label[32]; - ImGui::Checkbox("digital_0", &showDigital[0]); ImGui::SameLine(); - ImGui::Checkbox("digital_1", &showDigital[1]); ImGui::SameLine(); - ImGui::Checkbox("analog_0", &showAnalog[0]); ImGui::SameLine(); - ImGui::Checkbox("analog_1", &showAnalog[1]); - - static float t = 0; - if (!paused) { - t += ImGui::GetIO().DeltaTime; - //digital signal values - if (showDigital[0]) - dataDigital[0].AddPoint(t, sinf(2*t) > 0.45); - if (showDigital[1]) - dataDigital[1].AddPoint(t, sinf(2*t) < 0.45); - //Analog signal values - if (showAnalog[0]) - dataAnalog[0].AddPoint(t, sinf(2*t)); - if (showAnalog[1]) - dataAnalog[1].AddPoint(t, cosf(2*t)); - } - ImPlot::SetNextPlotLimitsY(-1, 1); - ImPlot::SetNextPlotLimitsX(t - 10.0, t, paused ? ImGuiCond_Once : ImGuiCond_Always); - if (ImPlot::BeginPlot("##Digital")) { - for (int i = 0; i < 2; ++i) { - if (showDigital[i] && dataDigital[i].Data.size() > 0) { - sprintf(label, "digital_%d", i); - ImPlot::PlotDigital(label, &dataDigital[i].Data[0].x, &dataDigital[i].Data[0].y, dataDigital[i].Data.size(), dataDigital[i].Offset, 2 * sizeof(float)); - } - } - for (int i = 0; i < 2; ++i) { - if (showAnalog[i]) { - sprintf(label, "analog_%d", i); - if (dataAnalog[i].Data.size() > 0) - ImPlot::PlotLine(label, &dataAnalog[i].Data[0].x, &dataAnalog[i].Data[0].y, dataAnalog[i].Data.size(), dataAnalog[i].Offset, 2 * sizeof(float)); - } - } - ImPlot::EndPlot(); - } - } if (ImGui::CollapsingHeader("Tables")) { #ifdef IMGUI_HAS_TABLE static ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_RowBg; diff --git a/implot_internal.h b/implot_internal.h index bb4f99a0..78d0a21f 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -1,6 +1,6 @@ // MIT License -// Copyright (c) 2020 Evan Pezent +// Copyright (c) 2021 Evan Pezent // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -43,7 +43,7 @@ #endif //----------------------------------------------------------------------------- -// [SECTION] Macros and Constants +// [SECTION] Constants //----------------------------------------------------------------------------- // Constants can be changed unless stated otherwise. We may move some of these @@ -60,6 +60,16 @@ // Maximum allowable timestamp value 01/01/3000 @ 12:00am (UTC) (DO NOT INCREASE THIS) #define IMPLOT_MAX_TIME 32503680000 +//----------------------------------------------------------------------------- +// [SECTION] Macros +//----------------------------------------------------------------------------- + +// Split ImU32 color into RGB components [0 255] +#define IM_COL32_SPLIT_RGB(col,r,g,b) \ + ImU32 r = ((col >> IM_COL32_R_SHIFT) & 0xFF); \ + ImU32 g = ((col >> IM_COL32_G_SHIFT) & 0xFF); \ + ImU32 b = ((col >> IM_COL32_B_SHIFT) & 0xFF); + //----------------------------------------------------------------------------- // [SECTION] Forward Declarations //----------------------------------------------------------------------------- @@ -340,7 +350,7 @@ struct ImPlotColormapData { ImVector KeyOffsets; ImVector Tables; ImVector TableSizes; - ImVector TableOffsets; + ImVector TableOffsets; ImGuiTextBuffer Text; ImVector TextOffsets; ImVector Quals; @@ -364,7 +374,7 @@ struct ImPlotColormapData { int idx = Count++; Map.SetInt(id,idx); _AppendTable(idx); - return idx; + return idx; } void _AppendTable(ImPlotColormap cmap) { @@ -381,58 +391,59 @@ struct ImPlotColormapData { else { int max_size = 255 * (key_count-1) + 1; Tables.reserve(off + max_size); - ImU32 last = keys[0]; - Tables.push_back(last); - int n = 1; + // ImU32 last = keys[0]; + // Tables.push_back(last); + // int n = 1; for (int i = 0; i < key_count-1; ++i) { for (int s = 0; s < 255; ++s) { ImU32 a = keys[i]; ImU32 b = keys[i+1]; ImU32 c = ImMixU32(a,b,s); - if (c != last) { + // if (c != last) { Tables.push_back(c); - last = c; - n++; - } + // last = c; + // n++; + // } } } ImU32 c = keys[key_count-1]; - if (c != last) { + // if (c != last) { Tables.push_back(c); - n++; - } - TableSizes.push_back(n); + // n++; + // } + // TableSizes.push_back(n); + TableSizes.push_back(max_size); } } void RebuildTables() { - Tables.resize(0); - TableSizes.resize(0); + Tables.resize(0); + TableSizes.resize(0); TableOffsets.resize(0); - for (int i = 0; i < Count; ++i) - _AppendTable(i); + for (int i = 0; i < Count; ++i) + _AppendTable(i); } - inline bool IsQual(ImPlotColormap cmap) const { return Quals[cmap]; } - inline const char* GetName(ImPlotColormap cmap) const { return Text.Buf.Data + TextOffsets[cmap]; } - inline ImPlotColormap GetIndex(const char* name) const { ImGuiID key = ImHashStr(name); return Map.GetInt(key,-1); } - - inline const ImU32* GetKeys(ImPlotColormap cmap) const { return &Keys[KeyOffsets[cmap]]; } - inline int GetKeyCount(ImPlotColormap cmap) const { return KeyCounts[cmap]; } - inline ImU32 GetKeyColor(ImPlotColormap cmap, int idx) const { return Keys[KeyOffsets[cmap]+idx]; } - inline void SetKeyColor(ImPlotColormap cmap, int idx, ImU32 value) { Keys[KeyOffsets[cmap]+idx] = value; RebuildTables(); } - - inline const ImU32* GetTable(ImPlotColormap cmap) const { return &Tables[TableOffsets[cmap]]; } - inline int GetTableSize(ImPlotColormap cmap) const { return TableSizes[cmap]; } - inline ImU32 GetTableColor(ImPlotColormap cmap, int idx) const { return Tables[TableOffsets[cmap]+idx]; } - - inline ImU32 LerpTable(ImPlotColormap cmap, float t) const { + inline bool IsQual(ImPlotColormap cmap) const { return Quals[cmap]; } + inline const char* GetName(ImPlotColormap cmap) const { return cmap < Count ? Text.Buf.Data + TextOffsets[cmap] : NULL; } + inline ImPlotColormap GetIndex(const char* name) const { ImGuiID key = ImHashStr(name); return Map.GetInt(key,-1); } + + inline const ImU32* GetKeys(ImPlotColormap cmap) const { return &Keys[KeyOffsets[cmap]]; } + inline int GetKeyCount(ImPlotColormap cmap) const { return KeyCounts[cmap]; } + inline ImU32 GetKeyColor(ImPlotColormap cmap, int idx) const { return Keys[KeyOffsets[cmap]+idx]; } + inline void SetKeyColor(ImPlotColormap cmap, int idx, ImU32 value) { Keys[KeyOffsets[cmap]+idx] = value; RebuildTables(); } + + inline const ImU32* GetTable(ImPlotColormap cmap) const { return &Tables[TableOffsets[cmap]]; } + inline int GetTableSize(ImPlotColormap cmap) const { return TableSizes[cmap]; } + inline ImU32 GetTableColor(ImPlotColormap cmap, int idx) const { return Tables[TableOffsets[cmap]+idx]; } + + inline ImU32 LerpTable(ImPlotColormap cmap, float t) const { int off = TableOffsets[cmap]; int siz = TableSizes[cmap]; int idx = Quals[cmap] ? ImClamp((int)(siz*t),0,siz-1) : (int)((siz - 1) * t + 0.5f); - return Tables[off + idx]; + return Tables[off + idx]; } - + }; // ImPlotPoint with positive/negative error values @@ -543,7 +554,7 @@ struct ImPlotTickCollection { Append(tick); } - const char* GetText(int idx) { + const char* GetText(int idx) const { return TextBuffer.Buf.Data + Ticks[idx].TextOffset; } @@ -1025,9 +1036,13 @@ inline ImU32 GetStyleColorU32(ImPlotCol idx) { return ImGui::ColorConvertFloat // Draws vertical text. The position is the bottom left of the text rect. IMPLOT_API void AddTextVertical(ImDrawList *DrawList, ImVec2 pos, ImU32 col, const char* text_begin, const char* text_end = NULL); // Calculates the size of vertical text -inline ImVec2 CalcTextSizeVertical(const char *text) { ImVec2 sz = ImGui::CalcTextSize(text); return ImVec2(sz.y, sz.x); } +inline ImVec2 CalcTextSizeVertical(const char *text) { + ImVec2 sz = ImGui::CalcTextSize(text); + return ImVec2(sz.y, sz.x); +} // Returns white or black text given background color inline ImU32 CalcTextColor(const ImVec4& bg) { return (bg.x * 0.299 + bg.y * 0.587 + bg.z * 0.114) > 0.5 ? IM_COL32_BLACK : IM_COL32_WHITE; } +inline ImU32 CalcTextColor(ImU32 bg) { return CalcTextColor(ImGui::ColorConvertU32ToFloat4(bg)); } // Clamps a label position so that it fits a rect defined by Min/Max inline ImVec2 ClampLabelPos(ImVec2 pos, const ImVec2& size, const ImVec2& Min, const ImVec2& Max) { @@ -1039,11 +1054,11 @@ inline ImVec2 ClampLabelPos(ImVec2 pos, const ImVec2& size, const ImVec2& Min, c } // Returns a color from the Color map given an index >= 0 (modulo will be performed). -IMPLOT_API ImU32 GetColormapColorU32(int idx); +IMPLOT_API ImU32 GetColormapColorU32(int idx, ImPlotColormap cmap); // Returns the next unused colormap color and advances the colormap. Can be used to skip colors if desired. IMPLOT_API ImU32 NextColormapColorU32(); // Linearly interpolates a color from the current colormap given t between 0 and 1. -IMPLOT_API ImU32 SampleColormapU32(float t); +IMPLOT_API ImU32 SampleColormapU32(float t, ImPlotColormap cmap); // Render a colormap bar IMPLOT_API void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed, bool continuous); diff --git a/implot_items.cpp b/implot_items.cpp index 71160c52..371be98b 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -135,6 +135,21 @@ void BustItemCache() { } } +void BustColorCache(const char* plot_title_id) { + ImPlotContext& gp = *GImPlot; + if (plot_title_id == NULL) { + BustItemCache(); + } + else { + ImPlotPlot* plot = gp.Plots.GetByKey(ImGui::GetCurrentWindow()->GetID(plot_title_id)); + if (plot == NULL) + return; + plot->ColormapIdx = 0; + plot->Items.Clear(); + plot->LegendData.Reset(); + } +} + //----------------------------------------------------------------------------- // Begin/EndItem //----------------------------------------------------------------------------- @@ -1830,7 +1845,7 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value if (scale_min == scale_max) { ImVec2 a = transformer(bounds_min); ImVec2 b = transformer(bounds_max); - ImU32 col = GetColormapColorU32(0); + ImU32 col = GetColormapColorU32(0,gp.Style.Colormap); DrawList.AddRectFilled(a, b, col); return; }