#!/usr/bin/env python # coding: utf-8 # ## 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) # ### 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) # In[ ]: from IPython.core.display import HTML def css_styling(): styles = open("./custom.css", "r").read() return HTML(styles) css_styling()