Skip to content

Commit

Permalink
Merge pull request #23 from enthought/feature/misc-volren-ui-knobs
Browse files Browse the repository at this point in the history
Misc volume viewer features
  • Loading branch information
tonysyu committed Oct 9, 2014
2 parents 4945211 + 5c51251 commit 342e308
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 30 deletions.
7 changes: 4 additions & 3 deletions ensemble/volren/volume_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ def _setup_mapper_types(self):
input = self.module_manager.source.outputs[0]
data_types = (vtkConstants.VTK_UNSIGNED_CHAR,
vtkConstants.VTK_UNSIGNED_SHORT)
mapper_types = []
if input.point_data.scalars.data_type not in data_types:
if 'FixedPointVolumeRayCastMapper' in self._available_mapper_types:
self._mapper_types = ['FixedPointVolumeRayCastMapper']
mapper_types = ['FixedPointVolumeRayCastMapper']
else:
error('Available volume mappers only work with \
unsigned_char or unsigned_short datatypes')
Expand All @@ -38,8 +39,8 @@ def _setup_mapper_types(self):
for mapper in check:
if mapper in self._available_mapper_types:
mapper_types.append(mapper)
self._mapper_types = mapper_types
self._mapper_types.append('VolumeTextureMapper3D')
mapper_types.append('VolumeTextureMapper3D')
self._mapper_types = mapper_types

def _volume_mapper_type_changed(self, value):
if self.module_manager is None:
Expand Down
50 changes: 34 additions & 16 deletions ensemble/volren/volume_cut_planes.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
from collections import OrderedDict

from mayavi import mlab
from mayavi.core.api import PipelineBase
from traits.api import Instance, Range, on_trait_change
from traits.api import Enum, Instance, List, Range, Str, on_trait_change

from .volume_scene_member import ABCVolumeSceneMember


CUT_COLORMAPS = OrderedDict([
(u'Gray', 'gray'),
(u'Bone', 'bone'),
(u'RdBu', 'RdBu'),
])


class VolumeCutPlanes(ABCVolumeSceneMember):
""" An object which adds image cut planes to a scene containing a Volume.
"""
Expand All @@ -14,6 +23,10 @@ class VolumeCutPlanes(ABCVolumeSceneMember):
image_plane_widget_y = Instance(PipelineBase)
image_plane_widget_z = Instance(PipelineBase)

# Colormap selection
available_cut_colormaps = List(Str, CUT_COLORMAPS.keys())
selected_cut_color_map = Enum(values='available_cut_colormaps')

# A global multiplier to the opacity transfer function.
slicer_alpha = Range(0.0, 1.0, value=1.0)

Expand Down Expand Up @@ -44,14 +57,15 @@ def add_actors_to_scene(self, scene_model, volume_actor):
# -------------------------------------------------------------------------

def _slicer_alpha_changed(self, alpha):
image_plane_widgets = (
self.image_plane_widget_x, self.image_plane_widget_y,
self.image_plane_widget_z
)
for ipw in image_plane_widgets:
if ipw is not None:
ipw.ipw.texture_plane_property.opacity = alpha
ipw.render()
for ipw in self._iter_image_plane_widgets():
ipw.ipw.texture_plane_property.opacity = alpha
ipw.render()

def _selected_cut_color_map_changed(self, new):
for ipw in self._iter_image_plane_widgets():
lut_manager = ipw.module_manager.scalar_lut_manager
lut_manager.lut_mode = CUT_COLORMAPS[new]
ipw.render()

@on_trait_change('cut_brightness,cut_contrast,image_plane_widget_z')
def _on_cut_brightness_contrast(self):
Expand All @@ -68,13 +82,8 @@ def _on_cut_brightness_contrast(self):
radius = data_radius * pow(2, -self.cut_contrast)
lut_manager.data_range = (level - radius, level + radius)

image_plane_widgets = (
self.image_plane_widget_x, self.image_plane_widget_y,
self.image_plane_widget_z
)
for ipw in image_plane_widgets:
if ipw is not None:
ipw.render()
for ipw in self._iter_image_plane_widgets():
ipw.render()

# -------------------------------------------------------------------------
# Private interface
Expand All @@ -87,3 +96,12 @@ def _find_volume_data_source(self, scene_model, volume_actor):
return child

return None

def _iter_image_plane_widgets(self):
image_plane_widgets = (
self.image_plane_widget_x, self.image_plane_widget_y,
self.image_plane_widget_z
)
for ipw in image_plane_widgets:
if ipw is not None:
yield ipw
50 changes: 46 additions & 4 deletions ensemble/volren/volume_renderer.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,44 @@
from mayavi.sources.vtk_data_source import VTKDataSource
from mayavi.tools.tools import add_dataset
from traits.api import HasStrictTraits, CInt, Instance, List, Property
from traits.api import (HasStrictTraits, CInt, Enum, Instance, List, Property,
Range)
from tvtk.api import tvtk

from ensemble.ctf.piecewise import PiecewiseFunction
from .volume_3d import Volume3D, volume3d
from .volume_data import VolumeData

CLIP_MAX = 512
QUALITY_SETTINGS = {
'best': {
'mapper': {
'sample_distance': 0.05,
},
'property': {
'shade': False,
'ambient': 0.5,
'diffuse': 0.4,
'specular': 0.1,
'specular_power': 100,
}
},
'default': {
'mapper': {
'sample_distance': 0.2,
},
'property': {
'shade': False
}
},
'performance': {
'mapper': {
'sample_distance': 1.0,
},
'property': {
'shade': False
}
},
}


class VolumeRenderer(HasStrictTraits):
Expand All @@ -30,10 +61,14 @@ class VolumeRenderer(HasStrictTraits):
# The transfer function components
opacities = Instance(PiecewiseFunction)
colors = Instance(PiecewiseFunction)
global_alpha = Range(0.0, 1.0, value=1.0)

# Clip plane positions
clip_bounds = List(CInt)

# Render quality setting
render_quality = Enum('default', QUALITY_SETTINGS.keys())

# -------------------------------------------------------------------------
# Public interface
# -------------------------------------------------------------------------
Expand Down Expand Up @@ -68,7 +103,7 @@ def set_transfer_function(self, colors=None, opacities=None):
# we need to jog a value that is exactly equal by a little bit.
if alphas[i-1][0] == alpha[0]:
x += 1e-8
opacity_tf.add_point(lerp(x), alpha[1])
opacity_tf.add_point(lerp(x), alpha[1] * self.global_alpha)

self._set_volume_ctf(color_tf, opacity_tf)

Expand All @@ -92,6 +127,12 @@ def _data_changed(self):
def _clip_bounds_changed(self):
self._set_volume_clip_planes()

def _global_alpha_changed(self):
self.set_transfer_function()

def _render_quality_changed(self):
self._setup_volume()

def _get_actor(self):
return self.volume.actors[0]

Expand All @@ -100,8 +141,9 @@ def _get_actor(self):
# -------------------------------------------------------------------------

def _setup_volume(self):
self.volume.volume_mapper.trait_set(sample_distance=0.2)
self.volume.volume_property.trait_set(shade=False)
render_settings = QUALITY_SETTINGS[self.render_quality]
self.volume.volume_mapper.trait_set(**render_settings['mapper'])
self.volume.volume_property.trait_set(**render_settings['property'])
self._set_volume_clip_planes()
self.set_transfer_function()

Expand Down
52 changes: 45 additions & 7 deletions examples/volume_viewer_window.enaml
Original file line number Diff line number Diff line change
@@ -1,28 +1,66 @@
from enaml.application import schedule
from enaml.layout.api import vbox
from enaml.widgets.api import Container, MainWindow
from enaml.stdlib.slider_transform import FloatTransform
from enaml.widgets.api import (ComboBox, Container, Form, Label, MainWindow,
Notebook, Page, Slider)
from traits_enaml.widgets.enable_canvas import EnableCanvas

from ensemble.volren.volume_viewer_ui import (
VolumeViewerClip, VolumeViewerContainer
VolumeViewerClip, VolumeViewerCanvas
)


enamldef VolumeViewerWindow(MainWindow): vr_win:
attr viewer

title = 'Volume Viewer'
initial_size=(800, 900)
initial_size=(800, 800)

activated ::
schedule(self.send_to_front)

Container:
constraints = [
vbox(vv_view, vv_clip),
vbox(vv_view, config_ui),
]

VolumeViewerContainer: vv_view:
VolumeViewerCanvas: vv_view:
viewer << vr_win.viewer

VolumeViewerClip: vv_clip:
viewer << vr_win.viewer
Notebook: config_ui:
tab_style = 'preferences'

Page:
title = 'Transfer function'
closable = False
Container:
EnableCanvas:
component << viewer.ctf_editor

Page:
title = 'Clipping'
closable = False
VolumeViewerClip:
viewer << vr_win.viewer

Page:
title = 'Misc'
closable = False
Form:
Label:
text = 'Render Quality:'
ComboBox:
items = ['Default', 'Best', 'Performance']
index = 0
selected_item ::
renderer = vr_win.viewer.volume_renderer
renderer.render_quality = self.selected_item.lower()

Label:
text = 'Volume Alpha:'
Slider:
FloatTransform:
value := viewer.volume_renderer.global_alpha
minimum = 0.0
maximum = 1.0
precision = 100

0 comments on commit 342e308

Please sign in to comment.