How to join/connect/group multiple objects in Mayavi2
Asked Answered
A

2

4

I would like to combine multiple Mayavi objects into a single "grouped" object so that I control all of their properties together. For example I created the following bi-convex lens shape by combining 3 built-in surfaces (two spheres and one cylinder). Now I would like to assign uniform properties (specularity, ambient color, etc) to all of the constituent surfaces at a time (not individually). Also, I would like to translate/rotate the lens as a whole. I am not sure how to accomplish this.

Here is the bi-convex lens created in Mayavi (code given below):

enter image description here

As it can be seen in the following figure, the above lens is composed of three surfaces:

enter image description here


Here is the code for building the bi-convex lens:


import numpy as np
from mayavi import mlab
from mayavi.sources.builtin_surface import BuiltinSurface
from mayavi.modules.surface import Surface
from mayavi.filters.transform_data import TransformData


def lensUsingMayaviBuiltinSphere(radius=0.5, semiDiam=0.25, thickness=0.9):
    """
    Render a bi-convex lens
    """
    engine = mlab.get_engine()
    sag = radius - np.sqrt(radius**2 - semiDiam**2)
    cyl_height = thickness - 2.0*sag   # thickness of the cylinder in between
    # Create Mayavi data sources -- sphere_h1_src, sphere_h2_src, cylinder_src    
    # half 1: source = sphere_h1_src
    sphere_h1_src = BuiltinSurface()
    engine.add_source(sphere_h1_src)
    sphere_h1_src.source = 'sphere'
    sphere_h1_src.data_source.radius = radius
    sphere_h1_src.data_source.center = np.array([ 0.,  0.,  -np.sqrt(radius**2 - semiDiam**2) + cyl_height/2.0])
    sphere_h1_src.data_source.end_phi = np.rad2deg(np.arcsin(semiDiam/radius)) #60.0
    sphere_h1_src.data_source.end_theta = 360.0
    sphere_h1_src.data_source.phi_resolution = 300
    sphere_h1_src.data_source.theta_resolution = 300
    # half 2: source = sphere_h2_src
    sphere_h2_src = BuiltinSurface()
    engine.add_source(sphere_h2_src)
    sphere_h2_src.source = 'sphere'
    sphere_h2_src.data_source.radius = radius
    sphere_h2_src.data_source.center = np.array([ 0.,  0.,  np.sqrt(radius**2 - semiDiam**2) - cyl_height/2.0])
    sphere_h2_src.data_source.start_phi = 180.0 - np.rad2deg(np.arcsin(semiDiam/radius))
    sphere_h2_src.data_source.end_phi = 180.0
    sphere_h2_src.data_source.end_theta = 360.0
    sphere_h2_src.data_source.phi_resolution = 300
    sphere_h2_src.data_source.theta_resolution = 300
    # cylinder source data in between 
    cylinder_src = BuiltinSurface()
    engine.add_source(cylinder_src)
    cylinder_src.source = 'cylinder'
    cylinder_src.data_source.center = np.array([ 0.,  0.,  0.])
    if cyl_height > 0:
        cylinder_src.data_source.height = cyl_height
    else:
        cylinder_src.data_source.height = 0.0
    cylinder_src.data_source.radius = semiDiam
    cylinder_src.data_source.capping = False
    cylinder_src.data_source.resolution = 50

    # Add transformation filter to align cylinder length along z-axis
    transform_data_filter = TransformData()
    engine.add_filter(transform_data_filter, cylinder_src)
    Rt_c = [ 1.0000,  0.0000,  0.0000,  0.00,
             0.0000,  0.0000, -1.0000,  0.00,
             0.0000,  1.0000,  0.0000,  0.00, 
             0.0000,  0.0000,  0.0000,  1.00]
    transform_data_filter.transform.matrix.__setstate__({'elements': Rt_c})
    transform_data_filter.widget.set_transform(transform_data_filter.transform)
    transform_data_filter.filter.update()
    transform_data_filter.widget.enabled = False   # disable the rotation control further.

    # Add surface modules to each source
    right_surface = Surface()
    engine.add_filter(right_surface, sphere_h1_src)
    left_surface = Surface()
    engine.add_filter(left_surface, sphere_h2_src)
    cyl_surface = Surface()
    engine.add_filter(cyl_surface, transform_data_filter)


fig = mlab.figure()
# Add lens
lensUsingMayaviBuiltinSphere(radius=2, semiDiam=1.2)
mlab.show()
Arcturus answered 7/3, 2014 at 4:32 Comment(0)
E
3

I don't know of a way to combine sources in the way you are looking for. I think in fact that is probably impossible since under the hood the BuiltinSurface object has specific vtk sources that are not what you want. It should however be possible to simply use a different source that gives what you want. In this case you could generate a biconvex lens with mlab.mesh:

a,c,h=3,1,.2

phi,theta = np.mgrid[0:2*np.pi:np.pi/250, 0:2*np.pi:np.pi/250]
x=a*np.cos(theta)*np.sin(phi)
y=a*np.sin(theta)*np.sin(phi)
z=c*np.cos(phi)+(h*(-1)**(np.cos(phi)<0))

mlab.mesh(x,y,z,color=(1,1,1)
mlab.show()

enter image description here

One minor difference is that this surface is smooth. This is the nature of sampling a single surface --i.e., this result is a direct consequence of what your question asks to do. If this is an important feature of your figure, I would suggest an entirely different approach: wrap the 3 sources in a class and have the event handler update the relevant attributes on all three.

Eckart answered 7/3, 2014 at 20:54 Comment(2)
Thank you very much for your reply. I will surely try the second methods that you have suggested sometime later. For now, I am trying to make do with the solution 1. However, I think the line z=c*np.cos(phi)*(h*(-1)**(np.cos(phi)<0)) is probably missing a piece. Could you please double check? Also, your example inspired me add a few things to get the "edges" that I wanted. I am putting that code and figure into a separate answer (as the comment section is really small)Arcturus
Yes it should have been z=c*np.cos(phi)+(h*(-1)**(np.cos(phi)<0)), + not *. Sorry.Eckart
A
1

Based on the code by @aestrivex, here is one way of getting the desired output (lens with sharp edges). Note that this is not a solution for connecting multiple Mayavi objects.

import numpy as np
from mayavi import mlab

# Control parameters
# r is the semi-diameter of the lens 
# c controls the center thickness of the lens
# h controls the curvature of the surfaces (lesser the value more the curvature)

r, c, h = 3, .75, .9 

delta_phi = np.pi/250.0             # phi == azimuth  (0 <= phi <= pi)
delta_theta = np.pi/100.0           # theta == zenith (0 <= theta <= pi)
phi, theta = np.mgrid[0:2.0*np.pi + delta_phi:delta_phi,0:np.pi + delta_theta:delta_theta]
# The Exact threshold values for masking tz, txy will change depending upon the
# sampling of theta. txy is always slightly less than tz. tz should be around 0.3
tz, txy = 0.279, 0.275
x = r*np.sin(theta)*np.cos(phi)*(np.abs(np.cos(theta)) > txy)
y = r*np.sin(theta)*np.sin(phi)*(np.abs(np.cos(theta)) > txy)
z = c*np.cos(theta)*(h**(-1)*( np.abs(np.cos(theta)) > tz))

mlab.mesh(x,y,z,color=(1,1,1))

mlab.show()

And here is the output: enter image description here

Arcturus answered 8/3, 2014 at 4:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.