diff --git a/implot.cpp b/implot.cpp index de03faa7..47c7f43f 100644 --- a/implot.cpp +++ b/implot.cpp @@ -429,19 +429,28 @@ void BustPlotCache() { } void FitPoint(const ImPlotPoint& p) { + FitPointX(p.x); + FitPointY(p.y); +} + +void FitPointX(double x) { ImPlotContext& gp = *GImPlot; - const ImPlotYAxis y_axis = gp.CurrentPlot->CurrentYAxis; ImPlotRange& ex_x = gp.ExtentsX; - ImPlotRange& ex_y = gp.ExtentsY[y_axis]; const bool log_x = ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_LogScale); - const bool log_y = ImHasFlag(gp.CurrentPlot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale); - if (!ImNanOrInf(p.x) && !(log_x && p.x <= 0)) { - ex_x.Min = p.x < ex_x.Min ? p.x : ex_x.Min; - ex_x.Max = p.x > ex_x.Max ? p.x : ex_x.Max; + if (!ImNanOrInf(x) && !(log_x && x <= 0)) { + ex_x.Min = x < ex_x.Min ? x : ex_x.Min; + ex_x.Max = x > ex_x.Max ? x : ex_x.Max; } - if (!ImNanOrInf(p.y) && !(log_y && p.y <= 0)) { - ex_y.Min = p.y < ex_y.Min ? p.y : ex_y.Min; - ex_y.Max = p.y > ex_y.Max ? p.y : ex_y.Max; +} + +void FitPointY(double y) { + ImPlotContext& gp = *GImPlot; + const ImPlotYAxis y_axis = gp.CurrentPlot->CurrentYAxis; + ImPlotRange& ex_y = gp.ExtentsY[y_axis]; + const bool log_y = ImHasFlag(gp.CurrentPlot->YAxis[y_axis].Flags, ImPlotAxisFlags_LogScale); + if (!ImNanOrInf(y) && !(log_y && y <= 0)) { + ex_y.Min = y < ex_y.Min ? y : ex_y.Min; + ex_y.Max = y > ex_y.Max ? y : ex_y.Max; } } diff --git a/implot.h b/implot.h index 9f0230cb..a3f66447 100644 --- a/implot.h +++ b/implot.h @@ -434,6 +434,10 @@ template IMPLOT_API void PlotErrorBarsH(const char* label_id, const template IMPLOT_API void PlotStems(const char* label_id, const T* values, int count, double y_ref=0, double xscale=1, double x0=0, int offset=0, int stride=sizeof(T)); template IMPLOT_API void PlotStems(const char* label_id, const T* xs, const T* ys, int count, double y_ref=0, int offset=0, int stride=sizeof(T)); +/// Plots infinite vertical or horizontal lines (e.g. for references or asymptotes). +template IMPLOT_API void PlotVLines(const char* label_id, const T* xs, int count, int offset=0, int stride=sizeof(T)); +template IMPLOT_API void PlotHLines(const char* label_id, const T* ys, int count, int offset=0, int stride=sizeof(T)); + // Plots a pie chart. If the sum of values > 1 or normalize is true, each value will be normalized. Center and radius are in plot units. #label_fmt can be set to NULL for no labels. template IMPLOT_API void PlotPieChart(const char* const label_ids[], const T* values, int count, double x, double y, double radius, bool normalize=false, const char* label_fmt="%.1f", double angle0=90); diff --git a/implot_demo.cpp b/implot_demo.cpp index 55e20aa6..c27dbf34 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -269,13 +269,13 @@ void ShowDemoWindow(bool* p_open) { ImGui::Checkbox("Fills",&show_fills); if (show_fills) { ImGui::SameLine(); - if (ImGui::RadioButton("To -INF",shade_mode == 0)) + if (ImGui::RadioButton("To -INF",shade_mode == 0)) shade_mode = 0; ImGui::SameLine(); - if (ImGui::RadioButton("To +INF",shade_mode == 1)) + if (ImGui::RadioButton("To +INF",shade_mode == 1)) shade_mode = 1; ImGui::SameLine(); - if (ImGui::RadioButton("To Ref",shade_mode == 2)) + if (ImGui::RadioButton("To Ref",shade_mode == 2)) shade_mode = 2; if (shade_mode == 2) { ImGui::SameLine(); @@ -436,6 +436,7 @@ void ShowDemoWindow(bool* p_open) { ImPlot::EndPlot(); } } + //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Stem Plots##")) { static double xs[51], ys1[51], ys2[51]; for (int i = 0; i < 51; ++i) { @@ -456,6 +457,15 @@ void ShowDemoWindow(bool* p_open) { } } //------------------------------------------------------------------------- + if (ImGui::CollapsingHeader("Infinite Lines")) { + static double vals[] = {0.25, 0.5, 0.75}; + if (ImPlot::BeginPlot("##Infinite")) { + ImPlot::PlotVLines("VLines",vals,3); + ImPlot::PlotHLines("HLines",vals,3); + ImPlot::EndPlot(); + } + } + //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Pie Charts")) { static const char* labels1[] = {"Frogs","Hogs","Dogs","Logs"}; static float data1[] = {0.15f, 0.30f, 0.2f, 0.05f}; diff --git a/implot_internal.h b/implot_internal.h index cb368a33..7df277cd 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -762,8 +762,12 @@ inline ImPlotScale GetCurrentScale() { return GImPlot->Scales[GetCurrentYAxis()] // Returns true if the user has requested data to be fit. inline bool FitThisFrame() { return GImPlot->FitThisFrame; } -// Extends the current plots axes so that it encompasses point p +// Extends the current plot's axes so that it encompasses point p IMPLOT_API void FitPoint(const ImPlotPoint& p); +// Extends the current plot's axes so that it encompasses a vertical line at x +IMPLOT_API void FitPointX(double x); +// Extends the current plot's axes so that it encompasses a horizontal line at y +IMPLOT_API void FitPointY(double y); // Returns true if two ranges overlap inline bool RangesOverlap(const ImPlotRange& r1, const ImPlotRange& r2) diff --git a/implot_items.cpp b/implot_items.cpp index 2e9840f6..a18296e2 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -302,6 +302,26 @@ struct GetterXsYRef { const int Stride; }; +// Interprets an array of Y points as ImPlotPoints where the X value is a constant reference value +template +struct GetterXRefYs { + GetterXRefYs(double x_ref, const T* ys, int count, int offset, int stride) : + XRef(x_ref), + Ys(ys), + Count(count), + Offset(count ? ImPosMod(offset, count) : 0), + Stride(stride) + { } + inline ImPlotPoint operator()(int idx) const { + return ImPlotPoint(XRef, (double)OffsetAndStride(Ys, idx, Count, Offset, Stride)); + } + const double XRef; + const T* const Ys; + const int Count; + const int Offset; + const int Stride; +}; + /// Interprets a user's function pointer as ImPlotPoints struct GetterFuncPtr { GetterFuncPtr(ImPlotPoint (*getter)(void* data, int idx), void* data, int count, int offset) : @@ -1123,11 +1143,11 @@ template inline void PlotShadedEx(const char* label_id, const Getter1& getter1, const Getter2& getter2, bool fit2) { if (BeginItem(label_id, ImPlotCol_Fill)) { if (FitThisFrame()) { - for (int i = 0; i < getter1.Count; ++i) - FitPoint(getter1(i)); + for (int i = 0; i < getter1.Count; ++i) + FitPoint(getter1(i)); if (fit2) { - for (int i = 0; i < getter2.Count; ++i) - FitPoint(getter2(i)); + for (int i = 0; i < getter2.Count; ++i) + FitPoint(getter2(i)); } } const ImPlotNextItemData& s = GetItemData(); @@ -1149,7 +1169,7 @@ template void PlotShaded(const char* label_id, const T* values, int count, double y_ref, double xscale, double x0, int offset, int stride) { bool fit2 = true; if (y_ref == -HUGE_VAL) { - fit2 = false; + fit2 = false; y_ref = GetPlotLimits().Y.Min; } if (y_ref == HUGE_VAL) { @@ -1176,7 +1196,7 @@ template void PlotShaded(const char* label_id, const T* xs, const T* ys, int count, double y_ref, int offset, int stride) { bool fit2 = true; if (y_ref == -HUGE_VAL) { - fit2 = false; + fit2 = false; y_ref = GetPlotLimits().Y.Min; } if (y_ref == HUGE_VAL) { @@ -1592,6 +1612,85 @@ template IMPLOT_API void PlotStems(const char* label_id, const ImU64* xs, template IMPLOT_API void PlotStems(const char* label_id, const float* xs, const float* ys, int count, double y_ref, int offset, int stride); template IMPLOT_API void PlotStems(const char* label_id, const double* xs, const double* ys, int count, double y_ref, int offset, int stride); +//----------------------------------------------------------------------------- +// INFINITE LINES +//----------------------------------------------------------------------------- + +template +void PlotVLines(const char* label_id, const T* xs, int count, int offset, int stride) { + if (BeginItem(label_id, ImPlotCol_Line)) { + const ImPlotLimits lims = GetPlotLimits(); + GetterXsYRef get_min(xs,lims.Y.Min,count,offset,stride); + GetterXsYRef get_max(xs,lims.Y.Max,count,offset,stride); + if (FitThisFrame()) { + for (int i = 0; i < get_min.Count; ++i) + FitPointX(get_min(i).x); + } + const ImPlotNextItemData& s = GetItemData(); + ImDrawList& DrawList = *GetPlotDrawList(); + // render stems + if (s.RenderLine) { + const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]); + switch (GetCurrentScale()) { + case ImPlotScale_LinLin: RenderLineSegments(get_min, get_max, TransformerLinLin(), DrawList, s.LineWeight, col_line); break; + case ImPlotScale_LogLin: RenderLineSegments(get_min, get_max, TransformerLogLin(), DrawList, s.LineWeight, col_line); break; + case ImPlotScale_LinLog: RenderLineSegments(get_min, get_max, TransformerLinLog(), DrawList, s.LineWeight, col_line); break; + case ImPlotScale_LogLog: RenderLineSegments(get_min, get_max, TransformerLogLog(), DrawList, s.LineWeight, col_line); break; + } + } + EndItem(); + } +} + +template IMPLOT_API void PlotVLines(const char* label_id, const ImS8* xs, int count, int offset, int stride); +template IMPLOT_API void PlotVLines(const char* label_id, const ImU8* xs, int count, int offset, int stride); +template IMPLOT_API void PlotVLines(const char* label_id, const ImS16* xs, int count, int offset, int stride); +template IMPLOT_API void PlotVLines(const char* label_id, const ImU16* xs, int count, int offset, int stride); +template IMPLOT_API void PlotVLines(const char* label_id, const ImS32* xs, int count, int offset, int stride); +template IMPLOT_API void PlotVLines(const char* label_id, const ImU32* xs, int count, int offset, int stride); +template IMPLOT_API void PlotVLines(const char* label_id, const ImS64* xs, int count, int offset, int stride); +template IMPLOT_API void PlotVLines(const char* label_id, const ImU64* xs, int count, int offset, int stride); +template IMPLOT_API void PlotVLines(const char* label_id, const float* xs, int count, int offset, int stride); +template IMPLOT_API void PlotVLines(const char* label_id, const double* xs, int count, int offset, int stride); + + +template +void PlotHLines(const char* label_id, const T* ys, int count, int offset, int stride) { + if (BeginItem(label_id, ImPlotCol_Line)) { + const ImPlotLimits lims = GetPlotLimits(); + GetterXRefYs get_min(lims.X.Min,ys,count,offset,stride); + GetterXRefYs get_max(lims.X.Max,ys,count,offset,stride); + if (FitThisFrame()) { + for (int i = 0; i < get_min.Count; ++i) + FitPointY(get_min(i).y); + } + const ImPlotNextItemData& s = GetItemData(); + ImDrawList& DrawList = *GetPlotDrawList(); + // render stems + if (s.RenderLine) { + const ImU32 col_line = ImGui::GetColorU32(s.Colors[ImPlotCol_Line]); + switch (GetCurrentScale()) { + case ImPlotScale_LinLin: RenderLineSegments(get_min, get_max, TransformerLinLin(), DrawList, s.LineWeight, col_line); break; + case ImPlotScale_LogLin: RenderLineSegments(get_min, get_max, TransformerLogLin(), DrawList, s.LineWeight, col_line); break; + case ImPlotScale_LinLog: RenderLineSegments(get_min, get_max, TransformerLinLog(), DrawList, s.LineWeight, col_line); break; + case ImPlotScale_LogLog: RenderLineSegments(get_min, get_max, TransformerLogLog(), DrawList, s.LineWeight, col_line); break; + } + } + EndItem(); + } +} + +template IMPLOT_API void PlotHLines(const char* label_id, const ImS8* ys, int count, int offset, int stride); +template IMPLOT_API void PlotHLines(const char* label_id, const ImU8* ys, int count, int offset, int stride); +template IMPLOT_API void PlotHLines(const char* label_id, const ImS16* ys, int count, int offset, int stride); +template IMPLOT_API void PlotHLines(const char* label_id, const ImU16* ys, int count, int offset, int stride); +template IMPLOT_API void PlotHLines(const char* label_id, const ImS32* ys, int count, int offset, int stride); +template IMPLOT_API void PlotHLines(const char* label_id, const ImU32* ys, int count, int offset, int stride); +template IMPLOT_API void PlotHLines(const char* label_id, const ImS64* ys, int count, int offset, int stride); +template IMPLOT_API void PlotHLines(const char* label_id, const ImU64* ys, int count, int offset, int stride); +template IMPLOT_API void PlotHLines(const char* label_id, const float* ys, int count, int offset, int stride); +template IMPLOT_API void PlotHLines(const char* label_id, const double* ys, int count, int offset, int stride); + //----------------------------------------------------------------------------- // PLOT PIE CHART //-----------------------------------------------------------------------------