Skip to content

Commit fd237a5

Browse files
committed
3d volume notebook
1 parent 9efb356 commit fd237a5

File tree

1 file changed

+259
-0
lines changed

1 file changed

+259
-0
lines changed

python/3d-volume.md

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
---
2+
jupyter:
3+
jupytext:
4+
notebook_metadata_filter: all
5+
text_representation:
6+
extension: .md
7+
format_name: markdown
8+
format_version: '1.1'
9+
jupytext_version: 1.1.1
10+
kernelspec:
11+
display_name: Python 3
12+
language: python
13+
name: python3
14+
language_info:
15+
codemirror_mode:
16+
name: ipython
17+
version: 3
18+
file_extension: .py
19+
mimetype: text/x-python
20+
name: python
21+
nbconvert_exporter: python
22+
pygments_lexer: ipython3
23+
version: 3.7.3
24+
plotly:
25+
description: How to make 3D Volume Plots in Python with Plotly.
26+
display_as: 3d_charts
27+
has_thumbnail: true
28+
language: python
29+
layout: user-guide
30+
name: 3D Volume Plots
31+
order: 12.1.1
32+
page_type: u-guide
33+
permalink: python/3d-volume-plots/
34+
thumbnail: thumbnail/3d-volume-plots.jpg
35+
title: Python 3D Volume Plots | plotly
36+
---
37+
38+
A volume plot with `go.Volume` shows several partially transparent isosurfaces for volume rendering. The API of `go.Volume` is close to the one of `go.Isosurface`. However, whereas [isosurface plots](./3d-isosurface-plots/) show all surfaces with the same opacity, tweaking the `opacityscale` parameter of `go.Volume` results in a depth effect and better volume rendering.
39+
40+
## Simple volume plot with go.Volume
41+
42+
In the three examples below, note that the default colormap is different whether isomin and isomax have the same sign or not.
43+
44+
```python
45+
import plotly.graph_objects as go
46+
import numpy as np
47+
X, Y, Z = np.mgrid[-8:8:50j, -8:8:50j, -8:8:50j]
48+
values = np.sin(X*Y*Z) / (X*Y*Z)
49+
50+
fig = go.Figure(data=go.Volume(
51+
x=X.flatten(),
52+
y=Y.flatten(),
53+
z=Z.flatten(),
54+
value=values.flatten(),
55+
isomin=0.1,
56+
isomax=0.8,
57+
opacity=0.1, # needs to be small to see through all surfaces
58+
surface_count=17, # needs to be a large number for good volume rendering
59+
))
60+
fig.show()
61+
```
62+
63+
```python
64+
import plotly.graph_objects as go
65+
import numpy as np
66+
X, Y, Z = np.mgrid[-1:1:50j, -1:1:50j, -1:1:50j]
67+
values = np.sin(np.pi*X) * np.cos(np.pi*Z) * np.sin(np.pi*Y)
68+
69+
fig = go.Figure(data=go.Volume(
70+
x=X.flatten(),
71+
y=Y.flatten(),
72+
z=Z.flatten(),
73+
value=values.flatten(),
74+
isomin=-0.1,
75+
isomax=0.8,
76+
opacity=0.1, # needs to be small to see through all surfaces
77+
surface_count=21, # needs to be a large number for good volume rendering
78+
))
79+
fig.show()
80+
```
81+
82+
```python
83+
import numpy as np
84+
import plotly.graph_objects as go
85+
86+
# Generate nicely looking random 3D-field
87+
np.random.seed(0)
88+
l = 50
89+
X, Y, Z = np.mgrid[:l, :l, :l]
90+
vol = np.zeros((l, l, l))
91+
pts = (l * np.random.rand(3, 15)).astype(np.int)
92+
vol[tuple(indices for indices in pts)] = 1
93+
from scipy import ndimage
94+
vol = ndimage.gaussian_filter(vol, 7)
95+
vol /= vol.max()
96+
97+
fig = go.Figure(data=go.Volume(
98+
x=X.flatten(), y=Y.flatten(), z=Z.flatten(),
99+
value=vol.flatten(),
100+
isomin=0.2,
101+
isomax=0.7,
102+
opacity=0.1,
103+
surface_count=21,
104+
))
105+
fig.show()
106+
```
107+
108+
### Defining the opacity scale of volume plots
109+
110+
In order to see through the volume, the different isosurfaces need to be partially transparent. This transparency is controlled by a global parameter, `opacity`, as well as an opacity scale mapping scalar values to opacity levels. The figure below shows that changing the opacity scale changes a lot the visualization, so that `opacityscale` should be chosen carefully (`uniform` corresponds to a uniform opacity, `min`/`max` maps the minimum/maximum value to a maximal opacity, and `extremes` maps both the minimum and maximum values to maximal opacity, with a dip in between).
111+
112+
```python
113+
import plotly.graph_objects as go
114+
from plotly.subplots import make_subplots
115+
fig = make_subplots(
116+
rows=2, cols=2,
117+
specs=[[{'type': 'volume'}, {'type': 'volume'}],
118+
[{'type': 'volume'}, {'type': 'volume'}]])
119+
120+
import numpy as np
121+
122+
X, Y, Z = np.mgrid[-8:8:50j, -8:8:50j, -8:8:50j]
123+
values = np.sin(X*Y*Z) / (X*Y*Z)
124+
125+
126+
fig.add_trace(go.Volume(
127+
opacityscale="uniform",
128+
), row=1, col=1)
129+
fig.add_trace(go.Volume(
130+
opacityscale="extremes",
131+
), row=1, col=2)
132+
fig.add_trace(go.Volume(
133+
opacityscale="min",
134+
), row=2, col=1)
135+
fig.add_trace(go.Volume(
136+
opacityscale="max",
137+
), row=2, col=2)
138+
fig.update_traces(x=X.flatten(), y=Y.flatten(), z=Z.flatten(), value=values.flatten(),
139+
isomin=0.15, isomax=0.9, opacity=0.1, surface_count=15)
140+
fig.show()
141+
```
142+
143+
### Defining a custom opacity scale
144+
145+
It is also possible to define a custom opacity scale, mapping scalar values to relative opacity values (between 0 and 1, the maximum opacity is given by the opacity keyword). This is useful to make a range of values completely transparent, as in the example below between -0.2 and 0.2.
146+
147+
```python
148+
import plotly.graph_objects as go
149+
import numpy as np
150+
X, Y, Z = np.mgrid[-1:1:50j, -1:1:50j, -1:1:50j]
151+
values = np.sin(np.pi*X) * np.cos(np.pi*Z) * np.sin(np.pi*Y)
152+
153+
fig = go.Figure(data=go.Volume(
154+
x=X.flatten(),
155+
y=Y.flatten(),
156+
z=Z.flatten(),
157+
value=values.flatten(),
158+
isomin=-0.5,
159+
isomax=0.5,
160+
opacity=0.1, # max opacity
161+
opacityscale=[[-0.5, 1], [-0.2, 0], [0.2, 0], [0.5, 1]],
162+
surface_count=21,
163+
colorscale='RdBu'
164+
))
165+
fig.show()
166+
```
167+
168+
### Adding caps to a volume plot
169+
170+
For a clearer visualization of internal surfaces, it is possible to remove the caps (color-coded surfaces on the sides of the visualization domain). Caps are visible by default. Compare below with and without caps.
171+
172+
```python
173+
import numpy as np
174+
import plotly.graph_objects as go
175+
176+
177+
X, Y, Z = np.mgrid[:1:20j, :1:20j, :1:20j]
178+
vol = (X - 1)**2 + (Y - 1)**2 + Z**2
179+
180+
181+
fig = go.Figure(data=go.Volume(
182+
x=X.flatten(), y=Y.flatten(), z=Z.flatten(),
183+
value=vol.flatten(),
184+
isomin=0.2,
185+
isomax=0.7,
186+
opacity=0.2,
187+
surface_count=21,
188+
caps= dict(x_show=True, y_show=True, z_show=True, x_fill=1), # with caps (default mode)
189+
))
190+
191+
# Change camera view for a better view of the sides, XZ plane
192+
# (see https://plot.ly/python/v3/3d-camera-controls/)
193+
fig.update_layout(scene_camera = dict(
194+
up=dict(x=0, y=0, z=1),
195+
center=dict(x=0, y=0, z=0),
196+
eye=dict(x=0.1, y=2.5, z=0.1)
197+
))
198+
199+
fig.show()
200+
```
201+
202+
```python
203+
import numpy as np
204+
import plotly.graph_objects as go
205+
206+
X, Y, Z = np.mgrid[:1:20j, :1:20j, :1:20j]
207+
vol = (X - 1)**2 + (Y - 1)**2 + Z**2
208+
209+
210+
fig = go.Figure(data=go.Volume(
211+
x=X.flatten(), y=Y.flatten(), z=Z.flatten(),
212+
value=vol.flatten(),
213+
isomin=0.2,
214+
isomax=0.7,
215+
opacity=0.2,
216+
surface_count=21,
217+
caps= dict(x_show=False, y_show=False, z_show=False), # no caps
218+
))
219+
fig.update_layout(scene_camera = dict(
220+
up=dict(x=0, y=0, z=1),
221+
center=dict(x=0, y=0, z=0),
222+
eye=dict(x=0.1, y=2.5, z=0.1)
223+
))
224+
225+
fig.show()
226+
```
227+
228+
### Adding slices to a volume plot
229+
230+
Slices through the volume can be added to the volume plot. In this example the isosurfaces are only partially filled so that the slice is more visible, and the caps were removed for the same purpose.
231+
232+
```python
233+
import numpy as np
234+
import plotly.graph_objects as go
235+
236+
X, Y, Z = np.mgrid[:1:20j, :1:20j, :1:20j]
237+
vol = (X - 1)**2 + (Y - 1)**2 + Z**2
238+
239+
240+
fig = go.Figure(data=go.Volume(
241+
x=X.flatten(), y=Y.flatten(), z=Z.flatten(),
242+
value=vol.flatten(),
243+
isomin=0.2,
244+
isomax=0.7,
245+
opacity=0.2,
246+
surface_count=21,
247+
slices_z=dict(show=True, locations=[0.4]),
248+
surface=dict(fill=0.5, pattern='odd'),
249+
caps= dict(x_show=False, y_show=False, z_show=False), # no caps
250+
))
251+
252+
fig.show()
253+
```
254+
255+
#### Reference
256+
See https://plot.ly/python/reference/#volume for more information and chart attribute options!
257+
258+
#### See also
259+
[3D isosurface documentation](./3d-isosurface-plots/)

0 commit comments

Comments
 (0)