Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added glue/viewers3d/__init__.py
Empty file.
Empty file.
47 changes: 47 additions & 0 deletions glue/viewers3d/common/layer_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from echo import CallbackProperty, keep_in_sync
from glue.core.message import LayerArtistUpdatedMessage
from glue.viewers.common.state import LayerState

__all__ = ['LayerState3D']


class LayerState3D(LayerState):
"""
A base state object for all Vispy layers
"""

color = CallbackProperty()
alpha = CallbackProperty()

def __init__(self, **kwargs):

super(LayerState3D, self).__init__(**kwargs)

self._sync_color = None
self._sync_alpha = None

self.add_callback('layer', self._layer_changed)
self._layer_changed()

self.add_global_callback(self._notify_layer_update)

def _notify_layer_update(self, **kwargs):
message = LayerArtistUpdatedMessage(self)
if self.layer is not None and self.layer.hub is not None:
self.layer.hub.broadcast(message)

def _layer_changed(self):

if self._sync_color is not None:
self._sync_color.stop_syncing()

if self._sync_alpha is not None:
self._sync_alpha.stop_syncing()

if self.layer is not None:

self.color = self.layer.style.color
self.alpha = self.layer.style.alpha

self._sync_color = keep_in_sync(self, 'color', self.layer.style, 'color')
self._sync_alpha = keep_in_sync(self, 'alpha', self.layer.style, 'alpha')
130 changes: 130 additions & 0 deletions glue/viewers3d/common/viewer_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import numpy as np

from echo import (CallbackProperty, SelectionCallbackProperty,
delay_callback, ListCallbackProperty)
from glue.core.state_objects import StateAttributeLimitsHelper
from glue.viewers.common.state import ViewerState

__all__ = ['ViewerState3D']


class ViewerState3D(ViewerState):
"""
A common state object for all 3D viewers
"""

x_att = SelectionCallbackProperty()
x_min = CallbackProperty(0)
x_max = CallbackProperty(1)
x_stretch = CallbackProperty(1.)

y_att = SelectionCallbackProperty(default_index=1)
y_min = CallbackProperty(0)
y_max = CallbackProperty(1)
y_stretch = CallbackProperty(1.)

z_att = SelectionCallbackProperty(default_index=2)
z_min = CallbackProperty(0)
z_max = CallbackProperty(1)
z_stretch = CallbackProperty(1.)

visible_axes = CallbackProperty(True)
perspective_view = CallbackProperty(False)
clip_data = CallbackProperty(True)
native_aspect = CallbackProperty(False)
line_width = CallbackProperty(1.)

layers = ListCallbackProperty()

_limits_cache = CallbackProperty()

def _update_priority(self, name):
if name == 'layers':
return 2
elif name.endswith(('_min', '_max')):
return 0
else:
return 1

def __init__(self, **kwargs):

super(ViewerState3D, self).__init__(**kwargs)

if self._limits_cache is None:
self._limits_cache = {}

self.x_lim_helper = StateAttributeLimitsHelper(self, attribute='x_att',
lower='x_min', upper='x_max',
cache=self._limits_cache)

self.y_lim_helper = StateAttributeLimitsHelper(self, attribute='y_att',
lower='y_min', upper='y_max',
cache=self._limits_cache)

self.z_lim_helper = StateAttributeLimitsHelper(self, attribute='z_att',
lower='z_min', upper='z_max',
cache=self._limits_cache)

# TODO: if limits_cache is re-assigned to a different object, we need to
# update the attribute helpers. However if in future we make limits_cache
# into a smart dictionary that can call callbacks when elements are
# changed then we shouldn't always call this. It'd also be nice to
# avoid this altogether and make it more clean.
self.add_callback('_limits_cache', self._update_limits_cache)

def reset_limits(self):
self.x_lim_helper.log = False
self.x_lim_helper.percentile = 100.
self.x_lim_helper.update_values(force=True)
self.y_lim_helper.log = False
self.y_lim_helper.percentile = 100.
self.y_lim_helper.update_values(force=True)
self.z_lim_helper.log = False
self.z_lim_helper.percentile = 100.
self.z_lim_helper.update_values(force=True)

def _update_limits_cache(self, *args):
self.x_lim_helper._cache = self._limits_cache
self.x_lim_helper._update_attribute()
self.y_lim_helper._cache = self._limits_cache
self.y_lim_helper._update_attribute()
self.z_lim_helper._cache = self._limits_cache
self.z_lim_helper._update_attribute()

@property
def aspect(self):
# TODO: this could be cached based on the limits, but is not urgent
aspect = np.array([1, 1, 1], dtype=float)
if self.native_aspect:
aspect[0] = 1.
aspect[1] = (self.y_max - self.y_min) / (self.x_max - self.x_min)
aspect[2] = (self.z_max - self.z_min) / (self.x_max - self.x_min)
aspect /= aspect.max()
return aspect

def reset(self):
pass

def flip_x(self):
self.x_lim_helper.flip_limits()

def flip_y(self):
self.y_lim_helper.flip_limits()

def flip_z(self):
self.z_lim_helper.flip_limits()

@property
def clip_limits(self):
return (self.x_min, self.x_max,
self.y_min, self.y_max,
self.z_min, self.z_max)

def set_limits(self, x_min, x_max, y_min, y_max, z_min, z_max):
with delay_callback(self, 'x_min', 'x_max', 'y_min', 'y_max', 'z_min', 'z_max'):
self.x_min = x_min
self.x_max = x_max
self.y_min = y_min
self.y_max = y_max
self.z_min = z_min
self.z_max = z_max
Empty file.
124 changes: 124 additions & 0 deletions glue/viewers3d/scatter/layer_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
from glue.config import colormaps
from echo import CallbackProperty, SelectionCallbackProperty, keep_in_sync, delay_callback
from glue.core.state_objects import StateAttributeLimitsHelper
from glue.core.data_combo_helper import ComponentIDComboHelper
from ..common.layer_state import LayerState3D

__all__ = ['ScatterLayerState']


class ScatterLayerState(LayerState3D):
"""
A state object for volume layers
"""

size_mode = SelectionCallbackProperty()
size = CallbackProperty()
size_attribute = SelectionCallbackProperty()
size_vmin = CallbackProperty()
size_vmax = CallbackProperty()
size_scaling = CallbackProperty(1)

color_mode = SelectionCallbackProperty()
cmap_attribute = SelectionCallbackProperty()
cmap_vmin = CallbackProperty()
cmap_vmax = CallbackProperty()
cmap = CallbackProperty()

xerr_visible = CallbackProperty(False)
xerr_attribute = SelectionCallbackProperty()
yerr_visible = CallbackProperty(False)
yerr_attribute = SelectionCallbackProperty()
zerr_visible = CallbackProperty(False)
zerr_attribute = SelectionCallbackProperty()

vector_visible = CallbackProperty(False)
vx_attribute = SelectionCallbackProperty()
vy_attribute = SelectionCallbackProperty()
vz_attribute = SelectionCallbackProperty()
vector_scaling = CallbackProperty(1)
vector_origin = SelectionCallbackProperty(default_index=1)
vector_arrowhead = CallbackProperty()

_size_limits_cache = CallbackProperty({})
_cmap_limits_cache = CallbackProperty({})

def __init__(self, layer=None, **kwargs):

self._sync_markersize = None

super(ScatterLayerState, self).__init__(layer=layer)

if self.layer is not None:

self.color = self.layer.style.color
self.size = self.layer.style.markersize
self.alpha = self.layer.style.alpha

ScatterLayerState.color_mode.set_choices(self, ['Fixed', 'Linear'])
ScatterLayerState.size_mode.set_choices(self, ['Fixed', 'Linear'])

self.size_att_helper = ComponentIDComboHelper(self, 'size_attribute')
self.cmap_att_helper = ComponentIDComboHelper(self, 'cmap_attribute')
self.xerr_att_helper = ComponentIDComboHelper(self, 'xerr_attribute', categorical=False)
self.yerr_att_helper = ComponentIDComboHelper(self, 'yerr_attribute', categorical=False)
self.zerr_att_helper = ComponentIDComboHelper(self, 'zerr_attribute', categorical=False)

self.vx_att_helper = ComponentIDComboHelper(self, 'vx_attribute', categorical=False)
self.vy_att_helper = ComponentIDComboHelper(self, 'vy_attribute', categorical=False)
self.vz_att_helper = ComponentIDComboHelper(self, 'vz_attribute', categorical=False)

self.size_lim_helper = StateAttributeLimitsHelper(self, attribute='size_attribute',
lower='size_vmin', upper='size_vmax',
cache=self._size_limits_cache)

self.cmap_lim_helper = StateAttributeLimitsHelper(self, attribute='cmap_attribute',
lower='cmap_vmin', upper='cmap_vmax',
cache=self._cmap_limits_cache)

vector_origin_display = {'tail': 'Tail of vector',
'middle': 'Middle of vector',
'tip': 'Tip of vector'}
ScatterLayerState.vector_origin.set_choices(self, ['tail', 'middle', 'tip'])
ScatterLayerState.vector_origin.set_display_func(self, vector_origin_display.get)

self.add_callback('layer', self._on_layer_change)
if layer is not None:
self._on_layer_change()

self.cmap = colormaps.members[0][1]

self.update_from_dict(kwargs)

def _on_layer_change(self, layer=None):

with delay_callback(self, 'cmap_vmin', 'cmap_vmax', 'size_vmin', 'size_vmax'):
helpers = [self.size_att_helper, self.cmap_att_helper,
self.xerr_att_helper, self.yerr_att_helper, self.zerr_att_helper,
self.vx_att_helper, self.vy_att_helper, self.vz_att_helper]
if self.layer is None:
for helper in helpers:
helper.set_multiple_data([])
else:
for helper in helpers:
helper.set_multiple_data([self.layer])

def update_priority(self, name):
return 0 if name.endswith(('vmin', 'vmax')) else 1

def _layer_changed(self):

super(ScatterLayerState, self)._layer_changed()

if self._sync_markersize is not None:
self._sync_markersize.stop_syncing()

if self.layer is not None:
self.size = self.layer.style.markersize
self._sync_markersize = keep_in_sync(self, 'size', self.layer.style, 'markersize')

def flip_size(self):
self.size_lim_helper.flip_limits()

def flip_cmap(self):
self.cmap_lim_helper.flip_limits()
25 changes: 25 additions & 0 deletions glue/viewers3d/scatter/viewer_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from glue.core.data_combo_helper import ComponentIDComboHelper
from glue.viewers3d.common.viewer_state import ViewerState3D

__all__ = ['ScatterViewerState3D']


class ScatterViewerState3D(ViewerState3D):

def __init__(self, **kwargs):

super(ScatterViewerState3D, self).__init__()

self.x_att_helper = ComponentIDComboHelper(self, 'x_att', categorical=False)
self.y_att_helper = ComponentIDComboHelper(self, 'y_att', categorical=False)
self.z_att_helper = ComponentIDComboHelper(self, 'z_att', categorical=False)

self.add_callback('layers', self._on_layers_change)

self.update_from_dict(kwargs)

def _on_layers_change(self, *args):
layers_data = [layer_state.layer for layer_state in self.layers]
self.x_att_helper.set_multiple_data(layers_data)
self.y_att_helper.set_multiple_data(layers_data)
self.z_att_helper.set_multiple_data(layers_data)
Empty file.
57 changes: 57 additions & 0 deletions glue/viewers3d/volume/layer_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from glue.core import Subset
from echo import (CallbackProperty, SelectionCallbackProperty,
delay_callback)
from glue.core.state_objects import StateAttributeLimitsHelper
from glue.core.data_combo_helper import ComponentIDComboHelper
from ..common.layer_state import LayerState3D

__all__ = ['VolumeLayerState']


class VolumeLayerState(LayerState3D):
"""
A state object for volume layers
"""

attribute = SelectionCallbackProperty()
vmin = CallbackProperty()
vmax = CallbackProperty()
subset_mode = CallbackProperty('data')
_limits_cache = CallbackProperty({})

def __init__(self, layer=None, **kwargs):

super(VolumeLayerState, self).__init__(layer=layer)

if self.layer is not None:

self.color = self.layer.style.color
self.alpha = self.layer.style.alpha

self.att_helper = ComponentIDComboHelper(self, 'attribute')

self.lim_helper = StateAttributeLimitsHelper(self, attribute='attribute',
lower='vmin', upper='vmax',
cache=self._limits_cache)

self.add_callback('layer', self._on_layer_change)
if layer is not None:
self._on_layer_change()

if isinstance(self.layer, Subset):
self.vmin = 0
self.vmax = 1

self.update_from_dict(kwargs)

def _on_layer_change(self, layer=None):

with delay_callback(self, 'vmin', 'vmin'):

if self.layer is None:
self.att_helper.set_multiple_data([])
else:
self.att_helper.set_multiple_data([self.layer])

def update_priority(self, name):
return 0 if name.endswith(('vmin', 'vmax')) else 1
Loading