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.plotly as py
import plotly.graph_objs as go
In [2]:
py.sign_in('empet', 'my_api_key')

Here we are working with the following colorscale:

In [3]:
pl_BrBG = [[0.0, 'rgb(84, 48, 5)'],
          [0.1, 'rgb(138, 80, 9)'],
          [0.2, 'rgb(191, 129, 45)'],
          [0.3, 'rgb(222, 192, 123)'],
          [0.4, 'rgb(246, 232, 195)'],
          [0.5, 'rgb(244, 244, 244)'],
          [0.6, 'rgb(199, 234, 229)'],
          [0.7, 'rgb(126, 203, 192)'],
          [0.8, 'rgb(53, 151, 143)'],
          [0.9, 'rgb(0, 101, 93)'],
          [1.0, 'rgb(0, 60, 48)']]

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

In [4]:
def get_the_slice(x,y,z, surfacecolor,  colorscale=pl_BrBG, showscale=False):
    return go.Surface(x=x,  # https://plot.ly/python/reference/#surface
                      y=y,
                      z=z,
                      surfacecolor=surfacecolor,
                      colorscale=colorscale,
                      showscale=showscale,
                      colorbar=dict(thickness=20, ticklen=4))
In [5]:
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 [6]:
volume = lambda x,y,z: x*np.exp(-x**2-y**2-z**2)
In [7]:
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 = volume(x,y,z)
sminz, smaxz = get_lims_colors(surfcolor_z)

slice_z = get_the_slice(x,y,z, surfcolor_z)
In [8]:
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 = volume(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 [9]:
slice_z.update(cmin=vmin, cmax=vmax)
slice_y.update(cmin=vmin, cmax=vmax, showscale=True)
In [ ]:
axis = dict(showbackground=True, 
            backgroundcolor="rgb(230, 230,230)",
            gridcolor="rgb(255, 255, 255)",      
            zerolinecolor="rgb(255, 255, 255)"  
            )


layout = go.Layout(
         title='Slices in volumetric data', 
         width=700,
         height=700,
         scene=dict(xaxis=axis,
                    yaxis=axis, 
                    zaxis=dict(axis, range=[-2,2]),
                    aspectratio=dict(x=1, y=1, z=1)
                    )
        )
In [11]:
fig = go.Figure(data=[slice_z, slice_y], layout=layout)
In [12]:
py.iplot(fig, filename='Slice-volumetric-1')
Out[12]:

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/5$, 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 [13]:
alpha = np.pi/5
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 = volume(x,y,z)
In [14]:
smino, smaxo = get_lims_colors(surfcolor_obl)
vmin=min([sminz, smino])
vmax=max([smaxz, smaxo])
In [15]:
slice_obl = get_the_slice(x,y,z, surfcolor_obl)
slice_obl.update(cmin=vmin, cmax=vmax, showscale=True)
In [16]:
slice_z.update( cmin=vmin,
                cmax=vmax)
In [17]:
fig = go.Figure(data=[slice_z, slice_obl], layout=layout)
py.iplot(fig, filename='Slice-volumetric-2')
Out[17]:

Nonlinear slices in volumetric data

In the same volume as above we define two parabolic slices and a planar one:

In [18]:
x = np.linspace(-2,2, 50)
y = np.linspace(-2,2, 50)
x, y = np.meshgrid(x,y)
z = -1.0*np.ones(x.shape)
surfcolor_zm1 = volume(x, y, z)
slice_zm1 = get_the_slice(x, y, z, surfcolor_zm1)
smin1, smax1 = get_lims_colors(surfcolor_zm1)
In [19]:
z = -0.98 + x**2 + y**2
surfcolor_parab1 = volume(x, y, z)
sminp1, smaxp1 = get_lims_colors(surfcolor_parab1)
slice_parab1 = get_the_slice(x, y, z, surfcolor_parab1)

z = 0.75 + x**2 + y**2
surfcolor_parab2 = volume(x, y, z)
sminp2, smaxp2 = get_lims_colors(surfcolor_parab2)
slice_parab2 = get_the_slice(x, y, z, surfcolor_parab2)

vmin=min([smin1, sminp1, sminp2])
vmax=max([smax1, smaxp1, smaxp2])
slice_zm1.update(cmin=vmin, cmax=vmax)
slice_parab1.update(cmin=vmin, cmax=vmax)
slice_parab2.update(cmin=vmin, cmax=vmax, showscale=True)
fig = go.Figure(data=[slice_zm1, slice_parab1, slice_parab2], layout=layout)
py.iplot(fig, filename='Nonlinear-slice')
Out[19]:
In [1]:
from IPython.core.display import HTML
def  css_styling():
    styles = open("./custom.css", "r").read()
    return HTML(styles)
css_styling()
Out[1]: