Skip to content

Commit 75cf63a

Browse files
committed
Make scrolling and zooming more consistent with expectations (epezent#386)
- 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
1 parent 1f7a8c0 commit 75cf63a

File tree

2 files changed

+51
-29
lines changed

2 files changed

+51
-29
lines changed

implot.cpp

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1943,8 +1943,7 @@ bool UpdateInput(ImPlotPlot& plot) {
19431943
if (IO.MouseDelta.x != 0 && !x_axis.IsPanLocked(increasing)) {
19441944
const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - IO.MouseDelta.x);
19451945
const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x - IO.MouseDelta.x);
1946-
x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l);
1947-
x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r);
1946+
x_axis.SetMinMax(x_axis.IsInverted() ? plot_r : plot_l, x_axis.IsInverted() ? plot_l : plot_r);
19481947
if (axis_equal && x_axis.OrthoAxis != nullptr)
19491948
x_axis.OrthoAxis->SetAspect(x_axis.GetAspect());
19501949
changed = true;
@@ -1959,8 +1958,7 @@ bool UpdateInput(ImPlotPlot& plot) {
19591958
if (IO.MouseDelta.y != 0 && !y_axis.IsPanLocked(increasing)) {
19601959
const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - IO.MouseDelta.y);
19611960
const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y - IO.MouseDelta.y);
1962-
y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b);
1963-
y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t);
1961+
y_axis.SetMinMax(y_axis.IsInverted() ? plot_t : plot_b, y_axis.IsInverted() ? plot_b : plot_t);
19641962
if (axis_equal && y_axis.OrthoAxis != nullptr)
19651963
y_axis.OrthoAxis->SetAspect(y_axis.GetAspect());
19661964
changed = true;
@@ -1995,36 +1993,30 @@ bool UpdateInput(ImPlotPlot& plot) {
19951993
const bool equal_zoom = axis_equal && x_axis.OrthoAxis != nullptr;
19961994
const bool equal_locked = (equal_zoom != false) && x_axis.OrthoAxis->IsInputLocked();
19971995
if (x_hov[i] && !x_axis.IsInputLocked() && !equal_locked) {
1998-
ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID);
19991996
if (zoom_rate != 0.0f) {
20001997
float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f;
20011998
const double plot_l = x_axis.PixelsToPlot(plot.PlotRect.Min.x - rect_size.x * tx * zoom_rate * correction);
20021999
const double plot_r = x_axis.PixelsToPlot(plot.PlotRect.Max.x + rect_size.x * (1 - tx) * zoom_rate * correction);
2003-
x_axis.SetMin(x_axis.IsInverted() ? plot_r : plot_l);
2004-
x_axis.SetMax(x_axis.IsInverted() ? plot_l : plot_r);
2000+
x_axis.SetMinMax(x_axis.IsInverted() ? plot_r : plot_l, x_axis.IsInverted() ? plot_l : plot_r, false, x_axis.IsInverted() ? (1 - tx) : tx);
20052001
if (axis_equal && x_axis.OrthoAxis != nullptr)
20062002
x_axis.OrthoAxis->SetAspect(x_axis.GetAspect());
20072003
changed = true;
20082004
}
2009-
}
20102005
}
20112006
for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
20122007
ImPlotAxis& y_axis = plot.YAxis(i);
20132008
const bool equal_zoom = axis_equal && y_axis.OrthoAxis != nullptr;
20142009
const bool equal_locked = equal_zoom && y_axis.OrthoAxis->IsInputLocked();
20152010
if (y_hov[i] && !y_axis.IsInputLocked() && !equal_locked) {
2016-
ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID);
20172011
if (zoom_rate != 0.0f) {
20182012
float correction = (plot.Hovered && equal_zoom) ? 0.5f : 1.0f;
20192013
const double plot_t = y_axis.PixelsToPlot(plot.PlotRect.Min.y - rect_size.y * ty * zoom_rate * correction);
20202014
const double plot_b = y_axis.PixelsToPlot(plot.PlotRect.Max.y + rect_size.y * (1 - ty) * zoom_rate * correction);
2021-
y_axis.SetMin(y_axis.IsInverted() ? plot_t : plot_b);
2022-
y_axis.SetMax(y_axis.IsInverted() ? plot_b : plot_t);
2015+
y_axis.SetMinMax(y_axis.IsInverted() ? plot_t : plot_b, y_axis.IsInverted() ? plot_b : plot_t, false, y_axis.IsInverted() ? (1 - tx) : tx);
20232016
if (axis_equal && y_axis.OrthoAxis != nullptr)
20242017
y_axis.OrthoAxis->SetAspect(y_axis.GetAspect());
20252018
changed = true;
20262019
}
2027-
}
20282020
}
20292021
}
20302022

@@ -2041,8 +2033,7 @@ bool UpdateInput(ImPlotPlot& plot) {
20412033
if (!x_axis.IsInputLocked() && x_can_change) {
20422034
const double p1 = x_axis.PixelsToPlot(plot.SelectStart.x);
20432035
const double p2 = x_axis.PixelsToPlot(IO.MousePos.x);
2044-
x_axis.SetMin(ImMin(p1, p2));
2045-
x_axis.SetMax(ImMax(p1, p2));
2036+
x_axis.SetMinMax(ImMin(p1, p2), ImMax(p1, p2));
20462037
changed = true;
20472038
}
20482039
}
@@ -2051,8 +2042,7 @@ bool UpdateInput(ImPlotPlot& plot) {
20512042
if (!y_axis.IsInputLocked() && y_can_change) {
20522043
const double p1 = y_axis.PixelsToPlot(plot.SelectStart.y);
20532044
const double p2 = y_axis.PixelsToPlot(IO.MousePos.y);
2054-
y_axis.SetMin(ImMin(p1, p2));
2055-
y_axis.SetMax(ImMax(p1, p2));
2045+
y_axis.SetMinMax(ImMin(p1, p2), ImMax(p1, p2));
20562046
changed = true;
20572047
}
20582048
}

implot_internal.h

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,50 @@ struct ImPlotAxis
714714
Ticker.Reset();
715715
}
716716

717+
inline void ConstrainZoom(double& _min, double& _max, double zoom_ratio = 0.5) {
718+
double z = _max - _min;
719+
if (z < ConstraintZoom.Min) {
720+
double delta = ConstraintZoom.Min - z;
721+
_min -= delta * zoom_ratio;
722+
_max += delta * (1.0 - zoom_ratio);
723+
} else if (z > ConstraintZoom.Max) {
724+
double delta = z - ConstraintZoom.Max;
725+
_min += delta * zoom_ratio;
726+
_max -= delta * (1.0 - zoom_ratio);
727+
}
728+
if (_max <= _min) {
729+
_max = _min + DBL_EPSILON;
730+
}
731+
}
732+
733+
inline void SetMinMax(double _min, double _max, bool force = false, double zoom_ratio = 0.5) {
734+
if (!force && IsLockedMin()) {
735+
SetMax(_max, force);
736+
return;
737+
}
738+
if (!force && IsLockedMax()) {
739+
SetMin(_min, force);
740+
return;
741+
}
742+
_min = ImConstrainNan(ImConstrainInf(_min));
743+
_max = ImConstrainNan(ImConstrainInf(_max));
744+
// Constrain the zoom first, so that the visible area stays centered if it needs correcting...
745+
ConstrainZoom(_min, _max, zoom_ratio);
746+
// .. then scroll to ensure it stays within limit constraints
747+
if (_min < ConstraintRange.Min) {
748+
_max = ImMin(_max + (ConstraintRange.Min - _min), ConstraintRange.Max);
749+
_min = ConstraintRange.Min;
750+
} else if (_max > ConstraintRange.Max) {
751+
_min = ImMax(_min - (_max - ConstraintRange.Max), ConstraintRange.Min);
752+
_max = ConstraintRange.Max;
753+
}
754+
Range.Min = _min;
755+
Range.Max = _max;
756+
PickerTimeMin = ImPlotTime::FromDouble(Range.Min);
757+
PickerTimeMax = ImPlotTime::FromDouble(Range.Max);
758+
UpdateTransformCache();
759+
};
760+
717761
inline bool SetMin(double _min, bool force=false) {
718762
if (!force && IsLockedMin())
719763
return false;
@@ -789,19 +833,7 @@ struct ImPlotAxis
789833
Range.Min = ConstraintRange.Min;
790834
if (Range.Max > ConstraintRange.Max)
791835
Range.Max = ConstraintRange.Max;
792-
double z = Range.Size();
793-
if (z < ConstraintZoom.Min) {
794-
double delta = (ConstraintZoom.Min - z) * 0.5;
795-
Range.Min -= delta;
796-
Range.Max += delta;
797-
}
798-
if (z > ConstraintZoom.Max) {
799-
double delta = (z - ConstraintZoom.Max) * 0.5;
800-
Range.Min += delta;
801-
Range.Max -= delta;
802-
}
803-
if (Range.Max <= Range.Min)
804-
Range.Max = Range.Min + DBL_EPSILON;
836+
ConstrainZoom(Range.Min, Range.Max);
805837
}
806838

807839
inline void UpdateTransformCache() {

0 commit comments

Comments
 (0)