From 40cbe886737d6860a390d4e222fb066dc716c7a0 Mon Sep 17 00:00:00 2001 From: Evan Pezent Date: Sun, 15 Nov 2020 21:47:06 -0600 Subject: [PATCH] Add ImPlotFlags_Equal and simplify axis-related structs in implot_internal.h (#147) * prototyping equal axes * equal proto * more refactors * euqal axes almost perfect * fitting axis equal working * fitting axis equal working * finish equal axis --- implot.cpp | 751 ++++++++++++++++++++++++++-------------------- implot.h | 11 +- implot_demo.cpp | 25 +- implot_internal.h | 163 +++++----- implot_items.cpp | 36 +-- 5 files changed, 539 insertions(+), 447 deletions(-) diff --git a/implot.cpp b/implot.cpp index 6e3573b..fce72b3 100644 --- a/implot.cpp +++ b/implot.cpp @@ -379,8 +379,8 @@ void Reset(ImPlotContext* ctx) { ImGui::EndChild(); ctx->ChildWindowMade = false; // reset the next plot/item data - ctx->NextPlotData = ImPlotNextPlotData(); - ctx->NextItemData = ImPlotNextItemData(); + ctx->NextPlotData.Reset(); + ctx->NextItemData.Reset(); // reset items count ctx->VisibleItemCount = 0; // reset ticks/labels @@ -461,10 +461,10 @@ void UpdateTransformCache() { ImPlotContext& gp = *GImPlot; // get pixels for transforms for (int i = 0; i < IMPLOT_Y_AXES; i++) { - gp.PixelRange[i] = ImRect(ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_Invert) ? gp.BB_Plot.Max.x : gp.BB_Plot.Min.x, - ImHasFlag(gp.CurrentPlot->YAxis[i].Flags, ImPlotAxisFlags_Invert) ? gp.BB_Plot.Min.y : gp.BB_Plot.Max.y, - ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_Invert) ? gp.BB_Plot.Min.x : gp.BB_Plot.Max.x, - ImHasFlag(gp.CurrentPlot->YAxis[i].Flags, ImPlotAxisFlags_Invert) ? gp.BB_Plot.Max.y : gp.BB_Plot.Min.y); + gp.PixelRange[i] = ImRect(ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_Invert) ? gp.CurrentPlot->PlotRect.Max.x : gp.CurrentPlot->PlotRect.Min.x, + ImHasFlag(gp.CurrentPlot->YAxis[i].Flags, ImPlotAxisFlags_Invert) ? gp.CurrentPlot->PlotRect.Min.y : gp.CurrentPlot->PlotRect.Max.y, + ImHasFlag(gp.CurrentPlot->XAxis.Flags, ImPlotAxisFlags_Invert) ? gp.CurrentPlot->PlotRect.Min.x : gp.CurrentPlot->PlotRect.Max.x, + ImHasFlag(gp.CurrentPlot->YAxis[i].Flags, ImPlotAxisFlags_Invert) ? gp.CurrentPlot->PlotRect.Max.y : gp.CurrentPlot->PlotRect.Min.y); gp.My[i] = (gp.PixelRange[i].Max.y - gp.PixelRange[i].Min.y) / gp.CurrentPlot->YAxis[i].Range.Size(); } gp.LogDenX = ImLog10(gp.CurrentPlot->XAxis.Range.Max / gp.CurrentPlot->XAxis.Range.Min); @@ -1194,9 +1194,9 @@ int LabelAxisValue(const ImPlotAxis& axis, const ImPlotTickCollection& ticks, do return snprintf(buff, size, "%.3E", value); } else if (ImHasFlag(axis.Flags, ImPlotAxisFlags_Time)) { - ImPlotTimeUnit unit = (axis.Direction == ImPlotOrientation_Horizontal) - ? GetUnitForRange(axis.Range.Size() / (gp.BB_Plot.GetWidth() / 100)) - : GetUnitForRange(axis.Range.Size() / (gp.BB_Plot.GetHeight() / 100)); + ImPlotTimeUnit unit = (axis.Orientation == ImPlotOrientation_Horizontal) + ? GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetWidth() / 100)) + : GetUnitForRange(axis.Range.Size() / (gp.CurrentPlot->PlotRect.GetHeight() / 100)); return FormatDateTime(ImPlotTime::FromDouble(value), buff, size, GetDateTimeFmt(TimeFormatMouseCursor, unit)); } else { @@ -1205,13 +1205,12 @@ int LabelAxisValue(const ImPlotAxis& axis, const ImPlotTickCollection& ticks, do } } -void UpdateAxisColors(int axis_flag, ImPlotAxisColor* col) { +void UpdateAxisColors(int axis_flag, ImPlotAxis* axis) { const ImVec4 col_label = GetStyleColorVec4(axis_flag); const ImVec4 col_grid = GetStyleColorVec4(axis_flag + 1); - col->Major = ImGui::GetColorU32(col_grid); - col->Minor = ImGui::GetColorU32(col_grid*ImVec4(1,1,1,GImPlot->Style.MinorAlpha)); - col->MajTxt = ImGui::GetColorU32(col_label); - col->MinTxt = ImGui::GetColorU32(col_label); + axis->ColorMaj = ImGui::GetColorU32(col_grid); + axis->ColorMin = ImGui::GetColorU32(col_grid*ImVec4(1,1,1,GImPlot->Style.MinorAlpha)); + axis->ColorTxt = ImGui::GetColorU32(col_label); } //----------------------------------------------------------------------------- @@ -1219,13 +1218,13 @@ void UpdateAxisColors(int axis_flag, ImPlotAxisColor* col) { //----------------------------------------------------------------------------- bool BeginPlot(const char* title, const char* x_label, const char* y_label, const ImVec2& size, - ImPlotFlags flags, ImPlotAxisFlags x_flags, ImPlotAxisFlags y_flags, ImPlotAxisFlags y2_flags, ImPlotAxisFlags y3_flags) + ImPlotFlags flags, ImPlotAxisFlags x_flags, ImPlotAxisFlags y1_flags, ImPlotAxisFlags y2_flags, ImPlotAxisFlags y3_flags) { IM_ASSERT_USER_ERROR(GImPlot != NULL, "No current context. Did you call ImPlot::CreateContext() or ImPlot::SetCurrentContext()?"); ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot == NULL, "Mismatched BeginPlot()/EndPlot()!"); IM_ASSERT_USER_ERROR(!(ImHasFlag(x_flags, ImPlotAxisFlags_Time) && ImHasFlag(x_flags, ImPlotAxisFlags_LogScale)), "ImPlotAxisFlags_Time and ImPlotAxisFlags_LogScale cannot be enabled at the same time!"); - IM_ASSERT_USER_ERROR(!ImHasFlag(y_flags, ImPlotAxisFlags_Time), "Y axes cannot display time formatted labels!"); + IM_ASSERT_USER_ERROR(!ImHasFlag(y1_flags, ImPlotAxisFlags_Time), "Y axes cannot display time formatted labels!"); // FRONT MATTER ----------------------------------------------------------- @@ -1250,7 +1249,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons if (just_created) { plot.Flags = flags; plot.XAxis.Flags = x_flags; - plot.YAxis[0].Flags = y_flags; + plot.YAxis[0].Flags = y1_flags; plot.YAxis[1].Flags = y2_flags; plot.YAxis[2].Flags = y3_flags; } @@ -1261,8 +1260,8 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons plot.Flags = flags; if (x_flags != plot.XAxis.PreviousFlags) plot.XAxis.Flags = x_flags; - if (y_flags != plot.YAxis[0].PreviousFlags) - plot.YAxis[0].Flags = y_flags; + if (y1_flags != plot.YAxis[0].PreviousFlags) + plot.YAxis[0].Flags = y1_flags; if (y2_flags != plot.YAxis[1].PreviousFlags) plot.YAxis[1].Flags = y2_flags; if (y3_flags != plot.YAxis[2].PreviousFlags) @@ -1271,7 +1270,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons plot.PreviousFlags = flags; plot.XAxis.PreviousFlags = x_flags; - plot.YAxis[0].PreviousFlags = y_flags; + plot.YAxis[0].PreviousFlags = y1_flags; plot.YAxis[1].PreviousFlags = y2_flags; plot.YAxis[2].PreviousFlags = y3_flags; @@ -1313,12 +1312,10 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } // AXIS STATES ------------------------------------------------------------ - gp.X = ImPlotAxisState(&plot.XAxis, gp.NextPlotData.HasXRange, gp.NextPlotData.XRangeCond, true); - gp.Y[0] = ImPlotAxisState(&plot.YAxis[0], gp.NextPlotData.HasYRange[0], gp.NextPlotData.YRangeCond[0], true); - gp.Y[1] = ImPlotAxisState(&plot.YAxis[1], gp.NextPlotData.HasYRange[1], gp.NextPlotData.YRangeCond[1], ImHasFlag(plot.Flags, ImPlotFlags_YAxis2)); - gp.Y[2] = ImPlotAxisState(&plot.YAxis[2], gp.NextPlotData.HasYRange[2], gp.NextPlotData.YRangeCond[2], ImHasFlag(plot.Flags, ImPlotFlags_YAxis3)); - - gp.LockPlot = gp.X.Lock && gp.Y[0].Lock && gp.Y[1].Lock && gp.Y[2].Lock; + plot.XAxis.HasRange = gp.NextPlotData.HasXRange; plot.XAxis.RangeCond = gp.NextPlotData.XRangeCond; plot.XAxis.Present = true; + plot.YAxis[0].HasRange = gp.NextPlotData.HasYRange[0]; plot.YAxis[0].RangeCond = gp.NextPlotData.YRangeCond[0]; plot.YAxis[0].Present = true; + plot.YAxis[1].HasRange = gp.NextPlotData.HasYRange[1]; plot.YAxis[1].RangeCond = gp.NextPlotData.YRangeCond[1]; plot.YAxis[1].Present = ImHasFlag(plot.Flags, ImPlotFlags_YAxis2); + plot.YAxis[2].HasRange = gp.NextPlotData.HasYRange[2]; plot.YAxis[2].RangeCond = gp.NextPlotData.YRangeCond[2]; plot.YAxis[2].Present = ImHasFlag(plot.Flags, ImPlotFlags_YAxis3); for (int i = 0; i < IMPLOT_Y_AXES; ++i) { if (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale) && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LogScale)) @@ -1336,13 +1333,21 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons for (int i = 0; i < IMPLOT_Y_AXES; ++i) plot.YAxis[i].Constrain(); + // constain equal axes for primary x and y if not approximately equal + // constains x to y since x pixel size depends on y labels width, and causes feedback loops in opposite case + if (ImHasFlag(plot.Flags, ImPlotFlags_Equal)) { + double xar = plot.XAxis.GetAspect(); + double yar = plot.YAxis[0].GetAspect(); + if (!ImAlmostEqual(xar,yar) && !plot.YAxis[0].IsLocked()) + plot.XAxis.SetAspect(yar); + } // AXIS COLORS ----------------------------------------------------------------- - UpdateAxisColors(ImPlotCol_XAxis, &gp.Col_X); - UpdateAxisColors(ImPlotCol_YAxis, &gp.Col_Y[0]); - UpdateAxisColors(ImPlotCol_YAxis2, &gp.Col_Y[1]); - UpdateAxisColors(ImPlotCol_YAxis3, &gp.Col_Y[2]); + UpdateAxisColors(ImPlotCol_XAxis, &plot.XAxis); + UpdateAxisColors(ImPlotCol_YAxis, &plot.YAxis[0]); + UpdateAxisColors(ImPlotCol_YAxis2, &plot.YAxis[1]); + UpdateAxisColors(ImPlotCol_YAxis3, &plot.YAxis[2]); // BB, PADDING, HOVER ----------------------------------------------------------- @@ -1352,21 +1357,21 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons frame_size.x = gp.Style.PlotMinSize.x; if (frame_size.y < gp.Style.PlotMinSize.y && size.y < 0.0f) frame_size.y = gp.Style.PlotMinSize.y; - gp.BB_Frame = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); - ImGui::ItemSize(gp.BB_Frame); - if (!ImGui::ItemAdd(gp.BB_Frame, ID, &gp.BB_Frame)) { + plot.FrameRect = ImRect(Window->DC.CursorPos, Window->DC.CursorPos + frame_size); + ImGui::ItemSize(plot.FrameRect); + if (!ImGui::ItemAdd(plot.FrameRect, ID, &plot.FrameRect)) { Reset(GImPlot); return false; } - gp.Hov_Frame = ImGui::ItemHoverable(gp.BB_Frame, ID); + plot.FrameHovered = ImGui::ItemHoverable(plot.FrameRect, ID); if (G.HoveredIdPreviousFrame != 0 && G.HoveredIdPreviousFrame != ID) - gp.Hov_Frame = false; + plot.FrameHovered = false; ImGui::SetItemAllowOverlap(); - ImGui::RenderFrame(gp.BB_Frame.Min, gp.BB_Frame.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, Style.FrameRounding); + ImGui::RenderFrame(plot.FrameRect.Min, plot.FrameRect.Max, GetStyleColorU32(ImPlotCol_FrameBg), true, Style.FrameRounding); // canvas/axes bb - gp.BB_Canvas = ImRect(gp.BB_Frame.Min + gp.Style.PlotPadding, gp.BB_Frame.Max - gp.Style.PlotPadding); - gp.BB_Axes = gp.BB_Frame; + plot.CanvasRect = ImRect(plot.FrameRect.Min + gp.Style.PlotPadding, plot.FrameRect.Max - gp.Style.PlotPadding); + plot.AxesRect = plot.FrameRect; // outside legend adjustments if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.GetLegendCount() > 0 && plot.LegendOutside) { @@ -1377,20 +1382,20 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons const bool south = ImHasFlag(plot.LegendLocation, ImPlotLocation_South) && !ImHasFlag(plot.LegendLocation, ImPlotLocation_North); const bool horz = plot.LegendOrientation == ImPlotOrientation_Horizontal; if ((west && !horz) || (west && horz && !north && !south)) { - gp.BB_Canvas.Min.x += (legend_size.x + gp.Style.LegendPadding.x); - gp.BB_Axes.Min.x += (legend_size.x + gp.Style.PlotPadding.x); + plot.CanvasRect.Min.x += (legend_size.x + gp.Style.LegendPadding.x); + plot.AxesRect.Min.x += (legend_size.x + gp.Style.PlotPadding.x); } if ((east && !horz) || (east && horz && !north && !south)) { - gp.BB_Canvas.Max.x -= (legend_size.x + gp.Style.LegendPadding.x); - gp.BB_Axes.Max.x -= (legend_size.x + gp.Style.PlotPadding.x); + plot.CanvasRect.Max.x -= (legend_size.x + gp.Style.LegendPadding.x); + plot.AxesRect.Max.x -= (legend_size.x + gp.Style.PlotPadding.x); } if ((north && horz) || (north && !horz && !west && !east)) { - gp.BB_Canvas.Min.y += (legend_size.y + gp.Style.LegendPadding.y); - gp.BB_Axes.Min.y += (legend_size.y + gp.Style.PlotPadding.y); + plot.CanvasRect.Min.y += (legend_size.y + gp.Style.LegendPadding.y); + plot.AxesRect.Min.y += (legend_size.y + gp.Style.PlotPadding.y); } if ((south && horz) || (south && !horz && !west && !east)) { - gp.BB_Canvas.Max.y -= (legend_size.y + gp.Style.LegendPadding.y); - gp.BB_Axes.Max.y -= (legend_size.y + gp.Style.PlotPadding.y); + plot.CanvasRect.Max.y -= (legend_size.y + gp.Style.LegendPadding.y); + plot.AxesRect.Max.y -= (legend_size.y + gp.Style.PlotPadding.y); } } @@ -1398,7 +1403,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons !ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoTickMarks) || !ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoTickLabels)); for (int i = 0; i < IMPLOT_Y_AXES; i++) { - gp.RenderY[i] = gp.Y[i].Present && + gp.RenderY[i] = plot.YAxis[i].Present && (!ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoGridLines) || !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoTickMarks) || !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoTickLabels)); @@ -1414,10 +1419,10 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } const float pad_top = title_size.x > 0.0f ? txt_height + gp.Style.LabelPadding.y : 0; - const float pad_bot = (gp.X.HasLabels ? txt_height + gp.Style.LabelPadding.y + (gp.X.IsTime ? txt_height + gp.Style.LabelPadding.y : 0) : 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); - const float plot_height = gp.BB_Canvas.GetHeight() - pad_top - pad_bot; + const float plot_height = plot.CanvasRect.GetHeight() - pad_top - pad_bot; // (2) get y tick labels (needed for left/right pad) for (int i = 0; i < IMPLOT_Y_AXES; i++) { @@ -1431,16 +1436,16 @@ 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) - + (gp.Y[0].HasLabels ? gp.YTicks[0].MaxWidth + gp.Style.LabelPadding.x : 0); - const float pad_right = ((gp.Y[1].Present && gp.Y[1].HasLabels) ? gp.YTicks[1].MaxWidth + gp.Style.LabelPadding.x : 0) - + ((gp.Y[1].Present && gp.Y[2].Present) ? gp.Style.LabelPadding.x + gp.Style.MinorTickLen.y : 0) - + ((gp.Y[2].Present && gp.Y[2].HasLabels) ? gp.YTicks[2].MaxWidth + 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 && 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); - const float plot_width = gp.BB_Canvas.GetWidth() - pad_left - pad_right; + const float plot_width = plot.CanvasRect.GetWidth() - pad_left - pad_right; // (4) get x ticks if (gp.RenderX && gp.NextPlotData.ShowDefaultTicksX) { - if (gp.X.IsTime) + if (plot.XAxis.IsTime()) AddTicksTime(plot.XAxis.Range, plot_width, gp.XTicks); else if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LogScale)) AddTicksLogarithmic(plot.XAxis.Range, (int)IM_ROUND(plot_width * 0.01f), gp.XTicks); @@ -1449,44 +1454,47 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } // (5) calc plot bb - gp.BB_Plot = ImRect(gp.BB_Canvas.Min + ImVec2(pad_left, pad_top), gp.BB_Canvas.Max - ImVec2(pad_right, pad_bot)); - gp.Hov_Plot = gp.BB_Plot.Contains(IO.MousePos) && gp.Hov_Frame; + plot.PlotRect = ImRect(plot.CanvasRect.Min + ImVec2(pad_left, pad_top), plot.CanvasRect.Max - ImVec2(pad_right, pad_bot)); + plot.PlotHovered = plot.PlotRect.Contains(IO.MousePos) && plot.FrameHovered; // x axis region bb and hover - gp.BB_X = ImRect(gp.BB_Plot.GetBL(), ImVec2(gp.BB_Plot.Max.x, gp.BB_Axes.Max.y)); - plot.XAxis.HoveredExt = gp.BB_X.Contains(IO.MousePos); - plot.XAxis.HoveredTot = plot.XAxis.HoveredExt || gp.Hov_Plot; + plot.XAxis.HoverRect = ImRect(plot.PlotRect.GetBL(), ImVec2(plot.PlotRect.Max.x, plot.AxesRect.Max.y)); + plot.XAxis.ExtHovered = plot.XAxis.HoverRect.Contains(IO.MousePos); + plot.XAxis.AllHovered = plot.XAxis.ExtHovered || plot.PlotHovered; // axis label reference - gp.YAxisReference[0] = gp.BB_Plot.Min.x; - gp.YAxisReference[1] = gp.BB_Plot.Max.x; - gp.YAxisReference[2] = !gp.Y[1].Present ? gp.BB_Plot.Max.x : (gp.YAxisReference[1] + (gp.Y[1].HasLabels ? gp.Style.LabelPadding.x + gp.YTicks[1].MaxWidth : 0) + gp.Style.LabelPadding.x + gp.Style.MinorTickLen.y); + 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); // y axis regions bb and hover - gp.BB_Y[0] = ImRect(ImVec2(gp.BB_Axes.Min.x, gp.BB_Plot.Min.y), ImVec2(gp.BB_Plot.Min.x, gp.BB_Plot.Max.y)); - gp.BB_Y[1] = gp.Y[2].Present - ? ImRect(gp.BB_Plot.GetTR(), ImVec2(gp.YAxisReference[2], gp.BB_Plot.Max.y)) - : ImRect(gp.BB_Plot.GetTR(), ImVec2(gp.BB_Axes.Max.x, gp.BB_Plot.Max.y)); + plot.YAxis[0].HoverRect = ImRect(ImVec2(plot.AxesRect.Min.x, plot.PlotRect.Min.y), ImVec2(plot.PlotRect.Min.x, plot.PlotRect.Max.y)); + plot.YAxis[1].HoverRect = plot.YAxis[2].Present + ? ImRect(plot.PlotRect.GetTR(), ImVec2(gp.YAxisReference[2], plot.PlotRect.Max.y)) + : ImRect(plot.PlotRect.GetTR(), ImVec2(plot.AxesRect.Max.x, plot.PlotRect.Max.y)); - gp.BB_Y[2] = ImRect(ImVec2(gp.YAxisReference[2], gp.BB_Plot.Min.y), ImVec2(gp.BB_Axes.Max.x, gp.BB_Plot.Max.y)); + plot.YAxis[2].HoverRect = ImRect(ImVec2(gp.YAxisReference[2], plot.PlotRect.Min.y), ImVec2(plot.AxesRect.Max.x, plot.PlotRect.Max.y)); for (int i = 0; i < IMPLOT_Y_AXES; ++i) { - plot.YAxis[i].HoveredExt = gp.Y[i].Present && gp.BB_Y[i].Contains(IO.MousePos); - plot.YAxis[i].HoveredTot = plot.YAxis[i].HoveredExt || gp.Hov_Plot; + plot.YAxis[i].ExtHovered = plot.YAxis[i].Present && plot.YAxis[i].HoverRect.Contains(IO.MousePos); + plot.YAxis[i].AllHovered = plot.YAxis[i].ExtHovered || plot.PlotHovered; } - const bool any_hov_y_axis_region = plot.YAxis[0].HoveredTot || plot.YAxis[1].HoveredTot || plot.YAxis[2].HoveredTot; + const bool any_hov_y_axis_region = plot.YAxis[0].AllHovered || plot.YAxis[1].AllHovered || plot.YAxis[2].AllHovered; bool hov_query = false; - if (gp.Hov_Frame && gp.Hov_Plot && plot.Queried && !plot.Querying) { + if (plot.FrameHovered && plot.PlotHovered && plot.Queried && !plot.Querying) { ImRect bb_query = plot.QueryRect; - - bb_query.Min += gp.BB_Plot.Min; - bb_query.Max += gp.BB_Plot.Min; - + bb_query.Min += plot.PlotRect.Min; + bb_query.Max += plot.PlotRect.Min; hov_query = bb_query.Contains(IO.MousePos); } + // AXIS ASPECT RATIOS + plot.XAxis.Pixels = plot.PlotRect.GetWidth(); + for (int i = 0; i < IMPLOT_Y_AXES; ++i) + plot.YAxis[i].Pixels = plot.PlotRect.GetHeight(); + // QUERY DRAG ------------------------------------------------------------- if (plot.DraggingQuery && (IO.MouseReleased[gp.InputMap.PanButton] || !IO.MouseDown[gp.InputMap.PanButton])) { plot.DraggingQuery = false; @@ -1496,7 +1504,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons plot.QueryRect.Min += IO.MouseDelta; plot.QueryRect.Max += IO.MouseDelta; } - if (gp.Hov_Frame && gp.Hov_Plot && hov_query && !plot.DraggingQuery && !plot.Selecting && !plot.LegendHovered) { + if (plot.FrameHovered && plot.PlotHovered && hov_query && !plot.DraggingQuery && !plot.Selecting && !plot.LegendHovered) { ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); const bool any_y_dragging = plot.YAxis[0].Dragging || plot.YAxis[1].Dragging || plot.YAxis[2].Dragging; if (IO.MouseDown[gp.InputMap.PanButton] && !plot.XAxis.Dragging && !any_y_dragging) { @@ -1506,6 +1514,8 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // DRAG INPUT ------------------------------------------------------------- + const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); + // end drags if (plot.XAxis.Dragging && (IO.MouseReleased[gp.InputMap.PanButton] || !IO.MouseDown[gp.InputMap.PanButton])) { plot.XAxis.Dragging = false; @@ -1522,32 +1532,55 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // do drag if (drag_in_progress) { UpdateTransformCache(); - if (!gp.X.Lock && plot.XAxis.Dragging) { - ImPlotPoint plot_tl = PixelsToPlot(gp.BB_Plot.Min - IO.MouseDelta, 0); - ImPlotPoint plot_br = PixelsToPlot(gp.BB_Plot.Max - IO.MouseDelta, 0); - if (!gp.X.LockMin) - plot.XAxis.SetMin(gp.X.Invert ? plot_br.x : plot_tl.x); - if (!gp.X.LockMax) - plot.XAxis.SetMax(gp.X.Invert ? plot_tl.x : plot_br.x); + bool equal_dragged = false; + // special case for axis equal and both x and y0 hovered + if (axis_equal && !plot.XAxis.IsLocked() && plot.XAxis.Dragging && !plot.YAxis[0].IsLocked() && plot.YAxis[0].Dragging) { + ImPlotPoint plot_tl = PixelsToPlot(plot.PlotRect.Min - IO.MouseDelta, 0); + ImPlotPoint plot_br = PixelsToPlot(plot.PlotRect.Max - IO.MouseDelta, 0); + if (!plot.XAxis.IsLockedMin()) + plot.XAxis.SetMin(plot.XAxis.IsInverted() ? plot_br.x : plot_tl.x); + if (!plot.XAxis.IsLockedMax()) + plot.XAxis.SetMax(plot.XAxis.IsInverted() ? plot_tl.x : plot_br.x); + if (!plot.YAxis[0].IsLockedMin()) + plot.YAxis[0].SetMin(plot.YAxis[0].IsInverted() ? plot_tl.y : plot_br.y); + if (!plot.YAxis[0].IsLockedMax()) + plot.YAxis[0].SetMax(plot.YAxis[0].IsInverted() ? plot_br.y : plot_tl.y); + double xar = plot.XAxis.GetAspect(); + double yar = plot.YAxis[0].GetAspect(); + if (!ImAlmostEqual(xar,yar) && !plot.YAxis[0].IsLocked()) + plot.XAxis.SetAspect(yar); + equal_dragged = true; + } + if (!plot.XAxis.IsLocked() && plot.XAxis.Dragging && !equal_dragged) { + ImPlotPoint plot_tl = PixelsToPlot(plot.PlotRect.Min - IO.MouseDelta, 0); + ImPlotPoint plot_br = PixelsToPlot(plot.PlotRect.Max - IO.MouseDelta, 0); + if (!plot.XAxis.IsLockedMin()) + plot.XAxis.SetMin(plot.XAxis.IsInverted() ? plot_br.x : plot_tl.x); + if (!plot.XAxis.IsLockedMax()) + plot.XAxis.SetMax(plot.XAxis.IsInverted() ? plot_tl.x : plot_br.x); + if (axis_equal) + plot.YAxis[0].SetAspect(plot.XAxis.GetAspect()); } for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (!gp.Y[i].Lock && plot.YAxis[i].Dragging) { - ImPlotPoint plot_tl = PixelsToPlot(gp.BB_Plot.Min - IO.MouseDelta, i); - ImPlotPoint plot_br = PixelsToPlot(gp.BB_Plot.Max - IO.MouseDelta, i); - if (!gp.Y[i].LockMin) - plot.YAxis[i].SetMin(gp.Y[i].Invert ? plot_tl.y : plot_br.y); - if (!gp.Y[i].LockMax) - plot.YAxis[i].SetMax(gp.Y[i].Invert ? plot_br.y : plot_tl.y); + if (!plot.YAxis[i].IsLocked() && plot.YAxis[i].Dragging && !(i == 0 && equal_dragged)) { + ImPlotPoint plot_tl = PixelsToPlot(plot.PlotRect.Min - IO.MouseDelta, i); + ImPlotPoint plot_br = PixelsToPlot(plot.PlotRect.Max - IO.MouseDelta, i); + if (!plot.YAxis[i].IsLockedMin()) + plot.YAxis[i].SetMin(plot.YAxis[i].IsInverted() ? plot_tl.y : plot_br.y); + if (!plot.YAxis[i].IsLockedMax()) + plot.YAxis[i].SetMax(plot.YAxis[i].IsInverted() ? plot_br.y : plot_tl.y); + if (i == 0 && axis_equal) + plot.XAxis.SetAspect(plot.YAxis[0].GetAspect()); } } // Set the mouse cursor based on which axes are moving. int direction = 0; - if (!gp.X.Lock && plot.XAxis.Dragging) { + if (!plot.XAxis.IsLocked() && plot.XAxis.Dragging) { direction |= (1 << 1); } for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (!gp.Y[i].Present) { continue; } - if (!gp.Y[i].Lock && plot.YAxis[i].Dragging) { + if (!plot.YAxis[i].Present) { continue; } + if (!plot.YAxis[i].IsLocked() && plot.YAxis[i].Dragging) { direction |= (1 << 2); break; } @@ -1564,12 +1597,12 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } } // start drag - if (!drag_in_progress && gp.Hov_Frame && IO.MouseClicked[gp.InputMap.PanButton] && ImHasFlag(IO.KeyMods, gp.InputMap.PanMod) && !plot.Selecting && !plot.LegendHovered && !hov_query && !plot.DraggingQuery) { - if (plot.XAxis.HoveredTot) { + if (!drag_in_progress && plot.FrameHovered && IO.MouseClicked[gp.InputMap.PanButton] && ImHasFlag(IO.KeyMods, gp.InputMap.PanMod) && !plot.Selecting && !plot.LegendHovered && !hov_query && !plot.DraggingQuery) { + if (plot.XAxis.AllHovered) { plot.XAxis.Dragging = true; } for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (plot.YAxis[i].HoveredTot) { + if (plot.YAxis[i].AllHovered) { plot.YAxis[i].Dragging = true; } } @@ -1577,31 +1610,52 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // SCROLL INPUT ----------------------------------------------------------- - if (gp.Hov_Frame && (plot.XAxis.HoveredTot || any_hov_y_axis_region) && IO.MouseWheel != 0) { + if (plot.FrameHovered && (plot.XAxis.AllHovered || any_hov_y_axis_region) && IO.MouseWheel != 0) { UpdateTransformCache(); float zoom_rate = IMPLOT_ZOOM_RATE; if (IO.MouseWheel > 0) zoom_rate = (-zoom_rate) / (1.0f + (2.0f * zoom_rate)); - float tx = ImRemap(IO.MousePos.x, gp.BB_Plot.Min.x, gp.BB_Plot.Max.x, 0.0f, 1.0f); - float ty = ImRemap(IO.MousePos.y, gp.BB_Plot.Min.y, gp.BB_Plot.Max.y, 0.0f, 1.0f); - if (plot.XAxis.HoveredTot && !gp.X.Lock) { - ImPlotAxisScale axis_scale(0, tx, ty, zoom_rate); - const ImPlotPoint& plot_tl = axis_scale.Min; - const ImPlotPoint& plot_br = axis_scale.Max; - if (!gp.X.LockMin) - plot.XAxis.SetMin(gp.X.Invert ? plot_br.x : plot_tl.x); - if (!gp.X.LockMax) - plot.XAxis.SetMax(gp.X.Invert ? plot_tl.x : plot_br.x); + float tx = ImRemap(IO.MousePos.x, plot.PlotRect.Min.x, plot.PlotRect.Max.x, 0.0f, 1.0f); + float ty = ImRemap(IO.MousePos.y, plot.PlotRect.Min.y, plot.PlotRect.Max.y, 0.0f, 1.0f); + bool equal_zoomed = false; + // special case for axis equal and both x and y0 hovered + if (axis_equal && plot.XAxis.AllHovered && !plot.XAxis.IsLocked() && plot.YAxis[0].AllHovered && !plot.YAxis[0].IsLocked()) { + const ImPlotPoint& plot_tl = PixelsToPlot(plot.PlotRect.Min - plot.PlotRect.GetSize() * ImVec2(tx * zoom_rate, ty * zoom_rate), 0); + const ImPlotPoint& plot_br = PixelsToPlot(plot.PlotRect.Max + plot.PlotRect.GetSize() * ImVec2((1 - tx) * zoom_rate, (1 - ty) * zoom_rate), 0); + if (!plot.XAxis.IsLockedMin()) + plot.XAxis.SetMin(plot.XAxis.IsInverted() ? plot_br.x : plot_tl.x); + if (!plot.XAxis.IsLockedMax()) + plot.XAxis.SetMax(plot.XAxis.IsInverted() ? plot_tl.x : plot_br.x); + if (!plot.YAxis[0].IsLockedMin()) + plot.YAxis[0].SetMin(plot.YAxis[0].IsInverted() ? plot_tl.y : plot_br.y); + if (!plot.YAxis[0].IsLockedMax()) + plot.YAxis[0].SetMax(plot.YAxis[0].IsInverted() ? plot_br.y : plot_tl.y); + double xar = plot.XAxis.GetAspect(); + double yar = plot.YAxis[0].GetAspect(); + if (!ImAlmostEqual(xar,yar) && !plot.YAxis[0].IsLocked()) + plot.XAxis.SetAspect(yar); + equal_zoomed = true; + } + if (plot.XAxis.AllHovered && !plot.XAxis.IsLocked() && !equal_zoomed) { + const ImPlotPoint& plot_tl = PixelsToPlot(plot.PlotRect.Min - plot.PlotRect.GetSize() * ImVec2(tx * zoom_rate, ty * zoom_rate), 0); + const ImPlotPoint& plot_br = PixelsToPlot(plot.PlotRect.Max + plot.PlotRect.GetSize() * ImVec2((1 - tx) * zoom_rate, (1 - ty) * zoom_rate), 0); + if (!plot.XAxis.IsLockedMin()) + plot.XAxis.SetMin(plot.XAxis.IsInverted() ? plot_br.x : plot_tl.x); + if (!plot.XAxis.IsLockedMax()) + plot.XAxis.SetMax(plot.XAxis.IsInverted() ? plot_tl.x : plot_br.x); + if (axis_equal) + plot.YAxis[0].SetAspect(plot.XAxis.GetAspect()); } for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (plot.YAxis[i].HoveredTot && !gp.Y[i].Lock) { - ImPlotAxisScale axis_scale(i, tx, ty, zoom_rate); - const ImPlotPoint& plot_tl = axis_scale.Min; - const ImPlotPoint& plot_br = axis_scale.Max; - if (!gp.Y[i].LockMin) - plot.YAxis[i].SetMin(gp.Y[i].Invert ? plot_tl.y : plot_br.y); - if (!gp.Y[i].LockMax) - plot.YAxis[i].SetMax(gp.Y[i].Invert ? plot_br.y : plot_tl.y); + if (plot.YAxis[i].AllHovered && !plot.YAxis[i].IsLocked() && !(i == 0 && equal_zoomed)) { + const ImPlotPoint& plot_tl = PixelsToPlot(plot.PlotRect.Min - plot.PlotRect.GetSize() * ImVec2(tx * zoom_rate, ty * zoom_rate), i); + const ImPlotPoint& plot_br = PixelsToPlot(plot.PlotRect.Max + plot.PlotRect.GetSize() * ImVec2((1 - tx) * zoom_rate, (1 - ty) * zoom_rate), i); + if (!plot.YAxis[i].IsLockedMin()) + plot.YAxis[i].SetMin(plot.YAxis[i].IsInverted() ? plot_tl.y : plot_br.y); + if (!plot.YAxis[i].IsLockedMax()) + plot.YAxis[i].SetMax(plot.YAxis[i].IsInverted() ? plot_br.y : plot_tl.y); + if (i == 0 && axis_equal) + plot.XAxis.SetAspect(plot.YAxis[0].GetAspect()); } } } @@ -1617,23 +1671,23 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons ImPlotPoint p2 = PixelsToPlot(IO.MousePos); const bool x_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.HorizontalMod) && ImFabs(select_size.x) > 2; const bool y_can_change = !ImHasFlag(IO.KeyMods,gp.InputMap.VerticalMod) && ImFabs(select_size.y) > 2; - if (!gp.X.LockMin && x_can_change) + if (!plot.XAxis.IsLockedMin() && x_can_change) plot.XAxis.SetMin(ImMin(p1.x, p2.x)); - if (!gp.X.LockMax && x_can_change) + if (!plot.XAxis.IsLockedMax() && x_can_change) plot.XAxis.SetMax(ImMax(p1.x, p2.x)); for (int i = 0; i < IMPLOT_Y_AXES; i++) { p1 = PixelsToPlot(plot.SelectStart, i); p2 = PixelsToPlot(IO.MousePos, i); - if (!gp.Y[i].LockMin && y_can_change) + if (!plot.YAxis[i].IsLockedMin() && y_can_change) plot.YAxis[i].SetMin(ImMin(p1.y, p2.y)); - if (!gp.Y[i].LockMax && y_can_change) + if (!plot.YAxis[i].IsLockedMax() && y_can_change) plot.YAxis[i].SetMax(ImMax(p1.y, p2.y)); } } plot.Selecting = false; } // bad selection - if (plot.Selecting && (ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect) || gp.LockPlot) && ImLengthSqr(plot.SelectStart - IO.MousePos) > 4) { + if (plot.Selecting && (ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect) || plot.IsLocked()) && ImLengthSqr(plot.SelectStart - IO.MousePos) > 4) { ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); } // cancel selection @@ -1641,20 +1695,20 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons plot.Selecting = false; } // begin selection or query - if (gp.Hov_Frame && gp.Hov_Plot && IO.MouseClicked[gp.InputMap.BoxSelectButton] && ImHasFlag(IO.KeyMods, gp.InputMap.BoxSelectMod)) { + if (plot.FrameHovered && plot.PlotHovered && IO.MouseClicked[gp.InputMap.BoxSelectButton] && ImHasFlag(IO.KeyMods, gp.InputMap.BoxSelectMod)) { plot.SelectStart = IO.MousePos; plot.Selecting = true; } // update query if (plot.Querying) { UpdateTransformCache(); - plot.QueryRect.Min.x = ImHasFlag(IO.KeyMods, gp.InputMap.HorizontalMod) ? gp.BB_Plot.Min.x : ImMin(plot.QueryStart.x, IO.MousePos.x); - plot.QueryRect.Max.x = ImHasFlag(IO.KeyMods, gp.InputMap.HorizontalMod) ? gp.BB_Plot.Max.x : ImMax(plot.QueryStart.x, IO.MousePos.x); - plot.QueryRect.Min.y = ImHasFlag(IO.KeyMods, gp.InputMap.VerticalMod) ? gp.BB_Plot.Min.y : ImMin(plot.QueryStart.y, IO.MousePos.y); - plot.QueryRect.Max.y = ImHasFlag(IO.KeyMods, gp.InputMap.VerticalMod) ? gp.BB_Plot.Max.y : ImMax(plot.QueryStart.y, IO.MousePos.y); + plot.QueryRect.Min.x = ImHasFlag(IO.KeyMods, gp.InputMap.HorizontalMod) ? plot.PlotRect.Min.x : ImMin(plot.QueryStart.x, IO.MousePos.x); + plot.QueryRect.Max.x = ImHasFlag(IO.KeyMods, gp.InputMap.HorizontalMod) ? plot.PlotRect.Max.x : ImMax(plot.QueryStart.x, IO.MousePos.x); + plot.QueryRect.Min.y = ImHasFlag(IO.KeyMods, gp.InputMap.VerticalMod) ? plot.PlotRect.Min.y : ImMin(plot.QueryStart.y, IO.MousePos.y); + plot.QueryRect.Max.y = ImHasFlag(IO.KeyMods, gp.InputMap.VerticalMod) ? plot.PlotRect.Max.y : ImMax(plot.QueryStart.y, IO.MousePos.y); - plot.QueryRect.Min -= gp.BB_Plot.Min; - plot.QueryRect.Max -= gp.BB_Plot.Min; + plot.QueryRect.Min -= plot.PlotRect.Min; + plot.QueryRect.Max -= plot.PlotRect.Min; } // end query if (plot.Querying && (IO.MouseReleased[gp.InputMap.QueryButton] || IO.MouseReleased[gp.InputMap.BoxSelectButton])) { @@ -1666,7 +1720,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons } // begin query - if (ImHasFlag(plot.Flags, ImPlotFlags_Query) && gp.Hov_Frame && gp.Hov_Plot && IO.MouseClicked[gp.InputMap.QueryButton] && ImHasFlag(IO.KeyMods, gp.InputMap.QueryMod)) { + if (ImHasFlag(plot.Flags, ImPlotFlags_Query) && plot.FrameHovered && plot.PlotHovered && IO.MouseClicked[gp.InputMap.QueryButton] && ImHasFlag(IO.KeyMods, gp.InputMap.QueryMod)) { plot.QueryRect = ImRect(0,0,0,0); plot.Querying = true; plot.Queried = true; @@ -1695,11 +1749,11 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // FIT ----------------------------------------------------------- // fit from double click - if ( IO.MouseDoubleClicked[gp.InputMap.FitButton] && gp.Hov_Frame && (plot.XAxis.HoveredTot || any_hov_y_axis_region) && !plot.LegendHovered && !hov_query ) { + if ( IO.MouseDoubleClicked[gp.InputMap.FitButton] && plot.FrameHovered && (plot.XAxis.AllHovered || any_hov_y_axis_region) && !plot.LegendHovered && !hov_query ) { gp.FitThisFrame = true; - gp.FitX = plot.XAxis.HoveredTot; + gp.FitX = plot.XAxis.AllHovered; for (int i = 0; i < IMPLOT_Y_AXES; i++) - gp.FitY[i] = plot.YAxis[i].HoveredTot; + gp.FitY[i] = plot.YAxis[i].AllHovered; } // fit from FitNextPlotAxes if (gp.NextPlotData.FitX) { @@ -1716,7 +1770,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // FOCUS ------------------------------------------------------------------ // focus window - if ((IO.MouseClicked[0] || IO.MouseClicked[1] || IO.MouseClicked[2]) && gp.Hov_Frame) + if ((IO.MouseClicked[0] || IO.MouseClicked[1] || IO.MouseClicked[2]) && plot.FrameHovered) ImGui::FocusWindow(ImGui::GetCurrentWindow()); UpdateTransformCache(); @@ -1729,7 +1783,7 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // RENDER ----------------------------------------------------------------- // grid bg - DrawList.AddRectFilled(gp.BB_Plot.Min, gp.BB_Plot.Max, GetStyleColorU32(ImPlotCol_PlotBg)); + DrawList.AddRectFilled(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBg)); // render axes PushPlotClipRect(); @@ -1752,33 +1806,33 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons // render grid if (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoGridLines)) { - float density = gp.XTicks.Size / gp.BB_Plot.GetWidth(); - ImVec4 col_min = ImGui::ColorConvertU32ToFloat4(gp.Col_X.Minor); + float density = gp.XTicks.Size / plot.PlotRect.GetWidth(); + ImVec4 col_min = ImGui::ColorConvertU32ToFloat4(plot.XAxis.ColorMin); col_min.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f); ImU32 col_min32 = ImGui::ColorConvertFloat4ToU32(col_min); for (int t = 0; t < gp.XTicks.Size; t++) { ImPlotTick& xt = gp.XTicks.Ticks[t]; if (xt.Level == 0) { if (xt.Major) - DrawList.AddLine(ImVec2(xt.PixelPos, gp.BB_Plot.Min.y), ImVec2(xt.PixelPos, gp.BB_Plot.Max.y), gp.Col_X.Major, gp.Style.MajorGridSize.x); + DrawList.AddLine(ImVec2(xt.PixelPos, plot.PlotRect.Min.y), ImVec2(xt.PixelPos, plot.PlotRect.Max.y), plot.XAxis.ColorMaj, gp.Style.MajorGridSize.x); else if (density < 0.2f) - DrawList.AddLine(ImVec2(xt.PixelPos, gp.BB_Plot.Min.y), ImVec2(xt.PixelPos, gp.BB_Plot.Max.y), col_min32, gp.Style.MinorGridSize.x); + DrawList.AddLine(ImVec2(xt.PixelPos, plot.PlotRect.Min.y), ImVec2(xt.PixelPos, plot.PlotRect.Max.y), col_min32, gp.Style.MinorGridSize.x); } } } for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (gp.Y[i].Present && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoGridLines)) { - float density = gp.YTicks[i].Size / gp.BB_Plot.GetHeight(); - ImVec4 col_min = ImGui::ColorConvertU32ToFloat4(gp.Col_Y[i].Minor); + if (plot.YAxis[i].Present && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoGridLines)) { + float density = gp.YTicks[i].Size / plot.PlotRect.GetHeight(); + ImVec4 col_min = ImGui::ColorConvertU32ToFloat4(plot.YAxis[i].ColorMin); col_min.w *= ImClamp(ImRemap(density, 0.1f, 0.2f, 1.0f, 0.0f), 0.0f, 1.0f); ImU32 col_min32 = ImGui::ColorConvertFloat4ToU32(col_min); for (int t = 0; t < gp.YTicks[i].Size; t++) { ImPlotTick& yt = gp.YTicks[i].Ticks[t]; if (yt.Major) - DrawList.AddLine(ImVec2(gp.BB_Plot.Min.x, yt.PixelPos), ImVec2(gp.BB_Plot.Max.x, yt.PixelPos), gp.Col_Y[i].Major, gp.Style.MajorGridSize.y); + DrawList.AddLine(ImVec2(plot.PlotRect.Min.x, yt.PixelPos), ImVec2(plot.PlotRect.Max.x, yt.PixelPos), plot.YAxis[i].ColorMaj, gp.Style.MajorGridSize.y); else if (density < 0.2f) - DrawList.AddLine(ImVec2(gp.BB_Plot.Min.x, yt.PixelPos), ImVec2(gp.BB_Plot.Max.x, yt.PixelPos), col_min32, gp.Style.MinorGridSize.y); + DrawList.AddLine(ImVec2(plot.PlotRect.Min.x, yt.PixelPos), ImVec2(plot.PlotRect.Max.x, yt.PixelPos), col_min32, gp.Style.MinorGridSize.y); } } } @@ -1789,39 +1843,39 @@ bool BeginPlot(const char* title, const char* x_label, const char* y_label, cons if (title_size.x > 0.0f && !ImHasFlag(plot.Flags, ImPlotFlags_NoTitle)) { ImU32 col = GetStyleColorU32(ImPlotCol_TitleText); const char* title_end = ImGui::FindRenderedTextEnd(title, NULL); - DrawList.AddText(ImVec2(gp.BB_Canvas.GetCenter().x - title_size.x * 0.5f, gp.BB_Canvas.Min.y),col,title,title_end); + DrawList.AddText(ImVec2(plot.CanvasRect.GetCenter().x - title_size.x * 0.5f, plot.CanvasRect.Min.y),col,title,title_end); } // render axis labels if (x_label) { const ImVec2 xLabel_size = ImGui::CalcTextSize(x_label); - const ImVec2 xLabel_pos(gp.BB_Plot.GetCenter().x - xLabel_size.x * 0.5f, gp.BB_Canvas.Max.y - txt_height); - DrawList.AddText(xLabel_pos, gp.Col_X.MajTxt, 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); - const ImVec2 yLabel_pos(gp.BB_Canvas.Min.x, gp.BB_Plot.GetCenter().y + yLabel_size.y * 0.5f); - AddTextVertical(&DrawList, yLabel_pos, gp.Col_Y[0].MajTxt, y_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); } // render tick labels - ImGui::PushClipRect(gp.BB_Frame.Min, gp.BB_Frame.Max, true); + ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true); if (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_NoTickLabels)) { for (int t = 0; t < gp.XTicks.Size; t++) { ImPlotTick *xt = &gp.XTicks.Ticks[t]; - if (xt->ShowLabel && xt->PixelPos >= gp.BB_Plot.Min.x - 1 && xt->PixelPos <= gp.BB_Plot.Max.x + 1) - DrawList.AddText(ImVec2(xt->PixelPos - xt->LabelSize.x * 0.5f, gp.BB_Plot.Max.y + gp.Style.LabelPadding.y + xt->Level * (txt_height + gp.Style.LabelPadding.y)), - xt->Major ? gp.Col_X.MajTxt : gp.Col_X.MinTxt, gp.XTicks.GetText(t)); + if (xt->ShowLabel && xt->PixelPos >= plot.PlotRect.Min.x - 1 && xt->PixelPos <= plot.PlotRect.Max.x + 1) + DrawList.AddText(ImVec2(xt->PixelPos - xt->LabelSize.x * 0.5f, plot.PlotRect.Max.y + gp.Style.LabelPadding.y + xt->Level * (txt_height + gp.Style.LabelPadding.y)), + xt->Major ? plot.XAxis.ColorTxt : plot.XAxis.ColorTxt, gp.XTicks.GetText(t)); } } for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (gp.Y[i].Present && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoTickLabels)) { + if (plot.YAxis[i].Present && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_NoTickLabels)) { for (int t = 0; t < gp.YTicks[i].Size; t++) { const float x_start = gp.YAxisReference[i] + (i == 0 ? (-gp.Style.LabelPadding.x - gp.YTicks[i].Ticks[t].LabelSize.x) : gp.Style.LabelPadding.x); ImPlotTick *yt = &gp.YTicks[i].Ticks[t]; - if (yt->ShowLabel && yt->PixelPos >= gp.BB_Plot.Min.y - 1 && yt->PixelPos <= gp.BB_Plot.Max.y + 1) { + if (yt->ShowLabel && yt->PixelPos >= plot.PlotRect.Min.y - 1 && yt->PixelPos <= plot.PlotRect.Max.y + 1) { ImVec2 start(x_start, yt->PixelPos - 0.5f * yt->LabelSize.y); - DrawList.AddText(start, yt->Major ? gp.Col_Y[i].MajTxt : gp.Col_Y[i].MinTxt, gp.YTicks[i].GetText(t)); + DrawList.AddText(start, yt->Major ? plot.YAxis[i].ColorTxt : plot.YAxis[i].ColorTxt, gp.YTicks[i].GetText(t)); } } } @@ -1867,28 +1921,24 @@ inline void EndDisabledControls(bool cond) { } } -void ShowAxisContextMenu(ImPlotAxisState& state, bool time_allowed) { +void ShowAxisContextMenu(ImPlotAxis& axis, ImPlotAxis* equal_axis, bool time_allowed) { ImGui::PushItemWidth(75); - ImPlotAxis& axis = *state.Axis; - bool total_lock = state.HasRange && state.RangeCond == ImGuiCond_Always; - bool logscale = ImHasFlag(axis.Flags, ImPlotAxisFlags_LogScale); - bool timescale = ImHasFlag(axis.Flags, ImPlotAxisFlags_Time); - bool grid = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoGridLines); - bool ticks = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoTickMarks); - bool labels = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoTickLabels); - double drag_speed = (axis.Range.Size() <= DBL_EPSILON) ? DBL_EPSILON * 1.0e+13 : 0.01 * axis.Range.Size(); // recover from almost equal axis limits. + bool always_locked = axis.IsAlwaysLocked(); + bool grid = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoGridLines); + bool ticks = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoTickMarks); + bool labels = !ImHasFlag(axis.Flags, ImPlotAxisFlags_NoTickLabels); + double drag_speed = (axis.Range.Size() <= DBL_EPSILON) ? DBL_EPSILON * 1.0e+13 : 0.01 * axis.Range.Size(); // recover from almost equal axis limits. - if (timescale) { + if (axis.IsTime()) { ImPlotTime tmin = ImPlotTime::FromDouble(axis.Range.Min); ImPlotTime tmax = ImPlotTime::FromDouble(axis.Range.Max); - BeginDisabledControls(total_lock); - if (ImGui::Checkbox("##LockMin", &state.LockMin)) - ImFlipFlag(axis.Flags, ImPlotAxisFlags_LockMin); - EndDisabledControls(total_lock); + BeginDisabledControls(always_locked); + ImGui::CheckboxFlags("##LockMin", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin); + EndDisabledControls(always_locked); ImGui::SameLine(); - BeginDisabledControls(state.LockMin); + BeginDisabledControls(axis.IsLockedMin()); if (ImGui::BeginMenu("Min Time")) { if (ShowTimePicker("mintime", &tmin)) { if (tmin >= tmax) @@ -1904,14 +1954,13 @@ void ShowAxisContextMenu(ImPlotAxisState& state, bool time_allowed) { } ImGui::EndMenu(); } - EndDisabledControls(state.LockMin); + EndDisabledControls(axis.IsLockedMin()); - BeginDisabledControls(total_lock); - if (ImGui::Checkbox("##LockMax", &state.LockMax)) - ImFlipFlag(axis.Flags, ImPlotAxisFlags_LockMax); - EndDisabledControls(total_lock); + BeginDisabledControls(always_locked); + ImGui::CheckboxFlags("##LockMax", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax); + EndDisabledControls(always_locked); ImGui::SameLine(); - BeginDisabledControls(state.LockMax); + BeginDisabledControls(axis.IsLockedMax()); if (ImGui::BeginMenu("Max Time")) { if (ShowTimePicker("maxtime", &tmax)) { if (tmax <= tmin) @@ -1927,46 +1976,48 @@ void ShowAxisContextMenu(ImPlotAxisState& state, bool time_allowed) { } ImGui::EndMenu(); } - EndDisabledControls(state.LockMax); + EndDisabledControls(axis.IsLockedMax()); } else { - BeginDisabledControls(total_lock); - if (ImGui::Checkbox("##LockMin", &state.LockMin)) - ImFlipFlag(axis.Flags, ImPlotAxisFlags_LockMin); - EndDisabledControls(total_lock); + BeginDisabledControls(always_locked); + ImGui::CheckboxFlags("##LockMin", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMin); + EndDisabledControls(always_locked); ImGui::SameLine(); - BeginDisabledControls(state.LockMin); + BeginDisabledControls(axis.IsLockedMin()); double temp_min = axis.Range.Min; - if (DragFloat("Min", &temp_min, (float)drag_speed, -HUGE_VAL, axis.Range.Max - DBL_EPSILON)) + if (DragFloat("Min", &temp_min, (float)drag_speed, -HUGE_VAL, axis.Range.Max - DBL_EPSILON)) { axis.SetMin(temp_min); - EndDisabledControls(state.LockMin); + if (equal_axis != NULL) + equal_axis->SetAspect(axis.GetAspect()); + } + EndDisabledControls(axis.IsLockedMin()); - BeginDisabledControls(total_lock); - if (ImGui::Checkbox("##LockMax", &state.LockMax)) - ImFlipFlag(axis.Flags, ImPlotAxisFlags_LockMax); - EndDisabledControls(total_lock); + BeginDisabledControls(always_locked); + ImGui::CheckboxFlags("##LockMax", (unsigned int*)&axis.Flags, ImPlotAxisFlags_LockMax); + EndDisabledControls(always_locked); ImGui::SameLine(); - BeginDisabledControls(state.LockMax); + BeginDisabledControls(axis.IsLockedMax()); double temp_max = axis.Range.Max; - if (DragFloat("Max", &temp_max, (float)drag_speed, axis.Range.Min + DBL_EPSILON, HUGE_VAL)) + if (DragFloat("Max", &temp_max, (float)drag_speed, axis.Range.Min + DBL_EPSILON, HUGE_VAL)) { axis.SetMax(temp_max); - EndDisabledControls(state.LockMax); + if (equal_axis != NULL) + equal_axis->SetAspect(axis.GetAspect()); + } + EndDisabledControls(axis.IsLockedMax()); } ImGui::Separator(); - if (ImGui::Checkbox("Invert", &state.Invert)) - ImFlipFlag(axis.Flags, ImPlotAxisFlags_Invert); - BeginDisabledControls(timescale && time_allowed); - if (ImGui::Checkbox("Log Scale", &logscale)) - ImFlipFlag(axis.Flags, ImPlotAxisFlags_LogScale); - EndDisabledControls(timescale && time_allowed); + + ImGui::CheckboxFlags("Invert",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Invert); + BeginDisabledControls(axis.IsTime() && time_allowed); + ImGui::CheckboxFlags("Log Scale",(unsigned int*)&axis.Flags, ImPlotAxisFlags_LogScale); + EndDisabledControls(axis.IsTime() && time_allowed); if (time_allowed) { - BeginDisabledControls(logscale); - if (ImGui::Checkbox("Time", ×cale)) - ImFlipFlag(axis.Flags, ImPlotAxisFlags_Time); - EndDisabledControls(logscale); + BeginDisabledControls(axis.IsLog()); + ImGui::CheckboxFlags("Time",(unsigned int*)&axis.Flags, ImPlotAxisFlags_Time); + EndDisabledControls(axis.IsLog()); } ImGui::Separator(); @@ -1976,14 +2027,13 @@ void ShowAxisContextMenu(ImPlotAxisState& state, bool time_allowed) { ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickMarks); if (ImGui::Checkbox("Labels", &labels)) ImFlipFlag(axis.Flags, ImPlotAxisFlags_NoTickLabels); - } void ShowPlotContextMenu(ImPlotPlot& plot) { - ImPlotContext& gp = *GImPlot; + const bool equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); if (ImGui::BeginMenu("X-Axis")) { ImGui::PushID("X"); - ShowAxisContextMenu(gp.X, true); + ShowAxisContextMenu(plot.XAxis, equal ? &plot.YAxis[0] : NULL, true); ImGui::PopID(); ImGui::EndMenu(); } @@ -2002,7 +2052,7 @@ void ShowPlotContextMenu(ImPlotPlot& plot) { } if (ImGui::BeginMenu(buf)) { ImGui::PushID(i); - ShowAxisContextMenu(gp.Y[i], false); + ShowAxisContextMenu(plot.YAxis[i], (equal && i == 0) ? &plot.XAxis : NULL, false); ImGui::PopID(); ImGui::EndMenu(); } @@ -2010,8 +2060,20 @@ void ShowPlotContextMenu(ImPlotPlot& plot) { ImGui::Separator(); if ((ImGui::BeginMenu("Settings"))) { + if (ImGui::MenuItem("Anti-Aliased Lines",NULL,ImHasFlag(plot.Flags, ImPlotFlags_AntiAliased))) + ImFlipFlag(plot.Flags, ImPlotFlags_AntiAliased); + if (ImGui::MenuItem("Equal", NULL, ImHasFlag(plot.Flags, ImPlotFlags_Equal))) + ImFlipFlag(plot.Flags, ImPlotFlags_Equal); + if (ImGui::MenuItem("Box Select",NULL,!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect))) + ImFlipFlag(plot.Flags, ImPlotFlags_NoBoxSelect); + if (ImGui::MenuItem("Query",NULL,ImHasFlag(plot.Flags, ImPlotFlags_Query))) + ImFlipFlag(plot.Flags, ImPlotFlags_Query); if (ImGui::MenuItem("Title",NULL,!ImHasFlag(plot.Flags, ImPlotFlags_NoTitle))) - ImFlipFlag(plot.Flags, ImPlotFlags_NoTitle); + ImFlipFlag(plot.Flags, ImPlotFlags_NoTitle); + if (ImGui::MenuItem("Mouse Position",NULL,!ImHasFlag(plot.Flags, ImPlotFlags_NoMousePos))) + ImFlipFlag(plot.Flags, ImPlotFlags_NoMousePos); + if (ImGui::MenuItem("Crosshairs",NULL,ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs))) + ImFlipFlag(plot.Flags, ImPlotFlags_Crosshairs); if ((ImGui::BeginMenu("Legend"))) { const float s = ImGui::GetFrameHeight(); if (ImGui::RadioButton("H", plot.LegendOrientation == ImPlotOrientation_Horizontal)) @@ -2033,41 +2095,11 @@ void ShowPlotContextMenu(ImPlotPlot& plot) { ImGui::PopStyleVar(); ImGui::EndMenu(); } - if (ImGui::MenuItem("Mouse Position",NULL,!ImHasFlag(plot.Flags, ImPlotFlags_NoMousePos))) - ImFlipFlag(plot.Flags, ImPlotFlags_NoMousePos); - if (ImGui::MenuItem("Crosshairs",NULL,ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs))) - ImFlipFlag(plot.Flags, ImPlotFlags_Crosshairs); - if (ImGui::MenuItem("Box Select",NULL,!ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect))) - ImFlipFlag(plot.Flags, ImPlotFlags_NoBoxSelect); - if (ImGui::MenuItem("Query",NULL,ImHasFlag(plot.Flags, ImPlotFlags_Query))) - ImFlipFlag(plot.Flags, ImPlotFlags_Query); - if (ImGui::MenuItem("Anti-Aliased Lines",NULL,ImHasFlag(plot.Flags, ImPlotFlags_AntiAliased))) - ImFlipFlag(plot.Flags, ImPlotFlags_AntiAliased); ImGui::EndMenu(); } if (ImGui::MenuItem("Legend",NULL,!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend))) { ImFlipFlag(plot.Flags, ImPlotFlags_NoLegend); } -#ifdef IMPLOT_DEBUG - if (ImGui::BeginMenu("Debug")) { - ImGui::PushItemWidth(50); - ImGui::LabelText("Plots", "%d", gp.Plots.GetSize()); - ImGui::LabelText("Color Mods", "%d", gp.ColorModifiers.size()); - ImGui::LabelText("Style Mods", "%d", gp.StyleModifiers.size()); - bool f = false; - ImGui::Selectable("BB_Frame",&f); if (ImGui::IsItemHovered()) ImGui::GetForegroundDrawList()->AddRect(gp.BB_Frame.Min, gp.BB_Frame.Max, IM_COL32(255,255,0,255)); - ImGui::Selectable("BB_Canvas",&f); if (ImGui::IsItemHovered()) ImGui::GetForegroundDrawList()->AddRect(gp.BB_Canvas.Min, gp.BB_Canvas.Max, IM_COL32(255,255,0,255)); - ImGui::Selectable("BB_Plot",&f); if (ImGui::IsItemHovered()) ImGui::GetForegroundDrawList()->AddRect(gp.BB_Plot.Min, gp.BB_Plot.Max, IM_COL32(255,255,0,255)); - ImGui::Selectable("BB_Axes",&f); if (ImGui::IsItemHovered()) ImGui::GetForegroundDrawList()->AddRect(gp.BB_Axes.Min, gp.BB_Axes.Max, IM_COL32(255,255,0,255)); - ImGui::Selectable("BB_X",&f); if (ImGui::IsItemHovered()) ImGui::GetForegroundDrawList()->AddRect(gp.BB_X.Min, gp.BB_X.Max, IM_COL32(255,255,0,255)); - ImGui::Selectable("BB_Y[0]",&f); if (ImGui::IsItemHovered()) ImGui::GetForegroundDrawList()->AddRect(gp.BB_Y[0].Min, gp.BB_Y[0].Max, IM_COL32(255,255,0,255)); - ImGui::Selectable("BB_Y[1]",&f); if (ImGui::IsItemHovered()) ImGui::GetForegroundDrawList()->AddRect(gp.BB_Y[1].Min, gp.BB_Y[1].Max, IM_COL32(255,255,0,255)); - ImGui::Selectable("BB_Y[2]",&f); if (ImGui::IsItemHovered()) ImGui::GetForegroundDrawList()->AddRect(gp.BB_Y[2].Min, gp.BB_Y[2].Max, IM_COL32(255,255,0,255)); - ImGui::PopItemWidth(); - ImGui::EndMenu(); - } -#endif - } //----------------------------------------------------------------------------- @@ -2086,7 +2118,7 @@ void EndPlot() { // AXIS STATES ------------------------------------------------------------ - const bool any_y_locked = gp.Y[0].Lock || gp.Y[1].Present ? gp.Y[1].Lock : false || gp.Y[2].Present ? gp.Y[2].Lock : false; + const bool any_y_locked = plot.YAxis[0].IsLocked() || plot.YAxis[1].Present ? plot.YAxis[1].IsLocked() : false || plot.YAxis[2].Present ? plot.YAxis[2].IsLocked() : false; const bool any_y_dragging = plot.YAxis[0].Dragging || plot.YAxis[1].Dragging || plot.YAxis[2].Dragging; @@ -2098,18 +2130,18 @@ void EndPlot() { for (int t = 0; t < gp.XTicks.Size; t++) { ImPlotTick *xt = &gp.XTicks.Ticks[t]; if (xt->Level == 0) - DrawList.AddLine(ImVec2(xt->PixelPos, gp.BB_Plot.Max.y), - ImVec2(xt->PixelPos, gp.BB_Plot.Max.y - (xt->Major ? gp.Style.MajorTickLen.x : gp.Style.MinorTickLen.x)), - gp.Col_X.Major, + DrawList.AddLine(ImVec2(xt->PixelPos, plot.PlotRect.Max.y), + ImVec2(xt->PixelPos, plot.PlotRect.Max.y - (xt->Major ? gp.Style.MajorTickLen.x : gp.Style.MinorTickLen.x)), + plot.XAxis.ColorMaj, xt->Major ? gp.Style.MajorTickSize.x : gp.Style.MinorTickSize.x); } } PopPlotClipRect(); - ImGui::PushClipRect(gp.BB_Plot.Min, ImVec2(gp.BB_Frame.Max.x, gp.BB_Plot.Max.y), true); + ImGui::PushClipRect(plot.PlotRect.Min, ImVec2(plot.FrameRect.Max.x, plot.PlotRect.Max.y), true); int axis_count = 0; for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (!gp.Y[i].Present) { continue; } + if (!plot.YAxis[i].Present) { continue; } axis_count++; float x_start = gp.YAxisReference[i]; @@ -2121,7 +2153,7 @@ void EndPlot() { ImVec2 start = ImVec2(x_start, yt->PixelPos); DrawList.AddLine(start, start + ImVec2(direction * ((!no_major && yt->Major) ? gp.Style.MajorTickLen.y : gp.Style.MinorTickLen.y), 0), - gp.Col_Y[i].Major, + plot.YAxis[i].ColorMaj, (!no_major && yt->Major) ? gp.Style.MajorTickSize.y : gp.Style.MinorTickSize.y); } } @@ -2129,8 +2161,8 @@ void EndPlot() { if (axis_count >= 3) { // Draw a bar next to the ticks to act as a visual separator. DrawList.AddLine( - ImVec2(x_start, gp.BB_Plot.Min.y), - ImVec2(x_start, gp.BB_Plot.Max.y), + ImVec2(x_start, plot.PlotRect.Min.y), + ImVec2(x_start, plot.PlotRect.Max.y), GetStyleColorU32(ImPlotCol_YAxisGrid3), 1); } } @@ -2157,7 +2189,7 @@ void EndPlot() { else pos.y -= size.y - an.Offset.y; if (an.Clamp) - pos = ClampLabelPos(pos, size, gp.BB_Plot.Min, gp.BB_Plot.Max); + pos = ClampLabelPos(pos, size, plot.PlotRect.Min, plot.PlotRect.Max); ImRect rect(pos,pos+size); if (an.Offset.x != 0 || an.Offset.y != 0) { ImVec2 corners[4] = {rect.GetTL(), rect.GetTR(), rect.GetBR(), rect.GetBL()}; @@ -2178,12 +2210,12 @@ void EndPlot() { PopPlotClipRect(); // render y-axis drag/drop hover - if ((gp.Y[1].Present || gp.Y[2].Present) && ImGui::IsDragDropPayloadBeingAccepted()) { + if ((plot.YAxis[1].Present || plot.YAxis[2].Present) && ImGui::IsDragDropPayloadBeingAccepted()) { for (int i = 0; i < IMPLOT_Y_AXES; ++i) { - if (plot.YAxis[i].HoveredExt) { + if (plot.YAxis[i].ExtHovered) { float x_loc = gp.YAxisReference[i]; - ImVec2 p1(x_loc - 5, gp.BB_Plot.Min.y - 5); - ImVec2 p2(x_loc + 5, gp.BB_Plot.Max.y + 5); + ImVec2 p1(x_loc - 5, plot.PlotRect.Min.y - 5); + ImVec2 p2(x_loc + 5, plot.PlotRect.Max.y + 5); DrawList.AddRect(p1, p2, ImGui::GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ImDrawCornerFlags_All, 2.0f); } } @@ -2196,21 +2228,21 @@ void EndPlot() { const bool wide_enough = ImFabs(select_bb.GetWidth()) > 2; const bool tall_enough = ImFabs(select_bb.GetHeight()) > 2; const bool big_enough = wide_enough && tall_enough; - if (plot.Selecting && !gp.LockPlot && !ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect)) { + if (plot.Selecting && !plot.IsLocked() && !ImHasFlag(plot.Flags, ImPlotFlags_NoBoxSelect)) { const ImVec4 col = GetStyleColorVec4(ImPlotCol_Selection); const ImU32 col_bg = ImGui::GetColorU32(col * ImVec4(1,1,1,0.25f)); const ImU32 col_bd = ImGui::GetColorU32(col); if (IO.KeyMods == (gp.InputMap.HorizontalMod | gp.InputMap.VerticalMod) && big_enough) { - DrawList.AddRectFilled(gp.BB_Plot.Min, gp.BB_Plot.Max, col_bg); - DrawList.AddRect( gp.BB_Plot.Min, gp.BB_Plot.Max, col_bd); + DrawList.AddRectFilled(plot.PlotRect.Min, plot.PlotRect.Max, col_bg); + DrawList.AddRect( plot.PlotRect.Min, plot.PlotRect.Max, col_bd); } - else if ((gp.X.Lock || IO.KeyMods == gp.InputMap.HorizontalMod) && tall_enough) { - DrawList.AddRectFilled(ImVec2(gp.BB_Plot.Min.x, select_bb.Min.y), ImVec2(gp.BB_Plot.Max.x, select_bb.Max.y), col_bg); - DrawList.AddRect( ImVec2(gp.BB_Plot.Min.x, select_bb.Min.y), ImVec2(gp.BB_Plot.Max.x, select_bb.Max.y), col_bd); + else if ((plot.XAxis.IsLocked() || IO.KeyMods == gp.InputMap.HorizontalMod) && tall_enough) { + DrawList.AddRectFilled(ImVec2(plot.PlotRect.Min.x, select_bb.Min.y), ImVec2(plot.PlotRect.Max.x, select_bb.Max.y), col_bg); + DrawList.AddRect( ImVec2(plot.PlotRect.Min.x, select_bb.Min.y), ImVec2(plot.PlotRect.Max.x, select_bb.Max.y), col_bd); } else if ((any_y_locked || IO.KeyMods == gp.InputMap.VerticalMod) && wide_enough) { - DrawList.AddRectFilled(ImVec2(select_bb.Min.x, gp.BB_Plot.Min.y), ImVec2(select_bb.Max.x, gp.BB_Plot.Max.y), col_bg); - DrawList.AddRect( ImVec2(select_bb.Min.x, gp.BB_Plot.Min.y), ImVec2(select_bb.Max.x, gp.BB_Plot.Max.y), col_bd); + DrawList.AddRectFilled(ImVec2(select_bb.Min.x, plot.PlotRect.Min.y), ImVec2(select_bb.Max.x, plot.PlotRect.Max.y), col_bg); + DrawList.AddRect( ImVec2(select_bb.Min.x, plot.PlotRect.Min.y), ImVec2(select_bb.Max.x, plot.PlotRect.Max.y), col_bd); } else if (big_enough) { DrawList.AddRectFilled(select_bb.Min, select_bb.Max, col_bg); @@ -2226,32 +2258,32 @@ void EndPlot() { const ImU32 col_bg = ImGui::GetColorU32(col); if (plot.Querying || plot.Queried) { if (plot.QueryRect.GetWidth() > 2 && plot.QueryRect.GetHeight() > 2) { - DrawList.AddRectFilled(plot.QueryRect.Min + gp.BB_Plot.Min, plot.QueryRect.Max + gp.BB_Plot.Min, col_bd); - DrawList.AddRect( plot.QueryRect.Min + gp.BB_Plot.Min, plot.QueryRect.Max + gp.BB_Plot.Min, col_bg); + DrawList.AddRectFilled(plot.QueryRect.Min + plot.PlotRect.Min, plot.QueryRect.Max + plot.PlotRect.Min, col_bd); + DrawList.AddRect( plot.QueryRect.Min + plot.PlotRect.Min, plot.QueryRect.Max + plot.PlotRect.Min, col_bg); } } else if (plot.Queried) { ImRect bb_query = plot.QueryRect; - bb_query.Min += gp.BB_Plot.Min; - bb_query.Max += gp.BB_Plot.Min; + bb_query.Min += plot.PlotRect.Min; + bb_query.Max += plot.PlotRect.Min; DrawList.AddRectFilled(bb_query.Min, bb_query.Max, col_bd); DrawList.AddRect( bb_query.Min, bb_query.Max, col_bg); } } // render crosshairs - if (ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs) && gp.Hov_Plot && gp.Hov_Frame && + if (ImHasFlag(plot.Flags, ImPlotFlags_Crosshairs) && plot.PlotHovered && plot.FrameHovered && !(plot.XAxis.Dragging || any_y_dragging) && !plot.Selecting && !plot.Querying && !plot.LegendHovered) { ImGui::SetMouseCursor(ImGuiMouseCursor_None); ImVec2 xy = IO.MousePos; - ImVec2 h1(gp.BB_Plot.Min.x, xy.y); + ImVec2 h1(plot.PlotRect.Min.x, xy.y); ImVec2 h2(xy.x - 5, xy.y); ImVec2 h3(xy.x + 5, xy.y); - ImVec2 h4(gp.BB_Plot.Max.x, xy.y); - ImVec2 v1(xy.x, gp.BB_Plot.Min.y); + ImVec2 h4(plot.PlotRect.Max.x, xy.y); + ImVec2 v1(xy.x, plot.PlotRect.Min.y); ImVec2 v2(xy.x, xy.y - 5); ImVec2 v3(xy.x, xy.y + 5); - ImVec2 v4(xy.x, gp.BB_Plot.Max.y); + ImVec2 v4(xy.x, plot.PlotRect.Max.y); ImU32 col = GetStyleColorU32(ImPlotCol_Crosshairs); DrawList.AddLine(h1, h2, col); DrawList.AddLine(h3, h4, col); @@ -2260,7 +2292,7 @@ void EndPlot() { } // render mouse pos (TODO: use LabelAxisValue) - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMousePos) && gp.Hov_Plot) { + if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMousePos) && plot.PlotHovered) { char buffer[128] = {}; ImBufferWriter writer(buffer, sizeof(buffer)); @@ -2269,7 +2301,7 @@ void EndPlot() { writer.Write("%.3E", gp.MousePos[0].x); } else if (ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_Time)) { - ImPlotTimeUnit unit = GetUnitForRange(plot.XAxis.Range.Size() / (gp.BB_Plot.GetWidth() / 100)); + ImPlotTimeUnit unit = GetUnitForRange(plot.XAxis.Range.Size() / (plot.PlotRect.GetWidth() / 100)); const int written = FormatDateTime(ImPlotTime::FromDouble(gp.MousePos[0].x), &writer.Buffer[writer.Pos], writer.Size - writer.Pos - 1, GetDateTimeFmt(TimeFormatMouseCursor, unit)); if (written > 0) writer.Pos += ImMin(written, writer.Size - writer.Pos - 1); @@ -2307,7 +2339,7 @@ void EndPlot() { } } const ImVec2 size = ImGui::CalcTextSize(buffer); - const ImVec2 pos = GetLocationPos(gp.BB_Plot, size, plot.MousePosLocation, gp.Style.MousePosPadding); + const ImVec2 pos = GetLocationPos(plot.PlotRect, size, plot.MousePosLocation, gp.Style.MousePosPadding); DrawList.AddText(pos, GetStyleColorU32(ImPlotCol_InlayText), buffer); } PopPlotClipRect(); @@ -2319,16 +2351,16 @@ void EndPlot() { // render legend if (!ImHasFlag(plot.Flags, ImPlotFlags_NoLegend) && plot.GetLegendCount() > 0) { const ImVec2 legend_size = CalcLegendSize(plot, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, plot.LegendOrientation); - const ImVec2 legend_pos = GetLocationPos(plot.LegendOutside ? gp.BB_Frame : gp.BB_Plot, + const ImVec2 legend_pos = GetLocationPos(plot.LegendOutside ? plot.FrameRect : plot.PlotRect, legend_size, plot.LegendLocation, plot.LegendOutside ? gp.Style.PlotPadding : gp.Style.LegendPadding); const ImRect legend_bb(legend_pos, legend_pos + legend_size); // test hover - plot.LegendHovered = gp.Hov_Frame && legend_bb.Contains(IO.MousePos); + plot.LegendHovered = plot.FrameHovered && legend_bb.Contains(IO.MousePos); if (plot.LegendOutside) - ImGui::PushClipRect(gp.BB_Frame.Min, gp.BB_Frame.Max, true); + ImGui::PushClipRect(plot.FrameRect.Min, plot.FrameRect.Max, true); else PushPlotClipRect(); ImU32 col_bg = GetStyleColorU32(ImPlotCol_LegendBg); @@ -2338,64 +2370,73 @@ void EndPlot() { ShowLegendEntries(plot, legend_bb, plot.LegendHovered, gp.Style.LegendInnerPadding, gp.Style.LegendSpacing, plot.LegendOrientation, DrawList); ImGui::PopClipRect(); } - if (plot.LegendFlipSide) { + if (plot.LegendFlipSideNextFrame) { plot.LegendOutside = !plot.LegendOutside; - plot.LegendFlipSide = false; + plot.LegendFlipSideNextFrame = false; } // render border if (gp.Style.PlotBorderSize > 0) - DrawList.AddRect(gp.BB_Plot.Min, gp.BB_Plot.Max, GetStyleColorU32(ImPlotCol_PlotBorder), 0, ImDrawCornerFlags_All, gp.Style.PlotBorderSize); + DrawList.AddRect(plot.PlotRect.Min, plot.PlotRect.Max, GetStyleColorU32(ImPlotCol_PlotBorder), 0, ImDrawCornerFlags_All, gp.Style.PlotBorderSize); // FIT DATA -------------------------------------------------------------- - + const bool axis_equal = ImHasFlag(plot.Flags, ImPlotFlags_Equal); if (gp.FitThisFrame && (gp.VisibleItemCount > 0 || plot.Queried)) { - if (gp.FitX && !ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LockMin) && !ImNanOrInf(gp.ExtentsX.Min)) { - plot.XAxis.Range.Min = (gp.ExtentsX.Min); + if (gp.FitX) { + if (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LockMin) && !ImNanOrInf(gp.ExtentsX.Min)) + plot.XAxis.Range.Min = (gp.ExtentsX.Min); + if (!ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LockMax) && !ImNanOrInf(gp.ExtentsX.Max)) + plot.XAxis.Range.Max = (gp.ExtentsX.Max); + if (ImAlmostEqual(plot.XAxis.Range.Max, plot.XAxis.Range.Min)) { + plot.XAxis.Range.Max += plot.XAxis.Range.Max * 1.01; + plot.XAxis.Range.Min -= plot.XAxis.Range.Max * 1.01; + } + plot.XAxis.Constrain(); + if (axis_equal && !gp.FitY[0]) + plot.YAxis[0].SetAspect(plot.XAxis.GetAspect()); } - if (gp.FitX && !ImHasFlag(plot.XAxis.Flags, ImPlotAxisFlags_LockMax) && !ImNanOrInf(gp.ExtentsX.Max)) { - plot.XAxis.Range.Max = (gp.ExtentsX.Max); - } - if ((plot.XAxis.Range.Max - plot.XAxis.Range.Min) <= (2.0 * FLT_EPSILON)) { - plot.XAxis.Range.Max += FLT_EPSILON; - plot.XAxis.Range.Min -= FLT_EPSILON; - } - plot.XAxis.Constrain(); for (int i = 0; i < IMPLOT_Y_AXES; i++) { - if (gp.FitY[i] && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LockMin) && !ImNanOrInf(gp.ExtentsY[i].Min)) { - plot.YAxis[i].Range.Min = (gp.ExtentsY[i].Min); + if (gp.FitY[i]) { + if (!ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LockMin) && !ImNanOrInf(gp.ExtentsY[i].Min)) + plot.YAxis[i].Range.Min = (gp.ExtentsY[i].Min); + if (!ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LockMax) && !ImNanOrInf(gp.ExtentsY[i].Max)) + plot.YAxis[i].Range.Max = (gp.ExtentsY[i].Max); + if (ImAlmostEqual(plot.YAxis[i].Range.Max, plot.YAxis[i].Range.Min)) { + plot.YAxis[i].Range.Max += plot.YAxis[i].Range.Max * 1.01; + plot.YAxis[i].Range.Min -= plot.YAxis[i].Range.Max * 1.01; + } + plot.YAxis[i].Constrain(); + if (i == 0 && axis_equal && !gp.FitX) + plot.XAxis.SetAspect(plot.YAxis[0].GetAspect()); } - if (gp.FitY[i] && !ImHasFlag(plot.YAxis[i].Flags, ImPlotAxisFlags_LockMax) && !ImNanOrInf(gp.ExtentsY[i].Max)) { - plot.YAxis[i].Range.Max = (gp.ExtentsY[i].Max); - } - if ((plot.YAxis[i].Range.Max - plot.YAxis[i].Range.Min) <= (2.0 * FLT_EPSILON)) { - plot.YAxis[i].Range.Max += FLT_EPSILON; - plot.YAxis[i].Range.Min -= FLT_EPSILON; - } - plot.YAxis[i].Constrain(); + } + if (axis_equal && gp.FitX && gp.FitY[0]) { + double aspect = ImMax(plot.XAxis.GetAspect(), plot.YAxis[0].GetAspect()); + plot.XAxis.SetAspect(aspect); + plot.YAxis[0].SetAspect(aspect); } } // CONTEXT MENUS ----------------------------------------------------------- - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && gp.Hov_Frame && gp.Hov_Plot && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !plot.LegendHovered) + if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && plot.FrameHovered && plot.PlotHovered && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !plot.LegendHovered) ImGui::OpenPopup("##PlotContext"); if (ImGui::BeginPopup("##PlotContext")) { ShowPlotContextMenu(plot); ImGui::EndPopup(); } - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && gp.Hov_Frame && plot.XAxis.HoveredExt && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !plot.LegendHovered) + if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && plot.FrameHovered && plot.XAxis.ExtHovered && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !plot.LegendHovered) ImGui::OpenPopup("##XContext"); if (ImGui::BeginPopup("##XContext")) { ImGui::Text("X-Axis"); ImGui::Separator(); - ShowAxisContextMenu(gp.X, true); + ShowAxisContextMenu(plot.XAxis, ImHasFlag(plot.Flags, ImPlotFlags_Equal) ? &plot.YAxis[0] : NULL, true); ImGui::EndPopup(); } for (int i = 0; i < IMPLOT_Y_AXES; ++i) { ImGui::PushID(i); - if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && gp.Hov_Frame && plot.YAxis[i].HoveredExt && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !plot.LegendHovered) + if (!ImHasFlag(plot.Flags, ImPlotFlags_NoMenus) && plot.FrameHovered && plot.YAxis[i].ExtHovered && IO.MouseDoubleClicked[gp.InputMap.ContextMenuButton] && !plot.LegendHovered) ImGui::OpenPopup("##YContext"); if (ImGui::BeginPopup("##YContext")) { if (i == 0) { @@ -2404,7 +2445,7 @@ void EndPlot() { else { ImGui::Text("Y-Axis %d", i + 1); ImGui::Separator(); } - ShowAxisContextMenu(gp.Y[i], false); + ShowAxisContextMenu(plot.YAxis[i], (i == 0 && ImHasFlag(plot.Flags, ImPlotFlags_Equal)) ? &plot.XAxis : NULL, false); ImGui::EndPopup(); } ImGui::PopID(); @@ -2525,13 +2566,13 @@ void SetPlotYAxis(ImPlotYAxis y_axis) { ImVec2 GetPlotPos() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotPos() needs to be called between BeginPlot() and EndPlot()!"); - return gp.BB_Plot.Min; + return gp.CurrentPlot->PlotRect.Min; } ImVec2 GetPlotSize() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "GetPlotSize() needs to be called between BeginPlot() and EndPlot()!"); - return gp.BB_Plot.GetSize(); + return gp.CurrentPlot->PlotRect.GetSize(); } ImDrawList* GetPlotDrawList() { @@ -2541,7 +2582,7 @@ ImDrawList* GetPlotDrawList() { void PushPlotClipRect() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "PushPlotClipRect() needs to be called between BeginPlot() and EndPlot()!"); - ImGui::PushClipRect(gp.BB_Plot.Min, gp.BB_Plot.Max, true); + ImGui::PushClipRect(gp.CurrentPlot->PlotRect.Min, gp.CurrentPlot->PlotRect.Max, true); } void PopPlotClipRect() { @@ -2551,13 +2592,13 @@ void PopPlotClipRect() { bool IsPlotHovered() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotHovered() needs to be called between BeginPlot() and EndPlot()!"); - return gp.Hov_Frame && gp.Hov_Plot; + return gp.CurrentPlot->FrameHovered && gp.CurrentPlot->PlotHovered; } bool IsPlotXAxisHovered() { ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotXAxisHovered() needs to be called between BeginPlot() and EndPlot()!"); - return gp.CurrentPlot->XAxis.HoveredExt; + return gp.CurrentPlot->XAxis.ExtHovered; } bool IsPlotYAxisHovered(ImPlotYAxis y_axis_in) { @@ -2565,7 +2606,7 @@ bool IsPlotYAxisHovered(ImPlotYAxis y_axis_in) { IM_ASSERT_USER_ERROR(y_axis_in >= -1 && y_axis_in < IMPLOT_Y_AXES, "y_axis needs to between -1 and IMPLOT_Y_AXES"); IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "IsPlotYAxisHovered() needs to be called between BeginPlot() and EndPlot()!"); const ImPlotYAxis y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; - return gp.CurrentPlot->YAxis[y_axis].HoveredExt; + return gp.CurrentPlot->YAxis[y_axis].ExtHovered; } ImPlotPoint GetPlotMousePos(ImPlotYAxis y_axis_in) { @@ -2604,8 +2645,8 @@ ImPlotLimits GetPlotQuery(ImPlotYAxis y_axis_in) { const ImPlotYAxis y_axis = y_axis_in >= 0 ? y_axis_in : gp.CurrentPlot->CurrentYAxis; UpdateTransformCache(); - ImPlotPoint p1 = PixelsToPlot(plot.QueryRect.Min + gp.BB_Plot.Min, y_axis); - ImPlotPoint p2 = PixelsToPlot(plot.QueryRect.Max + gp.BB_Plot.Min, y_axis); + ImPlotPoint p1 = PixelsToPlot(plot.QueryRect.Min + plot.PlotRect.Min, y_axis); + ImPlotPoint p2 = PixelsToPlot(plot.QueryRect.Max + plot.PlotRect.Min, y_axis); ImPlotLimits result; result.X.Min = ImMin(p1.x, p2.x); @@ -2672,10 +2713,10 @@ bool DragLineX(const char* id, double* value, bool show_label, const ImVec4& col ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "DragLineX() needs to be called between BeginPlot() and EndPlot()!"); const float grab_size = ImMax(5.0f, thickness); - float yt = gp.BB_Plot.Min.y; - float yb = gp.BB_Plot.Max.y; + float yt = gp.CurrentPlot->PlotRect.Min.y; + float yb = gp.CurrentPlot->PlotRect.Max.y; float x = IM_ROUND(PlotToPixels(*value,0).x); - const bool outside = x < (gp.BB_Plot.Min.x - grab_size / 2) || x > (gp.BB_Plot.Max.x + grab_size / 2); + const bool outside = x < (gp.CurrentPlot->PlotRect.Min.x - grab_size / 2) || x > (gp.CurrentPlot->PlotRect.Max.x + grab_size / 2); if (outside) return false; float len = gp.Style.MajorTickLen.x; @@ -2695,7 +2736,7 @@ bool DragLineX(const char* id, double* value, bool show_label, const ImVec4& col ImGui::InvisibleButton(id, ImVec2(grab_size, yb-yt)); ImGui::GetCurrentWindow()->DC.CursorPos = old_cursor_pos; if (ImGui::IsItemHovered() || ImGui::IsItemActive()) { - gp.Hov_Plot = false; + gp.CurrentPlot->PlotHovered = false; ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); if (show_label) { char buff[32]; @@ -2706,7 +2747,7 @@ bool DragLineX(const char* id, double* value, bool show_label, const ImVec4& col bool dragging = false; if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) { *value = ImPlot::GetPlotMousePos().x; - *value = ImClamp(*value, gp.X.Axis->Range.Min, gp.X.Axis->Range.Max); + *value = ImClamp(*value, gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max); dragging = true; } return dragging; @@ -2716,10 +2757,10 @@ bool DragLineY(const char* id, double* value, bool show_label, const ImVec4& col ImPlotContext& gp = *GImPlot; IM_ASSERT_USER_ERROR(gp.CurrentPlot != NULL, "DragLineY() needs to be called between BeginPlot() and EndPlot()!"); const float grab_size = ImMax(5.0f, thickness); - float xl = gp.BB_Plot.Min.x; - float xr = gp.BB_Plot.Max.x; + float xl = gp.CurrentPlot->PlotRect.Min.x; + float xr = gp.CurrentPlot->PlotRect.Max.x; float y = IM_ROUND(PlotToPixels(0, *value).y); - const bool outside = y < (gp.BB_Plot.Min.y - grab_size / 2) || y > (gp.BB_Plot.Max.y + grab_size / 2); + const bool outside = y < (gp.CurrentPlot->PlotRect.Min.y - grab_size / 2) || y > (gp.CurrentPlot->PlotRect.Max.y + grab_size / 2); if (outside) return false; float len = gp.Style.MajorTickLen.y; @@ -2741,7 +2782,7 @@ bool DragLineY(const char* id, double* value, bool show_label, const ImVec4& col ImGui::GetCurrentWindow()->DC.CursorPos = old_cursor_pos; int yax = GetCurrentYAxis(); if (ImGui::IsItemHovered() || ImGui::IsItemActive()) { - gp.Hov_Plot = false; + gp.CurrentPlot->PlotHovered = false; ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); if (show_label) { char buff[32]; @@ -2752,7 +2793,7 @@ bool DragLineY(const char* id, double* value, bool show_label, const ImVec4& col bool dragging = false; if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) { *value = ImPlot::GetPlotMousePos().y; - *value = ImClamp(*value, gp.Y[yax].Axis->Range.Min, gp.Y[yax].Axis->Range.Max); + *value = ImClamp(*value, gp.CurrentPlot->YAxis[yax].Range.Min, gp.CurrentPlot->YAxis[yax].Range.Max); dragging = true; } return dragging; @@ -2779,7 +2820,7 @@ bool DragPoint(const char* id, double* x, double* y, bool show_label, const ImVe ImGui::InvisibleButton(id, ImVec2(grab_size, grab_size)); ImGui::GetCurrentWindow()->DC.CursorPos = old_cursor_pos; if (ImGui::IsItemHovered() || ImGui::IsItemActive()) { - gp.Hov_Plot = false; + gp.CurrentPlot->PlotHovered = false; ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); if (show_label) { ImVec2 label_pos = pos + ImVec2(16 * GImGui->Style.MouseCursorScale, 8 * GImGui->Style.MouseCursorScale); @@ -2794,8 +2835,8 @@ bool DragPoint(const char* id, double* x, double* y, bool show_label, const ImVe if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) { *x = ImPlot::GetPlotMousePos().x; *y = ImPlot::GetPlotMousePos().y; - *x = ImClamp(*x, gp.X.Axis->Range.Min, gp.X.Axis->Range.Max); - *y = ImClamp(*y, gp.Y[yax].Axis->Range.Min, gp.Y[yax].Axis->Range.Max); + *x = ImClamp(*x, gp.CurrentPlot->XAxis.Range.Min, gp.CurrentPlot->XAxis.Range.Max); + *y = ImClamp(*y, gp.CurrentPlot->YAxis[yax].Range.Min, gp.CurrentPlot->YAxis[yax].Range.Max); dragging = true; } return dragging; @@ -2807,7 +2848,7 @@ void SetLegendLocation(ImPlotLocation location, ImPlotOrientation orientation, b gp.CurrentPlot->LegendLocation = location; gp.CurrentPlot->LegendOrientation = orientation; if (gp.CurrentPlot->LegendOutside != outside) - gp.CurrentPlot->LegendFlipSide = true; + gp.CurrentPlot->LegendFlipSideNextFrame = true; } void SetMousePosLocation(ImPlotLocation location) { @@ -3654,18 +3695,47 @@ void ShowUserGuide() { ImGui::BulletText("Click legend label icons to show/hide plot items."); } +void ShowAxisMetrics(ImPlotAxis* axis, bool show_axis_rects) { + ImGui::Bullet(); ImGui::Text("Flags: %d", axis->Flags); + ImGui::Bullet(); ImGui::Text("Range: [%f,%f]",axis->Range.Min, axis->Range.Max); + ImGui::Bullet(); ImGui::Text("Pixels: %f", axis->Pixels); + ImGui::Bullet(); ImGui::Text("Aspect: %f", axis->GetAspect()); + ImGui::Bullet(); ImGui::Text("Dragging: %s", axis->Dragging ? "true" : "false"); + ImGui::Bullet(); ImGui::Text("ExtHovered: %s", axis->ExtHovered ? "true" : "false"); + ImGui::Bullet(); ImGui::Text("AllHovered: %s", axis->AllHovered ? "true" : "false"); + ImGui::Bullet(); ImGui::Text("Present: %s", axis->Present ? "true" : "false"); + ImGui::Bullet(); ImGui::Text("HasRange: %s", axis->HasRange ? "true" : "false"); + ImGui::Bullet(); ImGui::Text("LinkedMin: %p", axis->LinkedMin); + ImGui::Bullet(); ImGui::Text("LinkedMax: %p", axis->LinkedMax); + if (show_axis_rects) { + ImDrawList& fg = *ImGui::GetForegroundDrawList(); + fg.AddRect(axis->HoverRect.Min, axis->HoverRect.Max, IM_COL32(0,255,0,255)); + } +} + void ShowMetricsWindow(bool* p_popen) { + + static bool show_plot_rects = false; + static bool show_axes_rects = false; + + ImDrawList& fg = *ImGui::GetForegroundDrawList(); + ImPlotContext& gp = *GImPlot; // ImGuiContext& g = *GImGui; ImGuiIO& io = ImGui::GetIO(); ImGui::Begin("ImPlot Metrics", p_popen); ImGui::Text("ImPlot " IMPLOT_VERSION); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); - ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3); ImGui::Separator(); int n_plots = gp.Plots.GetSize(); + if (ImGui::TreeNode("Tools")) { + ImGui::Checkbox("Show Plot Rects", &show_plot_rects); + ImGui::Checkbox("Show Axes Rects", &show_axes_rects); + ImGui::TreePop(); + } if (ImGui::TreeNode("Plots","Plots (%d)", n_plots)) { for (int p = 0; p < n_plots; ++p) { + // plot ImPlotPlot* plot = gp.Plots.GetByIndex(p); ImGui::PushID(p); if (ImGui::TreeNode("Plot", "Plot [ID=%u]", plot->ID)) { @@ -3677,16 +3747,41 @@ void ShowMetricsWindow(bool* p_popen) { if (ImGui::TreeNode("Item", "Item [ID=%u]", item->ID)) { ImGui::Bullet(); ImGui::Checkbox("Show", &item->Show); ImGui::Bullet(); ImGui::ColorEdit4("Color",&item->Color.x, ImGuiColorEditFlags_NoInputs); - ImGui::Bullet(); ImGui::Value("NameOffset",item->NameOffset); - ImGui::Bullet(); ImGui::Text("Name: %s", item->NameOffset != -1 ? plot->LegendData.Labels.Buf.Data + item->NameOffset : "N/A"); - ImGui::Bullet(); ImGui::Value("Hovered",item->LegendHovered); + ImGui::Bullet(); ImGui::Text("NameOffset: %d",item->NameOffset); + ImGui::Bullet(); ImGui::Text("Name: %s", item->NameOffset != -1 ? plot->LegendData.Labels.Buf.Data + item->NameOffset : "N/A"); + ImGui::Bullet(); ImGui::Value("Hovered: %s",item->LegendHovered ? "true" : "false"); ImGui::TreePop(); } ImGui::PopID(); } ImGui::TreePop(); } + if (ImGui::TreeNode("X-Axis")) { + ShowAxisMetrics(&plot->XAxis, show_axes_rects); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Y-Axis")) { + ShowAxisMetrics(&plot->YAxis[0], show_axes_rects); + ImGui::TreePop(); + } + if (ImHasFlag(plot->Flags, ImPlotFlags_YAxis2) && ImGui::TreeNode("Y-Axis 2")) { + ShowAxisMetrics(&plot->YAxis[1], show_axes_rects); + ImGui::TreePop(); + } + if (ImHasFlag(plot->Flags, ImPlotFlags_YAxis3) && ImGui::TreeNode("Y-Axis 3")) { + ShowAxisMetrics(&plot->YAxis[2], show_axes_rects); + ImGui::TreePop(); + } + ImGui::Bullet(); ImGui::Text("Flags: %d", plot->Flags); + ImGui::Bullet(); ImGui::Text("Selecting: %s", plot->Selecting ? "true" : "false"); + ImGui::Bullet(); ImGui::Text("Querying: %s", plot->Querying ? "true" : "false"); + ImGui::Bullet(); ImGui::Text("Queried: %s", plot->Queried ? "true" : "false"); + ImGui::Bullet(); ImGui::Text("FrameHovered: %s", plot->FrameHovered ? "true" : "false"); + ImGui::Bullet(); ImGui::Text("PlotHovered: %s", plot->PlotHovered ? "true" : "false"); + ImGui::Bullet(); ImGui::Text("LegendHovered: %s", plot->LegendHovered ? "true" : "false"); ImGui::TreePop(); + if (show_plot_rects) + fg.AddRect(plot->PlotRect.Min, plot->PlotRect.Max, IM_COL32(255,255,0,255)); } ImGui::PopID(); } diff --git a/implot.h b/implot.h index 3015c79..6efd30b 100644 --- a/implot.h +++ b/implot.h @@ -72,11 +72,12 @@ enum ImPlotFlags_ { ImPlotFlags_NoMousePos = 1 << 4, // the mouse position, in plot coordinates, will not be displayed inside of the plot ImPlotFlags_NoHighlight = 1 << 5, // plot items will not be highlighted when their legend entry is hovered ImPlotFlags_NoChild = 1 << 6, // a child window region will not be used to capture mouse scroll (can boost performance for single ImGui window applications) - ImPlotFlags_YAxis2 = 1 << 7, // enable a 2nd y-axis on the right side - ImPlotFlags_YAxis3 = 1 << 8, // enable a 3rd y-axis on the right side - ImPlotFlags_Query = 1 << 9, // the user will be able to draw query rects with middle-mouse - ImPlotFlags_Crosshairs = 1 << 10, // the default mouse cursor will be replaced with a crosshair when hovered - ImPlotFlags_AntiAliased = 1 << 11, // plot lines will be software anti-aliased (not recommended for density plots, prefer MSAA) + ImPlotFlags_Equal = 1 << 7, // primary x and y axes will be constrained to have the same units/pixel (does not apply to auxiliary y axes) + ImPlotFlags_YAxis2 = 1 << 8, // enable a 2nd y-axis on the right side + ImPlotFlags_YAxis3 = 1 << 9, // enable a 3rd y-axis on the right side + ImPlotFlags_Query = 1 << 10, // the user will be able to draw query rects with middle-mouse + ImPlotFlags_Crosshairs = 1 << 11, // the default mouse cursor will be replaced with a crosshair when hovered + ImPlotFlags_AntiAliased = 1 << 12, // plot lines will be software anti-aliased (not recommended for density plots, prefer MSAA) ImPlotFlags_CanvasOnly = ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoMousePos }; diff --git a/implot_demo.cpp b/implot_demo.cpp index ffed0bf..042dabc 100644 --- a/implot_demo.cpp +++ b/implot_demo.cpp @@ -33,6 +33,10 @@ #define sprintf sprintf_s #endif +#ifndef PI +#define PI 3.14159265358979323846 +#endif + // Encapsulates examples for customizing ImPlot. namespace MyImPlot { @@ -447,7 +451,7 @@ void ShowDemoWindow(bool* p_open) { } ImPlot::SetNextPlotLimits(0,1,0,1,ImGuiCond_Always); - if (ImPlot::BeginPlot("##Pie1", NULL, NULL, ImVec2(250,250), ImPlotFlags_NoMousePos, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations)) { + if (ImPlot::BeginPlot("##Pie1", NULL, NULL, ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMousePos, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations)) { ImPlot::PlotPieChart(labels1, data1, 4, 0.5, 0.5, 0.4, normalize, "%.2f"); ImPlot::EndPlot(); } @@ -459,7 +463,7 @@ void ShowDemoWindow(bool* p_open) { ImPlot::PushColormap(ImPlotColormap_Pastel); ImPlot::SetNextPlotLimits(0,1,0,1,ImGuiCond_Always); - if (ImPlot::BeginPlot("##Pie2", NULL, NULL, ImVec2(250,250), ImPlotFlags_NoMousePos, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations)) { + if (ImPlot::BeginPlot("##Pie2", NULL, NULL, ImVec2(250,250), ImPlotFlags_Equal | ImPlotFlags_NoMousePos, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations)) { ImPlot::PlotPieChart(labels2, data2, 5, 0.5, 0.5, 0.4, true, "%.0f", 180); ImPlot::EndPlot(); } @@ -737,6 +741,19 @@ void ShowDemoWindow(bool* p_open) { } } //------------------------------------------------------------------------- + if (ImGui::CollapsingHeader("Equal Axes")) { + static double xs[1000], ys[1000]; + for (int i = 0; i < 1000; ++i) { + double angle = i * 2 * PI / 999.0; + xs[i] = cos(angle); ys[i] = sin(angle); + } + ImPlot::SetNextPlotLimits(-1,1,-1,1); + if (ImPlot::BeginPlot("",0,0,ImVec2(-1,0),ImPlotFlags_Equal)) { + ImPlot::PlotLine("Circle",xs,ys,1000); + ImPlot::EndPlot(); + } + } + //------------------------------------------------------------------------- if (ImGui::CollapsingHeader("Querying")) { static ImVector data; static ImPlotLimits range, query; @@ -839,7 +856,7 @@ void ShowDemoWindow(bool* p_open) { static MyImPlot::WaveData data3(0.001, 0.2, 6, 0.5); ImPlot::PlotLineG("Item 1", MyImPlot::SineWave, &data1, 1000); // "Item 1" added to legend ImPlot::PlotLineG("Item 2##IDText", MyImPlot::SawWave, &data2, 1000); // "Item 2" added to legend, text after ## used for ID only - ImPlot::PlotLineG("##NotDisplayed", MyImPlot::SawWave, &data3, 1000); // plotted, but not added to legend + ImPlot::PlotLineG("##NotListed", MyImPlot::SawWave, &data3, 1000); // plotted, but not added to legend ImPlot::PlotLineG("Item 3", MyImPlot::SineWave, &data1, 1000); // "Item 3" added to legend ImPlot::PlotLineG("Item 3", MyImPlot::SawWave, &data2, 1000); // combined with previous "Item 3" ImPlot::EndPlot(); @@ -1174,7 +1191,7 @@ void ShowDemoWindow(bool* p_open) { ImGui::BulletText("The offset value indicates which circle point index is considered the first."); ImGui::BulletText("Offsets can be negative and/or larger than the actual data count."); ImGui::SliderInt("Offset", &offset, -2*k_points_per, 2*k_points_per); - if (ImPlot::BeginPlot("##strideoffset")) { + if (ImPlot::BeginPlot("##strideoffset",0,0,ImVec2(-1,0), ImPlotFlags_Equal)) { ImPlot::PushColormap(ImPlotColormap_Jet); char buff[16]; for (int c = 0; c < k_circles; ++c) { diff --git a/implot_internal.h b/implot_internal.h index c7415f5..3336ab2 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -107,6 +107,8 @@ inline double ImConstrainInf(double val) { return val == HUGE_VAL ? DBL_MAX : v inline double ImConstrainLog(double val) { return val <= 0 ? 0.001f : val; } // Turns numbers less than 0 to zero inline double ImConstrainTime(double val) { return val < IMPLOT_MIN_TIME ? IMPLOT_MIN_TIME : (val > IMPLOT_MAX_TIME ? IMPLOT_MAX_TIME : val); } +// True if two numbers are approximately equal using units in the last place. +inline bool ImAlmostEqual(double v1, double v2, int ulp = 2) { return ImAbs(v1-v2) < DBL_EPSILON * ImAbs(v1+v2) * ulp || ImAbs(v1-v2) < DBL_MIN; } // Offset calculator helper template @@ -119,7 +121,7 @@ struct ImOffsetCalculator { int Offsets[Count]; }; -// Character buffer writer helper +// Character buffer writer helper (FIXME: Can't we replace this with ImGuiTextBuffer?) struct ImBufferWriter { char* Buffer; @@ -205,7 +207,7 @@ enum ImPlotTimeFmt_ { // default [ 24 Hour Clock ] // [SECTION] ImPlot Structs //----------------------------------------------------------------------------- -// Combined data/time format spec +// Combined date/time format spec struct ImPlotDateTimeFmt { ImPlotDateTimeFmt(ImPlotDateFmt date_fmt, ImPlotTimeFmt time_fmt, bool use_24_hr_clk = false, bool use_iso_8601 = false) { Date = date_fmt; @@ -379,27 +381,34 @@ struct ImPlotTickCollection { // Axis state information that must persist after EndPlot struct ImPlotAxis { - ImPlotAxisFlags Flags; - ImPlotAxisFlags PreviousFlags; - ImPlotRange Range; - ImPlotOrientation Direction; - bool Dragging; - bool HoveredExt; - bool HoveredTot; - double* LinkedMin; - double* LinkedMax; - ImPlotTime PickerTimeMin, PickerTimeMax; - int PickerLevel; + ImPlotAxisFlags Flags; + ImPlotAxisFlags PreviousFlags; + ImPlotRange Range; + float Pixels; + ImPlotOrientation Orientation; + bool Dragging; + bool ExtHovered; + bool AllHovered; + bool Present; + bool HasRange; + double* LinkedMin; + double* LinkedMax; + ImPlotTime PickerTimeMin, PickerTimeMax; + int PickerLevel; + ImU32 ColorMaj, ColorMin, ColorTxt; + ImGuiCond RangeCond; + ImRect HoverRect; ImPlotAxis() { - Flags = PreviousFlags = ImPlotAxisFlags_None; - Range.Min = 0; - Range.Max = 1; - Dragging = false; - HoveredExt = false; - HoveredTot = false; - LinkedMin = LinkedMax = NULL; + Flags = PreviousFlags = ImPlotAxisFlags_None; + Range.Min = 0; + Range.Max = 1; + Dragging = false; + ExtHovered = false; + AllHovered = false; + LinkedMin = LinkedMax = NULL; PickerLevel = 0; + ColorMaj = ColorMin = ColorTxt = 0; } bool SetMin(double _min) { @@ -440,6 +449,21 @@ struct ImPlotAxis SetRange(range.Min, range.Max); } + void SetAspect(double unit_per_pix) { + double new_size = unit_per_pix * Pixels; + double delta = (new_size - Range.Size()) * 0.5f; + if (IsLocked()) + return; + else if (IsLockedMin() && !IsLockedMax()) + SetRange(Range.Min, Range.Max + 2*delta); + else if (!IsLockedMin() && IsLockedMax()) + SetRange(Range.Min - 2*delta, Range.Max); + else + SetRange(Range.Min - delta, Range.Max + delta); + } + + double GetAspect() const { return Range.Size() / Pixels; } + void Constrain() { Range.Min = ImConstrainNan(ImConstrainInf(Range.Min)); Range.Max = ImConstrainNan(ImConstrainInf(Range.Max)); @@ -454,42 +478,15 @@ struct ImPlotAxis if (Range.Max <= Range.Min) Range.Max = Range.Min + DBL_EPSILON; } -}; -// Axis state information only needed between BeginPlot/EndPlot -struct ImPlotAxisState -{ - ImPlotAxis* Axis; - ImGuiCond RangeCond; - bool HasRange; - bool Present; - bool HasLabels; - bool Invert; - bool LockMin; - bool LockMax; - bool Lock; - bool IsTime; - - ImPlotAxisState(ImPlotAxis* axis, bool has_range, ImGuiCond range_cond, bool present) { - Axis = axis; - HasRange = has_range; - RangeCond = range_cond; - Present = present; - HasLabels = !ImHasFlag(Axis->Flags, ImPlotAxisFlags_NoTickLabels); - Invert = ImHasFlag(Axis->Flags, ImPlotAxisFlags_Invert); - LockMin = ImHasFlag(Axis->Flags, ImPlotAxisFlags_LockMin) || (HasRange && RangeCond == ImGuiCond_Always); - LockMax = ImHasFlag(Axis->Flags, ImPlotAxisFlags_LockMax) || (HasRange && RangeCond == ImGuiCond_Always); - Lock = !Present || ((LockMin && LockMax) || (HasRange && RangeCond == ImGuiCond_Always)); - IsTime = ImHasFlag(Axis->Flags, ImPlotAxisFlags_Time); - } - - ImPlotAxisState() { } -}; - -struct ImPlotAxisColor -{ - ImU32 Major, Minor, MajTxt, MinTxt; - ImPlotAxisColor() { Major = Minor = MajTxt = MinTxt = 0; } + inline bool IsLabeled() const { return !ImHasFlag(Flags, ImPlotAxisFlags_NoTickLabels); } + inline bool IsInverted() const { return ImHasFlag(Flags, ImPlotAxisFlags_Invert); } + inline bool IsAlwaysLocked() const { return HasRange && RangeCond == ImGuiCond_Always; } + inline bool IsLockedMin() const { return ImHasFlag(Flags, ImPlotAxisFlags_LockMin) || IsAlwaysLocked(); } + inline bool IsLockedMax() const { return ImHasFlag(Flags, ImPlotAxisFlags_LockMax) || IsAlwaysLocked(); } + inline bool IsLocked() const { return !Present || ((IsLockedMin() && IsLockedMax()) || IsAlwaysLocked()); } + inline bool IsTime() const { return ImHasFlag(Flags, ImPlotAxisFlags_Time); } + inline bool IsLog() const { return ImHasFlag(Flags, ImPlotAxisFlags_LogScale); } }; // State information for Plot items @@ -541,20 +538,26 @@ struct ImPlotPlot bool DraggingQuery; bool LegendHovered; bool LegendOutside; - bool LegendFlipSide; + bool LegendFlipSideNextFrame; + bool FrameHovered; + bool PlotHovered; int ColormapIdx; int CurrentYAxis; ImPlotLocation MousePosLocation; ImPlotLocation LegendLocation; ImPlotOrientation LegendOrientation; + ImRect FrameRect; + ImRect CanvasRect; + ImRect PlotRect; + ImRect AxesRect; ImPlotPlot() { - Flags = PreviousFlags = ImPlotFlags_None; - XAxis.Direction = ImPlotOrientation_Horizontal; + Flags = PreviousFlags = ImPlotFlags_None; + XAxis.Orientation = ImPlotOrientation_Horizontal; for (int i = 0; i < IMPLOT_Y_AXES; ++i) - YAxis[i].Direction = ImPlotOrientation_Vertical; + YAxis[i].Orientation = ImPlotOrientation_Vertical; SelectStart = QueryStart = ImVec2(0,0); - Selecting = Querying = Queried = DraggingQuery = LegendHovered = LegendOutside = LegendFlipSide = false; + Selecting = Querying = Queried = DraggingQuery = LegendHovered = LegendOutside = LegendFlipSideNextFrame = false; ColormapIdx = CurrentYAxis = 0; LegendLocation = ImPlotLocation_North | ImPlotLocation_West; LegendOrientation = ImPlotOrientation_Vertical; @@ -564,6 +567,8 @@ struct ImPlotPlot int GetLegendCount() const { return LegendData.Indices.size(); } ImPlotItem* GetLegendItem(int i); const char* GetLegendLabel(int i); + + inline bool IsLocked() const { return XAxis.IsLocked() && YAxis[0].IsLocked() && YAxis[1].IsLocked() && YAxis[2].IsLocked(); } }; // Temporary data storage for upcoming plot @@ -584,7 +589,9 @@ struct ImPlotNextPlotData double* LinkedYmin[IMPLOT_Y_AXES]; double* LinkedYmax[IMPLOT_Y_AXES]; - ImPlotNextPlotData() { + ImPlotNextPlotData() { Reset(); } + + void Reset() { HasXRange = false; ShowDefaultTicksX = true; FitX = false; @@ -596,6 +603,7 @@ struct ImPlotNextPlotData LinkedYmin[i] = LinkedYmax[i] = NULL; } } + }; // Temporary data storage for upcoming item @@ -617,7 +625,8 @@ struct ImPlotNextItemData { bool HasHidden; bool Hidden; ImGuiCond HiddenCond; - ImPlotNextItemData() { + ImPlotNextItemData() { Reset(); } + void Reset() { for (int i = 0; i < 5; ++i) Colors[i] = IMPLOT_AUTO_COL; LineWeight = MarkerSize = MarkerWeight = FillAlpha = ErrorBarSize = ErrorBarWeight = DigitalBitHeight = DigitalBitGap = IMPLOT_AUTO; @@ -634,20 +643,6 @@ struct ImPlotContext { ImPlotItem* CurrentItem; ImPlotItem* PreviousItem; - // Bounding Boxes - ImRect BB_Frame; - ImRect BB_Canvas; - ImRect BB_Plot; - ImRect BB_Axes; - ImRect BB_X; - ImRect BB_Y[IMPLOT_Y_AXES]; - - // Axis States - ImPlotAxisColor Col_X; - ImPlotAxisColor Col_Y[IMPLOT_Y_AXES]; - ImPlotAxisState X; - ImPlotAxisState Y[IMPLOT_Y_AXES]; - // Tick Marks and Labels ImPlotTickCollection XTicks; ImPlotTickCollection YTicks[IMPLOT_Y_AXES]; @@ -671,16 +666,11 @@ struct ImPlotContext { bool FitX; bool FitY[IMPLOT_Y_AXES]; - // Hover states - bool Hov_Frame; - bool Hov_Plot; - // Axis Rendering Flags bool RenderX; bool RenderY[IMPLOT_Y_AXES]; // Axis Locking Flags - bool LockPlot; bool ChildWindowMade; // Style and Colormaps @@ -704,17 +694,6 @@ struct ImPlotContext { ImPlotPoint MousePos[IMPLOT_Y_AXES]; }; -struct ImPlotAxisScale -{ - ImPlotPoint Min, Max; - - ImPlotAxisScale(int y_axis, float tx, float ty, float zoom_rate) { - ImPlotContext& gp = *GImPlot; - Min = ImPlot::PixelsToPlot(gp.BB_Plot.Min - gp.BB_Plot.GetSize() * ImVec2(tx * zoom_rate, ty * zoom_rate), y_axis); - Max = ImPlot::PixelsToPlot(gp.BB_Plot.Max + gp.BB_Plot.GetSize() * ImVec2((1 - tx) * zoom_rate, (1 - ty) * zoom_rate), y_axis); - } -}; - //----------------------------------------------------------------------------- // [SECTION] Internal API // No guarantee of forward compatibility here! diff --git a/implot_items.cpp b/implot_items.cpp index 9f24785..2bad83b 100644 --- a/implot_items.cpp +++ b/implot_items.cpp @@ -162,7 +162,7 @@ bool BeginItem(const char* label_id, ImPlotCol recolor_from) { } if (!item->Show) { // reset next item data - gp.NextItemData = ImPlotNextItemData(); + gp.NextItemData.Reset(); gp.PreviousItem = item; gp.CurrentItem = NULL; return false; @@ -210,7 +210,7 @@ void EndItem() { // pop rendering clip rect PopPlotClipRect(); // reset next item data - gp.NextItemData = ImPlotNextItemData(); + gp.NextItemData.Reset(); // set current item gp.PreviousItem = gp.CurrentItem; gp.CurrentItem = NULL; @@ -730,13 +730,13 @@ inline void RenderLineStrip(const Getter& getter, const Transformer& transformer ImVec2 p1 = transformer(getter(0)); for (int i = 1; i < getter.Count; ++i) { ImVec2 p2 = transformer(getter(i)); - if (gp.BB_Plot.Overlaps(ImRect(ImMin(p1, p2), ImMax(p1, p2)))) + if (gp.CurrentPlot->PlotRect.Overlaps(ImRect(ImMin(p1, p2), ImMax(p1, p2)))) DrawList.AddLine(p1, p2, col, line_weight); p1 = p2; } } else { - RenderPrimitives(LineStripRenderer(getter, transformer, col, line_weight), DrawList, gp.BB_Plot); + RenderPrimitives(LineStripRenderer(getter, transformer, col, line_weight), DrawList, gp.CurrentPlot->PlotRect); } } @@ -748,12 +748,12 @@ inline void RenderLineSegments(const Getter1& getter1, const Getter2& getter2, c for (int i = 0; i < I; ++i) { ImVec2 p1 = transformer(getter1(i)); ImVec2 p2 = transformer(getter2(i)); - if (gp.BB_Plot.Overlaps(ImRect(ImMin(p1, p2), ImMax(p1, p2)))) + if (gp.CurrentPlot->PlotRect.Overlaps(ImRect(ImMin(p1, p2), ImMax(p1, p2)))) DrawList.AddLine(p1, p2, col, line_weight); } } else { - RenderPrimitives(LineSegmentsRenderer(getter1, getter2, transformer, col, line_weight), DrawList, gp.BB_Plot); + RenderPrimitives(LineSegmentsRenderer(getter1, getter2, transformer, col, line_weight), DrawList, gp.CurrentPlot->PlotRect); } } @@ -764,7 +764,7 @@ inline void RenderStairs(const Getter& getter, const Transformer& transformer, I ImVec2 p1 = transformer(getter(0)); for (int i = 1; i < getter.Count; ++i) { ImVec2 p2 = transformer(getter(i)); - if (gp.BB_Plot.Overlaps(ImRect(ImMin(p1, p2), ImMax(p1, p2)))) { + if (gp.CurrentPlot->PlotRect.Overlaps(ImRect(ImMin(p1, p2), ImMax(p1, p2)))) { ImVec2 p12(p2.x, p1.y); DrawList.AddLine(p1, p12, col, line_weight); DrawList.AddLine(p12, p2, col, line_weight); @@ -773,7 +773,7 @@ inline void RenderStairs(const Getter& getter, const Transformer& transformer, I } } else { - RenderPrimitives(StairsRenderer(getter, transformer, col, line_weight), DrawList, gp.BB_Plot); + RenderPrimitives(StairsRenderer(getter, transformer, col, line_weight), DrawList, gp.CurrentPlot->PlotRect); } } @@ -881,7 +881,7 @@ inline void RenderMarkers(Getter getter, Transformer transformer, ImDrawList& Dr ImPlotContext& gp = *GImPlot; for (int i = 0; i < getter.Count; ++i) { ImVec2 c = transformer(getter(i)); - if (gp.BB_Plot.Contains(c)) + if (gp.CurrentPlot->PlotRect.Contains(c)) marker_table[marker](DrawList, c, size, rend_mk_line, col_mk_line, rend_mk_fill, col_mk_fill, weight); } } @@ -1135,10 +1135,10 @@ inline void PlotShadedEx(const char* label_id, const Getter1& getter1, const Get if (s.RenderFill) { ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]); switch (GetCurrentScale()) { - case ImPlotScale_LinLin: RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLinLin(), col), DrawList, GImPlot->BB_Plot); break; - case ImPlotScale_LogLin: RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLogLin(), col), DrawList, GImPlot->BB_Plot); break; - case ImPlotScale_LinLog: RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLinLog(), col), DrawList, GImPlot->BB_Plot); break; - case ImPlotScale_LogLog: RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLogLog(), col), DrawList, GImPlot->BB_Plot); break; + case ImPlotScale_LinLin: RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLinLin(), col), DrawList, GImPlot->CurrentPlot->PlotRect); break; + case ImPlotScale_LogLin: RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLogLin(), col), DrawList, GImPlot->CurrentPlot->PlotRect); break; + case ImPlotScale_LinLog: RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLinLog(), col), DrawList, GImPlot->CurrentPlot->PlotRect); break; + case ImPlotScale_LogLog: RenderPrimitives(ShadedRenderer(getter1,getter2,TransformerLogLog(), col), DrawList, GImPlot->CurrentPlot->PlotRect); break; } } EndItem(); @@ -1777,7 +1777,7 @@ inline void PlotDigitalEx(const char* label_id, Getter getter) { 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))) { + if ((pMax.x > pMin.x) && (gp.CurrentPlot->PlotRect.Contains(pMin) || gp.CurrentPlot->PlotRect.Contains(pMax))) { // ImVec4 colAlpha = item->Color; // colAlpha.w = item->Highlight ? 1.0f : 0.9f; DrawList.AddRectFilled(pMin, pMax, ImGui::GetColorU32(s.Colors[ImPlotCol_Fill])); @@ -1832,10 +1832,10 @@ void PlotRectsEx(const char* label_id, const Getter& getter) { ImDrawList& DrawList = *GetPlotDrawList(); ImU32 col = ImGui::GetColorU32(s.Colors[ImPlotCol_Fill]); switch (GetCurrentScale()) { - case ImPlotScale_LinLin: RenderPrimitives(RectRenderer(getter, TransformerLinLin(), col), DrawList, GImPlot->BB_Plot); break; - case ImPlotScale_LogLin: RenderPrimitives(RectRenderer(getter, TransformerLogLin(), col), DrawList, GImPlot->BB_Plot); break; - case ImPlotScale_LinLog: RenderPrimitives(RectRenderer(getter, TransformerLinLog(), col), DrawList, GImPlot->BB_Plot); break; - case ImPlotScale_LogLog: RenderPrimitives(RectRenderer(getter, TransformerLogLog(), col), DrawList, GImPlot->BB_Plot); break; + case ImPlotScale_LinLin: RenderPrimitives(RectRenderer(getter, TransformerLinLin(), col), DrawList, GImPlot->CurrentPlot->PlotRect); break; + case ImPlotScale_LogLin: RenderPrimitives(RectRenderer(getter, TransformerLogLin(), col), DrawList, GImPlot->CurrentPlot->PlotRect); break; + case ImPlotScale_LinLog: RenderPrimitives(RectRenderer(getter, TransformerLinLog(), col), DrawList, GImPlot->CurrentPlot->PlotRect); break; + case ImPlotScale_LogLog: RenderPrimitives(RectRenderer(getter, TransformerLogLog(), col), DrawList, GImPlot->CurrentPlot->PlotRect); break; } } EndItem();