Skip to content

Commit

Permalink
move zoom-level and center logic to image viewer state
Browse files Browse the repository at this point in the history
  • Loading branch information
kecnry committed Jan 5, 2024
1 parent 6372879 commit a9c4418
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 20 deletions.
2 changes: 2 additions & 0 deletions jdaviz/configs/imviz/plugins/viewers.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class ImvizImageView(JdavizViewerMixin, BqplotImageView, AstrowidgetsImageViewer

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# provide reference from state back to viewer to use for zoom syncing
self.state._viewer = self
self.init_astrowidgets_api()
self._subscribe_to_layers_update()

Expand Down
22 changes: 3 additions & 19 deletions jdaviz/core/astrowidgets_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,7 @@ def zoom_level(self):
if self.shape is None: # pragma: no cover
raise ValueError('Viewer is still loading, try again later')

screenx = self.shape[1]
screeny = self.shape[0]
zoom_x = screenx / (self.state.x_max - self.state.x_min)
zoom_y = screeny / (self.state.y_max - self.state.y_min)

return max(zoom_x, zoom_y) # Similar to Ginga get_scale()
return self.state.zoom_level

# Loosely based on glue/viewers/image/state.py
@zoom_level.setter
Expand All @@ -196,19 +191,8 @@ def zoom_level(self, val):
if val == 'fit':
self.state.reset_limits()
return
else:
cur_xcen = (self.state.x_min + self.state.x_max) * 0.5
new_dx = self.shape[1] * 0.5 / val
new_x_min = cur_xcen - new_dx
new_x_max = cur_xcen + new_dx

with delay_callback(self.state, 'x_min', 'x_max'):
self.state.x_min = new_x_min - 0.5
self.state.x_max = new_x_max - 0.5

# We need to adjust the limits in here to avoid triggering all
# the update events then changing the limits again.
self.state._adjust_limits_aspect()

self.state.zoom_level = val

# Discussion on why we need two different ways to set zoom at
# https://github.com/astropy/astrowidgets/issues/144
Expand Down
68 changes: 67 additions & 1 deletion jdaviz/core/freezable_state.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from echo import delay_callback
from contextlib import contextmanager
from echo import delay_callback, CallbackProperty
import numpy as np

from glue.viewers.profile.state import ProfileViewerState
Expand Down Expand Up @@ -54,8 +55,73 @@ def _reset_x_limits(self, *event):
class FreezableBqplotImageViewerState(BqplotImageViewerState, FreezableState):
linked_by_wcs = False

zoom_level = CallbackProperty(1.0, docstring='Zoom-level')
zoom_center = CallbackProperty((0.0, 0.0), docstring='Coordinates of center of zoom box')

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._during_zoom_sync = False
self.add_callback('zoom_level', self._set_zoom_level)
self.add_callback('zoom_center', self._set_zoom_center)
for attr in ('x_min', 'x_max', 'y_min', 'y_max'):
self.add_callback(attr, self._set_axes_lim)

@contextmanager
def during_zoom_sync(self):
self._during_zoom_sync = True
try:
yield
except Exception:
self._during_zoom_sync = False
raise
self._during_zoom_sync = False

def _set_zoom_level(self, zoom_level):
if self._during_zoom_sync or not hasattr(self, '_viewer') or self._viewer.shape is None:
return

cur_xcen = (self.x_min + self.x_max) * 0.5
new_dx = self._viewer.shape[1] * 0.5 / zoom_level
new_x_min = cur_xcen - new_dx
new_x_max = cur_xcen + new_dx

with self.during_zoom_sync():
self.x_min = new_x_min - 0.5
self.x_max = new_x_max - 0.5

# We need to adjust the limits in here to avoid triggering all
# the update events then changing the limits again.
self._adjust_limits_aspect()

def _set_zoom_center(self, zoom_center):
if self._during_zoom_sync:
return

cur_xcen = (self.x_min + self.x_max) * 0.5
cur_ycen = (self.y_min + self.y_max) * 0.5
delta_x = zoom_center[0] - cur_xcen
delta_y = zoom_center[1] - cur_ycen

with self.during_zoom_sync():
self.x_min += delta_x
self.x_max += delta_x
self.y_min += delta_y
self.y_max += delta_y

def _set_axes_lim(self, *args):
if self._during_zoom_sync or not hasattr(self, '_viewer') or self._viewer.shape is None:
return

screenx = self._viewer.shape[1]
screeny = self._viewer.shape[0]
zoom_x = screenx / (self.x_max - self.x_min)
zoom_y = screeny / (self.y_max - self.y_min)
center_x = 0.5 * (self.x_max + self.x_min)
center_y = 0.5 * (self.y_max + self.y_min)

with self.during_zoom_sync():
self.zoom_level = max(zoom_x, zoom_y) # Similar to Ginga get_scale()
self.zoom_center = (center_x, center_y)

def reset_limits(self, *event):
if self.reference_data is None: # Nothing to do
Expand Down

0 comments on commit a9c4418

Please sign in to comment.