From 7d92d6f995091b74ab5c6c71339c1567b7431ec8 Mon Sep 17 00:00:00 2001 From: Nahor Date: Fri, 24 Nov 2023 12:49:43 -0800 Subject: [PATCH] Make scrolling and zooming more consistent with expectations (#537) - Make scrolling not affect the zoom level when near the axis limit constraint - Keep the zoom-out speed constant when one side of the axis is against the limit constraint while the mouse is on the other side - When doing box-selection with a size smaller that the minimum zoom, keep the final box centered on the selection instead of "left aligning" it. --- implot.cpp | 18 +++++---------- implot_internal.h | 58 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/implot.cpp b/implot.cpp index d9260281..5ac628b0 100644 --- a/implot.cpp +++ b/implot.cpp @@ -1943,8 +1943,7 @@ bool UpdateInput(ImPlotPlot& plot) { if (IO.MouseDelta.x != 0 && !x_axis.IsPanLocked(increasing)) { const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - IO.MouseDelta.x); const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x - IO.MouseDelta.x); - x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l); - x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r); + x_axis.SetMinMax(x_axis.IsInverted() ? plot_r : plot_l, x_axis.IsInverted() ? plot_l : plot_r); if (axis_equal && x_axis.OrthoAxis != nullptr) x_axis.OrthoAxis->SetAspect(x_axis.GetAspect()); changed = true; @@ -1959,8 +1958,7 @@ bool UpdateInput(ImPlotPlot& plot) { if (IO.MouseDelta.y != 0 && !y_axis.IsPanLocked(increasing)) { const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - IO.MouseDelta.y); const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y - IO.MouseDelta.y); - y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b); - y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t); + y_axis.SetMinMax(y_axis.IsInverted() ? plot_t : plot_b, y_axis.IsInverted() ? plot_b : plot_t); if (axis_equal && y_axis.OrthoAxis != nullptr) y_axis.OrthoAxis->SetAspect(y_axis.GetAspect()); changed = true; @@ -2000,8 +1998,7 @@ bool UpdateInput(ImPlotPlot& plot) { float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f; const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - rect_size.x * tx * zoom_rate * correction); const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x + rect_size.x * (1 - tx) * zoom_rate * correction); - x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l); - x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r); + x_axis.SetMinMax(x_axis.IsInverted() ? plot_r : plot_l, x_axis.IsInverted() ? plot_l : plot_r, false, x_axis.IsInverted() ? (1 - tx) : tx); if (axis_equal && x_axis.OrthoAxis != nullptr) x_axis.OrthoAxis->SetAspect(x_axis.GetAspect()); changed = true; @@ -2018,8 +2015,7 @@ bool UpdateInput(ImPlotPlot& plot) { float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f; const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - rect_size.y * ty * zoom_rate * correction); const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y + rect_size.y * (1 - ty) * zoom_rate * correction); - y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b); - y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t); + y_axis.SetMinMax(y_axis.IsInverted() ? plot_t : plot_b, y_axis.IsInverted() ? plot_b : plot_t, false, y_axis.IsInverted() ? (1 - tx) : tx); if (axis_equal && y_axis.OrthoAxis != nullptr) y_axis.OrthoAxis->SetAspect(y_axis.GetAspect()); changed = true; @@ -2041,8 +2037,7 @@ bool UpdateInput(ImPlotPlot& plot) { if (!x_axis.IsInputLocked() && x_can_change) { const double p1 = x_axis.PixelsToPlot(plot.SelectStart.x); const double p2 = x_axis.PixelsToPlot(IO.MousePos.x); - x_axis.SetMin(ImMin(p1, p2)); - x_axis.SetMax(ImMax(p1, p2)); + x_axis.SetMinMax(ImMin(p1, p2), ImMax(p1, p2)); changed = true; } } @@ -2051,8 +2046,7 @@ bool UpdateInput(ImPlotPlot& plot) { if (!y_axis.IsInputLocked() && y_can_change) { const double p1 = y_axis.PixelsToPlot(plot.SelectStart.y); const double p2 = y_axis.PixelsToPlot(IO.MousePos.y); - y_axis.SetMin(ImMin(p1, p2)); - y_axis.SetMax(ImMax(p1, p2)); + y_axis.SetMinMax(ImMin(p1, p2), ImMax(p1, p2)); changed = true; } } diff --git a/implot_internal.h b/implot_internal.h index 8c1650ef..d9fe9585 100644 --- a/implot_internal.h +++ b/implot_internal.h @@ -714,6 +714,50 @@ struct ImPlotAxis Ticker.Reset(); } + inline void ConstrainZoom(double& _min, double& _max, double zoom_ratio = 0.5) { + double z = _max - _min; + if (z < ConstraintZoom.Min) { + double delta = ConstraintZoom.Min - z; + _min -= delta * zoom_ratio; + _max += delta * (1.0 - zoom_ratio); + } else if (z > ConstraintZoom.Max) { + double delta = z - ConstraintZoom.Max; + _min += delta * zoom_ratio; + _max -= delta * (1.0 - zoom_ratio); + } + if (_max <= _min) { + _max = _min + DBL_EPSILON; + } + } + + inline void SetMinMax(double _min, double _max, bool force = false, double zoom_ratio = 0.5) { + if (!force && IsLockedMin()) { + SetMax(_max, force); + return; + } + if (!force && IsLockedMax()) { + SetMin(_min, force); + return; + } + _min = ImConstrainNan(ImConstrainInf(_min)); + _max = ImConstrainNan(ImConstrainInf(_max)); + // Constrain the zoom first, so that the visible area stays centered if it needs correcting... + ConstrainZoom(_min, _max, zoom_ratio); + // .. then scroll to ensure it stays within limit constraints + if (_min < ConstraintRange.Min) { + _max = ImMin(_max + (ConstraintRange.Min - _min), ConstraintRange.Max); + _min = ConstraintRange.Min; + } else if (_max > ConstraintRange.Max) { + _min = ImMax(_min - (_max - ConstraintRange.Max), ConstraintRange.Min); + _max = ConstraintRange.Max; + } + Range.Min = _min; + Range.Max = _max; + PickerTimeMin = ImPlotTime::FromDouble(Range.Min); + PickerTimeMax = ImPlotTime::FromDouble(Range.Max); + UpdateTransformCache(); + }; + inline bool SetMin(double _min, bool force=false) { if (!force && IsLockedMin()) return false; @@ -789,19 +833,7 @@ struct ImPlotAxis Range.Min = ConstraintRange.Min; if (Range.Max > ConstraintRange.Max) Range.Max = ConstraintRange.Max; - double z = Range.Size(); - if (z < ConstraintZoom.Min) { - double delta = (ConstraintZoom.Min - z) * 0.5; - Range.Min -= delta; - Range.Max += delta; - } - if (z > ConstraintZoom.Max) { - double delta = (z - ConstraintZoom.Max) * 0.5; - Range.Min += delta; - Range.Max -= delta; - } - if (Range.Max <= Range.Min) - Range.Max = Range.Min + DBL_EPSILON; + ConstrainZoom(Range.Min, Range.Max); } inline void UpdateTransformCache() {