Skip to content

Commit 9d3c68c

Browse files
authored
Merge pull request #115 from plotly/volume
3d volume notebook
2 parents 9efb356 + 9d019c7 commit 9d3c68c

File tree

1 file changed

+262
-0
lines changed

1 file changed

+262
-0
lines changed

python/3d-volume.md

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
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.2.3
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.2
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:40j, -8:8:40j, -8:8:40j]
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:30j, -1:1:30j, -1:1:30j]
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 = 30
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, 4)
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=25,
104+
))
105+
fig.update_layout(scene_xaxis_showticklabels=False,
106+
scene_yaxis_showticklabels=False,
107+
scene_zaxis_showticklabels=False)
108+
fig.show()
109+
```
110+
111+
### Defining the opacity scale of volume plots
112+
113+
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).
114+
115+
```python
116+
import plotly.graph_objects as go
117+
from plotly.subplots import make_subplots
118+
fig = make_subplots(
119+
rows=2, cols=2,
120+
specs=[[{'type': 'volume'}, {'type': 'volume'}],
121+
[{'type': 'volume'}, {'type': 'volume'}]])
122+
123+
import numpy as np
124+
125+
X, Y, Z = np.mgrid[-8:8:30j, -8:8:30j, -8:8:30j]
126+
values = np.sin(X*Y*Z) / (X*Y*Z)
127+
128+
129+
fig.add_trace(go.Volume(
130+
opacityscale="uniform",
131+
), row=1, col=1)
132+
fig.add_trace(go.Volume(
133+
opacityscale="extremes",
134+
), row=1, col=2)
135+
fig.add_trace(go.Volume(
136+
opacityscale="min",
137+
), row=2, col=1)
138+
fig.add_trace(go.Volume(
139+
opacityscale="max",
140+
), row=2, col=2)
141+
fig.update_traces(x=X.flatten(), y=Y.flatten(), z=Z.flatten(), value=values.flatten(),
142+
isomin=0.15, isomax=0.9, opacity=0.1, surface_count=15)
143+
fig.show()
144+
```
145+
146+
### Defining a custom opacity scale
147+
148+
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.
149+
150+
```python
151+
import plotly.graph_objects as go
152+
import numpy as np
153+
X, Y, Z = np.mgrid[-1:1:30j, -1:1:30j, -1:1:30j]
154+
values = np.sin(np.pi*X) * np.cos(np.pi*Z) * np.sin(np.pi*Y)
155+
156+
fig = go.Figure(data=go.Volume(
157+
x=X.flatten(),
158+
y=Y.flatten(),
159+
z=Z.flatten(),
160+
value=values.flatten(),
161+
isomin=-0.5,
162+
isomax=0.5,
163+
opacity=0.1, # max opacity
164+
opacityscale=[[-0.5, 1], [-0.2, 0], [0.2, 0], [0.5, 1]],
165+
surface_count=21,
166+
colorscale='RdBu'
167+
))
168+
fig.show()
169+
```
170+
171+
### Adding caps to a volume plot
172+
173+
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.
174+
175+
```python
176+
import numpy as np
177+
import plotly.graph_objects as go
178+
179+
180+
X, Y, Z = np.mgrid[:1:20j, :1:20j, :1:20j]
181+
vol = (X - 1)**2 + (Y - 1)**2 + Z**2
182+
183+
184+
fig = go.Figure(data=go.Volume(
185+
x=X.flatten(), y=Y.flatten(), z=Z.flatten(),
186+
value=vol.flatten(),
187+
isomin=0.2,
188+
isomax=0.7,
189+
opacity=0.2,
190+
surface_count=21,
191+
caps= dict(x_show=True, y_show=True, z_show=True, x_fill=1), # with caps (default mode)
192+
))
193+
194+
# Change camera view for a better view of the sides, XZ plane
195+
# (see https://plot.ly/python/v3/3d-camera-controls/)
196+
fig.update_layout(scene_camera = dict(
197+
up=dict(x=0, y=0, z=1),
198+
center=dict(x=0, y=0, z=0),
199+
eye=dict(x=0.1, y=2.5, z=0.1)
200+
))
201+
202+
fig.show()
203+
```
204+
205+
```python
206+
import numpy as np
207+
import plotly.graph_objects as go
208+
209+
X, Y, Z = np.mgrid[:1:20j, :1:20j, :1:20j]
210+
vol = (X - 1)**2 + (Y - 1)**2 + Z**2
211+
212+
213+
fig = go.Figure(data=go.Volume(
214+
x=X.flatten(), y=Y.flatten(), z=Z.flatten(),
215+
value=vol.flatten(),
216+
isomin=0.2,
217+
isomax=0.7,
218+
opacity=0.2,
219+
surface_count=21,
220+
caps= dict(x_show=False, y_show=False, z_show=False), # no caps
221+
))
222+
fig.update_layout(scene_camera = dict(
223+
up=dict(x=0, y=0, z=1),
224+
center=dict(x=0, y=0, z=0),
225+
eye=dict(x=0.1, y=2.5, z=0.1)
226+
))
227+
228+
fig.show()
229+
```
230+
231+
### Adding slices to a volume plot
232+
233+
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.
234+
235+
```python
236+
import numpy as np
237+
import plotly.graph_objects as go
238+
239+
X, Y, Z = np.mgrid[:1:20j, :1:20j, :1:20j]
240+
vol = (X - 1)**2 + (Y - 1)**2 + Z**2
241+
242+
243+
fig = go.Figure(data=go.Volume(
244+
x=X.flatten(), y=Y.flatten(), z=Z.flatten(),
245+
value=vol.flatten(),
246+
isomin=0.2,
247+
isomax=0.7,
248+
opacity=0.2,
249+
surface_count=21,
250+
slices_z=dict(show=True, locations=[0.4]),
251+
surface=dict(fill=0.5, pattern='odd'),
252+
caps= dict(x_show=False, y_show=False, z_show=False), # no caps
253+
))
254+
255+
fig.show()
256+
```
257+
258+
#### Reference
259+
See https://plot.ly/python/reference/#volume for more information and chart attribute options!
260+
261+
#### See also
262+
[3D isosurface documentation](./3d-isosurface-plots/)

0 commit comments

Comments
 (0)