Repository: bokeh/bokeh

Issue:
Zooming out with bound ranges doesn't zoom all the way
Bokeh version: 0.12.4

1. set `bounds='auto'` on `x_range` and `y_range` range
2. zoom in with mouse pointer near the lower left corner of the plot
3. zoom out with mouse pointer near the upper right corner of the plot

Observed behavior:
Plot is zoomed in just fine, but zooming out doesn't bring the plot to the same state as before apparently because zooming in doesn't try to move plot at all

Expected behavior:
The plot moves as to both be inside the specified bounds and to be zoomed out up to the initial state when zooming out is no longer possible without breaking bounds contstraint.


Discussion:
@p-himik I'm not able to picture what is going on from your description. Can you provide a complete, minimal, runnable code sample to reproduce what you are seeing? In these cases a link to a short screencast can also be helpful if that's feasible. 
Sure, it's as simple as this:
```python
import bokeh.plotting as bp
import bokeh.models as bm

f = bp.figure(tools='wheel_zoom', width=500, height=500,
              x_range=bm.DataRange1d(bounds='auto'),
              y_range=bm.DataRange1d(bounds='auto'))
f.toolbar.active_scroll = f.select(type=bm.WheelZoomTool)[0]
f.x([0, 10], [0, 10], size=20, color=['blue', 'red'])
```

When you see the plot:
1. point your mouse to the red cross
2. zoom in
3. move your mouse to where the blue cross was
4. zoom out

I'd expect that after you scroll a bit, you'd see the blue cross again. But because of the bounds and of the apparent fact that zooming doesn't try to move the plot, it just doesn't happen.
Right, "auto" bounds set max bounds that roughly envelope the entire data set. When zooming, the zoom always happens exactly around the point under the mouse, and the presence of bounds causing zooming to stop as soon as *any* bound is hit. When zoom in, then move the mouse to zoom back out, you hit the right side bound first, causing the zooming to stop 

I am not sure there is any different behavior that could be implemented that would be reasonable. "moving the plot" means that the zoom tool no longer only zooms exactly under the mouse, which seems extremely undesirable. (The most valuable thing about a wheel zoom is that I can go right to a point on the plot I care about and be sure to zoom in and out only around that point). And letting it zoom out further means that bounds are not respected (making setting bounds meaningless). 

It seem like you might just not want to set bounds at all? Or perhaps also add a reset tool to allow people to get back to the original maximally zoomed out state?
Yes, I solved the issue in my particular case by extending bounds to twice the size of the data.

But I think that there's a place for a better, more common solution.
Don't get me wrong, I completely get your point about zooming in/out on the particular point, and I agree with it. I just think that it's possible to have a flag that would make zooming have a bit higher priority than having the same point under mouse pointer during zooming. Otherwise, having any bounds immediately means that zooming out will be an issue. And reset is not a very clean solution either - if there are other plots in the same layout, there has to be a toolbox for each plot, or I will be forced to reset all plots at once in order to zoom out just one of them.

Again, it's just my opinion based on other things that can zoom in/out and have some bounds (e.g. a generic image viewer). Please feel free to close the issue if you don't think that implementing such flag or some other mechanism may be worth it.
I'm willing to entertain the idea. Do you have any suggestions regarding a property API for this?
I've been using Bokeh for just two days, so my picture of the API is not very complete/cohesive. I will post any ideas here as soon as they're backed by a deeper understanding of the library.
@p-himik no hurry, I look forward to hearing your input
Closing this discussion as stale. @p-himik if you have further thoughts, please feel free to re-open
Cursor-centered zooming works great, but the constraint should be lifted when unsatisfiable if that means honoring the API user's specified figure bounds and visualization user's request of zooming out.

Have this example:
```py
import numpy as np
from bokeh.io import show
from bokeh.plotting import figure

N = 10000
x = np.arange(N)
y = np.cumsum(np.random.random(x.size) - .5)

p = figure(plot_height=800, plot_width=1000, tools='xpan,xwheel_zoom',
           active_scroll='xwheel_zoom', active_drag='xpan')
p.line(x, y)

p.x_range.bounds = (0, N)
p.x_range.start = 0
p.x_range.end = N

show(p)
```
Do zoom in on the far right of the chart. Now zoom back out and see that that's working.
Now zoom back in on the far right of the chart, but then pan the chart slightly. Now try zooming out again completely (without panning to the (supposed) plot center after each zoom step).

Compare this to how Highcharts does it: https://jsfiddle.net/highcharts/6etwu5b4/

It is as clear as day this could be made a slight bit more user-friendly.

Please reopen as a feature request.
I have to reiterate that I hate how highchairs does things. I believe that when doing a wheel/scroll zoom, the absolute most invariant to maintain is that the zoom always and only happens exactly underneath the cursor position at the start of the motion. The singular reasons for a wheel/scroll zoom tool is to be able to zoom in precisely and accurately *on a single location* and this "friendly" behavior breaks that entirely. 
I agree that is of at most importance when _zooming in_, and also desirable when zooming out.

But the current state of it being so strict a requirement often breaks "zooming out to see the whole picture."
I would argue that's what the rest tool is for
@bryevdv I just stumbled upon this again. I don't think the reset tool is for this, at least because it does some other things as well. Another reason is that it may be really hard, if not impossible with the current state of things, to set up the desired state as the initial one.

I would suggest adding `centered_zoom_in` and `centered_zoom_out` parameters to `WheelZoomTool`. By default, they're `True` and represent current behavior. Setting them to `False` would remove `center` argument for `scale_range` function for the appropriate case of zooming in or out.

Resolving patch:
diff --git a/bokeh/models/tools.py b/bokeh/models/tools.py
--- a/bokeh/models/tools.py
+++ b/bokeh/models/tools.py
@@ -218,6 +218,12 @@ class WheelZoomTool(Scroll):
     vertically across the height of the plot.
     """)
 
+    maintain_focus = Bool(default=True, help="""
+    Whether or not zooming tool maintains its focus position. Setting it
+    to False results in a more "gliding" behavior, allowing one to
+    zoom out more smoothly, at the cost of losing the focus position.
+    """)
+
 
 class SaveTool(Action):
     ''' *toolbar icon*: |save_icon|


Test patch: