From 8f8d7ee7aca48f93565c5fef007d364ac7d6f956 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Wed, 25 Jun 2025 22:41:17 +0200 Subject: [PATCH 01/35] feat: Add support for offsets for both the rows and the colums in the heatmaps - This is usefull for our use case where we have a circular memory buffer which we want to randomly access certain indexes as we use them - It will modify the offset regardless of if the column major flag is set or not --- implot.h | 4 ++-- implot_demo.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ implot_items.cpp | 48 ++++++++++++++++++++++++------------------ 3 files changed, 84 insertions(+), 22 deletions(-) diff --git a/implot.h b/implot.h index ae1f6000..1919f223 100644 --- a/implot.h +++ b/implot.h @@ -898,8 +898,8 @@ IMPLOT_TMP void PlotInfLines(const char* label_id, const T* values, int count, I IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, ImPlotFormatter fmt, void* fmt_data=nullptr, double angle0=90, ImPlotPieChartFlags flags=0); IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* label_fmt="%.1f", double angle0=90, ImPlotPieChartFlags flags=0); -// Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels. -IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1), ImPlotHeatmapFlags flags=0); +// Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels. The rows and the columns offset can be specified which adds a offset to the row and column indices of the values +IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1), ImPlotHeatmapFlags flags=0, int offset_rows = 0, int offset_cols=0); // Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #range is left unspecified, the min/max of #values will be used as the range. // Otherwise, outlier values outside of the range are not binned. The largest bin count or density is returned. diff --git a/implot_demo.cpp b/implot_demo.cpp index 47298d64..678a63d1 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1929,6 +1929,59 @@ void Demo_OffsetAndStride() { // offset++; uncomment for animation! } +void Demo_HeatmapOffsets() { + static float values1[5][8] = {{0.8f, 2.4f, 2.5f, 3.9f, 0.0f, 4.0f, 0.0f, 5.1f}, + {2.4f, 0.0f, 4.0f, 1.0f, 2.7f, 0.0f, 0.0f, 3.2f}, + {1.1f, 2.4f, 0.8f, 4.3f, 1.9f, 4.4f, 0.0f, 1.3f}, + {0.6f, 0.0f, 0.3f, 0.0f, 3.1f, 0.0f, 0.0f, 1.2f}, + {0.7f, 1.7f, 0.6f, 2.6f, 2.2f, 6.2f, 0.0f, 5.1f}}; + static float scale_min = 0; + static float scale_max = 6.3f; + static const char* xlabels[] = {"C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8"}; + static const char* ylabels[] = {"R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8"}; + + static ImPlotColormap map = ImPlotColormap_Viridis; + 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"); + } + + ImGui::SameLine(); + ImGui::LabelText("##Colormap Index", "%s", "Change Colormap"); + ImGui::SetNextItemWidth(225); + ImGui::DragFloatRange2("Min / Max", &scale_min, &scale_max, 0.01f, -20, 20); + + static ImPlotHeatmapFlags hm_flags = 0; + + ImGui::CheckboxFlags("Column Major", (unsigned int*)&hm_flags, ImPlotHeatmapFlags_ColMajor); + + static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks; + + ImPlot::PushColormap(map); + const bool RowMajor = (hm_flags & ImPlotHeatmapFlags_ColMajor) == ImPlotHeatmapFlags_ColMajor; + int NumRows = !RowMajor ? 5 : 8; + int NumCols = RowMajor ? 5 : 8; + static int offset_rows = 0; + static int offset_cols = 0; + ImGui::SliderInt("Offset Rows", &offset_rows, -3 * NumRows, 3 * NumRows); + ImGui::SliderInt("Offset Cols", &offset_cols, -3 * NumCols, 3 * NumCols); + if (ImPlot::BeginPlot("##Heatmap1", ImVec2(225, 225), ImPlotFlags_NoLegend | ImPlotFlags_NoMouseText)) { + ImPlot::SetupAxes(nullptr, nullptr, axes_flags, axes_flags); + ImPlot::SetupAxisTicks(ImAxis_X1, 0 + 1.0 / 14.0, 1 - 1.0 / 14.0, NumCols, xlabels); + ImPlot::SetupAxisTicks(ImAxis_Y1, 1 - 1.0 / 14.0, 0 + 1.0 / 14.0, NumRows, ylabels); + ImPlot::PlotHeatmap("heat", values1[0], NumRows, NumCols, scale_min, scale_max, "%g", ImPlotPoint(0, 0), ImPlotPoint(1, 1), hm_flags, + offset_rows, offset_cols); + ImPlot::EndPlot(); + } + ImGui::SameLine(); + ImPlot::ColormapScale("##HeatScale", scale_min, scale_max, ImVec2(60, 225)); + + ImPlot::PopColormap(); +} + //----------------------------------------------------------------------------- void Demo_CustomDataAndGetters() { @@ -2278,6 +2331,7 @@ void ShowDemoWindow(bool* p_open) { } if (ImGui::BeginTabItem("Tools")) { DemoHeader("Offset and Stride", Demo_OffsetAndStride); + DemoHeader("Heatmap Offset", Demo_HeatmapOffsets); DemoHeader("Drag Points", Demo_DragPoints); DemoHeader("Drag Lines", Demo_DragLines); DemoHeader("Drag Rects", Demo_DragRects); diff --git a/implot_items.cpp b/implot_items.cpp index f7de3465..9da687ca 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2378,11 +2378,12 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES() template struct GetterHeatmapRowMaj { - GetterHeatmapRowMaj(const T* values, int rows, int cols, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : + GetterHeatmapRowMaj(const T* values, int count, int rows, int cols, int offset, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : Values(values), - Count(rows*cols), + Count(count), Rows(rows), Cols(cols), + Offset(offset), ScaleMin(scale_min), ScaleMax(scale_max), Width(width), @@ -2393,7 +2394,7 @@ struct GetterHeatmapRowMaj { HalfSize(Width*0.5, Height*0.5) { } template IMPLOT_INLINE RectC operator()(I idx) const { - double val = (double)Values[idx]; + double val = (double)Values[(idx + Offset) % Count]; const int r = idx / Cols; const int c = idx % Cols; const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height)); @@ -2406,18 +2407,19 @@ struct GetterHeatmapRowMaj { return rect; } const T* const Values; - const int Count, Rows, Cols; + const int Count, Rows, Cols, Offset; const double ScaleMin, ScaleMax, Width, Height, XRef, YRef, YDir; const ImPlotPoint HalfSize; }; template struct GetterHeatmapColMaj { - GetterHeatmapColMaj(const T* values, int rows, int cols, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : + GetterHeatmapColMaj(const T* values, int count, int rows, int cols, int offset, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : Values(values), - Count(rows*cols), + Count(count), Rows(rows), Cols(cols), + Offset(offset), ScaleMin(scale_min), ScaleMax(scale_max), Width(width), @@ -2428,7 +2430,7 @@ struct GetterHeatmapColMaj { HalfSize(Width*0.5, Height*0.5) { } template IMPLOT_INLINE RectC operator()(I idx) const { - double val = (double)Values[idx]; + double val = (double)Values[(idx + Offset) % Count]; const int r = idx % Rows; const int c = idx / Rows; const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height)); @@ -2441,18 +2443,19 @@ struct GetterHeatmapColMaj { return rect; } const T* const Values; - const int Count, Rows, Cols; + const int Count, Rows, Cols, Offset; const double ScaleMin, ScaleMax, Width, Height, XRef, YRef, YDir; const ImPlotPoint HalfSize; }; template -void RenderHeatmap(ImDrawList& draw_list, 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, bool col_maj) { +void RenderHeatmap(ImDrawList& draw_list, 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, bool col_maj, int offset_rows, int offset_cols) { ImPlotContext& gp = *GImPlot; Transformer2 transformer; + const int count = (cols * rows); if (scale_min == 0 && scale_max == 0) { T temp_min, temp_max; - ImMinMaxArray(values,rows*cols,&temp_min,&temp_max); + ImMinMaxArray(values, count, &temp_min, &temp_max); scale_min = (double)temp_min; scale_max = (double)temp_max; } @@ -2465,12 +2468,15 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } const double yref = reverse_y ? bounds_max.y : bounds_min.y; const double ydir = reverse_y ? -1 : 1; + int offset; if (col_maj) { - GetterHeatmapColMaj getter(values, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); + offset = ImPosMod(rows * offset_cols, count) + ImPosMod(offset_rows, cols); + GetterHeatmapColMaj getter(values, count, rows, cols, offset, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); RenderPrimitives1(getter); } else { - GetterHeatmapRowMaj getter(values, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); + offset = ImPosMod(cols * offset_rows, count) + ImPosMod(offset_cols, rows); + GetterHeatmapRowMaj getter(values, count, rows, cols, offset, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); RenderPrimitives1(getter); } // labels @@ -2487,9 +2493,10 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d p.y = yref + ydir * (0.5*h + r*h); ImVec2 px = transformer(p); char buff[32]; - ImFormatString(buff, 32, fmt, values[i]); + int UsedIndex = (i + offset) % count; + ImFormatString(buff, 32, fmt, values[UsedIndex]); ImVec2 size = ImGui::CalcTextSize(buff); - double t = ImClamp(ImRemap01((double)values[i], scale_min, scale_max),0.0,1.0); + double t = ImClamp(ImRemap01((double)values[UsedIndex], scale_min, scale_max), 0.0, 1.0); ImVec4 color = SampleColormap((float)t); ImU32 col = CalcTextColor(color); draw_list.AddText(px - size * 0.5f, col, buff); @@ -2505,9 +2512,10 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d p.y = yref + ydir * (0.5*h + r*h); ImVec2 px = transformer(p); char buff[32]; - ImFormatString(buff, 32, fmt, values[i]); + int UsedIndex = (i + offset) % count; + ImFormatString(buff, 32, fmt, values[UsedIndex]); ImVec2 size = ImGui::CalcTextSize(buff); - double t = ImClamp(ImRemap01((double)values[i], scale_min, scale_max),0.0,1.0); + double t = ImClamp(ImRemap01((double)values[UsedIndex], scale_min, scale_max), 0.0, 1.0); ImVec4 color = SampleColormap((float)t); ImU32 col = CalcTextColor(color); draw_list.AddText(px - size * 0.5f, col, buff); @@ -2519,7 +2527,7 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } 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, ImPlotHeatmapFlags flags) { +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, ImPlotHeatmapFlags flags, int offset_rows, int offset_cols) { if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max))) { if (rows <= 0 || cols <= 0) { EndItem(); @@ -2527,11 +2535,11 @@ void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, doub } ImDrawList& draw_list = *GetPlotDrawList(); const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); - RenderHeatmap(draw_list, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true, col_maj); + RenderHeatmap(draw_list, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true, col_maj, offset_rows, offset_cols); EndItem(); } } -#define INSTANTIATE_MACRO(T) template IMPLOT_API 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, ImPlotHeatmapFlags flags); +#define INSTANTIATE_MACRO(T) template IMPLOT_API 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, ImPlotHeatmapFlags flags, int offset_rows, int offset_cols); CALL_INSTANTIATE_FOR_NUMERIC_TYPES() #undef INSTANTIATE_MACRO @@ -2694,7 +2702,7 @@ double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count return max_count; } ImDrawList& draw_list = *GetPlotDrawList(); - RenderHeatmap(draw_list, &bin_counts.Data[0], y_bins, x_bins, 0, max_count, nullptr, range.Min(), range.Max(), false, col_maj); + RenderHeatmap(draw_list, &bin_counts.Data[0], y_bins, x_bins, 0, max_count, nullptr, range.Min(), range.Max(), false, col_maj, 0, 0); EndItem(); } return max_count; From 1ba30eef28ea3a9a218ebf37d1fba31957200479 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Wed, 25 Jun 2025 22:51:10 +0200 Subject: [PATCH 02/35] fix: Formating of function and comment --- implot.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/implot.h b/implot.h index 1919f223..4063c915 100644 --- a/implot.h +++ b/implot.h @@ -898,8 +898,8 @@ IMPLOT_TMP void PlotInfLines(const char* label_id, const T* values, int count, I IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, ImPlotFormatter fmt, void* fmt_data=nullptr, double angle0=90, ImPlotPieChartFlags flags=0); IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* label_fmt="%.1f", double angle0=90, ImPlotPieChartFlags flags=0); -// Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels. The rows and the columns offset can be specified which adds a offset to the row and column indices of the values -IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1), ImPlotHeatmapFlags flags=0, int offset_rows = 0, int offset_cols=0); +// Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels. A row and column offset can be specified to add a offset to the row and column indices +IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1), ImPlotHeatmapFlags flags=0, int offset_rows = 0, int offset_cols = 0); // Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #range is left unspecified, the min/max of #values will be used as the range. // Otherwise, outlier values outside of the range are not binned. The largest bin count or density is returned. From a6a456632634b2255033dd0ef460b1413023b840 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Thu, 26 Jun 2025 13:12:38 +0200 Subject: [PATCH 03/35] fix: Handle the column shift in row major and row shift in column major correctly - The wrong indexes were used for the offset when calculating where to draw data with the offset added --- implot_items.cpp | 61 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/implot_items.cpp b/implot_items.cpp index 9da687ca..32f40688 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2378,12 +2378,13 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES() template struct GetterHeatmapRowMaj { - GetterHeatmapRowMaj(const T* values, int count, int rows, int cols, int offset, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : + GetterHeatmapRowMaj(const T* values, int count, int rows, int cols, int row_index_offset, int col_index_offset, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : Values(values), Count(count), Rows(rows), Cols(cols), - Offset(offset), + RowIndexOffset(row_index_offset), + ColIndexOffset(col_index_offset), ScaleMin(scale_min), ScaleMax(scale_max), Width(width), @@ -2394,9 +2395,14 @@ struct GetterHeatmapRowMaj { HalfSize(Width*0.5, Height*0.5) { } template IMPLOT_INLINE RectC operator()(I idx) const { - double val = (double)Values[(idx + Offset) % Count]; const int r = idx / Cols; const int c = idx % Cols; + int offset = RowIndexOffset; + if(c + ColIndexOffset < Cols) + offset += ColIndexOffset; + else + offset += ColIndexOffset - Cols; + double val = (double)Values[(idx + offset) % Count]; const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height)); RectC rect; rect.Pos = p; @@ -2407,19 +2413,20 @@ struct GetterHeatmapRowMaj { return rect; } const T* const Values; - const int Count, Rows, Cols, Offset; + const int Count, Rows, Cols, RowIndexOffset, ColIndexOffset; const double ScaleMin, ScaleMax, Width, Height, XRef, YRef, YDir; const ImPlotPoint HalfSize; }; template struct GetterHeatmapColMaj { - GetterHeatmapColMaj(const T* values, int count, int rows, int cols, int offset, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : + GetterHeatmapColMaj(const T* values, int count, int rows, int cols, int row_index_offset, int col_index_offset, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : Values(values), Count(count), Rows(rows), Cols(cols), - Offset(offset), + RowIndexOffset(row_index_offset), + ColIndexOffset(col_index_offset), ScaleMin(scale_min), ScaleMax(scale_max), Width(width), @@ -2430,9 +2437,14 @@ struct GetterHeatmapColMaj { HalfSize(Width*0.5, Height*0.5) { } template IMPLOT_INLINE RectC operator()(I idx) const { - double val = (double)Values[(idx + Offset) % Count]; const int r = idx % Rows; const int c = idx / Rows; + int offset = ColIndexOffset; + if(r + RowIndexOffset < Rows) + offset += RowIndexOffset; + else + offset += RowIndexOffset - Rows; + double val = (double)Values[(idx + offset) % Count]; const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height)); RectC rect; rect.Pos = p; @@ -2443,7 +2455,7 @@ struct GetterHeatmapColMaj { return rect; } const T* const Values; - const int Count, Rows, Cols, Offset; + const int Count, Rows, Cols, RowIndexOffset, ColIndexOffset; const double ScaleMin, ScaleMax, Width, Height, XRef, YRef, YDir; const ImPlotPoint HalfSize; }; @@ -2468,15 +2480,18 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } const double yref = reverse_y ? bounds_max.y : bounds_min.y; const double ydir = reverse_y ? -1 : 1; - int offset; + int row_index_offset; + int col_index_offset; if (col_maj) { - offset = ImPosMod(rows * offset_cols, count) + ImPosMod(offset_rows, cols); - GetterHeatmapColMaj getter(values, count, rows, cols, offset, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); + row_index_offset = ImPosMod(offset_rows, rows); + col_index_offset = ImPosMod(rows * offset_cols, count); + GetterHeatmapColMaj getter(values, count, rows, cols, row_index_offset, col_index_offset, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); RenderPrimitives1(getter); } else { - offset = ImPosMod(cols * offset_rows, count) + ImPosMod(offset_cols, rows); - GetterHeatmapRowMaj getter(values, count, rows, cols, offset, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); + row_index_offset = ImPosMod(cols * offset_rows, count); + col_index_offset = ImPosMod(offset_cols, cols); + GetterHeatmapRowMaj getter(values, count, rows, cols, row_index_offset, col_index_offset, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); RenderPrimitives1(getter); } // labels @@ -2493,7 +2508,12 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d p.y = yref + ydir * (0.5*h + r*h); ImVec2 px = transformer(p); char buff[32]; - int UsedIndex = (i + offset) % count; + int offset = col_index_offset; + if(r + row_index_offset < rows) + offset += row_index_offset; + else + offset += row_index_offset - rows; + int UsedIndex = (i + offset) % count; ImFormatString(buff, 32, fmt, values[UsedIndex]); ImVec2 size = ImGui::CalcTextSize(buff); double t = ImClamp(ImRemap01((double)values[UsedIndex], scale_min, scale_max), 0.0, 1.0); @@ -2512,10 +2532,15 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d p.y = yref + ydir * (0.5*h + r*h); ImVec2 px = transformer(p); char buff[32]; - int UsedIndex = (i + offset) % count; - ImFormatString(buff, 32, fmt, values[UsedIndex]); - ImVec2 size = ImGui::CalcTextSize(buff); - double t = ImClamp(ImRemap01((double)values[UsedIndex], scale_min, scale_max), 0.0, 1.0); + int offset = row_index_offset; + if(c + col_index_offset < cols) + offset += col_index_offset; + else + offset += col_index_offset - cols; + int UsedIndex = (i + offset) % count; + ImFormatString(buff, 32, fmt, values[UsedIndex]); + ImVec2 size = ImGui::CalcTextSize(buff); + double t = ImClamp(ImRemap01((double)values[UsedIndex], scale_min, scale_max), 0.0, 1.0); ImVec4 color = SampleColormap((float)t); ImU32 col = CalcTextColor(color); draw_list.AddText(px - size * 0.5f, col, buff); From 85d9efb7d7c7de3a68cd6e6eed0efa275af9e826 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Thu, 26 Jun 2025 13:29:53 +0200 Subject: [PATCH 04/35] fix: Fix some formatting on the file --- implot_items.cpp | 80 ++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/implot_items.cpp b/implot_items.cpp index 32f40688..92706150 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2378,13 +2378,13 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES() template struct GetterHeatmapRowMaj { - GetterHeatmapRowMaj(const T* values, int count, int rows, int cols, int row_index_offset, int col_index_offset, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : + GetterHeatmapRowMaj(const T* values, int count, int rows, int cols, int row_index_offset, int col_index_offset, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : Values(values), Count(count), Rows(rows), Cols(cols), - RowIndexOffset(row_index_offset), - ColIndexOffset(col_index_offset), + RowIndexOffset(row_index_offset), + ColIndexOffset(col_index_offset), ScaleMin(scale_min), ScaleMax(scale_max), Width(width), @@ -2397,12 +2397,12 @@ struct GetterHeatmapRowMaj { template IMPLOT_INLINE RectC operator()(I idx) const { const int r = idx / Cols; const int c = idx % Cols; - int offset = RowIndexOffset; - if(c + ColIndexOffset < Cols) - offset += ColIndexOffset; - else - offset += ColIndexOffset - Cols; - double val = (double)Values[(idx + offset) % Count]; + int offset = RowIndexOffset; + if (c + ColIndexOffset < Cols) + offset += ColIndexOffset; + else + offset += ColIndexOffset - Cols; + double val = (double)Values[(idx + offset) % Count]; const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height)); RectC rect; rect.Pos = p; @@ -2420,13 +2420,13 @@ struct GetterHeatmapRowMaj { template struct GetterHeatmapColMaj { - GetterHeatmapColMaj(const T* values, int count, int rows, int cols, int row_index_offset, int col_index_offset, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : + GetterHeatmapColMaj(const T* values, int count, int rows, int cols, int row_index_offset, int col_index_offset, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : Values(values), Count(count), Rows(rows), Cols(cols), - RowIndexOffset(row_index_offset), - ColIndexOffset(col_index_offset), + RowIndexOffset(row_index_offset), + ColIndexOffset(col_index_offset), ScaleMin(scale_min), ScaleMax(scale_max), Width(width), @@ -2439,12 +2439,12 @@ struct GetterHeatmapColMaj { template IMPLOT_INLINE RectC operator()(I idx) const { const int r = idx % Rows; const int c = idx / Rows; - int offset = ColIndexOffset; - if(r + RowIndexOffset < Rows) - offset += RowIndexOffset; - else - offset += RowIndexOffset - Rows; - double val = (double)Values[(idx + offset) % Count]; + int offset = ColIndexOffset; + if (r + RowIndexOffset < Rows) + offset += RowIndexOffset; + else + offset += RowIndexOffset - Rows; + double val = (double)Values[(idx + offset) % Count]; const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height)); RectC rect; rect.Pos = p; @@ -2455,7 +2455,7 @@ struct GetterHeatmapColMaj { return rect; } const T* const Values; - const int Count, Rows, Cols, RowIndexOffset, ColIndexOffset; + const int Count, Rows, Cols, RowIndexOffset, ColIndexOffset; const double ScaleMin, ScaleMax, Width, Height, XRef, YRef, YDir; const ImPlotPoint HalfSize; }; @@ -2481,17 +2481,17 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d const double yref = reverse_y ? bounds_max.y : bounds_min.y; const double ydir = reverse_y ? -1 : 1; int row_index_offset; - int col_index_offset; + int col_index_offset; if (col_maj) { - row_index_offset = ImPosMod(offset_rows, rows); - col_index_offset = ImPosMod(rows * offset_cols, count); - GetterHeatmapColMaj getter(values, count, rows, cols, row_index_offset, col_index_offset, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); + row_index_offset = ImPosMod(offset_rows, rows); + col_index_offset = ImPosMod(rows * offset_cols, count); + GetterHeatmapColMaj getter(values, count, rows, cols, row_index_offset, col_index_offset, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); RenderPrimitives1(getter); } else { - row_index_offset = ImPosMod(cols * offset_rows, count); - col_index_offset = ImPosMod(offset_cols, cols); - GetterHeatmapRowMaj getter(values, count, rows, cols, row_index_offset, col_index_offset, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); + row_index_offset = ImPosMod(cols * offset_rows, count); + col_index_offset = ImPosMod(offset_cols, cols); + GetterHeatmapRowMaj getter(values, count, rows, cols, row_index_offset, col_index_offset, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); RenderPrimitives1(getter); } // labels @@ -2508,12 +2508,12 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d p.y = yref + ydir * (0.5*h + r*h); ImVec2 px = transformer(p); char buff[32]; - int offset = col_index_offset; - if(r + row_index_offset < rows) - offset += row_index_offset; - else - offset += row_index_offset - rows; - int UsedIndex = (i + offset) % count; + int offset = col_index_offset; + if (r + row_index_offset < rows) + offset += row_index_offset; + else + offset += row_index_offset - rows; + int UsedIndex = (i + offset) % count; ImFormatString(buff, 32, fmt, values[UsedIndex]); ImVec2 size = ImGui::CalcTextSize(buff); double t = ImClamp(ImRemap01((double)values[UsedIndex], scale_min, scale_max), 0.0, 1.0); @@ -2532,15 +2532,15 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d p.y = yref + ydir * (0.5*h + r*h); ImVec2 px = transformer(p); char buff[32]; - int offset = row_index_offset; - if(c + col_index_offset < cols) - offset += col_index_offset; + int offset = row_index_offset; + if (c + col_index_offset < cols) + offset += col_index_offset; else - offset += col_index_offset - cols; - int UsedIndex = (i + offset) % count; - ImFormatString(buff, 32, fmt, values[UsedIndex]); - ImVec2 size = ImGui::CalcTextSize(buff); - double t = ImClamp(ImRemap01((double)values[UsedIndex], scale_min, scale_max), 0.0, 1.0); + offset += col_index_offset - cols; + int UsedIndex = (i + offset) % count; + ImFormatString(buff, 32, fmt, values[UsedIndex]); + ImVec2 size = ImGui::CalcTextSize(buff); + double t = ImClamp(ImRemap01((double)values[UsedIndex], scale_min, scale_max), 0.0, 1.0); ImVec4 color = SampleColormap((float)t); ImU32 col = CalcTextColor(color); draw_list.AddText(px - size * 0.5f, col, buff); From d7bf0661492369f8fa700014b9ba09ac0adad68b Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Thu, 26 Jun 2025 13:38:43 +0200 Subject: [PATCH 05/35] fix: Use the short version of if statement when determining the offset --- implot_items.cpp | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/implot_items.cpp b/implot_items.cpp index 92706150..d326e342 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2397,11 +2397,7 @@ struct GetterHeatmapRowMaj { template IMPLOT_INLINE RectC operator()(I idx) const { const int r = idx / Cols; const int c = idx % Cols; - int offset = RowIndexOffset; - if (c + ColIndexOffset < Cols) - offset += ColIndexOffset; - else - offset += ColIndexOffset - Cols; + const int offset = RowIndexOffset + ((c + ColIndexOffset) < Cols ? ColIndexOffset : (ColIndexOffset - Cols)); double val = (double)Values[(idx + offset) % Count]; const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height)); RectC rect; @@ -2439,11 +2435,7 @@ struct GetterHeatmapColMaj { template IMPLOT_INLINE RectC operator()(I idx) const { const int r = idx % Rows; const int c = idx / Rows; - int offset = ColIndexOffset; - if (r + RowIndexOffset < Rows) - offset += RowIndexOffset; - else - offset += RowIndexOffset - Rows; + const int offset = ColIndexOffset + ((r + RowIndexOffset) < Rows ? RowIndexOffset : RowIndexOffset - Rows); double val = (double)Values[(idx + offset) % Count]; const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height)); RectC rect; @@ -2508,11 +2500,7 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d p.y = yref + ydir * (0.5*h + r*h); ImVec2 px = transformer(p); char buff[32]; - int offset = col_index_offset; - if (r + row_index_offset < rows) - offset += row_index_offset; - else - offset += row_index_offset - rows; + const int offset = col_index_offset + ((r + row_index_offset) < rows ? row_index_offset : (row_index_offset - rows)); int UsedIndex = (i + offset) % count; ImFormatString(buff, 32, fmt, values[UsedIndex]); ImVec2 size = ImGui::CalcTextSize(buff); @@ -2532,11 +2520,7 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d p.y = yref + ydir * (0.5*h + r*h); ImVec2 px = transformer(p); char buff[32]; - int offset = row_index_offset; - if (c + col_index_offset < cols) - offset += col_index_offset; - else - offset += col_index_offset - cols; + const int offset = row_index_offset + ((c + col_index_offset) < cols ? col_index_offset : (col_index_offset - cols)); int UsedIndex = (i + offset) % count; ImFormatString(buff, 32, fmt, values[UsedIndex]); ImVec2 size = ImGui::CalcTextSize(buff); From 50192187b8fe191f17f4f88bd0a51f0c6ad89ba2 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Thu, 26 Jun 2025 13:56:23 +0200 Subject: [PATCH 06/35] fix: Change the variable name to index to match other variables --- implot_items.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/implot_items.cpp b/implot_items.cpp index d326e342..503c6a38 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2501,10 +2501,10 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d ImVec2 px = transformer(p); char buff[32]; const int offset = col_index_offset + ((r + row_index_offset) < rows ? row_index_offset : (row_index_offset - rows)); - int UsedIndex = (i + offset) % count; - ImFormatString(buff, 32, fmt, values[UsedIndex]); + const int index = (i + offset) % count; + ImFormatString(buff, 32, fmt, values[index]); ImVec2 size = ImGui::CalcTextSize(buff); - double t = ImClamp(ImRemap01((double)values[UsedIndex], scale_min, scale_max), 0.0, 1.0); + double t = ImClamp(ImRemap01((double)values[index], scale_min, scale_max), 0.0, 1.0); ImVec4 color = SampleColormap((float)t); ImU32 col = CalcTextColor(color); draw_list.AddText(px - size * 0.5f, col, buff); @@ -2521,10 +2521,10 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d ImVec2 px = transformer(p); char buff[32]; const int offset = row_index_offset + ((c + col_index_offset) < cols ? col_index_offset : (col_index_offset - cols)); - int UsedIndex = (i + offset) % count; - ImFormatString(buff, 32, fmt, values[UsedIndex]); + const int index = (i + offset) % count; + ImFormatString(buff, 32, fmt, values[index]); ImVec2 size = ImGui::CalcTextSize(buff); - double t = ImClamp(ImRemap01((double)values[UsedIndex], scale_min, scale_max), 0.0, 1.0); + double t = ImClamp(ImRemap01((double)values[index], scale_min, scale_max), 0.0, 1.0); ImVec4 color = SampleColormap((float)t); ImU32 col = CalcTextColor(color); draw_list.AddText(px - size * 0.5f, col, buff); From cb9529c2c439e3324e74414004b6db2ecbbfe0ac Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:29:21 +0200 Subject: [PATCH 07/35] fix: Add missing brackets around the operation --- implot_items.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implot_items.cpp b/implot_items.cpp index 503c6a38..ad973670 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2435,7 +2435,7 @@ struct GetterHeatmapColMaj { template IMPLOT_INLINE RectC operator()(I idx) const { const int r = idx % Rows; const int c = idx / Rows; - const int offset = ColIndexOffset + ((r + RowIndexOffset) < Rows ? RowIndexOffset : RowIndexOffset - Rows); + const int offset = ColIndexOffset + ((r + RowIndexOffset) < Rows ? RowIndexOffset : (RowIndexOffset - Rows)); double val = (double)Values[(idx + offset) % Count]; const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height)); RectC rect; From 393ce31c39530bf952d805c766ba30a9651eb270 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Thu, 26 Jun 2025 17:45:29 +0200 Subject: [PATCH 08/35] fix: Structure the demo for the heatmap offsets a bit better --- implot_demo.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index 678a63d1..f9b9b3e5 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1960,18 +1960,21 @@ void Demo_HeatmapOffsets() { static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks; - ImPlot::PushColormap(map); const bool RowMajor = (hm_flags & ImPlotHeatmapFlags_ColMajor) == ImPlotHeatmapFlags_ColMajor; - int NumRows = !RowMajor ? 5 : 8; - int NumCols = RowMajor ? 5 : 8; + const int NumRows = RowMajor ? 8 : 5; + const int NumCols = RowMajor ? 5 : 8; static int offset_rows = 0; static int offset_cols = 0; + ImGui::SliderInt("Offset Rows", &offset_rows, -3 * NumRows, 3 * NumRows); ImGui::SliderInt("Offset Cols", &offset_cols, -3 * NumCols, 3 * NumCols); - if (ImPlot::BeginPlot("##Heatmap1", ImVec2(225, 225), ImPlotFlags_NoLegend | ImPlotFlags_NoMouseText)) { + + ImPlot::PushColormap(map); + + if (ImPlot::BeginPlot("##Heatmap1", ImVec2(500, 225), ImPlotFlags_NoLegend | ImPlotFlags_NoMouseText)) { ImPlot::SetupAxes(nullptr, nullptr, axes_flags, axes_flags); - ImPlot::SetupAxisTicks(ImAxis_X1, 0 + 1.0 / 14.0, 1 - 1.0 / 14.0, NumCols, xlabels); - ImPlot::SetupAxisTicks(ImAxis_Y1, 1 - 1.0 / 14.0, 0 + 1.0 / 14.0, NumRows, ylabels); + ImPlot::SetupAxisTicks(ImAxis_X1, 0 + 1.0 / (NumCols * 2.0), 1 - 1.0 / (NumCols * 2.0), NumCols, xlabels); + ImPlot::SetupAxisTicks(ImAxis_Y1, 1 - 1.0 / (NumRows * 2.0), 0 + 1.0 / (NumRows * 2.0), NumRows, ylabels); ImPlot::PlotHeatmap("heat", values1[0], NumRows, NumCols, scale_min, scale_max, "%g", ImPlotPoint(0, 0), ImPlotPoint(1, 1), hm_flags, offset_rows, offset_cols); ImPlot::EndPlot(); @@ -2331,7 +2334,7 @@ void ShowDemoWindow(bool* p_open) { } if (ImGui::BeginTabItem("Tools")) { DemoHeader("Offset and Stride", Demo_OffsetAndStride); - DemoHeader("Heatmap Offset", Demo_HeatmapOffsets); + DemoHeader("Heatmap Offsets", Demo_HeatmapOffsets); DemoHeader("Drag Points", Demo_DragPoints); DemoHeader("Drag Lines", Demo_DragLines); DemoHeader("Drag Rects", Demo_DragRects); From 91be7389eb7e6410671e7d8c54409047172a063b Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Thu, 26 Jun 2025 17:51:46 +0200 Subject: [PATCH 09/35] fix: Follow snake_case coding style for local variables --- implot_demo.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index f9b9b3e5..3d0b1385 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1966,16 +1966,16 @@ void Demo_HeatmapOffsets() { static int offset_rows = 0; static int offset_cols = 0; - ImGui::SliderInt("Offset Rows", &offset_rows, -3 * NumRows, 3 * NumRows); - ImGui::SliderInt("Offset Cols", &offset_cols, -3 * NumCols, 3 * NumCols); + ImGui::SliderInt("Offset Rows", &offset_rows, -3 * num_rows, 3 * num_rows); + ImGui::SliderInt("Offset Cols", &offset_cols, -3 * num_cols, 3 * num_cols); ImPlot::PushColormap(map); if (ImPlot::BeginPlot("##Heatmap1", ImVec2(500, 225), ImPlotFlags_NoLegend | ImPlotFlags_NoMouseText)) { ImPlot::SetupAxes(nullptr, nullptr, axes_flags, axes_flags); - ImPlot::SetupAxisTicks(ImAxis_X1, 0 + 1.0 / (NumCols * 2.0), 1 - 1.0 / (NumCols * 2.0), NumCols, xlabels); - ImPlot::SetupAxisTicks(ImAxis_Y1, 1 - 1.0 / (NumRows * 2.0), 0 + 1.0 / (NumRows * 2.0), NumRows, ylabels); - ImPlot::PlotHeatmap("heat", values1[0], NumRows, NumCols, scale_min, scale_max, "%g", ImPlotPoint(0, 0), ImPlotPoint(1, 1), hm_flags, + ImPlot::SetupAxisTicks(ImAxis_X1, 0 + 1.0 / (num_cols * 2.0), 1 - 1.0 / (num_cols * 2.0), num_cols, xlabels); + ImPlot::SetupAxisTicks(ImAxis_Y1, 1 - 1.0 / (num_rows * 2.0), 0 + 1.0 / (num_rows * 2.0), num_rows, ylabels); + ImPlot::PlotHeatmap("heat", values1[0], num_rows, num_cols, scale_min, scale_max, "%g", ImPlotPoint(0, 0), ImPlotPoint(1, 1), hm_flags, offset_rows, offset_cols); ImPlot::EndPlot(); } From 4e792782b1287f9da1a097a97fc3d7eaa267bbbf Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Thu, 26 Jun 2025 18:27:44 +0200 Subject: [PATCH 10/35] fix: Added missing items to commit --- implot_demo.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index 3d0b1385..218b9f72 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1960,9 +1960,9 @@ void Demo_HeatmapOffsets() { static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks; - const bool RowMajor = (hm_flags & ImPlotHeatmapFlags_ColMajor) == ImPlotHeatmapFlags_ColMajor; - const int NumRows = RowMajor ? 8 : 5; - const int NumCols = RowMajor ? 5 : 8; + const bool row_major = (hm_flags & ImPlotHeatmapFlags_ColMajor) == ImPlotHeatmapFlags_ColMajor; + const int num_rows = row_major ? 8 : 5; + const int num_cols = row_major ? 5 : 8; static int offset_rows = 0; static int offset_cols = 0; From c605765ee0b671aec4c436083de1322c843a9018 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Sat, 28 Jun 2025 16:03:30 +0200 Subject: [PATCH 11/35] feat: Added a function that can apply a row and column stride to the data - Added a object, HeatmapIndexerIdx, which supports determining which index to use based on the offset and stride for both the major and minor indexes - Reshuffled a bit of the code to not have to reuse the indexer - Add the stride to the demo by having a plot that uses the stride to produce different plots --- implot.h | 3 +- implot_demo.cpp | 54 +++++++++++------ implot_items.cpp | 147 ++++++++++++++++++++++++++++++----------------- 3 files changed, 131 insertions(+), 73 deletions(-) diff --git a/implot.h b/implot.h index 4063c915..37947658 100644 --- a/implot.h +++ b/implot.h @@ -898,7 +898,8 @@ IMPLOT_TMP void PlotInfLines(const char* label_id, const T* values, int count, I IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, ImPlotFormatter fmt, void* fmt_data=nullptr, double angle0=90, ImPlotPieChartFlags flags=0); IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* label_fmt="%.1f", double angle0=90, ImPlotPieChartFlags flags=0); -// Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels. A row and column offset can be specified to add a offset to the row and column indices +// Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels +IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* label_fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, ImPlotHeatmapFlags flags, int offset_rows, int offset_cols, int stride_rows, int stride_cols); IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1), ImPlotHeatmapFlags flags=0, int offset_rows = 0, int offset_cols = 0); // Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #range is left unspecified, the min/max of #values will be used as the range. diff --git a/implot_demo.cpp b/implot_demo.cpp index 218b9f72..19aad78c 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1929,16 +1929,17 @@ void Demo_OffsetAndStride() { // offset++; uncomment for animation! } -void Demo_HeatmapOffsets() { - static float values1[5][8] = {{0.8f, 2.4f, 2.5f, 3.9f, 0.0f, 4.0f, 0.0f, 5.1f}, - {2.4f, 0.0f, 4.0f, 1.0f, 2.7f, 0.0f, 0.0f, 3.2f}, - {1.1f, 2.4f, 0.8f, 4.3f, 1.9f, 4.4f, 0.0f, 1.3f}, - {0.6f, 0.0f, 0.3f, 0.0f, 3.1f, 0.0f, 0.0f, 1.2f}, - {0.7f, 1.7f, 0.6f, 2.6f, 2.2f, 6.2f, 0.0f, 5.1f}}; +void Demo_HeatmapOffsetAndStride() { + static float values[6][12] = { {0.8f, 2.8f, 2.5f, 3.9f, 0.1f, 4.0f, 0.0f, 5.1f, 2.3f, 0.7f, 4.2f, 1.2f}, + {2.4f, 0.2f, 4.0f, 1.0f, 2.7f, 0.0f, 0.0f, 3.2f, 3.5f, 2.1f, 1.3f, 1.4f}, + {1.1f, 2.3f, 0.8f, 4.3f, 1.9f, 4.4f, 0.0f, 1.3f, 5.2f, 3.2f, 6.0f, 5.9f}, + {0.6f, 0.4f, 0.3f, 0.0f, 3.1f, 0.0f, 0.0f, 1.2f, 1.4f, 4.5f, 4.6f, 4.7f}, + {0.7f, 1.7f, 0.6f, 2.6f, 2.2f, 6.2f, 0.0f, 5.1f, 3.4f, 3.8f, 1.1f, 0.9f}, + {0.2f, 2.6f, 6.1f, 1.2f, 4.2f, 5.2f, 0.0f, 4.1f, 1.2f, 2.8f, 4.8f, 0.8f} }; static float scale_min = 0; static float scale_max = 6.3f; - static const char* xlabels[] = {"C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8"}; - static const char* ylabels[] = {"R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8"}; + static const char* xlabels[] = { "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "C10", "C11", "C12" }; + static const char* ylabels[] = { "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "C9", "C10", "C11", "C12" }; static ImPlotColormap map = ImPlotColormap_Viridis; if (ImPlot::ColormapButton(ImPlot::GetColormapName(map), ImVec2(225, 0), map)) { @@ -1958,30 +1959,47 @@ void Demo_HeatmapOffsets() { ImGui::CheckboxFlags("Column Major", (unsigned int*)&hm_flags, ImPlotHeatmapFlags_ColMajor); - static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks; - const bool row_major = (hm_flags & ImPlotHeatmapFlags_ColMajor) == ImPlotHeatmapFlags_ColMajor; - const int num_rows = row_major ? 8 : 5; - const int num_cols = row_major ? 5 : 8; + const int num_rows = row_major ? 12 : 6; + const int num_cols = row_major ? 6 : 12; static int offset_rows = 0; static int offset_cols = 0; + static int stride_value = 2; ImGui::SliderInt("Offset Rows", &offset_rows, -3 * num_rows, 3 * num_rows); ImGui::SliderInt("Offset Cols", &offset_cols, -3 * num_cols, 3 * num_cols); + ImGui::SliderInt("Stride", &stride_value, 2, 4); ImPlot::PushColormap(map); - if (ImPlot::BeginPlot("##Heatmap1", ImVec2(500, 225), ImPlotFlags_NoLegend | ImPlotFlags_NoMouseText)) { + if (ImPlot::BeginPlot("##HeatmapOffsets", ImVec2(450, 225), ImPlotFlags_NoLegend | ImPlotFlags_NoMouseText)) { + static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks; ImPlot::SetupAxes(nullptr, nullptr, axes_flags, axes_flags); - ImPlot::SetupAxisTicks(ImAxis_X1, 0 + 1.0 / (num_cols * 2.0), 1 - 1.0 / (num_cols * 2.0), num_cols, xlabels); - ImPlot::SetupAxisTicks(ImAxis_Y1, 1 - 1.0 / (num_rows * 2.0), 0 + 1.0 / (num_rows * 2.0), num_rows, ylabels); - ImPlot::PlotHeatmap("heat", values1[0], num_rows, num_cols, scale_min, scale_max, "%g", ImPlotPoint(0, 0), ImPlotPoint(1, 1), hm_flags, - offset_rows, offset_cols); + ImPlot::SetupAxisTicks(ImAxis_X1, 0 + 1.0 / (num_cols * 2.0), 1 - 1.0 / (num_cols * 4.0), num_cols, xlabels); + ImPlot::SetupAxisTicks(ImAxis_Y1, 1 - 1.0 / (num_rows * 2.0), 0 + 1.0 / (num_rows * 4.0), num_rows, ylabels); + ImPlot::PlotHeatmap("Offset", values[0], num_rows, num_cols, scale_min, scale_max, "%g", ImPlotPoint(0, 0), ImPlotPoint(1, 1), hm_flags, offset_rows, offset_cols); ImPlot::EndPlot(); } ImGui::SameLine(); ImPlot::ColormapScale("##HeatScale", scale_min, scale_max, ImVec2(60, 225)); + if (ImPlot::BeginPlot("##HeatmapStrides", ImVec2(450, 225), ImPlotFlags_NoLegend | ImPlotFlags_NoMouseText)) { + static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels; + ImPlot::SetupAxes(nullptr, nullptr, axes_flags, axes_flags); + // The stride calculations should take into account when it either the row or the column divided by stride does not produces a integer value + const int updated_num_rows = num_rows / stride_value + (num_rows % stride_value == 0 ? 0 : 1); + const int updated_num_cols = num_cols / stride_value + (num_cols % stride_value == 0 ? 0 : 1); + // Depending on whether row major is used or col major, the offset applied to the row and the column needs to be updated + const int stride_offset_rows = row_major ? 1 : num_cols; + const int stride_offset_cols = row_major ? num_rows : 1; + // Draw the plots with the strides applied + ImPlot::PlotHeatmap("NoStride", values[0], num_rows, num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0, 0), ImPlotPoint(0.495, 0.49), hm_flags, offset_rows, offset_cols, sizeof(float) * stride_offset_rows, sizeof(float) * stride_offset_cols); + ImPlot::PlotHeatmap("RowStride", values[0], updated_num_rows, num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0, 0.51), ImPlotPoint(0.495, 1), hm_flags, offset_rows, offset_cols, stride_value * sizeof(float) * stride_offset_rows, sizeof(float) * stride_offset_cols); + ImPlot::PlotHeatmap("ColStride", values[0], num_rows, updated_num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0.505, 0), ImPlotPoint(1, 0.49), hm_flags, offset_rows, offset_cols, sizeof(float) * stride_offset_rows, stride_value * sizeof(float) * stride_offset_cols); + ImPlot::PlotHeatmap("RowColStride", values[0], updated_num_rows, updated_num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0.505, 0.51), ImPlotPoint(1, 1), hm_flags, offset_rows, offset_cols, stride_value * sizeof(float) * stride_offset_rows, stride_value * sizeof(float) * stride_offset_cols); + ImPlot::EndPlot(); + } + ImPlot::PopColormap(); } @@ -2334,7 +2352,7 @@ void ShowDemoWindow(bool* p_open) { } if (ImGui::BeginTabItem("Tools")) { DemoHeader("Offset and Stride", Demo_OffsetAndStride); - DemoHeader("Heatmap Offsets", Demo_HeatmapOffsets); + DemoHeader("Heatmap Offset and Stride", Demo_HeatmapOffsetAndStride); DemoHeader("Drag Points", Demo_DragPoints); DemoHeader("Drag Lines", Demo_DragLines); DemoHeader("Drag Rects", Demo_DragRects); diff --git a/implot_items.cpp b/implot_items.cpp index ad973670..fa079a82 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2377,14 +2377,50 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES() //----------------------------------------------------------------------------- template +struct HeatmapIndexerIdx { + HeatmapIndexerIdx(const T* data, int num_major, int num_minor, int major_offset, int minor_offset, int major_stride, int minor_stride) : + Data(data), + // If the stride does not have to be applied then use the major offset index as the number of major columns to shift by instead of applying the shift for the indexes + // It is a bit simpler to only have to append a constant to the index instead of having to add a constant to first determine the rows and then column offsets + MajorOffset((major_stride == sizeof(T) * num_minor) ? ImPosMod(num_minor * major_offset, num_minor * num_major) : ImPosMod(major_offset, num_major)), + MinorOffset(ImPosMod(minor_offset, num_minor)), + MajorStride(major_stride), + MinorStride(minor_stride), + Type(((MinorOffset == 0) << 0) | ((MajorOffset == 0) << 1) | ((MinorStride == sizeof(T)) << 2) | ((MajorStride == sizeof(T) * num_minor) << 3)) + { } + template IMPLOT_INLINE double operator()(I idx, int count, int major, int minor, int num_major, int num_minor) const { + // Get the data based based on the type + switch (Type) { + case 15: return Data[idx]; // No offset or stride + case 14: return Data[(((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]; // Minor offset + case 13: return Data[(MajorOffset + idx) % count]; // Major offset + case 12: return Data[(MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]; // Major+minor offset + case 11: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((idx)) * MinorStride); // Minor stride + case 10: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride); // Minor stride and minor offset + case 9: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + idx) % count) * MinorStride); // Minor stride and major offset + case 8: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride); // Minor stride and major + minor offset + case 7: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + minor * sizeof(T)); // Major stride + case 6: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T)); // Major stride and minor offset + case 5: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + minor * sizeof(T)); // Major stride and major offset + case 4: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T)); // Major stride and major+minor offset + case 3: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor)) * MinorStride); // Major+minor stride + case 2: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride); // Major+minor stride and minor offset + case 1: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor)) * MinorStride); // Major+minor stride and major offset + case 0: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride); // Major+minor stride and major+minor offset + default: return T(0); + } + } + const T* const Data; + const int MajorOffset, MinorOffset, MajorStride, MinorStride, Type; +}; + +template struct GetterHeatmapRowMaj { - GetterHeatmapRowMaj(const T* values, int count, int rows, int cols, int row_index_offset, int col_index_offset, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : - Values(values), - Count(count), + GetterHeatmapRowMaj(const _Indexer& indexer, int rows, int cols, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : + Indexer(indexer), + Count(rows*cols), Rows(rows), Cols(cols), - RowIndexOffset(row_index_offset), - ColIndexOffset(col_index_offset), ScaleMin(scale_min), ScaleMax(scale_max), Width(width), @@ -2397,8 +2433,7 @@ struct GetterHeatmapRowMaj { template IMPLOT_INLINE RectC operator()(I idx) const { const int r = idx / Cols; const int c = idx % Cols; - const int offset = RowIndexOffset + ((c + ColIndexOffset) < Cols ? ColIndexOffset : (ColIndexOffset - Cols)); - double val = (double)Values[(idx + offset) % Count]; + double val = Indexer(idx, Count, r, c, Rows, Cols); const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height)); RectC rect; rect.Pos = p; @@ -2408,21 +2443,19 @@ struct GetterHeatmapRowMaj { rect.Color = gp.ColormapData.LerpTable(gp.Style.Colormap, t); return rect; } - const T* const Values; - const int Count, Rows, Cols, RowIndexOffset, ColIndexOffset; + const _Indexer& Indexer; + const int Count, Rows, Cols; const double ScaleMin, ScaleMax, Width, Height, XRef, YRef, YDir; const ImPlotPoint HalfSize; }; -template +template struct GetterHeatmapColMaj { - GetterHeatmapColMaj(const T* values, int count, int rows, int cols, int row_index_offset, int col_index_offset, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : - Values(values), - Count(count), + GetterHeatmapColMaj(const _Indexer& indexer, int rows, int cols, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : + Indexer(indexer), + Count(rows*cols), Rows(rows), Cols(cols), - RowIndexOffset(row_index_offset), - ColIndexOffset(col_index_offset), ScaleMin(scale_min), ScaleMax(scale_max), Width(width), @@ -2435,8 +2468,7 @@ struct GetterHeatmapColMaj { template IMPLOT_INLINE RectC operator()(I idx) const { const int r = idx % Rows; const int c = idx / Rows; - const int offset = ColIndexOffset + ((r + RowIndexOffset) < Rows ? RowIndexOffset : (RowIndexOffset - Rows)); - double val = (double)Values[(idx + offset) % Count]; + double val = Indexer(idx, Count, c, r, Cols, Rows); const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height)); RectC rect; rect.Pos = p; @@ -2446,20 +2478,19 @@ struct GetterHeatmapColMaj { rect.Color = gp.ColormapData.LerpTable(gp.Style.Colormap, t); return rect; } - const T* const Values; - const int Count, Rows, Cols, RowIndexOffset, ColIndexOffset; + const _Indexer& Indexer; + const int Count, Rows, Cols; const double ScaleMin, ScaleMax, Width, Height, XRef, YRef, YDir; const ImPlotPoint HalfSize; }; template -void RenderHeatmap(ImDrawList& draw_list, 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, bool col_maj, int offset_rows, int offset_cols) { +void RenderHeatmap(ImDrawList& draw_list, 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, bool col_maj, int offset_rows, int offset_cols, int stride_rows, int stride_cols) { ImPlotContext& gp = *GImPlot; Transformer2 transformer; - const int count = (cols * rows); if (scale_min == 0 && scale_max == 0) { T temp_min, temp_max; - ImMinMaxArray(values, count, &temp_min, &temp_max); + ImMinMaxArray(values,rows*cols,&temp_min,&temp_max); scale_min = (double)temp_min; scale_max = (double)temp_max; } @@ -2472,27 +2503,17 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } const double yref = reverse_y ? bounds_max.y : bounds_min.y; const double ydir = reverse_y ? -1 : 1; - int row_index_offset; - int col_index_offset; if (col_maj) { - row_index_offset = ImPosMod(offset_rows, rows); - col_index_offset = ImPosMod(rows * offset_cols, count); - GetterHeatmapColMaj getter(values, count, rows, cols, row_index_offset, col_index_offset, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); - RenderPrimitives1(getter); - } - else { - row_index_offset = ImPosMod(cols * offset_rows, count); - col_index_offset = ImPosMod(offset_cols, cols); - GetterHeatmapRowMaj getter(values, count, rows, cols, row_index_offset, col_index_offset, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); + HeatmapIndexerIdx indexer(values, cols, rows, offset_cols, offset_rows, stride_cols, stride_rows); + GetterHeatmapColMaj> getter(indexer, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); RenderPrimitives1(getter); - } - // labels - if (fmt != nullptr) { - 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; - if (col_maj) { + + if (fmt != nullptr) { + const double w = getter.Width; + const double h = getter.Height; + const ImPlotPoint half_size = getter.HalfSize; + const int count = getter.Count; + int i = 0; for (int c = 0; c < cols; ++c) { for (int r = 0; r < rows; ++r) { ImPlotPoint p; @@ -2500,11 +2521,10 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d p.y = yref + ydir * (0.5*h + r*h); ImVec2 px = transformer(p); char buff[32]; - const int offset = col_index_offset + ((r + row_index_offset) < rows ? row_index_offset : (row_index_offset - rows)); - const int index = (i + offset) % count; - ImFormatString(buff, 32, fmt, values[index]); + double val = indexer(i, count, c, r, cols, rows); + ImFormatString(buff, 32, fmt, val); ImVec2 size = ImGui::CalcTextSize(buff); - double t = ImClamp(ImRemap01((double)values[index], scale_min, scale_max), 0.0, 1.0); + double t = ImClamp(ImRemap01(val, scale_min, scale_max), 0.0, 1.0); ImVec4 color = SampleColormap((float)t); ImU32 col = CalcTextColor(color); draw_list.AddText(px - size * 0.5f, col, buff); @@ -2512,7 +2532,18 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } } } - else { + } + else { + HeatmapIndexerIdx indexer(values, rows, cols, offset_rows, offset_cols, stride_rows, stride_cols); + GetterHeatmapRowMaj> getter(indexer, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); + RenderPrimitives1(getter); + + if (fmt != nullptr) { + const double w = getter.Width; + const double h = getter.Height; + const ImPlotPoint half_size = getter.HalfSize; + const int count = getter.Count; + int i = 0; for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { ImPlotPoint p; @@ -2520,11 +2551,10 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d p.y = yref + ydir * (0.5*h + r*h); ImVec2 px = transformer(p); char buff[32]; - const int offset = row_index_offset + ((c + col_index_offset) < cols ? col_index_offset : (col_index_offset - cols)); - const int index = (i + offset) % count; - ImFormatString(buff, 32, fmt, values[index]); + double val = indexer(i, count, r, c, rows, cols); + ImFormatString(buff, 32, fmt, val); ImVec2 size = ImGui::CalcTextSize(buff); - double t = ImClamp(ImRemap01((double)values[index], scale_min, scale_max), 0.0, 1.0); + double t = ImClamp(ImRemap01(val, scale_min, scale_max), 0.0, 1.0); ImVec4 color = SampleColormap((float)t); ImU32 col = CalcTextColor(color); draw_list.AddText(px - size * 0.5f, col, buff); @@ -2536,7 +2566,7 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } 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, ImPlotHeatmapFlags flags, int offset_rows, int offset_cols) { +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, ImPlotHeatmapFlags flags, int offset_rows, int offset_cols, int stride_rows, int stride_cols) { if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max))) { if (rows <= 0 || cols <= 0) { EndItem(); @@ -2544,11 +2574,20 @@ void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, doub } ImDrawList& draw_list = *GetPlotDrawList(); const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); - RenderHeatmap(draw_list, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true, col_maj, offset_rows, offset_cols); + RenderHeatmap(draw_list, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true, col_maj, offset_rows, offset_cols, stride_rows, stride_cols); EndItem(); } } -#define INSTANTIATE_MACRO(T) template IMPLOT_API 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, ImPlotHeatmapFlags flags, int offset_rows, int offset_cols); +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, ImPlotHeatmapFlags flags, int offset_rows, int offset_cols) +{ + const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); + PlotHeatmap(label_id, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, flags, offset_rows, offset_cols, col_maj ? sizeof(T) : sizeof(T) * cols, !col_maj ? sizeof(T) : sizeof(T) * rows); +} + +#define INSTANTIATE_MACRO(T) \ + template IMPLOT_API 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, ImPlotHeatmapFlags flags, int offset_rows, int offset_cols, int stride_rows, int stride_cols); \ + template IMPLOT_API 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, ImPlotHeatmapFlags flags, int offset_rows, int offset_cols); CALL_INSTANTIATE_FOR_NUMERIC_TYPES() #undef INSTANTIATE_MACRO @@ -2711,7 +2750,7 @@ double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count return max_count; } ImDrawList& draw_list = *GetPlotDrawList(); - RenderHeatmap(draw_list, &bin_counts.Data[0], y_bins, x_bins, 0, max_count, nullptr, range.Min(), range.Max(), false, col_maj, 0, 0); + RenderHeatmap(draw_list, &bin_counts.Data[0], y_bins, x_bins, 0, max_count, nullptr, range.Min(), range.Max(), false, col_maj, 0, 0, col_maj ? sizeof(T) : sizeof(T) * x_bins, !col_maj ? sizeof(T) : sizeof(T) * y_bins); EndItem(); } return max_count; From 9e6812cf77acd21b250e87aed95757cc76529da8 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Sat, 28 Jun 2025 16:14:49 +0200 Subject: [PATCH 12/35] refactor: Change offset_rows/cols to be named row/col_offset which fix: Build warning when casting type from T to double. Ensure that the double value is created --- implot.h | 4 ++-- implot_demo.cpp | 22 ++++++++++---------- implot_items.cpp | 52 ++++++++++++++++++++++++------------------------ 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/implot.h b/implot.h index 37947658..e1cbe628 100644 --- a/implot.h +++ b/implot.h @@ -899,8 +899,8 @@ IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* label_fmt="%.1f", double angle0=90, ImPlotPieChartFlags flags=0); // Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels -IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* label_fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, ImPlotHeatmapFlags flags, int offset_rows, int offset_cols, int stride_rows, int stride_cols); -IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1), ImPlotHeatmapFlags flags=0, int offset_rows = 0, int offset_cols = 0); +IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* label_fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols); +IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1), ImPlotHeatmapFlags flags=0, int row_offset = 0, int col_offset = 0); // Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #range is left unspecified, the min/max of #values will be used as the range. // Otherwise, outlier values outside of the range are not binned. The largest bin count or density is returned. diff --git a/implot_demo.cpp b/implot_demo.cpp index 19aad78c..3617957e 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1962,12 +1962,12 @@ void Demo_HeatmapOffsetAndStride() { const bool row_major = (hm_flags & ImPlotHeatmapFlags_ColMajor) == ImPlotHeatmapFlags_ColMajor; const int num_rows = row_major ? 12 : 6; const int num_cols = row_major ? 6 : 12; - static int offset_rows = 0; - static int offset_cols = 0; + static int row_offset = 0; + static int col_offset = 0; static int stride_value = 2; - ImGui::SliderInt("Offset Rows", &offset_rows, -3 * num_rows, 3 * num_rows); - ImGui::SliderInt("Offset Cols", &offset_cols, -3 * num_cols, 3 * num_cols); + ImGui::SliderInt("Row Offset", &row_offset, -3 * num_rows, 3 * num_rows); + ImGui::SliderInt("Column Offset", &col_offset, -3 * num_cols, 3 * num_cols); ImGui::SliderInt("Stride", &stride_value, 2, 4); ImPlot::PushColormap(map); @@ -1977,7 +1977,7 @@ void Demo_HeatmapOffsetAndStride() { ImPlot::SetupAxes(nullptr, nullptr, axes_flags, axes_flags); ImPlot::SetupAxisTicks(ImAxis_X1, 0 + 1.0 / (num_cols * 2.0), 1 - 1.0 / (num_cols * 4.0), num_cols, xlabels); ImPlot::SetupAxisTicks(ImAxis_Y1, 1 - 1.0 / (num_rows * 2.0), 0 + 1.0 / (num_rows * 4.0), num_rows, ylabels); - ImPlot::PlotHeatmap("Offset", values[0], num_rows, num_cols, scale_min, scale_max, "%g", ImPlotPoint(0, 0), ImPlotPoint(1, 1), hm_flags, offset_rows, offset_cols); + ImPlot::PlotHeatmap("Offset", values[0], num_rows, num_cols, scale_min, scale_max, "%g", ImPlotPoint(0, 0), ImPlotPoint(1, 1), hm_flags, row_offset, col_offset); ImPlot::EndPlot(); } ImGui::SameLine(); @@ -1990,13 +1990,13 @@ void Demo_HeatmapOffsetAndStride() { const int updated_num_rows = num_rows / stride_value + (num_rows % stride_value == 0 ? 0 : 1); const int updated_num_cols = num_cols / stride_value + (num_cols % stride_value == 0 ? 0 : 1); // Depending on whether row major is used or col major, the offset applied to the row and the column needs to be updated - const int stride_offset_rows = row_major ? 1 : num_cols; - const int stride_offset_cols = row_major ? num_rows : 1; + const int stride_row_offset = row_major ? 1 : num_cols; + const int stride_col_offset = row_major ? num_rows : 1; // Draw the plots with the strides applied - ImPlot::PlotHeatmap("NoStride", values[0], num_rows, num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0, 0), ImPlotPoint(0.495, 0.49), hm_flags, offset_rows, offset_cols, sizeof(float) * stride_offset_rows, sizeof(float) * stride_offset_cols); - ImPlot::PlotHeatmap("RowStride", values[0], updated_num_rows, num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0, 0.51), ImPlotPoint(0.495, 1), hm_flags, offset_rows, offset_cols, stride_value * sizeof(float) * stride_offset_rows, sizeof(float) * stride_offset_cols); - ImPlot::PlotHeatmap("ColStride", values[0], num_rows, updated_num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0.505, 0), ImPlotPoint(1, 0.49), hm_flags, offset_rows, offset_cols, sizeof(float) * stride_offset_rows, stride_value * sizeof(float) * stride_offset_cols); - ImPlot::PlotHeatmap("RowColStride", values[0], updated_num_rows, updated_num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0.505, 0.51), ImPlotPoint(1, 1), hm_flags, offset_rows, offset_cols, stride_value * sizeof(float) * stride_offset_rows, stride_value * sizeof(float) * stride_offset_cols); + ImPlot::PlotHeatmap("NoStride", values[0], num_rows, num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0, 0), ImPlotPoint(0.495, 0.49), hm_flags, row_offset, col_offset, sizeof(float) * stride_row_offset, sizeof(float) * stride_col_offset); + ImPlot::PlotHeatmap("RowStride", values[0], updated_num_rows, num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0, 0.51), ImPlotPoint(0.495, 1), hm_flags, row_offset, col_offset, stride_value * sizeof(float) * stride_row_offset, sizeof(float) * stride_col_offset); + ImPlot::PlotHeatmap("ColStride", values[0], num_rows, updated_num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0.505, 0), ImPlotPoint(1, 0.49), hm_flags, row_offset, col_offset, sizeof(float) * stride_row_offset, stride_value * sizeof(float) * stride_col_offset); + ImPlot::PlotHeatmap("RowColStride", values[0], updated_num_rows, updated_num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0.505, 0.51), ImPlotPoint(1, 1), hm_flags, row_offset, col_offset, stride_value * sizeof(float) * stride_row_offset, stride_value * sizeof(float) * stride_col_offset); ImPlot::EndPlot(); } diff --git a/implot_items.cpp b/implot_items.cpp index fa079a82..731d4a17 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2391,23 +2391,23 @@ struct HeatmapIndexerIdx { template IMPLOT_INLINE double operator()(I idx, int count, int major, int minor, int num_major, int num_minor) const { // Get the data based based on the type switch (Type) { - case 15: return Data[idx]; // No offset or stride - case 14: return Data[(((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]; // Minor offset - case 13: return Data[(MajorOffset + idx) % count]; // Major offset - case 12: return Data[(MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]; // Major+minor offset - case 11: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((idx)) * MinorStride); // Minor stride - case 10: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride); // Minor stride and minor offset - case 9: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + idx) % count) * MinorStride); // Minor stride and major offset - case 8: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride); // Minor stride and major + minor offset - case 7: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + minor * sizeof(T)); // Major stride - case 6: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T)); // Major stride and minor offset - case 5: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + minor * sizeof(T)); // Major stride and major offset - case 4: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T)); // Major stride and major+minor offset - case 3: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor)) * MinorStride); // Major+minor stride - case 2: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride); // Major+minor stride and minor offset - case 1: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor)) * MinorStride); // Major+minor stride and major offset - case 0: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride); // Major+minor stride and major+minor offset - default: return T(0); + case 15: return (double)(Data[idx]); // No offset or stride + case 14: return (double)(Data[(((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]); // Minor offset + case 13: return (double)(Data[(MajorOffset + idx) % count]); // Major offset + case 12: return (double)(Data[(MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]); // Major+minor offset + case 11: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((idx)) * MinorStride)); // Minor stride + case 10: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride)); // Minor stride and minor offset + case 9: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + idx) % count) * MinorStride)); // Minor stride and major offset + case 8: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride)); // Minor stride and major + minor offset + case 7: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + minor * sizeof(T))); // Major stride + case 6: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T))); // Major stride and minor offset + case 5: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + minor * sizeof(T))); // Major stride and major offset + case 4: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T))); // Major stride and major+minor offset + case 3: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor)) * MinorStride)); // Major+minor stride + case 2: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride)); // Major+minor stride and minor offset + case 1: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor)) * MinorStride)); // Major+minor stride and major offset + case 0: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride)); // Major+minor stride and major+minor offset + default: return (double)(T(0); } } const T* const Data; @@ -2485,7 +2485,7 @@ struct GetterHeatmapColMaj { }; template -void RenderHeatmap(ImDrawList& draw_list, 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, bool col_maj, int offset_rows, int offset_cols, int stride_rows, int stride_cols) { +void RenderHeatmap(ImDrawList& draw_list, 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, bool col_maj, int row_offset, int col_offset, int stride_rows, int stride_cols) { ImPlotContext& gp = *GImPlot; Transformer2 transformer; if (scale_min == 0 && scale_max == 0) { @@ -2504,7 +2504,7 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d const double yref = reverse_y ? bounds_max.y : bounds_min.y; const double ydir = reverse_y ? -1 : 1; if (col_maj) { - HeatmapIndexerIdx indexer(values, cols, rows, offset_cols, offset_rows, stride_cols, stride_rows); + HeatmapIndexerIdx indexer(values, cols, rows, col_offset, row_offset, stride_cols, stride_rows); GetterHeatmapColMaj> getter(indexer, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); RenderPrimitives1(getter); @@ -2534,7 +2534,7 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } } else { - HeatmapIndexerIdx indexer(values, rows, cols, offset_rows, offset_cols, stride_rows, stride_cols); + HeatmapIndexerIdx indexer(values, rows, cols, row_offset, col_offset, stride_rows, stride_cols); GetterHeatmapRowMaj> getter(indexer, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); RenderPrimitives1(getter); @@ -2566,7 +2566,7 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } 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, ImPlotHeatmapFlags flags, int offset_rows, int offset_cols, int stride_rows, int stride_cols) { +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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols) { if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max))) { if (rows <= 0 || cols <= 0) { EndItem(); @@ -2574,20 +2574,20 @@ void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, doub } ImDrawList& draw_list = *GetPlotDrawList(); const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); - RenderHeatmap(draw_list, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true, col_maj, offset_rows, offset_cols, stride_rows, stride_cols); + RenderHeatmap(draw_list, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true, col_maj, row_offset, col_offset, stride_rows, stride_cols); EndItem(); } } 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, ImPlotHeatmapFlags flags, int offset_rows, int offset_cols) +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, ImPlotHeatmapFlags flags, int row_offset, int col_offset) { const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); - PlotHeatmap(label_id, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, flags, offset_rows, offset_cols, col_maj ? sizeof(T) : sizeof(T) * cols, !col_maj ? sizeof(T) : sizeof(T) * rows); + PlotHeatmap(label_id, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, flags, row_offset, col_offset, col_maj ? sizeof(T) : sizeof(T) * cols, !col_maj ? sizeof(T) : sizeof(T) * rows); } #define INSTANTIATE_MACRO(T) \ - template IMPLOT_API 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, ImPlotHeatmapFlags flags, int offset_rows, int offset_cols, int stride_rows, int stride_cols); \ - template IMPLOT_API 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, ImPlotHeatmapFlags flags, int offset_rows, int offset_cols); + template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols); \ + template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset); CALL_INSTANTIATE_FOR_NUMERIC_TYPES() #undef INSTANTIATE_MACRO From 80119d12c8946dc6070229335790ae95a6d493dc Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Sat, 28 Jun 2025 16:17:53 +0200 Subject: [PATCH 13/35] fix: Add missing `(` --- implot_items.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implot_items.cpp b/implot_items.cpp index 731d4a17..8d1bd169 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2407,7 +2407,7 @@ struct HeatmapIndexerIdx { case 2: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride)); // Major+minor stride and minor offset case 1: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor)) * MinorStride)); // Major+minor stride and major offset case 0: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride)); // Major+minor stride and major+minor offset - default: return (double)(T(0); + default: return (double)(T(0)); } } const T* const Data; From 91b64d909dfc93dc6869f8fd9f66070192848f34 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Sat, 28 Jun 2025 16:24:06 +0200 Subject: [PATCH 14/35] refactor: Move the PlotHeatmap function with the default arguments before the function that has the stride and that does not have default arguments --- implot.h | 2 +- implot_items.cpp | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/implot.h b/implot.h index e1cbe628..eee0cf0e 100644 --- a/implot.h +++ b/implot.h @@ -899,8 +899,8 @@ IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* label_fmt="%.1f", double angle0=90, ImPlotPieChartFlags flags=0); // Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels +IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min = 0, double scale_max = 0, const char* label_fmt = "%.1f", const ImPlotPoint& bounds_min = ImPlotPoint(0, 0), const ImPlotPoint& bounds_max = ImPlotPoint(1, 1), ImPlotHeatmapFlags flags = 0, int row_offset = 0, int col_offset = 0); IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* label_fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols); -IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1), ImPlotHeatmapFlags flags=0, int row_offset = 0, int col_offset = 0); // Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #range is left unspecified, the min/max of #values will be used as the range. // Otherwise, outlier values outside of the range are not binned. The largest bin count or density is returned. diff --git a/implot_items.cpp b/implot_items.cpp index 8d1bd169..5d9b023f 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2565,6 +2565,13 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } } +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, ImPlotHeatmapFlags flags, int row_offset, int col_offset) +{ + const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); + PlotHeatmap(label_id, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, flags, row_offset, col_offset, col_maj ? sizeof(T) : sizeof(T) * cols, !col_maj ? sizeof(T) : sizeof(T) * rows); +} + 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols) { if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max))) { @@ -2578,16 +2585,10 @@ void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, doub EndItem(); } } -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, ImPlotHeatmapFlags flags, int row_offset, int col_offset) -{ - const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); - PlotHeatmap(label_id, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, flags, row_offset, col_offset, col_maj ? sizeof(T) : sizeof(T) * cols, !col_maj ? sizeof(T) : sizeof(T) * rows); -} #define INSTANTIATE_MACRO(T) \ - template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols); \ - template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset); + template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset); \ + template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols); CALL_INSTANTIATE_FOR_NUMERIC_TYPES() #undef INSTANTIATE_MACRO From 2b6aea377fb0a2fb138df8d39568d3f5ae1a8693 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Sat, 28 Jun 2025 16:26:30 +0200 Subject: [PATCH 15/35] =?UTF-8?q?fix:=20Compiler=20errors=20-=20comparison?= =?UTF-8?q?=20of=20integer=20expressions=20of=20different=20signedness:=20?= =?UTF-8?q?=E2=80=98int=E2=80=99=20and=20=E2=80=98unsigned=20int=E2=80=99?= =?UTF-8?q?=20for=20the=20major=5Fstride=20=3D=3D=20sizeof(T)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- implot_items.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/implot_items.cpp b/implot_items.cpp index 5d9b023f..332ee893 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2382,11 +2382,11 @@ struct HeatmapIndexerIdx { Data(data), // If the stride does not have to be applied then use the major offset index as the number of major columns to shift by instead of applying the shift for the indexes // It is a bit simpler to only have to append a constant to the index instead of having to add a constant to first determine the rows and then column offsets - MajorOffset((major_stride == sizeof(T) * num_minor) ? ImPosMod(num_minor * major_offset, num_minor * num_major) : ImPosMod(major_offset, num_major)), + MajorOffset((major_stride == int(sizeof(T)) * num_minor) ? ImPosMod(num_minor * major_offset, num_minor * num_major) : ImPosMod(major_offset, num_major)), MinorOffset(ImPosMod(minor_offset, num_minor)), MajorStride(major_stride), MinorStride(minor_stride), - Type(((MinorOffset == 0) << 0) | ((MajorOffset == 0) << 1) | ((MinorStride == sizeof(T)) << 2) | ((MajorStride == sizeof(T) * num_minor) << 3)) + Type(((MinorOffset == 0) << 0) | ((MajorOffset == 0) << 1) | ((MinorStride == int(sizeof(T))) << 2) | ((MajorStride == int(sizeof(T)) * num_minor) << 3)) { } template IMPLOT_INLINE double operator()(I idx, int count, int major, int minor, int num_major, int num_minor) const { // Get the data based based on the type From 32a70a2752e76a12ee3bb5ccdaec25967640e6ee Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Sat, 28 Jun 2025 16:29:37 +0200 Subject: [PATCH 16/35] refactor: Change order of function definitions --- implot_items.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/implot_items.cpp b/implot_items.cpp index 332ee893..8a80d2f9 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2565,13 +2565,6 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } } -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, ImPlotHeatmapFlags flags, int row_offset, int col_offset) -{ - const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); - PlotHeatmap(label_id, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, flags, row_offset, col_offset, col_maj ? sizeof(T) : sizeof(T) * cols, !col_maj ? sizeof(T) : sizeof(T) * rows); -} - 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols) { if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max))) { @@ -2586,9 +2579,16 @@ void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, doub } } +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, ImPlotHeatmapFlags flags, int row_offset, int col_offset) +{ + const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); + PlotHeatmap(label_id, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, flags, row_offset, col_offset, col_maj ? sizeof(T) : sizeof(T) * cols, !col_maj ? sizeof(T) : sizeof(T) * rows); +} + #define INSTANTIATE_MACRO(T) \ - template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset); \ - template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols); + template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols); \ + template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset); CALL_INSTANTIATE_FOR_NUMERIC_TYPES() #undef INSTANTIATE_MACRO From f0c4cf0277cfa09906687eb40a60ddd0971ba786 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Sat, 28 Jun 2025 16:32:00 +0200 Subject: [PATCH 17/35] fix: Change order of functions and ensure function does not have partial template initialisation --- implot_items.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/implot_items.cpp b/implot_items.cpp index 8a80d2f9..326cdf2a 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2566,7 +2566,14 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols) { +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, ImPlotHeatmapFlags flags, int row_offset, int col_offset) +{ + const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); + PlotHeatmap(label_id, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, flags, row_offset, col_offset, col_maj ? sizeof(T) : sizeof(T) * cols, !col_maj ? sizeof(T) : sizeof(T) * rows); +} + +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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols) { if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max))) { if (rows <= 0 || cols <= 0) { EndItem(); @@ -2579,16 +2586,9 @@ void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, doub } } -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, ImPlotHeatmapFlags flags, int row_offset, int col_offset) -{ - const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); - PlotHeatmap(label_id, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, flags, row_offset, col_offset, col_maj ? sizeof(T) : sizeof(T) * cols, !col_maj ? sizeof(T) : sizeof(T) * rows); -} - #define INSTANTIATE_MACRO(T) \ - template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols); \ - template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset); + template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset); \ + template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols); CALL_INSTANTIATE_FOR_NUMERIC_TYPES() #undef INSTANTIATE_MACRO From d33c29740020820a1db95fb77a2f23a335c363f1 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Sat, 28 Jun 2025 16:48:29 +0200 Subject: [PATCH 18/35] fix: Get rid of partial template initialisation and get rid of unused variable --- implot_items.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/implot_items.cpp b/implot_items.cpp index 326cdf2a..30334790 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2511,7 +2511,6 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d if (fmt != nullptr) { const double w = getter.Width; const double h = getter.Height; - const ImPlotPoint half_size = getter.HalfSize; const int count = getter.Count; int i = 0; for (int c = 0; c < cols; ++c) { @@ -2541,7 +2540,6 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d if (fmt != nullptr) { const double w = getter.Width; const double h = getter.Height; - const ImPlotPoint half_size = getter.HalfSize; const int count = getter.Count; int i = 0; for (int r = 0; r < rows; ++r) { @@ -2566,14 +2564,14 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset) +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, ImPlotHeatmapFlags flags, int row_offset, int col_offset) { const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); PlotHeatmap(label_id, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, flags, row_offset, col_offset, col_maj ? sizeof(T) : sizeof(T) * cols, !col_maj ? sizeof(T) : sizeof(T) * rows); } 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols) { +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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols) { if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max))) { if (rows <= 0 || cols <= 0) { EndItem(); From 42b519357445adf8938bf586d9495abd88827c80 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Sat, 28 Jun 2025 17:14:08 +0200 Subject: [PATCH 19/35] refactor: Rename stride_col/row to row/col_stride to match what was done for offsets --- implot.h | 2 +- implot_demo.cpp | 12 ++++++------ implot_items.cpp | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/implot.h b/implot.h index eee0cf0e..39ec7405 100644 --- a/implot.h +++ b/implot.h @@ -900,7 +900,7 @@ IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int // Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min = 0, double scale_max = 0, const char* label_fmt = "%.1f", const ImPlotPoint& bounds_min = ImPlotPoint(0, 0), const ImPlotPoint& bounds_max = ImPlotPoint(1, 1), ImPlotHeatmapFlags flags = 0, int row_offset = 0, int col_offset = 0); -IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* label_fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols); +IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* label_fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int row_stride, int col_stride); // Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #range is left unspecified, the min/max of #values will be used as the range. // Otherwise, outlier values outside of the range are not binned. The largest bin count or density is returned. diff --git a/implot_demo.cpp b/implot_demo.cpp index 3617957e..504f9872 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1990,13 +1990,13 @@ void Demo_HeatmapOffsetAndStride() { const int updated_num_rows = num_rows / stride_value + (num_rows % stride_value == 0 ? 0 : 1); const int updated_num_cols = num_cols / stride_value + (num_cols % stride_value == 0 ? 0 : 1); // Depending on whether row major is used or col major, the offset applied to the row and the column needs to be updated - const int stride_row_offset = row_major ? 1 : num_cols; - const int stride_col_offset = row_major ? num_rows : 1; + const int row_stride_offset = row_major ? 1 : num_cols; + const int col_stride_offset = row_major ? num_rows : 1; // Draw the plots with the strides applied - ImPlot::PlotHeatmap("NoStride", values[0], num_rows, num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0, 0), ImPlotPoint(0.495, 0.49), hm_flags, row_offset, col_offset, sizeof(float) * stride_row_offset, sizeof(float) * stride_col_offset); - ImPlot::PlotHeatmap("RowStride", values[0], updated_num_rows, num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0, 0.51), ImPlotPoint(0.495, 1), hm_flags, row_offset, col_offset, stride_value * sizeof(float) * stride_row_offset, sizeof(float) * stride_col_offset); - ImPlot::PlotHeatmap("ColStride", values[0], num_rows, updated_num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0.505, 0), ImPlotPoint(1, 0.49), hm_flags, row_offset, col_offset, sizeof(float) * stride_row_offset, stride_value * sizeof(float) * stride_col_offset); - ImPlot::PlotHeatmap("RowColStride", values[0], updated_num_rows, updated_num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0.505, 0.51), ImPlotPoint(1, 1), hm_flags, row_offset, col_offset, stride_value * sizeof(float) * stride_row_offset, stride_value * sizeof(float) * stride_col_offset); + ImPlot::PlotHeatmap("NoStride", values[0], num_rows, num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0, 0), ImPlotPoint(0.495, 0.49), hm_flags, row_offset, col_offset, sizeof(float) * row_stride_offset, sizeof(float) * col_stride_offset); + ImPlot::PlotHeatmap("RowStride", values[0], updated_num_rows, num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0, 0.51), ImPlotPoint(0.495, 1), hm_flags, row_offset, col_offset, stride_value * sizeof(float) * row_stride_offset, sizeof(float) * col_stride_offset); + ImPlot::PlotHeatmap("ColStride", values[0], num_rows, updated_num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0.505, 0), ImPlotPoint(1, 0.49), hm_flags, row_offset, col_offset, sizeof(float) * row_stride_offset, stride_value * sizeof(float) * col_stride_offset); + ImPlot::PlotHeatmap("RowColStride", values[0], updated_num_rows, updated_num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0.505, 0.51), ImPlotPoint(1, 1), hm_flags, row_offset, col_offset, stride_value * sizeof(float) * row_stride_offset, stride_value * sizeof(float) * col_stride_offset); ImPlot::EndPlot(); } diff --git a/implot_items.cpp b/implot_items.cpp index 30334790..18329a2c 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2485,7 +2485,7 @@ struct GetterHeatmapColMaj { }; template -void RenderHeatmap(ImDrawList& draw_list, 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, bool col_maj, int row_offset, int col_offset, int stride_rows, int stride_cols) { +void RenderHeatmap(ImDrawList& draw_list, 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, bool col_maj, int row_offset, int col_offset, int row_stride, int col_stride) { ImPlotContext& gp = *GImPlot; Transformer2 transformer; if (scale_min == 0 && scale_max == 0) { @@ -2504,7 +2504,7 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d const double yref = reverse_y ? bounds_max.y : bounds_min.y; const double ydir = reverse_y ? -1 : 1; if (col_maj) { - HeatmapIndexerIdx indexer(values, cols, rows, col_offset, row_offset, stride_cols, stride_rows); + HeatmapIndexerIdx indexer(values, cols, rows, col_offset, row_offset, col_stride, row_stride); GetterHeatmapColMaj> getter(indexer, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); RenderPrimitives1(getter); @@ -2533,7 +2533,7 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } } else { - HeatmapIndexerIdx indexer(values, rows, cols, row_offset, col_offset, stride_rows, stride_cols); + HeatmapIndexerIdx indexer(values, rows, cols, row_offset, col_offset, row_stride, col_stride); GetterHeatmapRowMaj> getter(indexer, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); RenderPrimitives1(getter); @@ -2571,7 +2571,7 @@ void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, doub } 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols) { +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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int row_stride, int col_stride) { if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max))) { if (rows <= 0 || cols <= 0) { EndItem(); @@ -2579,14 +2579,14 @@ void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, doub } ImDrawList& draw_list = *GetPlotDrawList(); const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); - RenderHeatmap(draw_list, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true, col_maj, row_offset, col_offset, stride_rows, stride_cols); + RenderHeatmap(draw_list, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true, col_maj, row_offset, col_offset, row_stride, col_stride); EndItem(); } } #define INSTANTIATE_MACRO(T) \ template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset); \ - template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int stride_rows, int stride_cols); + template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int row_stride, int col_stride); CALL_INSTANTIATE_FOR_NUMERIC_TYPES() #undef INSTANTIATE_MACRO From 3ef9dd3ae5feada2498205ca4e2a0986f47c1388 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Sat, 28 Jun 2025 17:35:18 +0200 Subject: [PATCH 20/35] refactor: Multiply the sizeof(float) into the offset of the row/column since it was happening anyway at each stage --- implot_demo.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index 504f9872..a952042a 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1990,13 +1990,13 @@ void Demo_HeatmapOffsetAndStride() { const int updated_num_rows = num_rows / stride_value + (num_rows % stride_value == 0 ? 0 : 1); const int updated_num_cols = num_cols / stride_value + (num_cols % stride_value == 0 ? 0 : 1); // Depending on whether row major is used or col major, the offset applied to the row and the column needs to be updated - const int row_stride_offset = row_major ? 1 : num_cols; - const int col_stride_offset = row_major ? num_rows : 1; + const int row_stride_offset = sizeof(float) * (row_major ? 1 : num_cols); + const int col_stride_offset = sizeof(float) * (row_major ? num_rows : 1); // Draw the plots with the strides applied - ImPlot::PlotHeatmap("NoStride", values[0], num_rows, num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0, 0), ImPlotPoint(0.495, 0.49), hm_flags, row_offset, col_offset, sizeof(float) * row_stride_offset, sizeof(float) * col_stride_offset); - ImPlot::PlotHeatmap("RowStride", values[0], updated_num_rows, num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0, 0.51), ImPlotPoint(0.495, 1), hm_flags, row_offset, col_offset, stride_value * sizeof(float) * row_stride_offset, sizeof(float) * col_stride_offset); - ImPlot::PlotHeatmap("ColStride", values[0], num_rows, updated_num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0.505, 0), ImPlotPoint(1, 0.49), hm_flags, row_offset, col_offset, sizeof(float) * row_stride_offset, stride_value * sizeof(float) * col_stride_offset); - ImPlot::PlotHeatmap("RowColStride", values[0], updated_num_rows, updated_num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0.505, 0.51), ImPlotPoint(1, 1), hm_flags, row_offset, col_offset, stride_value * sizeof(float) * row_stride_offset, stride_value * sizeof(float) * col_stride_offset); + ImPlot::PlotHeatmap("NoStride", values[0], num_rows, num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0, 0), ImPlotPoint(0.495, 0.49), hm_flags, row_offset, col_offset, row_stride_offset, col_stride_offset); + ImPlot::PlotHeatmap("RowStride", values[0], updated_num_rows, num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0, 0.51), ImPlotPoint(0.495, 1), hm_flags, row_offset, col_offset, stride_value * row_stride_offset, col_stride_offset); + ImPlot::PlotHeatmap("ColStride", values[0], num_rows, updated_num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0.505, 0), ImPlotPoint(1, 0.49), hm_flags, row_offset, col_offset, row_stride_offset, stride_value * col_stride_offset); + ImPlot::PlotHeatmap("RowColStride", values[0], updated_num_rows, updated_num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0.505, 0.51), ImPlotPoint(1, 1), hm_flags, row_offset, col_offset, stride_value * row_stride_offset, stride_value * col_stride_offset); ImPlot::EndPlot(); } From 5f09eb70fdd46952b85eb150443dd15859f4a0fb Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Sat, 28 Jun 2025 18:06:06 +0200 Subject: [PATCH 21/35] fix: Change plot names and bust the correct color cache for the correct plots --- implot_demo.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index a952042a..e0e7673a 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1947,7 +1947,8 @@ void Demo_HeatmapOffsetAndStride() { // 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("##HeatmapOffset"); + BustColorCache("##HeatmapOffsetAndStride"); } ImGui::SameLine(); @@ -1972,7 +1973,7 @@ void Demo_HeatmapOffsetAndStride() { ImPlot::PushColormap(map); - if (ImPlot::BeginPlot("##HeatmapOffsets", ImVec2(450, 225), ImPlotFlags_NoLegend | ImPlotFlags_NoMouseText)) { + if (ImPlot::BeginPlot("##HeatmapOffset", ImVec2(450, 225), ImPlotFlags_NoLegend | ImPlotFlags_NoMouseText)) { static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks; ImPlot::SetupAxes(nullptr, nullptr, axes_flags, axes_flags); ImPlot::SetupAxisTicks(ImAxis_X1, 0 + 1.0 / (num_cols * 2.0), 1 - 1.0 / (num_cols * 4.0), num_cols, xlabels); @@ -1983,7 +1984,7 @@ void Demo_HeatmapOffsetAndStride() { ImGui::SameLine(); ImPlot::ColormapScale("##HeatScale", scale_min, scale_max, ImVec2(60, 225)); - if (ImPlot::BeginPlot("##HeatmapStrides", ImVec2(450, 225), ImPlotFlags_NoLegend | ImPlotFlags_NoMouseText)) { + if (ImPlot::BeginPlot("##HeatmapOffsetAndStride", ImVec2(450, 225), ImPlotFlags_NoLegend | ImPlotFlags_NoMouseText)) { static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels; ImPlot::SetupAxes(nullptr, nullptr, axes_flags, axes_flags); // The stride calculations should take into account when it either the row or the column divided by stride does not produces a integer value From 88cc883615f7ee7dcffc835e19ee507484856455 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Sun, 29 Jun 2025 11:39:36 +0200 Subject: [PATCH 22/35] fix: Minor fixes noticed after running it again - The y labels should all be Rx instead of switching over to C - Fix the spacing for the axis ticks --- implot_demo.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index e0e7673a..2f3aece9 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1939,7 +1939,7 @@ void Demo_HeatmapOffsetAndStride() { static float scale_min = 0; static float scale_max = 6.3f; static const char* xlabels[] = { "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "C10", "C11", "C12" }; - static const char* ylabels[] = { "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "C9", "C10", "C11", "C12" }; + static const char* ylabels[] = { "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R12" }; static ImPlotColormap map = ImPlotColormap_Viridis; if (ImPlot::ColormapButton(ImPlot::GetColormapName(map), ImVec2(225, 0), map)) { @@ -1976,8 +1976,8 @@ void Demo_HeatmapOffsetAndStride() { if (ImPlot::BeginPlot("##HeatmapOffset", ImVec2(450, 225), ImPlotFlags_NoLegend | ImPlotFlags_NoMouseText)) { static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks; ImPlot::SetupAxes(nullptr, nullptr, axes_flags, axes_flags); - ImPlot::SetupAxisTicks(ImAxis_X1, 0 + 1.0 / (num_cols * 2.0), 1 - 1.0 / (num_cols * 4.0), num_cols, xlabels); - ImPlot::SetupAxisTicks(ImAxis_Y1, 1 - 1.0 / (num_rows * 2.0), 0 + 1.0 / (num_rows * 4.0), num_rows, ylabels); + ImPlot::SetupAxisTicks(ImAxis_X1, 0 + 1.0 / (num_cols * 2.0), 1 - 1.0 / (num_cols * 2.0), num_cols, xlabels); + ImPlot::SetupAxisTicks(ImAxis_Y1, 1 - 1.0 / (num_rows * 2.0), 0 + 1.0 / (num_rows * 2.0), num_rows, ylabels); ImPlot::PlotHeatmap("Offset", values[0], num_rows, num_cols, scale_min, scale_max, "%g", ImPlotPoint(0, 0), ImPlotPoint(1, 1), hm_flags, row_offset, col_offset); ImPlot::EndPlot(); } From 894500d0c54d3743335632fb4464f88cc37d9a02 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Sun, 29 Jun 2025 12:03:58 +0200 Subject: [PATCH 23/35] fix: Add the row and column stride as 2 independent values that can be controlled - Fix where each of the heatmaps will go in the plot. The position for the row and col stride plot depends on whether the row or column is major --- implot_demo.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index 2f3aece9..80ffd27a 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1965,11 +1965,13 @@ void Demo_HeatmapOffsetAndStride() { const int num_cols = row_major ? 6 : 12; static int row_offset = 0; static int col_offset = 0; - static int stride_value = 2; + static int row_stride_value = 2; + static int col_stride_value = 2; ImGui::SliderInt("Row Offset", &row_offset, -3 * num_rows, 3 * num_rows); ImGui::SliderInt("Column Offset", &col_offset, -3 * num_cols, 3 * num_cols); - ImGui::SliderInt("Stride", &stride_value, 2, 4); + ImGui::SliderInt("Row Stride", &row_stride_value, 1, 4); + ImGui::SliderInt("Col Stride", &col_stride_value, 1, 4); ImPlot::PushColormap(map); @@ -1988,16 +1990,26 @@ void Demo_HeatmapOffsetAndStride() { static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels; ImPlot::SetupAxes(nullptr, nullptr, axes_flags, axes_flags); // The stride calculations should take into account when it either the row or the column divided by stride does not produces a integer value - const int updated_num_rows = num_rows / stride_value + (num_rows % stride_value == 0 ? 0 : 1); - const int updated_num_cols = num_cols / stride_value + (num_cols % stride_value == 0 ? 0 : 1); + const int updated_num_rows = num_rows / row_stride_value + (num_rows % row_stride_value == 0 ? 0 : 1); + const int updated_num_cols = num_cols / col_stride_value + (num_cols % col_stride_value == 0 ? 0 : 1); // Depending on whether row major is used or col major, the offset applied to the row and the column needs to be updated const int row_stride_offset = sizeof(float) * (row_major ? 1 : num_cols); const int col_stride_offset = sizeof(float) * (row_major ? num_rows : 1); + // Plot each of the 4 heatmaps in one of the 4 corners + static const ImPlotPoint plot_positions[4][2] = { + {ImPlotPoint(0, 0), ImPlotPoint(0.495, 0.49)}, // Lower left corner + {ImPlotPoint(0.505, 0), ImPlotPoint(1, 0.49)}, // Upper left corner + {ImPlotPoint(0, 0.51), ImPlotPoint(0.495, 1)}, // Lower right corner + {ImPlotPoint(0.505, 0.51), ImPlotPoint(1, 1)}, // Upper right corner + }; + // Change the position row and col stride depending on whether row or column major is used + const int row_stride_plot_pos = row_major ? 2 : 1; + const int col_stride_plot_pos = !row_major ? 2 : 1; // Draw the plots with the strides applied - ImPlot::PlotHeatmap("NoStride", values[0], num_rows, num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0, 0), ImPlotPoint(0.495, 0.49), hm_flags, row_offset, col_offset, row_stride_offset, col_stride_offset); - ImPlot::PlotHeatmap("RowStride", values[0], updated_num_rows, num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0, 0.51), ImPlotPoint(0.495, 1), hm_flags, row_offset, col_offset, stride_value * row_stride_offset, col_stride_offset); - ImPlot::PlotHeatmap("ColStride", values[0], num_rows, updated_num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0.505, 0), ImPlotPoint(1, 0.49), hm_flags, row_offset, col_offset, row_stride_offset, stride_value * col_stride_offset); - ImPlot::PlotHeatmap("RowColStride", values[0], updated_num_rows, updated_num_cols, scale_min, scale_max, nullptr, ImPlotPoint(0.505, 0.51), ImPlotPoint(1, 1), hm_flags, row_offset, col_offset, stride_value * row_stride_offset, stride_value * col_stride_offset); + ImPlot::PlotHeatmap("NoStride", values[0], num_rows, num_cols, scale_min, scale_max, nullptr, plot_positions[0][0], plot_positions[0][1], hm_flags, row_offset, col_offset, row_stride_offset, col_stride_offset); + ImPlot::PlotHeatmap("RowStride", values[0], updated_num_rows, num_cols, scale_min, scale_max, nullptr, plot_positions[row_stride_plot_pos][0], plot_positions[row_stride_plot_pos][1], hm_flags, row_offset, col_offset, row_stride_value * row_stride_offset, col_stride_offset); + ImPlot::PlotHeatmap("ColStride", values[0], num_rows, updated_num_cols, scale_min, scale_max, nullptr, plot_positions[col_stride_plot_pos][0], plot_positions[col_stride_plot_pos][1], hm_flags, row_offset, col_offset, row_stride_offset, col_stride_value * col_stride_offset); + ImPlot::PlotHeatmap("RowColStride", values[0], updated_num_rows, updated_num_cols, scale_min, scale_max, nullptr, plot_positions[3][0], plot_positions[3][1], hm_flags, row_offset, col_offset, row_stride_value * row_stride_offset, col_stride_value * col_stride_offset); ImPlot::EndPlot(); } From dfa7892425defcdf6b66a47b6fc3a09b39fc47f5 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Sun, 29 Jun 2025 12:18:45 +0200 Subject: [PATCH 24/35] fix: Simplify the plotting by always plotting the row and column stride plots in the same exact location - Ignore the row or column major setting and plot the `RowStride` and `ColStride` in the same location so that when we change the flag the plots in the same locations are changed --- implot_demo.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index 80ffd27a..9e028039 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -2002,13 +2002,10 @@ void Demo_HeatmapOffsetAndStride() { {ImPlotPoint(0, 0.51), ImPlotPoint(0.495, 1)}, // Lower right corner {ImPlotPoint(0.505, 0.51), ImPlotPoint(1, 1)}, // Upper right corner }; - // Change the position row and col stride depending on whether row or column major is used - const int row_stride_plot_pos = row_major ? 2 : 1; - const int col_stride_plot_pos = !row_major ? 2 : 1; // Draw the plots with the strides applied ImPlot::PlotHeatmap("NoStride", values[0], num_rows, num_cols, scale_min, scale_max, nullptr, plot_positions[0][0], plot_positions[0][1], hm_flags, row_offset, col_offset, row_stride_offset, col_stride_offset); - ImPlot::PlotHeatmap("RowStride", values[0], updated_num_rows, num_cols, scale_min, scale_max, nullptr, plot_positions[row_stride_plot_pos][0], plot_positions[row_stride_plot_pos][1], hm_flags, row_offset, col_offset, row_stride_value * row_stride_offset, col_stride_offset); - ImPlot::PlotHeatmap("ColStride", values[0], num_rows, updated_num_cols, scale_min, scale_max, nullptr, plot_positions[col_stride_plot_pos][0], plot_positions[col_stride_plot_pos][1], hm_flags, row_offset, col_offset, row_stride_offset, col_stride_value * col_stride_offset); + ImPlot::PlotHeatmap("RowStride", values[0], updated_num_rows, num_cols, scale_min, scale_max, nullptr, plot_positions[1][0], plot_positions[1][1], hm_flags, row_offset, col_offset, row_stride_value * row_stride_offset, col_stride_offset); + ImPlot::PlotHeatmap("ColStride", values[0], num_rows, updated_num_cols, scale_min, scale_max, nullptr, plot_positions[2][0], plot_positions[2][1], hm_flags, row_offset, col_offset, row_stride_offset, col_stride_value * col_stride_offset); ImPlot::PlotHeatmap("RowColStride", values[0], updated_num_rows, updated_num_cols, scale_min, scale_max, nullptr, plot_positions[3][0], plot_positions[3][1], hm_flags, row_offset, col_offset, row_stride_value * row_stride_offset, col_stride_value * col_stride_offset); ImPlot::EndPlot(); } From c45bce649576668a58ade1f76f0c717db52d4b59 Mon Sep 17 00:00:00 2001 From: Aidan van Wyk <66202856+ACvanWyk@users.noreply.github.com> Date: Sun, 29 Jun 2025 12:50:58 +0200 Subject: [PATCH 25/35] revert: Put back the `.` Accidentally removed the char on one of the commits --- implot.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implot.h b/implot.h index 39ec7405..556170d0 100644 --- a/implot.h +++ b/implot.h @@ -898,7 +898,7 @@ IMPLOT_TMP void PlotInfLines(const char* label_id, const T* values, int count, I IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, ImPlotFormatter fmt, void* fmt_data=nullptr, double angle0=90, ImPlotPieChartFlags flags=0); IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* label_fmt="%.1f", double angle0=90, ImPlotPieChartFlags flags=0); -// Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels +// Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels. IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min = 0, double scale_max = 0, const char* label_fmt = "%.1f", const ImPlotPoint& bounds_min = ImPlotPoint(0, 0), const ImPlotPoint& bounds_max = ImPlotPoint(1, 1), ImPlotHeatmapFlags flags = 0, int row_offset = 0, int col_offset = 0); IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* label_fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int row_stride, int col_stride); From 972a4bee9e92a676281b40882fc4cea36d90f397 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Sun, 29 Jun 2025 21:14:51 +0200 Subject: [PATCH 26/35] fix: Determine the min max array values based on the offset and the stride of the heatmap - Needed to add a helper function that takes in the indexer to determine what the min and max value is. The helper function `ImHeatmapMinMaxArray` is based on the `ImMinMaxArray` function but takes in an indexer and goes over the major and minor indexes to determine where the min and max of the array taking into account the passed in offset and stride --- implot_items.cpp | 69 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/implot_items.cpp b/implot_items.cpp index 18329a2c..57875cde 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2388,26 +2388,31 @@ struct HeatmapIndexerIdx { MinorStride(minor_stride), Type(((MinorOffset == 0) << 0) | ((MajorOffset == 0) << 1) | ((MinorStride == int(sizeof(T))) << 2) | ((MajorStride == int(sizeof(T)) * num_minor) << 3)) { } + template IMPLOT_INLINE double operator()(I idx, int count, int major, int minor, int num_major, int num_minor) const { + return (double)GetData(idx, count, major, minor, num_major, num_minor); + } + + template IMPLOT_INLINE T GetData(I idx, int count, int major, int minor, int num_major, int num_minor) const { // Get the data based based on the type switch (Type) { - case 15: return (double)(Data[idx]); // No offset or stride - case 14: return (double)(Data[(((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]); // Minor offset - case 13: return (double)(Data[(MajorOffset + idx) % count]); // Major offset - case 12: return (double)(Data[(MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]); // Major+minor offset - case 11: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((idx)) * MinorStride)); // Minor stride - case 10: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride)); // Minor stride and minor offset - case 9: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + idx) % count) * MinorStride)); // Minor stride and major offset - case 8: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride)); // Minor stride and major + minor offset - case 7: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + minor * sizeof(T))); // Major stride - case 6: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T))); // Major stride and minor offset - case 5: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + minor * sizeof(T))); // Major stride and major offset - case 4: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T))); // Major stride and major+minor offset - case 3: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor)) * MinorStride)); // Major+minor stride - case 2: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride)); // Major+minor stride and minor offset - case 1: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor)) * MinorStride)); // Major+minor stride and major offset - case 0: return (double)(*(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride)); // Major+minor stride and major+minor offset - default: return (double)(T(0)); + case 15: return Data[idx]; // No offset or stride + case 14: return Data[(((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]; // Minor offset + case 13: return Data[(MajorOffset + idx) % count]; // Major offset + case 12: return Data[(MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]; // Major+minor offset + case 11: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((idx)) * MinorStride); // Minor stride + case 10: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride); // Minor stride and minor offset + case 9: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + idx) % count) * MinorStride); // Minor stride and major offset + case 8: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride); // Minor stride and major + minor offset + case 7: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + minor * sizeof(T)); // Major stride + case 6: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T)); // Major stride and minor offset + case 5: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + minor * sizeof(T)); // Major stride and major offset + case 4: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T)); // Major stride and major+minor offset + case 3: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor)) * MinorStride); // Major+minor stride + case 2: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride); // Major+minor stride and minor offset + case 1: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor)) * MinorStride); // Major+minor stride and major offset + case 0: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride); // Major+minor stride and major+minor offset + default: return T(0); } } const T* const Data; @@ -2484,13 +2489,34 @@ struct GetterHeatmapColMaj { const ImPlotPoint HalfSize; }; +template IMPLOT_INLINE void ImHeatmapMinMaxArray(const _Indexer& indexer, int num_major, int num_minor, T* min_out, T* max_out) { + const int count = num_major * num_minor; + T Min = indexer.GetData(0, count, 0, 0, num_major, num_minor); + T Max = Min; + for (int i = 1; i < count; ++i) { + const int minor = i % num_minor; + const int major = i / num_minor; + const T Value = indexer.GetData(i, count, major, minor, num_major, num_minor); + if (Value < Min) { Min = Value; } + if (Value > Max) { Max = Value; } + } + *min_out = Min; *max_out = Max; +} + template void RenderHeatmap(ImDrawList& draw_list, 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, bool col_maj, int row_offset, int col_offset, int row_stride, int col_stride) { ImPlotContext& gp = *GImPlot; Transformer2 transformer; if (scale_min == 0 && scale_max == 0) { T temp_min, temp_max; - ImMinMaxArray(values,rows*cols,&temp_min,&temp_max); + if (col_maj) { + const HeatmapIndexerIdx indexer(values, cols, rows, col_offset, row_offset, col_stride, row_stride); + ImHeatmapMinMaxArray>(indexer, cols, rows, &temp_min, &temp_max); + } + else { + const HeatmapIndexerIdx indexer(values, rows, cols, row_offset, col_offset, row_stride, col_stride); + ImHeatmapMinMaxArray>(indexer, rows, cols, &temp_min, &temp_max); + } scale_min = (double)temp_min; scale_max = (double)temp_max; } @@ -2504,7 +2530,7 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d const double yref = reverse_y ? bounds_max.y : bounds_min.y; const double ydir = reverse_y ? -1 : 1; if (col_maj) { - HeatmapIndexerIdx indexer(values, cols, rows, col_offset, row_offset, col_stride, row_stride); + const HeatmapIndexerIdx indexer(values, cols, rows, col_offset, row_offset, col_stride, row_stride); GetterHeatmapColMaj> getter(indexer, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); RenderPrimitives1(getter); @@ -2533,7 +2559,7 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } } else { - HeatmapIndexerIdx indexer(values, rows, cols, row_offset, col_offset, row_stride, col_stride); + const HeatmapIndexerIdx indexer(values, rows, cols, row_offset, col_offset, row_stride, col_stride); GetterHeatmapRowMaj> getter(indexer, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); RenderPrimitives1(getter); @@ -2564,8 +2590,7 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset) -{ +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, ImPlotHeatmapFlags flags, int row_offset, int col_offset) { const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); PlotHeatmap(label_id, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, flags, row_offset, col_offset, col_maj ? sizeof(T) : sizeof(T) * cols, !col_maj ? sizeof(T) : sizeof(T) * rows); } From ca7db6165230db7f1268c655950d3ad03f95402b Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Mon, 30 Jun 2025 12:44:19 +0200 Subject: [PATCH 27/35] optimise: Get rid of the switch statement being executed each time a index needs to be determined. When creating the object the type and a function pointer is stored which will be used to call the correct function which has the `Type` as template for the switch statement. In theory the compiler should be smart enough to compile the switch statment out for the loop and thus it is only necessary then to get the data. The overhead added for determining the data at a certain index should now be a lot similar to before the offset and stride was added when looping over each index. Implemented this in [godbolt](https://godbolt.org/z/qWrYq6je1) for testing and comparing what the compiler does --- implot_items.cpp | 81 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 24 deletions(-) diff --git a/implot_items.cpp b/implot_items.cpp index 57875cde..c7e09d77 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2376,6 +2376,31 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES() // [SECTION] PlotHeatmap //----------------------------------------------------------------------------- +template +IMPLOT_INLINE T HeatmapIndexData(const T* const data, I idx, int count, int major, int minor, int num_major, int num_minor, int major_offset, int minor_offset, int major_stride, int minor_stride) +{ + // Get the data based based on the type + switch(Type) { + case 15: return data[idx]; // No offset or stride + case 14: return data[(((minor + minor_offset) < num_minor ? minor_offset : (minor_offset - num_minor)) + idx) % count]; // Minor offset + case 13: return data[(major_offset + idx) % count]; // Major offset + case 12: return data[(major_offset + ((minor + minor_offset) < num_minor ? minor_offset : (minor_offset - num_minor)) + idx) % count]; // Major+minor offset + case 11: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((idx)) * minor_stride); // Minor stride + case 10: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((((minor + minor_offset) < num_minor ? minor_offset : (minor_offset - num_minor)) + idx) % count) * minor_stride); // Minor stride and minor offset + case 9: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major_offset + idx) % count) * minor_stride); // Minor stride and major offset + case 8: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major_offset + ((minor + minor_offset) < num_minor ? minor_offset : (minor_offset - num_minor)) + idx) % count) * minor_stride); // Minor stride and major + minor offset + case 7: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major)) * major_stride + minor * sizeof(T)); // Major stride + case 6: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major)) * major_stride + ((minor + minor_offset) % num_minor) * sizeof(T)); // Major stride and minor offset + case 5: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major + major_offset) % num_major) * major_stride + minor * sizeof(T)); // Major stride and major offset + case 4: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major + major_offset) % num_major) * major_stride + ((minor + minor_offset) % num_minor) * sizeof(T)); // Major stride and major+minor offset + case 3: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major)) * major_stride + (size_t)((minor)) * minor_stride); // Major+minor stride + case 2: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major)) * major_stride + (size_t)((minor + minor_offset) % num_minor) * minor_stride); // Major+minor stride and minor offset + case 1: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major + major_offset) % num_major) * major_stride + (size_t)((minor)) * minor_stride); // Major+minor stride and major offset + case 0: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major + major_offset) % num_major) * major_stride + (size_t)((minor + minor_offset) % num_minor) * minor_stride); // Major+minor stride and major+minor offset + default: return T(0); + } +} + template struct HeatmapIndexerIdx { HeatmapIndexerIdx(const T* data, int num_major, int num_minor, int major_offset, int minor_offset, int major_stride, int minor_stride) : @@ -2385,38 +2410,46 @@ struct HeatmapIndexerIdx { MajorOffset((major_stride == int(sizeof(T)) * num_minor) ? ImPosMod(num_minor * major_offset, num_minor * num_major) : ImPosMod(major_offset, num_major)), MinorOffset(ImPosMod(minor_offset, num_minor)), MajorStride(major_stride), - MinorStride(minor_stride), - Type(((MinorOffset == 0) << 0) | ((MajorOffset == 0) << 1) | ((MinorStride == int(sizeof(T))) << 2) | ((MajorStride == int(sizeof(T)) * num_minor) << 3)) - { } + MinorStride(minor_stride) + { + const int type = ((MinorOffset == 0) << 0) | ((MajorOffset == 0) << 1) | ((MinorStride == int(sizeof(T))) << 2) | ((MajorStride == int(sizeof(T)) * num_minor) << 3); + // Get the data based based on the type + switch(type) { + case 15: IndexDataFunc = &(HeatmapIndexData); break; + case 14: IndexDataFunc = &(HeatmapIndexData); break; + case 13: IndexDataFunc = &(HeatmapIndexData); break; + case 12: IndexDataFunc = &(HeatmapIndexData); break; + case 11: IndexDataFunc = &(HeatmapIndexData); break; + case 10: IndexDataFunc = &(HeatmapIndexData); break; + case 9: IndexDataFunc = &(HeatmapIndexData); break; + case 8: IndexDataFunc = &(HeatmapIndexData); break; + case 7: IndexDataFunc = &(HeatmapIndexData); break; + case 6: IndexDataFunc = &(HeatmapIndexData); break; + case 5: IndexDataFunc = &(HeatmapIndexData); break; + case 4: IndexDataFunc = &(HeatmapIndexData); break; + case 3: IndexDataFunc = &(HeatmapIndexData); break; + case 2: IndexDataFunc = &(HeatmapIndexData); break; + case 1: IndexDataFunc = &(HeatmapIndexData); break; + case 0: IndexDataFunc = &(HeatmapIndexData); break; + } + } template IMPLOT_INLINE double operator()(I idx, int count, int major, int minor, int num_major, int num_minor) const { return (double)GetData(idx, count, major, minor, num_major, num_minor); } template IMPLOT_INLINE T GetData(I idx, int count, int major, int minor, int num_major, int num_minor) const { - // Get the data based based on the type - switch (Type) { - case 15: return Data[idx]; // No offset or stride - case 14: return Data[(((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]; // Minor offset - case 13: return Data[(MajorOffset + idx) % count]; // Major offset - case 12: return Data[(MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]; // Major+minor offset - case 11: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((idx)) * MinorStride); // Minor stride - case 10: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride); // Minor stride and minor offset - case 9: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + idx) % count) * MinorStride); // Minor stride and major offset - case 8: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride); // Minor stride and major + minor offset - case 7: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + minor * sizeof(T)); // Major stride - case 6: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T)); // Major stride and minor offset - case 5: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + minor * sizeof(T)); // Major stride and major offset - case 4: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T)); // Major stride and major+minor offset - case 3: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor)) * MinorStride); // Major+minor stride - case 2: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride); // Major+minor stride and minor offset - case 1: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor)) * MinorStride); // Major+minor stride and major offset - case 0: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride); // Major+minor stride and major+minor offset - default: return T(0); - } + return IndexDataFunc(Data, idx, count, major, minor, num_major, num_minor, MajorOffset, MinorOffset, MajorStride, MinorStride); } const T* const Data; - const int MajorOffset, MinorOffset, MajorStride, MinorStride, Type; + const int MajorOffset, MinorOffset, MajorStride, MinorStride; + + template + struct IndexerTypeHelper + { + typedef K (*type)(const K* const, I, int, int, int, int, int, int, int, int, int); + }; + typename IndexerTypeHelper::type IndexDataFunc; }; template From 38247373121dfd508d798f19a1dadb95229733d3 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Mon, 30 Jun 2025 13:04:37 +0200 Subject: [PATCH 28/35] refactor: Refactor some match other coding style and removed the colormap selection from the `Demo_HeatmapOffsetAndStride` since it is not needed --- implot_demo.cpp | 33 +++++++++------------------------ implot_items.cpp | 3 +-- 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index 9e028039..087a3a1f 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1942,32 +1942,17 @@ void Demo_HeatmapOffsetAndStride() { static const char* ylabels[] = { "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R12" }; static ImPlotColormap map = ImPlotColormap_Viridis; - 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("##HeatmapOffset"); - BustColorCache("##HeatmapOffsetAndStride"); - } - - ImGui::SameLine(); - ImGui::LabelText("##Colormap Index", "%s", "Change Colormap"); - ImGui::SetNextItemWidth(225); - ImGui::DragFloatRange2("Min / Max", &scale_min, &scale_max, 0.01f, -20, 20); - static ImPlotHeatmapFlags hm_flags = 0; - + const bool row_major = (hm_flags & ImPlotHeatmapFlags_ColMajor) == ImPlotHeatmapFlags_ColMajor; + const int num_rows = row_major ? 12 : 6; + const int num_cols = row_major ? 6 : 12; + static int row_offset = 0; + static int col_offset = 0; + static int row_stride_value = 2; + static int col_stride_value = 2; + + ImGui::DragFloatRange2("Min / Max", &scale_min, &scale_max, 0.01f, -20, 20); ImGui::CheckboxFlags("Column Major", (unsigned int*)&hm_flags, ImPlotHeatmapFlags_ColMajor); - - const bool row_major = (hm_flags & ImPlotHeatmapFlags_ColMajor) == ImPlotHeatmapFlags_ColMajor; - const int num_rows = row_major ? 12 : 6; - const int num_cols = row_major ? 6 : 12; - static int row_offset = 0; - static int col_offset = 0; - static int row_stride_value = 2; - static int col_stride_value = 2; - ImGui::SliderInt("Row Offset", &row_offset, -3 * num_rows, 3 * num_rows); ImGui::SliderInt("Column Offset", &col_offset, -3 * num_cols, 3 * num_cols); ImGui::SliderInt("Row Stride", &row_stride_value, 1, 4); diff --git a/implot_items.cpp b/implot_items.cpp index c7e09d77..25bd4f44 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2377,8 +2377,7 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES() //----------------------------------------------------------------------------- template -IMPLOT_INLINE T HeatmapIndexData(const T* const data, I idx, int count, int major, int minor, int num_major, int num_minor, int major_offset, int minor_offset, int major_stride, int minor_stride) -{ +IMPLOT_INLINE T HeatmapIndexData(const T* const data, I idx, int count, int major, int minor, int num_major, int num_minor, int major_offset, int minor_offset, int major_stride, int minor_stride) { // Get the data based based on the type switch(Type) { case 15: return data[idx]; // No offset or stride From a7fd81d1714a246b9f5843ebd332bd8c799fa1da Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Mon, 30 Jun 2025 13:06:25 +0200 Subject: [PATCH 29/35] fix: Fix spacing and replace tabs with spaces --- implot_demo.cpp | 18 +++++++++--------- implot_items.cpp | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index 087a3a1f..c9e6486c 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1943,15 +1943,15 @@ void Demo_HeatmapOffsetAndStride() { static ImPlotColormap map = ImPlotColormap_Viridis; static ImPlotHeatmapFlags hm_flags = 0; - const bool row_major = (hm_flags & ImPlotHeatmapFlags_ColMajor) == ImPlotHeatmapFlags_ColMajor; - const int num_rows = row_major ? 12 : 6; - const int num_cols = row_major ? 6 : 12; - static int row_offset = 0; - static int col_offset = 0; - static int row_stride_value = 2; - static int col_stride_value = 2; - - ImGui::DragFloatRange2("Min / Max", &scale_min, &scale_max, 0.01f, -20, 20); + const bool row_major = (hm_flags & ImPlotHeatmapFlags_ColMajor) == ImPlotHeatmapFlags_ColMajor; + const int num_rows = row_major ? 12 : 6; + const int num_cols = row_major ? 6 : 12; + static int row_offset = 0; + static int col_offset = 0; + static int row_stride_value = 2; + static int col_stride_value = 2; + + ImGui::DragFloatRange2("Min / Max", &scale_min, &scale_max, 0.01f, -20, 20); ImGui::CheckboxFlags("Column Major", (unsigned int*)&hm_flags, ImPlotHeatmapFlags_ColMajor); ImGui::SliderInt("Row Offset", &row_offset, -3 * num_rows, 3 * num_rows); ImGui::SliderInt("Column Offset", &col_offset, -3 * num_cols, 3 * num_cols); diff --git a/implot_items.cpp b/implot_items.cpp index 25bd4f44..98e6d050 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2376,7 +2376,7 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES() // [SECTION] PlotHeatmap //----------------------------------------------------------------------------- -template +template IMPLOT_INLINE T HeatmapIndexData(const T* const data, I idx, int count, int major, int minor, int num_major, int num_minor, int major_offset, int minor_offset, int major_stride, int minor_stride) { // Get the data based based on the type switch(Type) { From 7f3bac6f5dc7e51966dda4016d03e90e38a60c37 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Tue, 1 Jul 2025 20:38:06 +0200 Subject: [PATCH 30/35] Revert "optimise: Get rid of the switch statement being executed each time a index needs to be determined." This reverts commit ca7db6165230db7f1268c655950d3ad03f95402b. Benchmarked this on `Quick-bench` and as far as I can tell the compiler will compile this out at higher optimisation levels. This does not add any improvement. --- implot_items.cpp | 80 +++++++++++++++--------------------------------- 1 file changed, 24 insertions(+), 56 deletions(-) diff --git a/implot_items.cpp b/implot_items.cpp index 98e6d050..57875cde 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2376,30 +2376,6 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES() // [SECTION] PlotHeatmap //----------------------------------------------------------------------------- -template -IMPLOT_INLINE T HeatmapIndexData(const T* const data, I idx, int count, int major, int minor, int num_major, int num_minor, int major_offset, int minor_offset, int major_stride, int minor_stride) { - // Get the data based based on the type - switch(Type) { - case 15: return data[idx]; // No offset or stride - case 14: return data[(((minor + minor_offset) < num_minor ? minor_offset : (minor_offset - num_minor)) + idx) % count]; // Minor offset - case 13: return data[(major_offset + idx) % count]; // Major offset - case 12: return data[(major_offset + ((minor + minor_offset) < num_minor ? minor_offset : (minor_offset - num_minor)) + idx) % count]; // Major+minor offset - case 11: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((idx)) * minor_stride); // Minor stride - case 10: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((((minor + minor_offset) < num_minor ? minor_offset : (minor_offset - num_minor)) + idx) % count) * minor_stride); // Minor stride and minor offset - case 9: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major_offset + idx) % count) * minor_stride); // Minor stride and major offset - case 8: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major_offset + ((minor + minor_offset) < num_minor ? minor_offset : (minor_offset - num_minor)) + idx) % count) * minor_stride); // Minor stride and major + minor offset - case 7: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major)) * major_stride + minor * sizeof(T)); // Major stride - case 6: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major)) * major_stride + ((minor + minor_offset) % num_minor) * sizeof(T)); // Major stride and minor offset - case 5: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major + major_offset) % num_major) * major_stride + minor * sizeof(T)); // Major stride and major offset - case 4: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major + major_offset) % num_major) * major_stride + ((minor + minor_offset) % num_minor) * sizeof(T)); // Major stride and major+minor offset - case 3: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major)) * major_stride + (size_t)((minor)) * minor_stride); // Major+minor stride - case 2: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major)) * major_stride + (size_t)((minor + minor_offset) % num_minor) * minor_stride); // Major+minor stride and minor offset - case 1: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major + major_offset) % num_major) * major_stride + (size_t)((minor)) * minor_stride); // Major+minor stride and major offset - case 0: return *(const T*)(const void*)((const unsigned char*)data + (size_t)((major + major_offset) % num_major) * major_stride + (size_t)((minor + minor_offset) % num_minor) * minor_stride); // Major+minor stride and major+minor offset - default: return T(0); - } -} - template struct HeatmapIndexerIdx { HeatmapIndexerIdx(const T* data, int num_major, int num_minor, int major_offset, int minor_offset, int major_stride, int minor_stride) : @@ -2409,46 +2385,38 @@ struct HeatmapIndexerIdx { MajorOffset((major_stride == int(sizeof(T)) * num_minor) ? ImPosMod(num_minor * major_offset, num_minor * num_major) : ImPosMod(major_offset, num_major)), MinorOffset(ImPosMod(minor_offset, num_minor)), MajorStride(major_stride), - MinorStride(minor_stride) - { - const int type = ((MinorOffset == 0) << 0) | ((MajorOffset == 0) << 1) | ((MinorStride == int(sizeof(T))) << 2) | ((MajorStride == int(sizeof(T)) * num_minor) << 3); - // Get the data based based on the type - switch(type) { - case 15: IndexDataFunc = &(HeatmapIndexData); break; - case 14: IndexDataFunc = &(HeatmapIndexData); break; - case 13: IndexDataFunc = &(HeatmapIndexData); break; - case 12: IndexDataFunc = &(HeatmapIndexData); break; - case 11: IndexDataFunc = &(HeatmapIndexData); break; - case 10: IndexDataFunc = &(HeatmapIndexData); break; - case 9: IndexDataFunc = &(HeatmapIndexData); break; - case 8: IndexDataFunc = &(HeatmapIndexData); break; - case 7: IndexDataFunc = &(HeatmapIndexData); break; - case 6: IndexDataFunc = &(HeatmapIndexData); break; - case 5: IndexDataFunc = &(HeatmapIndexData); break; - case 4: IndexDataFunc = &(HeatmapIndexData); break; - case 3: IndexDataFunc = &(HeatmapIndexData); break; - case 2: IndexDataFunc = &(HeatmapIndexData); break; - case 1: IndexDataFunc = &(HeatmapIndexData); break; - case 0: IndexDataFunc = &(HeatmapIndexData); break; - } - } + MinorStride(minor_stride), + Type(((MinorOffset == 0) << 0) | ((MajorOffset == 0) << 1) | ((MinorStride == int(sizeof(T))) << 2) | ((MajorStride == int(sizeof(T)) * num_minor) << 3)) + { } template IMPLOT_INLINE double operator()(I idx, int count, int major, int minor, int num_major, int num_minor) const { return (double)GetData(idx, count, major, minor, num_major, num_minor); } template IMPLOT_INLINE T GetData(I idx, int count, int major, int minor, int num_major, int num_minor) const { - return IndexDataFunc(Data, idx, count, major, minor, num_major, num_minor, MajorOffset, MinorOffset, MajorStride, MinorStride); + // Get the data based based on the type + switch (Type) { + case 15: return Data[idx]; // No offset or stride + case 14: return Data[(((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]; // Minor offset + case 13: return Data[(MajorOffset + idx) % count]; // Major offset + case 12: return Data[(MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]; // Major+minor offset + case 11: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((idx)) * MinorStride); // Minor stride + case 10: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride); // Minor stride and minor offset + case 9: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + idx) % count) * MinorStride); // Minor stride and major offset + case 8: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride); // Minor stride and major + minor offset + case 7: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + minor * sizeof(T)); // Major stride + case 6: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T)); // Major stride and minor offset + case 5: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + minor * sizeof(T)); // Major stride and major offset + case 4: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T)); // Major stride and major+minor offset + case 3: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor)) * MinorStride); // Major+minor stride + case 2: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride); // Major+minor stride and minor offset + case 1: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor)) * MinorStride); // Major+minor stride and major offset + case 0: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride); // Major+minor stride and major+minor offset + default: return T(0); + } } const T* const Data; - const int MajorOffset, MinorOffset, MajorStride, MinorStride; - - template - struct IndexerTypeHelper - { - typedef K (*type)(const K* const, I, int, int, int, int, int, int, int, int, int); - }; - typename IndexerTypeHelper::type IndexDataFunc; + const int MajorOffset, MinorOffset, MajorStride, MinorStride, Type; }; template From 0510efc88d7cd1eeaf7bc575dbd220231906d4e1 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Thu, 10 Jul 2025 14:42:36 +0200 Subject: [PATCH 31/35] feat: Use only one function call instead of having two separate function calls. - Added support for negative strides - TODO: Should still figure out what the default stride value should be. Currently using INT_MAX but not sure if it is valid? --- implot.h | 7 +++++-- implot_items.cpp | 16 +++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/implot.h b/implot.h index 556170d0..e4fcdac1 100644 --- a/implot.h +++ b/implot.h @@ -48,6 +48,8 @@ #include "imgui.h" #ifndef IMGUI_DISABLE +#include // INT_MAX + //----------------------------------------------------------------------------- // [SECTION] Macros and Defines //----------------------------------------------------------------------------- @@ -68,6 +70,8 @@ #define IMPLOT_AUTO_COL ImVec4(0,0,0,-1) // Macro for templated plotting functions; keeps header clean. #define IMPLOT_TMP template IMPLOT_API +// Default stride to use for heatmaps +#define IMPLOT_DEFAULT_HEATMAP_STRIDE INT_MAX //----------------------------------------------------------------------------- // [SECTION] Enums and Types @@ -899,8 +903,7 @@ IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* label_fmt="%.1f", double angle0=90, ImPlotPieChartFlags flags=0); // Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels. -IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min = 0, double scale_max = 0, const char* label_fmt = "%.1f", const ImPlotPoint& bounds_min = ImPlotPoint(0, 0), const ImPlotPoint& bounds_max = ImPlotPoint(1, 1), ImPlotHeatmapFlags flags = 0, int row_offset = 0, int col_offset = 0); -IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min, double scale_max, const char* label_fmt, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int row_stride, int col_stride); +IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min = 0, double scale_max = 0, const char* label_fmt = "%.1f", const ImPlotPoint& bounds_min = ImPlotPoint(0, 0), const ImPlotPoint& bounds_max = ImPlotPoint(1, 1), ImPlotHeatmapFlags flags = 0, int row_offset = 0, int col_offset = 0, int row_stride = IMPLOT_DEFAULT_HEATMAP_STRIDE, int col_stride = IMPLOT_DEFAULT_HEATMAP_STRIDE); // Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #range is left unspecified, the min/max of #values will be used as the range. // Otherwise, outlier values outside of the range are not binned. The largest bin count or density is returned. diff --git a/implot_items.cpp b/implot_items.cpp index 57875cde..8780f3e8 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2386,7 +2386,7 @@ struct HeatmapIndexerIdx { MinorOffset(ImPosMod(minor_offset, num_minor)), MajorStride(major_stride), MinorStride(minor_stride), - Type(((MinorOffset == 0) << 0) | ((MajorOffset == 0) << 1) | ((MinorStride == int(sizeof(T))) << 2) | ((MajorStride == int(sizeof(T)) * num_minor) << 3)) + Type(((MinorOffset == 0) << 0) | ((MajorOffset == 0) << 1) | ((MinorStride == int(sizeof(T)) && MajorStride > 0) << 2) | ((MajorStride == int(sizeof(T)) * num_minor && MinorStride > 0) << 3)) { } template IMPLOT_INLINE double operator()(I idx, int count, int major, int minor, int num_major, int num_minor) const { @@ -2589,12 +2589,6 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } } -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, ImPlotHeatmapFlags flags, int row_offset, int col_offset) { - const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); - PlotHeatmap(label_id, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, flags, row_offset, col_offset, col_maj ? sizeof(T) : sizeof(T) * cols, !col_maj ? sizeof(T) : sizeof(T) * rows); -} - 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int row_stride, int col_stride) { if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max))) { @@ -2604,14 +2598,14 @@ void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, doub } ImDrawList& draw_list = *GetPlotDrawList(); const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); - RenderHeatmap(draw_list, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true, col_maj, row_offset, col_offset, row_stride, col_stride); + const int updated_row_stride = (row_stride == IMPLOT_DEFAULT_HEATMAP_STRIDE) ? (col_maj ? sizeof(T) : sizeof(T) * cols) : row_stride; + const int updated_col_stride = (col_stride == IMPLOT_DEFAULT_HEATMAP_STRIDE) ? (!col_maj ? sizeof(T) : sizeof(T) * rows) : col_stride; + RenderHeatmap(draw_list, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true, col_maj, row_offset, col_offset, updated_row_stride, updated_col_stride); EndItem(); } } -#define INSTANTIATE_MACRO(T) \ - template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset); \ - template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int row_stride, int col_stride); +#define INSTANTIATE_MACRO(T) template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int row_stride, int col_stride); CALL_INSTANTIATE_FOR_NUMERIC_TYPES() #undef INSTANTIATE_MACRO From f3b37a7764603c3301ce85f3c28bd300c974223f Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Thu, 10 Jul 2025 14:45:58 +0200 Subject: [PATCH 32/35] feat: Add negative stride to demo plot to show that it works fix: It is col major that is true and not row major + Fix the order when it is determined after the checkbox --- implot_demo.cpp | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index c9e6486c..4f8079f1 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1935,7 +1935,7 @@ void Demo_HeatmapOffsetAndStride() { {1.1f, 2.3f, 0.8f, 4.3f, 1.9f, 4.4f, 0.0f, 1.3f, 5.2f, 3.2f, 6.0f, 5.9f}, {0.6f, 0.4f, 0.3f, 0.0f, 3.1f, 0.0f, 0.0f, 1.2f, 1.4f, 4.5f, 4.6f, 4.7f}, {0.7f, 1.7f, 0.6f, 2.6f, 2.2f, 6.2f, 0.0f, 5.1f, 3.4f, 3.8f, 1.1f, 0.9f}, - {0.2f, 2.6f, 6.1f, 1.2f, 4.2f, 5.2f, 0.0f, 4.1f, 1.2f, 2.8f, 4.8f, 0.8f} }; + {0.2f, 2.6f, 6.1f, 1.2f, 4.2f, 5.2f, 0.0f, 4.1f, 1.2f, 2.8f, 4.8f, 0.5f} }; static float scale_min = 0; static float scale_max = 6.3f; static const char* xlabels[] = { "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "C10", "C11", "C12" }; @@ -1943,9 +1943,6 @@ void Demo_HeatmapOffsetAndStride() { static ImPlotColormap map = ImPlotColormap_Viridis; static ImPlotHeatmapFlags hm_flags = 0; - const bool row_major = (hm_flags & ImPlotHeatmapFlags_ColMajor) == ImPlotHeatmapFlags_ColMajor; - const int num_rows = row_major ? 12 : 6; - const int num_cols = row_major ? 6 : 12; static int row_offset = 0; static int col_offset = 0; static int row_stride_value = 2; @@ -1953,10 +1950,15 @@ void Demo_HeatmapOffsetAndStride() { ImGui::DragFloatRange2("Min / Max", &scale_min, &scale_max, 0.01f, -20, 20); ImGui::CheckboxFlags("Column Major", (unsigned int*)&hm_flags, ImPlotHeatmapFlags_ColMajor); - ImGui::SliderInt("Row Offset", &row_offset, -3 * num_rows, 3 * num_rows); - ImGui::SliderInt("Column Offset", &col_offset, -3 * num_cols, 3 * num_cols); - ImGui::SliderInt("Row Stride", &row_stride_value, 1, 4); - ImGui::SliderInt("Col Stride", &col_stride_value, 1, 4); + + const bool col_major = (hm_flags & ImPlotHeatmapFlags_ColMajor) == ImPlotHeatmapFlags_ColMajor; + const int num_rows = col_major ? 12 : 6; + const int num_cols = col_major ? 6 : 12; + + ImGui::SliderInt("Row Offset", &row_offset, -24, 24); + ImGui::SliderInt("Column Offset", &col_offset, -24, 24); + ImGui::SliderInt("Row Stride", &row_stride_value, -7, 7); + ImGui::SliderInt("Col Stride", &col_stride_value, -7, 7); ImPlot::PushColormap(map); @@ -1975,11 +1977,15 @@ void Demo_HeatmapOffsetAndStride() { static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels; ImPlot::SetupAxes(nullptr, nullptr, axes_flags, axes_flags); // The stride calculations should take into account when it either the row or the column divided by stride does not produces a integer value - const int updated_num_rows = num_rows / row_stride_value + (num_rows % row_stride_value == 0 ? 0 : 1); - const int updated_num_cols = num_cols / col_stride_value + (num_cols % col_stride_value == 0 ? 0 : 1); + const int updated_num_rows = row_stride_value == 0 ? num_rows : (num_rows / abs(row_stride_value) + (num_rows % abs(row_stride_value) == 0 ? 0 : 1)); + const int updated_num_cols = col_stride_value == 0 ? num_cols : (num_cols / abs(col_stride_value) + (num_cols % abs(col_stride_value) == 0 ? 0 : 1)); // Depending on whether row major is used or col major, the offset applied to the row and the column needs to be updated - const int row_stride_offset = sizeof(float) * (row_major ? 1 : num_cols); - const int col_stride_offset = sizeof(float) * (row_major ? num_rows : 1); + const int row_stride_offset = sizeof(float) * (col_major ? 1 : num_cols); + const int col_stride_offset = sizeof(float) * (col_major ? num_rows : 1); + // Determine the offset to use in the array to use for the different plots depending on if the strides are positive or negative and if the row or column major has been selected + const int row_stride_plot_array_offset[2] = { (col_major || row_stride_value >= 0) ? 0 : 5, (!col_major || row_stride_value >= 0) ? 0 : 11 }; + const int col_stride_plot_array_offset[2] = { (!col_major || col_stride_value >= 0) ? 0 : 5, (col_major || col_stride_value >= 0) ? 0 : 11 }; + const int row_col_stride_plot_array_offset[2] = { ((!col_major && row_stride_value < 0) || (col_major && col_stride_value < 0)) ? 5 : 0, ((col_major && row_stride_value < 0) || (!col_major && col_stride_value < 0)) ? 11 : 0 }; // Plot each of the 4 heatmaps in one of the 4 corners static const ImPlotPoint plot_positions[4][2] = { {ImPlotPoint(0, 0), ImPlotPoint(0.495, 0.49)}, // Lower left corner @@ -1989,9 +1995,9 @@ void Demo_HeatmapOffsetAndStride() { }; // Draw the plots with the strides applied ImPlot::PlotHeatmap("NoStride", values[0], num_rows, num_cols, scale_min, scale_max, nullptr, plot_positions[0][0], plot_positions[0][1], hm_flags, row_offset, col_offset, row_stride_offset, col_stride_offset); - ImPlot::PlotHeatmap("RowStride", values[0], updated_num_rows, num_cols, scale_min, scale_max, nullptr, plot_positions[1][0], plot_positions[1][1], hm_flags, row_offset, col_offset, row_stride_value * row_stride_offset, col_stride_offset); - ImPlot::PlotHeatmap("ColStride", values[0], num_rows, updated_num_cols, scale_min, scale_max, nullptr, plot_positions[2][0], plot_positions[2][1], hm_flags, row_offset, col_offset, row_stride_offset, col_stride_value * col_stride_offset); - ImPlot::PlotHeatmap("RowColStride", values[0], updated_num_rows, updated_num_cols, scale_min, scale_max, nullptr, plot_positions[3][0], plot_positions[3][1], hm_flags, row_offset, col_offset, row_stride_value * row_stride_offset, col_stride_value * col_stride_offset); + ImPlot::PlotHeatmap("RowStride", &values[row_stride_plot_array_offset[0]][row_stride_plot_array_offset[1]], updated_num_rows, num_cols, scale_min, scale_max, nullptr, plot_positions[1][0], plot_positions[1][1], hm_flags, row_offset, col_offset, row_stride_value * row_stride_offset, col_stride_offset); + ImPlot::PlotHeatmap("ColStride", &values[col_stride_plot_array_offset[0]][col_stride_plot_array_offset[1]], num_rows, updated_num_cols, scale_min, scale_max, nullptr, plot_positions[2][0], plot_positions[2][1], hm_flags, row_offset, col_offset, row_stride_offset, col_stride_value * col_stride_offset); + ImPlot::PlotHeatmap("RowColStride", &values[row_col_stride_plot_array_offset[0]][row_col_stride_plot_array_offset[1]], updated_num_rows, updated_num_cols, scale_min, scale_max, nullptr, plot_positions[3][0], plot_positions[3][1], hm_flags, row_offset, col_offset, row_stride_value * row_stride_offset, col_stride_value * col_stride_offset); ImPlot::EndPlot(); } From 5b714f7b208b7ef7be5204429ee1e978a8a8ca6d Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Thu, 10 Jul 2025 15:05:38 +0200 Subject: [PATCH 33/35] fix: Also take into account the negative minor stride when determining the major offset --- implot_items.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/implot_items.cpp b/implot_items.cpp index 8780f3e8..fab1673f 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2382,7 +2382,7 @@ struct HeatmapIndexerIdx { Data(data), // If the stride does not have to be applied then use the major offset index as the number of major columns to shift by instead of applying the shift for the indexes // It is a bit simpler to only have to append a constant to the index instead of having to add a constant to first determine the rows and then column offsets - MajorOffset((major_stride == int(sizeof(T)) * num_minor) ? ImPosMod(num_minor * major_offset, num_minor * num_major) : ImPosMod(major_offset, num_major)), + MajorOffset((major_stride == int(sizeof(T)) * num_minor && minor_stride > 0) ? ImPosMod(num_minor * major_offset, num_minor * num_major) : ImPosMod(major_offset, num_major)), MinorOffset(ImPosMod(minor_offset, num_minor)), MajorStride(major_stride), MinorStride(minor_stride), From 5ff063b5c63770aff17fb0fe4d3b1b9522356e75 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Fri, 11 Jul 2025 16:23:24 +0200 Subject: [PATCH 34/35] fix: Get rid of abs function call doing nothing + Fix comments --- implot_demo.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index 4f8079f1..89ff50ee 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1976,24 +1976,24 @@ void Demo_HeatmapOffsetAndStride() { if (ImPlot::BeginPlot("##HeatmapOffsetAndStride", ImVec2(450, 225), ImPlotFlags_NoLegend | ImPlotFlags_NoMouseText)) { static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels; ImPlot::SetupAxes(nullptr, nullptr, axes_flags, axes_flags); - // The stride calculations should take into account when it either the row or the column divided by stride does not produces a integer value - const int updated_num_rows = row_stride_value == 0 ? num_rows : (num_rows / abs(row_stride_value) + (num_rows % abs(row_stride_value) == 0 ? 0 : 1)); - const int updated_num_cols = col_stride_value == 0 ? num_cols : (num_cols / abs(col_stride_value) + (num_cols % abs(col_stride_value) == 0 ? 0 : 1)); - // Depending on whether row major is used or col major, the offset applied to the row and the column needs to be updated + // The updated number of rows and columns should take into account when it either the row or the column divided by stride does not produces a integer value + const int updated_num_rows = row_stride_value == 0 ? num_rows : (num_rows / abs(row_stride_value) + (num_rows % row_stride_value == 0 ? 0 : 1)); + const int updated_num_cols = col_stride_value == 0 ? num_cols : (num_cols / abs(col_stride_value) + (num_cols % col_stride_value == 0 ? 0 : 1)); + // Depending on whether row major is used or col major, the stride offset applied to the row and the column needs to be updated const int row_stride_offset = sizeof(float) * (col_major ? 1 : num_cols); const int col_stride_offset = sizeof(float) * (col_major ? num_rows : 1); // Determine the offset to use in the array to use for the different plots depending on if the strides are positive or negative and if the row or column major has been selected const int row_stride_plot_array_offset[2] = { (col_major || row_stride_value >= 0) ? 0 : 5, (!col_major || row_stride_value >= 0) ? 0 : 11 }; const int col_stride_plot_array_offset[2] = { (!col_major || col_stride_value >= 0) ? 0 : 5, (col_major || col_stride_value >= 0) ? 0 : 11 }; const int row_col_stride_plot_array_offset[2] = { ((!col_major && row_stride_value < 0) || (col_major && col_stride_value < 0)) ? 5 : 0, ((col_major && row_stride_value < 0) || (!col_major && col_stride_value < 0)) ? 11 : 0 }; - // Plot each of the 4 heatmaps in one of the 4 corners + // Plot each of the 4 heatmaps in each of the 4 corners with a small gap between each plot static const ImPlotPoint plot_positions[4][2] = { {ImPlotPoint(0, 0), ImPlotPoint(0.495, 0.49)}, // Lower left corner {ImPlotPoint(0.505, 0), ImPlotPoint(1, 0.49)}, // Upper left corner {ImPlotPoint(0, 0.51), ImPlotPoint(0.495, 1)}, // Lower right corner {ImPlotPoint(0.505, 0.51), ImPlotPoint(1, 1)}, // Upper right corner }; - // Draw the plots with the strides applied + // Draw each of the 4 plots with the offsets and strides applied to each plot ImPlot::PlotHeatmap("NoStride", values[0], num_rows, num_cols, scale_min, scale_max, nullptr, plot_positions[0][0], plot_positions[0][1], hm_flags, row_offset, col_offset, row_stride_offset, col_stride_offset); ImPlot::PlotHeatmap("RowStride", &values[row_stride_plot_array_offset[0]][row_stride_plot_array_offset[1]], updated_num_rows, num_cols, scale_min, scale_max, nullptr, plot_positions[1][0], plot_positions[1][1], hm_flags, row_offset, col_offset, row_stride_value * row_stride_offset, col_stride_offset); ImPlot::PlotHeatmap("ColStride", &values[col_stride_plot_array_offset[0]][col_stride_plot_array_offset[1]], num_rows, updated_num_cols, scale_min, scale_max, nullptr, plot_positions[2][0], plot_positions[2][1], hm_flags, row_offset, col_offset, row_stride_offset, col_stride_value * col_stride_offset); From 07be6a8feeb6762a4f6adbffc83b71dca97a4a06 Mon Sep 17 00:00:00 2001 From: ACvanWyk <66202856+ACvanWyk@users.noreply.github.com> Date: Wed, 25 Jun 2025 22:41:17 +0200 Subject: [PATCH 35/35] feat: Add support for offsets and strides for both the rows and the colums in the heatmap - Works for both row and column major heatmap plots and depending on the major selected it will change the behaviour of the offsets and strides - The offset shifts the rows or columns by a positive or negative integer value to the data - Can apply a positive or negative strides to the data that is used in the heatmap --- implot.h | 6 +- implot_demo.cpp | 76 +++++++++++++++++++++++++ implot_items.cpp | 140 ++++++++++++++++++++++++++++++++++++----------- 3 files changed, 188 insertions(+), 34 deletions(-) diff --git a/implot.h b/implot.h index ae1f6000..e4fcdac1 100644 --- a/implot.h +++ b/implot.h @@ -48,6 +48,8 @@ #include "imgui.h" #ifndef IMGUI_DISABLE +#include // INT_MAX + //----------------------------------------------------------------------------- // [SECTION] Macros and Defines //----------------------------------------------------------------------------- @@ -68,6 +70,8 @@ #define IMPLOT_AUTO_COL ImVec4(0,0,0,-1) // Macro for templated plotting functions; keeps header clean. #define IMPLOT_TMP template IMPLOT_API +// Default stride to use for heatmaps +#define IMPLOT_DEFAULT_HEATMAP_STRIDE INT_MAX //----------------------------------------------------------------------------- // [SECTION] Enums and Types @@ -899,7 +903,7 @@ IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int IMPLOT_TMP void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, const char* label_fmt="%.1f", double angle0=90, ImPlotPieChartFlags flags=0); // Plots a 2D heatmap chart. Values are expected to be in row-major order by default. Leave #scale_min and scale_max both at 0 for automatic color scaling, or set them to a predefined range. #label_fmt can be set to nullptr for no labels. -IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min=0, double scale_max=0, const char* label_fmt="%.1f", const ImPlotPoint& bounds_min=ImPlotPoint(0,0), const ImPlotPoint& bounds_max=ImPlotPoint(1,1), ImPlotHeatmapFlags flags=0); +IMPLOT_TMP void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, double scale_min = 0, double scale_max = 0, const char* label_fmt = "%.1f", const ImPlotPoint& bounds_min = ImPlotPoint(0, 0), const ImPlotPoint& bounds_max = ImPlotPoint(1, 1), ImPlotHeatmapFlags flags = 0, int row_offset = 0, int col_offset = 0, int row_stride = IMPLOT_DEFAULT_HEATMAP_STRIDE, int col_stride = IMPLOT_DEFAULT_HEATMAP_STRIDE); // Plots a horizontal histogram. #bins can be a positive integer or an ImPlotBin_ method. If #range is left unspecified, the min/max of #values will be used as the range. // Otherwise, outlier values outside of the range are not binned. The largest bin count or density is returned. diff --git a/implot_demo.cpp b/implot_demo.cpp index 47298d64..89ff50ee 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1929,6 +1929,81 @@ void Demo_OffsetAndStride() { // offset++; uncomment for animation! } +void Demo_HeatmapOffsetAndStride() { + static float values[6][12] = { {0.8f, 2.8f, 2.5f, 3.9f, 0.1f, 4.0f, 0.0f, 5.1f, 2.3f, 0.7f, 4.2f, 1.2f}, + {2.4f, 0.2f, 4.0f, 1.0f, 2.7f, 0.0f, 0.0f, 3.2f, 3.5f, 2.1f, 1.3f, 1.4f}, + {1.1f, 2.3f, 0.8f, 4.3f, 1.9f, 4.4f, 0.0f, 1.3f, 5.2f, 3.2f, 6.0f, 5.9f}, + {0.6f, 0.4f, 0.3f, 0.0f, 3.1f, 0.0f, 0.0f, 1.2f, 1.4f, 4.5f, 4.6f, 4.7f}, + {0.7f, 1.7f, 0.6f, 2.6f, 2.2f, 6.2f, 0.0f, 5.1f, 3.4f, 3.8f, 1.1f, 0.9f}, + {0.2f, 2.6f, 6.1f, 1.2f, 4.2f, 5.2f, 0.0f, 4.1f, 1.2f, 2.8f, 4.8f, 0.5f} }; + static float scale_min = 0; + static float scale_max = 6.3f; + static const char* xlabels[] = { "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "C10", "C11", "C12" }; + static const char* ylabels[] = { "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R12" }; + + static ImPlotColormap map = ImPlotColormap_Viridis; + static ImPlotHeatmapFlags hm_flags = 0; + static int row_offset = 0; + static int col_offset = 0; + static int row_stride_value = 2; + static int col_stride_value = 2; + + ImGui::DragFloatRange2("Min / Max", &scale_min, &scale_max, 0.01f, -20, 20); + ImGui::CheckboxFlags("Column Major", (unsigned int*)&hm_flags, ImPlotHeatmapFlags_ColMajor); + + const bool col_major = (hm_flags & ImPlotHeatmapFlags_ColMajor) == ImPlotHeatmapFlags_ColMajor; + const int num_rows = col_major ? 12 : 6; + const int num_cols = col_major ? 6 : 12; + + ImGui::SliderInt("Row Offset", &row_offset, -24, 24); + ImGui::SliderInt("Column Offset", &col_offset, -24, 24); + ImGui::SliderInt("Row Stride", &row_stride_value, -7, 7); + ImGui::SliderInt("Col Stride", &col_stride_value, -7, 7); + + ImPlot::PushColormap(map); + + if (ImPlot::BeginPlot("##HeatmapOffset", ImVec2(450, 225), ImPlotFlags_NoLegend | ImPlotFlags_NoMouseText)) { + static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks; + ImPlot::SetupAxes(nullptr, nullptr, axes_flags, axes_flags); + ImPlot::SetupAxisTicks(ImAxis_X1, 0 + 1.0 / (num_cols * 2.0), 1 - 1.0 / (num_cols * 2.0), num_cols, xlabels); + ImPlot::SetupAxisTicks(ImAxis_Y1, 1 - 1.0 / (num_rows * 2.0), 0 + 1.0 / (num_rows * 2.0), num_rows, ylabels); + ImPlot::PlotHeatmap("Offset", values[0], num_rows, num_cols, scale_min, scale_max, "%g", ImPlotPoint(0, 0), ImPlotPoint(1, 1), hm_flags, row_offset, col_offset); + ImPlot::EndPlot(); + } + ImGui::SameLine(); + ImPlot::ColormapScale("##HeatScale", scale_min, scale_max, ImVec2(60, 225)); + + if (ImPlot::BeginPlot("##HeatmapOffsetAndStride", ImVec2(450, 225), ImPlotFlags_NoLegend | ImPlotFlags_NoMouseText)) { + static ImPlotAxisFlags axes_flags = ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels; + ImPlot::SetupAxes(nullptr, nullptr, axes_flags, axes_flags); + // The updated number of rows and columns should take into account when it either the row or the column divided by stride does not produces a integer value + const int updated_num_rows = row_stride_value == 0 ? num_rows : (num_rows / abs(row_stride_value) + (num_rows % row_stride_value == 0 ? 0 : 1)); + const int updated_num_cols = col_stride_value == 0 ? num_cols : (num_cols / abs(col_stride_value) + (num_cols % col_stride_value == 0 ? 0 : 1)); + // Depending on whether row major is used or col major, the stride offset applied to the row and the column needs to be updated + const int row_stride_offset = sizeof(float) * (col_major ? 1 : num_cols); + const int col_stride_offset = sizeof(float) * (col_major ? num_rows : 1); + // Determine the offset to use in the array to use for the different plots depending on if the strides are positive or negative and if the row or column major has been selected + const int row_stride_plot_array_offset[2] = { (col_major || row_stride_value >= 0) ? 0 : 5, (!col_major || row_stride_value >= 0) ? 0 : 11 }; + const int col_stride_plot_array_offset[2] = { (!col_major || col_stride_value >= 0) ? 0 : 5, (col_major || col_stride_value >= 0) ? 0 : 11 }; + const int row_col_stride_plot_array_offset[2] = { ((!col_major && row_stride_value < 0) || (col_major && col_stride_value < 0)) ? 5 : 0, ((col_major && row_stride_value < 0) || (!col_major && col_stride_value < 0)) ? 11 : 0 }; + // Plot each of the 4 heatmaps in each of the 4 corners with a small gap between each plot + static const ImPlotPoint plot_positions[4][2] = { + {ImPlotPoint(0, 0), ImPlotPoint(0.495, 0.49)}, // Lower left corner + {ImPlotPoint(0.505, 0), ImPlotPoint(1, 0.49)}, // Upper left corner + {ImPlotPoint(0, 0.51), ImPlotPoint(0.495, 1)}, // Lower right corner + {ImPlotPoint(0.505, 0.51), ImPlotPoint(1, 1)}, // Upper right corner + }; + // Draw each of the 4 plots with the offsets and strides applied to each plot + ImPlot::PlotHeatmap("NoStride", values[0], num_rows, num_cols, scale_min, scale_max, nullptr, plot_positions[0][0], plot_positions[0][1], hm_flags, row_offset, col_offset, row_stride_offset, col_stride_offset); + ImPlot::PlotHeatmap("RowStride", &values[row_stride_plot_array_offset[0]][row_stride_plot_array_offset[1]], updated_num_rows, num_cols, scale_min, scale_max, nullptr, plot_positions[1][0], plot_positions[1][1], hm_flags, row_offset, col_offset, row_stride_value * row_stride_offset, col_stride_offset); + ImPlot::PlotHeatmap("ColStride", &values[col_stride_plot_array_offset[0]][col_stride_plot_array_offset[1]], num_rows, updated_num_cols, scale_min, scale_max, nullptr, plot_positions[2][0], plot_positions[2][1], hm_flags, row_offset, col_offset, row_stride_offset, col_stride_value * col_stride_offset); + ImPlot::PlotHeatmap("RowColStride", &values[row_col_stride_plot_array_offset[0]][row_col_stride_plot_array_offset[1]], updated_num_rows, updated_num_cols, scale_min, scale_max, nullptr, plot_positions[3][0], plot_positions[3][1], hm_flags, row_offset, col_offset, row_stride_value * row_stride_offset, col_stride_value * col_stride_offset); + ImPlot::EndPlot(); + } + + ImPlot::PopColormap(); +} + //----------------------------------------------------------------------------- void Demo_CustomDataAndGetters() { @@ -2278,6 +2353,7 @@ void ShowDemoWindow(bool* p_open) { } if (ImGui::BeginTabItem("Tools")) { DemoHeader("Offset and Stride", Demo_OffsetAndStride); + DemoHeader("Heatmap Offset and Stride", Demo_HeatmapOffsetAndStride); DemoHeader("Drag Points", Demo_DragPoints); DemoHeader("Drag Lines", Demo_DragLines); DemoHeader("Drag Rects", Demo_DragRects); diff --git a/implot_items.cpp b/implot_items.cpp index f7de3465..fab1673f 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -2377,9 +2377,52 @@ CALL_INSTANTIATE_FOR_NUMERIC_TYPES() //----------------------------------------------------------------------------- template +struct HeatmapIndexerIdx { + HeatmapIndexerIdx(const T* data, int num_major, int num_minor, int major_offset, int minor_offset, int major_stride, int minor_stride) : + Data(data), + // If the stride does not have to be applied then use the major offset index as the number of major columns to shift by instead of applying the shift for the indexes + // It is a bit simpler to only have to append a constant to the index instead of having to add a constant to first determine the rows and then column offsets + MajorOffset((major_stride == int(sizeof(T)) * num_minor && minor_stride > 0) ? ImPosMod(num_minor * major_offset, num_minor * num_major) : ImPosMod(major_offset, num_major)), + MinorOffset(ImPosMod(minor_offset, num_minor)), + MajorStride(major_stride), + MinorStride(minor_stride), + Type(((MinorOffset == 0) << 0) | ((MajorOffset == 0) << 1) | ((MinorStride == int(sizeof(T)) && MajorStride > 0) << 2) | ((MajorStride == int(sizeof(T)) * num_minor && MinorStride > 0) << 3)) + { } + + template IMPLOT_INLINE double operator()(I idx, int count, int major, int minor, int num_major, int num_minor) const { + return (double)GetData(idx, count, major, minor, num_major, num_minor); + } + + template IMPLOT_INLINE T GetData(I idx, int count, int major, int minor, int num_major, int num_minor) const { + // Get the data based based on the type + switch (Type) { + case 15: return Data[idx]; // No offset or stride + case 14: return Data[(((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]; // Minor offset + case 13: return Data[(MajorOffset + idx) % count]; // Major offset + case 12: return Data[(MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count]; // Major+minor offset + case 11: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((idx)) * MinorStride); // Minor stride + case 10: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride); // Minor stride and minor offset + case 9: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + idx) % count) * MinorStride); // Minor stride and major offset + case 8: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((MajorOffset + ((minor + MinorOffset) < num_minor ? MinorOffset : (MinorOffset - num_minor)) + idx) % count) * MinorStride); // Minor stride and major + minor offset + case 7: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + minor * sizeof(T)); // Major stride + case 6: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T)); // Major stride and minor offset + case 5: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + minor * sizeof(T)); // Major stride and major offset + case 4: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + ((minor + MinorOffset) % num_minor) * sizeof(T)); // Major stride and major+minor offset + case 3: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor)) * MinorStride); // Major+minor stride + case 2: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major)) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride); // Major+minor stride and minor offset + case 1: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor)) * MinorStride); // Major+minor stride and major offset + case 0: return *(const T*)(const void*)((const unsigned char*)Data + (size_t)((major + MajorOffset) % num_major) * MajorStride + (size_t)((minor + MinorOffset) % num_minor) * MinorStride); // Major+minor stride and major+minor offset + default: return T(0); + } + } + const T* const Data; + const int MajorOffset, MinorOffset, MajorStride, MinorStride, Type; +}; + +template struct GetterHeatmapRowMaj { - GetterHeatmapRowMaj(const T* values, int rows, int cols, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : - Values(values), + GetterHeatmapRowMaj(const _Indexer& indexer, int rows, int cols, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : + Indexer(indexer), Count(rows*cols), Rows(rows), Cols(cols), @@ -2393,9 +2436,9 @@ struct GetterHeatmapRowMaj { HalfSize(Width*0.5, Height*0.5) { } template IMPLOT_INLINE RectC operator()(I idx) const { - double val = (double)Values[idx]; const int r = idx / Cols; const int c = idx % Cols; + double val = Indexer(idx, Count, r, c, Rows, Cols); const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height)); RectC rect; rect.Pos = p; @@ -2405,16 +2448,16 @@ struct GetterHeatmapRowMaj { rect.Color = gp.ColormapData.LerpTable(gp.Style.Colormap, t); return rect; } - const T* const Values; + const _Indexer& Indexer; const int Count, Rows, Cols; const double ScaleMin, ScaleMax, Width, Height, XRef, YRef, YDir; const ImPlotPoint HalfSize; }; -template +template struct GetterHeatmapColMaj { - GetterHeatmapColMaj(const T* values, int rows, int cols, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : - Values(values), + GetterHeatmapColMaj(const _Indexer& indexer, int rows, int cols, double scale_min, double scale_max, double width, double height, double xref, double yref, double ydir) : + Indexer(indexer), Count(rows*cols), Rows(rows), Cols(cols), @@ -2428,9 +2471,9 @@ struct GetterHeatmapColMaj { HalfSize(Width*0.5, Height*0.5) { } template IMPLOT_INLINE RectC operator()(I idx) const { - double val = (double)Values[idx]; const int r = idx % Rows; const int c = idx / Rows; + double val = Indexer(idx, Count, c, r, Cols, Rows); const ImPlotPoint p(XRef + HalfSize.x + c*Width, YRef + YDir * (HalfSize.y + r*Height)); RectC rect; rect.Pos = p; @@ -2440,19 +2483,40 @@ struct GetterHeatmapColMaj { rect.Color = gp.ColormapData.LerpTable(gp.Style.Colormap, t); return rect; } - const T* const Values; + const _Indexer& Indexer; const int Count, Rows, Cols; const double ScaleMin, ScaleMax, Width, Height, XRef, YRef, YDir; const ImPlotPoint HalfSize; }; +template IMPLOT_INLINE void ImHeatmapMinMaxArray(const _Indexer& indexer, int num_major, int num_minor, T* min_out, T* max_out) { + const int count = num_major * num_minor; + T Min = indexer.GetData(0, count, 0, 0, num_major, num_minor); + T Max = Min; + for (int i = 1; i < count; ++i) { + const int minor = i % num_minor; + const int major = i / num_minor; + const T Value = indexer.GetData(i, count, major, minor, num_major, num_minor); + if (Value < Min) { Min = Value; } + if (Value > Max) { Max = Value; } + } + *min_out = Min; *max_out = Max; +} + template -void RenderHeatmap(ImDrawList& draw_list, 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, bool col_maj) { +void RenderHeatmap(ImDrawList& draw_list, 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, bool col_maj, int row_offset, int col_offset, int row_stride, int col_stride) { ImPlotContext& gp = *GImPlot; Transformer2 transformer; if (scale_min == 0 && scale_max == 0) { T temp_min, temp_max; - ImMinMaxArray(values,rows*cols,&temp_min,&temp_max); + if (col_maj) { + const HeatmapIndexerIdx indexer(values, cols, rows, col_offset, row_offset, col_stride, row_stride); + ImHeatmapMinMaxArray>(indexer, cols, rows, &temp_min, &temp_max); + } + else { + const HeatmapIndexerIdx indexer(values, rows, cols, row_offset, col_offset, row_stride, col_stride); + ImHeatmapMinMaxArray>(indexer, rows, cols, &temp_min, &temp_max); + } scale_min = (double)temp_min; scale_max = (double)temp_max; } @@ -2466,20 +2530,15 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d const double yref = reverse_y ? bounds_max.y : bounds_min.y; const double ydir = reverse_y ? -1 : 1; if (col_maj) { - GetterHeatmapColMaj getter(values, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); - RenderPrimitives1(getter); - } - else { - GetterHeatmapRowMaj getter(values, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); + const HeatmapIndexerIdx indexer(values, cols, rows, col_offset, row_offset, col_stride, row_stride); + GetterHeatmapColMaj> getter(indexer, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); RenderPrimitives1(getter); - } - // labels - if (fmt != nullptr) { - 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; - if (col_maj) { + + if (fmt != nullptr) { + const double w = getter.Width; + const double h = getter.Height; + const int count = getter.Count; + int i = 0; for (int c = 0; c < cols; ++c) { for (int r = 0; r < rows; ++r) { ImPlotPoint p; @@ -2487,9 +2546,10 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d p.y = yref + ydir * (0.5*h + r*h); ImVec2 px = transformer(p); char buff[32]; - ImFormatString(buff, 32, fmt, values[i]); + double val = indexer(i, count, c, r, cols, rows); + ImFormatString(buff, 32, fmt, val); ImVec2 size = ImGui::CalcTextSize(buff); - double t = ImClamp(ImRemap01((double)values[i], scale_min, scale_max),0.0,1.0); + double t = ImClamp(ImRemap01(val, scale_min, scale_max), 0.0, 1.0); ImVec4 color = SampleColormap((float)t); ImU32 col = CalcTextColor(color); draw_list.AddText(px - size * 0.5f, col, buff); @@ -2497,7 +2557,17 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } } } - else { + } + else { + const HeatmapIndexerIdx indexer(values, rows, cols, row_offset, col_offset, row_stride, col_stride); + GetterHeatmapRowMaj> getter(indexer, rows, cols, scale_min, scale_max, (bounds_max.x - bounds_min.x) / cols, (bounds_max.y - bounds_min.y) / rows, bounds_min.x, yref, ydir); + RenderPrimitives1(getter); + + if (fmt != nullptr) { + const double w = getter.Width; + const double h = getter.Height; + const int count = getter.Count; + int i = 0; for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { ImPlotPoint p; @@ -2505,9 +2575,10 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d p.y = yref + ydir * (0.5*h + r*h); ImVec2 px = transformer(p); char buff[32]; - ImFormatString(buff, 32, fmt, values[i]); + double val = indexer(i, count, r, c, rows, cols); + ImFormatString(buff, 32, fmt, val); ImVec2 size = ImGui::CalcTextSize(buff); - double t = ImClamp(ImRemap01((double)values[i], scale_min, scale_max),0.0,1.0); + double t = ImClamp(ImRemap01(val, scale_min, scale_max), 0.0, 1.0); ImVec4 color = SampleColormap((float)t); ImU32 col = CalcTextColor(color); draw_list.AddText(px - size * 0.5f, col, buff); @@ -2519,7 +2590,7 @@ void RenderHeatmap(ImDrawList& draw_list, const T* values, int rows, int cols, d } 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, ImPlotHeatmapFlags flags) { +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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int row_stride, int col_stride) { if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max))) { if (rows <= 0 || cols <= 0) { EndItem(); @@ -2527,11 +2598,14 @@ void PlotHeatmap(const char* label_id, const T* values, int rows, int cols, doub } ImDrawList& draw_list = *GetPlotDrawList(); const bool col_maj = ImHasFlag(flags, ImPlotHeatmapFlags_ColMajor); - RenderHeatmap(draw_list, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true, col_maj); + const int updated_row_stride = (row_stride == IMPLOT_DEFAULT_HEATMAP_STRIDE) ? (col_maj ? sizeof(T) : sizeof(T) * cols) : row_stride; + const int updated_col_stride = (col_stride == IMPLOT_DEFAULT_HEATMAP_STRIDE) ? (!col_maj ? sizeof(T) : sizeof(T) * rows) : col_stride; + RenderHeatmap(draw_list, values, rows, cols, scale_min, scale_max, fmt, bounds_min, bounds_max, true, col_maj, row_offset, col_offset, updated_row_stride, updated_col_stride); EndItem(); } } -#define INSTANTIATE_MACRO(T) template IMPLOT_API 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, ImPlotHeatmapFlags flags); + +#define INSTANTIATE_MACRO(T) template IMPLOT_API 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, ImPlotHeatmapFlags flags, int row_offset, int col_offset, int row_stride, int col_stride); CALL_INSTANTIATE_FOR_NUMERIC_TYPES() #undef INSTANTIATE_MACRO @@ -2694,7 +2768,7 @@ double PlotHistogram2D(const char* label_id, const T* xs, const T* ys, int count return max_count; } ImDrawList& draw_list = *GetPlotDrawList(); - RenderHeatmap(draw_list, &bin_counts.Data[0], y_bins, x_bins, 0, max_count, nullptr, range.Min(), range.Max(), false, col_maj); + RenderHeatmap(draw_list, &bin_counts.Data[0], y_bins, x_bins, 0, max_count, nullptr, range.Min(), range.Max(), false, col_maj, 0, 0, col_maj ? sizeof(T) : sizeof(T) * x_bins, !col_maj ? sizeof(T) : sizeof(T) * y_bins); EndItem(); } return max_count;