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;