Skip to content

Commit

Permalink
handle mixed state for stretch params (knots) (#2623)
Browse files Browse the repository at this point in the history
* handle mixed state for stretch params (knots)
  • Loading branch information
kecnry authored Dec 18, 2023
1 parent 8128788 commit f35136d
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 8 deletions.
2 changes: 1 addition & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

New Features
------------
- Stretch bounds tool now enables dynamic adjustment of spline knots. [#2545]
- Stretch bounds tool now enables dynamic adjustment of spline knots. [#2545, #2623]

Cubeviz
^^^^^^^
Expand Down
12 changes: 10 additions & 2 deletions jdaviz/configs/default/plugins/plot_options/plot_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,12 @@ def vue_unmix_state(self, names):
for name in names:
sync_state = getattr(self, name)
sync_state.unmix_state()
if 'stretch_params' in names:
# there is no way to call send_state to force the update to the layers,
# so we'll force an update by clearing first
stretch_params = dict(self.stretch_params_value)
self.stretch_params_value = {}
self.stretch_params_value = stretch_params

def vue_set_value(self, data):
attr_name = data.get('name')
Expand Down Expand Up @@ -692,13 +698,15 @@ def apply_RGB_presets(self):
def vue_apply_RGB_presets(self, data):
self.apply_RGB_presets()

@observe('stretch_function_sync', 'stretch_vmin_sync', 'stretch_vmax_sync',
@observe('stretch_function_sync', 'stretch_params_sync',
'stretch_vmin_sync', 'stretch_vmax_sync',
'image_color_mode_sync', 'image_color_sync', 'image_colormap_sync')
def _update_stretch_hist_sync(self, msg={}):
# the histogram should show as mixed if ANY of the input parameters are mixed
# these should match in the @observe above, all_syncs here, as well as the strings
# passed to unmix_state in the <glue-state-sync-wrapper> in plot_options.vue
all_syncs = [self.stretch_function_sync, self.stretch_vmin_sync, self.stretch_vmax_sync,
all_syncs = [self.stretch_function_sync, self.stretch_params_sync,
self.stretch_vmin_sync, self.stretch_vmax_sync,
self.image_color_mode_sync, self.image_color_sync, self.image_colormap_sync]
self.stretch_hist_sync = {'in_subscribed_states': bool(np.any([sync.get('in_subscribed_states', False) for sync in all_syncs])), # noqa
'mixed': bool(np.any([sync.get('mixed', False) for sync in all_syncs]))} # noqa
Expand Down
3 changes: 2 additions & 1 deletion jdaviz/configs/default/plugins/plot_options/plot_options.vue
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,8 @@
<glue-state-sync-wrapper
:sync="stretch_hist_sync"
:multiselect="layer_multiselect"
@unmix-state="unmix_state(['stretch_function', 'stretch_vmin', 'stretch_vmax',
@unmix-state="unmix_state(['stretch_function', 'stretch_params',
'stretch_vmin', 'stretch_vmax',
'image_color_mode', 'image_color', 'image_colormap'])"
>
<jupyter-widget :widget="stretch_histogram_widget"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,15 @@ def test_track_mixed_states(imviz_helper):
assert po.image_color.value == "#00ff00"
assert po.layer.items[-1]["colors"][0] == "#00ff00"
assert po.layer.items[-2]["colors"][0] == "#00ff00"

# test spline stretch mixed state
po.viewer_selected = ["imviz-0"]
po.layer_selected = ["array_1"]
po.stretch_function_value = 'spline'
po.viewer_selected = ["imviz-0", "imviz-1"]
assert po.stretch_function_sync['mixed']

po.vue_unmix_state(['stretch_function', 'stretch_params'])
assert not po.stretch_function_sync['mixed']
assert po.stretch_function_value == 'spline'
assert not po.stretch_params_sync['mixed']
29 changes: 25 additions & 4 deletions jdaviz/core/template_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3223,6 +3223,8 @@ def _get_glue_value(self, state):
# return False if the layer itself is not visible. Setting this object
# to True will then set both glue_name and visible to True.
return getattr(state, glue_name) and getattr(state, 'visible')
if glue_name in ('c_min', 'c_max'):
return float(getattr(state, glue_name))

return getattr(state, glue_name)

Expand Down Expand Up @@ -3301,10 +3303,22 @@ def _on_viewer_layer_changed(self, msg=None):

def is_mixed(self, glue_values):
if len(glue_values) and isinstance(glue_values[0], dict):
# If we want to expose dictionary inputs in the plot options UI,
# this will need to be updated to check if any of the dictionaries
# in the list are not exact matches
return None
if len(np.unique([len(item) for item in glue_values], axis=0)) > 1:
# different lengths
return True

# guaranteed to have same lengths
if len(np.unique([list(item.keys()) for item in glue_values], axis=0)) > 1:
# different keys
return True

# guaranteed to each have the same set of keys, so now we can loop over keys and
# compare values
for k in glue_values[0].keys():
if len(np.unique([item.get(k) for item in glue_values], axis=0)) > 1:
return True

return False
return len(np.unique(glue_values, axis=0)) > 1

def _update_mixed_state(self):
Expand All @@ -3315,6 +3329,13 @@ def _update_mixed_state(self):
for state in self.linked_states:
current_glue_values.append(self._get_glue_value(state))
mixed = self.is_mixed(current_glue_values)
# ensure the value corresponds to the first entry, this prevents the case where a glue
# change to one of the linked_states changes the value that will be adopted when
# unmixing something in mixed state and results in more consistent and predictable
# behavior
self._processing_change_from_glue = True
self.value = current_glue_values[0]
self._processing_change_from_glue = False
self.sync = {**self.sync,
'mixed': mixed}

Expand Down

0 comments on commit f35136d

Please sign in to comment.