Logo Search packages:      
Sourcecode: mayavi2 version File versions  Download package

glyph.py

00001 """A component that allows one to place colored and scaled glyphs at
input point data.

"""
# Author: Prabhu Ramachandran <prabhu_r@users.sf.net>
# Copyright (c) 2005-2006, Enthought, Inc.
# License: BSD Style.

# Enthought library imports.
from enthought.traits.api import Instance, List, Trait, Bool, TraitPrefixList
from enthought.traits.ui.api import View, Group, Item, InstanceEditor
from enthought.tvtk.api import tvtk
from enthought.tvtk.tvtk_base import TraitRevPrefixMap
from enthought.persistence.state_pickler import set_state

# Local imports.
from enthought.mayavi.core.common import handle_children_state
from enthought.mayavi.core.component import Component
from enthought.mayavi.core.module import Module


######################################################################
# `Glyph` class.
######################################################################
class Glyph(Component):

    # The version of this class.  Used for persistence.
    __version__ = 0

    # Glyph position.  This can be one of ['head', 'tail', 'center'],
    # and indicates the position of the glyph with respect to the
    # input point data.  Please note that this will work correctly
    # only if you do not mess with the source glyph's basic size.  For
    # example if you use a ConeSource and set its height != 1, then the
    # 'head' and 'tail' options will not work correctly.    
    glyph_position = Trait('center', TraitPrefixList(['head', 'tail',
                                                      'center']),
                           desc='position of glyph w.r.t. data point')

    # The scaling mode to use when scaling the glyphs.  We could have
    # used the glyph's own scale mode but it allows users to set the
    # mode to use vector components for the scaling which I'd like to
    # disallow.
    scale_mode = Trait('scale_by_scalar',
                       TraitRevPrefixMap({'scale_by_vector': 1,
                                          'data_scaling_off': 3,
                                          'scale_by_scalar': 0}),
                       desc="if scaling is done using scalar or vector/normal magnitude"
                       )

    # The color mode to use when coloring the glyphs.  We could have
    # used the glyph's own color_mode trait but it allows users to set
    # the mode to use vector components for the scaling which I'd
    # like to disallow.
    color_mode = Trait('color_by_scalar',
                       TraitRevPrefixMap({'color_by_vector': 2,
                                          'color_by_scalar': 1,
                                          'no_coloring': 0}),
                       desc="if coloring is done by scalar or vector/normal magnitude"
                       )

    # Specify if the input points must be masked.  By mask we mean
    # that only a subset of the input points must be displayed.
    mask_input_points = Bool(False, desc="if input points are masked")

    # The MaskPoints filter.
    mask_points = Instance(tvtk.MaskPoints, args=(),
                           kw={'random_mode': True})

    # The Glyph3D instance.
    glyph = Instance(tvtk.Glyph3D, args=(), kw={'clamping': True})

    # The Source to use for the glyph.  This is chosen from
    # `self._glyph_list`.
    glyph_source = Instance(tvtk.Object)

    # The module associated with this component.  This is used to get
    # the data range of the glyph when the scale mode changes.  This
    # *must* be set if this module is to work correctly.
    module = Instance(Module)

    # Should we show the GUI option for changing the scalar mode or
    # not?  This is useful for vector glyphing modules where there it
    # does not make sense to scale the data based on scalars.
    show_scale_mode = Bool(True)

    # A list of predefined glyph sources that can be used.
    glyph_list = List(tvtk.Object)

    ########################################
    # Private traits.

    # The transformation to use to place glyph appropriately.
    _trfm = Instance(tvtk.TransformFilter, ())

    # Used for optimization.
    _updating = Bool(False)
    
    ########################################
    # View related traits.

    _glyph_group = Group(Item(name='vector_mode'),
                         Item(name='clamping'),
                         Item(name='orient'),
                         Item(name='scaling'),
                         Item(name='range'),
                         Item(name='scale_factor'))                         

    view = View(Group(Item(name='mask_input_points'),
                      Group(Item(name='mask_points',
                                 enabled_when='object.mask_input_points',
                                 style='custom', resizable=True),
                            show_labels=False,
                            ),
                      label='Masking',
                      ),
                Group(Group(Item(name='glyph_position'),
                            Item(name='scale_mode',
                                 enabled_when='object.show_scale_mode'),
                            Item(name='color_mode'),
                            ),
                      Group(Item(name='glyph', style='custom',
                                 resizable=True,
                                 editor=InstanceEditor(view=View(_glyph_group))),
                            show_labels=False),
                      label='Glyph',
                      selected=True,
                      ),
                Group(Item(name='glyph_source', style='custom',
                           resizable=True,
                           editor=InstanceEditor(name='glyph_list')),
                      label='Glyph Source',
                      show_labels=False,
                      ),
                resizable=True
                )

    ######################################################################
    # `Base` interface
    ######################################################################
    def __get_pure_state__(self):
        d = super(Glyph, self).__get_pure_state__()
        for attr in ('module', '_updating'):
            d.pop(attr, None)
        return d

    def __set_pure_state__(self, state):
        # Setup the glyph_list attribute by creating any new ones if
        # needed.
        handle_children_state(self.glyph_list, state.glyph_list)
        # Set their state.
        set_state(self, state, first=['glyph_list'], ignore=['*'])
        glyph_names = [x.__class__.__name__ for x in self.glyph_list]
        g_name = state.glyph_source.__metadata__['class_name']
        # Set the correct glyph_source.
        self.glyph_source = self.glyph_list[glyph_names.index(g_name)]
        set_state(self, state, ignore=['glyph_list'])
        self._glyph_position_changed(self.glyph_position)

    ######################################################################
    # `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.
        """
        # Setup the glyphs.
        glyphs = [tvtk.GlyphSource2D(glyph_type='arrow', filled=False),
                  tvtk.ArrowSource(),
                  tvtk.ConeSource(height=1.0, radius=0.2, resolution=5),
                  tvtk.CylinderSource(height=1.0, radius=0.15),
                  tvtk.SphereSource(),
                  tvtk.CubeSource(),
                  ]
        self.glyph_list = glyphs
        self.glyph_source = glyphs[0]

        self._trfm.transform = tvtk.Transform()

        # Call render when the any of the glyph's traits change.
        self.glyph.on_trait_change(self.render)
        self.mask_points.on_trait_change(self.render)

    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.
        """
        if (len(self.inputs) == 0) or (len(self.inputs[0].outputs) == 0):
            return

        self._mask_input_points_changed(self.mask_input_points)
        self._color_mode_changed(self.color_mode)
        self._scale_mode_changed(self.scale_mode)        
        self._glyph_position_changed(self.glyph_position)

        # Set our output.
        self.outputs = [self.glyph.output]

    def update_data(self):
        """Override this method so that it flushes the vtk pipeline if
        that is necessary.

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

    def render(self):
        if not self._updating:
            super(Glyph, self).render()

    ######################################################################
    # Non-public traits.
    ######################################################################
    def _glyph_source_changed(self, value):
        if value not in self.glyph_list:
            classes = [o.__class__ for o in self.glyph_list]
            vc = value.__class__
            if vc in classes:
                self.glyph_list[classes.index(vc)] = value
            else:
                self.glyph_list.append(value)

        # Now change the glyph's source trait.
        self._updating = True
        name = value.__class__.__name__
        if name == 'GlyphSource2D':            
            self.glyph.source = value.output
        else:
            self._trfm.input = value.output
            self.glyph.source = self._trfm.output            
        value.on_trait_change(self.render)
        self._updating = False

        # Now update the glyph position since the transformation might
        # be different.        
        self._glyph_position_changed(self.glyph_position)

    def _color_mode_changed(self, value):
        if len(self.inputs) == 0:
            return
        self._updating = True
        if value != 'no_coloring':
            self.glyph.color_mode = value
        self._updating = False

    def _scale_mode_changed(self, value):
        if (self.module is None) or (len(self.inputs) == 0):
            return

        self._updating = True
        try:
            glyph = self.glyph
            glyph.scale_mode = value
            
            mm = self.module.module_manager
            if glyph.scale_mode == 'scale_by_scalar':
                glyph.range = tuple(mm.scalar_lut_manager.data_range)
            else:
                glyph.range = tuple(mm.vector_lut_manager.data_range)
        finally:
            self._updating = False
            self.render()

    def _glyph_position_changed(self, value):
        if len(self.inputs) == 0:
            return
        tr = self._trfm.transform
        tr.identity()
        
        g = self.glyph_source
        name = g.__class__.__name__

        if value == 'tail':
            if name == 'GlyphSource2D':
                g.center = 0.5, 0.0, 0.0
            elif name == 'ArrowSource':
                pass
            else:
                tr.translate(0.5, 0.0, 0.0)
        elif value == 'head':
            if name == 'GlyphSource2D':
                g.center = -0.5, 0.0, 0.0
            elif name == 'ArrowSource':
                tr.translate(-1, 0, 0)
            else:
                tr.translate(-0.5, 0, 0)
        else:
            if name == 'GlyphSource2D':
                g.center = 0.0, 0.0, 0.0
            elif name == 'ArrowSource':
                tr.translate(-0.5, 0, 0)

        if name == 'CylinderSource':
            tr.rotate_z(90)
        
        self.render()

    def _mask_input_points_changed(self, value):
        inputs = self.inputs
        if len(inputs) == 0:
            return
        if value:
            mask = self.mask_points
            mask.input = inputs[0].outputs[0]
            self.glyph.input = mask.output
        else:
            self.glyph.input = inputs[0].outputs[0]
            

Generated by  Doxygen 1.6.0   Back to index