From fbbb499078a29f2a308e2e6be6d46d0e890f4f50 Mon Sep 17 00:00:00 2001 From: Mihaly Sisak Date: Sat, 18 Jan 2025 22:04:30 +0100 Subject: [PATCH 1/6] adding per-item colors with ColorGetterFuncPtr --- implot.h | 3 + implot_items.cpp | 465 ++++++++++++++++++++++++++++------------------- 2 files changed, 279 insertions(+), 189 deletions(-) diff --git a/implot.h b/implot.h index a7961c4f..35a381d2 100644 --- a/implot.h +++ b/implot.h @@ -589,6 +589,9 @@ typedef int (*ImPlotFormatter)(double value, char* buff, int size, void* user_da // Callback signature for data getter. typedef ImPlotPoint (*ImPlotGetter)(int idx, void* user_data); +// Callback signature for color getter. +typedef ImU32 (*ImPlotColorGetter)(ImPlotCol col, int idx, void* user_data); + // Callback signature for axis transform. typedef double (*ImPlotTransform)(double value, void* user_data); diff --git a/implot_items.cpp b/implot_items.cpp index 741eaaf2..2153a2f2 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -647,6 +647,32 @@ struct GetterError { const int Stride; }; +struct ColorGetter { + ColorGetter() { } + template IMPLOT_INLINE ImU32 operator()(ImPlotCol col, I idx) const { + (void)idx; + IM_ASSERT(col < 5); + return ImGui::GetColorU32(GetItemData().Colors[col]); + } + IMPLOT_INLINE bool is_fill_eq_line() const { + return ImGui::GetColorU32(GetItemData().Colors[ImPlotCol_Fill]) == ImGui::GetColorU32(GetItemData().Colors[ImPlotCol_Line]); + } +}; + +/// Interprets a user's function pointer as ImU32 color +struct ColorGetterFuncPtr { + ColorGetterFuncPtr(ImPlotColorGetter color, void* data) : + Color(color), + Data(data) + { } + template IMPLOT_INLINE ImU32 operator()(ImPlotCol col, I idx) const { + return Color(col, idx, Data); + } + IMPLOT_INLINE bool is_fill_eq_line() const { return false; } + ImPlotColorGetter Color; + void* const Data; +}; + //----------------------------------------------------------------------------- // [SECTION] Fitters //----------------------------------------------------------------------------- @@ -863,73 +889,84 @@ struct RendererBase { const int VtxConsumed; }; -template +template struct RendererLineStrip : RendererBase { - RendererLineStrip(const _Getter& getter, ImU32 col, float weight) : + RendererLineStrip(const _Getter& getter, const _ColorGetter& col, float weight) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f,weight)*0.5f) { P1 = this->Transformer(Getter(0)); + C1 = Col(ImPlotCol_Line, 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)); + ImU32 C2 = Col(ImPlotCol_Line, prim + 1); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { P1 = P2; return false; } - PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); + PrimLine(draw_list,P1,P2,HalfWeight,C1,UV0,UV1); P1 = P2; + C1 = C2; return true; } const _Getter& Getter; - const ImU32 Col; + const _ColorGetter& Col; mutable float HalfWeight; mutable ImVec2 P1; + mutable ImU32 C1; mutable ImVec2 UV0; mutable ImVec2 UV1; }; -template +template struct RendererLineStripSkip : RendererBase { - RendererLineStripSkip(const _Getter& getter, ImU32 col, float weight) : + RendererLineStripSkip(const _Getter& getter, const _ColorGetter& col, float weight) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col), HalfWeight(ImMax(1.0f,weight)*0.5f) { P1 = this->Transformer(Getter(0)); + C1 = Col(ImPlotCol_Line, 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)); + ImU32 C2 = Col(ImPlotCol_Line, prim + 1); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { - if (!ImNan(P2.x) && !ImNan(P2.y)) + if (!ImNan(P2.x) && !ImNan(P2.y)){ P1 = P2; + C1 = C2; + } return false; } - PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); - if (!ImNan(P2.x) && !ImNan(P2.y)) + PrimLine(draw_list,P1,P2,HalfWeight,C1,UV0,UV1); + if (!ImNan(P2.x) && !ImNan(P2.y)){ P1 = P2; + C1 = C2; + } return true; } const _Getter& Getter; - const ImU32 Col; + const _ColorGetter& Col; mutable float HalfWeight; mutable ImVec2 P1; + mutable ImU32 C1; mutable ImVec2 UV0; mutable ImVec2 UV1; }; -template +template struct RendererLineSegments1 : RendererBase { - RendererLineSegments1(const _Getter& getter, ImU32 col, float weight) : + RendererLineSegments1(const _Getter& getter, const _ColorGetter& col, float weight) : RendererBase(getter.Count / 2, 6, 4), Getter(getter), Col(col), @@ -941,21 +978,22 @@ struct RendererLineSegments1 : RendererBase { 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)); + ImU32 C1 = Col(ImPlotCol_Line, prim*2+0); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) return false; - PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); + PrimLine(draw_list,P1,P2,HalfWeight,C1,UV0,UV1); return true; } const _Getter& Getter; - const ImU32 Col; + const _ColorGetter& Col; mutable float HalfWeight; mutable ImVec2 UV0; mutable ImVec2 UV1; }; -template +template struct RendererLineSegments2 : RendererBase { - RendererLineSegments2(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, float weight) : + RendererLineSegments2(const _Getter1& getter1, const _Getter2& getter2, const _ColorGetter& col, float weight) : RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), Getter1(getter1), Getter2(getter2), @@ -968,22 +1006,23 @@ struct RendererLineSegments2 : RendererBase { 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)); + ImU32 C1 = Col(ImPlotCol_Line, prim); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) return false; - PrimLine(draw_list,P1,P2,HalfWeight,Col,UV0,UV1); + PrimLine(draw_list,P1,P2,HalfWeight,C1,UV0,UV1); return true; } const _Getter1& Getter1; const _Getter2& Getter2; - const ImU32 Col; + const _ColorGetter& Col; mutable float HalfWeight; mutable ImVec2 UV0; mutable ImVec2 UV1; }; -template +template struct RendererBarsFillV : RendererBase { - RendererBarsFillV(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double width) : + RendererBarsFillV(const _Getter1& getter1, const _Getter2& getter2, const _ColorGetter& col, double width) : RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), Getter1(getter1), Getter2(getter2), @@ -1000,6 +1039,7 @@ struct RendererBarsFillV : RendererBase { p2.x -= HalfWidth; ImVec2 P1 = this->Transformer(p1); ImVec2 P2 = this->Transformer(p2); + ImU32 C1 = Col(ImPlotCol_Fill, prim); float width_px = ImAbs(P1.x-P2.x); if (width_px < 1.0f) { P1.x += P1.x > P2.x ? (1-width_px) / 2 : (width_px-1) / 2; @@ -1009,19 +1049,19 @@ struct RendererBarsFillV : RendererBase { ImVec2 PMax = ImMax(P1, P2); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) return false; - PrimRectFill(draw_list,PMin,PMax,Col,UV); + PrimRectFill(draw_list,PMin,PMax,C1,UV); return true; } const _Getter1& Getter1; const _Getter2& Getter2; - const ImU32 Col; + const _ColorGetter& Col; const double HalfWidth; mutable ImVec2 UV; }; -template +template struct RendererBarsFillH : RendererBase { - RendererBarsFillH(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double height) : + RendererBarsFillH(const _Getter1& getter1, const _Getter2& getter2, const _ColorGetter& col, double height) : RendererBase(ImMin(getter1.Count, getter1.Count), 6, 4), Getter1(getter1), Getter2(getter2), @@ -1038,6 +1078,7 @@ struct RendererBarsFillH : RendererBase { p2.y -= HalfHeight; ImVec2 P1 = this->Transformer(p1); ImVec2 P2 = this->Transformer(p2); + ImU32 C1 = Col(ImPlotCol_Fill, prim); float height_px = ImAbs(P1.y-P2.y); if (height_px < 1.0f) { P1.y += P1.y > P2.y ? (1-height_px) / 2 : (height_px-1) / 2; @@ -1047,19 +1088,19 @@ struct RendererBarsFillH : RendererBase { ImVec2 PMax = ImMax(P1, P2); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) return false; - PrimRectFill(draw_list,PMin,PMax,Col,UV); + PrimRectFill(draw_list,PMin,PMax,C1,UV); return true; } const _Getter1& Getter1; const _Getter2& Getter2; - const ImU32 Col; + const _ColorGetter& Col; const double HalfHeight; mutable ImVec2 UV; }; -template +template struct RendererBarsLineV : RendererBase { - RendererBarsLineV(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double width, float weight) : + RendererBarsLineV(const _Getter1& getter1, const _Getter2& getter2, const _ColorGetter& col, double width, float weight) : RendererBase(ImMin(getter1.Count, getter1.Count), 24, 8), Getter1(getter1), Getter2(getter2), @@ -1077,6 +1118,7 @@ struct RendererBarsLineV : RendererBase { p2.x -= HalfWidth; ImVec2 P1 = this->Transformer(p1); ImVec2 P2 = this->Transformer(p2); + ImU32 C1 = Col(ImPlotCol_Line, prim); float width_px = ImAbs(P1.x-P2.x); if (width_px < 1.0f) { P1.x += P1.x > P2.x ? (1-width_px) / 2 : (width_px-1) / 2; @@ -1086,20 +1128,20 @@ struct RendererBarsLineV : RendererBase { ImVec2 PMax = ImMax(P1, P2); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) return false; - PrimRectLine(draw_list,PMin,PMax,Weight,Col,UV); + PrimRectLine(draw_list,PMin,PMax,Weight,C1,UV); return true; } const _Getter1& Getter1; const _Getter2& Getter2; - const ImU32 Col; + const _ColorGetter& Col; const double HalfWidth; const float Weight; mutable ImVec2 UV; }; -template +template struct RendererBarsLineH : RendererBase { - RendererBarsLineH(const _Getter1& getter1, const _Getter2& getter2, ImU32 col, double height, float weight) : + RendererBarsLineH(const _Getter1& getter1, const _Getter2& getter2, const _ColorGetter& col, double height, float weight) : RendererBase(ImMin(getter1.Count, getter1.Count), 24, 8), Getter1(getter1), Getter2(getter2), @@ -1117,6 +1159,7 @@ struct RendererBarsLineH : RendererBase { p2.y -= HalfHeight; ImVec2 P1 = this->Transformer(p1); ImVec2 P2 = this->Transformer(p2); + ImU32 C1 = Col(ImPlotCol_Line, prim); float height_px = ImAbs(P1.y-P2.y); if (height_px < 1.0f) { P1.y += P1.y > P2.y ? (1-height_px) / 2 : (height_px-1) / 2; @@ -1126,88 +1169,99 @@ struct RendererBarsLineH : RendererBase { ImVec2 PMax = ImMax(P1, P2); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) return false; - PrimRectLine(draw_list,PMin,PMax,Weight,Col,UV); + PrimRectLine(draw_list,PMin,PMax,Weight,C1,UV); return true; } const _Getter1& Getter1; const _Getter2& Getter2; - const ImU32 Col; + const _ColorGetter& Col; const double HalfHeight; const float Weight; mutable ImVec2 UV; }; -template +template struct RendererStairsPre : RendererBase { - RendererStairsPre(const _Getter& getter, ImU32 col, float weight) : + RendererStairsPre(const _Getter& getter, const _ColorGetter& col, float weight) : RendererBase(getter.Count - 1, 12, 8), Getter(getter), Col(col), HalfWeight(ImMax(1.0f,weight)*0.5f) { P1 = this->Transformer(Getter(0)); + C1 = Col(ImPlotCol_Line, 0); } 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 P2 = this->Transformer(Getter(prim + 1)); + ImU32 C2 = Col(ImPlotCol_Line, prim + 1); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { P1 = P2; + C1 = C2; return false; } - PrimRectFill(draw_list, ImVec2(P1.x - HalfWeight, P1.y), ImVec2(P1.x + HalfWeight, P2.y), Col, UV); - PrimRectFill(draw_list, ImVec2(P1.x, P2.y + HalfWeight), ImVec2(P2.x, P2.y - HalfWeight), Col, UV); + PrimRectFill(draw_list, ImVec2(P1.x - HalfWeight, P1.y), ImVec2(P1.x + HalfWeight, P2.y), C1, UV); + PrimRectFill(draw_list, ImVec2(P1.x, P2.y + HalfWeight), ImVec2(P2.x, P2.y - HalfWeight), C1, UV); P1 = P2; + C1 = C2; return true; } const _Getter& Getter; - const ImU32 Col; + const _ColorGetter& Col; mutable float HalfWeight; mutable ImVec2 P1; + mutable ImU32 C1; mutable ImVec2 UV; }; -template +template struct RendererStairsPost : RendererBase { - RendererStairsPost(const _Getter& getter, ImU32 col, float weight) : + RendererStairsPost(const _Getter& getter, const _ColorGetter& col, float weight) : RendererBase(getter.Count - 1, 12, 8), Getter(getter), Col(col), HalfWeight(ImMax(1.0f,weight) * 0.5f) { P1 = this->Transformer(Getter(0)); + C1 = Col(ImPlotCol_Fill, 0); } 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 P2 = this->Transformer(Getter(prim + 1)); + ImU32 C2 = Col(ImPlotCol_Fill, prim + 1); if (!cull_rect.Overlaps(ImRect(ImMin(P1, P2), ImMax(P1, P2)))) { P1 = P2; + C1 = C2; return false; } - PrimRectFill(draw_list, ImVec2(P1.x, P1.y + HalfWeight), ImVec2(P2.x, P1.y - HalfWeight), Col, UV); - PrimRectFill(draw_list, ImVec2(P2.x - HalfWeight, P2.y), ImVec2(P2.x + HalfWeight, P1.y), Col, UV); + PrimRectFill(draw_list, ImVec2(P1.x, P1.y + HalfWeight), ImVec2(P2.x, P1.y - HalfWeight), C1, UV); + PrimRectFill(draw_list, ImVec2(P2.x - HalfWeight, P2.y), ImVec2(P2.x + HalfWeight, P1.y), C1, UV); P1 = P2; + C1 = C2; return true; } const _Getter& Getter; - const ImU32 Col; + const _ColorGetter& Col; mutable float HalfWeight; mutable ImVec2 P1; + mutable ImU32 C1; mutable ImVec2 UV; }; -template +template struct RendererStairsPreShaded : RendererBase { - RendererStairsPreShaded(const _Getter& getter, ImU32 col) : + RendererStairsPreShaded(const _Getter& getter, const _ColorGetter& col) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col) { P1 = this->Transformer(Getter(0)); + C1 = Col(ImPlotCol_Fill, 0); Y0 = this->Transformer(ImPlotPoint(0,0)).y; } void Init(ImDrawList& draw_list) const { @@ -1215,31 +1269,36 @@ struct RendererStairsPreShaded : RendererBase { } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P2 = this->Transformer(Getter(prim + 1)); + ImU32 C2 = Col(ImPlotCol_Fill, prim + 1); ImVec2 PMin(ImMin(P1.x, P2.x), ImMin(Y0, P2.y)); ImVec2 PMax(ImMax(P1.x, P2.x), ImMax(Y0, P2.y)); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) { P1 = P2; + C1 = C2; return false; } - PrimRectFill(draw_list, PMin, PMax, Col, UV); + PrimRectFill(draw_list, PMin, PMax, C1, UV); P1 = P2; + C1 = C2; return true; } const _Getter& Getter; - const ImU32 Col; + const _ColorGetter& Col; float Y0; mutable ImVec2 P1; + mutable ImU32 C1; mutable ImVec2 UV; }; -template +template struct RendererStairsPostShaded : RendererBase { - RendererStairsPostShaded(const _Getter& getter, ImU32 col) : + RendererStairsPostShaded(const _Getter& getter, const _ColorGetter& col) : RendererBase(getter.Count - 1, 6, 4), Getter(getter), Col(col) { P1 = this->Transformer(Getter(0)); + C1 = Col(ImPlotCol_Fill, 0); Y0 = this->Transformer(ImPlotPoint(0,0)).y; } void Init(ImDrawList& draw_list) const { @@ -1247,28 +1306,32 @@ struct RendererStairsPostShaded : RendererBase { } IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P2 = this->Transformer(Getter(prim + 1)); + ImU32 C2 = Col(ImPlotCol_Fill, prim + 1); ImVec2 PMin(ImMin(P1.x, P2.x), ImMin(P1.y, Y0)); ImVec2 PMax(ImMax(P1.x, P2.x), ImMax(P1.y, Y0)); if (!cull_rect.Overlaps(ImRect(PMin, PMax))) { P1 = P2; + C1 = C2; return false; } - PrimRectFill(draw_list, PMin, PMax, Col, UV); + PrimRectFill(draw_list, PMin, PMax, C1, UV); P1 = P2; + C1 = C2; return true; } const _Getter& Getter; - const ImU32 Col; + const _ColorGetter& Col; float Y0; mutable ImVec2 P1; + mutable ImU32 C1; mutable ImVec2 UV; }; -template +template struct RendererShaded : RendererBase { - RendererShaded(const _Getter1& getter1, const _Getter2& getter2, ImU32 col) : + RendererShaded(const _Getter1& getter1, const _Getter2& getter2, const _ColorGetter& col) : RendererBase(ImMin(getter1.Count, getter2.Count) - 1, 6, 5), Getter1(getter1), Getter2(getter2), @@ -1276,6 +1339,7 @@ struct RendererShaded : RendererBase { { P11 = this->Transformer(Getter1(0)); P12 = this->Transformer(Getter2(0)); + C11 = Col(ImPlotCol_Fill, 0); } void Init(ImDrawList& draw_list) const { UV = draw_list._Data->TexUvWhitePixel; @@ -1283,29 +1347,31 @@ struct RendererShaded : RendererBase { IMPLOT_INLINE bool Render(ImDrawList& draw_list, const ImRect& cull_rect, int prim) const { ImVec2 P21 = this->Transformer(Getter1(prim+1)); ImVec2 P22 = this->Transformer(Getter2(prim+1)); + ImU32 C21 = Col(ImPlotCol_Fill, prim+1); ImRect rect(ImMin(ImMin(ImMin(P11,P12),P21),P22), ImMax(ImMax(ImMax(P11,P12),P21),P22)); if (!cull_rect.Overlaps(rect)) { P11 = P21; P12 = P22; + C11 = C21; return false; } 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 = C11; draw_list._VtxWritePtr[1].pos = P21; draw_list._VtxWritePtr[1].uv = UV; - draw_list._VtxWritePtr[1].col = Col; + draw_list._VtxWritePtr[1].col = C11; draw_list._VtxWritePtr[2].pos = intersection; draw_list._VtxWritePtr[2].uv = UV; - draw_list._VtxWritePtr[2].col = Col; + draw_list._VtxWritePtr[2].col = C11; draw_list._VtxWritePtr[3].pos = P12; draw_list._VtxWritePtr[3].uv = UV; - draw_list._VtxWritePtr[3].col = Col; + draw_list._VtxWritePtr[3].col = C11; draw_list._VtxWritePtr[4].pos = P22; draw_list._VtxWritePtr[4].uv = UV; - draw_list._VtxWritePtr[4].col = Col; + draw_list._VtxWritePtr[4].col = C11; draw_list._VtxWritePtr += 5; draw_list._IdxWritePtr[0] = (ImDrawIdx)(draw_list._VtxCurrentIdx); draw_list._IdxWritePtr[1] = (ImDrawIdx)(draw_list._VtxCurrentIdx + 1 + intersect); @@ -1317,13 +1383,15 @@ struct RendererShaded : RendererBase { draw_list._VtxCurrentIdx += 5; P11 = P21; P12 = P22; + C11 = C21; return true; } const _Getter1& Getter1; const _Getter2& Getter2; - const ImU32 Col; + const _ColorGetter& Col; mutable ImVec2 P11; mutable ImVec2 P12; + mutable ImU32 C11; mutable ImVec2 UV; }; @@ -1406,20 +1474,27 @@ void RenderPrimitives1(const _Getter& getter, Args... args) { RenderPrimitivesEx(_Renderer<_Getter>(getter,args...), draw_list, cull_rect); } -template