plotly.graph_objects.Volume cube

Hello,
I’m trying to plot a 3d cube inside a 3d cube using plotly.graph_objects.Volume such that:

  • The cube’s shape is (3,3,3) and it contains 27 values. It should be parallel to the axes.
  • At (1,1,1) there’s a different value, that should be displayed as an inner cube at the center of the bigger cube.

My code is similar to the code at plotly’s website 3d volume plots in Python
Here it is:

    import plotly.graph_objects as go
    import numpy as np
    X, Y, Z = np.mgrid[0:2:3j, 0:2:3j, 0:2:3j]
    values = np.ones(X.shape)*0.1
    values[1,1,1] = 1

    fig = go.Figure(data=go.Volume(
        x=X.flatten(),
        y=Y.flatten(),
        z=Z.flatten(),
        value=values.flatten(),
        isomin=0.1,
        isomax=0.8,
        opacity=0.1,  # needs to be small to see through all surfaces
        surface_count=17,  # needs to be a large number for good volume rendering
    ))
    fig.show()

What I get is

And what I’m looking for is something like this:

As you can see, both the shape and the colors are problematic in my first picture. The shape is especially bad. I obtained the second picture using Mesh3d, but this solution doesn’t work well because in my real use I have to define each voxel individually and it results in “walls” like these:

Thanks in advance for your help,
Raz

Hi @Raz,
From the go.Volume definition you are trying to plot 17 surfaces, each one being an isosurface, i.e. a level set defined by a value v₀, which is an element in np.linspace(isomin, isomax, 17), i.e. all points in your mgrid that have the corresponding value equal to v₀. But your cube discretization contains only 9 voxels, and go.Volume cannot succeed to get the expected level sets.
Maybe this approach https://nbviewer.org/github/empet/Voxel-data-visualization/blob/master/Voxel-visualization-with-unique-vertices.ipynb to plot voxelized data could help.
More precisely assign the value 1 to the voxels in your 3d array, that are to be displayed.

Hi, @empet ,
I’ve already managed to plot voxels using Mesh3d, as can be seen in pictures 2 and 3. However I need the voxels to have opacity in order to see what’s inside, and this results in the side effect shown in picture 3. That’s why I thought the go.Volume method could work well here. Why does my cube discretization contain only 9 voxels? It should have 27 voxels, 3x3x3 (Sorry if I miss something, I’m new to this). A sample code of a 3x3x3 cube parallel to the axes using go.Volume would be much appreciated.

Thanks, Raz

Hey, are you referring to the inside of the inner cube on your second image?

Hi @Raz maybe this is caused to some settings of the lighting, see also:
https://plotly.github.io/plotly.py-docs/generated/plotly.graph_objects.mesh3d.html#plotly.graph_objects.mesh3d.Lighting

You could try playing around with the arguments. roughness for example changes the effect.

I tried with this example:

def cubes(size, pos_x, pos_y, pos_z, color):
    # create points
    x, y, z = np.meshgrid(
        np.linspace(pos_x-size/2, pos_x+size/2, 2), 
        np.linspace(pos_y-size/2, pos_y+size/2, 2), 
        np.linspace(pos_z-size/2, pos_z+size/2, 2),
    )
    x = x.flatten()
    y = y.flatten()
    z = z.flatten()
    
    return go.Mesh3d(x=x, y=y, z=z, alphahull=1, flatshading=True, color=color, lighting={'diffuse': 0.1, 'specular': 2.0, 'roughness': 0.5})

fig = go.Figure()
# set edge length of cubes
size = 5

# add outer cube
fig.add_trace(cubes(20,0,0,0, 'rgba(255,100,0,0.1)'))

# add inner center cube
fig.add_trace(cubes(size,0,0,0, 'rgba(100,0,100,0.1)'))

# add inner cubes
fig.add_trace(cubes(size,size,0,0, 'rgba(100,0,100,0.1)'))
fig.add_trace(cubes(size,-size,0,0, 'rgba(100,0,100,0.1)'))

fig.add_trace(cubes(size,0,size,0, 'rgba(100,0,100,0.1)'))
fig.add_trace(cubes(size,0,-size,0, 'rgba(100,0,100,0.1)'))

fig.add_trace(cubes(size,0,0,size, 'rgba(100,0,100,0.1)'))
fig.add_trace(cubes(size,0,0,-size, 'rgba(100,0,100,0.1)'))

fig.show()

@Raz Yes, there are 27 voxels, but I omitted the last factor.
I tried your code with only two surfaces instead of 17, but the inner surface isn’t a cube. Plotly.js cannot carve a cube as an isosurface of value 0.8, because of a small number of voxels.
Lighting cannot transform the resulted surface into a cube.