1
"""A simple wrapper around tvtk.tools.mlab suitable for MayaVi2! This
2
is meant to be used from the embedded Python interpreter in MayaVi2 or
3
from IPython with the "-wthread" switch.
5
There are several test functions at the end of this file that are
6
illustrative to look at.
10
# Author: Prabhu Ramachandran <prabhu_r@users.sf.net>
11
# Copyright (c) 2007, Enthought, Inc.
14
#TODO: * Add optional scalars to plot3d
15
# * Make streamline display colors by default
16
# * Investigate why the old surf_regular seemed to give more beautiful
17
# surfaces than the current surf. See for instance the difference
18
# between test_surf_lattice and the old test_surf_regular
20
# Standard library imports.
23
# Enthought library imports.
24
from envisage import get_application
25
from tvtk.api import tvtk
26
from tvtk.tools import mlab
27
from mayavi.modules.axes import Axes
28
from traits.api import HasTraits, Instance
29
from traitsui.api import View, Item, Group
31
# MayaVi related imports.
32
from mayavi.services import IMAYAVI
33
from mayavi.sources.vtk_data_source import VTKDataSource
34
from mayavi.filters.filter_base import FilterBase
35
from mayavi.modules.surface import Surface
36
from mayavi.modules.vectors import Vectors
37
from mayavi.modules.iso_surface import IsoSurface
38
from mayavi.modules.streamline import Streamline
39
from mayavi.modules.glyph import Glyph
40
from mayavi.modules.text import Text
41
from mayavi.app import Mayavi
42
from mayavi.core.source import Source
43
from mayavi.core.module import Module
44
from mayavi.core.module_manager import ModuleManager
45
from mayavi.sources.array_source import ArraySource
47
__all__ = ["scalarscatter", "vectorscatter", "scalarfield",
48
"vectorfield", "isosurface", "vectors", "glyph", "streamline",
49
"quiver3d", "points3d", "surf", "contour_surf", "imshow", "outline",
50
"axes", "figure", "clf", "savefig", "xlabel", "ylabel", "zlabel",
51
"title", "scalarbar", "vectorbar"]
54
######################################################################
55
# Application and mayavi instances.
57
application = get_application()
59
if application is not None:
60
mayavi = application.get_service(IMAYAVI)
63
######################################################################
66
# This should be added as a new MayaVi module. It is here for testing
67
# and further improvements.
68
class ImageActor(Module):
71
actor = Instance(tvtk.ImageActor, allow_none=False)
73
view = View(Group(Item(name='actor', style='custom',
80
def setup_pipeline(self):
81
self.actor = tvtk.ImageActor()
83
def update_pipeline(self):
84
"""Override this method so that it *updates* the tvtk pipeline
85
when data upstream is known to have changed.
87
mm = self.module_manager
91
self.actor.input = src.outputs[0]
92
self.pipeline_changed = True
94
def update_data(self):
95
"""Override this method so that it flushes the vtk pipeline if
98
# Just set data_changed, the component should do the rest.
99
self.data_changed = True
101
def _actor_changed(self, old, new):
103
self.actors.remove(old)
104
self.actors.append(new)
106
######################################################################
108
def _make_glyph_data(points, vectors=None, scalars=None):
109
"""Makes the data for glyphs using mlab.
111
g = mlab.Glyphs(points, vectors, scalars)
114
def _make_default_figure():
115
"""Checks to see if a valid mayavi instance is running. If not
119
if mayavi is None or application.stopped is not None:
121
mayavi = get_application().get_service(IMAYAVI)
124
def _add_data(tvtk_data, name=''):
125
"""Add a TVTK data object `tvtk_data` to the mayavi pipleine.
126
Give the object a name of `name`.
128
if isinstance(tvtk_data, tvtk.Object):
131
elif isinstance(tvtk_data, Source):
135
"first argument should be either a TVTK object"\
136
" or a mayavi source"
140
_make_default_figure()
145
"""Traverse a tree accessing the nodes children attribute.
148
for leaf in node.children:
149
for leaflet in _traverse(leaf):
151
except AttributeError:
155
def _find_data(object):
156
"""Goes up the vtk pipeline to find the data sources of a given
159
if isinstance(object, ModuleManager):
160
inputs = [object.source]
161
elif hasattr(object, 'module_manager'):
162
inputs = [object.module_manager.source]
163
elif hasattr(object, 'data') or isinstance(object, ArraySource):
166
raise TypeError, 'Cannot find data source for given object'
171
if hasattr(input, 'inputs'):
172
inputs += input.inputs
173
elif hasattr(input, 'image_data'):
174
data_sources.append(input.image_data)
176
data_sources.append(input.data)
181
def _has_scalar_data(object):
182
"""Tests if an object has scalar data.
184
data_sources = _find_data(object)
185
for source in data_sources:
186
if source.point_data.scalars is not None:
188
elif source.cell_data.scalars is not None:
192
def _has_vector_data(object):
193
"""Tests if an object has vector data.
195
data_sources = _find_data(object)
196
for source in data_sources:
197
if source.point_data.vectors is not None:
199
elif source.cell_data.vectors is not None:
203
def _has_tensor_data(object):
204
"""Tests if an object has tensor data.
206
data_sources = _find_data(object)
207
for source in data_sources:
208
if source.point_data.tensors is not None:
210
elif source.cell_data.tensors is not None:
214
def _find_module_manager(object=None, data_type=None):
215
"""If an object is specified, returns its module_manager, elsewhere finds
216
the first module_manager in the scene.
219
for object in _traverse(gcf()):
220
if isinstance(object, ModuleManager):
221
if ((data_type == 'scalar' and not _has_scalar_data(object))
222
or (data_type == 'vector' and not _has_vector_data(object))
223
or (data_type == 'tensor' and not _has_tensor_data(object))):
227
print("No object in the scene has a color map")
229
if hasattr(object, 'module_manager'):
230
if ((data_type == 'scalar' and _has_scalar_data(object))
231
or (data_type == 'vector' and _has_vector_data(object))
232
or (data_type == 'tensor' and _has_tensor_data(object))
233
or data_type is None):
234
return object.module_manager
236
print("This object has no %s data" % data_type)
238
print("This object has no color map")
241
def _orient_colorbar(colorbar, orientation):
242
"""Orients the given colorbar (make it horizontal or vertical).
244
if orientation == "vertical":
245
colorbar.orientation = "vertical"
247
colorbar.height = 0.8
248
colorbar.position = (0.01, 0.15)
249
elif orientation == "horizontal":
250
colorbar.orientation = "horizontal"
252
colorbar.height = 0.17
253
colorbar.position = (0.1, 0.01)
255
print "Unknown orientation"
258
def _typical_distance(data_obj):
259
""" Returns a typical distance in a cloud of points.
260
This is done by taking the size of the bounding box, and dividing it
261
by the cubic root of the number of points.
263
x_min, x_max, y_min, y_max, z_min, z_max = data_obj.bounds
264
distance = scipy.sqrt(((x_max-x_min)**2 + (y_max-y_min)**2 +
265
(z_max-z_min)**2)/(4*
266
data_obj.number_of_points**(0.33)))
272
######################################################################
275
def scalarscatter(*args, **kwargs):
277
Creates scattered scalar data.
282
scalarscatter(s, ...)
283
scalarscatter(x, y, z, s, ...)
284
scalarscatter(x, y, z, f, ...)
286
If only 1 array s is passed the x, y and z arrays are assumed to be
287
made from the indices of vectors.
289
If 4 positional arguments are passed the last one must be an array s, or
290
a callable, f, that returns an array.
295
x -- x coordinates of the points of the mesh (optional).
297
y -- y coordinates of the points of the mesh (optional).
299
z -- z coordinates of the points of the mesh (optional).
303
f -- callable that is used to build the scalar data (only if 4
304
positional arguments are passed).
309
name -- The name of the vtk object created. Default: 'Scattered scalars'
311
extent -- [xmin, xmax, ymin, ymax, zmin, zmax]
312
Default is the x, y, z arrays extent.
317
x, y, z = scipy.indices(s.shape)
323
raise ValueError, "wrong number of arguments"
325
assert ( x.shape == y.shape and
326
y.shape == z.shape and
327
s.shape == z.shape ), "argument shape are not equal"
329
if 'extent' in kwargs:
330
xmin, xmax, ymin, ymax, zmin, zmax = kwargs.pop('extent')
331
x = xmin + x*(xmax - xmin)/float(x.max() - x.min()) -x.min()
332
y = ymin + y*(ymax - ymin)/float(y.max() - y.min()) -y.min()
333
z = zmin + z*(zmax - zmin)/float(z.max() - z.min()) -z.min()
335
points = scipy.c_[x.ravel(), y.ravel(), z.ravel()]
337
name = kwargs.pop('name', 'Scattered scalars')
339
data = _make_glyph_data(points, None, scalars)
340
data_obj = _add_data(data, name)
343
def vectorscatter(*args, **kwargs):
345
Creates scattered vector data.
350
vectorscatter(u, v, w, ...)
351
vectorscatter(x, y, z, u, v, w, ...)
352
vectorscatter(x, y, z, f, ...)
354
If only 3 arrays u, v, w are passed the x, y and z arrays are assumed to be
355
made from the indices of vectors.
357
If 4 positional arguments are passed the last one must be a callable, f,
358
that returns vectors.
363
x -- x coordinates of the points of the mesh (optional).
365
y -- y coordinates of the points of the mesh (optional).
367
z -- z coordinates of the points of the mesh (optional).
369
u -- x coordinnate of the vector field
371
v -- y coordinnate of the vector field
373
w -- z coordinnate of the vector field
375
f -- callable that is used to build the vector field (only if 4
376
positional arguments are passed).
381
name -- The name of the vtk object created. Default: 'Scattered vector'
383
extent -- [xmin, xmax, ymin, ymax, zmin, zmax]
384
Default is the x, y, z arrays extent.
386
scalars -- The scalars associated to the vectors. Defaults to none.
391
x, y, z = scipy.indices(u.shape)
393
x, y, z, u, v, w = args
396
assert callable(f), "when used with 4 arguments last argument must be callable"
399
raise ValueError, "wrong number of arguments"
401
assert ( x.shape == y.shape and
402
y.shape == z.shape and
403
z.shape == u.shape and
404
u.shape == v.shape and
405
v.shape == w.shape ), "argument shape are not equal"
407
if 'extent' in kwargs:
408
xmin, xmax, ymin, ymax, zmin, zmax = kwargs.pop('extent')
409
x = xmin + x*(xmax - xmin)/float(x.max() - x.min()) -x.min()
410
y = ymin + y*(ymax - ymin)/float(y.max() - y.min()) -y.min()
411
z = zmin + z*(zmax - zmin)/float(z.max() - z.min()) -z.min()
413
points = scipy.c_[x.ravel(), y.ravel(), z.ravel()]
414
vectors = scipy.c_[u.ravel(), v.ravel(), w.ravel()]
415
if 'scalars' in kwargs:
416
scalars = kwargs['scalars'].ravel()
419
name = kwargs.pop('name', 'Scattered vectors')
421
data = _make_glyph_data(points, vectors, scalars)
422
data_obj = _add_data(data, name)
425
def scalarfield(*args, **kwargs):
427
Creates a scalar field data.
433
scalarfield(x, y, z, s, ...)
434
scalarfield(x, y, z, f, ...)
436
If only 1 array s is passed the x, y and z arrays are assumed to
437
be made from the indices of the s array.
439
If the x, y and z arrays are passed they are supposed to have been
440
generated by `numpy.mgrid`. The function builds a scalar field assuming
441
the points are regularly spaced.
446
x -- x coordinates of the points of the mesh (optional).
448
y -- y coordinates of the points of the mesh (optional).
450
z -- z coordinates of the points of the mesh (optional).
454
f -- callable that is used to build the scalar field (only if 4
455
positional arguments are passed).
460
name -- The name of the vtk object created. Default: 'Scalar field'
462
extent -- [xmin, xmax, ymin, ymax, zmin, zmax]
463
Default is the x, y, z arrays extent.
466
# Get the keyword args.
467
name = kwargs.get('name', 'Scalar field')
471
x, y, z = scipy.indices(s.shape)
477
raise ValueError, "wrong number of arguments"
479
assert ( x.shape == y.shape and
480
y.shape == z.shape and
481
s.shape == z.shape ), "argument shape are not equal"
483
if 'extent' in kwargs:
484
xmin, xmax, ymin, ymax, zmin, zmax = kwargs.pop('extent')
485
x = xmin + x*(xmax - xmin)/float(x.max() - x.min()) -x.min()
486
y = ymin + y*(ymax - ymin)/float(y.max() - y.min()) -y.min()
487
z = zmin + z*(zmax - zmin)/float(z.max() - z.min()) -z.min()
489
points = scipy.c_[x.ravel(), y.ravel(), z.ravel()]
490
dx = x[1, 0, 0] - x[0, 0, 0]
491
dy = y[0, 1, 0] - y[0, 0, 0]
492
dz = z[0, 0, 1] - z[0, 0, 0]
494
data = ArraySource(scalar_data=s,
495
origin=[points[0, 0], points[0, 1], points[0, 2]],
496
spacing=[dx, dy, dz])
497
data_obj = _add_data(data, name)
500
def vectorfield(*args, **kwargs):
502
Creates a vector field data.
507
vectorfield(u, v, w, ...)
508
vectorfield(x, y, z, u, v, w, ...)
509
vectorfield(x, y, z, f, ...)
511
If only 3 arrays u, v, w are passed the x, y and z arrays are assumed to
512
be made from the indices of the u, v, w arrays.
514
If the x, y and z arrays are passed they are supposed to have been
515
generated by `numpy.mgrid`. The function builds a vector field assuming
516
the points are regularly spaced.
521
x -- x coordinates of the points of the mesh (optional).
523
y -- y coordinates of the points of the mesh (optional).
525
z -- z coordinates of the points of the mesh (optional).
527
u -- x coordinnate of the vector field
529
v -- y coordinnate of the vector field
531
w -- z coordinnate of the vector field
533
f -- callable that is used to build the vector field (only if 4
534
positional arguments are passed).
539
name -- The name of the vtk object created. Default: 'Vector field'
541
extent -- [xmin, xmax, ymin, ymax, zmin, zmax]
542
Default is the x, y, z arrays extent.
544
scalars -- The scalars associated to the vectors. Defaults to none.
546
transpose_vectors -- If the additional argument
547
transpose_vectors is passed, then the
548
input vectors array is suitably
549
transposed. By default transpose_vectors
550
is True so that the array is in the
551
correct format that VTK expects.
552
However, a transposed array is not
553
contiguous and thus a copy is made, this
554
also means that any changes to the users
555
input array will will not be reflected in
556
the renderered object (provided you know
557
how to do this). Thus, sometimes users
558
might want to provide already transposed
559
data suitably formatted. In these cases
560
one should set transpose_vectors to
564
# Get the keyword args.
565
transpose_vectors = kwargs.get('transpose_vectors', True)
569
x, y, z = scipy.indices(u.shape)
571
x, y, z, u, v, w = args
574
assert callable(f), "when used with 4 arguments last argument must be callable"
577
raise ValueError, "wrong number of arguments"
579
assert ( x.shape == y.shape and
580
y.shape == z.shape and
581
z.shape == u.shape and
582
u.shape == v.shape and
583
v.shape == w.shape ), "argument shape are not equal"
585
if 'extent' in kwargs:
586
xmin, xmax, ymin, ymax, zmin, zmax = kwargs.pop('extent')
587
x = xmin + x*(xmax - xmin)/float(x.max() - x.min()) -x.min()
588
y = ymin + y*(ymax - ymin)/float(y.max() - y.min()) -y.min()
589
z = zmin + z*(zmax - zmin)/float(z.max() - z.min()) -z.min()
591
points = scipy.c_[x.ravel(), y.ravel(), z.ravel()]
592
vectors = scipy.concatenate([u[..., scipy.newaxis],
593
v[..., scipy.newaxis],
594
w[..., scipy.newaxis] ],
596
if 'scalars' in kwargs:
597
scalars = kwargs['scalars']
600
name = kwargs.pop('name', 'Vector field')
601
dx = x[1, 0, 0] - x[0, 0, 0]
602
dy = y[0, 1, 0] - y[0, 0, 0]
603
dz = z[0, 0, 1] - z[0, 0, 0]
605
if not transpose_vectors:
606
vectors.shape = vectors.shape[::-1]
607
data = ArraySource(transpose_input_array=transpose_vectors,
610
origin=[points[0, 0], points[0, 1], points[0, 2]],
611
spacing=[dx, dy, dz])
612
data_obj = _add_data(data, name)
615
######################################################################
618
def isosurface(data_obj, name='IsoSurface', transparent=True,
620
""" Applies the Iso-Surface mayavi module to the given VTK data object.
624
# Check what type the 'contours' are and do whatever is needed.
632
iso.contour.contours = contours
634
assert type(contours) == int, "The contours argument must be an integer"
635
assert contours > 1, "The contours argument must be positivee"
636
iso.contour.set(auto_contours=True,
637
number_of_contours=contours)
639
mayavi.add_module(iso, obj=data_obj)
642
data_range = iso.module_manager.scalar_lut_manager.data_range
643
iso.module_manager.scalar_lut_manager.lut.alpha_range = \
645
data_range = ( scipy.mean(data_range)
646
+ 0.4 * ( data_range.max() - data_range.min())
647
* scipy.array([-1, 1]))
652
def vectors(data_obj, color=None, name='Vectors', mode='2d',
654
""" Applies the Vectors mayavi module to the given VTK data object.
656
v = Vectors(name=name)
657
mayavi.add_module(v, obj=data_obj)
658
mode_map = {'2d': 0, 'arrow': 1, 'cone': 2, 'cylinder': 3,
659
'sphere': 4, 'cube': 5, 'point': 6}
661
v.glyph.glyph_source = tvtk.PointSource(radius=0,
664
v.glyph.glyph_source = v.glyph.glyph_list[mode_map[mode]]
666
v.glyph.color_mode = 'no_coloring'
667
v.actor.property.color = color
668
elif _has_scalar_data(data_obj) :
669
v.glyph.color_mode = 'color_by_scalar'
671
v.glyph.color_mode = 'color_by_vector'
672
v.glyph.glyph.scale_factor = scale_factor
675
def glyph(data_obj, color=None, name='Glyph', mode='sphere',
677
""" Applies the Glyph mayavi module to the given VTK data object.
680
mayavi.add_module(g, obj=data_obj)
681
mode_map = {'2d': 0, 'arrow': 1, 'cone': 2, 'cylinder': 3,
682
'sphere': 4, 'cube': 5, 'point': 6}
684
g.glyph.glyph_source = tvtk.PointSource(radius=0,
687
g.glyph.glyph_source = g.glyph.glyph_list[mode_map[mode]]
689
g.actor.property.color = color
690
if _has_scalar_data(data_obj) :
691
g.glyph.color_mode = 'color_by_scalar'
692
g.glyph.scale_mode = 'scale_by_scalar'
693
g.glyph.glyph.scale_factor = scale_factor
696
#FIXME : just started this procedure !! Need to modify the color so that if
697
# none it warps a scalar. Need to add a kwarg for the source.
698
def streamline(data_obj, color=None, name='Streamline', ):
699
""" Applies the Streamline mayavi module to the given VTK data object.
702
mayavi.add_module(st, obj=data_obj)
704
st.actor.property.color = color
705
elif _has_scalar_data(data_obj) :
706
st.actor.mapper.scalar_visibility = True
708
st.actor.mapper.interpolate_scalars_before_mapping = True
709
st.actor.mapper.scalar_visibility = True
712
######################################################################
715
def quiver3d(*args, **kwargs):
717
Plots glyphs (like arrows) indicating the direction of the vectors
718
for a 3D volume of data supplied as arguments.
723
quiver3d(vectordata, ...)
724
quiver3d(u, v, w, ...)
725
quiver3d(x, y, z, u, v, w, ...)
726
quiver3d(x, y, z, f, ...)
728
If only one positional argument is passed, it should be VTK data
729
object with vector data.
731
If only 3 arrays u, v, w are passed the x, y and z arrays are assumed to be
732
made from the indices of vectors.
734
If 4 positional arguments are passed the last one must be a callable, f,
735
that returns vectors.
740
vectordata -- VTK data object with vector data, such as created
741
by vectorscatter of vectorfield.
743
x -- x coordinates of the points of the mesh (optional).
745
y -- y coordinates of the points of the mesh (optional).
747
z -- z coordinates of the points of the mesh (optional).
749
u -- x coordinnate of the vector field
751
v -- y coordinnate of the vector field
753
w -- z coordinnate of the vector field
755
f -- callable that is used to build the vector field (only if 4
756
positional arguments are passed).
761
name -- The name of the vtk object created. Default: 'Quiver3D'
763
mode -- This should be one of ['2d' (2d arrows),
764
'arrow', 'cone', 'cylinder', 'sphere', 'cube',
765
'point'] and depending on what is passed shows an
766
appropriate glyph. It defaults to a 3d arrow.
768
extent -- [xmin, xmax, ymin, ymax, zmin, zmax]
769
Default is the x, y, z arrays extent.
771
scalars -- The scalars used to display the color of the glyphs.
772
Defaults to the norm of the vectors.
774
color -- The color of the glyphs in the absence of scalars.
775
Default: (1., 0., 0.)
777
autoscale -- Autoscale the size of the glyph.
780
scale_factor -- Default 1
785
data_kwargs = kwargs.copy()
786
data_kwargs.pop('name','')
788
data_obj = vectorscatter(*args, **data_kwargs)
790
data_obj = vectorfield(*args, **data_kwargs)
792
if not 'name' in kwargs:
793
kwargs['name'] = 'Quiver3D'
795
if not 'mode' in kwargs:
796
kwargs['mode'] = 'arrow'
798
if not 'autoscale' in kwargs or kwargs['autoscale']:
799
scale_factor = kwargs.get('scale_factor', 1.)
800
kwargs['scale_factor'] = (scale_factor *
801
_typical_distance(_find_data(data_obj)[0]) )
802
kwargs.pop('autoscale', '')
804
return vectors(data_obj, **kwargs)
806
def points3d(*args, **kwargs):
808
Plots glyphs (like points) at the position of the supplied data.
813
points3d(scalardata, ...)
815
points3d(x, y, z, s, ...)
816
points3d(x, y, z, f, ...)
818
If only one positional argument is passed, it should be VTK data
819
object with scalar data.
821
If only 3 arrays x, y, z all the points are drawn with the same size
824
If 4 positional arguments are passed the last one can be an array s
825
or a callable f that gives the size and color of the glyph.
830
scalardata -- VTK data object with scalar data, such as created
833
x -- x coordinates of the points.
835
y -- y coordinates of the points.
837
z -- z coordinates of the points.
839
s -- array giving the color and size of the glyphs (optional).
841
f -- callable that returns the scalar associated with the points
842
as a function of position.
847
name -- The name of the vtk object created. Default: 'Points3D'
849
mode -- This should be one of ['2d' (2d arrows),
850
'arrow', 'cone', 'cylinder', 'sphere', 'cube',
851
'point'] and depending on what is passed shows an
852
appropriate glyph. It defaults to a sphere.
854
extent -- [xmin, xmax, ymin, ymax, zmin, zmax]
855
Default is the x, y, z arrays extent.
857
scalars -- The scalars used to display the color of the glyphs.
859
color -- The color of the glyphs. Overrides the scalar array.
860
Default: (1., 0., 0.).
862
autoscale -- Autoscale the size of the glyph.
865
scale_factor -- Default 1
870
data_kwargs = kwargs.copy()
871
data_kwargs.pop('name','')
878
s = scipy.ones(x.shape)
879
data_obj = scalarscatter(x, y, z, s, **data_kwargs)
881
if not 'name' in kwargs:
882
kwargs['name'] = 'Points3D'
884
if not 'mode' in kwargs:
885
kwargs['mode'] = 'sphere'
887
if not 'autoscale' in kwargs or kwargs['autoscale']:
888
scale_factor = kwargs.get('scale_factor', 1.)
889
kwargs['scale_factor'] = (0.6* scale_factor *
890
_typical_distance(_find_data(data_obj)[0]) )
891
kwargs.pop('autoscale', '')
893
g = glyph(data_obj, **kwargs)
895
g.glyph.scale_mode = 'data_scaling_off'
896
if 'color' in kwargs:
897
g.glyph.color_mode = 'no_coloring'
900
def contour3d(*args, **kwargs):
902
Plots iso-surfaces for a 3D volume of data suplied as arguments.
907
contour3d(scalars, ...)
908
contour3d(scalarfield, ...)
913
scalars -- A 3D array giving the value of the scalar on a grid.
915
scalarfield -- VTK data object with scalar field data, such as
916
created by scalarfield.
922
name -- The name of the vtk object created. Default: 'Contour3D'
924
transpose_scalars -- If the additional argument
925
transpose_scalars is passed, then the
926
input scalar array is suitably
927
transposed. By default transpose_scalars
928
is True so that the array is in the
929
correct format that VTK expects.
930
However, a transposed array is not
931
contiguous and thus a copy is made, this
932
also means that any changes to the users
933
input array will will not be reflected in
934
the renderered object (provided you know
935
how to do this). Thus, sometimes users
936
might want to provide already transposed
937
data suitably formatted. In these cases
938
one should set transpose_scalars to
942
contours -- Integer/list specifying number/list of
943
iso-surfaces. Specifying 0 shows no contours.
944
Specifying a list of values will only give the
945
requested contours asked for. Default: 3
947
extent -- [xmin, xmax, ymin, ymax, zmin, zmax]
948
Default is the shape of the scalars
950
transparent -- Boolean to make the opacity of the isosurfaces depend
951
on the scalar. Default: True
954
if hasattr(args[0], 'shape'):
956
assert len(scalars.shape) == 3, 'Only 3D arrays are supported.'
957
data_kwargs = kwargs.copy()
958
data_kwargs.pop('contours', '')
959
data_kwargs.pop('transparent', '')
960
if not 'name' in kwargs:
961
data_kwargs['name'] = 'Contour3D'
962
data_obj = scalarfield(scalars, **data_kwargs)
966
raise TypeError, "contour3d takes only one argument"
968
# Remove extra kwargs that are not needed for the iso-surface.
969
kwargs.pop('extent', '')
970
kwargs.pop('name', '')
972
return isosurface(data_obj, **kwargs)
974
######################################################################
975
# The mlab functionality.
977
def plot3d(x, y, z, radius=0.01, use_tubes=True, color=(1., 0., 0.) ,
979
"""Draws lines between points.
984
x -- x coordinates of the points of the line
986
y -- y coordinates of the points of the line
988
z -- z coordinates of the points of the line
993
color -- color of the line. Default: (1., 0., 0.)
995
use_tubes -- Enables the drawing of the lines as tubes. Default: True
997
radius -- radius of the tubes created. Default: 0.01
999
name -- The name of the vtk object created. Default: 'Plot3D'
1002
assert ( x.shape == y.shape and
1003
y.shape == z.shape ), "argument shape are not equal"
1005
points = scipy.c_[x, y, z]
1006
np = len(points) - 1
1007
lines = scipy.zeros((np, 2), 'l')
1008
lines[:,0] = scipy.arange(0, np-0.5, 1, 'l')
1009
lines[:,1] = scipy.arange(1, np+0.5, 1, 'l')
1010
pd = tvtk.PolyData(points=points, lines=lines)
1013
filter = tvtk.TubeFilter(number_of_sides=6)
1014
filter.radius = radius
1015
f = FilterBase(filter=filter, name='TubeFilter')
1016
mayavi.add_filter(f)
1018
s.actor.mapper.scalar_visibility = False
1019
mayavi.add_module(s)
1020
s.actor.property.color = color
1023
def surf(*args, **kwargs):
1025
Plots a surface using grid spaced data supplied as 2D arrays.
1030
surf(z, scalars=z, ...)
1031
surf(x, y, z, scalars=z, ...)
1033
If only one array z is passed the x and y arrays are assumed to be made
1034
of the indices of z.
1035
z is the elevation matrix. If no `scalars` argument is passed the color
1036
of the surface also represents the z matrix. Setting the `scalars` argument
1037
to None prevents this.
1042
x -- x coordinates of the points of the mesh (optional).
1044
y -- y coordinates of the points of the mesh (optional).
1046
z -- A 2D array giving the elevation of the mesh.
1047
Also will work if z is a callable which supports x and y arrays
1048
as the arguments, but x and y must then be supplied.
1053
extent -- [xmin, xmax, ymin, ymax, zmin, zmax]
1054
Default is the x, y, z arrays extent.
1056
scalars -- An array of the same shape as z that gives the color of the
1057
surface. This can also be a function that takes x and
1060
represention -- can be 'surface', 'wireframe', 'points', or 'mesh'
1061
Default is 'surface'
1063
color -- The color of the mesh in the absence of scalars.
1065
name -- The name of the vtk object created. Default is "Surf"
1069
x, y = scipy.indices(z.shape)
1074
if not 'scalars' in kwargs:
1075
kwargs['scalars'] = z
1076
if callable(kwargs['scalars']):
1077
kwargs['scalars'] = kwargs['scalars'](x, y)
1078
if 'color' in kwargs and kwargs['color']:
1079
kwargs['scalar_visibility'] = False
1080
if 'extent' in kwargs:
1081
xmin, xmax, ymin, ymax, zmin, zmax = kwargs.pop('extent')
1082
x = xmin + x*(xmax - xmin)/float(x.max() - x.min()) -x.min()
1083
y = ymin + y*(ymax - ymin)/float(y.max() - y.min()) -y.min()
1084
z = zmin + z*(zmax - zmin)/float(z.max() - z.min()) -z.min()
1085
return _surf(x, y, z, **kwargs)
1087
def _surf(x, y, z, scalars=None, scalar_visibility=True,
1088
color=(0.5, 1.0, 0.5), name='Surf', representation='surface',
1089
tube_radius=0.05, sphere_radius=0.05, ):
1090
""" Functions that does the work for "surf". It is called with the right
1091
number of arguments after the "surf" fonction does the magic to
1092
translate the user-supplied arguments into something this function
1094
triangles, points = mlab.make_triangles_points(x, y, z, scalars)
1095
data = mlab.make_triangle_polydata(triangles, points, scalars)
1096
_add_data(data, name)
1098
if representation == 'mesh':
1099
# Extract the edges.
1100
ef = tvtk.ExtractEdges()
1101
extract_edges = FilterBase(filter=ef, name='ExtractEdges')
1102
mayavi.add_filter(extract_edges)
1104
# Now show the lines with tubes.
1105
tf = tvtk.TubeFilter(radius=tube_radius)
1106
tube = FilterBase(filter=tf, name='TubeFilter')
1107
mayavi.add_filter(tube)
1108
s = Surface(name='Tubes')
1109
s.actor.mapper.scalar_visibility = scalar_visibility
1110
mayavi.add_module(s)
1111
s.actor.property.color = color
1113
# Show the points with glyphs.
1114
g = Glyph(name='Spheres')
1115
g.glyph.glyph_source = g.glyph.glyph_list[4]
1116
g.glyph.glyph_source.radius = sphere_radius
1117
extract_edges.add_child(g)
1118
g.glyph.scale_mode = 'data_scaling_off'
1119
g.actor.mapper.scalar_visibility=scalar_visibility
1120
g.actor.property.color = color
1124
s.actor.mapper.scalar_visibility = scalar_visibility
1125
mayavi.add_module(s)
1126
s.actor.property.color = color
1127
s.actor.property.representation = representation
1130
def contour_surf(*args, **kwargs):
1131
""" Plots the contours of a surface using grid spaced data supplied as
1134
Function signatures::
1136
contour_surf(z, scalars=z, ...)
1137
contour_surf(surf_object, ...)
1138
contour_surf(x, y, z, scalars=z, ...)
1140
If only one array z is passed the x and y arrays are assumed to be made
1141
of the indices of z.
1142
z is the elevation matrix. If no `scalars` argument is passed the
1143
contours are contour lines of the elevation, elsewhere they are
1144
contour lines of the scalar array.
1145
A surf object can also be passed in which case the function adds contours
1146
to the existing surf.
1152
x coordinates of the points of the mesh (optional).
1154
y coordinates of the points of the mesh (optional).
1156
A 2D array giving the elevation of the mesh.
1157
Also will work if z is a callable which supports x and y arrays
1158
as the arguments, but x and y must then be supplied.
1163
extent : [xmin, xmax, ymin, ymax, zmin, zmax]
1164
Default is the x, y, z arrays extent.
1166
Integer/list specifying number/list of
1167
iso-surfaces. Specifying 0 shows no contours.
1168
Specifying a list of values will only give the
1169
requested contours asked for. Default: 10
1171
An array of the same shape as z that gives the scalar
1172
data to plot the contours of. This can also be a function
1173
that takes x and y as arguments.
1175
The color of the contour lines. If None, this is given by
1178
If this argument is given the contours are drawn on a
1179
plane at the altitude specified by this argument instead
1180
of on the surface. Currently this cannot be used when a
1181
surf_object is passed as an argument.
1183
The name of the vtk object created. Default is "Contour Surf"
1187
contours = kwargs.pop('contours', 10)
1188
if len(args) == 1 and isinstance(args[0], Surface):
1189
if 'contour_z' in kwargs:
1190
raise TypeError, 'contour_z cannot be used when passing a surf_object'
1191
data_object = _find_module_manager(args[0]).source
1192
mm = ModuleManager()
1193
mayavi.add_module(mm, obj=data_object)
1194
s = Surface(name=kwargs.get('name', 'Contours'))
1197
if 'contour_z' in kwargs:
1198
if not 'scalars' in kwargs:
1199
kwargs['scalars'] = args[-1]
1200
contour_z = kwargs.pop('contour_z')
1202
args[-1] = scipy.zeros_like(args[0])
1203
if not 'name' in kwargs:
1204
kwargs['name'] = "Contour Surf"
1205
s = surf(*args, **kwargs)
1206
s.enable_contours = True
1208
# Check what type the 'contours' are and do whatever is needed.
1213
contour_list = False
1216
s.contour.contours = contours
1217
s.contour.set(auto_contours=False)
1219
assert type(contours) == int, "The contours argument must be an integer"
1220
assert contours > 1, "The contours argument must be positive"
1221
s.contour.set(auto_contours=True,
1222
number_of_contours=contours)
1226
def imshow(arr, extent=None, interpolate=False,
1227
lut_mode='blue-red', file_name='',
1229
"""Allows one to view a 2D Numeric array as an image. This works
1230
best for very large arrays (like 1024x1024 arrays).
1235
arr -- Array to be viewed.
1240
scale -- Scale the x, y and z axis as per passed values.
1241
Defaults to [1.0, 1.0, 1.0].
1243
extent -- [xmin, xmax, ymin, ymax]
1244
Default is the x, y arrays extent
1246
interpolate -- Boolean to interpolate the data in the image.
1249
assert len(arr.shape) == 2, "Only 2D arrays can be viewed!"
1253
xmin, xmax, ymin, ymax = extent
1260
xa = scipy.linspace(xmin, xmax, nx, 'f')
1261
ya = scipy.linspace(ymin, ymax, ny, 'f')
1263
arr_flat = scipy.ravel(arr)
1264
min_val = min(arr_flat)
1265
max_val = max(arr_flat)
1267
sp = mlab._create_structured_points_direct(xa, ya)
1269
from mayavi.core.lut_manager import LUTManager
1270
lut = LUTManager(lut_mode=lut_mode, file_name=file_name)
1271
lut.data_range = min_val, max_val
1272
a = lut.lut.map_scalars(arr_flat, 0, 0)
1274
sp.point_data.scalars = a
1275
sp.scalar_type = 'unsigned_char'
1276
sp.number_of_scalar_components = 4
1277
d = _add_data(sp, name)
1280
ia.actor.interpolate = interpolate
1281
mayavi.add_module(ia)
1284
######################################################################
1285
# Non data-related drawing elements
1286
def outline(object=None, color=None, name='Outline'):
1287
"""Creates an outline for the current data.
1292
object -- the object for which we create the outline
1295
color -- The color triplet, eg: ( 1., 0., 0.)
1297
from mayavi.modules.outline import Outline
1298
mayavi = _make_default_figure()
1300
for obj in _traverse(scene):
1301
if isinstance(obj, Outline) and obj.name == name:
1305
o = Outline(name=name)
1306
if object is not None:
1309
mayavi.add_module(o)
1311
color = scene.scene.foreground
1312
if not color is None:
1313
o.actor.property.color = color
1316
def axes(color=None, xlabel=None, ylabel=None, zlabel=None,
1317
object=None, name='Axes'):
1318
"""Creates an axes for the current data.
1323
color -- The color triplet, eg: (1., 0., 0.)
1325
xlabel -- the label of the x axis, default: ''
1327
ylabel -- the label of the y axis, default: ''
1329
zlabel -- the label of the z axis, default: ''
1331
object -- the object for which we create the axes.
1333
mayavi = _make_default_figure()
1336
if object is not None:
1341
for obj in _traverse(scene):
1342
if isinstance(obj, Axes) and obj.name == name:
1347
mayavi.add_module(a)
1351
color = scene.scene.foreground
1358
if color is not None:
1359
a.property.color = color
1360
if xlabel is not None:
1361
a.axes.x_label = xlabel
1362
if ylabel is not None:
1363
a.axes.y_label = ylabel
1364
if zlabel is not None:
1365
a.axes.z_label = zlabel
1369
"""If you are running from IPython this will start up mayavi for
1370
you! This returns the current running MayaVi script instance.
1372
global mayavi, application
1373
if mayavi is not None and application.stopped is None:
1375
return mayavi.engine.current_scene
1378
m.script.new_scene()
1379
engine = m.script.engine
1381
application = m.application
1382
return mayavi.engine.current_scene
1385
"""Return a handle to the current figure.
1387
return mayavi.engine.current_scene
1390
"""Clear the current figure.
1394
scene.children[:] = []
1395
except AttributeError:
1398
def savefig(filename, size=None, **kwargs):
1399
""" Save the current scene.
1400
The output format are deduced by the extension to filename.
1401
Possibilities are png, jpg, bmp, tiff, ps, eps, pdf, rib (renderman),
1402
oogl (geomview), iv (OpenInventor), vrml, obj (wavefront)
1404
If an additional size (2-tuple) argument is passed the window
1405
is resized to the specified size in order to produce a
1406
suitably sized output image. Please note that when the window
1407
is resized, the window may be obscured by other widgets and
1408
the camera zoom is not reset which is likely to produce an
1409
image that does not reflect what is seen on screen.
1411
Any extra keyword arguments are passed along to the respective
1412
image format's save method.
1414
gcf().scene.save(filename, size=size, **kwargs)
1417
"""Creates a set of axes if there isn't already one, and sets the x label
1419
return axes(xlabel=text)
1422
"""Creates a set of axes if there isn't already one, and sets the y label
1424
return axes(ylabel=text)
1427
""" Creates a set of axes if there isn't already one, and sets the z label
1429
return axes(zlabel=text)
1431
def title(text=None, color=None, size=None, name='Title'):
1432
"""Creates a title for the figure.
1437
text -- The text of the title, default: ''
1439
color -- The color triplet, eg: ( 1., 0., 0.)
1441
size -- The size, default: 1
1444
for object in _traverse(scene):
1445
if isinstance(object, Text) and object.name==name:
1450
mayavi.add_module(t)
1452
color = scene.scene.foreground
1455
if color is not None:
1456
t.property.color = color
1457
if text is not None:
1459
if text is not None or size is not None:
1460
t.width = min(0.05*size*len(t.text), 1)
1461
t.x_position = 0.5*(1 - t.width)
1465
def text(x=0, y=0, text='', color=None, size=None, name='Text'):
1466
"""Adds a text on the figure.
1470
x -- x position on the screen of the origin of the text, default: 0
1472
y -- y position on the screen of the origin of the text, default: 0
1474
text -- The text, default: ''
1476
color -- The color triplet, eg: ( 1., 0., 0.)
1478
size -- The size, default: 1
1482
mayavi.add_module(t)
1484
color = scene.scene.foreground
1486
t.property.color = color
1493
def scalarbar(object=None, title=None, orientation=None):
1494
"""Adds a colorbar for the scalar color mapping of the given object.
1496
If no object is specified, the first object with scalar data in the scene
1502
title -- The title string
1504
orientation -- Can be 'horizontal' or 'vertical'
1506
module_manager = _find_module_manager(object=object, data_type="scalar")
1507
if module_manager is None:
1509
if not module_manager.scalar_lut_manager.show_scalar_bar:
1512
if orientation is None:
1513
orientation = 'horizontal'
1514
colorbar = module_manager.scalar_lut_manager.scalar_bar
1515
if title is not None:
1516
colorbar.title = title
1517
if orientation is not None:
1518
_orient_colorbar(colorbar, orientation)
1519
module_manager.scalar_lut_manager.show_scalar_bar = True
1522
def vectorbar(object=None, title=None, orientation=None):
1523
"""Adds a colorbar for the vector color mapping of the given object.
1525
If no object is specified, the first object with vector data in the scene
1531
object -- Optional object to get the vector lut from
1533
title -- The title string
1535
orientation -- Can be 'horizontal' or 'vertical'
1537
module_manager = _find_module_manager(object=object, data_type="vector")
1538
if module_manager is None:
1540
if not module_manager.vector_lut_manager.show_scalar_bar:
1542
orientation = 'horizontal'
1543
colorbar = module_manager.vector_lut_manager.scalar_bar
1544
if title is not None:
1545
colorbar.title = title
1546
if orientation is not None:
1547
_orient_colorbar(colorbar, orientation)
1548
module_manager.vector_lut_manager.show_scalar_bar = True
1551
def colorbar(object=None, title=None, orientation=None):
1552
"""Adds a colorbar for the color mapping of the given object.
1554
If the object has scalar data, the scalar color mapping is
1555
represented. Elsewhere the vector color mapping is represented, if
1557
If no object is specified, the first object with a color map in the scene
1563
object -- Optional object to get the vector lut from
1565
title -- The title string
1567
orientation -- Can be 'horizontal' or 'vertical'
1569
colorbar = scalarbar(object=object, title=title, orientation=orientation)
1570
if colorbar is None:
1571
colorbar = vectorbar(object=object, title=title, orientation=orientation)
1575
######################################################################
1577
######################################################################
1579
"""Generates a pretty set of lines."""
1580
n_mer, n_long = 6, 11
1583
phi = scipy.arange(0.0, 2*pi + 0.5*dphi, dphi, 'd')
1585
x = scipy.cos(mu)*(1+scipy.cos(n_long*mu/n_mer)*0.5)
1586
y = scipy.sin(mu)*(1+scipy.cos(n_long*mu/n_mer)*0.5)
1587
z = scipy.sin(n_long*mu/n_mer)*0.5
1589
l = plot3d(x, y, z, radius=0.05, color=(0.0, 0.0, 0.8))
1592
def test_molecule():
1593
"""Generates and shows a Caffeine molecule."""
1594
o = [[30, 62, 19],[8, 21, 10]]
1595
ox, oy, oz = map(scipy.array, zip(*o))
1596
n = [[31, 21, 11], [18, 42, 14], [55, 46, 17], [56, 25, 13]]
1597
nx, ny, nz = map(scipy.array, zip(*n))
1598
c = [[5, 49, 15], [30, 50, 16], [42, 42, 15], [43, 29, 13], [18, 28, 12],
1599
[32, 6, 8], [63, 36, 15], [59, 60, 20]]
1600
cx, cy, cz = map(scipy.array, zip(*c))
1601
h = [[23, 5, 7], [32, 0, 16], [37, 5, 0], [73, 36, 16], [69, 60, 20],
1602
[54, 62, 28], [57, 66, 12], [6, 59, 16], [1, 44, 22], [0, 49, 6]]
1603
hx, hy, hz = map(scipy.array, zip(*h))
1605
oxygen = points3d(ox, oy, oz, scale_factor=8, autoscale=False,
1606
color=(1,0,0), name='Oxygen')
1607
nitrogen = points3d(nx, ny, nz, scale_factor=10, autoscale=False,
1608
color=(0,0,1), name='Nitrogen')
1609
carbon = points3d(cx, cy, cz, scale_factor=10, autoscale=False,
1610
color=(0,1,0), name='Carbon')
1611
hydrogen = points3d(hx, hy, hz, scale_factor=5, autoscale=False,
1612
color=(1,1,1), name='Hydrogen')
1614
def test_surf_lattice():
1615
"""Test Surf on regularly spaced co-ordinates like MayaVi."""
1617
sin, cos = scipy.sin, scipy.cos
1618
return sin(x+y) + sin(2*x - y) + cos(3*x+4*y)
1619
#return scipy.sin(x*y)/(x*y)
1621
x, y = scipy.mgrid[-7.:7.05:0.1, -5.:5.05:0.05]
1623
cs = contour_surf(x, y, f, contour_z=0)
1626
def test_simple_surf():
1627
"""Test Surf with a simple collection of points."""
1628
x, y = scipy.mgrid[0:3:1,0:3:1]
1629
return surf(x, y, scipy.asarray(x, 'd'))
1632
"""A very pretty picture of spherical harmonics translated from
1633
the octaviz example."""
1637
dphi, dtheta = pi/250.0, pi/250.0
1638
[phi,theta] = scipy.mgrid[0:pi+dphi*1.5:dphi,0:2*pi+dtheta*1.5:dtheta]
1639
m0 = 4; m1 = 3; m2 = 2; m3 = 3; m4 = 6; m5 = 2; m6 = 6; m7 = 4;
1640
r = sin(m0*phi)**m1 + cos(m2*phi)**m3 + sin(m4*theta)**m5 + cos(m6*theta)**m7
1641
x = r*sin(phi)*cos(theta)
1643
z = r*sin(phi)*sin(theta);
1645
return surf(x, y, z)
1647
def test_mesh_sphere():
1648
"""Create a simple sphere and test the mesh."""
1652
du, dv = pi/20.0, pi/20.0
1653
phi, theta = scipy.mgrid[0.01:pi+du*1.5:du, 0:2*pi+dv*1.5:dv]
1655
x = r*sin(phi)*cos(theta)
1656
y = r*sin(phi)*sin(theta)
1658
s = surf(x, y, z, representation='mesh',
1659
tube_radius=0.01, sphere_radius=0.025)
1662
"""Create a fancy looking mesh (example taken from octaviz)."""
1666
du, dv = pi/20.0, pi/20.0
1667
u, v = scipy.mgrid[0.01:pi+du*1.5:du, 0:2*pi+dv*1.5:dv]
1668
x = (1- cos(u))*cos(u+2*pi/3) * cos(v + 2*pi/3.0)*0.5
1669
y = (1- cos(u))*cos(u+2*pi/3) * cos(v - 2*pi/3.0)*0.5
1672
m = surf(x, y, z, scalar_visibility=True, representation='mesh',
1673
tube_radius=0.0075, sphere_radius=0.02)
1676
"""Show a large random array."""
1677
z_large = scipy.random.random((1024, 512))
1678
i = imshow(z_large, extent=[0., 1., 0., 1.])
1680
def test_contour3d():
1682
xmin, xmax, ymin, ymax, zmin, zmax = [-5,5,-5,5,-5,5]
1683
x, y, z = scipy.ogrid[xmin:xmax:dims[0]*1j,
1684
ymin:ymax:dims[1]*1j,
1685
zmin:zmax:dims[2]*1j]
1691
scalars = x*x*0.5 + y*y + z*z*2.0
1693
contour3d(scalars, contours=4)
1695
# Show an outline and zoom appropriately.
1697
mayavi.engine.current_scene.scene.isometric_view()
1699
def test_quiver3d():
1701
xmin, xmax, ymin, ymax, zmin, zmax = [-5,5,-5,5,-5,5]
1702
x, y, z = scipy.mgrid[xmin:xmax:dims[0]*1j,
1703
ymin:ymax:dims[1]*1j,
1704
zmin:zmax:dims[2]*1j]
1717
quiver3d(x, y, z, u, v, w)
1719
# Show an outline and zoom appropriately.
1721
mayavi.engine.current_scene.scene.isometric_view()
1723
def test_quiver3d_2d_data():
1725
xmin, xmax, ymin, ymax = [-5,5,-5,5]
1726
x, y = scipy.mgrid[xmin:xmax:dims[0]*1j,
1727
ymin:ymax:dims[1]*1j]
1735
w = scipy.zeros_like(x)
1737
quiver3d(x, y, w, u, v, w)
1739
# Show an outline and zoom appropriately.
1741
mayavi.engine.current_scene.scene.isometric_view()