Search code examples
pythonenthoughtmayavi

How to change tube radius of a line plot in Mayavi?


I am trying to dynamically modify the tube radius of a 3D line plot in Mayavi2. For example

from traits.api import HasTraits, Float, Instance, on_trait_change
from traitsui.api import View, Item, Group
from mayavi.core.api import PipelineBase
from mayavi.core.ui.api import MayaviScene, SceneEditor, MlabSceneModel
import numpy

def curve():
    n_mer, n_long = 6, 11
    pi = numpy.pi
    dphi = pi / 1000.0
    phi = numpy.arange(0.0, 2 * pi + 0.5 * dphi, dphi)
    mu = phi * n_mer
    x = numpy.cos(mu) * (1 + numpy.cos(n_long * mu / n_mer) * 0.5)
    y = numpy.sin(mu) * (1 + numpy.cos(n_long * mu / n_mer) * 0.5)
    z = numpy.sin(n_long * mu / n_mer) * 0.5
    t = numpy.sin(mu)

    return x, y, z, t

class MyModel(HasTraits):
    radius = Float(0.025)
    scene = Instance(MlabSceneModel, ())
    plot = Instance(PipelineBase)

    @on_trait_change('radius,scene.activated')
    def update_plot(self):
        x, y, z, t = curve()
        if self.plot is None:
            self.plot = self.scene.mlab.plot3d(x, y, z, t,
                                tube_radius=self.radius, colormap='Spectral')
        else:
            print self.radius
            self.plot.mlab_source.set(tube_radius=self.radius)
            self.scene.mlab.draw()

    view = View(Item('scene', editor=SceneEditor(scene_class=MayaviScene),
                     height=250, width=300, show_label=False),
                Group(
                        'radius',
                     ),
                resizable=True,
                )

my_model = MyModel()
my_model.configure_traits()

This gives:

enter image description here

However, when I change the radius nothing happens with the visual line plot.


Solution

  • Don't use trait.set or trait.reset to set mayavi or vtk attributes. Indeed the MLineSource that you are using doesn't have such an attribute, but it probably wouldn't work even if it did.

    It's usually useful to find the mayavi attribute manually that controls this feature to see what mayavi object it is assigned to. In this case, going through the mayavi pipeline GUI shows that it is on the tube filter.

    mlab.plot3d is a helper function that tries to do everything for you and doesn't maintain a reference to the filters used. But in general it is good practice to keep references to each step in the mayavi pipeline if you construct the pipeline yourself. That way you can easily access the mayavi object which controls this.

    If you don't construct the pipeline yourself you can always get to it in the pipeline by manually navigating the tree of parent and child objects. In this case you can access it like so:

    @on_trait_change('radius,scene.activated')
        def update_plot(self):
            x, y, z, t = curve()
            if self.plot is None:
                self.plot = self.scene.mlab.plot3d(x, y, z, t,
                                    tube_radius=self.radius, colormap='Spectral')
            else:
                self.plot.parent.parent.filter.radius = self.radius