Skip to content

Commit

Permalink
imviz matched zoom: use center & radius instead of corners (#3215)
Browse files Browse the repository at this point in the history
* imviz matched zoom: use center & radius instead of corners
* apply modification to PixelMatchedZoomMixin
* delay callback across all zoom state attrs
* update tests
  • Loading branch information
kecnry authored Oct 17, 2024
1 parent 0133d7d commit 9a1fe09
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 11 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ Other Changes and Additions
Bug Fixes
---------

- Improved performance and removed jittering for the matched box zoom tool. [#3215]

Cubeviz
^^^^^^^

Expand Down
4 changes: 3 additions & 1 deletion jdaviz/configs/cubeviz/plugins/tests/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ def test_spectrum_at_spaxel_altkey_true(cubeviz_helper, spectrum1d_cube,
flux_viewer.state.x_max = 40
flux_viewer.state.y_max = 35
v = uncert_viewer
assert (v.state.x_min, v.state.x_max, v.state.y_min, v.state.y_max) == (20, 40, 15, 35)
assert v.state.zoom_center_x == flux_viewer.state.zoom_center_x
assert v.state.zoom_center_y == flux_viewer.state.zoom_center_y
assert v.state.zoom_radius == flux_viewer.state.zoom_radius
t_linkedpan.deactivate()


Expand Down
8 changes: 6 additions & 2 deletions jdaviz/configs/cubeviz/plugins/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@


class _PixelMatchedZoomMixin(_MatchedZoomMixin):
match_keys = ('x_min', 'x_max', 'y_min', 'y_max')
disable_matched_zoom_in_other_viewer = False
match_axes = []
disable_matched_zoom_in_other_viewer = True

@property
def match_keys(self):
return ['zoom_center_x', 'zoom_center_y', 'zoom_radius']

def _is_matched_viewer(self, viewer):
return isinstance(viewer, BqplotImageView)
Expand Down
6 changes: 5 additions & 1 deletion jdaviz/configs/imviz/plugins/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@


class _ImvizMatchedZoomMixin(_MatchedZoomMixin):
match_keys = ('x_min', 'x_max', 'y_min', 'y_max')
match_axes = []
disable_matched_zoom_in_other_viewer = True

@property
def match_keys(self):
return ['zoom_center_x', 'zoom_center_y', 'zoom_radius']

def _is_matched_viewer(self, viewer):
return isinstance(viewer, BqplotImageView)

Expand Down
16 changes: 11 additions & 5 deletions jdaviz/configs/imviz/tests/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,25 @@ def test_panzoom_tools(self):

t = v.toolbar.tools['jdaviz:boxzoommatch']
# original limits (x_min, x_max, y_min, y_max): -0.5 9.5 -0.5 9.5
# original limits (zoom_center_x, zoom_center_y, zoom_radius): 4.5 4.5 5.0
original_limits = (v.state.x_min, v.state.x_max, v.state.y_min, v.state.y_max)
assert_allclose(original_limits, (-0.5, 9.5, -0.5, 9.5))
original_center_rad = (v.state.zoom_center_x, v.state.zoom_center_y, v.state.zoom_radius)
assert_allclose(original_center_rad, (4.5, 4.5, 5.0))
assert_allclose((v2.state.x_min, v2.state.x_max, v2.state.y_min, v2.state.y_max), original_limits) # noqa
assert_allclose((v2.state.zoom_center_x, v2.state.zoom_center_y, v2.state.zoom_radius), original_center_rad) # noqa
t.activate()
t.save_prev_zoom()
v.state.x_min, v.state.x_max, v.state.y_min, v.state.y_max = (1, 8, 1, 8)
# second viewer should match these changes
assert (v2.state.x_min, v2.state.x_max, v2.state.y_min, v2.state.y_max) == (1, 8, 1, 8)
# second viewer should match these changes, wrt zoom center and radius
assert v2.state.zoom_center_x == v.state.zoom_center_x
assert v2.state.zoom_center_y == v.state.zoom_center_y
assert v2.state.zoom_radius == v.state.zoom_radius

v.toolbar.tools['jdaviz:prevzoom'].activate()
# both should revert since they're still linked
assert_allclose((v.state.x_min, v.state.x_max, v.state.y_min, v.state.y_max), original_limits) # noqa
assert_allclose((v2.state.x_min, v2.state.x_max, v2.state.y_min, v2.state.y_max), original_limits) # noqa
# both should revert since they're still linked (boxzoommatch will re-activate)
assert_allclose((v.state.zoom_center_x, v.state.zoom_center_y, v.state.zoom_radius), original_center_rad) # noqa
assert_allclose((v2.state.zoom_center_x, v2.state.zoom_center_y, v2.state.zoom_radius), original_center_rad) # noqa

v.toolbar.tools['jdaviz:prevzoom'].activate()
# both should revert since they're still linked
Expand Down
20 changes: 18 additions & 2 deletions jdaviz/core/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import numpy as np
from echo import delay_callback
from functools import cached_property
from glue.config import viewer_tool
from glue.core import HubListener
from glue.viewers.common.tool import Tool
Expand Down Expand Up @@ -67,6 +68,14 @@ def match_keys(self):
keys += [f'{ax}_min', f'{ax}_max']
return keys

@cached_property
def delay_callback_keys(self):
all_keys = ['x_min', 'x_max', 'y_min', 'y_max',
'zoom_center_x', 'zoom_center_y', 'zoom_radius']
return [k for k in all_keys
if np.all([hasattr(v.state, k)
for v in self._iter_matched_viewers(include_self=True)])]

def activate(self):
if self.disable_matched_zoom_in_other_viewer:
# mapping limits are not guaranteed to roundtrip, so we need to disable
Expand Down Expand Up @@ -115,8 +124,8 @@ def on_limits_change(self, *args):
viewer.zoom_level = old_level * float(to_fov_sky / orig_fov_sky)
viewer.center_on(sky_cen)

else:
with delay_callback(viewer.state, *self.match_keys):
elif len(self.match_axes):
with delay_callback(viewer.state, *self.delay_callback_keys):
for ax in self.match_axes:
if None in orig_lims.values():
orig_range = np.inf
Expand All @@ -134,6 +143,13 @@ def on_limits_change(self, *args):
if not np.isnan(value) and (orig_value is None or
abs(value-orig_lims.get(k, np.inf)) > tol):
setattr(viewer.state, k, value)
else:
# match keys, but not match axes (e.g., zoom_center and zoom_radius)
with delay_callback(viewer.state, *self.delay_callback_keys):
for k in self.match_keys:
value = to_lims.get(k)
if not np.isnan(value):
setattr(viewer.state, k, value)

def is_visible(self):
return len(self.viewer.jdaviz_app._viewer_store) > 1
Expand Down

0 comments on commit 9a1fe09

Please sign in to comment.