Determining axes zoom state

A couple of days ago, a reader of Matlab’s official Desktop blog asked whether it is possible to determine if an axes has been zoomed or not.

I have encountered this question myself some time ago, when I tried to customize a radar plot: The grid in radar plots does not automatically re-draw when the plot is zoomed, as in regular rectangular plots. Instead, the radial (angle) and circular (range) grid lines are statically painted when the plot is created, and remain fixed when the plot is zoomed or panned. Therefore, if we zoom in on a small-enough plot segment, we will not see any grid lines at all. It would be beneficial to repaint the grid-lines upon every zoom and pan event. I will not dive into the details today, but the important thing for today’s article is that the algorithm needed to know whether or not the axes is currently zoomed or not.

The official response is that this information is not readily available. We could of course store the axes limits somewhere and then compare them to the current limits in run-time. This will cause complications if the plotted data (and thereby the axes limits) change automatically during program use, even without any zooming/panning. It is also problematic if the user resets the zoom limits (as Jiro has correctly pointed out).

Today I’ll highlight another possible solution, that perhaps not many Matlab users are familiar with: Apparently, whenever zooming or panning occur, a hidden ApplicationData object is automatically added to the affected axes, with information about the original axes meta-data: limits, aspect ratio, camera info and view angles. This object is also automatically updated whenever we use zoom reset to reset the zoom limits.

So basically, all we have to do in run-time is to compare our current access limits with the stored info: if they are the same (or if the app-data object is missing) then the plot is unzoomed and unpanned; otherwise it is. So simple.

origInfo = getappdata(gca, 'matlab_graphics_resetplotview');
if isempty(origInfo)
   isZoomed = false;
elseif isequal(get(gca,'XLim'), origInfo.XLim) && ...
       isequal(get(gca,'YLim'), origInfo.YLim) && ...
       isequal(get(gca,'ZLim'), origInfo.ZLim)
   isZoomed = false;
   isZoomed = true;

This still does not solve the problem of limit changes when the plot data is changed, but it may perhaps be a bit easier to use than manually storing the limits before plotting. (And yes, of course the if-else statement above could be made a one-liner logical construct, but I think this way is more readable).

Zooming and panning add some additional data to ApplicationData, and also use/modify some other hidden properties of the axes handle (use the getundoc function to see them). If you use one of them for any useful purpose, please report your usage in a comment below.

Categories: Handle graphics, Hidden property, Medium risk of breaking in future versions, Stock Matlab function, Undocumented feature

Tags: , ,

Bookmark and SharePrint Print

11 Responses to Determining axes zoom state

  1. Richard says:

    Hi Yair, I am the original asker on the blog, I thought I’d repeat my comment here. The main reason I want to know this is because I’m developing a code to automatically produce ‘tight’ figures without excess space around them for publication. I also want this code to work for subplots by getting the minimum required bounding box (based on the ‘TightInset’ and ‘Position’ properties) of every axes in the figure. My problem is, if a 3D plot has been zoomed, these properties are no longer useful, and the figure should be ignored, or left as it is. 2D plots that have been zoomed are fine, as the axes stay where they are in the figure, but the whole view of the figure changes if any 3D axes have been zoomed. I have found a way to determine if an axes is 3D, using the view function. Perhaps using your tips I can get the last part.

    P.S. I’ve read quite a few of your blog articles over the last year or so, it’s been very useful, thanks!

  2. Aleksander Hammernes says:

    Would it be possible to get the zoom area ?
    let say you activate the zoom function in a axes, normally this off course works fine, you can click and drag your mouse inside
    the axes, and it zoom`s the axes to your wishes. how ever not in my gui…… in need to replot to make it work for me, so is there a
    way to “capture” the x and y limits from the drag dragable zoom ?

  3. Adam says:

    Hi Yair. Do you know if something has changed in R2014b that would cause this to no longer work when the figure containing the axes is not yet visible when zoom reset is called? It is one of a number of related things in my code that has broken from R2014a all seemingly related to whether or not a figure is actually visible yet or whether it is the figure in focus. I fixed this case by just checking if the zoominfo object is empty (it is now in R2014b, it wasn’t in R2014a when triggered from the OpeningFcn) and another case where creating a zoom object without an explicit figure used to match to the correct figure by default in R2013a, but picked up the figure that spawned it instead in R2014b was easy to fix.

    I couldn’t see anything in the described graphics changes though that would explain this behaviour. My troubles with findjobj also seem to relate to this type of issue, seemingly EDT effects that were not present in R2014a, but now break the code in R2014b. In that case I just had to stop using the functionality, at least until I have more time to investigate. The axis zoom state information in this blog has been very useful to me though so thank you for sharing.

  4. Eduard Braun says:

    This is a very interesting way to determine if the axes are zoomed.

    However I’m wondering if it is possible to do this the other way round: Start with unzoomed axes and programmatically bring them into a state as if they were zoomed in by the user?

    For clarification: If I simply set XLim and/or YLim on the axes I can zoom in a portion of the graph but the user can not zoom out anymore… Therefore it’s not the same as if the user zoomed in (since then he obviously can zoom out again).

    • @Eduard – you can use the zoom function with a certain factor, for example, zoom(2.5)

    • Eduard Braun says:

      Thanks for feedback!

      I had looked at zoom before but sadly it doesn’t allow set specific numeric limits on the axes which makes it useless for many cases.

      However it turned out the actual solution is pretty simple:
      1. Set the limits of the axes to the desired “zoomed out” state by setting appropriate limits.
      2. Use zoom(figure,'reset') – this is were the “magic” happens and the current state of the figure is internally saved as the completely zoomed out state.
      3. Achieve the desired zooming by simply setting appropriate limits on the axes.

      Steps 1 and 3 look redundant (and one would actually end up with the same visible content when just executing step 3.), however if one omits steps 1. and 2. it would not be possible to manually zoom out of the figure again!

  5. Adee Ran says:

    Thanks for exposing this (yet another) undocumented feature!

    I used a variant to set the “original zoom” of only the horizontal axis – which “zoom reset” does not seem to support:

    rpv = getappdata(gca, 'matlab_graphics_resetplotview');
    rpv.XLim = [tmin tmax]; % Use values calculated elsewhere
    setappdata(gca, 'matlab_graphics_resetplotview', rpv);

    Works like a charm.

  6. tim says:

    The suggested solution seems to fail when zoom is constrained to only one axis and/or zoom mode is changed. Not all lim fields exist in ‘matlab_graphics_resetplotview’ for constrained axis zoom (so testing the others will throw an error). It also seems that fields are not updated when zoom mode is changed (eg from horizontal to unconstrained). A change in mode can however be detected by comparing gca’s limmode for each axis to those in matlab_graphics_resetplotview. But I don’t think it is then possible to tell if the current zoom value (ie lim) is different to its original setting if that axis did not have its zoom mode on originally.
    A minimalist solution would be to store the 3 axes’ limits on creation.

    NB r2016a

Leave a Reply

Your email address will not be published. Required fields are marked *