Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Fix data cursor for images with additional transform #18737

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 5, 2021

Conversation

alexschlueter
Copy link
Contributor

@alexschlueter alexschlueter commented Oct 15, 2020

PR Summary

When an image artist gets an additional transform supplied via set_transform or the transform=... argument, the data cursor will give values at the wrong coordinates. Example:

import matplotlib.pyplot as plt
from matplotlib.transforms import Affine2D
import numpy as np

fig, ax = plt.subplots()
trans = Affine2D().scale(2).rotate(0.5)
im = ax.imshow(np.arange(100).reshape(10, 10),
                transform=trans + ax.transData)
ax.set_xlim(-12, 20)
ax.set_ylim(-5, 30)
plt.show()

This happens because the current AxesImage.get_cursor_data uses the xdata and ydata attributes in the MouseEvent, which only undo the axis transform, but not the additional artist transform.

This PR adds a test and fixes the issue by applying the inversion of the image artists transform directly to the MouseEvents x and y attributes.

PR Checklist

  • Has pytest style unit tests (and pytest passes).
  • Is Flake 8 compliant (run flake8 on changed files to check).
  • New features are documented, with examples if plot related.
  • Documentation is sphinx and numpydoc compliant (the docs should build without error).
  • Conforms to Matplotlib style conventions (install flake8-docstrings and pydocstyle<4 and run flake8 --docstring-convention=all).
  • New features have an entry in doc/users/next_whats_new/ (follow instructions in README.rst there).
  • API changes documented in doc/api/next_api_changes/ (follow instructions in README.rst there).

array_extent = Bbox([[0, 0], arr.shape[:2]])
trans = BboxTransform(boxin=data_extent, boxout=array_extent)
point = trans.transform([event.ydata, event.xdata])
data_extent = Bbox([[xmin, ymin], [xmax, ymax]])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused - was the previous version just a huge bug?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you referring to the switched xdata and ydata coordinates in the original version? I think this was fine originally (this code was covered quite well by tests in test_cursor_data()).
The first index of the array runs along the y-axis, while the second index runs along the x-axis. This means that a flip has to happen somewhere.
Originally, the flip was in the line point = trans.transform([event.ydata, event.xdata]). However, since we now want to apply a different transform first, we have to input the points in the right order (first x, then y). The flip still has to happen though, so we instead flip arr.shape and change i, j to j, i when we get the result.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got it - I didn't trace it all through so was confused...

@jklymak
Copy link
Member

jklymak commented Oct 15, 2020

Can you provide a self-contained example in the description to make it easier for reviewers to test themselves?

@jklymak jklymak added this to the v3.4.0 milestone Oct 15, 2020
@alexschlueter
Copy link
Contributor Author

Example added 👍

@@ -661,7 +661,8 @@ def contains(self, mouseevent):
# collection on nonlinear transformed coordinates.
# TODO: consider returning image coordinates (shouldn't
# be too difficult given that the image is rectilinear
x, y = mouseevent.xdata, mouseevent.ydata
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you aren't using xdata/ydata any more, does that mean the comment about about figimage is fixed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it should be fine to remove the self.axes is not mouseevent.inaxes check now and contains() should also work for FigureImage. However, FigureImage doesn't have a get_cursor_data() yet, so I think the only place where contains would be used for a FigureImage is for the pick events? I'm not sure about that though, maybe @anntzer can help since he wrote the original comment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's likely that this fixes some of the problems I had noticed when I put in the comment, but I haven't reviewed this carefully enough to see whether all problems are indeed fixed.

@alexschlueter
Copy link
Contributor Author

Looking into this more, it seems like the current MouseEvent / contains code is built with the assumption that artists don't set their own transform. Otherwise, using the axes transData to set xdata and ydata in the event does not generally make sense, since artists would always have to invert their own specific transform to check for contains. Maybe this should be changed globally?

@QuLogic QuLogic added the status: needs comment/discussion needs consensus on next step label Jan 5, 2021
Copy link
Member

@jklymak jklymak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, sorry this sat for so long!

@jklymak jklymak merged commit 98023a2 into matplotlib:master Jan 5, 2021
@jklymak jklymak removed the status: needs comment/discussion needs consensus on next step label Jan 5, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants