From 9031c55d09afec7427dacfbdc567ce894f0b0d97 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Fri, 13 Feb 2026 12:52:12 +0100 Subject: [PATCH 01/23] feat: create color indexer and color getter --- implot_items.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/implot_items.cpp b/implot_items.cpp index dd7a1c5b..70934d7f 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -564,6 +564,28 @@ struct IndexerConst { typedef double value_type; }; +//----------------------------------------------------------------------------- +// [SECTION] Color Indexers +//----------------------------------------------------------------------------- + +struct IndexerIdxColor { + IndexerIdxColor(const ImU32* data, int count) : + Data(data), + Count(count) + { } + template IMPLOT_INLINE ImU32 operator[](I idx) const { + return Data[idx]; + } + const ImU32* Data; + int Count; +}; + +struct IndexerConstColor { + IndexerConstColor(ImU32 color) : Color(color) { } + template IMPLOT_INLINE ImU32 operator[](I) const { return Color; } + const ImU32 Color; +}; + //----------------------------------------------------------------------------- // [SECTION] Getters //----------------------------------------------------------------------------- @@ -684,6 +706,16 @@ struct GetterError { typedef ImPlotPointError value_type; }; +template +struct GetterColor { + GetterColor(_IndexerColor color, int count) : Indexer(color), Count(count) { } + template IMPLOT_INLINE ImU32 operator()(I idx) const { + return ImU32(Indexer[idx]); + } + const _IndexerColor Indexer; + const int Count; +}; + //----------------------------------------------------------------------------- // [SECTION] Fitters //----------------------------------------------------------------------------- From abf076faf31c322ff133271657f1cccc04b604d3 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Fri, 13 Feb 2026 13:03:07 +0100 Subject: [PATCH 02/23] feat: add ImPlotProp_LineColors and ImPlotProp_FillColors properties --- implot.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/implot.h b/implot.h index 115eb01a..065e0846 100644 --- a/implot.h +++ b/implot.h @@ -137,8 +137,10 @@ enum ImAxis_ { // Plotting properties. These provide syntactic sugar for creating ImPlotSpecs from (ImPlotProp,value) pairs. See ImPlotSpec documentation. enum ImPlotProp_ { ImPlotProp_LineColor, // line color (applies to lines, bar edges); IMPLOT_AUTO_COL will use next Colormap color or current item color + ImPlotProp_LineColors, // array of colors for each line; if nullptr, use LineColor for all lines ImPlotProp_LineWeight, // line weight in pixels (applies to lines, bar edges, marker edges) ImPlotProp_FillColor, // fill color (applies to shaded regions, bar faces); IMPLOT_AUTO_COL will use next Colormap color or current item color + ImPlotProp_FillColors, // array of colors for each fill; if nullptr, use FillColor for all fills ImPlotProp_FillAlpha, // alpha multiplier (applies to FillColor and MarkerFillColor) ImPlotProp_Marker, // marker type; specify ImPlotMarker_Auto to use the next unused marker ImPlotProp_MarkerSize, // size of markers (radius) *in pixels* @@ -501,8 +503,10 @@ enum ImPlotBin_ { // }); struct ImPlotSpec { ImVec4 LineColor = IMPLOT_AUTO_COL; // line color (applies to lines, bar edges); IMPLOT_AUTO_COL will use next Colormap color or current item color + ImU32* LineColors = nullptr; // array of colors for each line; if nullptr, use LineColor for all lines float LineWeight = 1.0f; // line weight in pixels (applies to lines, bar edges, marker edges) ImVec4 FillColor = IMPLOT_AUTO_COL; // fill color (applies to shaded regions, bar faces); IMPLOT_AUTO_COL will use next Colormap color or current item color + ImU32* FillColors = nullptr; // array of colors for each fill; if nullptr, use FillColor for all fills float FillAlpha = 1.0f; // alpha multiplier (applies to FillColor and MarkerFillColor) ImPlotMarker Marker = ImPlotMarker_None; // marker type; specify ImPlotMarker_Auto to use the next unused marker float MarkerSize = 4; // size of markers (radius) *in pixels* @@ -551,6 +555,16 @@ struct ImPlotSpec { IM_ASSERT(0 && "User provided an ImPlotProp which cannot be set from scalar value!"); } + // Set a property from a pointer value. + void SetProp(ImPlotProp prop, ImU32* v) { + switch (prop) { + case ImPlotProp_LineColors : LineColors = v; return; + case ImPlotProp_FillColors : FillColors = v; return; + default: break; + } + IM_ASSERT(0 && "User provided an ImPlotProp which cannot be set from pointer value!"); + } + // Set a property from an ImVec4 value. void SetProp(ImPlotProp prop, const ImVec4& v) { switch (prop) { From 38aba546451e1c46038372a7333c8cadd35f0a1b Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Fri, 13 Feb 2026 13:31:28 +0100 Subject: [PATCH 03/23] feat: use GetterColor in RendererLineStrip --- implot_demo.cpp | 21 ++++++++++++++++++ implot_items.cpp | 56 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 59 insertions(+), 18 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index cf6bc402..5a7aaaa6 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1061,6 +1061,26 @@ void Demo_NaNValues() { //----------------------------------------------------------------------------- +void Demo_PerPointColors() { + static float xs1[1001], ys1[1001]; + static ImU32 colors1[1001]; + for (int i = 0; i < 1001; ++i) { + xs1[i] = i * 0.001f; + ys1[i] = 0.5f + 0.5f * sinf(50 * (xs1[i] + (float)ImGui::GetTime() / 10)); + colors1[i] = ImGui::GetColorU32(ImVec4(ys1[i], 0.5f, 1.0f - ys1[i], 1.0f)); + } + + if (ImPlot::BeginPlot("Colorful Line Plot", ImVec2(-1,0))) { + ImPlot::PlotLine("f(x)", xs1, ys1, 1001, { + ImPlotProp_LineColor, ImVec4(1,0,1,1), + ImPlotProp_LineColors, colors1, + }); + ImPlot::EndPlot(); + } +} + +//----------------------------------------------------------------------------- + void Demo_LogScale() { static double xs[1001], ys1[1001], ys2[1001], ys3[1001]; for (int i = 0; i < 1001; ++i) { @@ -2396,6 +2416,7 @@ void ShowDemoWindow(bool* p_open) { DemoHeader("Images", Demo_Images); DemoHeader("Markers and Text", Demo_MarkersAndText); DemoHeader("NaN Values", Demo_NaNValues); + DemoHeader("Per Point Colors", Demo_PerPointColors); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Subplots")) { diff --git a/implot_items.cpp b/implot_items.cpp index 70934d7f..558daa9f 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -950,12 +950,12 @@ struct RendererBase { const int VtxConsumed; }; -template +template struct RendererLineStrip : RendererBase { - RendererLineStrip(const _Getter& getter, ImU32 col, float weight) : + RendererLineStrip(const _Getter& getter, const _GetterColor& getter_color, float weight) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), - Col(col), + GetterColor(getter_color), HalfWeight(ImMax(1.0f,weight)*0.5f) { P1 = this->Transformer(Getter[0]); @@ -969,24 +969,25 @@ struct RendererLineStrip : RendererBase { P1 = P2; return false; } - PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); + ImU32 col = GetterColor[prim]; + PrimLine(draw_list,P1,P2,HalfWeight,col,UV0,UV1); P1 = P2; return true; } const _Getter& Getter; - const ImU32 Col; + const _GetterColor& GetterColor; mutable float HalfWeight; mutable ImVec2 P1; mutable ImVec2 UV0; mutable ImVec2 UV1; }; -template +template struct RendererLineStripSkip : RendererBase { - RendererLineStripSkip(const _Getter& getter, ImU32 col, float weight) : + RendererLineStripSkip(const _Getter& getter, const _GetterColor& getter_color, float weight) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), - Col(col), + GetterColor(getter_color), HalfWeight(ImMax(1.0f,weight)*0.5f) { P1 = this->Transformer(Getter[0]); @@ -1001,13 +1002,14 @@ struct RendererLineStripSkip : RendererBase { P1 = P2; return false; } - PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); + ImU32 col = GetterColor[prim]; + PrimLine(draw_list,P1,P2,HalfWeight,col,UV0,UV1); if (!ImNan(P2.x) && !ImNan(P2.y)) P1 = P2; return true; } const _Getter& Getter; - const ImU32 Col; + const _GetterColor& GetterColor; mutable float HalfWeight; mutable ImVec2 P1; mutable ImVec2 UV0; @@ -1827,16 +1829,34 @@ void PlotLineEx(const char* label_id, const _Getter& getter, const ImPlotSpec& s RenderPrimitives1(getter,col_line,s.Spec.LineWeight); } else if (ImHasFlag(spec.Flags, ImPlotLineFlags_Loop)) { - if (ImHasFlag(spec.Flags, ImPlotLineFlags_SkipNaN)) - RenderPrimitives1(GetterLoop<_Getter>(getter),col_line,s.Spec.LineWeight); - else - RenderPrimitives1(GetterLoop<_Getter>(getter),col_line,s.Spec.LineWeight); + if (s.Spec.LineColors != NULL) { + IndexerIdxColor color_indexer(s.Spec.LineColors, getter.Count); + if (ImHasFlag(spec.Flags, ImPlotLineFlags_SkipNaN)) + RenderPrimitives2(GetterLoop<_Getter>(getter),color_indexer,s.Spec.LineWeight); + else + RenderPrimitives2(GetterLoop<_Getter>(getter),color_indexer,s.Spec.LineWeight); + } else { + IndexerConstColor color_indexer(col_line); + if (ImHasFlag(spec.Flags, ImPlotLineFlags_SkipNaN)) + RenderPrimitives2(GetterLoop<_Getter>(getter),color_indexer,s.Spec.LineWeight); + else + RenderPrimitives2(GetterLoop<_Getter>(getter),color_indexer,s.Spec.LineWeight); + } } else { - if (ImHasFlag(spec.Flags, ImPlotLineFlags_SkipNaN)) - RenderPrimitives1(getter,col_line,s.Spec.LineWeight); - else - RenderPrimitives1(getter,col_line,s.Spec.LineWeight); + if (s.Spec.LineColors != NULL) { + IndexerIdxColor color_indexer(s.Spec.LineColors, getter.Count); + if (ImHasFlag(spec.Flags, ImPlotLineFlags_SkipNaN)) + RenderPrimitives2(getter,color_indexer,s.Spec.LineWeight); + else + RenderPrimitives2(getter,color_indexer,s.Spec.LineWeight); + } else { + IndexerConstColor color_indexer(col_line); + if (ImHasFlag(spec.Flags, ImPlotLineFlags_SkipNaN)) + RenderPrimitives2(getter,color_indexer,s.Spec.LineWeight); + else + RenderPrimitives2(getter,color_indexer,s.Spec.LineWeight); + } } } } From 5f6f77972e9a50120c046e2fb7fd6b6993f7f672 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Fri, 13 Feb 2026 13:46:34 +0100 Subject: [PATCH 04/23] feat: per-index line colors demo --- implot_demo.cpp | 54 +++++++++++++++++++++++++++++++++++++----------- implot_items.cpp | 19 +++++++++++------ 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index 5a7aaaa6..71e59ccf 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1061,20 +1061,50 @@ void Demo_NaNValues() { //----------------------------------------------------------------------------- -void Demo_PerPointColors() { - static float xs1[1001], ys1[1001]; - static ImU32 colors1[1001]; - for (int i = 0; i < 1001; ++i) { - xs1[i] = i * 0.001f; - ys1[i] = 0.5f + 0.5f * sinf(50 * (xs1[i] + (float)ImGui::GetTime() / 10)); - colors1[i] = ImGui::GetColorU32(ImVec4(ys1[i], 0.5f, 1.0f - ys1[i], 1.0f)); +void Demo_PerIndexColors() { + static float xs1[101], ys1[101], ys2[101], ys3[101]; + static ImU32 colors_rainbow[101]; + static ImU32 colors_colormap[101]; + + ImPlot::PushColormap(ImPlotColormap_Hot); + for (int i = 0; i < 101; ++i) { + xs1[i] = i * 0.01f; + ys1[i] = 0.5f + 0.3f * sinf(20 * xs1[i]); + ys2[i] = 0.2f + 0.3f * sinf(15 * xs1[i] + 1.0f); + ys3[i] = -0.1f + 0.3f * sinf(10 * xs1[i] + 2.0f); + + // Rainbow colors (HSV sweep) + float hue = (float)i / 100.0f; + colors_rainbow[i] = ImColor::HSV(hue, 0.8f, 0.9f); + + // Sample from current colormap + float t = (float)i / 100.0f; + colors_colormap[i] = ImColor(ImPlot::SampleColormap(t)); } + ImPlot::PopColormap(); + + if (ImPlot::BeginPlot("Colorful Lines", ImVec2(-1,0))) { + ImPlot::SetupAxes("x","y"); + ImPlot::SetupAxesLimits(0, 1, -0.5, 1.0); + + // 1. Constant color via LineColor (default behavior) + ImPlot::PlotLine("Const Color", xs1, ys1, 101, { + ImPlotProp_LineColor, ImVec4(1, 0.5f, 0, 1), + ImPlotProp_LineWeight, 1.0f + }); + + // 2. Per-vertex colors via LineColors (rainbow) + ImPlot::PlotLine("Rainbow Colors", xs1, ys2, 101, { + ImPlotProp_LineColors, colors_rainbow, + ImPlotProp_LineWeight, 2.0f + }); - if (ImPlot::BeginPlot("Colorful Line Plot", ImVec2(-1,0))) { - ImPlot::PlotLine("f(x)", xs1, ys1, 1001, { - ImPlotProp_LineColor, ImVec4(1,0,1,1), - ImPlotProp_LineColors, colors1, + // 3. Per-vertex colors sampled from colormap + ImPlot::PlotLine("Colormap Colors", xs1, ys3, 101, { + ImPlotProp_LineColors, colors_colormap, + ImPlotProp_LineWeight, 3.0f }); + ImPlot::EndPlot(); } } @@ -2416,7 +2446,7 @@ void ShowDemoWindow(bool* p_open) { DemoHeader("Images", Demo_Images); DemoHeader("Markers and Text", Demo_MarkersAndText); DemoHeader("NaN Values", Demo_NaNValues); - DemoHeader("Per Point Colors", Demo_PerPointColors); + DemoHeader("Per-Index Colors", Demo_PerIndexColors); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Subplots")) { diff --git a/implot_items.cpp b/implot_items.cpp index 558daa9f..b3daf4b9 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -1016,12 +1016,12 @@ struct RendererLineStripSkip : RendererBase { mutable ImVec2 UV1; }; -template +template struct RendererLineSegments1 : RendererBase { - RendererLineSegments1(const _Getter& getter, ImU32 col, float weight) : + RendererLineSegments1(const _Getter& getter, const _GetterColor& getter_color, float weight) : RendererBase(getter.Count / 2, 6, 4), Getter(getter), - Col(col), + GetterColor(getter_color), HalfWeight(ImMax(1.0f,weight)*0.5f) { } void Init(ImDrawList& draw_list) const { @@ -1032,11 +1032,12 @@ struct RendererLineSegments1 : RendererBase { 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,Col,UV0,UV1); + ImU32 col = GetterColor[prim*2]; + PrimLine(draw_list,P1,P2,HalfWeight,col,UV0,UV1); return true; } const _Getter& Getter; - const ImU32 Col; + const _GetterColor& GetterColor; mutable float HalfWeight; mutable ImVec2 UV0; mutable ImVec2 UV1; @@ -1826,7 +1827,13 @@ void PlotLineEx(const char* label_id, const _Getter& getter, const ImPlotSpec& s if (s.RenderLine) { const ImU32 col_line = ImGui::GetColorU32(s.Spec.LineColor); if (ImHasFlag(spec.Flags,ImPlotLineFlags_Segments)) { - RenderPrimitives1(getter,col_line,s.Spec.LineWeight); + if (s.Spec.LineColors != NULL) { + IndexerIdxColor color_indexer(s.Spec.LineColors, getter.Count); + RenderPrimitives2(getter,color_indexer,s.Spec.LineWeight); + } else { + IndexerConstColor color_indexer(col_line); + RenderPrimitives2(getter,color_indexer,s.Spec.LineWeight); + } } else if (ImHasFlag(spec.Flags, ImPlotLineFlags_Loop)) { if (s.Spec.LineColors != NULL) { From e9de1a602fecd1164d58100a45a8b050d69074d8 Mon Sep 17 00:00:00 2001 From: Breno Cunha Queiroz Date: Fri, 13 Feb 2026 14:18:22 +0100 Subject: [PATCH 05/23] feat: PlotShaded with per-index color --- implot_demo.cpp | 29 +++++++++++++++++++ implot_items.cpp | 75 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 82 insertions(+), 22 deletions(-) diff --git a/implot_demo.cpp b/implot_demo.cpp index 71e59ccf..f45b7f5c 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -1107,6 +1107,35 @@ void Demo_PerIndexColors() { ImPlot::EndPlot(); } + + if (ImPlot::BeginPlot("Colorful Shaded", ImVec2(-1,0))) { + ImPlot::SetupAxes("x","y"); + ImPlot::SetupAxesLimits(0, 1, -0.5, 1.0); + + // Constant color with FillAlpha + static double y_ref = 0.0; + ImPlot::PlotShaded("Const Color", xs1, ys3, 101, y_ref, { + ImPlotProp_FillColor, ImVec4(0.3f, 0.7f, 1.0f, 1.0f), + ImPlotProp_FillAlpha, 0.4f + }); + + // Per-vertex colors with FillAlpha (rainbow) + ImPlot::PlotShaded("Rainbow Shaded", xs1, ys1, 101, y_ref, { + ImPlotProp_FillColors, colors_rainbow, + ImPlotProp_FillAlpha, 0.5f + }); + + // Per-vertex colors with PlotLine (colormap) + ImPlot::PlotLine("Colormap Line+Fill", xs1, ys2, 101, { + ImPlotProp_LineColors, colors_colormap, + ImPlotProp_FillColors, colors_colormap, + ImPlotProp_LineWeight, 2.0f, + ImPlotProp_FillAlpha, 0.3f, + ImPlotProp_Flags, ImPlotLineFlags_Shaded + }); + + ImPlot::EndPlot(); + } } //----------------------------------------------------------------------------- diff --git a/implot_items.cpp b/implot_items.cpp index b3daf4b9..6fdc9559 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -708,12 +708,19 @@ struct GetterError { template struct GetterColor { - GetterColor(_IndexerColor color, int count) : Indexer(color), Count(count) { } - template IMPLOT_INLINE ImU32 operator()(I idx) const { - return ImU32(Indexer[idx]); - } - const _IndexerColor Indexer; - const int Count; + GetterColor(_IndexerColor color, int count, float alpha = 1.0f) : Indexer(color), Count(count), Alpha(alpha) { } + template IMPLOT_INLINE ImU32 operator[](I idx) const { + ImU32 col = Indexer[idx]; + if (Alpha < 1.0f) { + ImVec4 col_vec = ImGui::ColorConvertU32ToFloat4(col); + col_vec.w *= Alpha; + col = ImGui::GetColorU32(col_vec); + } + return col; + } + const _IndexerColor Indexer; + const int Count; + const float Alpha; }; //----------------------------------------------------------------------------- @@ -1356,13 +1363,13 @@ struct RendererStairsPostShaded : RendererBase { -template +template struct RendererShaded : RendererBase { - RendererShaded(const _Getter1& getter1, const _Getter2& getter2, ImU32 col) : + RendererShaded(const _Getter1& getter1, const _Getter2& getter2, const _GetterColor& getter_color) : RendererBase(ImMin(getter1.Count, getter2.Count) - 1, 6, 5), Getter1(getter1), Getter2(getter2), - Col(col) + GetterColor(getter_color) { P11 = this->Transformer(Getter1[0]); P12 = this->Transformer(Getter2[0]); @@ -1379,23 +1386,24 @@ struct RendererShaded : RendererBase { P12 = P22; return false; } + ImU32 col = GetterColor[prim]; const int intersect = (P11.y > P12.y && P22.y > P21.y) || (P12.y > P11.y && P21.y > P22.y); const ImVec2 intersection = intersect == 0 ? ImVec2(0,0) : Intersection(P11,P21,P12,P22); draw_list._VtxWritePtr[0].pos = P11; draw_list._VtxWritePtr[0].uv = UV; - draw_list._VtxWritePtr[0].col = Col; + draw_list._VtxWritePtr[0].col = col; draw_list._VtxWritePtr[1].pos = P21; draw_list._VtxWritePtr[1].uv = UV; - draw_list._VtxWritePtr[1].col = Col; + draw_list._VtxWritePtr[1].col = col; draw_list._VtxWritePtr[2].pos = intersection; draw_list._VtxWritePtr[2].uv = UV; - draw_list._VtxWritePtr[2].col = Col; + draw_list._VtxWritePtr[2].col = col; draw_list._VtxWritePtr[3].pos = P12; draw_list._VtxWritePtr[3].uv = UV; - draw_list._VtxWritePtr[3].col = Col; + draw_list._VtxWritePtr[3].col = col; draw_list._VtxWritePtr[4].pos = P22; draw_list._VtxWritePtr[4].uv = UV; - draw_list._VtxWritePtr[4].col = Col; + draw_list._VtxWritePtr[4].col = col; draw_list._VtxWritePtr += 5; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1 + intersect); @@ -1411,7 +1419,7 @@ struct RendererShaded : RendererBase { } const _Getter1& Getter1; const _Getter2& Getter2; - const ImU32 Col; + const _GetterColor& GetterColor; mutable ImVec2 P11; mutable ImVec2 P12; mutable ImVec2 UV; @@ -1503,6 +1511,13 @@ void RenderPrimitives2(const _Getter1& getter1, const _Getter2& getter2, Args... RenderPrimitivesEx(_Renderer<_Getter1,_Getter2>(getter1,getter2,args...), draw_list, cull_rect); } +template