21
21
22
22
23
23
class Animate :
24
+ """
25
+ Animate objects for matplotlib 3d
26
+
27
+ An instance of this class behaves like an Axes3D and supports proxies for
28
+
29
+ - ``plot``
30
+ - ``quiver``
31
+ - ``text``
32
+
33
+ which renders them and also places corresponding objects into a display list.
34
+ These objects are ``Line``, ``Quiver`` and ``Text``. Only these primitives will
35
+ be animated.
36
+
37
+ The objects are all drawn relative to the origin, and will be transformed according
38
+ to the transform that is being animated.
39
+
40
+ Example::
41
+
42
+ anim = animate.Animate(dims=[0,2]) # set up the 3D axes
43
+ anim.trplot(T, frame='A', color='green') # draw the frame
44
+ anim.run(loop=True) # animate it
45
+ """
24
46
25
47
def __init__ (self , axes = None , dims = None , projection = 'ortho' , labels = ['X' , 'Y' , 'Z' ], ** kwargs ):
48
+ """
49
+ Construct an Animate object
50
+
51
+ :param axes: the axes to plot into, defaults to current axes
52
+ :type axes: Axes3D reference
53
+ :param dims: dimension of plot volume as [xmin, xmax, ymin, ymax,zmin, zmax].
54
+ If dims is [min, max] those limits are applied to the x-, y- and z-axes.
55
+ :type dims: array_like
56
+ :param projection: 3D projection: ortho [default] or persp
57
+ :type projection: str
58
+ :param labels: labels for the axes, defaults to X, Y and Z
59
+ :type labels: 3-tuple of strings
60
+
61
+ Will setup to plot into an existing or a new Axes3D instance.
62
+
63
+ """
26
64
self .displaylist = []
27
65
28
66
if axes is None :
@@ -50,17 +88,52 @@ def __init__(self, axes=None, dims=None, projection='ortho', labels=['X', 'Y', '
50
88
self .ax = axes
51
89
52
90
# set flag for 2d or 3d axes, flag errors on the methods called later
53
-
54
- def draw (self , T ):
55
- for x in self .displaylist :
56
- x .draw (T )
91
+
92
+ def trplot (self , T , ** kwargs ):
93
+ """
94
+ Define the transform to animate
95
+
96
+ :param T: an SO(3) or SE(3) pose to be displayed as coordinate frame
97
+ :type: numpy.ndarray, shape=(3,3) or (4,4)
98
+
99
+ Is polymorphic with ``base.trplot`` and accepts the same parameters. This sets
100
+ up the animation but doesn't execute it.
101
+
102
+ :seealso: :func:`run`
103
+
104
+ """
105
+ # stash the final value
106
+ if tr .isrot (T ):
107
+ self .T = tr .r2t (T )
108
+ else :
109
+ self .T = T
110
+ # draw axes at the origin
111
+ tr .trplot (np .eye (4 ), axes = self , ** kwargs )
57
112
58
113
def run (self , movie = None , axes = None , repeat = True , interval = 50 , nframes = 100 , ** kwargs ):
59
-
114
+ """
115
+ Run the animation
116
+
117
+ :param interval: number of steps in the animation [defaault 100]
118
+ :type interval: int
119
+ :param repeat: animate in endless loop [default False]
120
+ :type repeat: bool
121
+ :param interval: number of milliseconds between frames [default 50]
122
+ :type interval: int
123
+ :param movie: name of file to write MP4 movie into
124
+ :type movie: str
125
+
126
+ Animates a 3D coordinate frame moving from the world frame to a frame represented by the SO(3) or SE(3) matrix to the current axes.
127
+
128
+ Notes:
129
+
130
+ - the ``movie`` option requires the ffmpeg package to be installed: ``conda install -c conda-forge ffmpeg``
131
+ - invokes the draw() method of every object in the display list
132
+ """
133
+
60
134
def update (frame , a ):
61
- s = frame / 100.0
62
- T = tr .transl (0.5 * s , 0.5 * s , 0.5 * s ) @ tr .trotx (math .pi * s )
63
- a .draw (T )
135
+ T = tr .trinterp (self .T , s = frame / nframes )
136
+ a ._draw (T )
64
137
return a .artists ()
65
138
66
139
# blit leaves a trail and first frame
@@ -75,17 +148,38 @@ def update(frame, a):
75
148
print ('creating movie' , movie )
76
149
FFwriter = animation .FFMpegWriter (fps = 10 , extra_args = ['-vcodec' , 'libx264' ])
77
150
ani .save (movie , writer = FFwriter )
78
- # TODO needs conda install -c conda-forge ffmpeg
79
151
80
152
def __repr__ (self ):
153
+ """
154
+ Human readable version of the display list
155
+
156
+ :param self: the animation
157
+ :type self: Animate
158
+ :returns: readable version of the display list
159
+ :rtype: str
160
+ """
81
161
return ', ' .join ([x .type for x in self .displaylist ])
82
162
83
163
def artists (self ):
164
+ """
165
+ List of artists that need to be updated
166
+
167
+ :param self: the animation
168
+ :type self: Animate
169
+ :returns: list of artists
170
+ :rtype: list
171
+ """
84
172
return [x .h for x in self .displaylist ]
173
+
174
+
175
+ def _draw (self , T ):
176
+ for x in self .displaylist :
177
+ x .draw (T )
178
+
85
179
86
180
#------------------- plot()
87
181
88
- class Line :
182
+ class _Line :
89
183
90
184
def __init__ (self , anim , h , xs , ys , zs ):
91
185
p = zip (xs , ys , zs )
@@ -99,13 +193,30 @@ def draw(self, T):
99
193
self .h .set_data (p [0 , :], p [1 , :])
100
194
self .h .set_3d_properties (p [2 , :])
101
195
102
- def plot (self , xs , ys , zs , * args , ** kwargs ):
103
- h , = self .ax .plot (xs , ys , zs , * args , ** kwargs )
104
- self .displaylist .append (Animate .Line (self , h , xs , ys , zs ))
196
+ def plot (self , x , y , z , * args , ** kwargs ):
197
+ """
198
+ Plot a polyline
199
+
200
+ :param x: list of x-coordinates
201
+ :type x: array_like
202
+ :param y: list of y-coordinates
203
+ :type y: array_like
204
+ :param z: list of z-coordinates
205
+ :type z: array_like
206
+
207
+ Other arguments as accepted by the matplotlib method.
208
+
209
+ All arrays must have the same length.
210
+
211
+ :seealso: :func:`matplotlib.pyplot.plot`
212
+ """
213
+
214
+ h , = self .ax .plot (x , y , z , * args , ** kwargs )
215
+ self .displaylist .append (Animate ._Line (self , h , x , y , z ))
105
216
106
217
#------------------- quiver()
107
218
108
- class Quiver :
219
+ class _Quiver :
109
220
110
221
def __init__ (self , anim , h ):
111
222
self .type = 'quiver'
@@ -130,12 +241,36 @@ def draw(self, T):
130
241
self .h .set_segments (p )
131
242
132
243
def quiver (self , x , y , z , u , v , w , * args , ** kwargs ):
244
+ """
245
+ Plot a quiver
246
+
247
+ :param x: list of base x-coordinates
248
+ :type x: array_like
249
+ :param y: list of base y-coordinates
250
+ :type y: array_like
251
+ :param z: list of base z-coordinates
252
+ :type z: array_like
253
+ :param u: list of vector x-coordinates
254
+ :type u: array_like
255
+ :param v: list of vector y-coordinates
256
+ :type v: array_like
257
+ :param w: list of vector z-coordinates
258
+ :type w: array_like
259
+
260
+ Draws a series of arrows, the bases defined by corresponding elements of
261
+ (x,y,z) and the vector has components defined by corresponding elements of
262
+ (u,v,w).
263
+
264
+ Other arguments as accepted by the matplotlib method.
265
+
266
+ :seealso: :func:`matplotlib.pyplot.quiver`
267
+ """
133
268
h = self .ax .quiver (x , y , z , u , v , w , * args , ** kwargs )
134
- self .displaylist .append (Animate .Quiver (self , h ))
269
+ self .displaylist .append (Animate ._Quiver (self , h ))
135
270
136
271
#------------------- text()
137
272
138
- class Text :
273
+ class _Text :
139
274
140
275
def __init__ (self , anim , h , x , y , z ):
141
276
self .type = 'text'
@@ -148,11 +283,25 @@ def draw(self, T):
148
283
# x2, y2, _ = proj3d.proj_transform(p[0], p[1], p[2], self.anim.ax.get_proj())
149
284
# self.h.set_position((x2, y2))
150
285
self .h .set_position ((p [0 ], p [1 ]))
151
- self .h .set_3d_properties (p [2 ])
286
+ self .h .set_3d_properties (z = p [2 ], zdir = 'x' )
152
287
153
288
def text (self , x , y , z , * args , ** kwargs ):
289
+ """
290
+ Plot text
291
+
292
+ :param x: x-coordinate
293
+ :type x: float
294
+ :param y: float
295
+ :type y: array_like
296
+ :param z: z-coordinate
297
+ :type z: float
298
+
299
+ Other arguments as accepted by the matplotlib method.
300
+
301
+ :seealso: :func:`matplotlib.pyplot.text`
302
+ """
154
303
h = self .ax .text3D (x , y , z , * args , ** kwargs )
155
- self .displaylist .append (Animate .Text (self , h , x , y , z ))
304
+ self .displaylist .append (Animate ._Text (self , h , x , y , z ))
156
305
157
306
#------------------- scatter()
158
307
@@ -178,19 +327,3 @@ def set_ylabel(self, *args, **kwargs):
178
327
179
328
def set_zlabel (self , * args , ** kwargs ):
180
329
self .ax .set_zlabel (* args , ** kwargs )
181
-
182
-
183
- def tranimate (T , ** kwargs ):
184
- anim = Animate (** kwargs )
185
- tr .trplot (T , axes = anim , ** kwargs )
186
- anim .run (** kwargs )
187
-
188
-
189
- tranimate (tr .transl (0 , 0 , 0 ), frame = 'A' , arrow = False , dims = [0 , 5 ]) # , movie='bob.mp4')
190
-
191
-
192
- # a = trplot_a( tr.transl(1,2,3), frame='A', rviz=True, width=1)
193
- # print(a)
194
- # a.draw(tr.transl(0, 0, -1))
195
- # trplot_a( tr.transl(3,1, 2), color='red', width=3, frame='B')
196
- # trplot_a( tr.transl(4, 3, 1)@tr.trotx(math.pi/3), color='green', frame='c')
0 commit comments