diff --git a/implot.cpp b/implot.cpp index a6202fd..1383654 100644 --- a/implot.cpp +++ b/implot.cpp @@ -318,6 +318,7 @@ struct ImPlotAxisColor { struct ImPlotItem { ImPlotItem() { Show = true; + SeenThisFrame = false; Highlight = false; Color = NextColor(); NameOffset = -1; @@ -325,6 +326,7 @@ struct ImPlotItem { } ~ImPlotItem() { ID = 0; } bool Show; + bool SeenThisFrame; bool Highlight; ImVec4 Color; int NameOffset; @@ -637,6 +639,9 @@ struct TransformerLogLog { ImPlotItem* RegisterItem(const char* label_id) { ImGuiID id = ImGui::GetID(label_id); ImPlotItem* item = gp.CurrentPlot->Items.GetOrAddByKey(id); + if (item->SeenThisFrame) + return item; + item->SeenThisFrame = true; int idx = gp.CurrentPlot->Items.GetIndex(item); item->ID = id; gp.LegendIndices.push_back(idx); @@ -1807,6 +1812,11 @@ void EndPlot() { } // CLEANUP ---------------------------------------------------------------- + // reset the plot items for the next frame + for (int i = 0; i < gp.CurrentPlot->Items.GetSize(); ++i) { + gp.CurrentPlot->Items.GetByIndex(i)->SeenThisFrame = false; + } + // Pop ImGui::PushID at the end of BeginPlot ImGui::PopID(); // Reset context for next plot @@ -1958,6 +1968,7 @@ static const ImPlotStyleVarInfo GPlotStyleVarInfo[] = { ImGuiDataType_S32, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, Marker) }, // ImPlotStyleVar_Marker { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerSize) }, // ImPlotStyleVar_MarkerSize { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, MarkerWeight) }, // ImPlotStyleVar_MarkerWeight + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, FillAlpha) }, // ImPlotStyleVar_FillAlpha { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarSize) }, // ImPlotStyleVar_ErrorBarSize { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, ErrorBarWeight) }, // ImPlotStyleVar_ErrorBarWeight { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImPlotStyle, DigitalBitHeight) }, // ImPlotStyleVar_DigitalBitHeight @@ -2231,14 +2242,17 @@ struct LineRenderer { template struct LineFillRenderer { - LineFillRenderer(Getter _getter, Transformer _transformer, ImU32 col, float zero) { - getter = _getter; - transformer = _transformer; - Col = col; - Zero = zero; + LineFillRenderer(Getter _getter, Transformer _transformer, ImU32 col, float zero) : + getter(_getter), + transformer(_transformer), + Col(col), + Zero(zero) + { + Prims = getter.Count - 1; p1 = transformer(getter(0)); } - inline void operator()(ImDrawList& DrawList, ImVec2 uv) { + inline bool operator()(ImDrawList& DrawList, ImVec2 uv, int prim) { + ImVec2 p2 = transformer(getter(prim + 1)); const int crosses_zero = (p1.y > Zero && p2.y < Zero) || (p1.y < Zero && p2.y > Zero); // could do y*y < 0 earlier on const float xmid = p1.x + (p2.x - p1.x) / (p2.y-p1.y) * (Zero - p1.y); DrawList._VtxWritePtr[0].pos = p1; @@ -2265,7 +2279,11 @@ struct LineFillRenderer { DrawList._IdxWritePtr[5] = (ImDrawIdx)(DrawList._VtxCurrentIdx + 4); DrawList._IdxWritePtr += 6; DrawList._VtxCurrentIdx += 5; + p1 = p2; + return true; } + Getter getter; + Transformer transformer; int Prims; ImU32 Col; float Zero; @@ -2274,6 +2292,59 @@ struct LineFillRenderer { static const int VtxConsumed = 5; }; +template +struct ShadedRenderer { + + ShadedRenderer(Getter1 _getter1, Getter2 _getter2, Transformer _transformer, ImU32 col) : + getter1(_getter1), + getter2(_getter2), + transformer(_transformer), + Col(col) + { + Prims = ImMin(getter1.Count, getter2.Count) - 1; + p11 = transformer(getter1(0)); + p12 = transformer(getter2(0)); + } + + inline bool operator()(ImDrawList& DrawList, ImVec2 uv, int prim) { + ImVec2 p21 = transformer(getter1(prim+1)); + ImVec2 p22 = transformer(getter2(prim+1)); + DrawList._VtxWritePtr[0].pos = p11; + DrawList._VtxWritePtr[0].uv = uv; + DrawList._VtxWritePtr[0].col = Col; + DrawList._VtxWritePtr[1].pos = p12; + DrawList._VtxWritePtr[1].uv = uv; + DrawList._VtxWritePtr[1].col = Col; + DrawList._VtxWritePtr[2].pos = p21; + DrawList._VtxWritePtr[2].uv = uv; + DrawList._VtxWritePtr[2].col = Col; + DrawList._VtxWritePtr[3].pos = p22; + 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 + 2); + 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; + p11 = p21; + p12 = p22; + return true; + } + + Getter1 getter1; + Getter2 getter2; + Transformer transformer; + int Prims; + ImU32 Col; + ImVec2 p11, p12; + static const int IdxConsumed = 6; + static const int VtxConsumed = 4; +}; + // struct RectRenderer { // RectRenderer(ImU32 col) { Col = col; } // inline void operator()(ImDrawList& DrawList, const ImVec2& p1, const ImVec2& p2, ImVec2 uv) { @@ -2310,7 +2381,7 @@ struct LineFillRenderer { template inline void RenderPrimitives(Renderer renderer, ImDrawList& DrawList, bool cull) { - int prims_remain = renderer.Prims; + int prims = renderer.Prims; int prims_culled = 0; int idx = 0; const ImVec2 uv = DrawList._Data->TexUvWhitePixel; @@ -2365,6 +2436,7 @@ template inline void RenderLineFill(Getter getter, Transformer transformer, ImDrawList& DrawList, ImU32 col_fill) { // TODO: Culling float zero = transformer(0,0).y; + RenderPrimitives(LineFillRenderer(getter,transformer,col_fill,zero), DrawList, false); // RenderPrimitives(getter, transformer, LineFillRenderer(col_fill, zero), DrawList, false); } @@ -2583,6 +2655,47 @@ void PlotLine(const char* label_id, ImPlotPoint (*getter_func)(void* data, int i return PlotEx(label_id, getter); } +//----------------------------------------------------------------------------- +// PLOT SHADED +//----------------------------------------------------------------------------- + +void PlotShaded(const char* label_id, const double* xs, const double* ys1, const double* ys2, int count, int offset, int stride) { + 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; + + GetterXsYs getter1(xs, ys1, count, offset, stride); + GetterXsYs getter2(xs, ys2, count, offset, stride); + + 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 (gp.FitThisFrame) { + for (int i = 0; i < count; ++i) { + ImPlotPoint p1 = getter1(i); + ImPlotPoint p2 = getter2(i); + FitPoint(p1); + FitPoint(p2); + } + } + + PushPlotClipRect(); + RenderPrimitives(ShadedRenderer,GetterXsYs,TransformerLinLin>(getter1,getter2,TransformerLinLin(y_axis), ImGui::GetColorU32(col_fill)), DrawList, false); + PopPlotClipRect(); +} + + //----------------------------------------------------------------------------- // PLOT SCATTER //----------------------------------------------------------------------------- diff --git a/implot.h b/implot.h index f58b318..e3635f8 100644 --- a/implot.h +++ b/implot.h @@ -93,6 +93,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_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 @@ -164,6 +165,7 @@ struct ImPlotStyle { ImPlotMarker Marker; // = ImPlotMarker_None, marker specification float MarkerSize; // = 4, marker size in pixels (roughly the marker's "radius") float MarkerWeight; // = 1, outline weight of markers in pixels + float FillAlpha; // = 1, alpha modifier applied to plot fills float ErrorBarSize; // = 5, error bar whisker width in pixels float ErrorBarWeight; // = 1.5, error bar whisker weight in pixels float DigitalBitHeight; // = 8, digital channels bit height (at y = 1.0f) in pixels @@ -209,6 +211,10 @@ void PlotLine(const char* label_id, const ImVec2* data, int count, int offset = void PlotLine(const char* label_id, const ImPlotPoint* data, int count, int offset = 0); void PlotLine(const char* label_id, ImPlotPoint (*getter)(void* data, int idx), void* data, int count, int offset = 0); +// Plots a shaded (filled) region between two lines. Intersecting lines are not currently supported. +void PlotShaded(const char* label_id, const double* xs, const double* ys1, const double* ys2, int count, int offset = 0, int stride = sizeof(double)); + + // Plots a standard 2D scatter plot. void PlotScatter(const char* label_id, const float* values, int count, int offset = 0, int stride = sizeof(float)); void PlotScatter(const char* label_id, const double* values, int count, int offset = 0, int stride = sizeof(double)); diff --git a/implot_demo.cpp b/implot_demo.cpp index cef1e0d..717562b 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -224,6 +224,25 @@ void ShowDemoWindow(bool* p_open) { ImPlot::EndPlot(); } } + if (ImGui::CollapsingHeader("Shaded Plots")) { + static double xs[1001], ys[1001], ys1[1001], ys2[1001]; + srand(0); + for (int i = 0; i < 1001; ++i) { + xs[i] = i * 0.001f; + ys[i] = 0.5f + 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); + } + static float alpha = 0.25f; + ImGui::DragFloat("Alpha",&alpha,0.01f,0,1); + if (ImPlot::BeginPlot("Shaded Plots")) { + ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, alpha); + ImPlot::PlotShaded("Uncertain Data",xs,ys1,ys2,1001); + ImPlot::PlotLine("Uncertain Data", xs, ys, 1001); + ImPlot::PopStyleVar(); + ImPlot::EndPlot(); + } + } //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Scatter Plots")) { srand(0);