diff --git a/implot.cpp b/implot.cpp index 9332fd3..e77b1b8 100644 --- a/implot.cpp +++ b/implot.cpp @@ -31,6 +31,7 @@ Below is a change-log of API breaking changes only. If you are using one of the When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all implot files. You can read releases logs https://github.com/epezent/implot/releases for more details. +- 2020/06/13 (0.4) - The flags `ImPlotAxisFlag_Adaptive` and `ImPlotFlags_Cull` were removed. Both are now done internally by default. - 2020/06/03 (0.3) - The signature and behavior of PlotPieChart was changed so that data with sum less than 1 can optionally be normalized. The label format can now be specified as well. - 2020/06/01 (0.3) - SetPalette was changed to `SetColormap` for consistency with other plotting libraries. `RestorePalette` was removed. Use `SetColormap(ImPlotColormap_Default)`. - 2020/05/31 (0.3) - Plot functions taking custom ImVec2* getters were removed. Use the ImPlotPoint* getter versions instead. @@ -75,8 +76,7 @@ You can read releases logs https://github.com/epezent/implot/releases for more d } \ } -// Special Color used to specific that a plot item color should set determined automatically. -#define IM_COL_AUTO ImVec4(0,0,0,-1) + // The maximum number of support y-axes #define MAX_Y_AXES 3 @@ -94,18 +94,18 @@ ImPlotStyle::ImPlotStyle() { DigitalBitHeight = 8; DigitalBitGap = 4; - Colors[ImPlotCol_Line] = IM_COL_AUTO; - Colors[ImPlotCol_Fill] = IM_COL_AUTO; - Colors[ImPlotCol_MarkerOutline] = IM_COL_AUTO; - Colors[ImPlotCol_MarkerFill] = IM_COL_AUTO; - Colors[ImPlotCol_ErrorBar] = IM_COL_AUTO; - Colors[ImPlotCol_FrameBg] = IM_COL_AUTO; - Colors[ImPlotCol_PlotBg] = IM_COL_AUTO; - Colors[ImPlotCol_PlotBorder] = IM_COL_AUTO; - Colors[ImPlotCol_XAxis] = IM_COL_AUTO; - Colors[ImPlotCol_YAxis] = IM_COL_AUTO; - Colors[ImPlotCol_YAxis2] = IM_COL_AUTO; - Colors[ImPlotCol_YAxis3] = IM_COL_AUTO; + Colors[ImPlotCol_Line] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_Fill] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_MarkerOutline] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_MarkerFill] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_ErrorBar] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_FrameBg] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_PlotBg] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_PlotBorder] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_XAxis] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_YAxis] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_YAxis2] = IMPLOT_COL_AUTO; + Colors[ImPlotCol_YAxis3] = IMPLOT_COL_AUTO; Colors[ImPlotCol_Selection] = ImVec4(1,1,0,1); Colors[ImPlotCol_Query] = ImVec4(0,1,0,1); } @@ -183,6 +183,15 @@ inline int PosMod(int l, int r) { return (l % r + r) % r; } +// Returns the intersection point of two lines A and B (assumes they are not parallel!) +inline ImVec2 Intersection(const ImVec2& a1, const ImVec2& a2, const ImVec2& b1, const ImVec2& b2) { + float v1 = (a1.x * a2.y - a1.y * a2.x); + float v2 = (b1.x * b2.y - b1.y * b2.x); + float v3 = ((a1.x - a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x - b2.x)); + return ImVec2((v1 * (b1.x - b2.x) - v2 * (a1.x - a2.x)) / v3, + (v1 * (b1.y - b2.y) - v2 * (a1.y - a2.y)) / v3); +} + // Turns NANs to 0s inline double ConstrainNan(double val) { return isnan(val) ? 0 : val; @@ -586,63 +595,6 @@ ImVec2 PlotToPixels(const ImPlotPoint& plt, int y_axis) { return PlotToPixels(plt.x, plt.y, y_axis); } -// Transformer functors - -struct TransformerLinLin { - TransformerLinLin(int y_axis) : YAxis(y_axis) {} - - inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); } - inline ImVec2 operator()(double x, double y) { - return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), - (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); - } - - int YAxis; -}; - -struct TransformerLogLin { - TransformerLogLin(int y_axis) : YAxis(y_axis) {} - - inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); } - inline ImVec2 operator()(double x, double y) { - double t = ImLog10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX; - x = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t); - return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), - (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); - } - - int YAxis; -}; - -struct TransformerLinLog { - TransformerLinLog(int y_axis) : YAxis(y_axis) {} - - inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); } - inline ImVec2 operator()(double x, double y) { - double t = ImLog10(y / gp.CurrentPlot->YAxis[YAxis].Range.Min) / gp.LogDenY[YAxis]; - y = ImLerp(gp.CurrentPlot->YAxis[YAxis].Range.Min, gp.CurrentPlot->YAxis[YAxis].Range.Max, (float)t); - return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), - (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); - } - int YAxis; -}; - -struct TransformerLogLog { - TransformerLogLog(int y_axis) : YAxis(y_axis) {} - - inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); } - inline ImVec2 operator()(double x, double y) { - double t = ImLog10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX; - x = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t); - t = ImLog10(y / gp.CurrentPlot->YAxis[YAxis].Range.Min) / gp.LogDenY[YAxis]; - y = ImLerp(gp.CurrentPlot->YAxis[YAxis].Range.Min, gp.CurrentPlot->YAxis[YAxis].Range.Max, (float)t); - return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), - (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); - } - - int YAxis; -}; - //----------------------------------------------------------------------------- // Legend Utils //----------------------------------------------------------------------------- @@ -959,12 +911,12 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons plot.XAxis.Divisions = (int)IM_ROUND(0.003 * gp.BB_Canvas.GetWidth()); if (plot.XAxis.Divisions < 2) plot.XAxis.Divisions = 2; - + for (int i = 0; i < MAX_Y_AXES; i++) { plot.YAxis[i].Divisions = (int)IM_ROUND(0.003 * gp.BB_Canvas.GetHeight()); if (plot.YAxis[i].Divisions < 2) plot.YAxis[i].Divisions = 2; - + } // COLORS ----------------------------------------------------------------- @@ -2067,9 +2019,243 @@ void PopStyleVar(int count) { } //----------------------------------------------------------------------------- -// RENDERING FUNCTIONS +// GETTERS //----------------------------------------------------------------------------- +template +inline T OffsetAndStride(const T* data, int idx, int count, int offset, int stride) { + idx = PosMod(offset + idx, count); + return *(const T*)(const void*)((const unsigned char*)data + (size_t)idx * stride); +} + +template +struct GetterYs { + GetterYs(const T* ys, int count, int offset, int stride) { + Ys = ys; + Count = count; + Offset = PosMod(offset, count);; + Stride = stride; + } + const T* Ys; + int Count; + int Offset; + int Stride; + inline ImPlotPoint operator()(int idx) { + return ImPlotPoint((T)idx, OffsetAndStride(Ys, idx, Count, Offset, Stride)); + } +}; + +template +struct GetterXsYs { + GetterXsYs(const T* xs, const T* ys, int count, int offset, int stride) { + Xs = xs; Ys = ys; + Count = count; + Offset = PosMod(offset, count);; + Stride = stride; + } + const T* Xs; + const T* Ys; + int Count; + int Offset; + int Stride; + inline ImPlotPoint operator()(int idx) { + return ImPlotPoint(OffsetAndStride(Xs, idx, Count, Offset, Stride), OffsetAndStride(Ys, idx, Count, Offset, Stride)); + } +}; + +template +struct GetterXsYRef { + GetterXsYRef(const T* xs, T y_ref, int count, int offset, int stride) { + Xs = xs; + YRef = y_ref; + Count = count; + Offset = PosMod(offset, count);; + Stride = stride; + } + const T* Xs; + T YRef; + int Count; + int Offset; + int Stride; + inline ImPlotPoint operator()(int idx) { + return ImPlotPoint(OffsetAndStride(Xs, idx, Count, Offset, Stride), YRef); + } +}; + +struct GetterImVec2 { + GetterImVec2(const ImVec2* data, int count, int offset) { + Data = data; + Count = count; + Offset = PosMod(offset, count); + } + inline ImPlotPoint operator()(int idx) { return ImPlotPoint(Data[idx].x, Data[idx].y); } + const ImVec2* Data; + int Count; + int Offset; +}; + +struct GetterImPlotPoint { + GetterImPlotPoint(const ImPlotPoint* data, int count, int offset) { + Data = data; + Count = count; + Offset = PosMod(offset, count); + } + inline ImPlotPoint operator()(int idx) { return Data[idx]; } + const ImPlotPoint* Data; + int Count; + int Offset; +}; + +struct GetterFuncPtrImPlotPoint { + GetterFuncPtrImPlotPoint(ImPlotPoint (*g)(void* data, int idx), void* d, int count, int offset) { + getter = g; + Data = d; + Count = count; + Offset = PosMod(offset, count); + } + inline ImPlotPoint operator()(int idx) { return getter(Data, idx); } + ImPlotPoint (*getter)(void* data, int idx); + void* Data; + int Count; + int Offset; +}; + +template +struct GetterBarV { + const T* Ys; T XShift; int Count; int Offset; int Stride; + GetterBarV(const T* ys, T xshift, int count, int offset, int stride) { Ys = ys; XShift = xshift; Count = count; Offset = offset; Stride = stride; } + inline ImPlotPoint operator()(int idx) { return ImPlotPoint((T)idx + XShift, OffsetAndStride(Ys, idx, Count, Offset, Stride)); } +}; + +template +struct GetterBarH { + const T* Xs; T YShift; int Count; int Offset; int Stride; + GetterBarH(const T* xs, T yshift, int count, int offset, int stride) { Xs = xs; YShift = yshift; Count = count; Offset = offset; Stride = stride; } + inline ImPlotPoint operator()(int idx) { return ImPlotPoint(OffsetAndStride(Xs, idx, Count, Offset, Stride), (T)idx + YShift); } +}; + +struct ImPlotPointError { + ImPlotPointError(double _x, double _y, double _neg, double _pos) { + x = _x; y = _y; neg = _neg; pos = _pos; + } + double x, y, neg, pos; +}; + +template +struct GetterError { + const T* Xs; const T* Ys; const T* Neg; const T* Pos; int Count; int Offset; int Stride; + GetterError(const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset, int stride) { + Xs = xs; Ys = ys; Neg = neg; Pos = pos; Count = count; Offset = offset; Stride = stride; + } + ImPlotPointError operator()(int idx) { + return ImPlotPointError(OffsetAndStride(Xs, idx, Count, Offset, Stride), + OffsetAndStride(Ys, idx, Count, Offset, Stride), + OffsetAndStride(Neg, idx, Count, Offset, Stride), + OffsetAndStride(Pos, idx, Count, Offset, Stride)); + } +}; + +//----------------------------------------------------------------------------- +// TRANSFORMERS +//----------------------------------------------------------------------------- + +struct TransformerLinLin { + TransformerLinLin(int y_axis) : YAxis(y_axis) {} + + inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); } + inline ImVec2 operator()(double x, double y) { + return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), + (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); + } + + int YAxis; +}; + +struct TransformerLogLin { + TransformerLogLin(int y_axis) : YAxis(y_axis) {} + + inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); } + inline ImVec2 operator()(double x, double y) { + double t = ImLog10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX; + x = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t); + return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), + (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); + } + + int YAxis; +}; + +struct TransformerLinLog { + TransformerLinLog(int y_axis) : YAxis(y_axis) {} + + inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); } + inline ImVec2 operator()(double x, double y) { + double t = ImLog10(y / gp.CurrentPlot->YAxis[YAxis].Range.Min) / gp.LogDenY[YAxis]; + y = ImLerp(gp.CurrentPlot->YAxis[YAxis].Range.Min, gp.CurrentPlot->YAxis[YAxis].Range.Max, (float)t); + return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), + (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); + } + int YAxis; +}; + +struct TransformerLogLog { + TransformerLogLog(int y_axis) : YAxis(y_axis) {} + + inline ImVec2 operator()(const ImPlotPoint& plt) { return (*this)(plt.x, plt.y); } + inline ImVec2 operator()(double x, double y) { + double t = ImLog10(x / gp.CurrentPlot->XAxis.Range.Min) / gp.LogDenX; + x = ImLerp(gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max, (float)t); + t = ImLog10(y / gp.CurrentPlot->YAxis[YAxis].Range.Min) / gp.LogDenY[YAxis]; + y = ImLerp(gp.CurrentPlot->YAxis[YAxis].Range.Min, gp.CurrentPlot->YAxis[YAxis].Range.Max, (float)t); + return ImVec2( (float)(gp.PixelRange[YAxis].Min.x + gp.Mx * (x - gp.CurrentPlot->XAxis.Range.Min)), + (float)(gp.PixelRange[YAxis].Min.y + gp.My[YAxis] * (y - gp.CurrentPlot->YAxis[YAxis].Range.Min)) ); + } + + int YAxis; +}; + +//----------------------------------------------------------------------------- +// RENDERERS +//----------------------------------------------------------------------------- + +/// Renders primitive shapes in bulk as efficiently as possible. +template +inline void RenderPrimitives(Renderer renderer, ImDrawList& DrawList) { + int prims = renderer.Prims; + int prims_culled = 0; + int idx = 0; + const ImVec2 uv = DrawList._Data->TexUvWhitePixel; + while (prims) { + // find how many can be reserved up to end of current draw command's limit + int cnt = (int)ImMin(size_t(prims), (((size_t(1) << sizeof(ImDrawIdx) * 8) - 1 - DrawList._VtxCurrentIdx) / Renderer::VtxConsumed)); + // make sure at least this many elements can be rendered to avoid situations where at the end of buffer this slow path is not taken all the time + if (cnt >= ImMin(64, prims)) { + if (prims_culled >= cnt) + prims_culled -= cnt; // reuse previous reservation + else { + DrawList.PrimReserve((cnt - prims_culled) * Renderer::IdxConsumed, (cnt - prims_culled) * Renderer::VtxConsumed); // add more elements to previous reservation + prims_culled = 0; + } + } + else + { + if (prims_culled > 0) { + DrawList.PrimUnreserve(prims_culled * Renderer::IdxConsumed, prims_culled * Renderer::VtxConsumed); + prims_culled = 0; + } + cnt = (int)ImMin(size_t(prims), (((size_t(1) << sizeof(ImDrawIdx) * 8) - 1 - 0/*DrawList._VtxCurrentIdx*/) / Renderer::VtxConsumed)); + DrawList.PrimReserve(cnt * Renderer::IdxConsumed, cnt * Renderer::VtxConsumed); // reserve new draw command + } + prims -= cnt; + for (int ie = idx + cnt; idx != ie; ++idx) { + if (!renderer(DrawList, uv, idx)) + prims_culled++; + } + } + if (prims_culled > 0) + DrawList.PrimUnreserve(prims_culled * Renderer::IdxConsumed, prims_culled * Renderer::VtxConsumed); +} + #define SQRT_1_2 0.70710678118f #define SQRT_3_2 0.86602540378f @@ -2191,10 +2377,10 @@ struct LineRenderer { inline LineRenderer(Getter _getter, Transformer _transformer, ImU32 col, float weight) : getter(_getter), transformer(_transformer) - { + { Prims = getter.Count - 1; - Col = col; - Weight = weight; + Col = col; + Weight = weight; p1 = transformer(getter(0)); } inline bool operator()(ImDrawList& DrawList, ImVec2 uv, int prim) { @@ -2246,17 +2432,24 @@ struct LineRenderer { static const int VtxConsumed = 4; }; -inline ImVec2 Intersection(const ImVec2& a1, const ImVec2& a2, const ImVec2& b1, const ImVec2& b2) { - float v1 = (a1.x * a2.y - a1.y * a2.x); - float v2 = (b1.x * b2.y - b1.y * b2.x); - float v3 = ((a1.x - a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x - b2.x)); - return ImVec2((v1 * (b1.x - b2.x) - v2 * (a1.x - a2.x)) / v3, - (v1 * (b1.y - b2.y) - v2 * (a1.y - a2.y)) / v3); +template +inline void RenderLineStrip(Getter getter, Transformer transformer, ImDrawList& DrawList, float line_weight, ImU32 col) { + if (HasFlag(gp.CurrentPlot->Flags, ImPlotFlags_AntiAliased)) { + ImVec2 p1 = transformer(getter(0)); + for (int i = 0; i < getter.Count; ++i) { + ImVec2 p2 = transformer(getter(i)); + if (gp.BB_Plot.Overlaps(ImRect(ImMin(p1, p2), ImMax(p1, p2)))) + DrawList.AddLine(p1, p2, col, line_weight); + p1 = p2; + } + } + else { + RenderPrimitives(LineRenderer(getter, transformer, col, line_weight), DrawList); + } } template struct ShadedRenderer { - ShadedRenderer(Getter1 _getter1, Getter2 _getter2, Transformer _transformer, ImU32 col) : getter1(_getter1), getter2(_getter2), @@ -2272,7 +2465,7 @@ struct ShadedRenderer { ImVec2 p21 = transformer(getter1(prim+1)); ImVec2 p22 = transformer(getter2(prim+1)); const int intersect = (p11.y > p12.y && p22.y > p21.y) || (p12.y > p11.y && p21.y > p22.y); - ImVec2 intersection = Intersection(p11,p21,p12,p22); + ImVec2 intersection = Intersection(p11,p21,p12,p22); DrawList._VtxWritePtr[0].pos = p11; DrawList._VtxWritePtr[0].uv = uv; DrawList._VtxWritePtr[0].col = Col; @@ -2311,219 +2504,73 @@ struct ShadedRenderer { static const int VtxConsumed = 5; }; -// struct RectRenderer { -// RectRenderer(ImU32 col) { Col = col; } -// inline void operator()(ImDrawList& DrawList, const ImVec2& p1, const ImVec2& p2, ImVec2 uv) { -// DrawList._VtxWritePtr[0].pos.x = p1.x; -// DrawList._VtxWritePtr[0].pos.y = p1.y; -// DrawList._VtxWritePtr[0].uv = uv; -// DrawList._VtxWritePtr[0].col = Col; -// DrawList._VtxWritePtr[1].pos.x = p2.x; -// DrawList._VtxWritePtr[1].pos.y = p1.y; -// DrawList._VtxWritePtr[1].uv = uv; -// DrawList._VtxWritePtr[1].col = Col; -// DrawList._VtxWritePtr[2].pos.x = p2.x; -// DrawList._VtxWritePtr[2].pos.y = p2.y; -// DrawList._VtxWritePtr[2].uv = uv; -// DrawList._VtxWritePtr[2].col = Col; -// DrawList._VtxWritePtr[3].pos.x = p1.x; -// DrawList._VtxWritePtr[3].pos.y = p2.y; -// DrawList._VtxWritePtr[3].uv = uv; -// DrawList._VtxWritePtr[3].col = Col; -// DrawList._VtxWritePtr += 4; -// DrawList._IdxWritePtr[0] = (ImDrawIdx)(DrawList._VtxCurrentIdx); -// DrawList._IdxWritePtr[1] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1); -// DrawList._IdxWritePtr[2] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3); -// DrawList._IdxWritePtr[3] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 1); -// DrawList._IdxWritePtr[4] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 2); -// DrawList._IdxWritePtr[5] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 3); -// DrawList._IdxWritePtr += 6; -// DrawList._VtxCurrentIdx += 4; -// } -// ImU32 Col; -// static const int IdxConsumed = 6; -// static const int VtxConsumed = 4; -// }; +//----------------------------------------------------------------------------- +// RENDERING UTILS +//----------------------------------------------------------------------------- -template -inline void RenderPrimitives(Renderer renderer, ImDrawList& DrawList) { - int prims = renderer.Prims; - int prims_culled = 0; - int idx = 0; - const ImVec2 uv = DrawList._Data->TexUvWhitePixel; - while (prims) { - // find how many can be reserved up to end of current draw command's limit - int cnt = (int)ImMin(size_t(prims), (((size_t(1) << sizeof(ImDrawIdx) * 8) - 1 - DrawList._VtxCurrentIdx) / Renderer::VtxConsumed)); - // make sure at least this many elements can be rendered to avoid situations where at the end of buffer this slow path is not taken all the time - if (cnt >= ImMin(64, prims)) { - if (prims_culled >= cnt) - prims_culled -= cnt; // reuse previous reservation - else { - DrawList.PrimReserve((cnt - prims_culled) * Renderer::IdxConsumed, (cnt - prims_culled) * Renderer::VtxConsumed); // add more elements to previous reservation - prims_culled = 0; - } - } - else - { - if (prims_culled > 0) { - DrawList.PrimUnreserve(prims_culled * Renderer::IdxConsumed, prims_culled * Renderer::VtxConsumed); - prims_culled = 0; - } - cnt = (int)ImMin(size_t(prims), (((size_t(1) << sizeof(ImDrawIdx) * 8) - 1 - 0/*DrawList._VtxCurrentIdx*/) / Renderer::VtxConsumed)); - DrawList.PrimReserve(cnt * Renderer::IdxConsumed, cnt * Renderer::VtxConsumed); // reserve new draw command - } - prims -= cnt; - for (int ie = idx + cnt; idx != ie; ++idx) { - if (!renderer(DrawList, uv, idx)) - prims_culled++; - } - } - if (prims_culled > 0) - DrawList.PrimUnreserve(prims_culled * Renderer::IdxConsumed, prims_culled * Renderer::VtxConsumed); +// Returns true if a style color is set to be automaticaly determined +inline bool ColorIsAuto(ImPlotCol idx) { + return gp.Style.Colors[idx].w == -1; +} +// Recolors an item from an the current ImPlotCol if it is not automatic (i.e. alpha != -1) +inline void TryRecolorItem(ImPlotItem* item, ImPlotCol idx) { + if (gp.Style.Colors[idx].w != -1) + item->Color = gp.Style.Colors[idx]; +} +// Returns true if lines will render (e.g. basic lines, bar outlines) +inline bool WillLineRender() { + return gp.Style.Colors[ImPlotCol_Line].w != 0 && gp.Style.LineWeight > 0; +} +// Returns true if fills will render (e.g. shaded plots, bar fills) +inline bool WillFillRender() { + return gp.Style.Colors[ImPlotCol_Fill].w != 0 && gp.Style.FillAlpha > 0; +} +// Returns true if marker outlines will render +inline bool WillMarkerOutlineRender() { + return gp.Style.Colors[ImPlotCol_MarkerOutline].w != 0 && gp.Style.MarkerWeight > 0; +} +// Returns true if mark fill will render +inline bool WillMarkerFillRender() { + return gp.Style.Colors[ImPlotCol_MarkerFill].w != 0 && gp.Style.FillAlpha > 0; +} +// Gets the line color for an item +inline ImVec4 GetLineColor(ImPlotItem* item) { + return ColorIsAuto(ImPlotCol_Line) ? item->Color : gp.Style.Colors[ImPlotCol_Line]; +} +// Gets the fill color for an item +inline ImVec4 GetItemFillColor(ImPlotItem* item) { + ImVec4 col = ColorIsAuto(ImPlotCol_Fill) ? item->Color : gp.Style.Colors[ImPlotCol_Fill]; + col.w *= gp.Style.FillAlpha; + return col; +} +// Gets the marker outline color for an item +inline ImVec4 GetMarkerOutlineColor(ImPlotItem* item) { + return ColorIsAuto(ImPlotCol_MarkerOutline) ? GetLineColor(item) : gp.Style.Colors[ImPlotCol_MarkerOutline]; +} +// Gets the marker fill color for an item +inline ImVec4 GetMarkerFillColor(ImPlotItem* item) { + ImVec4 col = ColorIsAuto(ImPlotCol_MarkerFill) ? GetLineColor(item) :gp.Style.Colors[ImPlotCol_MarkerFill]; + col.w *= gp.Style.FillAlpha; + return col; +} +// Gets the error bar color +inline ImVec4 GetErrorBarColor() { + return ColorIsAuto(ImPlotCol_ErrorBar) ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : gp.Style.Colors[ImPlotCol_ErrorBar]; } //----------------------------------------------------------------------------- -// GETTERS +// PLOT LINES / MARKERS //----------------------------------------------------------------------------- -template -inline T OffsetAndStride(const T* data, int idx, int count, int offset, int stride) { - idx = PosMod(offset + idx, count); - return *(const T*)(const void*)((const unsigned char*)data + (size_t)idx * stride); -} - -template -struct GetterYs { - GetterYs(const T* ys, int count, int offset, int stride) { - Ys = ys; - Count = count; - Offset = PosMod(offset, count);; - Stride = stride; - } - const T* Ys; - int Count; - int Offset; - int Stride; - inline ImPlotPoint operator()(int idx) { - return ImPlotPoint((T)idx, OffsetAndStride(Ys, idx, Count, Offset, Stride)); - } -}; - -template -struct GetterXsYs { - GetterXsYs(const T* xs, const T* ys, int count, int offset, int stride) { - Xs = xs; Ys = ys; - Count = count; - Offset = PosMod(offset, count);; - Stride = stride; - } - const T* Xs; - const T* Ys; - int Count; - int Offset; - int Stride; - inline ImPlotPoint operator()(int idx) { - return ImPlotPoint(OffsetAndStride(Xs, idx, Count, Offset, Stride), OffsetAndStride(Ys, idx, Count, Offset, Stride)); - } -}; - -template -struct GetterXsYRef { - GetterXsYRef(const T* xs, T y_ref, int count, int offset, int stride) { - Xs = xs; - YRef = y_ref; - Count = count; - Offset = PosMod(offset, count);; - Stride = stride; - } - const T* Xs; - T YRef; - int Count; - int Offset; - int Stride; - inline ImPlotPoint operator()(int idx) { - return ImPlotPoint(OffsetAndStride(Xs, idx, Count, Offset, Stride), YRef); - } -}; - - -struct GetterImVec2 { - GetterImVec2(const ImVec2* data, int count, int offset) { - Data = data; - Count = count; - Offset = PosMod(offset, count); - } - inline ImPlotPoint operator()(int idx) { return ImPlotPoint(Data[idx].x, Data[idx].y); } - const ImVec2* Data; - int Count; - int Offset; -}; - -struct GetterImPlotPoint { - GetterImPlotPoint(const ImPlotPoint* data, int count, int offset) { - Data = data; - Count = count; - Offset = PosMod(offset, count); - } - inline ImPlotPoint operator()(int idx) { return Data[idx]; } - const ImPlotPoint* Data; - int Count; - int Offset; -}; - -struct GetterFuncPtrImPlotPoint { - GetterFuncPtrImPlotPoint(ImPlotPoint (*g)(void* data, int idx), void* d, int count, int offset) { - getter = g; - Data = d; - Count = count; - Offset = PosMod(offset, count); - } - inline ImPlotPoint operator()(int idx) { return getter(Data, idx); } - ImPlotPoint (*getter)(void* data, int idx); - void* Data; - int Count; - int Offset; -}; - -//----------------------------------------------------------------------------- -// PLOT -//----------------------------------------------------------------------------- - -template -inline void RenderLineStrip(Getter getter, Transformer transformer, ImDrawList& DrawList, float line_weight, ImU32 col) { - if (HasFlag(gp.CurrentPlot->Flags, ImPlotFlags_AntiAliased)) { - ImVec2 p1 = transformer(getter(0)); - for (int i = 0; i < getter.Count; ++i) { - ImVec2 p2 = transformer(getter(i)); - if (gp.BB_Plot.Overlaps(ImRect(ImMin(p1, p2), ImMax(p1, p2)))) - DrawList.AddLine(p1, p2, col, line_weight); - p1 = p2; - } - } - else { - RenderPrimitives(LineRenderer(getter, transformer, col, line_weight), DrawList); - } -} - template inline void PlotEx(const char* label_id, Getter getter) { IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotEx() needs to be called between BeginPlot() and EndPlot()!"); - ImPlotState* plot = gp.CurrentPlot; - const int y_axis = plot->CurrentYAxis; ImPlotItem* item = RegisterItem(label_id); if (!item->Show) return; - - ImDrawList & DrawList = *ImGui::GetWindowDrawList(); - - const bool rend_line = gp.Style.Colors[ImPlotCol_Line].w != 0 && gp.Style.LineWeight > 0; - - ImU32 col_line = gp.Style.Colors[ImPlotCol_Line].w == -1 ? ImGui::GetColorU32(item->Color) : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_Line]); - - if (gp.Style.Colors[ImPlotCol_Line].w != -1) - item->Color = gp.Style.Colors[ImPlotCol_Line]; + TryRecolorItem(item, ImPlotCol_Line); // find data extents if (gp.FitThisFrame) { @@ -2532,9 +2579,15 @@ inline void PlotEx(const char* label_id, Getter getter) FitPoint(p); } } + + ImDrawList& DrawList = *ImGui::GetWindowDrawList(); + ImPlotState* plot = gp.CurrentPlot; + const int y_axis = plot->CurrentYAxis; + PushPlotClipRect(); // render line - if (getter.Count > 1 && rend_line) { + if (getter.Count > 1 && WillLineRender()) { + ImU32 col_line = ImGui::GetColorU32(GetLineColor(item)); const float line_weight = item->Highlight ? gp.Style.LineWeight * 2 : gp.Style.LineWeight; if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale) && HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) RenderLineStrip(getter, TransformerLogLog(y_axis), DrawList, line_weight, col_line); @@ -2547,10 +2600,10 @@ inline void PlotEx(const char* label_id, Getter getter) } // render markers if (gp.Style.Marker != ImPlotMarker_None) { - const bool rend_mk_line = gp.Style.Colors[ImPlotCol_MarkerOutline].w != 0 && gp.Style.MarkerWeight > 0; - const bool rend_mk_fill = gp.Style.Colors[ImPlotCol_MarkerFill].w != 0; - const ImU32 col_mk_line = gp.Style.Colors[ImPlotCol_MarkerOutline].w == -1 ? col_line : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_MarkerOutline]); - const ImU32 col_mk_fill = gp.Style.Colors[ImPlotCol_MarkerFill].w == -1 ? col_line : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_MarkerFill]); + const bool rend_mk_line = WillMarkerOutlineRender(); + const bool rend_mk_fill = WillMarkerFillRender(); + const ImU32 col_mk_line = ImGui::GetColorU32(GetMarkerOutlineColor(item)); + const ImU32 col_mk_fill = ImGui::GetColorU32(GetMarkerFillColor(item)); if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale) && HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) RenderMarkers(getter, TransformerLogLog(y_axis), DrawList, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill); else if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale)) @@ -2680,22 +2733,15 @@ template inline void PlotShadedEx(const char* label_id, Getter1 getter1, Getter2 getter2) { IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotShaded() needs to be called between BeginPlot() and EndPlot()!"); - ImPlotState* plot = gp.CurrentPlot; - const int y_axis = plot->CurrentYAxis; ImPlotItem* item = RegisterItem(label_id); if (!item->Show) - return; + return; + TryRecolorItem(item, ImPlotCol_Fill); - ImDrawList & DrawList = *ImGui::GetWindowDrawList(); - - ImVec4 col_fill = gp.Style.Colors[ImPlotCol_Fill].w == -1 ? (item->Color) : (gp.Style.Colors[ImPlotCol_Fill]); - col_fill.w *= gp.Style.FillAlpha; - - if (gp.Style.Colors[ImPlotCol_Fill].w != -1) - item->Color = gp.Style.Colors[ImPlotCol_Fill]; - - // bool cull = HasFlag(plot->Flags, ImPlotFlags_CullData); + if (!WillFillRender()) + return; + // find data extents if (gp.FitThisFrame) { for (int i = 0; i < ImMin(getter1.Count, getter2.Count); ++i) { ImPlotPoint p1 = getter1(i); @@ -2705,15 +2751,21 @@ inline void PlotShadedEx(const char* label_id, Getter1 getter1, Getter2 getter2) } } + ImDrawList & DrawList = *ImGui::GetWindowDrawList(); + ImPlotState* plot = gp.CurrentPlot; + const int y_axis = plot->CurrentYAxis; + + ImU32 col = ImGui::GetColorU32(GetItemFillColor(item)); + PushPlotClipRect(); if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale) && HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) - RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLogLog(y_axis), ImGui::GetColorU32(col_fill)), DrawList); + RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLogLog(y_axis), col), DrawList); else if (HasFlag(plot->XAxis.Flags, ImPlotAxisFlags_LogScale)) - RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLogLin(y_axis), ImGui::GetColorU32(col_fill)), DrawList); + RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLogLin(y_axis), col), DrawList); else if (HasFlag(plot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale)) - RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLinLog(y_axis), ImGui::GetColorU32(col_fill)), DrawList); + RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLinLog(y_axis), col), DrawList); else - RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLinLin(y_axis), ImGui::GetColorU32(col_fill)), DrawList); + RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLinLin(y_axis), col), DrawList); PopPlotClipRect(); } @@ -2751,50 +2803,16 @@ void PlotShaded(const char* label_id, const double* xs, const double* ys, int co // PLOT BAR V //----------------------------------------------------------------------------- -// TODO: Migrate to RenderPrimitives - -template -struct GetterBarV { - const T* Ys; T XShift; int Count; int Offset; int Stride; - GetterBarV(const T* ys, T xshift, int count, int offset, int stride) { Ys = ys; XShift = xshift; Count = count; Offset = offset; Stride = stride; } - inline ImPlotPoint operator()(int idx) { return ImPlotPoint((T)idx + XShift, OffsetAndStride(Ys, idx, Count, Offset, Stride)); } -}; - -template -struct GetterBarH { - const T* Xs; T YShift; int Count; int Offset; int Stride; - GetterBarH(const T* xs, T yshift, int count, int offset, int stride) { Xs = xs; YShift = yshift; Count = count; Offset = offset; Stride = stride; } - inline ImPlotPoint operator()(int idx) { return ImPlotPoint(OffsetAndStride(Xs, idx, Count, Offset, Stride), (T)idx + YShift); } -}; - - template void PlotBarsEx(const char* label_id, Getter getter, TWidth width) { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotBars() needs to be called between BeginPlot() and EndPlot()!"); ImPlotItem* item = RegisterItem(label_id); if (!item->Show) return; + TryRecolorItem(item, ImPlotCol_Fill); - ImDrawList & DrawList = *ImGui::GetWindowDrawList(); - - bool rend_line = gp.Style.Colors[ImPlotCol_Line].w != 0 && gp.Style.LineWeight > 0; - bool rend_fill = gp.Style.Colors[ImPlotCol_Fill].w != 0; - - ImU32 col_line = gp.Style.Colors[ImPlotCol_Line].w == -1 ? ImGui::GetColorU32(item->Color) : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_Line]); - ImU32 col_fill = gp.Style.Colors[ImPlotCol_Fill].w == -1 ? col_line : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_Fill]); - - if (rend_fill && col_line == col_fill) - rend_line = false; - - if (gp.Style.Colors[ImPlotCol_Line].w != -1) - item->Color = gp.Style.Colors[ImPlotCol_Line]; - - PushPlotClipRect(); - - TWidth half_width = width / 2; - + const TWidth half_width = width / 2; // find data extents if (gp.FitThisFrame) { for (int i = 0; i < getter.Count; ++i) { @@ -2804,6 +2822,15 @@ void PlotBarsEx(const char* label_id, Getter getter, TWidth width) { } } + ImU32 col_line = ImGui::GetColorU32(GetLineColor(item)); + ImU32 col_fill = ImGui::GetColorU32(GetItemFillColor(item)); + const bool rend_fill = WillFillRender(); + bool rend_line = WillLineRender(); + if (rend_fill && col_line == col_fill) + rend_line = false; + + ImDrawList & DrawList = *ImGui::GetWindowDrawList(); + PushPlotClipRect(); for (int i = 0; i < getter.Count; ++i) { ImPlotPoint p = getter(i); if (p.y == 0) @@ -2860,31 +2887,14 @@ void PlotBars(const char* label_id, ImPlotPoint (*getter_func)(void* data, int i template void PlotBarsHEx(const char* label_id, Getter getter, THeight height) { - IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotBarsH() needs to be called between BeginPlot() and EndPlot()!"); ImPlotItem* item = RegisterItem(label_id); if (!item->Show) return; + TryRecolorItem(item, ImPlotCol_Fill); - ImDrawList & DrawList = *ImGui::GetWindowDrawList(); - - bool rend_line = gp.Style.Colors[ImPlotCol_Line].w != 0 && gp.Style.LineWeight > 0; - bool rend_fill = gp.Style.Colors[ImPlotCol_Fill].w != 0; - - ImU32 col_line = gp.Style.Colors[ImPlotCol_Line].w == -1 ? ImGui::GetColorU32(item->Color) : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_Line]); - ImU32 col_fill = gp.Style.Colors[ImPlotCol_Fill].w == -1 ? col_line : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_Fill]); - - if (rend_fill && col_line == col_fill) - rend_line = false; - - if (gp.Style.Colors[ImPlotCol_Line].w != -1) - item->Color = gp.Style.Colors[ImPlotCol_Line]; - - PushPlotClipRect(); - - THeight half_height = height / 2; - + const THeight half_height = height / 2; // find data extents if (gp.FitThisFrame) { for (int i = 0; i < getter.Count; ++i) { @@ -2894,6 +2904,15 @@ void PlotBarsHEx(const char* label_id, Getter getter, THeight height) { } } + ImU32 col_line = ImGui::GetColorU32(GetLineColor(item)); + ImU32 col_fill = ImGui::GetColorU32(GetItemFillColor(item)); + const bool rend_fill = WillFillRender(); + bool rend_line = WillLineRender(); + if (rend_fill && col_line == col_fill) + rend_line = false; + + PushPlotClipRect(); + ImDrawList & DrawList = *ImGui::GetWindowDrawList(); for (int i = 0; i < getter.Count; ++i) { ImPlotPoint p = getter(i); if (p.x == 0) @@ -2946,45 +2965,14 @@ void PlotBarsH(const char* label_id, ImPlotPoint (*getter_func)(void* data, int // PLOT ERROR BARS //----------------------------------------------------------------------------- -struct ImPlotPointError { - ImPlotPointError(double _x, double _y, double _neg, double _pos) { - x = _x; y = _y; neg = _neg; pos = _pos; - } - double x, y, neg, pos; -}; - -template -struct GetterError { - const T* Xs; const T* Ys; const T* Neg; const T* Pos; int Count; int Offset; int Stride; - GetterError(const T* xs, const T* ys, const T* neg, const T* pos, int count, int offset, int stride) { - Xs = xs; Ys = ys; Neg = neg; Pos = pos; Count = count; Offset = offset; Stride = stride; - } - ImPlotPointError operator()(int idx) { - return ImPlotPointError(OffsetAndStride(Xs, idx, Count, Offset, Stride), - OffsetAndStride(Ys, idx, Count, Offset, Stride), - OffsetAndStride(Neg, idx, Count, Offset, Stride), - OffsetAndStride(Pos, idx, Count, Offset, Stride)); - } -}; - template void PlotErrorBarsEx(const char* label_id, Getter getter) { IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotErrorBars() needs to be called between BeginPlot() and EndPlot()!"); - ImGuiID id = ImGui::GetID(label_id); - ImPlotItem* item = gp.CurrentPlot->Items.GetByKey(id); - if (item != NULL && item->Show == false) + ImPlotItem* item = RegisterItem(label_id); + if (!item->Show) return; - ImDrawList & DrawList = *ImGui::GetWindowDrawList(); - - PushPlotClipRect(); - - const ImU32 col = gp.Style.Colors[ImPlotCol_ErrorBar].w == -1 ? ImGui::GetColorU32(ImGuiCol_Text) : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_ErrorBar]); - const bool rend_whisker = gp.Style.ErrorBarSize > 0; - - const float half_whisker = gp.Style.ErrorBarSize * 0.5f; - // find data extents if (gp.FitThisFrame) { for (int i = 0; i < getter.Count; ++i) { @@ -2994,6 +2982,13 @@ void PlotErrorBarsEx(const char* label_id, Getter getter) { } } + const ImU32 col = ImGui::GetColorU32(GetErrorBarColor()); + const bool rend_whisker = gp.Style.ErrorBarSize > 0; + const float half_whisker = gp.Style.ErrorBarSize * 0.5f; + + ImDrawList & DrawList = *ImGui::GetWindowDrawList(); + + PushPlotClipRect(); for (int i = 0; i < getter.Count; ++i) { ImPlotPointError e = getter(i); ImVec2 p1 = PlotToPixels(e.x, e.y - e.neg); @@ -3041,20 +3036,10 @@ template void PlotErrorBarsHEx(const char* label_id, Getter getter) { IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PlotErrorBarsH() needs to be called between BeginPlot() and EndPlot()!"); - ImGuiID id = ImGui::GetID(label_id); - ImPlotItem* item = gp.CurrentPlot->Items.GetByKey(id); - if (item != NULL && item->Show == false) + ImPlotItem* item = RegisterItem(label_id); + if (!item->Show) return; - ImDrawList& DrawList = *ImGui::GetWindowDrawList(); - - PushPlotClipRect(); - - const ImU32 col = gp.Style.Colors[ImPlotCol_ErrorBar].w == -1 ? ImGui::GetColorU32(ImGuiCol_Text) : ImGui::GetColorU32(gp.Style.Colors[ImPlotCol_ErrorBar]); - const bool rend_whisker = gp.Style.ErrorBarSize > 0; - - const float half_whisker = gp.Style.ErrorBarSize * 0.5f; - // find data extents if (gp.FitThisFrame) { for (int i = 0; i < getter.Count; ++i) { @@ -3064,6 +3049,13 @@ void PlotErrorBarsHEx(const char* label_id, Getter getter) { } } + const ImU32 col = ImGui::GetColorU32(GetErrorBarColor()); + const bool rend_whisker = gp.Style.ErrorBarSize > 0; + const float half_whisker = gp.Style.ErrorBarSize * 0.5f; + + ImDrawList& DrawList = *ImGui::GetWindowDrawList(); + + PushPlotClipRect(); for (int i = 0; i < getter.Count; ++i) { ImPlotPointError e = getter(i); ImVec2 p1 = PlotToPixels(e.x - e.neg, e.y); @@ -3108,7 +3100,7 @@ void PlotErrorBarsH(const char* label_id, const double* xs, const double* ys, co // PLOT PIE CHART //----------------------------------------------------------------------------- -inline void DrawPieSlice(ImDrawList& DrawList, const ImPlotPoint& center, double radius, double a0, double a1, ImU32 col) { +inline void RenderPieSlice(ImDrawList& DrawList, const ImPlotPoint& center, double radius, double a0, double a1, ImU32 col) { static const float resolution = 50 / (2 * IM_PI); static ImVec2 buffer[50]; buffer[0] = PlotToPixels(center); @@ -3139,16 +3131,16 @@ void PlotPieChartEx(const char** label_ids, const T* values, int count, T x, T y T a1 = angle0 * 2 * IM_PI / 360.0f; for (int i = 0; i < count; ++i) { ImPlotItem* item = RegisterItem(label_ids[i]); - ImU32 col = ImGui::GetColorU32(item->Color); + ImU32 col = ImGui::GetColorU32(GetItemFillColor(item)); T percent = normalize ? values[i] / sum : values[i]; a1 = a0 + 2 * IM_PI * percent; if (item->Show) { if (percent < 0.5) { - DrawPieSlice(DrawList, center, radius, a0, a1, col); + RenderPieSlice(DrawList, center, radius, a0, a1, col); } else { - DrawPieSlice(DrawList, center, radius, a0, a0 + (a1 - a0) * 0.5f, col); - DrawPieSlice(DrawList, center, radius, a0 + (a1 - a0) * 0.5f, a1, col); + RenderPieSlice(DrawList, center, radius, a0, a0 + (a1 - a0) * 0.5f, col); + RenderPieSlice(DrawList, center, radius, a0 + (a1 - a0) * 0.5f, a1, col); } } a0 = a1; @@ -3166,8 +3158,8 @@ void PlotPieChartEx(const char** label_ids, const T* values, int count, T x, T y ImVec2 size = ImGui::CalcTextSize(buffer); T angle = a0 + (a1 - a0) * 0.5f; ImVec2 pos = PlotToPixels(center.x + 0.5f * radius * cos(angle), center.y + 0.5f * radius * sin(angle)); - DrawList.AddText(pos - size * 0.5f + ImVec2(1,1), IM_COL32(0,0,0,255), buffer); - DrawList.AddText(pos - size * 0.5f, IM_COL32(255,255,255,255), buffer); + ImU32 col = CalcTextColor(GetItemFillColor(item)); + DrawList.AddText(pos - size * 0.5f, col, buffer); } a0 = a1; } @@ -3208,13 +3200,13 @@ void RenderHeatmap(Transformer transformer, ImDrawList& DrawList, const T* value ImVec2 b = transformer(p.x + half_size.x, p.y + half_size.y); float t = (float)Remap(values[i], scale_min, scale_max, T(0), T(1)); ImVec4 color = LerpColormap(t); + color.w *= gp.Style.FillAlpha; ImU32 col = ImGui::GetColorU32(color); DrawList.AddRectFilled(a, b, col); i++; } } if (fmt != NULL) { - // this has to go in its own loop due to PrimReserve above i = 0; for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { @@ -3287,23 +3279,14 @@ inline void PlotDigitalEx(const char* label_id, Getter getter, int count, int of ImPlotItem* item = RegisterItem(label_id); if (!item->Show) return; - - ImDrawList & DrawList = *ImGui::GetWindowDrawList(); - - const bool rend_line = gp.Style.Colors[ImPlotCol_Line].w != 0 && gp.Style.LineWeight > 0; - - if (gp.Style.Colors[ImPlotCol_Line].w != -1) - item->Color = gp.Style.Colors[ImPlotCol_Line]; - - ImGui::PushClipRect(gp.BB_Plot.Min, gp.BB_Plot.Max, true); - - const float line_weight = item->Highlight ? gp.Style.LineWeight * 2 : gp.Style.LineWeight; - - const int ax = gp.CurrentPlot->CurrentYAxis; + TryRecolorItem(item, ImPlotCol_Line); // render digital signals as "pixel bases" rectangles - if (count > 1 && rend_line) { - // + PushPlotClipRect(); + if (count > 1 && WillLineRender()) { + ImDrawList & DrawList = *ImGui::GetWindowDrawList(); + const float line_weight = item->Highlight ? gp.Style.LineWeight * 2 : gp.Style.LineWeight; + const int y_axis = gp.CurrentPlot->CurrentYAxis; const int segments = count - 1; int i1 = offset; int pixYMax = 0; @@ -3321,9 +3304,8 @@ inline void PlotDigitalEx(const char* label_id, Getter getter, int count, int of ImVec2 pMin = PlotToPixels(itemData1); ImVec2 pMax = PlotToPixels(itemData2); int pixY_Offset = 20; //20 pixel from bottom due to mouse cursor label - - pMin.y = (gp.PixelRange[ax].Min.y) + ((-gp.DigitalPlotOffset) - pixY_Offset); - pMax.y = (gp.PixelRange[ax].Min.y) + ((-gp.DigitalPlotOffset) - pixY_0 - pixY_1 - pixY_Offset); + pMin.y = (gp.PixelRange[y_axis].Min.y) + ((-gp.DigitalPlotOffset) - pixY_Offset); + pMax.y = (gp.PixelRange[y_axis].Min.y) + ((-gp.DigitalPlotOffset) - pixY_0 - pixY_1 - pixY_Offset); //plot only one rectangle for same digital state while (((s+2) < segments) && (itemData1.y == itemData2.y)) { const int i3 = (i1 + 1) % count; @@ -3333,10 +3315,10 @@ inline void PlotDigitalEx(const char* label_id, Getter getter, int count, int of s++; } //do not extend plot outside plot range - if (pMin.x < gp.PixelRange[ax].Min.x) pMin.x = gp.PixelRange[ax].Min.x; - if (pMax.x < gp.PixelRange[ax].Min.x) pMax.x = gp.PixelRange[ax].Min.x; - if (pMin.x > gp.PixelRange[ax].Max.x) pMin.x = gp.PixelRange[ax].Max.x; - if (pMax.x > gp.PixelRange[ax].Max.x) pMax.x = gp.PixelRange[ax].Max.x; + if (pMin.x < gp.PixelRange[y_axis].Min.x) pMin.x = gp.PixelRange[y_axis].Min.x; + if (pMax.x < gp.PixelRange[y_axis].Min.x) pMax.x = gp.PixelRange[y_axis].Min.x; + if (pMin.x > gp.PixelRange[y_axis].Max.x) pMin.x = gp.PixelRange[y_axis].Max.x; + if (pMax.x > gp.PixelRange[y_axis].Max.x) pMax.x = gp.PixelRange[y_axis].Max.x; //plot a rectangle that extends up to x2 with y1 height if ((pMax.x > pMin.x) && (gp.BB_Plot.Contains(pMin) || gp.BB_Plot.Contains(pMax))) { ImVec4 colAlpha = item->Color; @@ -3347,8 +3329,7 @@ inline void PlotDigitalEx(const char* label_id, Getter getter, int count, int of gp.DigitalPlotItemCnt++; gp.DigitalPlotOffset += pixYMax; } - - ImGui::PopClipRect(); + PopPlotClipRect(); } //----------------------------------------------------------------------------- @@ -3611,10 +3592,8 @@ void SetColormap(ImPlotColormap colormap, int samples) { ImVec4(1.0000f, 0.3333f, 0.f, 1.0f), ImVec4(1.0000f, 0.f, 0.f, 1.0f) }; - // TODO: Calculate offsets at compile time gp.Colormap = &cdata[coffs.Offsets[colormap]]; gp.ColormapSize = csizes[colormap]; - if (samples > 1) { static ImVector resampled; resampled.resize(samples); diff --git a/implot.h b/implot.h index a53fdf8..7bc1eb3 100644 --- a/implot.h +++ b/implot.h @@ -91,7 +91,7 @@ enum ImPlotStyleVar_ { ImPlotStyleVar_Marker, // int, marker specification ImPlotStyleVar_MarkerSize, // float, marker size in pixels (roughly the marker's "radius") ImPlotStyleVar_MarkerWeight, // float, outline weight of markers in pixels - ImPlotStyleVar_FillAlpha, // float, alpha modifier applied to plot fills + ImPlotStyleVar_FillAlpha, // float, alpha modifier applied to all plot item fills ImPlotStyleVar_ErrorBarSize, // float, error bar whisker width in pixels ImPlotStyleVar_ErrorBarWeight, // float, error bar whisker weight in pixels ImPlotStyleVar_DigitalBitHeight, // float, digital channels bit height (at 1) in pixels @@ -289,6 +289,9 @@ ImPlotLimits GetPlotQuery(int y_axis = -1); // Provides access to plot style structure for permanant modifications to colors, sizes, etc. ImPlotStyle& GetStyle(); +// Special color used to indicate that a style color should be deduced automatically from defaults or colormaps. +#define IMPLOT_COL_AUTO ImVec4(0,0,0,-1) + // Temporarily modify a plot color. Don't forget to call PopStyleColor! void PushStyleColor(ImPlotCol idx, ImU32 col); // Temporarily modify a plot color. Don't forget to call PopStyleColor! @@ -307,9 +310,9 @@ void PopStyleVar(int count = 1); void SetColormap(ImPlotColormap colormap, int samples = 0); // Sets a custom colormap. void SetColormap(const ImVec4* colors, int num_colors); -// Returns the size of the current colormap +// Returns the size of the current colormap. int GetColormapSize(); -// Returns a color from the Color map given an index > 0 (modulo will be performed) +// Returns a color from the Color map given an index >= 0 (modulo will be performed) ImVec4 GetColormapColor(int index); // Linearly interpolates a color from the current colormap given t between 0 and 1. ImVec4 LerpColormap(float t); diff --git a/implot_demo.cpp b/implot_demo.cpp index 8493eeb..03132e9 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -35,7 +35,7 @@ /// NB: You don't ever need to typdef of define values for ImPlot. This /// is only being done here for the sake of demoing both precision types. -// #define IMPLOT_DEMO_USE_DOUBLE +#define IMPLOT_DEMO_USE_DOUBLE #ifdef IMPLOT_DEMO_USE_DOUBLE typedef double t_float; typedef ImPlotPoint t_float2; @@ -171,7 +171,6 @@ void ShowDemoWindow(bool* p_open) { #else ImGui::BulletText("The demo data precision is: float"); #endif - } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Line Plots")) { @@ -198,7 +197,7 @@ void ShowDemoWindow(bool* p_open) { } } //------------------------------------------------------------------------- - if (ImGui::CollapsingHeader("Filled Line Plots")) { + if (ImGui::CollapsingHeader("Filled Line Plots")) { static t_float xs1[101], ys1[101], ys2[101], ys3[101]; srand(0); for (int i = 0; i < 101; ++i) { @@ -238,9 +237,9 @@ void ShowDemoWindow(bool* p_open) { xs[i] = i * 0.001f; ys[i] = 0.25f + 0.25f * Sin(25 * xs[i]) * Sin(5 * xs[i]) + RandomRange(-0.01f, 0.01f); ys1[i] = ys[i] + RandomRange(0.1f, 0.12f); - ys2[i] = ys[i] - RandomRange(0.1f, 0.12f); - ys3[i] = 0.75f + 0.2f * Sin(25 * xs[i]); - ys4[i] = 0.75f + 0.1f * Cos(25 * xs[i]); + ys2[i] = ys[i] - RandomRange(0.1f, 0.12f); + ys3[i] = 0.75f + 0.2f * Sin(25 * xs[i]); + ys4[i] = 0.75f + 0.1f * Cos(25 * xs[i]); } static float alpha = 0.25f; ImGui::DragFloat("Alpha",&alpha,0.01f,0,1); @@ -272,11 +271,9 @@ void ShowDemoWindow(bool* p_open) { ImPlot::PlotScatter("Data 1", xs1, ys1, 100); ImPlot::PushStyleVar(ImPlotStyleVar_MarkerSize, 6); ImPlot::PushStyleVar(ImPlotStyleVar_Marker, ImPlotMarker_Square); - ImPlot::PushStyleColor(ImPlotCol_MarkerFill, ImVec4(1,0,0,0.25f)); - ImPlot::PushStyleColor(ImPlotCol_MarkerOutline, ImVec4(0,0,0,0)); + ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f); ImPlot::PlotScatter("Data 2", xs2, ys2, 50); - ImPlot::PopStyleColor(2); - ImPlot::PopStyleVar(2); + ImPlot::PopStyleVar(3); ImPlot::EndPlot(); } } @@ -308,7 +305,6 @@ void ShowDemoWindow(bool* p_open) { ImPlot::PlotBars("Final Exam", final, 10, 0.2f, 0); ImPlot::PlotBars("Course Grade", grade, 10, 0.2f, 0.2f); } - ImPlot::SetColormap(ImPlotColormap_Default); ImPlot::EndPlot(); } } @@ -331,7 +327,7 @@ void ShowDemoWindow(bool* p_open) { ImPlot::PushStyleVar(ImPlotStyleVar_ErrorBarSize, size); ImPlot::PushStyleVar(ImPlotStyleVar_ErrorBarWeight, weight); ImPlot::PlotBars("Bar", xs, bar, 5, 0.5f); - // error bars should have the same label ID as the associated plot + // error bars can be grouped with the associated item by using the same label ID ImPlot::PlotErrorBars("Bar", xs, bar, err1, 5); ImPlot::PushStyleVar(ImPlotStyleVar_Marker, ImPlotMarker_Circle); ImPlot::PushStyleVar(ImPlotStyleVar_MarkerSize, 3);