How to do 3D Plot surface with isolinies

Hi,
I have a 3D domain with data and trying to visualize slices of data in 3D space.
I can show the data by colours, but not by contours, What Im doing wrong?

import plotly.graph_objects as go
import numpy as np

d = [[-10,10,32], [-15,15,48], [-10,10,32]]
x_values = np.linspace(d[0][0], d[0][1], d[0][2])
y_values = np.linspace(d[1][0], d[1][1], d[1][2])
z_values = np.linspace(d[2][0], d[2][1], d[2][2])
X, Y, Z = np.meshgrid(x_values, y_values, z_values)
BB = np.sin(np.sqrt(X**2 + Y**2 + Z**2))

idx = 10
z_plane = BB[:, :, idx]
sp_z = go.Surface(
            contours = {
                        "z": {"show": True, "start": BB.min(), "end": BB.max(), "size": 0.05, "color":"white"}
                       },
            x=X[:, :, idx], y=Y[:, :, idx], z=Z[:, :, idx],
            surfacecolor=z_plane,
            colorscale='Hot', showscale=False, opacity=0.7)
fig = go.Figure(data=[sp_z])
fig.show()

Any advice would be great!

Hi @luxs ,

is srf variable is suppose to be sp_z ?

I haven’t explore much about 3D plot, but your contours don’t show up maybe affected by z values that have same values.

1 Like

Hi

is srf variable is suppose to be sp_z ?
Yes, you are right, this is just a typo. I’ve fixed it in the original post, but this does not change anything. The isolines are not plotted.

It looks like I have some problems with contours syntaxis, but can;t work out which one.

Hi @luxs, welcome to the forums.

It seems that you are interested in the contours only. If this is the case, you can use go.Contour().

An example:

import plotly.graph_objects as go
import numpy as np

d = [[-10,10,32], [-15,15,48], [-10,10,32]]
x_values = np.linspace(d[0][0], d[0][1], d[0][2])
y_values = np.linspace(d[1][0], d[1][1], d[1][2])
z_values = np.linspace(d[2][0], d[2][1], d[2][2])
X, Y, Z = np.meshgrid(x_values, y_values, z_values)
BB = np.sin(np.sqrt(X**2 + Y**2 + Z**2))

idx = 10
z_plane = BB[:, :, idx]
sp_z = go.Contour(
    z=BB[:, :, idx],
    colorscale='Hot', 
    showscale=False, 
    opacity=0.7
)
fig = go.Figure(data=[sp_z])
fig.show()

You are actually right. In order to make the contours work in a surface plot, the surface can’t be a plane parallel to the xy-plane:

import plotly.graph_objects as go
import numpy as np

x= np.linspace(0, 50, 2)
y= np.linspace(0, 50, 2)
z= 10 * np.array([[0,0],[0,0]])

data = go.Surface(
        x=x,
        y=y,
        z=z,
        opacity=0.3,
        showscale=False,
    )

fig.update_traces(
    contours_z=dict(
        show=True, 
        usecolormap=True,                        
        project_z=True
    )
)

fig = go.Figure(data)

won’t show contours.

Substituting

z= 10 * np.array([[0,0],[0,0]])

with

z= 10 * np.array([[0,0],[1,1]])

will show contours.
mrep contours

1 Like

Hi,
This is a lovely picture and I would like to make the same, but this should be done on the plane within 3D coordinates because this contour plane is a part of a more complicated 3D image.

If you are interested in just the function BB defined in the original post, then this code defines a slice and the lines, in each slice:

import numpy as np
import plotly.graph_objs as go

def get_the_slice(x,y,z, surfacecolor,  colorscale="Hot", showscale=False):
    return go.Surface(x=x,# https://plot.ly/python/reference/#surface
                   y=y,
                   z=z,
                   surfacecolor=surfacecolor,
                   colorscale=colorscale,
                   showscale=showscale)

def get_lims_colors(surfacecolor):# color limits for a slice
    return np.min(surfacecolor), np.max(surfacecolor)


def circle(r):
    t=np.linspace(0, 2*np.pi, 100)
    return r*np.cos(t), r*np.sin(t)


BB=lambda x,y,z:    x**2+y**2+z**2     #x*np.exp(-x**2-y**2-z**2)
h=0.45
x=np.linspace(-15,15, 200)
y=np.linspace(-15,15, 200)
x,y=np.meshgrid(x,y)
z=h*np.ones(x.shape) #Slice at the height z=0.45
surfcolor_z=BB(x,y,z)
sminz, smaxz=get_lims_colors(surfcolor_z)

slice_z=get_the_slice(x,y,z, surfcolor_z)
slice_z.update(cmin=sminz, cmax=smaxz, opacity=0.65,  showscale=True)
fig=go.Figure(slice_z)

fig.update_layout(title_text='Slices in volumetric data', title_x=0.5,
         width=600,
         height=600,
                  showlegend=False,
         scene_zaxis_range=[-15,15]);

for rad in np.linspace(0, 13, 13):
    x, y= circle(rad)
    fig.add_trace(go.Scatter3d(x=x, y=y, z=(h+0.1)*np.ones(x.shape), mode="lines", line_color="white", line_width=2.5 ))

fig.show()

LE: The slice definition was inspired by that used in this old notebook posted on chart-studio

Hi empet,
The picture looks great!
But it looks like you do not plot real isolines or contours according to BB values in the 3D domain, but the rater just draws the circles. If it is the only option available, it is ok, it is not a problem for me to calculate traces for isolines.
I’ve seen this notebook and I do pretty similar things - this is a visualization of 3D magnetic field.

Just to understand, you want to add the contour (lines) as trace in the existing 3d figure at a discrete z coordinate, right?

Hi AIMPED,
I would like to make a contour plot, preferably independent from colours, on the 3D plane.
Why independent? Because for making a nice picture, it is good to use a range of colours, but for clarity, it is enough to have only a few isolines, so the step for isolines should be created independently
So basically, I would like to draw isolines on top of my coloured plane (or very close to it)

Is that a yes?

Yes! You do understand correctly!

By definition z- contourlines of a surface are the lines of intersection of the surface with the planes of eq z= const But in your case the surface itself is z= const. Could you write down mathematically what do you want to plot? The circles I plotted are not meaningless because the function BB takes constant values across the circles centered at the center of the plane z= 0.45 , and any other z= cst.

Hi empet
If you have a look at my original post, you can see that I have 2 lines of code.

z_plane = BB[:, :, idx]
surfacecolor=z_plane

Now I want to draw isolines according to the values in z_plane - which is a slice of my function values in the domain

This is exactly I have plotted. The locus of points where BB takes constant values are spheres These spheres intersected to planes z= cst are circles.

Yes, it is related to this case, but you’ve plotted it because you know the answer. Which means that I need to manually calculate these isolines.
So this is a really lovely idea of how to technically draw the lines, but It is not use Plotly ability to calculate them

With this small change, I will need to rewrite the code of line calculating.

BB=lambda x,y,z:    (x+10)**2+y**2+z**2     #x*np.exp(-x**2-y**2-z**2)

But in the real task - these BB data are unknown - they are from somewhere else.

I think this is the thing. Plotly does this calculation in JS and it’s not accesible from the python side. Either you search for the corresponding functions in the plotly source code on github or you might get some inspiration from this post:

I stressed when I posted the first time, above, that “If you are interested in just the function BB, defined in the original post, then this code defines a slice and the lines, in each slice”. I didn’t say that this method is generally applicable to any case.

I think I will try to convert my 3D case into the 2D plane - calculate isolines plotly and then try to convert them from 2D to 3D by adding one coordinate…