Beispiel #1
0
class CameraPosition(HasTraits):
    position = CArray(shape=(3, ), dtype='float64')
    focal_point = CArray(shape=(3, ), dtype='float64')
    view_up = CArray(shape=(3, ), dtype='float64')
    clipping_range = CArray(shape=(2, ), dtype='float64')
    distance = Float
    num_steps = Int(10)
    orientation_wxyz = CArray(shape=(4, ), dtype='float64')
Beispiel #2
0
class ElevationFilterFactory(PipeFactory):
    """Applies the Elevation Filter mayavi filter to the given VTK object."""

    high_point = CArray(default=[0, 0, 1],
                        shape=(3, ),
                        adapts="filter.high_point",
                        help="The end point of the projection line")

    low_point = CArray(default=[0, 0, 0],
                       shape=(3, ),
                       adapts="filter.low_point",
                       help="The start point of the projection line")

    _target = Instance(filters.ElevationFilter, ())
Beispiel #3
0
class Axes(AxesLikeModuleFactory):
    """ Creates axes for the current (or given) object."""

    xlabel = String(None,
                    adapts='axes.x_label',
                    help='the label of the x axis')

    ylabel = String(None,
                    adapts='axes.y_label',
                    help='the label of the y axis')

    zlabel = String(None,
                    adapts='axes.z_label',
                    help='the label of the z axis')

    nb_labels = Range(0,
                      50,
                      2,
                      adapts='axes.number_of_labels',
                      desc='The number of labels along each direction')

    ranges = Trait(
        None,
        None,
        CArray(shape=(6, )),
        help="""[xmin, xmax, ymin, ymax, zmin, zmax]
                            Ranges of the labels displayed on the axes.
                            Default is the object's extents.""",
    )

    x_axis_visibility = true(
        adapts='axes.x_axis_visibility',
        help="Whether or not the x axis is visible (boolean)")

    y_axis_visibility = true(
        adapts='axes.y_axis_visibility',
        help="Whether or not the y axis is visible (boolean)")

    z_axis_visibility = true(
        adapts='axes.z_axis_visibility',
        help="Whether or not the z axis is visible (boolean)")

    _target = Instance(modules.Axes, ())

    def _extent_changed(self):
        """ Code to modify the extents for 
        """
        axes = self._target
        axes.axes.use_data_bounds = False
        axes.axes.bounds = self.extent
        if self.ranges is None:
            axes.axes.ranges = \
                axes.module_manager.source.outputs[0].bounds

    def _ranges_changed(self):
        if self.ranges is not None:
            self._target.axes.ranges = self.ranges
            self._target.axes.use_ranges = True
Beispiel #4
0
class Text3D(ModuleFactory):
    """ Positions text at a 3D location in the scene.
    
        **Function signature**::
        
            text3d(x, y, z, text, ...) 

        x, y, and z are the position of the origin of the text. The 
        text is positionned in 3D, in figure coordinnates.
        """

    _target = Instance(modules.Text3D, ())

    scale = Either(CFloat(1),
                   CArray(shape=(3, )),
                   help="""The scale of the text, in figure units.
                                Either a float, or 3-tuple of floats.""")

    orientation = CArray(shape=(3, ),
                         adapts='orientation',
                         desc="""the angles giving the orientation of the
                        text. If the text is oriented to the camera,
                        these angles are referenced to the axis of the
                        camera. If not, these angles are referenced to
                        the z axis.""")

    orient_to_camera = true(adapts='orient_to_camera',
                            desc="""if the text is kept oriented to the
                        camera, or is pointing in a specific direction,
                        regardless of the camera position.""")

    def __init__(self, x, y, z, text, **kwargs):
        """ Override init as for different positional arguments."""
        if not 'scale' in kwargs:
            kwargs['scale'] = 1
        super(Text3D, self).__init__(None, **kwargs)
        self._target.text = text
        self._target.position = (x, y, z)

    def _scale_changed(self):
        scale = self.scale
        if operator.isNumberType(scale):
            scale = scale * np.ones((3, ))
        self._target.scale = scale
Beispiel #5
0
class AxesLikeModuleFactory(SingletonModuleFactory):
    """ Base class for axes and outline"""

    extent = CArray(
        shape=(6, ),
        help="""[xmin, xmax, ymin, ymax, zmin, zmax]
                            Default is the object's extents.""",
    )

    def _extent_changed(self):
        """ There is no universal way of setting extents for decoration
            objects. This should be implemented in subclasses
        """
        pass

    # Override the color and opacity handlers: axes and outlines do not
    # behave like other modules

    def _color_changed(self):
        if self.color:
            try:
                self._target.property.color = self.color
            except AttributeError:
                try:
                    self._target.actor.property.color = self.color
                except AttributeError:
                    pass

    def _opacity_changed(self):
        try:
            self._target.property.opacity = self.opacity
        except AttributeError:
            try:
                self._target.actor.property.opacity = self.opacity
            except AttributeError:
                pass

    def __init__(self, *args, **kwargs):
        """ Overide the call method to be able to catch the extents of
            the object, if any.
        """
        SingletonModuleFactory.__init__(self, *args, **kwargs)
        if not 'extent' in kwargs:
            try:
                # XXX: Do not use tools.set_extent, as it does not work
                # on axes.
                self.extent = self._parent.actor.actor.bounds
            except AttributeError:
                """ Either this is not a module, or it has no actors"""
Beispiel #6
0
class HierarchyImporter(HasTraits):
    pf = Any
    min_grid_level = Int(0)
    max_level = Int(1)
    number_of_levels = Range(0, 13)
    max_import_levels = Property(depends_on='min_grid_level')
    field = Str("Density")
    field_list = List
    center_on_max = Bool(True)
    center = CArray(shape=(3, ), dtype='float64')
    cache = Bool(True)
    smoothed = Bool(True)
    show_grids = Bool(True)

    def _field_list_default(self):
        fl = self.pf.h.field_list
        df = self.pf.h.derived_field_list
        fl.sort()
        df.sort()
        return fl + df

    default_view = View(
        Item('min_grid_level',
             editor=RangeEditor(low=0, high_name='max_level')),
        Item('number_of_levels',
             editor=RangeEditor(low=1, high_name='max_import_levels')),
        Item('field', editor=EnumEditor(name='field_list')),
        Item('center_on_max'),
        Item('center', enabled_when='not object.center_on_max'),
        Item('smoothed'),
        Item('cache', label='Pre-load data'),
        Item('show_grids'),
        buttons=OKCancelButtons)

    def _center_default(self):
        return [0.5, 0.5, 0.5]

    @cached_property
    def _get_max_import_levels(self):
        return min(13, self.pf.h.max_level - self.min_grid_level + 1)
Beispiel #7
0
 def validate( self, object, name, value):
     if operator.isNumberType(value):
         value = np.atleast_1d(value)
     return CArray.validate(self, object, name, value)
Beispiel #8
0
class DataSourceWizard(HasTraits):

    data_sources = Dict

    _data_sources_names = Property(depends_on='data_sources')

    def _get__data_sources_names(self):
        names = []
        for name in self.data_sources:
            try:
                self.data_sources[name] + 1
                names.append(name)
            except TypeError:
                pass
        names.sort()
        return names


    # Dictionnary mapping the views
    data_type = Trait('point',
            {'A surface':
                    'surface',
            'A set of points, that can be connected by lines':
                    'point',
            'A set of vectors':
                    'vector',
            'Volumetric data':
                    'volumetric',
            })


    position_type = Trait('image data',
                     {'Specified explicitly':
                        'explicit',
                      'Implicitely positioned on a regular grid':
                        'image data',
                      'On an orthogonal grid with varying spacing':
                        'orthogonal grid',
                        })


    # The array that is used for finding out the shape of the grid,
    # when creating an ImageData
    grid_shape_source = Property(depends_on='grid_shape_source_')

    def _get_grid_shape_source(self):
        if self.grid_shape_source_ == '':
            # catter for improperly initialized view
            keys = self._data_sources_names
            if not self.grid_shape.any():
                self.grid_shape = \
                        self.data_sources[keys[0]].shape
            return keys[0]
        elif self.grid_shape_source_[:16]=='Shape of array: ':
            return self.grid_shape_source_[17:-1]
        else:
            return ""

    # Shadow traits for grid_shape_source
    grid_shape_source_ = Str

    def _grid_shape_source_changed(self):
        if not self.grid_shape_source == '':
            array_shape = \
                    atleast_3d(self.data_sources[self.grid_shape_source]).shape
            grid_shape = ones((3, ))
            grid_shape[:len(array_shape)] = array_shape
            self.grid_shape = grid_shape 
    
    _grid_shape_source_labels = Property(depends_on='_data_sources_names')

    def _get__grid_shape_source_labels(self):
        values = ['Shape of array: "%s"' % name
                    for name in  self._data_sources_names]
        values.sort
        values.append('Specified explicitly')
        return values

    # The shape of the grid array. Used when position is implicit
    grid_shape = CArray(shape=(3,), dtype='i')

    # Whether or not the data points should be connected.
    lines = false

    # The scalar data selection
    scalar_data = Str('', help="Select the array that gives the value of the "
                            "scalars plotted.")

    position_x = Str(help="Select the array that gives the x "
                        "position of the data points")
    
    position_y = Str(help="Select the array that gives the y "
                        "position of the data points")
    
    position_z = Str(help="Select the array that gives the z "
                        "position of the data points")

    connectivity_triangles = Str

    has_vector_data = false(help="""Do you want to plot vector components?""")

    # A boolean to ask the user if he wants to load scalar data
    has_scalar_data = false

    vector_u = Str

    vector_v = Str

    vector_w =  Str


    #----------------------------------------------------------------------
    # Public interface
    #----------------------------------------------------------------------

    def init_arrays(self):
        # Force all the array names to be properly initialized
        array_names = set(self.data_sources.keys())
        if len(array_names) == 0:
            # We should probably bail out here.
            return False
        for attr in ('position_x', 'position_y', 'position_z',
                     'scalar_data', 'vector_u', 'vector_v',
                     'vector_w', 'connectivity_triangles',
                     ):
            if len(array_names) > 0:
                array_name = array_names.pop()
            setattr(self, attr, array_name)


    def guess_arrays(self):
        """ Do some guess work on the arrays to find sensible default.
        """
        array_names = set(self._data_sources_names)
        found_some = False
        if set(('x', 'y', 'z')).issubset(array_names):
            self.position_x = 'x'
            self.position_y = 'y'
            self.position_z = 'z'
            array_names = array_names.difference(('x', 'y', 'z'))
            found_some = True
        elif set(('X', 'Y', 'Z')).issubset(array_names):
            self.position_x = 'X'
            self.position_y = 'Y'
            self.position_z = 'Z'
            array_names = array_names.difference(('X', 'Y', 'Z'))
            found_some = True

        if set(('u', 'v', 'w')).issubset(array_names):
            self.vector_u = 'u'
            self.vector_v = 'v'
            self.vector_w = 'w'
            array_names = array_names.difference(('u', 'v', 'w'))
            found_some = True
        elif set(('U', 'V', 'W')).issubset(array_names):
            self.vector_u = 'U'
            self.vector_v = 'V'
            self.vector_w = 'W'
            array_names = array_names.difference(('U', 'V', 'W'))
            found_some = True

        if found_some:
            # Need to re-attribute the guessed names.
            for attr in ('scalar_data', 'vector_u', 'vector_v',
                        'vector_w', 'connectivity_triangles'):
                if len(array_names) > 0:
                    setattr(self, attr, array_names.pop())
                else:
                    break


    def build_data_source(self):
        """ This is where we apply the selections made by the user in
            in the wizard to build the data source.
        """
        factory = DataSourceFactory()
        # Keep a reference to the factory to be able to replay it, say
        # on other data.
        self._factory = factory
        if self.data_type_ == 'point':
            # The user wants to explicitly position vector, 
            # thus only sensible data structures for points is with
            # explicit positioning.
            self.position_type_ == 'explicit'
            # In addition, this view does not allow for
            # connectivity. 
            factory.unstructured = True
            factory.connected = False
        else:
            factory.connected = True

        if (self.position_type_ == "image data" 
                and not self.data_type_=="point"):
            if not self.has_scalar_data and not self.vector_u=='':
                # With image data we need a scalar array always:
                factory.scalar_data = ones(self.grid_shape)
            factory.position_implicit = True
        else:
            factory.position_x = self.get_sdata(self.position_x)
            factory.position_y = self.get_sdata(self.position_y)
            factory.position_z = self.get_sdata(self.position_z)
        if self.position_type_ == "orthogonal grid":
            factory.orthogonal_grid = True
        if self.position_type_ == "explicit" and self.data_type_ == "surface":
            factory.connectivity_triangles = self.get_data(
                                                self.connectivity_triangles)
        if self.lines and self.data_type_=="point":
            factory.lines = True

        if self.has_vector_data or self.data_type_ == 'vector':
            # In the vector view, the user is not explicitly asked to 
            # Enable vectors.
            factory.has_vector_data = True
            factory.vector_u = self.get_sdata(self.vector_u)
            factory.vector_v = self.get_sdata(self.vector_v)
            factory.vector_w = self.get_sdata(self.vector_w)
        
        if self.has_scalar_data or self.data_type_ == 'volumetric':
            # In the volumetric view, the user is not explicitly asked to 
            # Enable scalars.
            factory.scalar_data = self.get_sdata(self.scalar_data)

        if self.connectivity_triangles == '':
            factory.connectivity_triangles = None

        self.data_source = factory.build_data_source()

        if self.has_scalar_data:
            if hasattr(self.data_source, 'scalar_name'):
               self.data_source.scalar_name = self.scalar_data
            elif hasattr(self.data_source, 'point_scalar_name'):
               self.data_source.point_scalar_name = self.scalars

        
    #----------------------------------------------------------------------
    # Private interface
    #----------------------------------------------------------------------

    def get_data(self, name): 
        return self.data_sources[name]


    def get_sdata(self, name):
        ary = self.data_sources[name]
        if not self.data_type_ == 'point':
            ary = resize(ary, self.grid_shape)
        return ary


    def active_arrays(self):
        """ Return the list of the active array-selection drop-downs.
        """
        arrays = []
        if self.data_type_ == 'point' or self.position_type_ == 'explicit':
            arrays.extend(
                    ['position_x' ,'position_y', 'position_z',])
        if self.data_type_ == 'vector' or self.has_vector_data:
            arrays.extend(['vector_u', 'vector_v', 'vector_w'])
        if self.has_scalar_data or self.data_type_ == 'volumetric':
            arrays.extend(['scalar_data'])
        return arrays


    def check_arrays(self):
        """ Checks that all the array have the right size.
        """
        arrays_to_check = self.active_arrays()
        if len(arrays_to_check)==0:
            return True
        size = self.get_data(getattr(self, arrays_to_check.pop())).size
        for attr in arrays_to_check: 
            if not self.get_data(getattr(self, attr)).size == size:
                return False
        if ( self.data_type_ == 'surface' 
                and self.position_type_ == "explicit"):
            if not self.connectivity_triangles.size/3 == size:
                return False
        return True
Beispiel #9
0
class Text3D(Module):
    # The version of this class.  Used for persistence.
    __version__ = 0

    # The Mayavi Actor.
    actor = Instance(Actor, allow_none=False, record=True)

    # And the text source
    vector_text = Instance(tvtk.VectorText, allow_none=False, record=True)

    # The text to be displayed.
    text = Str('Text',
               desc='the text to be displayed',
               enter_set=True,
               auto_set=False)

    # The position of the actor
    position = CArray(value=(0., 0., 0.),
                      cols=3,
                      desc='the world coordinates of the text',
                      enter_set=True,
                      auto_set=False)

    # The scale of the actor
    scale = CArray(value=(1., 1., 1.),
                   cols=3,
                   desc='the scale of the text',
                   enter_set=True,
                   auto_set=False)

    # The orientation of the actor
    orientation = CArray(value=(0., 0., 0.),
                         cols=3,
                         desc='the orientation angles of the text',
                         enter_set=True,
                         auto_set=False)

    # Orient actor to camera
    orient_to_camera = Bool(True, desc='if the text is kept facing the camera')

    input_info = PipelineInfo(datasets=['any'],
                              attribute_types=['any'],
                              attributes=['any'])

    ########################################
    # The view of this object.

    view = View(
        Group(
            Item(name='text'),
            Group(Item(name='position'),
                  show_labels=False,
                  show_border=True,
                  label='Position'),
            Group(Item(name='scale'),
                  show_labels=False,
                  show_border=True,
                  label='Scale'),
            Group(Item(name='orient_to_camera'),
                  Item(name='orientation', label='Angles'),
                  show_border=True,
                  label='Orientation'),
            label='Text',
        ),
        Group(Item(name='actor', style='custom', show_label=False),
              label='Actor'),
    )

    ######################################################################
    # `Module` interface
    ######################################################################
    def setup_pipeline(self):
        """Override this method so that it *creates* the tvtk
        pipeline.

        This method is invoked when the object is initialized via
        `__init__`.  Note that at the time this method is called, the
        tvtk data pipeline will *not* yet be setup.  So upstream data
        will not be available.  The idea is that you simply create the
        basic objects and setup those parts of the pipeline not
        dependent on upstream sources and filters.  You should also
        set the `actors` attribute up at this point.
        """
        self.vector_text = tvtk.VectorText(text=self.text)
        self.outputs = [self.vector_text.output]
        self.actor = Actor()
        self._text_changed(self.text)

    def update_pipeline(self):
        """Override this method so that it *updates* the tvtk pipeline
        when data upstream is known to have changed.

        This method is invoked (automatically) when any of the inputs
        sends a `pipeline_changed` event.
        """
        self.pipeline_changed = True

    ######################################################################
    # Non-public interface
    ######################################################################
    def _text_changed(self, value):
        vector_text = self.vector_text
        if vector_text is None:
            return
        vector_text.text = str(value)
        self.render()

    def _actor_changed(self, old, new):
        new.scene = self.scene
        new.inputs = [self]
        self._change_components(old, new)
        old_actor = None
        if old is not None:
            old_actor = old.actor
        new.actor = self._get_actor_or_follower(old=old_actor)
        self.actors = new.actors
        self.render()

    def _orient_to_camera_changed(self):
        self.actor.actor = \
                    self._get_actor_or_follower(old=self.actor.actor)

    def _get_actor_or_follower(self, old=None):
        """ Get a tvtk.Actor or a tvtk.Follower for the actor of the
            object and wire the callbacks to it.
            If old is given, it is the old actor, to remove its
            callbacks.
        """
        if self.orient_to_camera:
            new = tvtk.Follower()
            if self.scene is not None:
                new.camera = self.scene.camera
        else:
            new = tvtk.Actor()
        if old is not None:
            self.sync_trait('position', old, 'position', remove=True)
            self.sync_trait('scale', old, 'scale', remove=True)
            self.sync_trait('orientation', old, 'orientation', remove=True)

        self.sync_trait('position', new, 'position')
        self.sync_trait('scale', new, 'scale')
        self.sync_trait('orientation', new, 'orientation')
        return new

    def _scene_changed(self, old, new):
        super(Text3D, self)._scene_changed(old, new)
        if new is not None and self.orient_to_camera:
            self.actor.actor.camera = new.camera
Beispiel #10
0
class YTScene(HasTraits):

    # Traits
    importer = Instance(HierarchyImporter)
    pf = Delegate("importer")
    min_grid_level = Delegate("importer")
    number_of_levels = Delegate("importer")
    field = Delegate("importer")
    center = CArray(shape=(3, ), dtype='float64')
    center_on_max = Delegate("importer")
    smoothed = Delegate("importer")
    cache = Delegate("importer")
    show_grids = Delegate("importer")

    camera_path = Instance(CameraControl)
    #window = Instance(ivtk.IVTKWithCrustAndBrowser)
    #python_shell = Delegate('window')
    #scene = Delegate('window')
    scene = Instance(HasTraits)
    operators = List(HasTraits)

    # State variables
    _grid_boundaries_actor = None

    # Views
    def _window_default(self):
        # Should experiment with passing in a pipeline browser
        # that has two root objects -- one for TVTKBases, i.e. the render
        # window, and one that accepts our objects
        return ivtk.IVTKWithCrustAndBrowser(size=(800, 600), stereo=1)

    def _camera_path_default(self):
        return CameraControl(yt_scene=self, camera=self.scene.camera)

    def __init__(self, **traits):
        HasTraits.__init__(self, **traits)
        max_level = min(self.pf.h.max_level,
                        self.min_grid_level + self.number_of_levels - 1)
        self.extracted_pf = ExtractedParameterFile(self.pf,
                                                   self.min_grid_level,
                                                   max_level,
                                                   offset=None)
        self.extracted_hierarchy = self.extracted_pf.h
        self._hdata_set = tvtk.HierarchicalBoxDataSet()
        self._ugs = []
        self._grids = []
        self._min_val = 1e60
        self._max_val = -1e60
        gid = 0
        if self.cache:
            for grid_set in self.extracted_hierarchy.get_levels():
                for grid in grid_set:
                    grid[self.field]
        for l, grid_set in enumerate(self.extracted_hierarchy.get_levels()):
            gid = self._add_level(grid_set, l, gid)
        if self.show_grids:
            self.toggle_grid_boundaries()

    def _center_default(self):
        return self.extracted_hierarchy._convert_coords([0.5, 0.5, 0.5])

    def do_center_on_max(self):
        self.center = self.extracted_hierarchy._convert_coords(
            self.pf.h.find_max("Density")[1])
        self.scene.camera.focal_point = self.center

    def _add_level(self, grid_set, level, gid):
        for grid in grid_set:
            self._hdata_set.set_refinement_ratio(level, 2)
            gid = self._add_grid(grid, gid, level)
        return gid

    def _add_grid(self, grid, gid, level=0):
        mylog.debug("Adding grid %s on level %s (%s)", grid.id, level,
                    grid.Level)
        if grid in self._grids: return
        self._grids.append(grid)

        scalars = grid.get_vertex_centered_data(self.field,
                                                smoothed=self.smoothed)

        left_index = grid.get_global_startindex()
        origin = grid.LeftEdge
        dds = grid.dds
        right_index = left_index + scalars.shape - 1
        ug = tvtk.UniformGrid(origin=origin,
                              spacing=dds,
                              dimensions=grid.ActiveDimensions + 1)
        if self.field not in self.pf.field_info or \
            self.pf.field_info[self.field].take_log:
            scalars = na.log10(scalars)
        ug.point_data.scalars = scalars.transpose().ravel()
        ug.point_data.scalars.name = self.field
        if grid.Level != self.min_grid_level + self.number_of_levels - 1:
            ug.cell_visibility_array = grid.child_mask.transpose().ravel()
        else:
            ug.cell_visibility_array = na.ones(grid.ActiveDimensions,
                                               dtype='int').ravel()
        self._ugs.append((grid, ug))
        self._hdata_set.set_data_set(level, gid, left_index, right_index, ug)

        self._min_val = min(self._min_val, scalars.min())
        self._max_val = max(self._max_val, scalars.max())

        gid += 1
        return gid

    def _add_data_to_ug(self, field):
        for g, ug in self._ugs:
            scalars_temp = grid.get_vertex_centered_data(
                field, smoothed=self.smoothed)
            ii = ug.point_data.add_array(scalars_temp.transpose().ravel())
            ug.point_data.get_array(ii).name = field

    def zoom(self, dist, unit='1'):
        vec = self.scene.camera.focal_point - \
              self.scene.camera.position
        self.scene.camera.position += \
            vec * dist/self._grids[0].pf[unit]
        self.scene.render()

    def toggle_grid_boundaries(self):
        if self._grid_boundaries_actor is None:
            # We don't need to track this stuff right now.
            ocf = tvtk.OutlineCornerFilter(
                executive=tvtk.CompositeDataPipeline(), corner_factor=0.5)
            ocf.input = self._hdata_set
            ocm = tvtk.HierarchicalPolyDataMapper(
                input_connection=ocf.output_port)
            self._grid_boundaries_actor = tvtk.Actor(mapper=ocm)
            self.scene.add_actor(self._grid_boundaries_actor)
        else:
            self._grid_boundaries_actor.visibility = \
            (not self._grid_boundaries_actor.visibility)

    def _add_sphere(self, origin=(0.0, 0.0, 0.0), normal=(0, 1, 0)):
        sphere = tvtk.Sphere(center=origin, radius=0.25)
        cutter = tvtk.Cutter(executive=tvtk.CompositeDataPipeline(),
                             cut_function=sphere)
        cutter.input = self._hdata_set
        lut_manager = LUTManager(data_name=self.field, scene=self.scene)
        smap = tvtk.HierarchicalPolyDataMapper(
            scalar_range=(self._min_val, self._max_val),
            lookup_table=lut_manager.lut,
            input_connection=cutter.output_port)
        sactor = tvtk.Actor(mapper=smap)
        self.scene.add_actors(sactor)
        return sphere, lut_manager

    def _add_plane(self, origin=(0.0, 0.0, 0.0), normal=(0, 1, 0)):
        plane = tvtk.Plane(origin=origin, normal=normal)
        cutter = tvtk.Cutter(executive=tvtk.CompositeDataPipeline(),
                             cut_function=plane)
        cutter.input = self._hdata_set
        lut_manager = LUTManager(data_name=self.field, scene=self.scene)
        smap = tvtk.HierarchicalPolyDataMapper(
            scalar_range=(self._min_val, self._max_val),
            lookup_table=lut_manager.lut,
            input_connection=cutter.output_port)
        sactor = tvtk.Actor(mapper=smap)
        self.scene.add_actors(sactor)
        return plane, lut_manager

    def add_plane(self, origin=(0.0, 0.0, 0.0), normal=(0, 1, 0)):
        self.operators.append(self._add_plane(origin, normal))
        return self.operators[-1]

    def _add_axis_plane(self, axis):
        normal = [0, 0, 0]
        normal[axis] = 1
        np, lut_manager = self._add_plane(self.center, normal=normal)
        LE = self.extracted_hierarchy.min_left_edge
        RE = self.extracted_hierarchy.max_right_edge
        self.operators.append(
            MappingPlane(vmin=LE[axis],
                         vmax=RE[axis],
                         vdefault=self.center[axis],
                         post_call=self.scene.render,
                         plane=np,
                         axis=axis,
                         coord=0.0,
                         lut_manager=lut_manager,
                         scene=self.scene))

    def add_x_plane(self):
        self._add_axis_plane(0)
        return self.operators[-1]

    def add_y_plane(self):
        self._add_axis_plane(1)
        return self.operators[-1]

    def add_z_plane(self):
        self._add_axis_plane(2)
        return self.operators[-1]

    def add_contour(self, val=None):
        if val is None:
            if self._min_val != self._min_val:
                self._min_val = 1.0
            val = (self._max_val + self._min_val) * 0.5
        cubes = tvtk.MarchingCubes(executive=tvtk.CompositeDataPipeline())
        cubes.input = self._hdata_set
        cubes.set_value(0, val)
        lut_manager = LUTManager(data_name=self.field, scene=self.scene)
        cube_mapper = tvtk.HierarchicalPolyDataMapper(
            input_connection=cubes.output_port, lookup_table=lut_manager.lut)
        cube_mapper.color_mode = 'map_scalars'
        cube_mapper.scalar_range = (self._min_val, self._max_val)
        cube_actor = tvtk.Actor(mapper=cube_mapper)
        self.scene.add_actors(cube_actor)
        self.operators.append(
            MappingMarchingCubes(operator=cubes,
                                 vmin=self._min_val,
                                 vmax=self._max_val,
                                 vdefault=val,
                                 mapper=cube_mapper,
                                 post_call=self.scene.render,
                                 lut_manager=lut_manager,
                                 scene=self.scene))
        return self.operators[-1]

    def add_isocontour(self, val=None):
        if val is None: val = (self._max_val + self._min_val) * 0.5
        isocontour = tvtk.ContourFilter(executive=tvtk.CompositeDataPipeline())
        isocontour.input = self._hdata_set
        isocontour.generate_values(1, (val, val))
        lut_manager = LUTManager(data_name=self.field, scene=self.scene)
        isocontour_normals = tvtk.PolyDataNormals(
            executive=tvtk.CompositeDataPipeline())
        isocontour_normals.input_connection = isocontour.output_port
        iso_mapper = tvtk.HierarchicalPolyDataMapper(
            input_connection=isocontour_normals.output_port,
            lookup_table=lut_manager.lut)
        iso_mapper.scalar_range = (self._min_val, self._max_val)
        iso_actor = tvtk.Actor(mapper=iso_mapper)
        self.scene.add_actors(iso_actor)
        self.operators.append(
            MappingIsoContour(operator=isocontour,
                              vmin=self._min_val,
                              vmax=self._max_val,
                              vdefault=val,
                              mapper=iso_mapper,
                              post_call=self.scene.render,
                              lut_manager=lut_manager,
                              scene=self.scene))
        return self.operators[-1]