Slice in volumetric data, via Plotly

A volume included in a parallelepiped is described by the values of a scalar field, $f(x,y,z)$, with $x\in[a,b]$, $y\in [c,d]$, $z\in[e,f]$. A slice in this volume is visualized by coloring the surface of the slice, according to the values of the function f, restricted to that surface.

In order to plot a planar or a nonlinear slice of equation z=s(x,y) one proceeds as follows:

  • define a meshgrid in x,y;
  • evaluate z=s(x,y)
  • define an instance of the Plotly Surface class, that represents the surface z=s(x,y)
  • this surface is colored according to the values, f(x,y,z), at its points. More precisely, the normalized values of the function f are mapped to a colormap/colorscale.

With obvious modications we get slices of equation $x=s(y,z), y=s(z,x)$.

In [ ]:
import numpy as np
import plotly.graph_objects as go
from IPython

Define a function that returns a slice as a Plotly Surface:

In [ ]:
def get_the_slice(x,y,z, surfacecolor):
    return go.Surface(x=x,
                      y=y,
                      z=z,
                      surfacecolor=surfacecolor,
                      coloraxis='coloraxis')
In [ ]:
def get_lims_colors(surfacecolor):# color limits for a slice
    return np.min(surfacecolor), np.max(surfacecolor)

Let us plot the slices z=0 and y=-0.5 in the volume defined by:

In [ ]:
scalar_f = lambda x,y,z: x*np.exp(-x**2-y**2-z**2)
In [ ]:
x = np.linspace(-2,2, 50)
y = np.linspace(-2,2, 50)
x, y = np.meshgrid(x,y)
z = np.zeros(x.shape)
surfcolor_z = scalar_f(x,y,z)
sminz, smaxz = get_lims_colors(surfcolor_z)

slice_z = get_the_slice(x, y, z, surfcolor_z)
In [ ]:
x = np.linspace(-2,2, 50)
z = np.linspace(-2,2, 50)
x, z = np.meshgrid(x,y)
y = -0.5 * np.ones(x.shape)
surfcolor_y = scalar_f(x,y,z)
sminy, smaxy = get_lims_colors(surfcolor_y)
vmin = min([sminz, sminy])
vmax = max([smaxz, smaxy])
slice_y = get_the_slice(x, y, z, surfcolor_y)

In order to be able to compare the two slices, we choose a unique interval of values to be mapped to the colorscale:

In [ ]:
def colorax(vmin, vmax):
    return dict(cmin=vmin,
                cmax=vmax)
In [ ]:
fig1 = go.Figure(data=[slice_z, slice_y])
fig1.update_layout(
         title_text='Slices in volumetric data', 
         title_x=0.5,
         width=700,
         height=700,
         scene_zaxis_range=[-2,2], 
         coloraxis=dict(colorscale='BrBG',
                        colorbar_thickness=25,
                        colorbar_len=0.75,
                        **colorax(vmin, vmax)))            
      
#fig1.show()  
In [1]:
from IPython.display import IFrame    
IFrame('https://chart-studio.plotly.com/~empet/13862', width=700, height=700)
Out[1]:

Oblique slice in volumetric data

As an example we plot comparatively two slices: a slice through $z=0$ and an oblique planar slice, that is defined by rotating the plane z=0 by $\alpha=\pi/4$, about Oy.

Rotating the plane $z=c$ about Oy (from Oz towards Ox) with $\alpha$ radians we get the plane of equation $z=c/\cos(\alpha)-x\tan(\alpha)$

In [ ]:
alpha = np.pi/4
x = np.linspace(-2, 2, 50)
y = np.linspace(-2, 2, 50)
x, y = np.meshgrid(x,y)
z = -x * np.tan(alpha)

surfcolor_obl = scalar_f(x,y,z)
In [ ]:
smino, smaxo = get_lims_colors(surfcolor_obl)
vmin = min([sminz, smino])
vmax = max([smaxz, smaxo])
In [ ]:
slice_obl = get_the_slice(x,y,z, surfcolor_obl)
In [ ]:
fig2 = go.Figure(data=[slice_z, slice_obl], layout=fig1.layout)
fig2.update_layout( coloraxis=colorax(vmin, vmax))

#fig2.show() 
In [2]:
IFrame('https://chart-studio.plotly.com/~empet/13864', width=700, height=700)
Out[2]:
In [ ]:
from IPython.core.display import HTML
def  css_styling():
    styles = open("./custom.css", "r").read()
    return HTML(styles)
css_styling()