From 35b1e6702e77ac5de8aed1d88f53be5b5b7ffda1 Mon Sep 17 00:00:00 2001 From: Brianna Major Date: Wed, 2 Apr 2025 17:10:55 -0400 Subject: [PATCH] Support zoom by scroll wheel Signed-off-by: Brianna Major --- hexrdgui/constants.py | 2 ++ hexrdgui/image_canvas.py | 66 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/hexrdgui/constants.py b/hexrdgui/constants.py index c89c7543a..cffa9dde5 100644 --- a/hexrdgui/constants.py +++ b/hexrdgui/constants.py @@ -154,3 +154,5 @@ class LLNLTransform: ] FIDDLE_FRAMES = 4 + +SCROLL_ZOOM_FACTOR = 1.5 \ No newline at end of file diff --git a/hexrdgui/image_canvas.py b/hexrdgui/image_canvas.py index d1de09d53..78db2313f 100644 --- a/hexrdgui/image_canvas.py +++ b/hexrdgui/image_canvas.py @@ -26,7 +26,9 @@ from hexrdgui.calibration.polar_plot import polar_viewer from hexrdgui.calibration.raw_iviewer import raw_iviewer from hexrdgui.calibration.stereo_plot import stereo_viewer -from hexrdgui.constants import OverlayType, PolarXAxisType, ViewType +from hexrdgui.constants import ( + SCROLL_ZOOM_FACTOR, OverlayType, PolarXAxisType, ViewType +) from hexrdgui.create_hedm_instrument import create_view_hedm_instrument from hexrdgui.hexrd_config import HexrdConfig from hexrdgui.masking.constants import MaskType @@ -76,6 +78,7 @@ def __init__(self, parent=None, image_names=None): self.raw_view_images_dict = {} self._mask_boundary_artists = [] self._latest_compute_view_worker = None + self.original_lims = {} # Track the current mode so that we can more lazily clear on change. self.mode = None @@ -122,6 +125,7 @@ def setup_connections(self): self.on_beam_energy_modified) HexrdConfig().panel_distortion_modified.connect( self.on_panel_distortion_changed) + self.mpl_connect('scroll_event', self.on_scroll) @property def thread_pool(self): @@ -180,6 +184,7 @@ def clear_figure(self): self.remove_all_overlay_artists() self.clear_azimuthal_integral_axis() self.mode = None + self.original_lims.clear() def clear_azimuthal_integral_axis(self): self.clear_wppf_plot() @@ -1912,6 +1917,65 @@ def draw_mask_boundaries(self, axis, det=None): **kwargs, ) + def on_scroll(self, event): + if event.inaxes is None: + return + + # Get the current axis + ax = event.inaxes + # Remember the original limits so we can't zoom out too far + key = self.mode if ax.title.get_text() == '' else ax.title.get_text() + if key not in self.original_lims: + self.original_lims[key] = [ax.get_xlim(), ax.get_ylim()] + + # Get the current x and y limits + cur_xlim = ax.get_xlim() + cur_ylim = ax.get_ylim() + + # Get the cursor position + xdata = event.xdata + ydata = event.ydata + + if event.button == 'up': + scale_factor = 1/SCROLL_ZOOM_FACTOR # zoom in + else: + scale_factor = SCROLL_ZOOM_FACTOR # zoom out + + # Set new shape + new_width = (cur_xlim[1] - cur_xlim[0]) * scale_factor + new_height = (cur_ylim[1] - cur_ylim[0]) * scale_factor + + # Calculate new limits while keeping cursor position fixed + relx = (cur_xlim[1] - xdata)/(cur_xlim[1] - cur_xlim[0]) + rely = (cur_ylim[1] - ydata)/(cur_ylim[1] - cur_ylim[0]) + new_xlim_left = xdata - new_width * (1-relx) + new_xlim_right = xdata + new_width * relx + new_ylim_bottom = ydata - new_height * (1-rely) + new_ylim_top = ydata + new_height * rely + + # Set the new limits + original_xlim, original_ylim = self.original_lims[key] + orig_xlim_left, orig_xlim_right = original_xlim + orig_ylim_bottom, orig_ylim_top = original_ylim + ax.set_xlim([ + max(orig_xlim_left, new_xlim_left), + min(orig_xlim_right, new_xlim_right) + ]) + if self.mode == ViewType.raw or self.mode == ViewType.polar: + # y-axis is reversed + ax.set_ylim([ + min(orig_ylim_bottom, new_ylim_bottom), + max(orig_ylim_top, new_ylim_top) + ]) + else: + ax.set_ylim([ + max(orig_ylim_bottom, new_ylim_bottom), + min(orig_ylim_top, new_ylim_top) + ]) + + # Redraw the canvas + self.draw_idle() + class PolarXAxisTickLocator(AutoLocator): """Subclass the tick locator so we can modify its behavior