Skip to content

Commit

Permalink
Make scrolling and zooming more consistent with expectations (epezent…
Browse files Browse the repository at this point in the history
…#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
  • Loading branch information
Nahor committed Nov 24, 2023
1 parent 1f7a8c0 commit 75cf63a
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 29 deletions.
22 changes: 6 additions & 16 deletions implot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -1995,36 +1993,30 @@ bool UpdateInput(ImPlotPlot& plot) {
const bool equal_zoom = axis_equal && x_axis.OrthoAxis != nullptr;
const bool equal_locked = (equal_zoom != false) && x_axis.OrthoAxis->IsInputLocked();
if (x_hov[i] && !x_axis.IsInputLocked() && !equal_locked) {
ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID);
if (zoom_rate != 0.0f) {
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;
}
}
}
for (int i = 0; i < IMPLOT_NUM_Y_AXES; i++) {
ImPlotAxis& y_axis = plot.YAxis(i);
const bool equal_zoom = axis_equal && y_axis.OrthoAxis != nullptr;
const bool equal_locked = equal_zoom && y_axis.OrthoAxis->IsInputLocked();
if (y_hov[i] && !y_axis.IsInputLocked() && !equal_locked) {
ImGui::SetKeyOwner(ImGuiKey_MouseWheelY, plot.ID);
if (zoom_rate != 0.0f) {
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;
}
}
}
}

Expand All @@ -2041,8 +2033,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;
}
}
Expand All @@ -2051,8 +2042,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;
}
}
Expand Down
58 changes: 45 additions & 13 deletions implot_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
Expand Down

0 comments on commit 75cf63a

Please sign in to comment.