Skip to content

Commit 0e0abd1

Browse files
authored
Merge pull request #7 from boutproject/surface
New block type for 'surface' plots
2 parents e1bf6a5 + ce32267 commit 0e0abd1

File tree

10 files changed

+1232
-0
lines changed

10 files changed

+1232
-0
lines changed

animatplot/blocks/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
from .lineplots import Line, ParametricLine, Scatter
33
from .vectors import Quiver, vector_comp
44
from .image_like import Pcolormesh, Imshow
5+
from .surface import Surface
56
from .update import Nuke, Update
67
from .title import Title

animatplot/blocks/surface.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from .base import Block
2+
import matplotlib.pyplot as plt
3+
import numpy as np
4+
5+
6+
class Surface(Block):
7+
"""Animates a surface (wrapping :meth:`mpl_toolkits.mplot3d.axes3d.plot_surface`)
8+
9+
Parameters
10+
----------
11+
X : 1D or 2D np.ndarray, optional
12+
Y : 1D or 2D np.ndarray, optional
13+
C : list of 2D np.ndarray or a 3D np.ndarray
14+
ax : matplotlib.axes.Axes, optional
15+
The matplotlib axes to attach the block to.
16+
Must be created with 'projection="3d"'.
17+
Defaults to matplotlib.pyplot.gca()
18+
t_axis : int, optional
19+
The axis of the array that represents time. Defaults to 0.
20+
No effect if C is a list.
21+
fixed_vscale: bool, default True
22+
By default, set the vertical scale using the overall minimum and maximum of the
23+
array. If set to False, scale is calculated independently for each time slice.
24+
25+
Attributes
26+
----------
27+
ax : matplotlib axis
28+
The matplotlib axes that the block is attached to.
29+
30+
Notes
31+
-----
32+
All other keyword arguments get passed to ``ax.plot_surface``
33+
see :meth:`mpl_toolkits.mplot3d.axes3d.plot_surface` for details.
34+
"""
35+
def __init__(self, *args, ax=None, t_axis=0, fixed_vscale=True, **kwargs):
36+
self.kwargs = kwargs
37+
38+
if len(args) == 1:
39+
self.C = args[0]
40+
x1d = np.arange(self.C[0].shape[0])
41+
y1d = np.arange(self.C[0].shape[1])
42+
self.Y, self.X = np.meshgrid(y1d, x1d)
43+
elif len(args) == 3:
44+
self.X, self.Y, self.C = args
45+
if len(self.X.shape) not in [1, 2]:
46+
raise TypeError('X must be a 1D or 2D arrays')
47+
if len(self.Y.shape) not in [1, 2]:
48+
raise TypeError('Y must be a 1D or 2D arrays')
49+
else:
50+
raise TypeError(
51+
'Illegal arguments to Surface; see help(ax.plot_surface)')
52+
53+
if self.kwargs.get("color") is None and self.kwargs.get("cmap") is None:
54+
# No user-specified colors for plot. Need to set to a fixed value to avoid
55+
# cycling during the animation
56+
self.kwargs["color"] = "C0"
57+
58+
super().__init__(ax, t_axis)
59+
60+
self._is_list = isinstance(self.C, list)
61+
self.C = np.asanyarray(self.C)
62+
63+
if fixed_vscale:
64+
self.ax.set_zlim([self.C.min(), self.C.max()])
65+
66+
Slice = self._make_slice(0, 3)
67+
68+
self.poly = self.ax.plot_surface(self.X, self.Y, self.C[Slice], **self.kwargs)
69+
70+
def _update(self, i):
71+
Slice = self._make_slice(i, 3)
72+
self.ax.collections.clear()
73+
self.poly = self.ax.plot_surface(self.X, self.Y, self.C[Slice], **self.kwargs)
74+
return self.poly
75+
76+
def __len__(self):
77+
if self._is_list:
78+
return self.C.shape[0]
79+
return self.C.shape[self.t_axis]

docs/source/api.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ These blocks are built to animate data.
4949
Pcolormesh
5050
Imshow
5151
Scatter
52+
Surface
5253

5354
Graph Label Blocks
5455
~~~~~~~~~~~~~~~~~~

docs/source/gallery/surface.gif

1.5 MB
Loading

0 commit comments

Comments
 (0)