Question

Is it possible in mayavi to specify individually both the size and the colors of every point?

That API is cumbersome to me.

points3d(x, y, z...)
points3d(x, y, z, s, ...)
points3d(x, y, z, f, ...)

x, y and z are numpy arrays, or lists, all of the same shape, giving the positions of the points.
If only 3 arrays x, y, z are given, all the points are drawn with the same size and color.
In addition, you can pass a fourth array s of the same shape as x, y, and z giving an associated scalar value for each point, or a function f(x, y, z) returning the scalar value. This scalar value can be used to modulate the color and the size of the points.

So in this case scalar controls both the size and the color and it's not possible to disentangle them. I want a way to specify size as a (N,1) array and color as another (N,1) array individually..

Seems complicated?

Was it helpful?

Solution

Each VTK source has a dataset for both scalars and vectors.

The trick I use in my program to getting the color and size to differ is to bypass the mayavi source and directly in the VTK source, use scalars for color and vectors for size (it probably works the other way around as well).

nodes = points3d(x,y,z)
nodes.glyph.scale_mode = 'scale_by_vector'

#this sets the vectors to be a 3x5000 vector showing some random scalars
nodes.mlab_source.dataset.point_data.vectors = np.tile( np.random.random((5000,)), (3,1))

nodes.mlab_source.dataset.point_data.scalars = np.random.random((5000,))

You may need to transpose the 5000x3 vector data or otherwise shift the matrix dimensions somehow.

enter image description here

OTHER TIPS

I agree that the API that Mayavi provides here is unpleasant. The Mayavi documentation suggests the following hack (which I have paraphrased slightly) to independently adjust the size and color of points.

pts = mayavi.mlab.quiver3d(x, y, z, sx, sy, sz, scalars=c, mode="sphere", scale_factor=f)
pts.glyph.color_mode = "color_by_scalar"
pts.glyph.glyph_source.glyph_source.center = [0,0,0]

This will display x,y,z points as spheres, even though you're calling mayavi.mlab.quiver3d. Mayavi will use the norm of sx,sy,sz vectors to determine the size the points, and will use the scalar values in c to index into a color map. You can optionally supply a constant size scaling factor, which will be applied to all the points.

This is certainly not the most self-documenting code you'll ever write, but it works.

I also agree that API is ugly. I just did a simple and complete example with using @aestrivex's idea:

from mayavi.mlab import *
import numpy as np

K = 10
xx = np.arange(0, K, 1)
yy = np.arange(0, K, 1)

x, y = np.meshgrid(xx, yy)
x, y = x.flatten(), y.flatten()
z = np.zeros(K*K)

colors = 1.0 * (x + y)/(max(x)+max(y))

nodes = points3d(x, y, z, scale_factor=0.5)
nodes.glyph.scale_mode = 'scale_by_vector'

nodes.mlab_source.dataset.point_data.scalars = colors

show()

which produces:

enter image description here

If, as in my case, anyone is trying to for example update the scale of a single point, or the color of a point upon a clicking action or a keypress event, you may need to add the following line to make sure it updates the scalars even after the figure is already displayed (I add the complete example of my function that modifies the size of a point upon clicking on it as it might be helpful to some people) :

def picker(picker):
    if picker.actor in glyphs.actor.actors:
        point_id = picker.point_id//glyph_points.shape[0]
        # If the no points have been selected, we have '-1'
        if point_id != -1:
            glyphs.mlab_source.dataset.point_data.scalars[point_id] = 10
            # following line is necessary for the live update
            glyphs.mlab_source.dataset.modified()

# you would typically use this function the following way in your main :

figure = mlab.gcf()
mlab.clf()

# define your points
pts = ...

# define scalars or they will be defined to None by default
s = len(pts)*[1]
glyphs = mlab.points3d(pts[:,0], pts[:,1], pts[:,2], s, scale_factor=1, mode='cube')

glyph_points = glyphs.glyph.glyph_source.glyph_source.output.points.to_array()

picker = figure.on_mouse_pick(picker, button='Left')
picker.tolerance = 0.01

mlab.show()

Inspired from this example : https://docs.enthought.com/mayavi/mayavi/auto/example_select_red_balls.html

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top