From 5b59b47bae490be7591e54b3ca167aecfbddaa5f Mon Sep 17 00:00:00 2001 From: Yan Date: Wed, 6 Jan 2021 10:34:03 -0500 Subject: [PATCH] Allow second and third axes to have labels (#163) * Allow second and third axes to have labels * Handle both axes in one loop * fix compiler warnings, add ImPlotAxisFlags_NoLabel * fix bug when y2 label hidden Co-authored-by: epezent --- implot.cpp | 52 ++++++++++++++++++++++++++++++++++++++----------- implot.h | 23 ++++++++++++---------- implot_demo.cpp | 6 ++++-- 3 files changed, 58 insertions(+), 23 deletions(-) diff --git a/implot.cpp b/implot.cpp index 3517169..de03faa 100644 --- a/implot.cpp +++ b/implot.cpp @@ -1219,8 +1219,9 @@ void UpdateAxisColors(int axis_flag, ImPlotAxis* axis) { // BeginPlot() //----------------------------------------------------------------------------- -bool BeginPlot(const char* title, const char* x_label, const char* y_label, const ImVec2& size, - ImPlotFlags flags, ImPlotAxisFlags x_flags, ImPlotAxisFlags y1_flags, ImPlotAxisFlags y2_flags, ImPlotAxisFlags y3_flags) +bool BeginPlot(const char* title, const char* x_label, const char* y1_label, const ImVec2& size, + ImPlotFlags flags, ImPlotAxisFlags x_flags, ImPlotAxisFlags y1_flags, ImPlotAxisFlags y2_flags, ImPlotAxisFlags y3_flags, + const char* y2_label, const char* y3_label) { IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; @@ -1420,9 +1421,11 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons title_size = ImGui::CalcTextSize(title, NULL, true); } + const bool show_x_label = x_label && !ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoLabel); + const float pad_top = title_size.x > 0.0f ? txt_height + gp.Style.LabelPadding.y : 0; const float pad_bot = (plot.XAxis.IsLabeled() ? txt_height + gp.Style.LabelPadding.y + (plot.XAxis.IsTime() ? txt_height + gp.Style.LabelPadding.y : 0) : 0) - + (x_label ? txt_height + gp.Style.LabelPadding.y : 0); + + (show_x_label ? txt_height + gp.Style.LabelPadding.y : 0); const float plot_height = plot.CanvasRect.GetHeight() - pad_top - pad_bot; @@ -1437,11 +1440,17 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } // (3) calc left/right pad - const float pad_left = (y_label ? txt_height + gp.Style.LabelPadding.x : 0) + const bool show_y1_label = y1_label && !ImHasFlag(plot.YAxis[0].Flags, ImPlotAxisFlags_NoLabel); + const bool show_y2_label = y2_label && !ImHasFlag(plot.YAxis[1].Flags, ImPlotAxisFlags_NoLabel); + const bool show_y3_label = y3_label && !ImHasFlag(plot.YAxis[2].Flags, ImPlotAxisFlags_NoLabel); + + const float pad_left = (show_y1_label ? txt_height + gp.Style.LabelPadding.x : 0) + (plot.YAxis[0].IsLabeled() ? gp.YTicks[0].MaxWidth + gp.Style.LabelPadding.x : 0); const float pad_right = ((plot.YAxis[1].Present && plot.YAxis[1].IsLabeled()) ? gp.YTicks[1].MaxWidth + gp.Style.LabelPadding.x : 0) + + ((plot.YAxis[1].Present && show_y2_label) ? txt_height + gp.Style.LabelPadding.x : 0) + ((plot.YAxis[1].Present && plot.YAxis[2].Present) ? gp.Style.LabelPadding.x + gp.Style.MinorTickLen.y : 0) - + ((plot.YAxis[2].Present && plot.YAxis[2].IsLabeled()) ? gp.YTicks[2].MaxWidth + gp.Style.LabelPadding.x : 0); + + ((plot.YAxis[2].Present && plot.YAxis[2].IsLabeled()) ? gp.YTicks[2].MaxWidth + gp.Style.LabelPadding.x : 0) + + ((plot.YAxis[2].Present && show_y3_label) ? txt_height + gp.Style.LabelPadding.x : 0); const float plot_width = plot.CanvasRect.GetWidth() - pad_left - pad_right; @@ -1467,7 +1476,12 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // axis label reference gp.YAxisReference[0] = plot.PlotRect.Min.x; gp.YAxisReference[1] = plot.PlotRect.Max.x; - gp.YAxisReference[2] = !plot.YAxis[1].Present ? plot.PlotRect.Max.x : (gp.YAxisReference[1] + (plot.YAxis[1].IsLabeled() ? gp.Style.LabelPadding.x + gp.YTicks[1].MaxWidth : 0) + gp.Style.LabelPadding.x + gp.Style.MinorTickLen.y); + gp.YAxisReference[2] = !plot.YAxis[1].Present + ? plot.PlotRect.Max.x + : gp.YAxisReference[1] + + (plot.YAxis[1].IsLabeled() ? gp.Style.LabelPadding.x + gp.YTicks[1].MaxWidth : 0) + + (show_y2_label ? txt_height + gp.Style.LabelPadding.x : 0) + + gp.Style.LabelPadding.x + gp.Style.MinorTickLen.y; // y axis regions bb and hover plot.YAxis[0].HoverRect = ImRect(ImVec2(plot.AxesRect.Min.x, plot.PlotRect.Min.y), ImVec2(plot.PlotRect.Min.x, plot.PlotRect.Max.y)); @@ -1849,15 +1863,27 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } // render axis labels - if (x_label) { + if (show_x_label) { const ImVec2 xLabel_size = ImGui::CalcTextSize(x_label); const ImVec2 xLabel_pos(plot.PlotRect.GetCenter().x - xLabel_size.x * 0.5f, plot.CanvasRect.Max.y - txt_height); DrawList.AddText(xLabel_pos, plot.XAxis.ColorTxt, x_label); } - if (y_label) { - const ImVec2 yLabel_size = CalcTextSizeVertical(y_label); + + if (show_y1_label) { + const ImVec2 yLabel_size = CalcTextSizeVertical(y1_label); const ImVec2 yLabel_pos(plot.CanvasRect.Min.x, plot.PlotRect.GetCenter().y + yLabel_size.y * 0.5f); - AddTextVertical(&DrawList, yLabel_pos, plot.YAxis[0].ColorTxt, y_label); + AddTextVertical(&DrawList, yLabel_pos, plot.YAxis[0].ColorTxt, y1_label); + } + + const char* y_labels[] = {y2_label, y3_label}; + for (int i = 1; i < IMPLOT_Y_AXES; i++) { + const char* current_label = y_labels[i-1]; + if (plot.YAxis[i].Present && current_label && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoLabel)) { + const ImVec2 yLabel_size = CalcTextSizeVertical(current_label); + float label_offset = (plot.YAxis[i].IsLabeled() ? gp.YTicks[i].MaxWidth + gp.Style.LabelPadding.x : 0.0f) + gp.Style.LabelPadding.x; + const ImVec2 yLabel_pos(gp.YAxisReference[i] + label_offset, plot.PlotRect.GetCenter().y + yLabel_size.y * 0.5f); + AddTextVertical(&DrawList, yLabel_pos, plot.YAxis[i].ColorTxt, current_label); + } } // render tick labels @@ -1883,6 +1909,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } } ImGui::PopClipRect(); + // clear legend plot.LegendData.Reset(); // push plot ID into stack @@ -1927,6 +1954,7 @@ void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool time_all ImGui::PushItemWidth(75); bool always_locked = axis.IsAlwaysLocked(); + bool label = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoLabel); bool grid = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoGridLines); bool ticks = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoTickMarks); bool labels = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoTickLabels); @@ -2023,11 +2051,13 @@ void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool time_all } ImGui::Separator(); + if (ImGui::Checkbox("Label", &label)) + ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoLabel); if (ImGui::Checkbox("Grid Lines", &grid)) ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoGridLines); if (ImGui::Checkbox("Tick Marks", &ticks)) ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickMarks); - if (ImGui::Checkbox("Labels", &labels)) + if (ImGui::Checkbox("Tick Labels", &labels)) ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickLabels); } diff --git a/implot.h b/implot.h index e225879..db4d2fb 100644 --- a/implot.h +++ b/implot.h @@ -84,16 +84,17 @@ enum ImPlotFlags_ { // Options for plot axes (X and Y). enum ImPlotAxisFlags_ { ImPlotAxisFlags_None = 0, // default - ImPlotAxisFlags_NoGridLines = 1 << 0, // no grid lines will be displayed - ImPlotAxisFlags_NoTickMarks = 1 << 1, // no tick marks will be displayed - ImPlotAxisFlags_NoTickLabels = 1 << 2, // no text labels will be displayed - ImPlotAxisFlags_LogScale = 1 << 3, // a logartithmic (base 10) axis scale will be used (mutually exclusive with ImPlotAxisFlags_Time) - ImPlotAxisFlags_Time = 1 << 4, // axis will display date/time formatted labels (mutually exclusive with ImPlotAxisFlags_LogScale) - ImPlotAxisFlags_Invert = 1 << 5, // the axis will be inverted - ImPlotAxisFlags_LockMin = 1 << 6, // the axis minimum value will be locked when panning/zooming - ImPlotAxisFlags_LockMax = 1 << 7, // the axis maximum value will be locked when panning/zooming + ImPlotAxisFlags_NoLabel = 1 << 0, // the axis label will not be displayed (axis labels also hidden if string is NULL) + ImPlotAxisFlags_NoGridLines = 1 << 1, // the axis grid lines will not be displayed + ImPlotAxisFlags_NoTickMarks = 1 << 2, // the axis tick marks will not be displayed + ImPlotAxisFlags_NoTickLabels = 1 << 3, // the axis tick labels will not be displayed + ImPlotAxisFlags_LogScale = 1 << 4, // a logartithmic (base 10) axis scale will be used (mutually exclusive with ImPlotAxisFlags_Time) + ImPlotAxisFlags_Time = 1 << 5, // axis will display date/time formatted labels (mutually exclusive with ImPlotAxisFlags_LogScale) + ImPlotAxisFlags_Invert = 1 << 6, // the axis will be inverted + ImPlotAxisFlags_LockMin = 1 << 7, // the axis minimum value will be locked when panning/zooming + ImPlotAxisFlags_LockMax = 1 << 8, // the axis maximum value will be locked when panning/zooming ImPlotAxisFlags_Lock = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax, - ImPlotAxisFlags_NoDecorations = ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels + ImPlotAxisFlags_NoDecorations = ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels }; // Plot styling colors. @@ -343,7 +344,9 @@ IMPLOT_API bool BeginPlot(const char* title_id, ImPlotAxisFlags x_flags = ImPlotAxisFlags_None, ImPlotAxisFlags y_flags = ImPlotAxisFlags_None, ImPlotAxisFlags y2_flags = ImPlotAxisFlags_NoGridLines, - ImPlotAxisFlags y3_flags = ImPlotAxisFlags_NoGridLines); + ImPlotAxisFlags y3_flags = ImPlotAxisFlags_NoGridLines, + const char* y2_label = NULL, + const char* y3_label = NULL); // Only call EndPlot() if BeginPlot() returns true! Typically called at the end // of an if statement conditioned on BeginPlot(). diff --git a/implot_demo.cpp b/implot_demo.cpp index 3908c42..30ac942 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -705,9 +705,11 @@ void ShowDemoWindow(bool* p_open) { ImPlot::SetNextPlotLimits(0.1, 100, 0, 10); ImPlot::SetNextPlotLimitsY(0, 1, ImGuiCond_Once, 1); ImPlot::SetNextPlotLimitsY(0, 300, ImGuiCond_Once, 2); - if (ImPlot::BeginPlot("Multi-Axis Plot", NULL, NULL, ImVec2(-1,0), + if (ImPlot::BeginPlot("Multi-Axis Plot", NULL, "Y-Axis 1", ImVec2(-1,0), (y2_axis ? ImPlotFlags_YAxis2 : 0) | - (y3_axis ? ImPlotFlags_YAxis3 : 0))) { + (y3_axis ? ImPlotFlags_YAxis3 : 0), + ImPlotAxisFlags_None, ImPlotAxisFlags_None, ImPlotAxisFlags_NoGridLines, ImPlotAxisFlags_NoGridLines, + "Y-Axis 2", "Y-Axis 3")) { ImPlot::PlotLine("f(x) = x", xs, xs, 1001); ImPlot::PlotLine("f(x) = sin(x)*3+1", xs, ys1, 1001); if (y2_axis) {