diff --git a/implot.cpp b/implot.cpp index 5910e93d..05b8503c 100644 --- a/implot.cpp +++ b/implot.cpp @@ -4596,6 +4596,19 @@ ImVec4 SampleColormap(float t, ImPlotColormap cmap) { return ImGui::ColorConvertU32ToFloat4(SampleColormapU32(t,cmap)); } +ImU32 ConvertValueToColor(float value, float v_min, float v_max, ImPlotColormap colormap) { + return SampleColormapU32(ImClamp(ImRemap01(value, v_min, v_max), 0.f, 1.f), colormap); +} + +template +void ConvertValueToColor(T* value, ImU32* cs, int count, float v_min, float v_max, ImPlotColormap colormap) { + for (int i = 0; i < count; i++) + cs[i] = SampleColormapU32(ImClamp(ImRemap01(static_cast(value[i]), v_min, v_max), 0.f, 1.f), colormap); +} +template void ConvertValueToColor(double* value, ImU32* cs, int count, float v_min, float v_max, ImPlotColormap colormap); +template void ConvertValueToColor(float* value, ImU32* cs, int count, float v_min, float v_max, ImPlotColormap colormap); +template void ConvertValueToColor(int* value, ImU32* cs, int count, float v_min, float v_max, ImPlotColormap colormap); + void RenderColorBar(const ImU32* colors, int size, ImDrawList& DrawList, const ImRect& bounds, bool vert, bool reversed, bool continuous) { const int n = continuous ? size - 1 : size; ImU32 col1, col2; diff --git a/implot.h b/implot.h index d4acc66d..7d84753b 100644 --- a/implot.h +++ b/implot.h @@ -238,12 +238,14 @@ enum ImPlotLineFlags_ { ImPlotLineFlags_SkipNaN = 1 << 12, // NaNs values will be skipped instead of rendered as missing data ImPlotLineFlags_NoClip = 1 << 13, // markers (if displayed) on the edge of a plot will not be clipped ImPlotLineFlags_Shaded = 1 << 14, // a filled region between the line and horizontal origin will be rendered; use PlotShaded for more advanced cases + ImPlotLineFlags_PerPointCustomColor = 1 << 15, // each point is assigned with a specific color according to its value }; // Flags for PlotScatter enum ImPlotScatterFlags_ { - ImPlotScatterFlags_None = 0, // default - ImPlotScatterFlags_NoClip = 1 << 10, // markers on the edge of a plot will not be clipped + ImPlotScatterFlags_None = 0, // default + ImPlotScatterFlags_NoClip = 1 << 10, // markers on the edge of a plot will not be clipped + ImPlotScatterFlags_PerPointCustomColor = 1 << 11, // markers with multiple colors }; // Flags for PlotStairs @@ -473,9 +475,11 @@ enum ImPlotBin_ { IM_MSVC_RUNTIME_CHECKS_OFF struct ImPlotPoint { double x, y; - IMPLOT_API constexpr ImPlotPoint() : x(0.0), y(0.0) { } - IMPLOT_API constexpr ImPlotPoint(double _x, double _y) : x(_x), y(_y) { } - IMPLOT_API constexpr ImPlotPoint(const ImVec2& p) : x((double)p.x), y((double)p.y) { } + ImU32 c; + IMPLOT_API constexpr ImPlotPoint() : x(0.0), y(0.0), c(ImU32()) { } + IMPLOT_API constexpr ImPlotPoint(double _x, double _y) : x(_x), y(_y), c(ImU32()) { } + IMPLOT_API constexpr ImPlotPoint(const ImVec2& p) : x((double)p.x), y((double)p.y), c(ImU32()) { } + IMPLOT_API constexpr ImPlotPoint(double _x, double _y, ImU32 _c) : x(_x), y(_y), c(_c) { } IMPLOT_API double& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1); return ((double*)(void*)(char*)this)[idx]; } IMPLOT_API double operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1); return ((const double*)(const void*)(const char*)this)[idx]; } #ifdef IMPLOT_POINT_CLASS_EXTRA @@ -861,11 +865,13 @@ IMPLOT_API void SetNextAxesToFit(); // Plots a standard 2D line plot. IMPLOT_TMP void PlotLine(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotLineFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotLine(const char* label_id, const T* xs, const T* ys, int count, ImPlotLineFlags flags=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotLine(const char* label_id, const T* xs, const T* ys, const ImU32* cs, int count, ImPlotLineFlags flags = 0, int offset = 0, int stride = sizeof(T)); IMPLOT_API void PlotLineG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotLineFlags flags=0); // Plots a standard 2D scatter plot. Default marker is ImPlotMarker_Circle. IMPLOT_TMP void PlotScatter(const char* label_id, const T* values, int count, double xscale=1, double xstart=0, ImPlotScatterFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_TMP void PlotScatter(const char* label_id, const T* xs, const T* ys, int count, ImPlotScatterFlags flags=0, int offset=0, int stride=sizeof(T)); +IMPLOT_TMP void PlotScatter(const char* label_id, const T* xs, const T* ys, const ImU32* cs, int count, ImPlotScatterFlags flags=0, int offset=0, int stride=sizeof(T)); IMPLOT_API void PlotScatterG(const char* label_id, ImPlotGetter getter, void* data, int count, ImPlotScatterFlags flags=0); // Plots a a stairstep graph. The y value is continued constantly to the right from every x position, i.e. the interval [x[i], x[i+1]) has the value y[i] @@ -930,6 +936,10 @@ IMPLOT_API void PlotText(const char* text, double x, double y, const ImVec2& pix // Plots a dummy item (i.e. adds a legend entry colored by ImPlotCol_Line) IMPLOT_API void PlotDummy(const char* label_id, ImPlotDummyFlags flags=0); +// Plots a 2D filled contour without lines, given a regular/irregular grid of data. +IMPLOT_TMP void PlotContourFill(const char* label_id, const T* xs, const T* ys, const ImU32* cs, int x_count, int y_count, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max); + + //----------------------------------------------------------------------------- // [SECTION] Plot Tools //----------------------------------------------------------------------------- @@ -1191,6 +1201,9 @@ IMPLOT_API ImVec4 GetColormapColor(int idx, ImPlotColormap cmap = IMPLOT_AUTO); // Sample a color from the current colormap given t between 0 and 1. IMPLOT_API ImVec4 SampleColormap(float t, ImPlotColormap cmap = IMPLOT_AUTO); +// Convert values to colors using a specific colormap. +IMPLOT_TMP void ConvertValueToColor(T* value, ImU32* cs, int count, float v_min, float v_max, ImPlotColormap colormap); + // Shows a vertical color scale with linear spaced ticks using the specified color map. Use double hashes to hide label (e.g. "##NoLabel"). If scale_min > scale_max, the scale to color mapping will be reversed. IMPLOT_API void ColormapScale(const char* label, double scale_min, double scale_max, const ImVec2& size = ImVec2(0,0), const char* format = "%g", ImPlotColormapScaleFlags flags = 0, ImPlotColormap cmap = IMPLOT_AUTO); // Shows a horizontal slider with a colormap gradient background. Optionally returns the color sampled at t in [0 1]. diff --git a/implot_demo.cpp b/implot_demo.cpp index 2c079ad3..7ddd3941 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -299,11 +299,50 @@ void Demo_LinePlots() { xs2[i] = i * 1/19.0f; ys2[i] = xs2[i] * xs2[i]; } + if (ImPlot::BeginPlot("Line Plots")) { ImPlot::SetupAxes("x","y"); ImPlot::PlotLine("f(x)", xs1, ys1, 1001); ImPlot::SetNextMarkerStyle(ImPlotMarker_Circle); - ImPlot::PlotLine("g(x)", xs2, ys2, 20,ImPlotLineFlags_Segments); + ImPlot::PlotLine("g(x)", xs2, ys2, 20, ImPlotLineFlags_Segments); + ImPlot::EndPlot(); + } + + static ImU32 colors3[150], colors4[200]; + static float scale_min = FLT_MAX; + static float scale_max = -FLT_MAX; + + static float xs3[150], ys3[150], zs3[150]; + for (int i = 0; i < 150; i++) + { + xs3[i] = i; + ys3[i] = 1.2 * i * i; + zs3[i] = cosf(i * 0.1f); + scale_min = (scale_min > zs3[i]) ? zs3[i] : scale_min; + scale_max = (scale_max < zs3[i]) ? zs3[i] : scale_max; + } + + static float xs4[200], ys4[200], zs4[200]; + for (int i = 0; i < 200; i++) + { + xs4[i] = i; + ys4[i] = 0.6f * i * i * (1.f + sinf(50.f * (xs4[i] + ImGui::GetTime() / 10))); + zs4[i] = sinf(i * 0.1f); + } + + const char* colormaps[] = { "Viridis", "Plasma", "Hot", "Cool", "Pink", "Jet", "Twilight", "RdBu", "BrBG", "PiYG", "Spectral", "Greys" }; + static int sel_colormap = 5; // Jet by default + ImGui::Combo("Choose colormap", &sel_colormap, colormaps, IM_ARRAYSIZE(colormaps)); + ImGui::DragFloatRange2("Scale Min / Max", &scale_min, &scale_max, 0.1); + ImPlotColormap cmap = GetColormapIndex(colormaps[sel_colormap]); + ImPlot::ConvertValueToColor(zs3, colors3, 150, scale_min, scale_max, cmap); + ImPlot::ConvertValueToColor(zs4, colors4, 200, scale_min, scale_max, cmap); + ImPlot::ColormapScale("##Z-Scale", floorf(scale_min), ceilf(scale_max), ImVec2(60, 300), "%.2f", 0, cmap); + ImGui::SameLine(); + if (ImPlot::BeginPlot("Line Plots with Per Point Custom Colors")) { + ImPlot::SetupAxes("x", "y"); + ImPlot::PlotLine("h(x)", xs3, ys3, colors3, 150, ImPlotLineFlags_Segments|ImPlotLineFlags_PerPointCustomColor); + ImPlot::PlotLine("i(x)", xs4, ys4, colors4, 200, ImPlotLineFlags_PerPointCustomColor); ImPlot::EndPlot(); } } @@ -394,25 +433,57 @@ void Demo_ShadedPlots() { //----------------------------------------------------------------------------- void Demo_ScatterPlots() { + static ImU32 colors1[100], colors2[50]; + static float scale_min = FLT_MAX; + static float scale_max = -FLT_MAX; + const char* colormaps[] = { "Viridis", "Plasma", "Hot", "Cool", "Pink", "Jet", "Twilight", "RdBu", "BrBG", "PiYG", "Spectral", "Greys" }; + static int sel_colormap = 5; // Jet by default + srand(0); - static float xs1[100], ys1[100]; + static float xs1[100], ys1[100], vs1[100]; for (int i = 0; i < 100; ++i) { xs1[i] = i * 0.01f; ys1[i] = xs1[i] + 0.1f * ((float)rand() / (float)RAND_MAX); + vs1[i] = sin(i * 0.1f); + scale_min = (scale_min > vs1[i]) ? vs1[i] : scale_min; + scale_max = (scale_max < vs1[i]) ? vs1[i] : scale_max; } - static float xs2[50], ys2[50]; + static float xs2[50], ys2[50], vs2[50]; for (int i = 0; i < 50; i++) { xs2[i] = 0.25f + 0.2f * ((float)rand() / (float)RAND_MAX); ys2[i] = 0.75f + 0.2f * ((float)rand() / (float)RAND_MAX); + vs2[i] = cos(i * 0.1f); } - if (ImPlot::BeginPlot("Scatter Plot")) { - ImPlot::PlotScatter("Data 1", xs1, ys1, 100); - ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); - ImPlot::SetNextMarkerStyle(ImPlotMarker_Square, 6, ImPlot::GetColormapColor(1), IMPLOT_AUTO, ImPlot::GetColormapColor(1)); - ImPlot::PlotScatter("Data 2", xs2, ys2, 50); - ImPlot::PopStyleVar(); - ImPlot::EndPlot(); + static bool ppc_color = false; + ImGui::Checkbox("Per Point Custom Color", &ppc_color); + if (!ppc_color) { + if (ImPlot::BeginPlot("Scatter Plot")) { + ImPlot::PlotScatter("Data 1", xs1, ys1, 100); + ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); + ImPlot::SetNextMarkerStyle(ImPlotMarker_Square, 6, ImPlot::GetColormapColor(1), IMPLOT_AUTO, ImPlot::GetColormapColor(1)); + ImPlot::PlotScatter("Data 2", xs2, ys2, 50); + ImPlot::PopStyleVar(); + ImPlot::EndPlot(); + } + } + else { + ImGui::Combo("##ScatterColormap", &sel_colormap, colormaps, IM_ARRAYSIZE(colormaps)); + ImGui::SameLine(); + ImGui::Text("Choose colormap"); + ImGui::DragFloatRange2("Min / Max", &scale_min, &scale_max, 0.1); + ImPlotColormap cmap = GetColormapIndex(colormaps[sel_colormap]); + ImPlot::ConvertValueToColor(vs1, colors1, 100, scale_min, scale_max, cmap); + ImPlot::ConvertValueToColor(vs2, colors2, 50, scale_min, scale_max, cmap); + ImPlot::ColormapScale("##Z-Scale", scale_min, scale_max, ImVec2(60, 300), "%.2f", 0, cmap); + ImGui::SameLine(); + if (ImPlot::BeginPlot("Scatter Plot with Per Point Custom Colors")) { + ImPlot::PlotScatter("Data 1", xs1, ys1, colors1, 100, ImPlotScatterFlags_PerPointCustomColor); + ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); + ImPlot::SetNextMarkerStyle(ImPlotMarker_Square, 6); + ImPlot::PlotScatter("Data 2", xs2, ys2, colors2, 50, ImPlotScatterFlags_PerPointCustomColor); + ImPlot::EndPlot(); + } } } @@ -1003,6 +1074,49 @@ void Demo_NaNValues() { } } +//----------------------------------------------------------------------------- +void Demo_ContourMap() { + constexpr int M = 240; + constexpr int N = 160; + static float delta = 0.025f; + static float xs[M * N]; + static float ys[M * N]; + static float zs[M * N]; + static ImU32 colors[M * N]; + + static float scale_min = FLT_MAX; + static float scale_max = -FLT_MAX; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N; j++) { + int idx = j * M + i; + xs[idx] = -3.f + i * delta; + ys[idx] = -2.f + j * delta; + zs[idx] = (1.f - 0.5f * xs[idx] + powf(xs[idx], 5) + powf(ys[idx], 3)) * expf(-xs[idx] * xs[idx] - ys[idx] * ys[idx]); + scale_min = (scale_min < zs[idx]) ? scale_min : zs[idx]; + scale_max = (scale_max > zs[idx]) ? scale_max : zs[idx]; + } + } + + // Choose colormap + const char* colormaps[] = { "Viridis", "Plasma", "Hot", "Cool", "Pink", "Jet", "Twilight", "RdBu", "BrBG", "PiYG", "Spectral", "Greys" }; + static int sel_colormap = 7; // RdBu by default + ImGui::SetNextItemWidth(360); + ImGui::Combo("##ContourColormap", &sel_colormap, colormaps, IM_ARRAYSIZE(colormaps)); + ImGui::SameLine(); + ImGui::Text("Choose colormap"); + ImGui::SetNextItemWidth(360); + ImGui::DragFloatRange2("Min / Max", &scale_min, &scale_max, 0.01f, -3.f, 3.f); + + ImPlotColormap cmap = ImPlot::GetColormapIndex(colormaps[sel_colormap]); + ImPlot::ConvertValueToColor(zs, colors, M * N, scale_min, scale_max, cmap); + if (ImPlot::BeginPlot("##Contour", ImVec2(M * 2, N * 2))) { + ImPlot::PlotContourFill("Contour Map", xs, ys, colors, M, N, ImPlotPoint(-3.f, -2.f), ImPlotPoint(-3.f + M * delta, -2.f + N * delta)); + ImPlot::EndPlot(); + } + ImGui::SameLine(); + ImPlot::ColormapScale("##Z-Scale", scale_min, scale_max, ImVec2(60, N * 2), "%.2f", 0, cmap); +} + //----------------------------------------------------------------------------- void Demo_LogScale() { @@ -2264,6 +2378,7 @@ void ShowDemoWindow(bool* p_open) { DemoHeader("Images", Demo_Images); DemoHeader("Markers and Text", Demo_MarkersAndText); DemoHeader("NaN Values", Demo_NaNValues); + DemoHeader("Contour Map", Demo_ContourMap); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Subplots")) { diff --git a/implot_items.cpp b/implot_items.cpp index 3b00ae4c..afd8756e 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -521,6 +521,22 @@ struct IndexerIdx { int Stride; }; +struct IndexerIdxC { + IndexerIdxC(const ImU32* data, int count, int offset = 0, int stride = sizeof(ImU32)) : + Data(data), + Count(count), + Offset(count ? ImPosMod(offset, count) : 0), + Stride(stride) + { } + template IMPLOT_INLINE ImU32 operator()(I idx) const { + return (ImU32)IndexData(Data, idx, Count, Offset, Stride); + } + const ImU32* Data; + int Count; + int Offset; + int Stride; +}; + template struct IndexerAdd { IndexerAdd(const _Indexer1& indexer1, const _Indexer2& indexer2, double scale1 = 1, double scale2 = 1) @@ -570,6 +586,18 @@ struct GetterXY { const int Count; }; +template +struct GetterXYC { + GetterXYC(_IndexerX x, _IndexerY y, _IndexerC c, int count) : IndexerX(x), IndexerY(y), IndexerC(c), Count(count) { } + template IMPLOT_INLINE ImPlotPoint operator()(I idx) const { + return ImPlotPoint(IndexerX(idx), IndexerY(idx), IndexerC(idx)); + } + const _IndexerX IndexerX; + const _IndexerY IndexerY; + const _IndexerC IndexerC; + const int Count; +}; + /// Interprets a user's function pointer as ImPlotPoints struct GetterFuncPtr { GetterFuncPtr(ImPlotGetter getter, void* data, int count) : @@ -883,7 +911,7 @@ struct RendererLineStrip : RendererBase { P1 = P2; return false; } - PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); + PrimLine(draw_list, P1, P2, HalfWeight, Col, UV0, UV1); P1 = P2; return true; } @@ -895,6 +923,35 @@ struct RendererLineStrip : RendererBase { mutable ImVec2 UV1; }; +template +struct RendererLineStripC : RendererBase { + RendererLineStripC(const _Getter& getter, float weight) : + RendererBase(getter.Count - 1, 6, 4), + Getter(getter), + HalfWeight(ImMax(1.0f, weight) * 0.5f) + { + P1 = this->Transformer(Getter(0)); + } + void Init(ImDrawList& draw_list) const { + GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImVec2 P2 = this->Transformer(Getter(prim + 1)); + if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { + P1 = P2; + return false; + } + PrimLine(draw_list, P1, P2, HalfWeight, Getter(prim).c, UV0, UV1); + P1 = P2; + return true; + } + const _Getter& Getter; + mutable float HalfWeight; + mutable ImVec2 P1; + mutable ImVec2 UV0; + mutable ImVec2 UV1; +}; + template struct RendererLineStripSkip : RendererBase { RendererLineStripSkip(const _Getter& getter, ImU32 col, float weight) : @@ -928,6 +985,37 @@ struct RendererLineStripSkip : RendererBase { mutable ImVec2 UV1; }; +template +struct RendererLineStripSkipC : RendererBase { + RendererLineStripSkipC(const _Getter& getter, float weight) : + RendererBase(getter.Count - 1, 6, 4), + Getter(getter), + HalfWeight(ImMax(1.0f, weight) * 0.5f) + { + P1 = this->Transformer(Getter(0)); + } + void Init(ImDrawList& draw_list) const { + GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImVec2 P2 = this->Transformer(Getter(prim + 1)); + if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { + if (!ImNan(P2.x) && !ImNan(P2.y)) + P1 = P2; + return false; + } + PrimLine(draw_list, P1, P2, HalfWeight, Getter(prim).c, UV0, UV1); + if (!ImNan(P2.x) && !ImNan(P2.y)) + P1 = P2; + return true; + } + const _Getter& Getter; + mutable float HalfWeight; + mutable ImVec2 P1; + mutable ImVec2 UV0; + mutable ImVec2 UV1; +}; + template struct RendererLineSegments1 : RendererBase { RendererLineSegments1(const _Getter& getter, ImU32 col, float weight) : @@ -954,6 +1042,30 @@ struct RendererLineSegments1 : RendererBase { mutable ImVec2 UV1; }; +template +struct RendererLineSegments1C : RendererBase { + RendererLineSegments1C(const _Getter& getter, float weight) : + RendererBase(getter.Count / 2, 6, 4), + Getter(getter), + HalfWeight(ImMax(1.0f, weight) * 0.5f) + { } + void Init(ImDrawList& draw_list) const { + GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImVec2 P1 = this->Transformer(Getter(prim * 2 + 0)); + ImVec2 P2 = this->Transformer(Getter(prim * 2 + 1)); + if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) + return false; + PrimLine(draw_list, P1, P2, HalfWeight, Getter(prim).c, UV0, UV1); + return true; + } + const _Getter& Getter; + mutable float HalfWeight; + mutable ImVec2 UV0; + mutable ImVec2 UV1; +}; + template struct RendererLineSegments2 : RendererBase { RendererLineSegments2(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, float weight) : @@ -982,6 +1094,32 @@ struct RendererLineSegments2 : RendererBase { mutable ImVec2 UV1; }; +template +struct RendererLineSegments2C : RendererBase { + RendererLineSegments2C(const _Getter1& getter1, const _Getter2& getter2, float weight) : + RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), + Getter1(getter1), + Getter2(getter2), + HalfWeight(ImMax(1.0f, weight) * 0.5f) + { } + void Init(ImDrawList& draw_list) const { + GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImVec2 P1 = this->Transformer(Getter1(prim)); + ImVec2 P2 = this->Transformer(Getter2(prim)); + if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) + return false; + PrimLine(draw_list, P1, P2, HalfWeight, Getter1(prim).c, UV0, UV1); + return true; + } + const _Getter1& Getter1; + const _Getter2& Getter2; + mutable float HalfWeight; + mutable ImVec2 UV0; + mutable ImVec2 UV1; +}; + template struct RendererBarsFillV : RendererBase { RendererBarsFillV(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double width) : @@ -1265,8 +1403,6 @@ struct RendererStairsPostShaded : RendererBase { mutable ImVec2 UV; }; - - template struct RendererShaded : RendererBase { RendererShaded(const _Getter1& getter1, const _Getter2& getter2, ImU32 col) : @@ -1356,6 +1492,94 @@ struct RendererRectC : RendererBase { mutable ImVec2 UV; }; +template +struct RendererContourFill : RendererBase { + RendererContourFill(const _Getter& getter, int x_count, int y_count) : + RendererBase((x_count - 1)* (y_count - 1), 6, 4), + Getter(getter), + XCount(x_count), YCount(y_count) + { } + void Init(ImDrawList& draw_list) const { + UV = draw_list._Data->TexUvWhitePixel; + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + int x = prim % (XCount - 1); + int y = prim / (XCount - 1); + + ImPlotPoint p_plot[4]; + p_plot[0] = Getter(x + y * XCount); + p_plot[1] = Getter(x + 1 + y * XCount); + p_plot[2] = Getter(x + 1 + (y + 1) * XCount); + p_plot[3] = Getter(x + (y + 1) * XCount); + + // Check if the coordinates of vertices and their values are valid numbers + if (isnan(p_plot[0].x) || isnan(p_plot[0].y) || isnan(p_plot[1].x) || isnan(p_plot[1].y) + || isnan(p_plot[2].x) || isnan(p_plot[2].y) || isnan(p_plot[3].x) || isnan(p_plot[3].y)) + return false; + + // Project the quad vertices to screen space + ImVec2 p[4]; + p[0] = this->Transformer(ImVec2(p_plot[0].x, p_plot[0].y)); + p[1] = this->Transformer(ImVec2(p_plot[1].x, p_plot[1].y)); + p[2] = this->Transformer(ImVec2(p_plot[2].x, p_plot[2].y)); + p[3] = this->Transformer(ImVec2(p_plot[3].x, p_plot[3].y)); + + // Check if the quad is outside the culling box + if (!cull_rect.Contains(p[0]) && !cull_rect.Contains(p[1]) && + !cull_rect.Contains(p[2]) && !cull_rect.Contains(p[3])) + return false; + // colors + ImU32 cols[4]; + cols[0] = p_plot[0].c; + cols[1] = p_plot[1].c; + cols[2] = p_plot[2].c; + cols[3] = p_plot[3].c; + + // Add vertices for two triangles + draw_list._VtxWritePtr[0].pos.x = p[0].x; + draw_list._VtxWritePtr[0].pos.y = p[0].y; + draw_list._VtxWritePtr[0].uv = UV; + draw_list._VtxWritePtr[0].col = cols[0]; + + draw_list._VtxWritePtr[1].pos.x = p[1].x; + draw_list._VtxWritePtr[1].pos.y = p[1].y; + draw_list._VtxWritePtr[1].uv = UV; + draw_list._VtxWritePtr[1].col = cols[1]; + + draw_list._VtxWritePtr[2].pos.x = p[2].x; + draw_list._VtxWritePtr[2].pos.y = p[2].y; + draw_list._VtxWritePtr[2].uv = UV; + draw_list._VtxWritePtr[2].col = cols[2]; + + draw_list._VtxWritePtr[3].pos.x = p[3].x; + draw_list._VtxWritePtr[3].pos.y = p[3].y; + draw_list._VtxWritePtr[3].uv = UV; + draw_list._VtxWritePtr[3].col = cols[3]; + + draw_list._VtxWritePtr += 4; + + // Add indices for two triangles + draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); + draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1); + draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); + + draw_list._IdxWritePtr[3] = (ImDrawIdx)(draw_list._VtxCurrentIdx); + draw_list._IdxWritePtr[4] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 2); + draw_list._IdxWritePtr[5] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 3); + + draw_list._IdxWritePtr += 6; + + // Update vertex count + draw_list._VtxCurrentIdx += 4; + + return true; + } + const _Getter& Getter; + mutable ImVec2 UV; + const int XCount; + const int YCount; +}; + //----------------------------------------------------------------------------- // [SECTION] RenderPrimitives //----------------------------------------------------------------------------- @@ -1460,7 +1684,6 @@ struct RendererMarkersFill : RendererBase { mutable ImVec2 UV; }; - template struct RendererMarkersLine : RendererBase { RendererMarkersLine(const _Getter& getter, const ImVec2* marker, int count, float size, float weight, ImU32 col) : @@ -1497,6 +1720,80 @@ struct RendererMarkersLine : RendererBase { mutable ImVec2 UV1; }; +template +struct RendererMarkersFillC : RendererBase { + RendererMarkersFillC(const _Getter& getter, const ImVec2* marker, int count, float size) : + RendererBase(getter.Count, (count - 2) * 3, count), + Getter(getter), + Marker(marker), + Count(count), + Size(size) + { } + void Init(ImDrawList& draw_list) const { + UV = draw_list._Data->TexUvWhitePixel; + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImVec2 p = this->Transformer(Getter(prim)); + if (p.x >= cull_rect.Min.x && p.y >= cull_rect.Min.y && p.x <= cull_rect.Max.x && p.y <= cull_rect.Max.y) { + for (int i = 0; i < Count; i++) { + draw_list._VtxWritePtr[0].pos.x = p.x + Marker[i].x * Size; + draw_list._VtxWritePtr[0].pos.y = p.y + Marker[i].y * Size; + draw_list._VtxWritePtr[0].uv = UV; + draw_list._VtxWritePtr[0].col = Getter(prim).c; + draw_list._VtxWritePtr++; + } + for (int i = 2; i < Count; i++) { + draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); + draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + i - 1); + draw_list._IdxWritePtr[2] = (ImDrawIdx)(draw_list._VtxCurrentIdx + i); + draw_list._IdxWritePtr += 3; + } + draw_list._VtxCurrentIdx += (ImDrawIdx)Count; + return true; + } + return false; + } + const _Getter& Getter; + const ImVec2* Marker; + const int Count; + const float Size; + mutable ImVec2 UV; +}; + +template +struct RendererMarkersLineC : RendererBase { + RendererMarkersLineC(const _Getter& getter, const ImVec2* marker, int count, float size, float weight) : + RendererBase(getter.Count, count / 2 * 6, count / 2 * 4), + Getter(getter), + Marker(marker), + Count(count), + HalfWeight(ImMax(1.0f, weight) * 0.5f), + Size(size) + { } + void Init(ImDrawList& draw_list) const { + GetLineRenderProps(draw_list, HalfWeight, UV0, UV1); + } + IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { + ImVec2 p = this->Transformer(Getter(prim)); + if (p.x >= cull_rect.Min.x && p.y >= cull_rect.Min.y && p.x <= cull_rect.Max.x && p.y <= cull_rect.Max.y) { + for (int i = 0; i < Count; i = i + 2) { + ImVec2 p1(p.x + Marker[i].x * Size, p.y + Marker[i].y * Size); + ImVec2 p2(p.x + Marker[i + 1].x * Size, p.y + Marker[i + 1].y * Size); + PrimLine(draw_list, p1, p2, HalfWeight, Getter(prim).c, UV0, UV1); + } + return true; + } + return false; + } + const _Getter& Getter; + const ImVec2* Marker; + const int Count; + mutable float HalfWeight; + const float Size; + mutable ImVec2 UV0; + mutable ImVec2 UV1; +}; + static const ImVec2 MARKER_FILL_CIRCLE[10] = {ImVec2(1.0f, 0.0f), ImVec2(0.809017f, 0.58778524f),ImVec2(0.30901697f, 0.95105654f),ImVec2(-0.30901703f, 0.9510565f),ImVec2(-0.80901706f, 0.5877852f),ImVec2(-1.0f, 0.0f),ImVec2(-0.80901694f, -0.58778536f),ImVec2(-0.3090171f, -0.9510565f),ImVec2(0.30901712f, -0.9510565f),ImVec2(0.80901694f, -0.5877853f)}; static const ImVec2 MARKER_FILL_SQUARE[4] = {ImVec2(SQRT_1_2,SQRT_1_2), ImVec2(SQRT_1_2,-SQRT_1_2), ImVec2(-SQRT_1_2,-SQRT_1_2), ImVec2(-SQRT_1_2,SQRT_1_2)}; static const ImVec2 MARKER_FILL_DIAMOND[4] = {ImVec2(1, 0), ImVec2(0, -1), ImVec2(-1, 0), ImVec2(0, 1)}; @@ -1566,6 +1863,35 @@ void RenderMarkers(const _Getter& getter, ImPlotMarker marker, float size, bool } } +template +void RenderMarkersC(const _Getter& getter, ImPlotMarker marker, float size, bool rend_fill, bool rend_line, float weight) { + if (rend_fill) { + switch (marker) { + case ImPlotMarker_Circle: RenderPrimitives1(getter, MARKER_FILL_CIRCLE, 10, size); break; + case ImPlotMarker_Square: RenderPrimitives1(getter, MARKER_FILL_SQUARE, 4, size); break; + case ImPlotMarker_Diamond: RenderPrimitives1(getter, MARKER_FILL_DIAMOND, 4, size); break; + case ImPlotMarker_Up: RenderPrimitives1(getter, MARKER_FILL_UP, 3, size); break; + case ImPlotMarker_Down: RenderPrimitives1(getter, MARKER_FILL_DOWN, 3, size); break; + case ImPlotMarker_Left: RenderPrimitives1(getter, MARKER_FILL_LEFT, 3, size); break; + case ImPlotMarker_Right: RenderPrimitives1(getter, MARKER_FILL_RIGHT, 3, size); break; + } + } + if (rend_line) { + switch (marker) { + case ImPlotMarker_Circle: RenderPrimitives1(getter, MARKER_LINE_CIRCLE, 20, size, weight); break; + case ImPlotMarker_Square: RenderPrimitives1(getter, MARKER_LINE_SQUARE, 8, size, weight); break; + case ImPlotMarker_Diamond: RenderPrimitives1(getter, MARKER_LINE_DIAMOND, 8, size, weight); break; + case ImPlotMarker_Up: RenderPrimitives1(getter, MARKER_LINE_UP, 6, size, weight); break; + case ImPlotMarker_Down: RenderPrimitives1(getter, MARKER_LINE_DOWN, 6, size, weight); break; + case ImPlotMarker_Left: RenderPrimitives1(getter, MARKER_LINE_LEFT, 6, size, weight); break; + case ImPlotMarker_Right: RenderPrimitives1(getter, MARKER_LINE_RIGHT, 6, size, weight); break; + case ImPlotMarker_Asterisk: RenderPrimitives1(getter, MARKER_LINE_ASTERISK, 6, size, weight); break; + case ImPlotMarker_Plus: RenderPrimitives1(getter, MARKER_LINE_PLUS, 4, size, weight); break; + case ImPlotMarker_Cross: RenderPrimitives1(getter, MARKER_LINE_CROSS, 4, size, weight); break; + } + } +} + //----------------------------------------------------------------------------- // [SECTION] PlotLine //----------------------------------------------------------------------------- @@ -1585,21 +1911,32 @@ void PlotLineEx(const char* label_id, const _Getter& getter, ImPlotLineFlags fla RenderPrimitives2(getter,getter2,col_fill); } if (s.RenderLine) { - const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]); + ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]); if (ImHasFlag(flags,ImPlotLineFlags_Segments)) { - RenderPrimitives1(getter,col_line,s.LineWeight); + if (ImHasFlag(flags, ImPlotLineFlags_PerPointCustomColor)) + RenderPrimitives1(getter, s.LineWeight); + else + RenderPrimitives1(getter,col_line,s.LineWeight); } else if (ImHasFlag(flags, ImPlotLineFlags_Loop)) { if (ImHasFlag(flags, ImPlotLineFlags_SkipNaN)) RenderPrimitives1(GetterLoop<_Getter>(getter),col_line,s.LineWeight); - else - RenderPrimitives1(GetterLoop<_Getter>(getter),col_line,s.LineWeight); + else { + if (ImHasFlag(flags, ImPlotLineFlags_PerPointCustomColor)) + RenderPrimitives1(GetterLoop<_Getter>(getter), s.LineWeight); + else + RenderPrimitives1(GetterLoop<_Getter>(getter), col_line, s.LineWeight); + } } else { if (ImHasFlag(flags, ImPlotLineFlags_SkipNaN)) RenderPrimitives1(getter,col_line,s.LineWeight); - else - RenderPrimitives1(getter,col_line,s.LineWeight); + else { + if (ImHasFlag(flags, ImPlotLineFlags_PerPointCustomColor)) + RenderPrimitives1(getter, s.LineWeight); + else + RenderPrimitives1(getter, col_line, s.LineWeight); + } } } } @@ -1629,9 +1966,16 @@ void PlotLine(const char* label_id, const T* xs, const T* ys, int count, ImPlotL PlotLineEx(label_id, getter, flags); } +template +void PlotLine(const char* label_id, const T* xs, const T* ys, const ImU32* cs, int count, ImPlotLineFlags flags, int offset, int stride) { + GetterXYC, IndexerIdx, IndexerIdxC> getter(IndexerIdx(xs, count, offset, stride), IndexerIdx(ys, count, offset, stride), IndexerIdxC(cs, count, offset, stride), count); + PlotLineEx(label_id, getter, flags); +} + #define INSTANTIATE_MACRO(T) \ template IMPLOT_API void PlotLine (const char* label_id, const T* values, int count, double xscale, double x0, ImPlotLineFlags flags, int offset, int stride); \ - template IMPLOT_API void PlotLine(const char* label_id, const T* xs, const T* ys, int count, ImPlotLineFlags flags, int offset, int stride); + template IMPLOT_API void PlotLine(const char* label_id, const T* xs, const T* ys, int count, ImPlotLineFlags flags, int offset, int stride); \ + template IMPLOT_API void PlotLine(const char* label_id, const T* xs, const T* ys, const ImU32* cs, int count, ImPlotLineFlags flags, int offset, int stride); CALL_INSTANTIATE_FOR_NUMERIC_TYPES() #undef INSTANTIATE_MACRO @@ -1659,9 +2003,15 @@ void PlotScatterEx(const char* label_id, const Getter& getter, ImPlotScatterFlag PopPlotClipRect(); PushPlotClipRect(s.MarkerSize); } - const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]); - const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerFill]); - RenderMarkers(getter, marker, s.MarkerSize, s.RenderMarkerFill, col_fill, s.RenderMarkerLine, col_line, s.MarkerWeight); + + if (ImHasFlag(flags, ImPlotScatterFlags_PerPointCustomColor)) { + RenderMarkersC(getter, marker, s.MarkerSize, s.RenderMarkerFill, s.RenderMarkerLine, s.MarkerWeight); + } + else { + const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerOutline]); + const ImU32 col_fill = ImGui::GetColorU32(s.Colors[ImPlotCol_MarkerFill]); + RenderMarkers(getter, marker, s.MarkerSize, s.RenderMarkerFill, col_fill, s.RenderMarkerLine, col_line, s.MarkerWeight); + } } EndItem(); } @@ -1676,12 +2026,22 @@ void PlotScatter(const char* label_id, const T* values, int count, double xscale template void PlotScatter(const char* label_id, const T* xs, const T* ys, int count, ImPlotScatterFlags flags, int offset, int stride) { GetterXY,IndexerIdx> getter(IndexerIdx(xs,count,offset,stride),IndexerIdx(ys,count,offset,stride),count); - return PlotScatterEx(label_id, getter, flags); + PlotScatterEx(label_id, getter, flags); +} + +template +void PlotScatter(const char* label_id, const T* xs, const T* ys, const ImU32* cs, int count, ImPlotScatterFlags flags, int offset, int stride) { + GetterXYC, IndexerIdx, IndexerIdxC> getter( + IndexerIdx(xs, count, offset, stride), + IndexerIdx(ys, count, offset, stride), + IndexerIdxC(cs, count, offset, stride), count); + PlotScatterEx(label_id, getter, flags); } #define INSTANTIATE_MACRO(T) \ template IMPLOT_API void PlotScatter(const char* label_id, const T* values, int count, double xscale, double x0, ImPlotScatterFlags flags, int offset, int stride); \ - template IMPLOT_API void PlotScatter(const char* label_id, const T* xs, const T* ys, int count, ImPlotScatterFlags flags, int offset, int stride); + template IMPLOT_API void PlotScatter(const char* label_id, const T* xs, const T* ys, int count, ImPlotScatterFlags flags, int offset, int stride); \ + template IMPLOT_API void PlotScatter(const char* label_id, const T* xs, const T* ys, const ImU32* cs, int count, ImPlotScatterFlags flags, int offset, int stride); CALL_INSTANTIATE_FOR_NUMERIC_TYPES() #undef INSTANTIATE_MACRO @@ -2847,6 +3207,40 @@ void PlotDummy(const char* label_id, ImPlotDummyFlags flags) { EndItem(); } +//----------------------------------------------------------------------------- +// [SECTION] PlotContourFill +//----------------------------------------------------------------------------- + +template +void PlotContourFillEx(const char* label_id, const Getter& getter, int x_count, int y_count, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) { + if (getter.Count < 4) + return; + if (BeginItemEx(label_id, FitterRect(bounds_min, bounds_max))) { + // Render fill + ImPlotContext& gp = *GImPlot; + gp.CurrentItem->Color = getter(0).c; + if (getter.Count >= 4) + RenderPrimitives1(getter, x_count, y_count); + EndItem(); + } +} + +template +void PlotContourFill(const char* label_id, const T* xs, const T* ys, const ImU32* cs, int x_count, int y_count, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max) +{ + GetterXYC, IndexerIdx, IndexerIdxC> getter( + IndexerIdx(xs, x_count * y_count), + IndexerIdx(ys, y_count * y_count), + IndexerIdxC(cs, x_count * y_count), + x_count * y_count); + PlotContourFillEx(label_id, getter, x_count, y_count, bounds_min, bounds_max); +} + +#define INSTANTIATE_MACRO(T) \ + template IMPLOT_API void PlotContourFill (const char* label_id, const T* xs, const T* ys, const ImU32* cs, int x_count, int y_count, const ImPlotPoint& bounds_min, const ImPlotPoint& bounds_max); +CALL_INSTANTIATE_FOR_NUMERIC_TYPES() +#undef INSTANTIATE_MACRO + } // namespace ImPlot #endif // #ifndef IMGUI_DISABLE