Spherical "wedges" with Volume plot?

I really like the shaded 3d plot offered by Volume plot. Can it be used to show a “spherical wedge”? I’d like to specify radius, theta, and phi constraints (spherical coordinates). Here’s some related code:

import numpy as np
import plotly.graph_objects as go

# this code is based on an example at https://plotly.com/python/3d-volume-plots/ 
# it produces a partial sphere in one "octant" from rectangular coordinates
X, Y, Z = np.mgrid[:1:10j, :1:10j, :1:10j]
vol = X**2 + Y**2 + Z**2

fig = go.Figure(data=go.Volume(
    x=X.flatten(), y=Y.flatten(), z=Z.flatten(),
    value=vol.flatten(),
    isomin=0.2, 
    isomax=0.7,
    opacity=0.2,
    ))

# this is a (non-working) attempt at what I'd really like: a partial sphere specified by spherical coordinates,
# in order to show smaller or larger "wedges" than the above code
# radius, theta, phi = np.mgrid[:1:10j, np.pi*(1/8):np.pi*(3/8):10j, np.pi*(1/8):np.pi*(3/8):10j]

# X = radius * np.sin(phi) * np.cos(theta)
# Y = radius * np.sin(phi) * np.sin(theta)
# Z = radius * np.cos(phi)

# show = np.sqrt(X**2+Y**2+Z**2) >= radius.min() & \
#         np.sqrt(X**2+Y**2+Z**2) <= radius.max() & \
#         np.arctan2(Y,X) <= theta.max() & \
#         np.arctan2(Y,X) >= theta.min() & \
#         np.arccos(Z/np.sqrt(X**2+Y**2+Z**2)) <= phi.max() & \
#         np.arccos(Z/np.sqrt(X**2+Y**2+Z**2)) >= phi.min()

# fig = go.Figure(data=go.Volume(
#     x=X.flatten(), y=Y.flatten(), z=Z.flatten(),
#     value=show.flatten(),
#     isomin=.9,
#     isomax=1.1,
#     opacity=0.2,
#     ))

fig.show()

Hi @danc welcome to the forums.

You want to display a sphere with a variable phi? Is that the question?

variable phi, theta, and radius please
here’s a picture that may help:
image

Thank you AIMPED, I’ll take another look at Mesh3d. The volume plot examples seemed so close to what I wanted so I gravitated there

HI @danc try this. The solution might not work for every combination of theta, phi and radius ranges, I did not test it fully. Also you might increase the number of points for the np.linspace of theta and phi for a better resolution but be aware of possible browser freeze if increasing too drastically. Also, try investigating how to calculate the triangulation better instead of using alphahull=1, for example here

import plotly.graph_objs as go
import numpy as np

# Define the parameters for the partial sphere
radius_min = 0.8  
radius_max = 1.0  
theta_min = np.pi / 4  
theta_max = np.pi / 2/4  
phi_min = np.pi / 4  
phi_max = np.pi / 2/4  

# Create a grid of points in spherical coordinates within the specified ranges
radius = np.linspace(radius_min, radius_max, 2)
theta = np.linspace(theta_min, theta_max, 5)
phi = np.linspace(phi_min, phi_max, 5)
radius, theta, phi = np.meshgrid(radius, theta, phi)

# Convert spherical coordinates to Cartesian coordinates
x = radius * np.sin(theta) * np.cos(phi)
y = radius * np.sin(theta) * np.sin(phi)
z = radius * np.cos(theta)

# Create a 3D Mesh plot
mesh = go.Mesh3d(
    x=x.flatten(),
    y=y.flatten(),
    z=z.flatten(),
    alphahull=1,
    flatshading=True
)

# Create the layout for the 3D plot
layout = go.Layout(
    scene=dict(
        xaxis_title='X',
        yaxis_title='Y',
        zaxis_title='Z',
        aspectmode='data',
    ),
    height=800,
    width=800,
    xaxis={'scaleanchor': 'y'},
)

# Create a figure and add the mesh plot and layout
fig = go.Figure(data=[mesh], layout=layout)

# Show the 3D plot
fig.show()


mrep mesh3d

That’s very helpful, thank you. You’re right that there are some curiosities at the boundaries with certain combinations, but even an approximate visualization is helpful in my case. I’ll explore that a bit more sometime and tinker with alphahull settings too.

Side note: the plot does not appear for me on a different machine that’s running an older version of plotly (v5.6.0). Hopefully upgrading will fix it.

The wedge defined as volume is bounded by rough surfaces, and the plot as mesh3d, with alphahull=1 looks much bettter. However I post here the code for volume to illustrate how is expressed the complement of the wedge within the unit cube, i.e. the region to be invalidated (its points are set on np.nan):

import plotly.graph_objects as go
import numpy as np
from numpy import pi, arctan2, sqrt
F=lambda x, y, z: sqrt(x**2+y**2+z**2)

rmin=0.6
rmax=0.9
phi_min=pi/12
phi_max=pi/3
theta_min=pi/12
theta_max=pi/4
def invalidate(x, y, z):
    c1 = np.logical_or(phi_max <arctan2(y,x),   arctan2(y,x) < phi_min)  
    c2 =np.logical_or(theta_max < arctan2(sqrt(x**2+y**2), z), arctan2(sqrt(x**2+y**2), z)< theta_min)  
    c3 =np.logical_or(F(x,y,z) <rmin, rmax < F(x,y,z))
    return np.logical_or(np.logical_or(c1, c2), c3)


x, y, z= np.mgrid[0:1:90j, 0:1:90j, 0:1:90j]
Fvals = F(x, y, z);
Fvals[np.where(invalidate(x, y, z))]=np.nan   #rmin-0.5
pl_color = [[0, 'rgb(225, 83, 131)'],
            [1.0, 'rgb(225, 83, 131)']]

fig=go.Figure(go.Volume(
               x=x.flatten(),
               y=y.flatten(),
               z=z.flatten(),
               value=Fvals.flatten(),
               surface=dict(show=True, fill= 1, pattern="A+B+C+D", count=2),
               colorscale=pl_color, 
               showscale=False,       
               isomin=rmin,
               isomax=rmax             
               )
              )
fig.update_layout(width=800, height=800, scene_camera_eye=dict(x=1.5, y=-1.5, z=1.25),
                 font_size=11)

The wedge is included in the unit cube, and is supposed to be parameterized by:

x= r*cos(phi)*sin(theta)
y= r*sin(phi)*sin(theta)
z=r*cos(theta)

with 0.6 <=r<=0.9, pi/12<=phi<=pi/3, pi/12<=theta<=pi/4

That’s also a good choice @empet, thank you. I can see where invalidation might be useful for a variety of situations. I also like the use of functions and np.where for indexing - great tools.

1 Like

Thanks for sharing this approach @empet, I always appreciate your answers, specially when it gets to these kind of questions.

What happened to this explosion of thanks for the go.Volume post? It’s a reaction like for my first good response. I’ve been around for about 10 years. I’m no longer active here because I migrated from Python to Julia, and now use PlotlyJS.jl. But I do visit the Plotly forum from time to time and answer questions when I know the topic in question is not so common, and has been little discussed in the community.
Thank you all! :slight_smile:

1 Like

Yeah, I see your responses all over the forum. If you haven’t been getting much thanks along the way that’s sad - free plotly tool and free support from you, what’s not to love? ok, maybe don’t ask that to Mathworks :wink: