Skip to content

Snap selectors #21374

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 3 commits into from
Apr 21, 2022
Merged

Snap selectors #21374

merged 3 commits into from
Apr 21, 2022

Conversation

ericpre
Copy link
Member

@ericpre ericpre commented Oct 16, 2021

PR Summary

Snap SpanSelector position to specific values.

import matplotlib.pyplot as plt
from matplotlib.widgets import SpanSelector, RectangleSelector
import numpy as np

y = np.arange(25)
snap_values = np.append(y-0.5, [y[-1] + 0.5])
snap_values_2D = np.array([y]*2)

fig, ax = plt.subplots()
ax.plot(y, 'o')

span = SpanSelector(ax, print, direction='horizontal',
                    interactive=True,
                    drag_from_anywhere=True,
                    snap_values=snap_values)

@tacaswell
Copy link
Member

This PR is affected by a re-writing of our history to remove a large number of accidentally committed files see discourse for details.

To recover this PR it will need be rebased onto the new default branch (main). There are several ways to accomplish this, but we recommend (assuming that you call the matplotlib/matplotlib remote "upstream"

git remote update
git checkout main
git merge --ff-only upstream/main
git checkout YOUR_BRANCH
git rebase --onto=main upstream/old_master
# git rebase -i main # if you prefer
git push --force-with-lease   # assuming you are tracking your branch

If you do not feel comfortable doing this or need any help please reach out to any of the Matplotlib developers. We can either help you with the process or do it for you.

Thank you for your contributions to Matplotlib and sorry for the inconvenience.

@ericpre ericpre force-pushed the snap_selectors branch 2 times, most recently from 267d970 to 1fd11a4 Compare December 5, 2021 12:16
@timhoffm
Copy link
Member

timhoffm commented Dec 5, 2021

What is the use case for this? I could imagine snapping to artist bounding boxes or points in a Line2D, but snapping to user specified values seems not too useful (or a reasonable user-facing API). Why and how do you anticipate this will be used? Without a clear idea of that, I'm afraid this complicates the code base and adds a maintainance burden without a clear benefit.

@ericpre
Copy link
Member Author

ericpre commented Dec 6, 2021

The motivation for libraries using matplotlib selectors is to support cases where only a subset of selection is valid. It is implemented in a generic manner to be flexible and avoid an heuristic approach.
A common scientific use case is to be able to select a range of "binned data" obtained from measurement, where events are measured in intervals. In this case, using a selector with an end inside a bin doesn't have physical meaning and it is useful to be able to be to use a selector unambiguously. In the hyperspy library, we had a number of horrible hacks on the matplotlib selector and patches to do this...
This is illustrated by the following example, where the intervals are clearly visible and the selector extents are snapped to the ends of the intervals:

import matplotlib.pyplot as plt
from matplotlib.widgets import SpanSelector
import numpy as np

x = np.arange(8)
values = np.random.random(size=8)
snap_values = np.append(x-0.5, [x[-1] + 0.5])

_, ax = plt.subplots()
plt.plot(values, drawstyle='steps-mid')

span = SpanSelector(ax, print, direction='horizontal',
                    interactive=True,
                    drag_from_anywhere=True,
                    snap_values=snap_values)

This is relevant for many detectors used in spectroscopy, digital camera, etc.

@timhoffm
Copy link
Member

timhoffm commented Dec 6, 2021

Ok, for a SpanSelector this makes sense.

I'm much more unclear what the API and desired effects should be for the 2D selectors. It seems that there x and y are just independent sets of snap-positions per axis. 1) You might want to have correlations, e.g. for scatter(x, y) with random x, y I could want to snap a PolygonSelector to data points only. 2) If they are supposed to be independent a 2D array is not a good choice. I might have a different number of snap positions for x and y.

IMHO one would have to carefully think through use cases and suitable API & behavior per Selector separately. Otherwise, we may end in a corner we don't want to be in. How about starting with SpanSelector, which seems pretty straight forward? And leave the other selectors for later?

On a more conecptual note, it may (maybe at a later time) be desirable to inject the snap logic, i.e. pass a function in that does the snapping: snap(x, y) -> x_snapped, y_snapped. We should keep this in mind when naming: snap_values=snap is okish but maybe there's a better name. We could also consider building the internal logic around such an approach.

@ericpre
Copy link
Member Author

ericpre commented Dec 7, 2021

For 2D selectors, it will be the same for images, which have been acquired with a digital camera, where the intensity of each pixels is expected to measure a number of events hitting this specific pixels.

import matplotlib.pyplot as plt
from matplotlib.widgets import RectangleSelector
import numpy as np

values = np.random.random(size=(8, 8))
snap_values_2D = np.tile(np.mgrid[-0.5:8.5], (2, 1))

_, ax = plt.subplots()
plt.imshow(values)

rect = RectangleSelector(ax, print,
                         interactive=True,
                         drag_from_anywhere=True,
                         snap_values=snap_values_2D)

For scatter, I don't know and indeed I don't think that it makes sense.

On a more conecptual note, it may (maybe at a later time) be desirable to inject the snap logic, i.e. pass a function in that does the snapping: snap(x, y) -> x_snapped, y_snapped. We should keep this in mind when naming: snap_values=snap is okish but maybe there's a better name. We could also consider building the internal logic around such an approach.

I explore a bit this option, but I couldn't get anyway nice. The approach of this PR has the big advantage of being simple.

@ericpre ericpre force-pushed the snap_selectors branch 2 times, most recently from 5bd6cd6 to cd8c924 Compare December 11, 2021 13:01
@ericpre
Copy link
Member Author

ericpre commented Apr 7, 2022

Sorry for coming back on this only now, I rebased and added this feature for the SpanSelector only - the changes with other selectors (RectangleSelector, PolygonSelector) have been removed.

@ericpre ericpre requested a review from timhoffm April 7, 2022 16:58
Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com>
@oscargus oscargus merged commit a7b7260 into matplotlib:main Apr 21, 2022
@QuLogic QuLogic added this to the v3.6.0 milestone Apr 21, 2022
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.

5 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy