diplotocus 0.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3686 @@
1
+ import numpy as np
2
+ import numbers,inspect
3
+ import matplotlib as mpl
4
+ import matplotlib.pyplot as plt
5
+ from svgpath2mpl import parse_path
6
+ from matplotlib import transforms
7
+ from matplotlib.textpath import TextPath
8
+ from matplotlib.patches import PathPatch
9
+ from matplotlib.font_manager import FontProperties
10
+
11
+ from . import easings
12
+
13
+ def dealias(mpl_obj,kwargs):
14
+ if mpl_obj is None:
15
+ return kwargs
16
+ for alias in mpl_obj._alias_map:
17
+ has_main_alias = alias in kwargs
18
+ for other_alias in mpl_obj._alias_map[alias]:
19
+ if other_alias in kwargs:
20
+ if has_main_alias:
21
+ raise TypeError('Got both \'{}\' and \'{}\', which are aliases of one another'.format(alias,other_alias))
22
+ else:
23
+ has_main_alias = True
24
+ kwargs[alias] = kwargs[other_alias]
25
+ kwargs.pop(other_alias)
26
+
27
+ return kwargs
28
+
29
+ def to_np_array(obj):
30
+ obj = np.array(obj)
31
+ if obj.ndim == 0:
32
+ obj = np.ravel(obj)
33
+ return obj
34
+
35
+ class Animation:
36
+ """
37
+ A parent Animation object for all plotting objects.
38
+
39
+ Parameters
40
+ ----------
41
+ duration : float
42
+ Duration of the animation
43
+ delay : float, default=0
44
+ Delay before starting
45
+ easing : callable, optional
46
+ Easing function
47
+ axis : matplotlib.axes.Axes, optional
48
+ Axis to plot on
49
+ """
50
+ def __init__(self,easing=None,axis=None,**kwargs):
51
+ self.anims = []
52
+ self.tl = None
53
+ self.set_easing(easing)
54
+ self.set_axis(axis)
55
+ self.animate_easing = None
56
+ self.obj = None
57
+ self.base_color = None
58
+ self.tween_properties = []
59
+ self.x_min = 0
60
+ self.x_max = 0
61
+ self.tween_starts = []
62
+ self.tween_ends = []
63
+ self.transforms = []
64
+ self.x = None
65
+ self.y = None
66
+ self.persistent = False
67
+ self.T = transforms.Affine2D()
68
+ if not hasattr(self, 'mpl_obj_type'):
69
+ self.mpl_obj_type = None
70
+ if not hasattr(self, 'mpl_plot_type'):
71
+ self.mpl_plot_type = None
72
+ self.alias_map = self.get_alias_map()
73
+ self.possible_kwargs = self.get_possible_kwargs()
74
+
75
+ kwargs = self.clean_kwargs(kwargs)
76
+ kwargs['animated'] = True
77
+ self.kwargs = kwargs
78
+ self.kwargs = dealias(self.mpl_obj_type,self.kwargs)
79
+ if 'alpha' not in self.kwargs:
80
+ self.kwargs['alpha'] = 1
81
+
82
+ def function(self):
83
+ pass
84
+
85
+ def init(self):
86
+ pass
87
+
88
+ def get_alias_map(self):
89
+ if self.mpl_obj_type is None:
90
+ return None
91
+ alias_map = self.mpl_obj_type._alias_map
92
+ if self.mpl_plot_type == plt.plot and False:
93
+ alias_map['color'] = ['c']
94
+ return alias_map
95
+
96
+ def get_possible_kwargs(self):
97
+ if self.mpl_obj_type is None or self.mpl_plot_type is None:
98
+ return {}
99
+ sig = inspect.signature(self.mpl_obj_type)
100
+ kwargs = [
101
+ param.name for param in sig.parameters.values() if
102
+ (param.kind == param.KEYWORD_ONLY or param.kind == param.VAR_KEYWORD or param.default != param.empty)
103
+ and param.name != 'kwargs']
104
+
105
+ sig = inspect.signature(self.mpl_plot_type)
106
+ kwargs += [
107
+ param.name for param in sig.parameters.values() if
108
+ (param.kind == param.KEYWORD_ONLY or param.kind == param.VAR_KEYWORD or param.default != param.empty)
109
+ and param.name != 'kwargs']
110
+ return kwargs
111
+
112
+ def get_main_alias(self,properties):
113
+ if self.mpl_obj_type is None:
114
+ return properties
115
+ is_iterable = isinstance(properties,(list,np.ndarray,tuple))
116
+ properties = to_np_array(properties).astype(np.object_)
117
+ for i,property in enumerate(properties):
118
+ if property not in self.alias_map:
119
+ for alias in self.alias_map:
120
+ if property in self.alias_map[alias]:
121
+ properties[i] = alias
122
+ if is_iterable:
123
+ return properties
124
+ return properties[0]
125
+
126
+ def set_axis(self,axis):
127
+ self.axis = axis
128
+ if self.axis is None and self.tl is not None:
129
+ self.set_axis(self.tl.main_axis)
130
+ return self
131
+
132
+ def set_easing(self,easing):
133
+ self.easing = easing
134
+ return self
135
+
136
+ def apply(self,x,animate_easing):
137
+ self.animate_easing = animate_easing
138
+ if x < self.x_min:
139
+ return
140
+ if x >= self.x_max and self.persistent == False:
141
+ self.clean(self.x_max-2,clear_anims=False)
142
+ return
143
+ self.T = transforms.Affine2D()
144
+ kwargs = self._tween(self.kwargs.copy(),x)
145
+ _x = x
146
+ if self.x_max-self.x_min == 1:#handle 1 frame anims
147
+ _x = self.x_max
148
+ self.anim_function(_x,kwargs)
149
+ self.check_transforms(x)
150
+
151
+ def anim_function(self,x,kwargs):
152
+ data_x = to_np_array(self.x)
153
+ data_y = to_np_array(self.y)
154
+ if self.__class__.__name__ == 'errorbar':
155
+ data_x_err = to_np_array(self.xerr)
156
+ data_y_err = to_np_array(self.yerr)
157
+ elif self.__class__.__name__ == 'fill_between':
158
+ y1 = to_np_array(self.y1)
159
+ y2 = to_np_array(self.y2)
160
+ elif self.__class__.__name__ == 'fill_betweenx':
161
+ x1 = to_np_array(self.x1)
162
+ x2 = to_np_array(self.x2)
163
+
164
+ #First pass to get frame if sequencing
165
+ for anim in self.anims:
166
+ if anim['name'] != 'sequence':
167
+ continue
168
+ if self.x is None:
169
+ continue
170
+ if x < anim['delay'] or x > anim['delay'] + anim['duration']:
171
+ continue
172
+
173
+ t = self.get_t_from_x(anim,x)
174
+
175
+ if len(data_x.shape) == 1:
176
+ data_x = data_x.reshape((-1,1))
177
+ data_y = data_y.reshape((-1,1))
178
+
179
+ _i = max(min(round(t*len(data_x)),len(data_x)-1),0)
180
+ data_x = data_x[_i]
181
+ data_y = data_y[_i]
182
+
183
+ if self.__class__.__name__ == 'errorbar':
184
+ data_x_err = data_x_err[_i]
185
+ data_y_err = data_y_err[_i]
186
+ elif self.__class__.__name__ == 'fill_between':
187
+ y1 = y1[_i]
188
+ y2 = y2[_i]
189
+ elif self.__class__.__name__ == 'fill_betweenx':
190
+ x1 = x1[_i]
191
+ x2 = x2[_i]
192
+
193
+ #Second pass to cut points if drawing or erasing
194
+ i_max = -1
195
+ for anim in self.anims:
196
+ if anim['name'] != 'draw':
197
+ continue
198
+ if self.x is None:
199
+ continue
200
+ if x < anim['delay'] or x > anim['delay'] + anim['duration']:
201
+ continue
202
+ t = self.get_t_from_x(anim,x)
203
+
204
+ if 'alpha' in kwargs:
205
+ alpha = to_np_array(kwargs['alpha'])
206
+ if np.sum(alpha) == 0:
207
+ kwargs['alpha'] = 1
208
+
209
+ if anim['reverse']:
210
+ i_max = max(round((1-t)*len(data_x)),0)
211
+ else:
212
+ i_max = min(round(t*len(data_x)),len(data_x))
213
+ break
214
+
215
+ if i_max > 0:
216
+ data_x = data_x[:i_max]
217
+ if data_y is not None:
218
+ data_y = data_y[:i_max]
219
+ if self.__class__.__name__ == 'errorbar':
220
+ data_x_err = data_x_err[:i_max]
221
+ data_y_err = data_y_err[:i_max]
222
+ elif self.__class__.__name__ == 'fill_between':
223
+ y1 = y1[:i_max]
224
+ y2 = y2[:i_max]
225
+ elif self.__class__.__name__ == 'fill_betweenx':
226
+ x1 = x1[:i_max]
227
+ x2 = x2[:i_max]
228
+ if 'c' in kwargs and isinstance(kwargs['c'],(list,np.ndarray)):
229
+ kwargs['c'] = kwargs['c'][:i_max]
230
+ elif i_max == 0:
231
+ data_x = []
232
+ data_y = []
233
+ if self.__class__.__name__ == 'errorbar':
234
+ data_x_err = []
235
+ data_y_err = []
236
+ elif self.__class__.__name__ == 'fill_between':
237
+ y1 = []
238
+ y2 = []
239
+ elif self.__class__.__name__ == 'fill_betweenx':
240
+ x1 = []
241
+ x2 = []
242
+ if 'c' in kwargs and isinstance(kwargs['c'],(list,np.ndarray)):
243
+ kwargs['c'] = []
244
+
245
+ #Third pass to morph
246
+ for anim in self.anims:
247
+ if anim['name'] != 'morph':
248
+ continue
249
+ if self.x is None:
250
+ continue
251
+ if x < anim['delay'] or x > anim['delay'] + anim['duration']:
252
+ continue
253
+ t = self.get_t_from_x(anim,x)
254
+
255
+ if data_x is None or data_y is None:
256
+ continue
257
+
258
+ new_data_x = []
259
+ new_data_y = []
260
+ for i in range(len(data_x)):
261
+ new_data_x.append(data_x[i] + (anim['new_x'][i] - data_x[i])*t)
262
+ if len(data_y) == len(anim['new_y']):
263
+ new_data_y.append(data_y[i] + (anim['new_y'][i] - data_y[i])*t)
264
+ data_x = new_data_x
265
+ data_y = new_data_y
266
+
267
+ if self.__class__.__name__ == 'errorbar':
268
+ new_data_x_err = []
269
+ new_data_y_err = []
270
+ for i in range(len(data_x_err)):
271
+ new_data_x_err.append(data_x_err[i] + (anim['new_x_err'][i] - data_x_err[i])*t)
272
+ new_data_y_err.append(data_y_err[i] + (anim['new_y_err'][i] - data_y_err[i])*t)
273
+ data_x_err = new_data_x_err
274
+ data_y_err = new_data_y_err
275
+
276
+ if self.__class__.__name__ == 'errorbar':
277
+ kwargs['xerr'] = data_x_err
278
+ kwargs['yerr'] = data_y_err
279
+ elif self.__class__.__name__ == 'fill_between':
280
+ kwargs['y1'] = y1
281
+ kwargs['y2'] = y2
282
+ elif self.__class__.__name__ == 'fill_betweenx':
283
+ kwargs['x1'] = x1
284
+ kwargs['x2'] = x2
285
+
286
+ data_x = to_np_array(data_x)
287
+ data_y = to_np_array(data_y)
288
+
289
+ if self.function is not None:
290
+ self.function(data_x,data_y,x,kwargs)
291
+
292
+ def initialize(self,timeline):
293
+ self.tl = timeline
294
+ if self.axis is None:
295
+ self.set_axis(self.tl.main_axis)
296
+
297
+ self.clean(0)
298
+ self.compute_timings()
299
+ self.init()
300
+
301
+ def compute_timings(self):
302
+ x_max = 0
303
+ x_min = np.inf
304
+ for anim in self.anims:
305
+ if anim['duration'] + anim['delay'] > x_max:
306
+ x_max = anim['duration'] + anim['delay']
307
+ self.persistent = anim['persistent']
308
+ if anim['delay'] < x_min:
309
+ x_min = anim['delay']
310
+ self.x_max = x_max
311
+ self.x_min = x_min
312
+
313
+ def get_t_from_x(self,anim,x):
314
+ easing = easings.easeLinear()
315
+ if self.tl.easing is not None:
316
+ easing = self.tl.easing
317
+ if self.easing is not None:
318
+ easing = self.easing
319
+ if self.animate_easing is not None:
320
+ easing = self.animate_easing
321
+ if anim['easing'] is not None:
322
+ easing = anim['easing']
323
+
324
+ if anim['duration'] == 1 and x == anim['delay']:
325
+ return 1
326
+ return easing.ease((x - anim['delay']) / max(1, anim['duration'] - 1))
327
+
328
+ def sanitize_colors(self,properties,starts,ends):
329
+ for i in range(len(properties)):
330
+ if properties[i] in ['c','colors','color','edgecolor','facecolor','edgecolors','facecolors']:
331
+ starts[i] = list(mpl.colors.to_rgba_array(starts[i])[0])
332
+ ends[i] = list(mpl.colors.to_rgba_array(ends[i])[0])
333
+ return properties,starts,ends
334
+
335
+ def tween(self,property,start,end,duration,delay=0,easing=None,persistent=True):
336
+ """Animate a change in a property of the plot object.
337
+
338
+ Parameters
339
+ ----------
340
+ property : str
341
+ the name of the property to animate.
342
+ start : any
343
+ the starting value of the property.
344
+ end : any
345
+ the ending value of the property.
346
+ duration : float
347
+ the number of frames the animation runs from.
348
+ delay : float, default=0
349
+ the number of frames after what the animation starts playing.
350
+ easing : callable, optional
351
+ the easing used for this animation. If None, a linear easing is applied.
352
+ """
353
+ return self.tweens([property],[start],[end],duration,delay,easing,persistent=persistent)
354
+
355
+ def tweens(self,properties,starts,ends,duration,delay=0,easing=None,persistent=True):
356
+ """Animate multiple properties at once.
357
+
358
+ Parameters
359
+ ----------
360
+ properties : list[str]
361
+ a list of the names of the properties to animate.
362
+ starts : list[Any]
363
+ the starting values of the properties.
364
+ ends : list[Any]
365
+ the ending values of the properties.
366
+ duration : float
367
+ the number of frames the animation runs from.
368
+ delay : float, default=0
369
+ the number of frames after what the animation starts playing.
370
+ easing : callable, optional
371
+ the easing used for this animation. If None, a linear easing is applied.
372
+ """
373
+ starts = [to_np_array(start) for start in starts]
374
+ ends = [to_np_array(end) for end in ends]
375
+ properties = self.get_main_alias(properties)
376
+ properties,starts,ends = self.sanitize_colors(properties,starts,ends)
377
+
378
+ for property,start,end in zip(properties,starts,ends):
379
+ new_anim = {
380
+ 'name':'tween',
381
+ 'duration':duration,
382
+ 'delay':delay,
383
+ 'easing':easing,
384
+ 'property':property,
385
+ 'start':start,
386
+ 'end':end,
387
+ 'persistent':persistent,
388
+ 'played':False
389
+ }
390
+ self.anims.append(new_anim)
391
+ self.compute_timings()
392
+ return self
393
+
394
+ def _tween(self,kwargs,x):
395
+ for anim in self.anims:
396
+ if x < anim['delay']:
397
+ continue
398
+ if anim['name'] != 'tween':
399
+ continue
400
+ _x = min(x,anim['duration'] + anim['delay'])
401
+ t = self.get_t_from_x(anim,_x)
402
+ t = np.clip(t,0,1)
403
+ start = to_np_array(anim['start'])
404
+ end = to_np_array(anim['end'])
405
+ current = start + (end - start)*t
406
+ kwargs[anim['property']] = current
407
+ return kwargs
408
+
409
+ def show(self,duration,delay=0,easing=None,persistent=True):
410
+ """Fade in plot objects.
411
+
412
+ This is a shorthand animation that fades in plot objects. It is equivalent to `tween('alpha',0,1)`.
413
+
414
+ Parameters
415
+ ----------
416
+ duration : float
417
+ the number of frames the animation runs from.
418
+ delay : float, default=0
419
+ the number of frames after what the animation starts playing.
420
+ easing : callable, optional
421
+ the easing used for this animation. If None, a linear easing is applied.
422
+ persistent : bool, default=True
423
+ if True, the plot object will continue to be plotted after its last animation has played.
424
+ """
425
+ return self.tween('alpha',start=0,end=1,duration=duration,delay=delay,easing=easing,persistent=persistent)
426
+
427
+ def hide(self,duration,delay=0,easing=None,persistent=True):
428
+ """Fade out plot objects.
429
+
430
+ This is a shorthand animation that fades out plot objects. It is equivalent to `tween('alpha',1,0)`.
431
+
432
+ Parameters
433
+ ----------
434
+ duration : float
435
+ the number of frames the animation runs from.
436
+ delay : float, default=0
437
+ the number of frames after what the animation starts playing.
438
+ easing : callable, optional
439
+ the easing used for this animation. If None, a linear easing is applied.
440
+ persistent : bool, default=True
441
+ if True, the plot object will continue to be plotted after its last animation has played.
442
+ """
443
+ return self.tween('alpha',start=1,end=0,duration=duration,delay=delay,easing=easing,persistent=persistent)
444
+
445
+ def draw(self,duration,reverse=False,delay=0,easing=None,persistent=True):
446
+ """Animate the plot object by sequentially adding more points of the dataset.
447
+
448
+ Parameters
449
+ ----------
450
+ duration : float
451
+ the number of frames the animation runs from.
452
+ delay : float, default=0
453
+ the number of frames after what the animation starts playing.
454
+ easing : callable, optional
455
+ the easing used for this animation. If None, a linear easing is applied.
456
+ reverse : bool, default=False
457
+ if True, removes points sequentially instead of adding points.
458
+ persistent : bool, default=True
459
+ if True, the plot object will continue to be plotted after its last animation has played.
460
+ """
461
+ self.anims.append({
462
+ 'name':'draw',
463
+ 'duration':duration,
464
+ 'delay':delay,
465
+ 'easing':easing,
466
+ 'reverse':reverse,
467
+ 'persistent':persistent,
468
+ 'played':False
469
+ })
470
+ self.compute_timings()
471
+ return self
472
+
473
+ def scale(self,start_scale,end_scale,duration,center=None,delay=0,easing=None,persistent=True):
474
+ """Scale a plot object from one size to another.
475
+
476
+ Parameters
477
+ ----------
478
+ start_scale : float or tuple[float, float]
479
+ the starting scale of the plot object.
480
+ end_scale : float or tuple[float, float]
481
+ the ending scale of the plot object.
482
+ duration : float
483
+ the number of frames the animation runs from.
484
+ delay : float, default=0
485
+ the number of frames after what the animation starts playing.
486
+ easing : callable, optional
487
+ the easing used for this animation. If None, a linear easing is applied.
488
+ persistent : bool, default=True
489
+ if True, the plot object will continue to be plotted after its last animation has played.
490
+
491
+ You can either pass scalar values, in which case the plot object will be uniformly scaled, or a 2-tuple for independent X and Y scales.
492
+ """
493
+ if isinstance(start_scale,(list,tuple,np.ndarray)):
494
+ scalex_start,scaley_start = start_scale
495
+ else:
496
+ scalex_start = start_scale
497
+ scaley_start = start_scale
498
+ if isinstance(end_scale,(list,tuple,np.ndarray)):
499
+ scalex_end,scaley_end = end_scale
500
+ else:
501
+ scalex_end = end_scale
502
+ scaley_end = end_scale
503
+
504
+ self.anims.append({
505
+ 'name':'scale',
506
+ 'duration':duration,
507
+ 'delay':delay,
508
+ 'easing':easing,
509
+ 'start':(scalex_start,scaley_start),
510
+ 'end':(scalex_end,scaley_end),
511
+ 'persistent':persistent,
512
+ 'center':center,
513
+ 'played':False
514
+ })
515
+ self.compute_timings()
516
+ return self
517
+
518
+ def _scale(self, t, start, end, center=None):
519
+ if center is None:
520
+ cx, cy = self.get_center()
521
+ else:
522
+ cx, cy = center
523
+ scale_x = start[0] + (end[0] - start[0]) * t
524
+ scale_y = start[1] + (end[1] - start[1]) * t
525
+
526
+ # Update Affine2D for non-collections
527
+ self.T = (
528
+ self.T
529
+ .translate(-cx, -cy)
530
+ .scale(scale_x, scale_y)
531
+ .translate(cx, cy)
532
+ )
533
+
534
+ obj = to_np_array(self.obj)
535
+ for o in obj:
536
+ if isinstance(o, mpl.collections.Collection) and self.mpl_obj_type == mpl.collections.Collection:
537
+ offsets = np.asarray(o.get_offsets(), dtype=float).reshape(-1, 2)
538
+ offsets_centered = offsets - np.array([cx, cy])
539
+
540
+ new_offsets = offsets_centered * np.array([scale_x, scale_y]) + np.array([cx, cy])
541
+
542
+ new_offsets = new_offsets.reshape(-1, 2)
543
+ o.set_offsets(new_offsets)
544
+ else:
545
+ o.set_transform(self.T + self.axis.transData)
546
+
547
+ def rotate(self,start_angle,end_angle,duration,center=None,delay=0,easing=None,persistent=True):
548
+ """Rotate a plot object from one angle to another.
549
+
550
+ Parameters
551
+ ----------
552
+ start_angle : float
553
+ the starting angle of the rotation in radians.
554
+ end_angle : float
555
+ the ending angle of the rotation in radians.
556
+ duration : float
557
+ the number of frames the animation runs from.
558
+ delay : float, default=0
559
+ the number of frames after what the animation starts playing.
560
+ easing : callable, optional
561
+ the easing used for this animation. If None, a linear easing is applied.
562
+ persistent : bool, default=True
563
+ if True, the plot object will continue to be plotted after its last animation has played.
564
+ """
565
+ self.anims.append({
566
+ 'name':'rotate',
567
+ 'duration':duration,
568
+ 'delay':delay,
569
+ 'easing':easing,
570
+ 'start':start_angle,
571
+ 'end':end_angle,
572
+ 'center':center,
573
+ 'persistent':persistent,
574
+ 'played':False
575
+ })
576
+ self.compute_timings()
577
+ return self
578
+
579
+ def _rotate(self, t, start, end, center=None):
580
+ if center is None:
581
+ cx, cy = self.get_center()
582
+ else:
583
+ cx, cy = center
584
+ rotation = start + (end - start) * t
585
+ self.T = (
586
+ self.T
587
+ .translate(-cx, -cy)
588
+ .rotate_deg(rotation)
589
+ .translate(cx, cy)
590
+ )
591
+
592
+ obj = to_np_array(self.obj)
593
+ for o in obj:
594
+ if isinstance(o, mpl.collections.Collection) and self.mpl_obj_type == mpl.collections.Collection:
595
+ offsets = np.asarray(o.get_offsets(), dtype=float).reshape(-1, 2)
596
+ offsets_centered = offsets - np.array([cx, cy])
597
+
598
+ theta = np.deg2rad(rotation)
599
+ R = np.array([[np.cos(theta), -np.sin(theta)],
600
+ [np.sin(theta), np.cos(theta)]])
601
+
602
+ new_offsets = offsets_centered @ R.T + np.array([cx, cy])
603
+
604
+ new_offsets = new_offsets.reshape(-1, 2)
605
+ o.set_offsets(new_offsets)
606
+ else:
607
+ o.set_transform(self.T + self.axis.transData)
608
+
609
+ def translate(self,start_pos,end_pos,duration,delay=0,easing=None,persistent=True):
610
+ """Move a plot object from one position to another.
611
+
612
+ Parameters
613
+ ----------
614
+ start_pos : array-like
615
+ where the translation starts, relative to the current center of the plot object.
616
+ end_pos : array-like
617
+ where the translation ends, relative to the current center of the plot object.
618
+ duration : float
619
+ the number of frames the animation runs from.
620
+ delay : float, default=0
621
+ the number of frames after what the animation starts playing.
622
+ easing : callable, optional
623
+ the easing used for this animation. If None, a linear easing is applied.
624
+ persistent : bool, default=True
625
+ if True, the plot object will continue to be plotted after its last animation has played.
626
+ """
627
+ self.anims.append({
628
+ 'name':'translate',
629
+ 'duration':duration,
630
+ 'delay':delay,
631
+ 'easing':easing,
632
+ 'start':start_pos,
633
+ 'end':end_pos,
634
+ 'persistent':persistent,
635
+ 'played':False
636
+ })
637
+ self.compute_timings()
638
+ return self
639
+
640
+ def _translate(self,t,start,end):
641
+ pos_x = start[0] + (end[0] - start[0])*t
642
+ pos_y = start[1] + (end[1] - start[1])*t
643
+ self.T = self.T.translate(pos_x,pos_y)
644
+ obj = to_np_array(self.obj)
645
+ for o in obj:
646
+ if self.mpl_obj_type == mpl.collections.Collection:
647
+ offsets = o.get_offsets()
648
+ o.set_offsets(offsets + np.array([pos_x,pos_y]))
649
+ else:
650
+ o.set_transform(self.T + self.axis.transData)
651
+
652
+ def morph(self,new_x,new_y,duration,delay=0,easing=None,persistent=True):
653
+ """Morph between the base dataset and a new dataset.
654
+
655
+ Parameters
656
+ ----------
657
+ new_x : array-like or float
658
+ the x data points to morph to.
659
+ new_y : array-like or float
660
+ the y data points to morph to.
661
+ duration : float
662
+ the number of frames the animation runs from.
663
+ delay : float, default=0
664
+ the number of frames after what the animation starts playing.
665
+ easing : callable, optional
666
+ the easing used for this animation. If None, a linear easing is applied.
667
+ persistent : bool, default=True
668
+ if True, the plot object will continue to be plotted after its last animation has played.
669
+
670
+ For plot objects that take 1D datasets (e.g. hist()), `morph()` only accepts `new_x`.
671
+ """
672
+ # TODO : if new_x/new_y not the same size, resample them to match
673
+ if isinstance(new_x,numbers.Number):
674
+ new_x = [new_x]
675
+ new_y = [new_y]
676
+ new_x = to_np_array(new_x)
677
+ new_y = to_np_array(new_y)
678
+
679
+ self.anims.append({
680
+ 'name':'morph',
681
+ 'duration':duration,
682
+ 'delay':delay,
683
+ 'easing':easing,
684
+ 'new_x':new_x,
685
+ 'new_y':new_y,
686
+ 'persistent':persistent,
687
+ 'played':False
688
+ })
689
+ self.compute_timings()
690
+ return self
691
+
692
+ def sequence(self,duration,delay=0,easing=None,persistent=True):
693
+ """Plot one datapoint or row of the dataset per frame.
694
+
695
+ Parameters
696
+ ----------
697
+ duration : float
698
+ the number of frames the animation runs from.
699
+ delay : float, default=0
700
+ the number of frames after what the animation starts playing.
701
+ easing : callable, optional
702
+ the easing used for this animation. If None, a linear easing is applied.
703
+ persistent : bool, default=True
704
+ if True, the plot object will continue to be plotted after its last animation has played.
705
+
706
+ If the dataset is 1-dimensional, this plots a single datapoint per frame, if 2D, it will plot one row per frame. This animation can be used if you have a predefined list of datapoints you want to animate frame-by-frame.
707
+ """
708
+ self.anims.append({
709
+ 'name':'sequence',
710
+ 'duration':duration,
711
+ 'delay':delay,
712
+ 'easing':easing,
713
+ 'persistent':persistent,
714
+ 'played':False
715
+ })
716
+ self.compute_timings()
717
+ return self
718
+
719
+ def clean(self,x,clear_anims=True):
720
+ if self.obj is not None and self.base_color is None:
721
+ if isinstance(self.obj,(list,np.ndarray)):
722
+ self.base_color = self.obj[0].get_color()
723
+ elif hasattr(self.obj,'get_color'):
724
+ self.base_color = self.obj.get_color()
725
+ if 'color' not in self.kwargs and 'c' not in self.kwargs and self.base_color is not None:
726
+ self.kwargs['color'] = self.base_color
727
+
728
+ for anim in self.anims:
729
+ t = self.get_t_from_x(anim,x)
730
+ if t != 1:
731
+ continue
732
+ if anim['name'] == 'morph':
733
+ self.x = anim['new_x']
734
+ self.y = anim['new_y']
735
+
736
+ if x >= self.x_min-1 and x < self.x_max-1 and self.obj is not None:
737
+ if isinstance(self.obj,list):
738
+ for obj in self.obj:
739
+ try:
740
+ obj.remove()
741
+ except:
742
+ pass
743
+ else:
744
+ try:
745
+ self.obj.remove()
746
+ except:
747
+ pass
748
+ self.obj = None
749
+ elif x == self.x_max and clear_anims:
750
+ self.end_animation()
751
+
752
+ def end_animation(self):
753
+ self.anims = []
754
+
755
+ def clean_kwargs(self,kwargs):
756
+ if 'duration' in kwargs:
757
+ del kwargs['duration']
758
+ if 'delay' in kwargs:
759
+ del kwargs['delay']
760
+ if 'easing' in kwargs:
761
+ del kwargs['delay']
762
+ if 'axis' in kwargs:
763
+ del kwargs['axis']
764
+ return kwargs
765
+
766
+ def check_transforms(self,x):
767
+ if self.mpl_obj_type is None:
768
+ return
769
+ for anim in self.anims:
770
+ if x < anim['delay']:
771
+ continue
772
+ _x = min(x,anim['duration'] + anim['delay']-1)
773
+
774
+ t = self.get_t_from_x(anim,_x)
775
+ if anim['name'] == 'scale':
776
+ start = to_np_array(anim['start'])
777
+ end = to_np_array(anim['end'])
778
+ self._scale(t,start,end,center=anim['center'])
779
+ if anim['name'] == 'translate':
780
+ start = to_np_array(anim['start'])
781
+ end = to_np_array(anim['end'])
782
+ self._translate(t,start,end)
783
+ if anim['name'] == 'rotate':
784
+ start = to_np_array(anim['start'])
785
+ end = to_np_array(anim['end'])
786
+ self._rotate(t,start,end,center=anim['center'])
787
+
788
+ def get_center(self):
789
+ if self.mpl_obj_type == mpl.lines.Line2D:
790
+ bbox = self.obj[0].get_bbox().get_points()
791
+ cx = (bbox[0][0] + bbox[1][0])/2
792
+ cy = (bbox[0][1] + bbox[1][1])/2
793
+ elif self.mpl_obj_type == mpl.patches.PathPatch:
794
+ bbox = self.path.get_extents()
795
+ cx = (bbox.x0 + bbox.x1) / 2
796
+ cy = (bbox.y0 + bbox.y1) / 2
797
+ elif self.mpl_obj_type == mpl.contour.QuadContourSet:
798
+ bboxes = [p.get_extents() for p in self.obj.get_paths()]
799
+ bbox = mpl.transforms.Bbox.union(bboxes)
800
+ cx = (bbox.x0 + bbox.x1) / 2
801
+ cy = (bbox.y0 + bbox.y1) / 2
802
+ elif self.mpl_obj_type == mpl.collections.Collection:
803
+ offsets = self.obj.get_offsets()
804
+ if len(offsets) == 0:
805
+ cx,cy = 0,0
806
+ else:
807
+ cx = np.mean(offsets[:, 0])
808
+ cy = np.mean(offsets[:, 1])
809
+ elif self.mpl_obj_type == mpl.collections.QuadMesh:
810
+ coords = self.obj.get_coordinates()
811
+ x0 = np.nanmin(coords[..., 0])
812
+ x1 = np.nanmax(coords[..., 0])
813
+ y0 = np.nanmin(coords[..., 1])
814
+ y1 = np.nanmax(coords[..., 1])
815
+
816
+ cx = 0.5 * (x0 + x1)
817
+ cy = 0.5 * (y0 + y1)
818
+ elif self.mpl_obj_type == mpl.patches.Rectangle:
819
+ rects = self.obj.patches
820
+ if isinstance(rects, list) and len(rects) > 0:
821
+ centers_x = []
822
+ centers_y = []
823
+ for rect in rects:
824
+ bbox = rect.get_bbox()
825
+ cx = (bbox.x0 + bbox.x1) / 2
826
+ cy = (bbox.y0 + bbox.y1) / 2
827
+ centers_x.append(cx)
828
+ centers_y.append(cy)
829
+ cx = np.mean(centers_x)
830
+ cy = np.mean(centers_y)
831
+ else:
832
+ bbox = self.obj.get_bbox()
833
+ cx = (bbox.x0 + bbox.x1) / 2
834
+ cy = (bbox.y0 + bbox.y1) / 2
835
+ elif self.mpl_obj_type == mpl.collections.FillBetweenPolyCollection:
836
+ bboxes = [p.get_extents() for p in self.obj.get_paths() if p.vertices.size]
837
+ bbox = mpl.transforms.Bbox.union(bboxes)
838
+ cx = 0.5 * (bbox.x0 + bbox.x1)
839
+ cy = 0.5 * (bbox.y0 + bbox.y1)
840
+ return (cx,cy)
841
+
842
+ class axis_zoom(Animation):
843
+ """
844
+ Zooms into the axis given a zoom value.
845
+
846
+ Parameters
847
+ ----------
848
+ zoom : float
849
+ Zoom level
850
+ duration : float
851
+ Duration of the animation
852
+ delay : float, default=0
853
+ Delay before starting
854
+ easing : callable, optional
855
+ Easing function
856
+ axis : matplotlib.axes.Axes, optional
857
+ Axis to plot on
858
+
859
+ Example
860
+ --------
861
+ Zoom 2 times in 100 frames:
862
+ >>> axis_zoom(zoom=2,duration=100)
863
+ """
864
+ def __init__(self,zoom,duration,delay=0,easing=None,axis=None,*args,**kwargs):
865
+ super().__init__(axis=axis,*args, **kwargs)
866
+ self.anims = [{
867
+ 'name':'axis_move',
868
+ 'duration':duration,
869
+ 'delay':delay,
870
+ 'easing':easing,
871
+ 'persistent':True,
872
+ 'zoom':zoom,
873
+ 'played':False
874
+ }]
875
+ self.compute_timings()
876
+
877
+ def init(self):
878
+ if 'start_width' not in self.anims[0]:
879
+ self.anims[0]['start_width'] = self.axis.get_xlim()[1] - self.axis.get_xlim()[0]
880
+ self.anims[0]['start_height'] = self.axis.get_ylim()[1] - self.axis.get_ylim()[0]
881
+ self.anims[0]['end_width'] = self.anims[0]['start_width']/self.anims[0]['zoom']
882
+ self.anims[0]['end_height'] = self.anims[0]['start_height']/self.anims[0]['zoom']
883
+
884
+ def function(self,data_x,data_y,x,kwargs):
885
+ anim = self.anims[0]
886
+ if x < anim['delay']:
887
+ return
888
+ _x = min(x,anim['duration'] + anim['delay']-1)
889
+ t = self.get_t_from_x(anim,x)
890
+
891
+ center = ((self.axis.get_xlim()[1]+self.axis.get_xlim()[0])/2,(self.axis.get_ylim()[1]+self.axis.get_ylim()[0])/2)
892
+ width = anim['start_width'] + (anim['end_width'] - anim['start_width'])*t
893
+ height = anim['start_height'] + (anim['end_height'] - anim['start_height'])*t
894
+ x_range = (center[0] - width/2, center[0] + width/2)
895
+ y_range = (center[1] - height/2, center[1] + height/2)
896
+ self.axis.set_xlim(x_range[0],x_range[1])
897
+ self.axis.set_ylim(y_range[0],y_range[1])
898
+
899
+ class axis_limits(Animation):
900
+ """
901
+ Reframe axis to the given limits (on the x-axis, y-axis or both depending on passed arguments)
902
+
903
+ Parameters
904
+ ----------
905
+ xlim : array-like, default=None
906
+ Target limits of the x-axis
907
+ ylim : array-like, default=None
908
+ Target limits of the y-axis
909
+ duration : float
910
+ Duration of the animation
911
+ delay : float, default=0
912
+ Delay before starting
913
+ easing : callable, optional
914
+ Easing function
915
+ axis : matplotlib.axes.Axes, optional
916
+ Axis to plot on
917
+
918
+ Example
919
+ --------
920
+ Reframe x-axis to limits [0-2] in 100 frames:
921
+ >>> axis_limits(xlim=(0,2),duration=100)
922
+ """
923
+ def __init__(self,duration,xlim=None,ylim=None,delay=0,easing=None,axis=None,*args,**kwargs):
924
+ if xlim is None and ylim is None:
925
+ raise ValueError('Both xlim and ylim cannot be empty.')
926
+ super().__init__(axis=axis,*args, **kwargs)
927
+ self.anims = [{
928
+ 'name':'axis_move',
929
+ 'duration':duration,
930
+ 'delay':delay,
931
+ 'easing':easing,
932
+ 'persistent':True,
933
+ 'xlim':xlim,
934
+ 'ylim':ylim,
935
+ 'played':False
936
+ }]
937
+ self.compute_timings()
938
+
939
+ def init(self):
940
+ if 'start_xlim' not in self.anims[0]:
941
+ self.anims[0]['start_xlim'] = self.axis.get_xlim()
942
+ self.anims[0]['start_ylim'] = self.axis.get_ylim()
943
+
944
+ def function(self,data_x,data_y,x,kwargs):
945
+ anim = self.anims[0]
946
+ if x < anim['delay']:
947
+ return
948
+ _x = min(x,anim['duration'] + anim['delay']-1)
949
+ t = self.get_t_from_x(anim,x)
950
+
951
+ if anim['xlim'] is not None:
952
+ xlim_left = anim['start_xlim'][0] + (anim['xlim'][0] - anim['start_xlim'][0])*t
953
+ xlim_right = anim['start_xlim'][1] + (anim['xlim'][1] - anim['start_xlim'][1])*t
954
+ self.axis.set_xlim(xlim_left,xlim_right)
955
+ if anim['ylim'] is not None:
956
+ ylim_bottom = anim['start_ylim'][0] + (anim['ylim'][0] - anim['start_ylim'][0])*t
957
+ ylim_top = anim['start_ylim'][1] + (anim['ylim'][1] - anim['start_ylim'][1])*t
958
+ self.axis.set_ylim(ylim_bottom,ylim_top)
959
+
960
+ class axis_move(Animation):
961
+ """
962
+ Move the center of the axis to a given position.
963
+
964
+ Parameters
965
+ ----------
966
+ pos : array-like
967
+ Target center position
968
+ duration : float
969
+ Duration of the animation
970
+ delay : float, default=0
971
+ Delay before starting
972
+ easing : callable, optional
973
+ Easing function
974
+ axis : matplotlib.axes.Axes, optional
975
+ Axis to plot on
976
+
977
+ Example
978
+ --------
979
+ Recenter the axis to (-1,-1) in 100 frames:
980
+ >>> axis_move(pos=(-1,-1),duration=100)
981
+ """
982
+ def __init__(self,end_pos,duration,start_pos=None,delay=0,easing=None,axis=None,*args,**kwargs):
983
+ super().__init__(axis=axis,*args, **kwargs)
984
+ self.anims = [{
985
+ 'name':'axis_move',
986
+ 'duration':duration,
987
+ 'delay':delay,
988
+ 'easing':easing,
989
+ 'persistent':True,
990
+ 'start':start_pos,
991
+ 'end':end_pos,
992
+ 'played':False
993
+ }]
994
+ self.compute_timings()
995
+
996
+ def init(self):
997
+ if self.anims[0]['start'] is None:
998
+ self.anims[0]['start'] = [
999
+ (self.axis.get_xlim()[0]+self.axis.get_xlim()[1])/2,
1000
+ (self.axis.get_ylim()[0]+self.axis.get_ylim()[1])/2,
1001
+ ]
1002
+
1003
+ def function(self,data_x,data_y,x,kwargs):
1004
+ anim = self.anims[0]
1005
+ if x < anim['delay']:
1006
+ return
1007
+ _x = min(x,anim['duration'] + anim['delay']-1)
1008
+ t = self.get_t_from_x(anim,x)
1009
+ width = self.axis.get_xlim()[1]-self.axis.get_xlim()[0]
1010
+ height = self.axis.get_ylim()[1]-self.axis.get_ylim()[0]
1011
+ pos_x = anim['start'][0] + (anim['end'][0] - anim['start'][0])*t
1012
+ pos_y = anim['start'][1] + (anim['end'][1] - anim['start'][1])*t
1013
+ self.axis.set_xlim(pos_x-width/2,pos_x+width/2)
1014
+ self.axis.set_ylim(pos_y-height/2,pos_y+height/2)
1015
+
1016
+ class axis_alpha(Animation):
1017
+ """
1018
+ Change the opacity of the axis.
1019
+
1020
+ Parameters
1021
+ ----------
1022
+ start_alpha : float
1023
+ Initial alpha value
1024
+ end_alpha : float
1025
+ Final alpha value
1026
+ duration : float
1027
+ Duration of the animation
1028
+ delay : float, default=0
1029
+ Delay before starting
1030
+ easing : callable, optional
1031
+ Easing function
1032
+ axis : matplotlib.axes.Axes, optional
1033
+ Axis to plot on
1034
+
1035
+ Example
1036
+ --------
1037
+ Make the axis appear in 100 frames
1038
+ >>> axis_alpha(start_alpha=0,end_alpha=1,duration=100)
1039
+ """
1040
+ def __init__(self,start_alpha,end_alpha,duration,delay=0,alpha_objs=True,easing=None,axis=None,*args,**kwargs):
1041
+ super().__init__(axis=axis,*args, **kwargs)
1042
+ self.anims = [{
1043
+ 'name':'axis_move',
1044
+ 'duration':duration,
1045
+ 'delay':delay,
1046
+ 'easing':easing,
1047
+ 'persistent':True,
1048
+ 'start':start_alpha,
1049
+ 'end':end_alpha,
1050
+ 'alpha_objs':alpha_objs,
1051
+ 'played':False
1052
+ }]
1053
+ self.compute_timings()
1054
+
1055
+ def init(self):
1056
+ pass
1057
+
1058
+ def function(self,data_x,data_y,x,kwargs):
1059
+ anim = self.anims[0]
1060
+ if x < anim['delay']:
1061
+ return
1062
+ _x = min(x,anim['duration'] + anim['delay']-1)
1063
+ t = self.get_t_from_x(anim,x)
1064
+ alpha = anim['start'] + (anim['end'] - anim['start'])*t
1065
+ alpha = np.clip(alpha,0,1)
1066
+
1067
+ self.axis.patch.set_alpha(alpha)
1068
+
1069
+ for spine in self.axis.spines.values():
1070
+ spine.set_alpha(alpha)
1071
+
1072
+ for tick in self.axis.xaxis.get_major_ticks() + self.axis.yaxis.get_major_ticks():
1073
+ for line in tick.tick1line, tick.tick2line:
1074
+ line.set_alpha(alpha)
1075
+ label = tick.label1
1076
+ label.set_alpha(alpha)
1077
+
1078
+ self.axis.xaxis.label.set_alpha(alpha)
1079
+ self.axis.yaxis.label.set_alpha(alpha)
1080
+
1081
+ self.axis.title.set_alpha(alpha)
1082
+
1083
+ if anim['alpha_objs']:
1084
+ for line in self.axis.get_xgridlines() + self.axis.get_ygridlines():
1085
+ line.set_alpha(alpha)
1086
+ for line in self.axis.lines:
1087
+ line.set_alpha(alpha)
1088
+ for col in self.axis.collections:
1089
+ col.set_alpha(alpha)
1090
+ for patch in self.axis.patches:
1091
+ patch.set_alpha(alpha)
1092
+ for img in self.axis.images:
1093
+ img.set_alpha(alpha)
1094
+ for txt in self.axis.texts:
1095
+ txt.set_alpha(alpha)
1096
+ leg = self.axis.get_legend()
1097
+ if leg:
1098
+ leg.get_frame().set_alpha(alpha)
1099
+ for txt in leg.get_texts():
1100
+ txt.set_alpha(alpha)
1101
+
1102
+ class fig_width_ratio(Animation):
1103
+ """
1104
+ Resize subplots' widths.
1105
+
1106
+ Parameters
1107
+ ----------
1108
+ start_widths : array-like
1109
+ Starting width ratios
1110
+ end_widths : array-like
1111
+ End width ratios
1112
+ duration : float
1113
+ Duration of the animation
1114
+ delay : float, default=0
1115
+ Delay before starting
1116
+ easing : callable, optional
1117
+ Easing function
1118
+ axis : matplotlib.axes.Axes, optional
1119
+ Axis to plot on
1120
+
1121
+ Example
1122
+ --------
1123
+ Resize 2 subplots from equal widths to a ratio of 2:1 in 100 frames:
1124
+ >>> fig_width_ratio(start_widths=(1,1),end_widths=(1,2),duration=100)
1125
+ """
1126
+ def __init__(self,start_widths,end_widths,duration,delay=0,easing=None,axis=None,*args,**kwargs):
1127
+ super().__init__(axis=axis,*args, **kwargs)
1128
+ self.grid = None
1129
+ start_widths = to_np_array(start_widths).astype(float)
1130
+ end_widths = to_np_array(end_widths).astype(float)
1131
+ start_widths = np.maximum(start_widths,1e-3)
1132
+ end_widths = np.maximum(end_widths,1e-3)
1133
+ #Normalize the ratios so scaling with time remains linear
1134
+ start_widths = start_widths/np.sum(start_widths)
1135
+ end_widths = end_widths/np.sum(end_widths)
1136
+ self.anims = [{
1137
+ 'name':'axis_move',
1138
+ 'duration':duration,
1139
+ 'delay':delay,
1140
+ 'easing':easing,
1141
+ 'persistent':True,
1142
+ 'start':start_widths,
1143
+ 'end':end_widths,
1144
+ 'played':False
1145
+ }]
1146
+ self.compute_timings()
1147
+
1148
+ def init(self):
1149
+ if self.tl.fig.get_layout_engine() is None:
1150
+ #Band-aid fix
1151
+ self.tl.fig.set_layout_engine('tight')
1152
+ if self.grid is None:
1153
+ self.grid = self.axis.get_gridspec()
1154
+
1155
+ def function(self,data_x,data_y,x,kwargs):
1156
+ anim = self.anims[0]
1157
+ if x < anim['delay']:
1158
+ return
1159
+ _x = min(x,anim['duration'] + anim['delay']-1)
1160
+ t = self.get_t_from_x(anim,x)
1161
+
1162
+ widths = anim['start'] + (anim['end'] - anim['start'])*t
1163
+
1164
+ self.grid.set_width_ratios(widths)
1165
+
1166
+ axes = to_np_array(self.tl.fig.get_axes())
1167
+ shape = self.tl.fig.axes[0].get_subplotspec().get_gridspec().get_geometry()
1168
+ axes = axes.reshape(shape)
1169
+ for i in range(len(widths)):
1170
+ if widths[i] < 1e-3:
1171
+ for row in axes:
1172
+ row[i].set_axis_off()
1173
+ else:
1174
+ for row in axes:
1175
+ if row[i].axison == False:
1176
+ row[i].set_axis_on()
1177
+
1178
+ class fig_height_ratio(Animation):
1179
+ """
1180
+ Resize subplots' heights.
1181
+
1182
+ Parameters
1183
+ ----------
1184
+ start_heights : array-like
1185
+ Starting height ratios
1186
+ end_heights : array-like
1187
+ End height ratios
1188
+ duration : float
1189
+ Duration of the animation
1190
+ delay : float, default=0
1191
+ Delay before starting
1192
+ easing : callable, optional
1193
+ Easing function
1194
+ axis : matplotlib.axes.Axes, optional
1195
+ Axis to plot on
1196
+
1197
+ Example
1198
+ --------
1199
+ Resize 2 subplots from equal heights to a ratio of 2:1 in 100 frames:
1200
+ >>> fig_width_ratio(start_heights=(1,1),end_heights=(1,2),duration=100)
1201
+ """
1202
+ def __init__(self,start_heights,end_heights,duration,delay=0,easing=None,axis=None,*args,**kwargs):
1203
+ super().__init__(axis=axis,*args, **kwargs)
1204
+ self.grid = None
1205
+ start_heights = to_np_array(start_heights)
1206
+ end_heights = to_np_array(end_heights)
1207
+ start_heights = np.maximum(start_heights,1e-3)
1208
+ end_heights = np.maximum(end_heights,1e-3)
1209
+ #Normalize the ratios so scaling with time remains linear
1210
+ start_heights = start_heights/np.sum(start_heights)
1211
+ end_heights = end_heights/np.sum(end_heights)
1212
+ self.anims = [{
1213
+ 'name':'axis_move',
1214
+ 'duration':duration,
1215
+ 'delay':delay,
1216
+ 'easing':easing,
1217
+ 'persistent':True,
1218
+ 'start':start_heights,
1219
+ 'end':end_heights,
1220
+ 'played':False
1221
+ }]
1222
+ self.compute_timings()
1223
+
1224
+ def init(self):
1225
+ if self.tl.fig.get_layout_engine() is None:
1226
+ #Band-aid fix
1227
+ self.tl.fig.set_layout_engine('tight')
1228
+ if self.grid is None:
1229
+ self.grid = self.axis.get_gridspec()
1230
+
1231
+ def function(self,data_x,data_y,x,kwargs):
1232
+ anim = self.anims[0]
1233
+ if x < anim['delay']:
1234
+ return
1235
+ _x = min(x,anim['duration'] + anim['delay']-1)
1236
+ t = self.get_t_from_x(anim,x)
1237
+
1238
+ heights = anim['start'] + (anim['end'] - anim['start'])*t
1239
+
1240
+ self.grid.set_height_ratios(heights)
1241
+
1242
+ axes = to_np_array(self.tl.fig.get_axes())
1243
+ shape = self.tl.fig.axes[0].get_subplotspec().get_gridspec().get_geometry()
1244
+ axes = axes.reshape(shape)
1245
+ for i in range(len(heights)):
1246
+ if heights[i] < 1e-3:
1247
+ for column in axes.T:
1248
+ column[i].set_axis_off()
1249
+ else:
1250
+ for column in axes.T:
1251
+ if column[i].axison == False:
1252
+ column[i].set_axis_on()
1253
+
1254
+ class scatter(Animation):
1255
+ """
1256
+ A scatter plot of *y* vs. *x* with varying marker size and/or color.
1257
+
1258
+ Parameters
1259
+ ----------
1260
+ x, y : float or array-like, shape (n, )
1261
+ The data positions.
1262
+
1263
+ duration : float
1264
+ Duration of the animation
1265
+ delay : float, default=0
1266
+ Delay before starting
1267
+ easing : callable, optional
1268
+ Easing function
1269
+ axis : matplotlib.axes.Axes, optional
1270
+ Axis to plot on
1271
+
1272
+
1273
+ s : float or array-like, shape (n, ), optional
1274
+ The marker size in points**2 (typographic points are 1/72 in.).
1275
+ Default is ``rcParams['lines.markersize'] ** 2``.
1276
+
1277
+ The linewidth and edgecolor can visually interact with the marker
1278
+ size, and can lead to artifacts if the marker size is smaller than
1279
+ the linewidth.
1280
+
1281
+ If the linewidth is greater than 0 and the edgecolor is anything
1282
+ but *'none'*, then the effective size of the marker will be
1283
+ increased by half the linewidth because the stroke will be centered
1284
+ on the edge of the shape.
1285
+
1286
+ To eliminate the marker edge either set *linewidth=0* or
1287
+ *edgecolor='none'*.
1288
+
1289
+ c : array-like or list of color or color, optional
1290
+ The marker colors. Possible values:
1291
+
1292
+ - A scalar or sequence of n numbers to be mapped to colors using
1293
+ *cmap* and *norm*.
1294
+ - A 2D array in which the rows are RGB or RGBA.
1295
+ - A sequence of colors of length n.
1296
+ - A single color format string.
1297
+
1298
+ Note that *c* should not be a single numeric RGB or RGBA sequence
1299
+ because that is indistinguishable from an array of values to be
1300
+ colormapped. If you want to specify the same RGB or RGBA value for
1301
+ all points, use a 2D array with a single row. Otherwise,
1302
+ value-matching will have precedence in case of a size matching with
1303
+ *x* and *y*.
1304
+
1305
+ If you wish to specify a single color for all points
1306
+ prefer the *color* keyword argument.
1307
+
1308
+ Defaults to `None`. In that case the marker color is determined
1309
+ by the value of *color*, *facecolor* or *facecolors*. In case
1310
+ those are not specified or `None`, the marker color is determined
1311
+ by the next color of the ``Axes``' current "shape and fill" color
1312
+ cycle. This cycle defaults to axes.prop_cycle.
1313
+
1314
+ marker : `~.markers.MarkerStyle`, default: scatter.marker
1315
+ The marker style. *marker* can be either an instance of the class
1316
+ or the text shorthand for a particular marker.
1317
+ See :mod:`matplotlib.markers` for more information about marker
1318
+ styles.
1319
+
1320
+ cmap : str or `~matplotlib.colors.Colormap`, default: image.cmap
1321
+ The Colormap instance or registered colormap name used to map scalar data
1322
+ to colors.
1323
+
1324
+ This parameter is ignored if *c* is RGB(A).
1325
+
1326
+ norm : str or `~matplotlib.colors.Normalize`, optional
1327
+ The normalization method used to scale scalar data to the [0, 1] range
1328
+ before mapping to colors using *cmap*. By default, a linear scaling is
1329
+ used, mapping the lowest value to 0 and the highest to 1.
1330
+
1331
+ If given, this can be one of the following:
1332
+
1333
+ - An instance of `.Normalize` or one of its subclasses
1334
+ (see :ref:`colormapnorms`).
1335
+ - A scale name, i.e. one of "linear", "log", "symlog", "logit", etc. For a
1336
+ list of available scales, call `matplotlib.scale.get_scale_names()`.
1337
+ In that case, a suitable `.Normalize` subclass is dynamically generated
1338
+ and instantiated.
1339
+
1340
+ This parameter is ignored if *c* is RGB(A).
1341
+
1342
+ vmin, vmax : float, optional
1343
+ When using scalar data and no explicit *norm*, *vmin* and *vmax* define
1344
+ the data range that the colormap covers. By default, the colormap covers
1345
+ the complete value range of the supplied data. It is an error to use
1346
+ *vmin*/*vmax* when a *norm* instance is given (but using a `str` *norm*
1347
+ name together with *vmin*/*vmax* is acceptable).
1348
+
1349
+ This parameter is ignored if *c* is RGB(A).
1350
+
1351
+ alpha : float, default: None
1352
+ The alpha blending value, between 0 (transparent) and 1 (opaque).
1353
+
1354
+ linewidths : float or array-like, default: lines.linewidth
1355
+ The linewidth of the marker edges. Note: The default *edgecolors*
1356
+ is 'face'. You may want to change this as well.
1357
+
1358
+ edgecolors : {'face', 'none', *None*} or color or list of color, default: scatter.edgecolors
1359
+ The edge color of the marker. Possible values:
1360
+
1361
+ - 'face': The edge color will always be the same as the face color.
1362
+ - 'none': No patch boundary will be drawn.
1363
+ - A color or sequence of colors.
1364
+
1365
+ For non-filled markers, *edgecolors* is ignored. Instead, the color
1366
+ is determined like with 'face', i.e. from *c*, *colors*, or
1367
+ *facecolors*.
1368
+
1369
+ colorizer : `~matplotlib.colorizer.Colorizer` or None, default: None
1370
+ The Colorizer object used to map color to data. If None, a Colorizer
1371
+ object is created from a *norm* and *cmap*.
1372
+
1373
+ This parameter is ignored if *c* is RGB(A).
1374
+
1375
+ plotnonfinite : bool, default: False
1376
+ Whether to plot points with nonfinite *c* (i.e. ``inf``, ``-inf``
1377
+ or ``nan``). If ``True`` the points are drawn with the *bad*
1378
+ colormap color (see `.Colormap.set_bad`).
1379
+
1380
+ Returns
1381
+ -------
1382
+ `~matplotlib.collections.PathCollection`
1383
+
1384
+ Other Parameters
1385
+ ----------------
1386
+ data : indexable object, optional
1387
+ If given, the following parameters also accept a string ``s``, which is
1388
+ interpreted as ``data[s]`` if ``s`` is a key in ``data``:
1389
+
1390
+ *x*, *y*, *s*, *linewidths*, *edgecolors*, *c*, *facecolor*, *facecolors*, *color*
1391
+ **kwargs : `~matplotlib.collections.PathCollection` properties
1392
+ Properties:
1393
+ agg_filter: a filter function, which takes a (m, n, 3) float array and a dpi value, and returns a (m, n, 3) array and two offsets from the bottom left corner of the image
1394
+ alpha: array-like or float or None
1395
+ animated: bool
1396
+ antialiased or aa or antialiaseds: bool or list of bools
1397
+ array: array-like or None
1398
+ capstyle: `.CapStyle` or {'butt', 'projecting', 'round'}
1399
+ clim: (vmin: float, vmax: float)
1400
+ clip_box: `~matplotlib.transforms.BboxBase` or None
1401
+ clip_on: bool
1402
+ clip_path: Patch or (Path, Transform) or None
1403
+ cmap: `.Colormap` or str or None
1404
+ color: color or list of RGBA tuples
1405
+ edgecolor or ec or edgecolors: color or list of color or 'face'
1406
+ facecolor or facecolors or fc: color or list of color
1407
+ figure: `~matplotlib.figure.Figure` or `~matplotlib.figure.SubFigure`
1408
+ gid: str
1409
+ hatch: {'/', '\\', '|', '-', '+', 'x', 'o', 'O', '.', '*'}
1410
+ hatch_linewidth: unknown
1411
+ in_layout: bool
1412
+ joinstyle: `.JoinStyle` or {'miter', 'round', 'bevel'}
1413
+ label: object
1414
+ linestyle or dashes or linestyles or ls: str or tuple or list thereof
1415
+ linewidth or linewidths or lw: float or list of floats
1416
+ mouseover: bool
1417
+ norm: `.Normalize` or str or None
1418
+ offset_transform or transOffset: `.Transform`
1419
+ offsets: (N, 2) or (2,) array-like
1420
+ path_effects: list of `.AbstractPathEffect`
1421
+ paths: unknown
1422
+ picker: None or bool or float or callable
1423
+ pickradius: float
1424
+ rasterized: bool
1425
+ sizes: `numpy.ndarray` or None
1426
+ sketch_params: (scale: float, length: float, randomness: float)
1427
+ snap: bool or None
1428
+ transform: `~matplotlib.transforms.Transform`
1429
+ url: str
1430
+ urls: list of str or None
1431
+ visible: bool
1432
+ zorder: float
1433
+
1434
+ See Also
1435
+ --------
1436
+ plot : To plot scatter plots when markers are identical in size and
1437
+ color.
1438
+
1439
+ Notes
1440
+ -----
1441
+
1442
+ .. note::
1443
+
1444
+ This is the :ref:`pyplot wrapper <pyplot_interface>` for `.axes.Axes.scatter`.
1445
+
1446
+ * The `.plot` function will be faster for scatterplots where markers
1447
+ don't vary in size or color.
1448
+
1449
+ * Any or all of *x*, *y*, *s*, and *c* may be masked arrays, in which
1450
+ case all masks will be combined and only unmasked points will be
1451
+ plotted.
1452
+
1453
+ * Fundamentally, scatter works with 1D arrays; *x*, *y*, *s*, and *c*
1454
+ may be input as N-D arrays, but within scatter they will be
1455
+ flattened. The exception is *c*, which will be flattened only if its
1456
+ size matches the size of *x* and *y*.
1457
+ """
1458
+ def __init__(self,x,y,easing=None,axis=None,*args,**kwargs):
1459
+ self.mpl_obj_type = mpl.collections.Collection
1460
+ self.mpl_plot_type = plt.plot
1461
+ super().__init__(easing=easing,axis=axis,*args, **kwargs)
1462
+
1463
+ self.x = to_np_array(x)
1464
+ self.y = to_np_array(y)
1465
+ if self.x.size != self.y.size:
1466
+ raise ValueError("x and y must be the same size")
1467
+
1468
+ def clean(self,x,clear_anims=True):
1469
+ if self.obj is not None and self.base_color is None:
1470
+ self.base_color = self.obj.get_edgecolors()[0]
1471
+ if 'color' not in self.kwargs and 'c' not in self.kwargs:
1472
+ self.kwargs['color'] = self.base_color
1473
+ super().clean(x,clear_anims)
1474
+
1475
+ def function(self,data_x,data_y,x,kwargs):
1476
+ if 'c' in kwargs and 'color' in kwargs:
1477
+ kwargs.pop('color')
1478
+ if 'c' in kwargs:
1479
+ c = to_np_array(kwargs['c'])
1480
+ if c.ndim == 1 and (len(c) == 3 or len(c) == 4) and len(c) != len(data_x) and (c <= 1).all():
1481
+ kwargs['c'] = c.reshape(1,-1)
1482
+ self.obj = self.axis.scatter(data_x,data_y,**kwargs)
1483
+
1484
+ class plot(Animation):
1485
+ """
1486
+ Plot y versus x as lines and/or markers.
1487
+
1488
+ Call signatures::
1489
+
1490
+ plot([x], y, [fmt], *, data=None, **kwargs)
1491
+ plot([x], y, [fmt], [x2], y2, [fmt2], ..., **kwargs)
1492
+
1493
+ The coordinates of the points or line nodes are given by *x*, *y*.
1494
+
1495
+ The optional parameter *fmt* is a convenient way for defining basic
1496
+ formatting like color, marker and linestyle. It's a shortcut string
1497
+ notation described in the *Notes* section below.
1498
+
1499
+ >>> plot(x, y) # plot x and y using default line style and color
1500
+ >>> plot(x, y, 'bo') # plot x and y using blue circle markers
1501
+ >>> plot(y) # plot y using x as index array 0..N-1
1502
+ >>> plot(y, 'r+') # ditto, but with red plusses
1503
+
1504
+ You can use `.Line2D` properties as keyword arguments for more
1505
+ control on the appearance. Line properties and *fmt* can be mixed.
1506
+ The following two calls yield identical results:
1507
+
1508
+ >>> plot(x, y, 'go--', linewidth=2, markersize=12)
1509
+ >>> plot(x, y, color='green', marker='o', linestyle='dashed',
1510
+ ... linewidth=2, markersize=12)
1511
+
1512
+ When conflicting with *fmt*, keyword arguments take precedence.
1513
+
1514
+
1515
+ **Plotting labelled data**
1516
+
1517
+ There's a convenient way for plotting objects with labelled data (i.e.
1518
+ data that can be accessed by index ``obj['y']``). Instead of giving
1519
+ the data in *x* and *y*, you can provide the object in the *data*
1520
+ parameter and just give the labels for *x* and *y*::
1521
+
1522
+ >>> plot('xlabel', 'ylabel', data=obj)
1523
+
1524
+ All indexable objects are supported. This could e.g. be a `dict`, a
1525
+ `pandas.DataFrame` or a structured numpy array.
1526
+
1527
+
1528
+ **Plotting multiple sets of data**
1529
+
1530
+ There are various ways to plot multiple sets of data.
1531
+
1532
+ - The most straight forward way is just to call `plot` multiple times.
1533
+ Example:
1534
+
1535
+ >>> plot(x1, y1, 'bo')
1536
+ >>> plot(x2, y2, 'go')
1537
+
1538
+ - If *x* and/or *y* are 2D arrays, a separate data set will be drawn
1539
+ for every column. If both *x* and *y* are 2D, they must have the
1540
+ same shape. If only one of them is 2D with shape (N, m) the other
1541
+ must have length N and will be used for every data set m.
1542
+
1543
+ Example:
1544
+
1545
+ >>> x = [1, 2, 3]
1546
+ >>> y = np.array([[1, 2], [3, 4], [5, 6]])
1547
+ >>> plot(x, y)
1548
+
1549
+ is equivalent to:
1550
+
1551
+ >>> for col in range(y.shape[1]):
1552
+ ... plot(x, y[:, col])
1553
+
1554
+ - The third way is to specify multiple sets of *[x]*, *y*, *[fmt]*
1555
+ groups::
1556
+
1557
+ >>> plot(x1, y1, 'g^', x2, y2, 'g-')
1558
+
1559
+ In this case, any additional keyword argument applies to all
1560
+ datasets. Also, this syntax cannot be combined with the *data*
1561
+ parameter.
1562
+
1563
+ By default, each line is assigned a different style specified by a
1564
+ 'style cycle'. The *fmt* and line property parameters are only
1565
+ necessary if you want explicit deviations from these defaults.
1566
+ Alternatively, you can also change the style cycle using
1567
+ axes.prop_cycle.
1568
+
1569
+
1570
+ Parameters
1571
+ ----------
1572
+ x, y : array-like or float
1573
+ The horizontal / vertical coordinates of the data points.
1574
+ *x* values are optional and default to ``range(len(y))``.
1575
+
1576
+ duration : float
1577
+ Duration of the animation
1578
+ delay : float, default=0
1579
+ Delay before starting
1580
+ easing : callable, optional
1581
+ Easing function
1582
+ axis : matplotlib.axes.Axes, optional
1583
+ Axis to plot on
1584
+
1585
+
1586
+ Commonly, these parameters are 1D arrays.
1587
+
1588
+ They can also be scalars, or two-dimensional (in that case, the
1589
+ columns represent separate data sets).
1590
+
1591
+ These arguments cannot be passed as keywords.
1592
+
1593
+ fmt : str, optional
1594
+ A format string, e.g. 'ro' for red circles. See the *Notes*
1595
+ section for a full description of the format strings.
1596
+
1597
+ Format strings are just an abbreviation for quickly setting
1598
+ basic line properties. All of these and more can also be
1599
+ controlled by keyword arguments.
1600
+
1601
+ This argument cannot be passed as keyword.
1602
+
1603
+ data : indexable object, optional
1604
+ An object with labelled data. If given, provide the label names to
1605
+ plot in *x* and *y*.
1606
+
1607
+ .. note::
1608
+ Technically there's a slight ambiguity in calls where the
1609
+ second label is a valid *fmt*. ``plot('n', 'o', data=obj)``
1610
+ could be ``plt(x, y)`` or ``plt(y, fmt)``. In such cases,
1611
+ the former interpretation is chosen, but a warning is issued.
1612
+ You may suppress the warning by adding an empty format string
1613
+ ``plot('n', 'o', '', data=obj)``.
1614
+
1615
+ Returns
1616
+ -------
1617
+ list of `.Line2D`
1618
+ A list of lines representing the plotted data.
1619
+
1620
+ Other Parameters
1621
+ ----------------
1622
+ scalex, scaley : bool, default: True
1623
+ These parameters determine if the view limits are adapted to the
1624
+ data limits. The values are passed on to
1625
+ `~.axes.Axes.autoscale_view`.
1626
+
1627
+ **kwargs : `~matplotlib.lines.Line2D` properties, optional
1628
+ *kwargs* are used to specify properties like a line label (for
1629
+ auto legends), linewidth, antialiasing, marker face color.
1630
+ Example::
1631
+
1632
+ >>> plot([1, 2, 3], [1, 2, 3], 'go-', label='line 1', linewidth=2)
1633
+ >>> plot([1, 2, 3], [1, 4, 9], 'rs', label='line 2')
1634
+
1635
+ If you specify multiple lines with one plot call, the kwargs apply
1636
+ to all those lines. In case the label object is iterable, each
1637
+ element is used as labels for each set of data.
1638
+
1639
+ Here is a list of available `.Line2D` properties:
1640
+
1641
+ Properties:
1642
+ agg_filter: a filter function, which takes a (m, n, 3) float array and a dpi value, and returns a (m, n, 3) array and two offsets from the bottom left corner of the image
1643
+ alpha: float or None
1644
+ animated: bool
1645
+ antialiased or aa: bool
1646
+ clip_box: `~matplotlib.transforms.BboxBase` or None
1647
+ clip_on: bool
1648
+ clip_path: Patch or (Path, Transform) or None
1649
+ color or c: color
1650
+ dash_capstyle: `.CapStyle` or {'butt', 'projecting', 'round'}
1651
+ dash_joinstyle: `.JoinStyle` or {'miter', 'round', 'bevel'}
1652
+ dashes: sequence of floats (on/off ink in points) or (None, None)
1653
+ data: (2, N) array or two 1D arrays
1654
+ drawstyle or ds: {'default', 'steps', 'steps-pre', 'steps-mid', 'steps-post'}, default: 'default'
1655
+ figure: `~matplotlib.figure.Figure` or `~matplotlib.figure.SubFigure`
1656
+ fillstyle: {'full', 'left', 'right', 'bottom', 'top', 'none'}
1657
+ gapcolor: color or None
1658
+ gid: str
1659
+ in_layout: bool
1660
+ label: object
1661
+ linestyle or ls: {'-', '--', '-.', ':', '', (offset, on-off-seq), ...}
1662
+ linewidth or lw: float
1663
+ marker: marker style string, `~.path.Path` or `~.markers.MarkerStyle`
1664
+ markeredgecolor or mec: color
1665
+ markeredgewidth or mew: float
1666
+ markerfacecolor or mfc: color
1667
+ markerfacecoloralt or mfcalt: color
1668
+ markersize or ms: float
1669
+ markevery: None or int or (int, int) or slice or list[int] or float or (float, float) or list[bool]
1670
+ mouseover: bool
1671
+ path_effects: list of `.AbstractPathEffect`
1672
+ picker: float or callable[[Artist, Event], tuple[bool, dict]]
1673
+ pickradius: float
1674
+ rasterized: bool
1675
+ sketch_params: (scale: float, length: float, randomness: float)
1676
+ snap: bool or None
1677
+ solid_capstyle: `.CapStyle` or {'butt', 'projecting', 'round'}
1678
+ solid_joinstyle: `.JoinStyle` or {'miter', 'round', 'bevel'}
1679
+ transform: unknown
1680
+ url: str
1681
+ visible: bool
1682
+ xdata: 1D array
1683
+ ydata: 1D array
1684
+ zorder: float
1685
+
1686
+ See Also
1687
+ --------
1688
+ scatter : XY scatter plot with markers of varying size and/or color (
1689
+ sometimes also called bubble chart).
1690
+
1691
+ Notes
1692
+ -----
1693
+
1694
+ .. note::
1695
+
1696
+ This is the :ref:`pyplot wrapper <pyplot_interface>` for `.axes.Axes.plot`.
1697
+
1698
+ **Format Strings**
1699
+
1700
+ A format string consists of a part for color, marker and line::
1701
+
1702
+ fmt = '[marker][line][color]'
1703
+
1704
+ Each of them is optional. If not provided, the value from the style
1705
+ cycle is used. Exception: If ``line`` is given, but no ``marker``,
1706
+ the data will be a line without markers.
1707
+
1708
+ Other combinations such as ``[color][marker][line]`` are also
1709
+ supported, but note that their parsing may be ambiguous.
1710
+
1711
+ **Markers**
1712
+
1713
+ ============= ===============================
1714
+ character description
1715
+ ============= ===============================
1716
+ ``'.'`` point marker
1717
+ ``','`` pixel marker
1718
+ ``'o'`` circle marker
1719
+ ``'v'`` triangle_down marker
1720
+ ``'^'`` triangle_up marker
1721
+ ``'<'`` triangle_left marker
1722
+ ``'>'`` triangle_right marker
1723
+ ``'1'`` tri_down marker
1724
+ ``'2'`` tri_up marker
1725
+ ``'3'`` tri_left marker
1726
+ ``'4'`` tri_right marker
1727
+ ``'8'`` octagon marker
1728
+ ``'s'`` square marker
1729
+ ``'p'`` pentagon marker
1730
+ ``'P'`` plus (filled) marker
1731
+ ``'*'`` star marker
1732
+ ``'h'`` hexagon1 marker
1733
+ ``'H'`` hexagon2 marker
1734
+ ``'+'`` plus marker
1735
+ ``'x'`` x marker
1736
+ ``'X'`` x (filled) marker
1737
+ ``'D'`` diamond marker
1738
+ ``'d'`` thin_diamond marker
1739
+ ``'|'`` vline marker
1740
+ ``'_'`` hline marker
1741
+ ============= ===============================
1742
+
1743
+ **Line Styles**
1744
+
1745
+ ============= ===============================
1746
+ character description
1747
+ ============= ===============================
1748
+ ``'-'`` solid line style
1749
+ ``'--'`` dashed line style
1750
+ ``'-.'`` dash-dot line style
1751
+ ``':'`` dotted line style
1752
+ ============= ===============================
1753
+
1754
+ Example format strings::
1755
+
1756
+ 'b' # blue markers with default shape
1757
+ 'or' # red circles
1758
+ '-g' # green solid line
1759
+ '--' # dashed line with default color
1760
+ '^k:' # black triangle_up markers connected by a dotted line
1761
+
1762
+ **Colors**
1763
+
1764
+ The supported color abbreviations are the single letter codes
1765
+
1766
+ ============= ===============================
1767
+ character color
1768
+ ============= ===============================
1769
+ ``'b'`` blue
1770
+ ``'g'`` green
1771
+ ``'r'`` red
1772
+ ``'c'`` cyan
1773
+ ``'m'`` magenta
1774
+ ``'y'`` yellow
1775
+ ``'k'`` black
1776
+ ``'w'`` white
1777
+ ============= ===============================
1778
+
1779
+ and the ``'CN'`` colors that index into the default property cycle.
1780
+
1781
+ If the color is the only part of the format string, you can
1782
+ additionally use any `matplotlib.colors` spec, e.g. full names
1783
+ (``'green'``) or hex strings (``'#008000'``).
1784
+ """
1785
+ def __init__(self,x,y,easing=None,axis=None,*args,**kwargs):
1786
+ self.mpl_obj_type = mpl.lines.Line2D
1787
+ self.mpl_plot_type = plt.plot
1788
+ super().__init__(easing=easing,axis=axis,*args, **kwargs)
1789
+
1790
+ self.x = to_np_array(x)
1791
+ self.y = to_np_array(y)
1792
+ if self.x.size != self.y.size:
1793
+ raise ValueError("x and y must be the same size")
1794
+
1795
+ def function(self,data_x,data_y,x,kwargs):
1796
+ if isinstance(kwargs['alpha'],np.ndarray):
1797
+ kwargs['alpha'] = kwargs['alpha'][0]
1798
+ self.obj = self.axis.plot(data_x,data_y,**kwargs)
1799
+
1800
+ class step(Animation):
1801
+ """
1802
+ Make a step plot.
1803
+
1804
+ Call signatures::
1805
+
1806
+ step(x, y, [fmt], *, data=None, where='pre', **kwargs)
1807
+ step(x, y, [fmt], x2, y2, [fmt2], ..., *, where='pre', **kwargs)
1808
+
1809
+ This is just a thin wrapper around `.plot` which changes some
1810
+ formatting options. Most of the concepts and parameters of plot can be
1811
+ used here as well.
1812
+
1813
+ .. note::
1814
+
1815
+ This method uses a standard plot with a step drawstyle: The *x*
1816
+ values are the reference positions and steps extend left/right/both
1817
+ directions depending on *where*.
1818
+
1819
+ For the common case where you know the values and edges of the
1820
+ steps, use `~.Axes.stairs` instead.
1821
+
1822
+ Parameters
1823
+ ----------
1824
+ x : array-like
1825
+ 1D sequence of x positions. It is assumed, but not checked, that
1826
+ it is uniformly increasing.
1827
+
1828
+ y : array-like
1829
+ 1D sequence of y levels.
1830
+
1831
+ fmt : str, optional
1832
+ A format string, e.g. 'g' for a green line. See `.plot` for a more
1833
+ detailed description.
1834
+
1835
+ Note: While full format strings are accepted, it is recommended to
1836
+ only specify the color. Line styles are currently ignored (use
1837
+ the keyword argument *linestyle* instead). Markers are accepted
1838
+ and plotted on the given positions, however, this is a rarely
1839
+ needed feature for step plots.
1840
+
1841
+ where : {'pre', 'post', 'mid'}, default: 'pre'
1842
+ Define where the steps should be placed:
1843
+
1844
+ - 'pre': The y value is continued constantly to the left from
1845
+ every *x* position, i.e. the interval ``(x[i-1], x[i]]`` has the
1846
+ value ``y[i]``.
1847
+ - 'post': The y value is continued constantly to the right from
1848
+ every *x* position, i.e. the interval ``[x[i], x[i+1])`` has the
1849
+ value ``y[i]``.
1850
+ - 'mid': Steps occur half-way between the *x* positions.
1851
+
1852
+ data : indexable object, optional
1853
+ An object with labelled data. If given, provide the label names to
1854
+ plot in *x* and *y*.
1855
+
1856
+ **kwargs
1857
+ Additional parameters are the same as those for `.plot`.
1858
+
1859
+ Returns
1860
+ -------
1861
+ list of `.Line2D`
1862
+ Objects representing the plotted data.
1863
+ """
1864
+ def __init__(self,x,y,easing=None,axis=None,*args, **kwargs):
1865
+ self.mpl_obj_type = mpl.lines.Line2D
1866
+ self.mpl_plot_type = plt.plot
1867
+ super().__init__(easing=easing,axis=axis,*args, **kwargs)
1868
+
1869
+ self.x = to_np_array(x)
1870
+ self.y = to_np_array(y)
1871
+ if self.x.size != self.y.size:
1872
+ raise ValueError("x and y must be the same size")
1873
+
1874
+ def function(self,data_x,data_y,x,kwargs):
1875
+ if isinstance(kwargs['alpha'],np.ndarray):
1876
+ kwargs['alpha'] = kwargs['alpha'][0]
1877
+
1878
+ self.obj = self.axis.step(data_x,data_y,**kwargs)
1879
+
1880
+ class fill_between(Animation):
1881
+ """
1882
+ Fill the area between two horizontal curves.
1883
+
1884
+ The curves are defined by the points (*x*, *y1*) and (*x*,
1885
+ *y2*). This creates one or multiple polygons describing the filled
1886
+ area.
1887
+
1888
+ You may exclude some horizontal sections from filling using *where*.
1889
+
1890
+ By default, the edges connect the given points directly. Use *step*
1891
+ if the filling should be a step function, i.e. constant in between
1892
+ *x*.
1893
+
1894
+ Parameters
1895
+ ----------
1896
+ x : array-like
1897
+ The x coordinates of the nodes defining the curves.
1898
+
1899
+ duration : float
1900
+ Duration of the animation
1901
+ delay : float, default=0
1902
+ Delay before starting
1903
+ easing : callable, optional
1904
+ Easing function
1905
+ axis : matplotlib.axes.Axes, optional
1906
+ Axis to plot on
1907
+
1908
+
1909
+ y1 : array-like or float
1910
+ The y coordinates of the nodes defining the first curve.
1911
+
1912
+ y2 : array-like or float, default: 0
1913
+ The y coordinates of the nodes defining the second curve.
1914
+
1915
+ where : array-like of bool, optional
1916
+ Define *where* to exclude some horizontal regions from being filled.
1917
+ The filled regions are defined by the coordinates ``x[where]``.
1918
+ More precisely, fill between ``x[i]`` and ``x[i+1]`` if
1919
+ ``where[i] and where[i+1]``. Note that this definition implies
1920
+ that an isolated *True* value between two *False* values in *where*
1921
+ will not result in filling. Both sides of the *True* position
1922
+ remain unfilled due to the adjacent *False* values.
1923
+
1924
+ interpolate : bool, default: False
1925
+ This option is only relevant if *where* is used and the two curves
1926
+ are crossing each other.
1927
+
1928
+ Semantically, *where* is often used for *y1* > *y2* or
1929
+ similar. By default, the nodes of the polygon defining the filled
1930
+ region will only be placed at the positions in the *x* array.
1931
+ Such a polygon cannot describe the above semantics close to the
1932
+ intersection. The x-sections containing the intersection are
1933
+ simply clipped.
1934
+
1935
+ Setting *interpolate* to *True* will calculate the actual
1936
+ intersection point and extend the filled region up to this point.
1937
+
1938
+ step : {'pre', 'post', 'mid'}, optional
1939
+ Define *step* if the filling should be a step function,
1940
+ i.e. constant in between *x*. The value determines where the
1941
+ step will occur:
1942
+
1943
+ - 'pre': The y value is continued constantly to the left from
1944
+ every *x* position, i.e. the interval ``(x[i-1], x[i]]``
1945
+ has the value ``y[i]``.
1946
+ - 'post': The y value is continued constantly to the right from
1947
+ every *x* position, i.e. the interval ``[x[i], x[i+1])``
1948
+ has the value ``y[i]``.
1949
+ - 'mid': Steps occur half-way between the *x* positions.
1950
+
1951
+ Returns
1952
+ -------
1953
+ `.FillBetweenPolyCollection`
1954
+ A `.FillBetweenPolyCollection` containing the plotted polygons.
1955
+
1956
+ Other Parameters
1957
+ ----------------
1958
+ data : indexable object, optional
1959
+ If given, the following parameters also accept a string ``s``, which is
1960
+ interpreted as ``data[s]`` if ``s`` is a key in ``data``:
1961
+
1962
+ *x*, *y1*, *y2*, *where*
1963
+
1964
+ **kwargs
1965
+ All other keyword arguments are passed on to
1966
+ `.FillBetweenPolyCollection`. They control the `.Polygon` properties:
1967
+
1968
+ Properties:
1969
+ agg_filter: a filter function, which takes a (m, n, 3) float array and a dpi value, and returns a (m, n, 3) array and two offsets from the bottom left corner of the image
1970
+ alpha: array-like or float or None
1971
+ animated: bool
1972
+ antialiased or aa or antialiaseds: bool or list of bools
1973
+ array: array-like or None
1974
+ capstyle: `.CapStyle` or {'butt', 'projecting', 'round'}
1975
+ clim: (vmin: float, vmax: float)
1976
+ clip_box: `~matplotlib.transforms.BboxBase` or None
1977
+ clip_on: bool
1978
+ clip_path: Patch or (Path, Transform) or None
1979
+ cmap: `.Colormap` or str or None
1980
+ color: color or list of RGBA tuples
1981
+ data: array-like
1982
+ edgecolor or ec or edgecolors: color or list of color or 'face'
1983
+ facecolor or facecolors or fc: color or list of color
1984
+ figure: `~matplotlib.figure.Figure` or `~matplotlib.figure.SubFigure`
1985
+ gid: str
1986
+ hatch: {'/', '\\', '|', '-', '+', 'x', 'o', 'O', '.', '*'}
1987
+ hatch_linewidth: unknown
1988
+ in_layout: bool
1989
+ joinstyle: `.JoinStyle` or {'miter', 'round', 'bevel'}
1990
+ label: object
1991
+ linestyle or dashes or linestyles or ls: str or tuple or list thereof
1992
+ linewidth or linewidths or lw: float or list of floats
1993
+ mouseover: bool
1994
+ norm: `.Normalize` or str or None
1995
+ offset_transform or transOffset: `.Transform`
1996
+ offsets: (N, 2) or (2,) array-like
1997
+ path_effects: list of `.AbstractPathEffect`
1998
+ paths: list of array-like
1999
+ picker: None or bool or float or callable
2000
+ pickradius: float
2001
+ rasterized: bool
2002
+ sizes: `numpy.ndarray` or None
2003
+ sketch_params: (scale: float, length: float, randomness: float)
2004
+ snap: bool or None
2005
+ transform: `~matplotlib.transforms.Transform`
2006
+ url: str
2007
+ urls: list of str or None
2008
+ verts: list of array-like
2009
+ verts_and_codes: unknown
2010
+ visible: bool
2011
+ zorder: float
2012
+
2013
+ See Also
2014
+ --------
2015
+ fill_between : Fill between two sets of y-values.
2016
+ fill_betweenx : Fill between two sets of x-values.
2017
+
2018
+ Notes
2019
+ -----
2020
+
2021
+ .. note::
2022
+
2023
+ This is the :ref:`pyplot wrapper <pyplot_interface>` for `.axes.Axes.fill_between`.
2024
+ """
2025
+ def __init__(self,x,y1,y2,easing=None,axis=None, *args, **kwargs):
2026
+ self.mpl_obj_type = mpl.collections.FillBetweenPolyCollection
2027
+ self.mpl_plot_type = plt.fill_between
2028
+ super().__init__(easing=easing,axis=axis,*args, **kwargs)
2029
+ self.x = to_np_array(x)
2030
+ self.y = to_np_array(x)
2031
+ self.y1 = to_np_array(y1).reshape(-1)
2032
+ self.y2 = to_np_array(y2).reshape(-1)
2033
+ if self.y1.size != self.x.size:
2034
+ self.y1 = np.ones_like(self.x)*self.y1[0]
2035
+ if self.y2.size != self.x.size:
2036
+ self.y2 = np.ones_like(self.x)*self.y2[0]
2037
+
2038
+ def clean(self,x,clear_anims=True):
2039
+ if self.obj is not None and self.base_color is None:
2040
+ self.base_color = self.obj.get_facecolor()
2041
+ if 'facecolor' not in self.kwargs:
2042
+ self.kwargs['facecolor'] = self.base_color
2043
+ super().clean(x,clear_anims)
2044
+
2045
+ def function(self,data_x,data_y,x,kwargs):
2046
+ self.obj = self.axis.fill_between(x=data_x,**kwargs)
2047
+
2048
+ class fill_betweenx(Animation):
2049
+ """
2050
+ Fill the area between two vertical curves.
2051
+
2052
+ The curves are defined by the points (*y*, *x1*) and (*y*,
2053
+ *x2*). This creates one or multiple polygons describing the filled
2054
+ area.
2055
+
2056
+ You may exclude some vertical sections from filling using *where*.
2057
+
2058
+ By default, the edges connect the given points directly. Use *step*
2059
+ if the filling should be a step function, i.e. constant in between
2060
+ *y*.
2061
+
2062
+ Parameters
2063
+ ----------
2064
+ y : array-like
2065
+ The y coordinates of the nodes defining the curves.
2066
+
2067
+ duration : float
2068
+ Duration of the animation
2069
+ delay : float, default=0
2070
+ Delay before starting
2071
+ easing : callable, optional
2072
+ Easing function
2073
+ axis : matplotlib.axes.Axes, optional
2074
+ Axis to plot on
2075
+
2076
+
2077
+ x1 : array-like or float
2078
+ The x coordinates of the nodes defining the first curve.
2079
+
2080
+ x2 : array-like or float, default: 0
2081
+ The x coordinates of the nodes defining the second curve.
2082
+
2083
+ where : array-like of bool, optional
2084
+ Define *where* to exclude some vertical regions from being filled.
2085
+ The filled regions are defined by the coordinates ``y[where]``.
2086
+ More precisely, fill between ``y[i]`` and ``y[i+1]`` if
2087
+ ``where[i] and where[i+1]``. Note that this definition implies
2088
+ that an isolated *True* value between two *False* values in *where*
2089
+ will not result in filling. Both sides of the *True* position
2090
+ remain unfilled due to the adjacent *False* values.
2091
+
2092
+ interpolate : bool, default: False
2093
+ This option is only relevant if *where* is used and the two curves
2094
+ are crossing each other.
2095
+
2096
+ Semantically, *where* is often used for *x1* > *x2* or
2097
+ similar. By default, the nodes of the polygon defining the filled
2098
+ region will only be placed at the positions in the *y* array.
2099
+ Such a polygon cannot describe the above semantics close to the
2100
+ intersection. The y-sections containing the intersection are
2101
+ simply clipped.
2102
+
2103
+ Setting *interpolate* to *True* will calculate the actual
2104
+ intersection point and extend the filled region up to this point.
2105
+
2106
+ step : {'pre', 'post', 'mid'}, optional
2107
+ Define *step* if the filling should be a step function,
2108
+ i.e. constant in between *y*. The value determines where the
2109
+ step will occur:
2110
+
2111
+ - 'pre': The x value is continued constantly to the left from
2112
+ every *y* position, i.e. the interval ``(y[i-1], y[i]]``
2113
+ has the value ``x[i]``.
2114
+ - 'post': The y value is continued constantly to the right from
2115
+ every *y* position, i.e. the interval ``[y[i], y[i+1])``
2116
+ has the value ``x[i]``.
2117
+ - 'mid': Steps occur half-way between the *y* positions.
2118
+
2119
+ Returns
2120
+ -------
2121
+ `.FillBetweenPolyCollection`
2122
+ A `.FillBetweenPolyCollection` containing the plotted polygons.
2123
+
2124
+ Other Parameters
2125
+ ----------------
2126
+ data : indexable object, optional
2127
+ If given, the following parameters also accept a string ``s``, which is
2128
+ interpreted as ``data[s]`` if ``s`` is a key in ``data``:
2129
+
2130
+ *y*, *x1*, *x2*, *where*
2131
+
2132
+ **kwargs
2133
+ All other keyword arguments are passed on to
2134
+ `.FillBetweenPolyCollection`. They control the `.Polygon` properties:
2135
+
2136
+ Properties:
2137
+ agg_filter: a filter function, which takes a (m, n, 3) float array and a dpi value, and returns a (m, n, 3) array and two offsets from the bottom left corner of the image
2138
+ alpha: array-like or float or None
2139
+ animated: bool
2140
+ antialiased or aa or antialiaseds: bool or list of bools
2141
+ array: array-like or None
2142
+ capstyle: `.CapStyle` or {'butt', 'projecting', 'round'}
2143
+ clim: (vmin: float, vmax: float)
2144
+ clip_box: `~matplotlib.transforms.BboxBase` or None
2145
+ clip_on: bool
2146
+ clip_path: Patch or (Path, Transform) or None
2147
+ cmap: `.Colormap` or str or None
2148
+ color: color or list of RGBA tuples
2149
+ data: array-like
2150
+ edgecolor or ec or edgecolors: color or list of color or 'face'
2151
+ facecolor or facecolors or fc: color or list of color
2152
+ figure: `~matplotlib.figure.Figure` or `~matplotlib.figure.SubFigure`
2153
+ gid: str
2154
+ hatch: {'/', '\\', '|', '-', '+', 'x', 'o', 'O', '.', '*'}
2155
+ hatch_linewidth: unknown
2156
+ in_layout: bool
2157
+ joinstyle: `.JoinStyle` or {'miter', 'round', 'bevel'}
2158
+ label: object
2159
+ linestyle or dashes or linestyles or ls: str or tuple or list thereof
2160
+ linewidth or linewidths or lw: float or list of floats
2161
+ mouseover: bool
2162
+ norm: `.Normalize` or str or None
2163
+ offset_transform or transOffset: `.Transform`
2164
+ offsets: (N, 2) or (2,) array-like
2165
+ path_effects: list of `.AbstractPathEffect`
2166
+ paths: list of array-like
2167
+ picker: None or bool or float or callable
2168
+ pickradius: float
2169
+ rasterized: bool
2170
+ sizes: `numpy.ndarray` or None
2171
+ sketch_params: (scale: float, length: float, randomness: float)
2172
+ snap: bool or None
2173
+ transform: `~matplotlib.transforms.Transform`
2174
+ url: str
2175
+ urls: list of str or None
2176
+ verts: list of array-like
2177
+ verts_and_codes: unknown
2178
+ visible: bool
2179
+ zorder: float
2180
+
2181
+ See Also
2182
+ --------
2183
+ fill_between : Fill between two sets of y-values.
2184
+ fill_betweenx : Fill between two sets of x-values.
2185
+
2186
+ Notes
2187
+ -----
2188
+
2189
+ .. note::
2190
+
2191
+ This is the :ref:`pyplot wrapper <pyplot_interface>` for `.axes.Axes.fill_betweenx`.
2192
+ """
2193
+ def __init__(self,y,x1,x2,easing=None,axis=None, *args, **kwargs):
2194
+ self.mpl_obj_type = mpl.collections.FillBetweenPolyCollection
2195
+ self.mpl_plot_type = plt.fill_betweenx
2196
+ super().__init__(easing=easing,axis=axis,*args, **kwargs)
2197
+ self.x = to_np_array(y)
2198
+ self.y = to_np_array(y)
2199
+ self.x1 = to_np_array(x1).reshape(-1)
2200
+ self.x2 = to_np_array(x2).reshape(-1)
2201
+ if self.x1.size != self.y.size:
2202
+ self.x1 = np.ones_like(self.y)*self.x1[0]
2203
+ if self.x2.size != self.y.size:
2204
+ self.x2 = np.ones_like(self.y)*self.x2[0]
2205
+
2206
+ def clean(self,x,clear_anims=True):
2207
+ if self.obj is not None and self.base_color is None:
2208
+ self.base_color = self.obj.get_facecolor()
2209
+ if 'facecolor' not in self.kwargs:
2210
+ self.kwargs['facecolor'] = self.base_color
2211
+ super().clean(x,clear_anims)
2212
+
2213
+ def function(self,data_x,data_y,x,kwargs):
2214
+ self.obj = self.axis.fill_betweenx(y=data_x,**kwargs)
2215
+
2216
+ class axvline(Animation):
2217
+ """
2218
+ Add a vertical line spanning the whole or fraction of the Axes.
2219
+
2220
+ Note: If you want to set y-limits in data coordinates, use
2221
+ `~.Axes.vlines` instead.
2222
+
2223
+ Parameters
2224
+ ----------
2225
+ x : float, default: 0
2226
+ x position in :ref:`data coordinates <coordinate-systems>`.
2227
+
2228
+ duration : float
2229
+ Duration of the animation
2230
+ delay : float, default=0
2231
+ Delay before starting
2232
+ easing : callable, optional
2233
+ Easing function
2234
+ axis : matplotlib.axes.Axes, optional
2235
+ Axis to plot on
2236
+
2237
+
2238
+ ymin : float, default: 0
2239
+ The start y-position in :ref:`axes coordinates <coordinate-systems>`.
2240
+ Should be between 0 and 1, 0 being the bottom of the plot, 1 the
2241
+ top of the plot.
2242
+
2243
+ ymax : float, default: 1
2244
+ The end y-position in :ref:`axes coordinates <coordinate-systems>`.
2245
+ Should be between 0 and 1, 0 being the bottom of the plot, 1 the
2246
+ top of the plot.
2247
+
2248
+ Returns
2249
+ -------
2250
+ `~matplotlib.lines.Line2D`
2251
+ A `.Line2D` specified via two points ``(x, ymin)``, ``(x, ymax)``.
2252
+ Its transform is set such that *x* is in
2253
+ :ref:`data coordinates <coordinate-systems>` and *y* is in
2254
+ :ref:`axes coordinates <coordinate-systems>`.
2255
+
2256
+ This is still a generic line and the vertical character is only
2257
+ realized through using identical *x* values for both points. Thus,
2258
+ if you want to change the *x* value later, you have to provide two
2259
+ values ``line.set_xdata([3, 3])``.
2260
+
2261
+ Other Parameters
2262
+ ----------------
2263
+ **kwargs
2264
+ Valid keyword arguments are `.Line2D` properties, except for
2265
+ 'transform':
2266
+
2267
+ Properties:
2268
+ agg_filter: a filter function, which takes a (m, n, 3) float array and a dpi value, and returns a (m, n, 3) array and two offsets from the bottom left corner of the image
2269
+ alpha: float or None
2270
+ animated: bool
2271
+ antialiased or aa: bool
2272
+ clip_box: `~matplotlib.transforms.BboxBase` or None
2273
+ clip_on: bool
2274
+ clip_path: Patch or (Path, Transform) or None
2275
+ color or c: color
2276
+ dash_capstyle: `.CapStyle` or {'butt', 'projecting', 'round'}
2277
+ dash_joinstyle: `.JoinStyle` or {'miter', 'round', 'bevel'}
2278
+ dashes: sequence of floats (on/off ink in points) or (None, None)
2279
+ data: (2, N) array or two 1D arrays
2280
+ drawstyle or ds: {'default', 'steps', 'steps-pre', 'steps-mid', 'steps-post'}, default: 'default'
2281
+ figure: `~matplotlib.figure.Figure` or `~matplotlib.figure.SubFigure`
2282
+ fillstyle: {'full', 'left', 'right', 'bottom', 'top', 'none'}
2283
+ gapcolor: color or None
2284
+ gid: str
2285
+ in_layout: bool
2286
+ label: object
2287
+ linestyle or ls: {'-', '--', '-.', ':', '', (offset, on-off-seq), ...}
2288
+ linewidth or lw: float
2289
+ marker: marker style string, `~.path.Path` or `~.markers.MarkerStyle`
2290
+ markeredgecolor or mec: color
2291
+ markeredgewidth or mew: float
2292
+ markerfacecolor or mfc: color
2293
+ markerfacecoloralt or mfcalt: color
2294
+ markersize or ms: float
2295
+ markevery: None or int or (int, int) or slice or list[int] or float or (float, float) or list[bool]
2296
+ mouseover: bool
2297
+ path_effects: list of `.AbstractPathEffect`
2298
+ picker: float or callable[[Artist, Event], tuple[bool, dict]]
2299
+ pickradius: float
2300
+ rasterized: bool
2301
+ sketch_params: (scale: float, length: float, randomness: float)
2302
+ snap: bool or None
2303
+ solid_capstyle: `.CapStyle` or {'butt', 'projecting', 'round'}
2304
+ solid_joinstyle: `.JoinStyle` or {'miter', 'round', 'bevel'}
2305
+ transform: unknown
2306
+ url: str
2307
+ visible: bool
2308
+ xdata: 1D array
2309
+ ydata: 1D array
2310
+ zorder: float
2311
+
2312
+ See Also
2313
+ --------
2314
+ vlines : Add vertical lines in data coordinates.
2315
+ axvspan : Add a vertical span (rectangle) across the axis.
2316
+ axline : Add a line with an arbitrary slope.
2317
+
2318
+ Notes
2319
+ -----
2320
+
2321
+ .. note::
2322
+
2323
+ This is the :ref:`pyplot wrapper <pyplot_interface>` for `.axes.Axes.axvline`.
2324
+
2325
+ Examples
2326
+ --------
2327
+ * draw a thick red vline at *x* = 0 that spans the yrange::
2328
+
2329
+ >>> axvline(linewidth=4, color='r')
2330
+
2331
+ * draw a default vline at *x* = 1 that spans the yrange::
2332
+
2333
+ >>> axvline(x=1)
2334
+
2335
+ * draw a default vline at *x* = .5 that spans the middle half of
2336
+ the yrange::
2337
+
2338
+ >>> axvline(x=.5, ymin=0.25, ymax=0.75)
2339
+ """
2340
+ def __init__(self, x,easing=None,axis=None, *args, **kwargs):
2341
+ self.mpl_obj_type = mpl.lines.Line2D
2342
+ self.mpl_plot_type = plt.axvline
2343
+ super().__init__(easing=easing,axis=axis,*args, **kwargs)
2344
+ self.x = x
2345
+
2346
+ def function(self,data_x,data_y,x,kwargs):
2347
+ if 'ymax' in kwargs:
2348
+ kwargs['ymax'] = kwargs['ymax'][0]
2349
+ if 'ymin' in kwargs:
2350
+ kwargs['ymin'] = kwargs['ymin'][0]
2351
+
2352
+ if len(data_x) > 0:
2353
+ self.obj = [self.axis.axvline(data_x,**kwargs)]
2354
+
2355
+ class axhline(Animation):
2356
+ """
2357
+ Add a horizontal line spanning the whole or fraction of the Axes.
2358
+
2359
+ Note: If you want to set x-limits in data coordinates, use
2360
+ `~.Axes.hlines` instead.
2361
+
2362
+ Parameters
2363
+ ----------
2364
+ y : float, default: 0
2365
+ y position in :ref:`data coordinates <coordinate-systems>`.
2366
+
2367
+ xmin : float, default: 0
2368
+ The start x-position in :ref:`axes coordinates <coordinate-systems>`.
2369
+ Should be between 0 and 1, 0 being the far left of the plot,
2370
+ 1 the far right of the plot.
2371
+
2372
+ xmax : float, default: 1
2373
+ The end x-position in :ref:`axes coordinates <coordinate-systems>`.
2374
+ Should be between 0 and 1, 0 being the far left of the plot,
2375
+ 1 the far right of the plot.
2376
+
2377
+ Returns
2378
+ -------
2379
+ `~matplotlib.lines.Line2D`
2380
+ A `.Line2D` specified via two points ``(xmin, y)``, ``(xmax, y)``.
2381
+ Its transform is set such that *x* is in
2382
+ :ref:`axes coordinates <coordinate-systems>` and *y* is in
2383
+ :ref:`data coordinates <coordinate-systems>`.
2384
+
2385
+ This is still a generic line and the horizontal character is only
2386
+ realized through using identical *y* values for both points. Thus,
2387
+ if you want to change the *y* value later, you have to provide two
2388
+ values ``line.set_ydata([3, 3])``.
2389
+
2390
+ Other Parameters
2391
+ ----------------
2392
+ **kwargs
2393
+ Valid keyword arguments are `.Line2D` properties, except for
2394
+ 'transform':
2395
+
2396
+ %(Line2D:kwdoc)s
2397
+
2398
+ See Also
2399
+ --------
2400
+ hlines : Add horizontal lines in data coordinates.
2401
+ axhspan : Add a horizontal span (rectangle) across the axis.
2402
+ axline : Add a line with an arbitrary slope.
2403
+
2404
+ Examples
2405
+ --------
2406
+ * draw a thick red hline at 'y' = 0 that spans the xrange::
2407
+
2408
+ >>> axhline(linewidth=4, color='r')
2409
+
2410
+ * draw a default hline at 'y' = 1 that spans the xrange::
2411
+
2412
+ >>> axhline(y=1)
2413
+
2414
+ * draw a default hline at 'y' = .5 that spans the middle half of
2415
+ the xrange::
2416
+
2417
+ >>> axhline(y=.5, xmin=0.25, xmax=0.75)
2418
+ """
2419
+ def __init__(self, y,easing=None,axis=None, *args, **kwargs):
2420
+ self.mpl_obj_type = mpl.lines.Line2D
2421
+ self.mpl_plot_type = plt.axvline
2422
+ super().__init__(easing=easing,axis=axis,*args, **kwargs)
2423
+ self.x = y
2424
+
2425
+ def function(self,data_x,data_y,x,kwargs):
2426
+ if 'xmax' in kwargs:
2427
+ kwargs['xmax'] = kwargs['xmax'][0]
2428
+ if 'xmin' in kwargs:
2429
+ kwargs['xmin'] = kwargs['xmin'][0]
2430
+ if len(data_x) > 0:
2431
+ self.obj = [self.axis.axhline(data_x,**kwargs)]
2432
+
2433
+ class errorbar(Animation):
2434
+ """
2435
+ Plot y versus x as lines and/or markers with attached errorbars.
2436
+
2437
+ *x*, *y* define the data locations, *xerr*, *yerr* define the errorbar
2438
+ sizes. By default, this draws the data markers/lines as well as the
2439
+ errorbars. Use fmt='none' to draw errorbars without any data markers.
2440
+
2441
+ .. versionadded:: 3.7
2442
+ Caps and error lines are drawn in polar coordinates on polar plots.
2443
+
2444
+
2445
+ Parameters
2446
+ ----------
2447
+ x, y : float or array-like
2448
+ The data positions.
2449
+
2450
+ duration : float
2451
+ Duration of the animation
2452
+ delay : float, default=0
2453
+ Delay before starting
2454
+ easing : callable, optional
2455
+ Easing function
2456
+ axis : matplotlib.axes.Axes, optional
2457
+ Axis to plot on
2458
+
2459
+
2460
+ xerr, yerr : float or array-like, shape(N,) or shape(2, N), optional
2461
+ The errorbar sizes:
2462
+
2463
+ - scalar: Symmetric +/- values for all data points.
2464
+ - shape(N,): Symmetric +/-values for each data point.
2465
+ - shape(2, N): Separate - and + values for each bar. First row
2466
+ contains the lower errors, the second row contains the upper
2467
+ errors.
2468
+ - *None*: No errorbar.
2469
+
2470
+ All values must be >= 0.
2471
+
2472
+ fmt : str, default: ''
2473
+ The format for the data points / data lines. See `.plot` for
2474
+ details.
2475
+
2476
+ Use 'none' (case-insensitive) to plot errorbars without any data
2477
+ markers.
2478
+
2479
+ ecolor : color, default: None
2480
+ The color of the errorbar lines. If None, use the color of the
2481
+ line connecting the markers.
2482
+
2483
+ elinewidth : float, default: None
2484
+ The linewidth of the errorbar lines. If None, the linewidth of
2485
+ the current style is used.
2486
+
2487
+ capsize : float, default: errorbar.capsize
2488
+ The length of the error bar caps in points.
2489
+
2490
+ capthick : float, default: None
2491
+ An alias to the keyword argument *markeredgewidth* (a.k.a. *mew*).
2492
+ This setting is a more sensible name for the property that
2493
+ controls the thickness of the error bar cap in points. For
2494
+ backwards compatibility, if *mew* or *markeredgewidth* are given,
2495
+ then they will over-ride *capthick*. This may change in future
2496
+ releases.
2497
+
2498
+ barsabove : bool, default: False
2499
+ If True, will plot the errorbars above the plot
2500
+ symbols. Default is below.
2501
+
2502
+ lolims, uplims, xlolims, xuplims : bool or array-like, default: False
2503
+ These arguments can be used to indicate that a value gives only
2504
+ upper/lower limits. In that case a caret symbol is used to
2505
+ indicate this. *lims*-arguments may be scalars, or array-likes of
2506
+ the same length as *xerr* and *yerr*. To use limits with inverted
2507
+ axes, `~.Axes.set_xlim` or `~.Axes.set_ylim` must be called before
2508
+ :meth:`errorbar`. Note the tricky parameter names: setting e.g.
2509
+ *lolims* to True means that the y-value is a *lower* limit of the
2510
+ True value, so, only an *upward*-pointing arrow will be drawn!
2511
+
2512
+ errorevery : int or (int, int), default: 1
2513
+ draws error bars on a subset of the data. *errorevery* =N draws
2514
+ error bars on the points (x[::N], y[::N]).
2515
+ *errorevery* =(start, N) draws error bars on the points
2516
+ (x[start::N], y[start::N]). e.g. errorevery=(6, 3)
2517
+ adds error bars to the data at (x[6], x[9], x[12], x[15], ...).
2518
+ Used to avoid overlapping error bars when two series share x-axis
2519
+ values.
2520
+
2521
+ Returns
2522
+ -------
2523
+ `.ErrorbarContainer`
2524
+ The container contains:
2525
+
2526
+ - data_line : A `~matplotlib.lines.Line2D` instance of x, y plot markers
2527
+ and/or line.
2528
+ - caplines : A tuple of `~matplotlib.lines.Line2D` instances of the error
2529
+ bar caps.
2530
+ - barlinecols : A tuple of `.LineCollection` with the horizontal and
2531
+ vertical error ranges.
2532
+
2533
+ Other Parameters
2534
+ ----------------
2535
+ data : indexable object, optional
2536
+ If given, the following parameters also accept a string ``s``, which is
2537
+ interpreted as ``data[s]`` if ``s`` is a key in ``data``:
2538
+
2539
+ *x*, *y*, *xerr*, *yerr*
2540
+
2541
+ **kwargs
2542
+ All other keyword arguments are passed on to the `~.Axes.plot` call
2543
+ drawing the markers. For example, this code makes big red squares
2544
+ with thick green edges::
2545
+
2546
+ x, y, yerr = rand(3, 10)
2547
+ errorbar(x, y, yerr, marker='s', mfc='red',
2548
+ mec='green', ms=20, mew=4)
2549
+
2550
+ where *mfc*, *mec*, *ms* and *mew* are aliases for the longer
2551
+ property names, *markerfacecolor*, *markeredgecolor*, *markersize*
2552
+ and *markeredgewidth*.
2553
+
2554
+ Valid kwargs for the marker properties are:
2555
+
2556
+ - *dashes*
2557
+ - *dash_capstyle*
2558
+ - *dash_joinstyle*
2559
+ - *drawstyle*
2560
+ - *fillstyle*
2561
+ - *linestyle*
2562
+ - *marker*
2563
+ - *markeredgecolor*
2564
+ - *markeredgewidth*
2565
+ - *markerfacecolor*
2566
+ - *markerfacecoloralt*
2567
+ - *markersize*
2568
+ - *markevery*
2569
+ - *solid_capstyle*
2570
+ - *solid_joinstyle*
2571
+
2572
+ Refer to the corresponding `.Line2D` property for more details:
2573
+
2574
+ Properties:
2575
+ agg_filter: a filter function, which takes a (m, n, 3) float array and a dpi value, and returns a (m, n, 3) array and two offsets from the bottom left corner of the image
2576
+ alpha: float or None
2577
+ animated: bool
2578
+ antialiased or aa: bool
2579
+ clip_box: `~matplotlib.transforms.BboxBase` or None
2580
+ clip_on: bool
2581
+ clip_path: Patch or (Path, Transform) or None
2582
+ color or c: color
2583
+ dash_capstyle: `.CapStyle` or {'butt', 'projecting', 'round'}
2584
+ dash_joinstyle: `.JoinStyle` or {'miter', 'round', 'bevel'}
2585
+ dashes: sequence of floats (on/off ink in points) or (None, None)
2586
+ data: (2, N) array or two 1D arrays
2587
+ drawstyle or ds: {'default', 'steps', 'steps-pre', 'steps-mid', 'steps-post'}, default: 'default'
2588
+ figure: `~matplotlib.figure.Figure` or `~matplotlib.figure.SubFigure`
2589
+ fillstyle: {'full', 'left', 'right', 'bottom', 'top', 'none'}
2590
+ gapcolor: color or None
2591
+ gid: str
2592
+ in_layout: bool
2593
+ label: object
2594
+ linestyle or ls: {'-', '--', '-.', ':', '', (offset, on-off-seq), ...}
2595
+ linewidth or lw: float
2596
+ marker: marker style string, `~.path.Path` or `~.markers.MarkerStyle`
2597
+ markeredgecolor or mec: color
2598
+ markeredgewidth or mew: float
2599
+ markerfacecolor or mfc: color
2600
+ markerfacecoloralt or mfcalt: color
2601
+ markersize or ms: float
2602
+ markevery: None or int or (int, int) or slice or list[int] or float or (float, float) or list[bool]
2603
+ mouseover: bool
2604
+ path_effects: list of `.AbstractPathEffect`
2605
+ picker: float or callable[[Artist, Event], tuple[bool, dict]]
2606
+ pickradius: float
2607
+ rasterized: bool
2608
+ sketch_params: (scale: float, length: float, randomness: float)
2609
+ snap: bool or None
2610
+ solid_capstyle: `.CapStyle` or {'butt', 'projecting', 'round'}
2611
+ solid_joinstyle: `.JoinStyle` or {'miter', 'round', 'bevel'}
2612
+ transform: unknown
2613
+ url: str
2614
+ visible: bool
2615
+ xdata: 1D array
2616
+ ydata: 1D array
2617
+ zorder: float
2618
+
2619
+ Notes
2620
+ -----
2621
+
2622
+ .. note::
2623
+
2624
+ This is the :ref:`pyplot wrapper <pyplot_interface>` for `.axes.Axes.errorbar`.
2625
+ """
2626
+ def __init__(self,x,y,xerr=None,yerr=None,easing=None,axis=None, *args, **kwargs):
2627
+ self.mpl_obj_type = mpl.lines.Line2D
2628
+ self.mpl_plot_type = plt.axvline
2629
+ super().__init__(easing=easing,axis=axis,*args, **kwargs)
2630
+ self.x = x
2631
+ self.y = y
2632
+ if xerr is None:
2633
+ xerr = np.zeros_like(x)
2634
+ if yerr is None:
2635
+ yerr = np.zeros_like(y)
2636
+ self.xerr = xerr
2637
+ self.yerr = yerr
2638
+
2639
+ def morph(self,new_x,new_y,duration,new_xerr=None,new_yerr=None,delay=0,easing=None,persistent=True):
2640
+ if new_xerr is None:
2641
+ new_xerr = self.xerr
2642
+ if new_yerr is None:
2643
+ new_yerr = self.yerr
2644
+ if isinstance(new_x,numbers.Number):
2645
+ new_x = [new_x]
2646
+ new_y = [new_y]
2647
+ new_xerr = [new_xerr]
2648
+ new_yerr = [new_yerr]
2649
+
2650
+ self.anims.append({
2651
+ 'name':'morph',
2652
+ 'duration':duration,
2653
+ 'delay':delay,
2654
+ 'easing':easing,
2655
+ 'new_x':new_x,
2656
+ 'new_y':new_y,
2657
+ 'new_x_err':new_xerr,
2658
+ 'new_y_err':new_yerr,
2659
+ 'persistent':persistent,
2660
+ 'played':False
2661
+ })
2662
+ self.compute_timings()
2663
+ return self
2664
+
2665
+ def function(self,data_x,data_y,x,kwargs):
2666
+ obj = self.axis.errorbar(data_x,data_y,**kwargs)
2667
+ obj = [obj.lines[0]] + list(obj.lines[1]) + list(obj.lines[2])
2668
+ self.obj = obj
2669
+
2670
+ class hist(Animation):
2671
+ """
2672
+ Compute and plot a histogram.
2673
+
2674
+ This method uses `numpy.histogram` to bin the data in *x* and count the
2675
+ number of values in each bin, then draws the distribution either as a
2676
+ `.BarContainer` or `.Polygon`. The *bins*, *range*, *density*, and
2677
+ *weights* parameters are forwarded to `numpy.histogram`.
2678
+
2679
+ If the data has already been binned and counted, use `~.bar` or
2680
+ `~.stairs` to plot the distribution::
2681
+
2682
+ counts, bins = np.histogram(x)
2683
+ plt.stairs(counts, bins)
2684
+
2685
+ Alternatively, plot pre-computed bins and counts using ``hist()`` by
2686
+ treating each bin as a single point with a weight equal to its count::
2687
+
2688
+ plt.hist(bins[:-1], bins, weights=counts)
2689
+
2690
+ The data input *x* can be a singular array, a list of datasets of
2691
+ potentially different lengths ([*x0*, *x1*, ...]), or a 2D ndarray in
2692
+ which each column is a dataset. Note that the ndarray form is
2693
+ transposed relative to the list form. If the input is an array, then
2694
+ the return value is a tuple (*n*, *bins*, *patches*); if the input is a
2695
+ sequence of arrays, then the return value is a tuple
2696
+ ([*n0*, *n1*, ...], *bins*, [*patches0*, *patches1*, ...]).
2697
+
2698
+ Masked arrays are not supported.
2699
+
2700
+ Parameters
2701
+ ----------
2702
+ x : (n,) array or sequence of (n,) arrays
2703
+ Input values, this takes either a single array or a sequence of
2704
+ arrays which are not required to be of the same length.
2705
+
2706
+ duration : float
2707
+ Duration of the animation
2708
+ delay : float, default=0
2709
+ Delay before starting
2710
+ easing : callable, optional
2711
+ Easing function
2712
+ axis : matplotlib.axes.Axes, optional
2713
+ Axis to plot on
2714
+
2715
+
2716
+ bins : int or sequence or str, default: hist.bins
2717
+ If *bins* is an integer, it defines the number of equal-width bins
2718
+ in the range.
2719
+
2720
+ If *bins* is a sequence, it defines the bin edges, including the
2721
+ left edge of the first bin and the right edge of the last bin;
2722
+ in this case, bins may be unequally spaced. All but the last
2723
+ (righthand-most) bin is half-open. In other words, if *bins* is::
2724
+
2725
+ [1, 2, 3, 4]
2726
+
2727
+ then the first bin is ``[1, 2)`` (including 1, but excluding 2) and
2728
+ the second ``[2, 3)``. The last bin, however, is ``[3, 4]``, which
2729
+ *includes* 4.
2730
+
2731
+ If *bins* is a string, it is one of the binning strategies
2732
+ supported by `numpy.histogram_bin_edges`: 'auto', 'fd', 'doane',
2733
+ 'scott', 'stone', 'rice', 'sturges', or 'sqrt'.
2734
+
2735
+ range : tuple or None, default: None
2736
+ The lower and upper range of the bins. Lower and upper outliers
2737
+ are ignored. If not provided, *range* is ``(x.min(), x.max())``.
2738
+ Range has no effect if *bins* is a sequence.
2739
+
2740
+ If *bins* is a sequence or *range* is specified, autoscaling
2741
+ is based on the specified bin range instead of the
2742
+ range of x.
2743
+
2744
+ density : bool, default: False
2745
+ If ``True``, draw and return a probability density: each bin
2746
+ will display the bin's raw count divided by the total number of
2747
+ counts *and the bin width*
2748
+ (``density = counts / (sum(counts) * np.diff(bins))``),
2749
+ so that the area under the histogram integrates to 1
2750
+ (``np.sum(density * np.diff(bins)) == 1``).
2751
+
2752
+ If *stacked* is also ``True``, the sum of the histograms is
2753
+ normalized to 1.
2754
+
2755
+ weights : (n,) array-like or None, default: None
2756
+ An array of weights, of the same shape as *x*. Each value in
2757
+ *x* only contributes its associated weight towards the bin count
2758
+ (instead of 1). If *density* is ``True``, the weights are
2759
+ normalized, so that the integral of the density over the range
2760
+ remains 1.
2761
+
2762
+ cumulative : bool or -1, default: False
2763
+ If ``True``, then a histogram is computed where each bin gives the
2764
+ counts in that bin plus all bins for smaller values. The last bin
2765
+ gives the total number of datapoints.
2766
+
2767
+ If *density* is also ``True`` then the histogram is normalized such
2768
+ that the last bin equals 1.
2769
+
2770
+ If *cumulative* is a number less than 0 (e.g., -1), the direction
2771
+ of accumulation is reversed. In this case, if *density* is also
2772
+ ``True``, then the histogram is normalized such that the first bin
2773
+ equals 1.
2774
+
2775
+ bottom : array-like or float, default: 0
2776
+ Location of the bottom of each bin, i.e. bins are drawn from
2777
+ ``bottom`` to ``bottom + hist(x, bins)`` If a scalar, the bottom
2778
+ of each bin is shifted by the same amount. If an array, each bin
2779
+ is shifted independently and the length of bottom must match the
2780
+ number of bins. If None, defaults to 0.
2781
+
2782
+ histtype : {'bar', 'barstacked', 'step', 'stepfilled'}, default: 'bar'
2783
+ The type of histogram to draw.
2784
+
2785
+ - 'bar' is a traditional bar-type histogram. If multiple data
2786
+ are given the bars are arranged side by side.
2787
+ - 'barstacked' is a bar-type histogram where multiple
2788
+ data are stacked on top of each other.
2789
+ - 'step' generates a lineplot that is by default unfilled.
2790
+ - 'stepfilled' generates a lineplot that is by default filled.
2791
+
2792
+ align : {'left', 'mid', 'right'}, default: 'mid'
2793
+ The horizontal alignment of the histogram bars.
2794
+
2795
+ - 'left': bars are centered on the left bin edges.
2796
+ - 'mid': bars are centered between the bin edges.
2797
+ - 'right': bars are centered on the right bin edges.
2798
+
2799
+ orientation : {'vertical', 'horizontal'}, default: 'vertical'
2800
+ If 'horizontal', `~.Axes.barh` will be used for bar-type histograms
2801
+ and the *bottom* kwarg will be the left edges.
2802
+
2803
+ rwidth : float or None, default: None
2804
+ The relative width of the bars as a fraction of the bin width. If
2805
+ ``None``, automatically compute the width.
2806
+
2807
+ Ignored if *histtype* is 'step' or 'stepfilled'.
2808
+
2809
+ log : bool, default: False
2810
+ If ``True``, the histogram axis will be set to a log scale.
2811
+
2812
+ color : color or list of color or None, default: None
2813
+ Color or sequence of colors, one per dataset. Default (``None``)
2814
+ uses the standard line color sequence.
2815
+
2816
+ label : str or list of str, optional
2817
+ String, or sequence of strings to match multiple datasets. Bar
2818
+ charts yield multiple patches per dataset, but only the first gets
2819
+ the label, so that `~.Axes.legend` will work as expected.
2820
+
2821
+ stacked : bool, default: False
2822
+ If ``True``, multiple data are stacked on top of each other If
2823
+ ``False`` multiple data are arranged side by side if histtype is
2824
+ 'bar' or on top of each other if histtype is 'step'
2825
+
2826
+ Returns
2827
+ -------
2828
+ n : array or list of arrays
2829
+ The values of the histogram bins. See *density* and *weights* for a
2830
+ description of the possible semantics. If input *x* is an array,
2831
+ then this is an array of length *nbins*. If input is a sequence of
2832
+ arrays ``[data1, data2, ...]``, then this is a list of arrays with
2833
+ the values of the histograms for each of the arrays in the same
2834
+ order. The dtype of the array *n* (or of its element arrays) will
2835
+ always be float even if no weighting or normalization is used.
2836
+
2837
+ bins : array
2838
+ The edges of the bins. Length nbins + 1 (nbins left edges and right
2839
+ edge of last bin). Always a single array even when multiple data
2840
+ sets are passed in.
2841
+
2842
+ patches : `.BarContainer` or list of a single `.Polygon` or list of such objects
2843
+ Container of individual artists used to create the histogram
2844
+ or list of such containers if there are multiple input datasets.
2845
+
2846
+ Other Parameters
2847
+ ----------------
2848
+ data : indexable object, optional
2849
+ If given, the following parameters also accept a string ``s``, which is
2850
+ interpreted as ``data[s]`` if ``s`` is a key in ``data``:
2851
+
2852
+ *x*, *weights*
2853
+
2854
+ **kwargs
2855
+ `~matplotlib.patches.Patch` properties. The following properties
2856
+ additionally accept a sequence of values corresponding to the
2857
+ datasets in *x*:
2858
+ *edgecolor*, *facecolor*, *linewidth*, *linestyle*, *hatch*.
2859
+
2860
+ .. versionadded:: 3.10
2861
+ Allowing sequences of values in above listed Patch properties.
2862
+
2863
+ See Also
2864
+ --------
2865
+ hist2d : 2D histogram with rectangular bins
2866
+ hexbin : 2D histogram with hexagonal bins
2867
+ stairs : Plot a pre-computed histogram
2868
+ bar : Plot a pre-computed histogram
2869
+
2870
+ Notes
2871
+ -----
2872
+
2873
+ .. note::
2874
+
2875
+ This is the :ref:`pyplot wrapper <pyplot_interface>` for `.axes.Axes.hist`.
2876
+
2877
+ For large numbers of bins (>1000), plotting can be significantly
2878
+ accelerated by using `~.Axes.stairs` to plot a pre-computed histogram
2879
+ (``plt.stairs(*np.histogram(data))``), or by setting *histtype* to
2880
+ 'step' or 'stepfilled' rather than 'bar' or 'barstacked'.
2881
+ """
2882
+ def __init__(self,x,easing=None,axis=None, *args, **kwargs):
2883
+ self.mpl_obj_type = mpl.patches.Rectangle
2884
+ self.mpl_plot_type = plt.hist
2885
+ super().__init__(easing=easing,axis=axis,*args, **kwargs)
2886
+ self.x = to_np_array(x)
2887
+ self.y = np.zeros_like(self.x)
2888
+
2889
+ def clean(self,x,clear_anims=True):
2890
+ if self.obj is not None and self.base_color is None:
2891
+ self.base_color = self.obj[0].get_facecolor()
2892
+ if 'color' not in self.kwargs:
2893
+ self.kwargs['color'] = self.base_color
2894
+ super().clean(x,clear_anims)
2895
+
2896
+ def morph(self,new_x,duration,delay=0,easing=None,persistent=True,**kwargs):
2897
+ if isinstance(new_x,numbers.Number):
2898
+ new_x = [new_x]
2899
+
2900
+ self.anims.append({
2901
+ 'name':'morph',
2902
+ 'duration':duration,
2903
+ 'delay':delay,
2904
+ 'easing':easing,
2905
+ 'new_x':new_x,
2906
+ 'new_y':np.zeros_like(self.y),
2907
+ 'persistent':persistent,
2908
+ 'played':False
2909
+ })
2910
+ self.compute_timings()
2911
+ return self
2912
+
2913
+ def function(self,data_x,data_y,x,kwargs):
2914
+ if isinstance(kwargs['alpha'],np.ndarray):
2915
+ kwargs['alpha'] = kwargs['alpha'][0]
2916
+
2917
+ if 'bins' in kwargs:
2918
+ kwargs['bins'] = to_np_array(kwargs['bins'])
2919
+
2920
+ if 'bins' in kwargs and len(kwargs['bins']) == 1:
2921
+ kwargs['bins'] = int(kwargs['bins'][0])
2922
+
2923
+ h,edges,self.obj = self.axis.hist(data_x,**kwargs)
2924
+
2925
+ class hist2d(Animation):
2926
+ """
2927
+ Make a 2D histogram plot.
2928
+
2929
+ Parameters
2930
+ ----------
2931
+ x, y : array-like, shape (n, )
2932
+ Input values
2933
+
2934
+ duration : float
2935
+ Duration of the animation
2936
+ delay : float, default=0
2937
+ Delay before starting
2938
+ easing : callable, optional
2939
+ Easing function
2940
+ axis : matplotlib.axes.Axes, optional
2941
+ Axis to plot on
2942
+
2943
+
2944
+ bins : None or int or [int, int] or array-like or [array, array]
2945
+
2946
+ The bin specification:
2947
+
2948
+ - If int, the number of bins for the two dimensions
2949
+ (``nx = ny = bins``).
2950
+ - If ``[int, int]``, the number of bins in each dimension
2951
+ (``nx, ny = bins``).
2952
+ - If array-like, the bin edges for the two dimensions
2953
+ (``x_edges = y_edges = bins``).
2954
+ - If ``[array, array]``, the bin edges in each dimension
2955
+ (``x_edges, y_edges = bins``).
2956
+
2957
+ The default value is 10.
2958
+
2959
+ range : array-like shape(2, 2), optional
2960
+ The leftmost and rightmost edges of the bins along each dimension
2961
+ (if not specified explicitly in the bins parameters): ``[[xmin,
2962
+ xmax], [ymin, ymax]]``. All values outside of this range will be
2963
+ considered outliers and not tallied in the histogram.
2964
+
2965
+ density : bool, default: False
2966
+ Normalize histogram. See the documentation for the *density*
2967
+ parameter of `~.Axes.hist` for more details.
2968
+
2969
+ weights : array-like, shape (n, ), optional
2970
+ An array of values w_i weighing each sample (x_i, y_i).
2971
+
2972
+ cmin, cmax : float, default: None
2973
+ All bins that has count less than *cmin* or more than *cmax* will not be
2974
+ displayed (set to NaN before passing to `~.Axes.pcolormesh`) and these count
2975
+ values in the return value count histogram will also be set to nan upon
2976
+ return.
2977
+
2978
+ Returns
2979
+ -------
2980
+ h : 2D array
2981
+ The bi-dimensional histogram of samples x and y. Values in x are
2982
+ histogrammed along the first dimension and values in y are
2983
+ histogrammed along the second dimension.
2984
+ xedges : 1D array
2985
+ The bin edges along the x-axis.
2986
+ yedges : 1D array
2987
+ The bin edges along the y-axis.
2988
+ image : `~.matplotlib.collections.QuadMesh`
2989
+
2990
+ Other Parameters
2991
+ ----------------
2992
+ cmap : str or `~matplotlib.colors.Colormap`, default: image.cmap
2993
+ The Colormap instance or registered colormap name used to map scalar data
2994
+ to colors.
2995
+
2996
+ norm : str or `~matplotlib.colors.Normalize`, optional
2997
+ The normalization method used to scale scalar data to the [0, 1] range
2998
+ before mapping to colors using *cmap*. By default, a linear scaling is
2999
+ used, mapping the lowest value to 0 and the highest to 1.
3000
+
3001
+ If given, this can be one of the following:
3002
+
3003
+ - An instance of `.Normalize` or one of its subclasses
3004
+ (see :ref:`colormapnorms`).
3005
+ - A scale name, i.e. one of "linear", "log", "symlog", "logit", etc. For a
3006
+ list of available scales, call `matplotlib.scale.get_scale_names()`.
3007
+ In that case, a suitable `.Normalize` subclass is dynamically generated
3008
+ and instantiated.
3009
+
3010
+ vmin, vmax : float, optional
3011
+ When using scalar data and no explicit *norm*, *vmin* and *vmax* define
3012
+ the data range that the colormap covers. By default, the colormap covers
3013
+ the complete value range of the supplied data. It is an error to use
3014
+ *vmin*/*vmax* when a *norm* instance is given (but using a `str` *norm*
3015
+ name together with *vmin*/*vmax* is acceptable).
3016
+
3017
+ colorizer : `~matplotlib.colorizer.Colorizer` or None, default: None
3018
+ The Colorizer object used to map color to data. If None, a Colorizer
3019
+ object is created from a *norm* and *cmap*.
3020
+
3021
+ alpha : ``0 <= scalar <= 1`` or ``None``, optional
3022
+ The alpha blending value.
3023
+
3024
+ data : indexable object, optional
3025
+ If given, the following parameters also accept a string ``s``, which is
3026
+ interpreted as ``data[s]`` if ``s`` is a key in ``data``:
3027
+
3028
+ *x*, *y*, *weights*
3029
+
3030
+ **kwargs
3031
+ Additional parameters are passed along to the
3032
+ `~.Axes.pcolormesh` method and `~matplotlib.collections.QuadMesh`
3033
+ constructor.
3034
+
3035
+ See Also
3036
+ --------
3037
+ hist : 1D histogram plotting
3038
+ hexbin : 2D histogram with hexagonal bins
3039
+
3040
+ Notes
3041
+ -----
3042
+
3043
+ .. note::
3044
+
3045
+ This is the :ref:`pyplot wrapper <pyplot_interface>` for `.axes.Axes.hist2d`.
3046
+
3047
+ - Currently ``hist2d`` calculates its own axis limits, and any limits
3048
+ previously set are ignored.
3049
+ - Rendering the histogram with a logarithmic color scale is
3050
+ accomplished by passing a `.colors.LogNorm` instance to the *norm*
3051
+ keyword argument. Likewise, power-law normalization (similar
3052
+ in effect to gamma correction) can be accomplished with
3053
+ `.colors.PowerNorm`.
3054
+ """
3055
+ def __init__(self,x,y,easing=None,axis=None, *args, **kwargs):
3056
+ self.mpl_obj_type = mpl.collections.QuadMesh
3057
+ self.mpl_plot_type = plt.hist2d
3058
+ super().__init__(easing=easing,axis=axis,*args, **kwargs)
3059
+ self.x = to_np_array(x)
3060
+ self.y = to_np_array(y)
3061
+
3062
+ def function(self,data_x,data_y,x,kwargs):
3063
+ if isinstance(kwargs['alpha'],np.ndarray):
3064
+ kwargs['alpha'] = kwargs['alpha'][0]
3065
+
3066
+ if 'bins' in kwargs:
3067
+ kwargs['bins'] = to_np_array(kwargs['bins'])
3068
+
3069
+ if 'bins' in kwargs and len(kwargs['bins']) == 1:
3070
+ kwargs['bins'] = int(kwargs['bins'][0])
3071
+
3072
+ if 'color' in kwargs:
3073
+ kwargs.pop('color')
3074
+
3075
+ #Have to reset xlim and ylim because hist2d does not respect the imposed limits
3076
+ old_xlim = self.axis.get_xlim()
3077
+ old_ylim = self.axis.get_ylim()
3078
+ h,xedges,yedges,self.obj = self.axis.hist2d(data_x,data_y,**kwargs)
3079
+ self.axis.set_xlim(old_xlim)
3080
+ self.axis.set_ylim(old_ylim)
3081
+
3082
+ class contourf(Animation):
3083
+ """
3084
+ Plot filled contours.
3085
+
3086
+ Call signature::
3087
+
3088
+ contourf([X, Y,] Z, /, [levels], **kwargs)
3089
+
3090
+ The arguments *X*, *Y*, *Z* are positional-only.
3091
+
3092
+ `.contour` and `.contourf` draw contour lines and filled contours,
3093
+ respectively. Except as noted, function signatures and return values
3094
+ are the same for both versions.
3095
+
3096
+ Parameters
3097
+ ----------
3098
+ X, Y : array-like, optional
3099
+ The coordinates of the values in *Z*.
3100
+
3101
+ duration : float
3102
+ Duration of the animation
3103
+ delay : float, default=0
3104
+ Delay before starting
3105
+ easing : callable, optional
3106
+ Easing function
3107
+ axis : matplotlib.axes.Axes, optional
3108
+ Axis to plot on
3109
+
3110
+
3111
+ *X* and *Y* must both be 2D with the same shape as *Z* (e.g.
3112
+ created via `numpy.meshgrid`), or they must both be 1-D such
3113
+ that ``len(X) == N`` is the number of columns in *Z* and
3114
+ ``len(Y) == M`` is the number of rows in *Z*.
3115
+
3116
+ *X* and *Y* must both be ordered monotonically.
3117
+
3118
+ If not given, they are assumed to be integer indices, i.e.
3119
+ ``X = range(N)``, ``Y = range(M)``.
3120
+
3121
+ Z : (M, N) array-like
3122
+ The height values over which the contour is drawn. Color-mapping is
3123
+ controlled by *cmap*, *norm*, *vmin*, and *vmax*.
3124
+
3125
+ levels : int or array-like, optional
3126
+ Determines the number and positions of the contour lines / regions.
3127
+
3128
+ If an int *n*, use `~matplotlib.ticker.MaxNLocator`, which tries
3129
+ to automatically choose no more than *n+1* "nice" contour levels
3130
+ between minimum and maximum numeric values of *Z*.
3131
+
3132
+ If array-like, draw contour lines at the specified levels.
3133
+ The values must be in increasing order.
3134
+
3135
+ Returns
3136
+ -------
3137
+ `~.contour.QuadContourSet`
3138
+
3139
+ Other Parameters
3140
+ ----------------
3141
+ corner_mask : bool, default: contour.corner_mask
3142
+ Enable/disable corner masking, which only has an effect if *Z* is
3143
+ a masked array. If ``False``, any quad touching a masked point is
3144
+ masked out. If ``True``, only the triangular corners of quads
3145
+ nearest those points are always masked out, other triangular
3146
+ corners comprising three unmasked points are contoured as usual.
3147
+
3148
+ colors : color or list of color, optional
3149
+ The colors of the levels, i.e. the lines for `.contour` and the
3150
+ areas for `.contourf`.
3151
+
3152
+ The sequence is cycled for the levels in ascending order. If the
3153
+ sequence is shorter than the number of levels, it's repeated.
3154
+
3155
+ As a shortcut, a single color may be used in place of one-element lists, i.e.
3156
+ ``'red'`` instead of ``['red']`` to color all levels with the same color.
3157
+
3158
+ .. versionchanged:: 3.10
3159
+ Previously a single color had to be expressed as a string, but now any
3160
+ valid color format may be passed.
3161
+
3162
+ By default (value *None*), the colormap specified by *cmap*
3163
+ will be used.
3164
+
3165
+ alpha : float, default: 1
3166
+ The alpha blending value, between 0 (transparent) and 1 (opaque).
3167
+
3168
+ cmap : str or `~matplotlib.colors.Colormap`, default: image.cmap
3169
+ The Colormap instance or registered colormap name used to map scalar data
3170
+ to colors.
3171
+
3172
+ This parameter is ignored if *colors* is set.
3173
+
3174
+ norm : str or `~matplotlib.colors.Normalize`, optional
3175
+ The normalization method used to scale scalar data to the [0, 1] range
3176
+ before mapping to colors using *cmap*. By default, a linear scaling is
3177
+ used, mapping the lowest value to 0 and the highest to 1.
3178
+
3179
+ If given, this can be one of the following:
3180
+
3181
+ - An instance of `.Normalize` or one of its subclasses
3182
+ (see :ref:`colormapnorms`).
3183
+ - A scale name, i.e. one of "linear", "log", "symlog", "logit", etc. For a
3184
+ list of available scales, call `matplotlib.scale.get_scale_names()`.
3185
+ In that case, a suitable `.Normalize` subclass is dynamically generated
3186
+ and instantiated.
3187
+
3188
+ This parameter is ignored if *colors* is set.
3189
+
3190
+ vmin, vmax : float, optional
3191
+ When using scalar data and no explicit *norm*, *vmin* and *vmax* define
3192
+ the data range that the colormap covers. By default, the colormap covers
3193
+ the complete value range of the supplied data. It is an error to use
3194
+ *vmin*/*vmax* when a *norm* instance is given (but using a `str` *norm*
3195
+ name together with *vmin*/*vmax* is acceptable).
3196
+
3197
+ If *vmin* or *vmax* are not given, the default color scaling is based on
3198
+ *levels*.
3199
+
3200
+ This parameter is ignored if *colors* is set.
3201
+
3202
+ colorizer : `~matplotlib.colorizer.Colorizer` or None, default: None
3203
+ The Colorizer object used to map color to data. If None, a Colorizer
3204
+ object is created from a *norm* and *cmap*.
3205
+
3206
+ This parameter is ignored if *colors* is set.
3207
+
3208
+ origin : {*None*, 'upper', 'lower', 'image'}, default: None
3209
+ Determines the orientation and exact position of *Z* by specifying
3210
+ the position of ``Z[0, 0]``. This is only relevant, if *X*, *Y*
3211
+ are not given.
3212
+
3213
+ - *None*: ``Z[0, 0]`` is at X=0, Y=0 in the lower left corner.
3214
+ - 'lower': ``Z[0, 0]`` is at X=0.5, Y=0.5 in the lower left corner.
3215
+ - 'upper': ``Z[0, 0]`` is at X=N+0.5, Y=0.5 in the upper left
3216
+ corner.
3217
+ - 'image': Use the value from image.origin.
3218
+
3219
+ extent : (x0, x1, y0, y1), optional
3220
+ If *origin* is not *None*, then *extent* is interpreted as in
3221
+ `.imshow`: it gives the outer pixel boundaries. In this case, the
3222
+ position of Z[0, 0] is the center of the pixel, not a corner. If
3223
+ *origin* is *None*, then (*x0*, *y0*) is the position of Z[0, 0],
3224
+ and (*x1*, *y1*) is the position of Z[-1, -1].
3225
+
3226
+ This argument is ignored if *X* and *Y* are specified in the call
3227
+ to contour.
3228
+
3229
+ locator : ticker.Locator subclass, optional
3230
+ The locator is used to determine the contour levels if they
3231
+ are not given explicitly via *levels*.
3232
+ Defaults to `~.ticker.MaxNLocator`.
3233
+
3234
+ extend : {'neither', 'both', 'min', 'max'}, default: 'neither'
3235
+ Determines the ``contourf``-coloring of values that are outside the
3236
+ *levels* range.
3237
+
3238
+ If 'neither', values outside the *levels* range are not colored.
3239
+ If 'min', 'max' or 'both', color the values below, above or below
3240
+ and above the *levels* range.
3241
+
3242
+ Values below ``min(levels)`` and above ``max(levels)`` are mapped
3243
+ to the under/over values of the `.Colormap`. Note that most
3244
+ colormaps do not have dedicated colors for these by default, so
3245
+ that the over and under values are the edge values of the colormap.
3246
+ You may want to set these values explicitly using
3247
+ `.Colormap.set_under` and `.Colormap.set_over`.
3248
+
3249
+ .. note::
3250
+
3251
+ An existing `.QuadContourSet` does not get notified if
3252
+ properties of its colormap are changed. Therefore, an explicit
3253
+ call `~.ContourSet.changed()` is needed after modifying the
3254
+ colormap. The explicit call can be left out, if a colorbar is
3255
+ assigned to the `.QuadContourSet` because it internally calls
3256
+ `~.ContourSet.changed()`.
3257
+
3258
+ Example::
3259
+
3260
+ x = np.arange(1, 10)
3261
+ y = x.reshape(-1, 1)
3262
+ h = x * y
3263
+
3264
+ cs = plt.contourf(h, levels=[10, 30, 50],
3265
+ colors=['#808080', '#A0A0A0', '#C0C0C0'], extend='both')
3266
+ cs.cmap.set_over('red')
3267
+ cs.cmap.set_under('blue')
3268
+ cs.changed()
3269
+
3270
+ xunits, yunits : registered units, optional
3271
+ Override axis units by specifying an instance of a
3272
+ :class:`matplotlib.units.ConversionInterface`.
3273
+
3274
+ antialiased : bool, optional
3275
+ Enable antialiasing, overriding the defaults. For
3276
+ filled contours, the default is *False*. For line contours,
3277
+ it is taken from lines.antialiased.
3278
+
3279
+ nchunk : int >= 0, optional
3280
+ If 0, no subdivision of the domain. Specify a positive integer to
3281
+ divide the domain into subdomains of *nchunk* by *nchunk* quads.
3282
+ Chunking reduces the maximum length of polygons generated by the
3283
+ contouring algorithm which reduces the rendering workload passed
3284
+ on to the backend and also requires slightly less RAM. It can
3285
+ however introduce rendering artifacts at chunk boundaries depending
3286
+ on the backend, the *antialiased* flag and value of *alpha*.
3287
+
3288
+ linewidths : float or array-like, default: contour.linewidth
3289
+ *Only applies to* `.contour`.
3290
+
3291
+ The line width of the contour lines.
3292
+
3293
+ If a number, all levels will be plotted with this linewidth.
3294
+
3295
+ If a sequence, the levels in ascending order will be plotted with
3296
+ the linewidths in the order specified.
3297
+
3298
+ If None, this falls back to lines.linewidth.
3299
+
3300
+ linestyles : {*None*, 'solid', 'dashed', 'dashdot', 'dotted'}, optional
3301
+ *Only applies to* `.contour`.
3302
+
3303
+ If *linestyles* is *None*, the default is 'solid' unless the lines are
3304
+ monochrome. In that case, negative contours will instead take their
3305
+ linestyle from the *negative_linestyles* argument.
3306
+
3307
+ *linestyles* can also be an iterable of the above strings specifying a set
3308
+ of linestyles to be used. If this iterable is shorter than the number of
3309
+ contour levels it will be repeated as necessary.
3310
+
3311
+ negative_linestyles : {*None*, 'solid', 'dashed', 'dashdot', 'dotted'}, optional
3312
+ *Only applies to* `.contour`.
3313
+
3314
+ If *linestyles* is *None* and the lines are monochrome, this argument
3315
+ specifies the line style for negative contours.
3316
+
3317
+ If *negative_linestyles* is *None*, the default is taken from
3318
+ contour.negative_linestyle.
3319
+
3320
+ *negative_linestyles* can also be an iterable of the above strings
3321
+ specifying a set of linestyles to be used. If this iterable is shorter than
3322
+ the number of contour levels it will be repeated as necessary.
3323
+
3324
+ hatches : list[str], optional
3325
+ *Only applies to* `.contourf`.
3326
+
3327
+ A list of cross hatch patterns to use on the filled areas.
3328
+ If None, no hatching will be added to the contour.
3329
+
3330
+ algorithm : {'mpl2005', 'mpl2014', 'serial', 'threaded'}, optional
3331
+ Which contouring algorithm to use to calculate the contour lines and
3332
+ polygons. The algorithms are implemented in
3333
+ `ContourPy <https://github.com/contourpy/contourpy>`_, consult the
3334
+ `ContourPy documentation <https://contourpy.readthedocs.io>`_ for
3335
+ further information.
3336
+
3337
+ The default is taken from contour.algorithm.
3338
+
3339
+ clip_path : `~matplotlib.patches.Patch` or `.Path` or `.TransformedPath`
3340
+ Set the clip path. See `~matplotlib.artist.Artist.set_clip_path`.
3341
+
3342
+ .. versionadded:: 3.8
3343
+
3344
+ data : indexable object, optional
3345
+ If given, all parameters also accept a string ``s``, which is
3346
+ interpreted as ``data[s]`` if ``s`` is a key in ``data``.
3347
+
3348
+ Notes
3349
+ -----
3350
+
3351
+ .. note::
3352
+
3353
+ This is the :ref:`pyplot wrapper <pyplot_interface>` for `.axes.Axes.contourf`.
3354
+
3355
+ 1. `.contourf` differs from the MATLAB version in that it does not draw
3356
+ the polygon edges. To draw edges, add line contours with calls to
3357
+ `.contour`.
3358
+
3359
+ 2. `.contourf` fills intervals that are closed at the top; that is, for
3360
+ boundaries *z1* and *z2*, the filled region is::
3361
+
3362
+ z1 < Z <= z2
3363
+
3364
+ except for the lowest interval, which is closed on both sides (i.e.
3365
+ it includes the lowest value).
3366
+
3367
+ 3. `.contour` and `.contourf` use a `marching squares
3368
+ <https://en.wikipedia.org/wiki/Marching_squares>`_ algorithm to
3369
+ compute contour locations. More information can be found in
3370
+ `ContourPy documentation <https://contourpy.readthedocs.io>`_.
3371
+ """
3372
+ def __init__(self, z, x=None, y=None,easing=None,axis=None, *args, **kwargs):
3373
+ self.mpl_obj_type = mpl.contour.QuadContourSet
3374
+ self.mpl_plot_type = plt.contourf
3375
+ super().__init__(easing=easing,axis=axis,*args,**kwargs)
3376
+ self.x = np.ravel(z)
3377
+ self.y = np.ravel(z)
3378
+ self._x = x
3379
+ self._y = y
3380
+ if self._x is None:
3381
+ self._x = range(z.shape[0])
3382
+ if self._y is None:
3383
+ self._y = range(z.shape[1])
3384
+ self._x = np.array(self._x)
3385
+ self._y = np.array(self._y)
3386
+ self.init_shape = z.shape
3387
+
3388
+ def morph(self,new_z,duration,delay=0,easing=None,persistent=True,**kwargs):
3389
+ self.anims.append({
3390
+ 'name':'morph',
3391
+ 'duration':duration,
3392
+ 'delay':delay,
3393
+ 'easing':easing,
3394
+ 'new_x':np.ravel(new_z),
3395
+ 'new_y':np.ravel(new_z),
3396
+ 'persistent':persistent,
3397
+ 'played':False
3398
+ })
3399
+ self.compute_timings()
3400
+ return self
3401
+
3402
+ def function(self,data_x,data_y,x,kwargs):
3403
+ if isinstance(kwargs['alpha'],np.ndarray):
3404
+ kwargs['alpha'] = kwargs['alpha'][0]
3405
+
3406
+ if 'bins' in kwargs:
3407
+ kwargs['bins'] = to_np_array(kwargs['bins'])
3408
+
3409
+ if 'bins' in kwargs and len(kwargs['bins']) == 1:
3410
+ kwargs['bins'] = int(kwargs['bins'][0])
3411
+
3412
+ if 'color' in kwargs:
3413
+ kwargs.pop('color')
3414
+
3415
+ kwargs.pop('animated')
3416
+
3417
+ if len(np.array(data_x).shape) == 1:
3418
+ new_data_x = np.zeros(self.init_shape)
3419
+ for i in range(0, len(data_x)):
3420
+ new_data_x[i//self.init_shape[0],i%self.init_shape[0]] = data_x[i]
3421
+ data_x = new_data_x
3422
+
3423
+ #Have to reset xlim and ylim because contourf does not respect the imposed limits
3424
+ old_xlim = self.axis.get_xlim()
3425
+ old_ylim = self.axis.get_ylim()
3426
+ z = data_x
3427
+ self.obj = self.axis.contourf(self._x,self._y,z,**kwargs)
3428
+ self.axis.set_xlim(old_xlim)
3429
+ self.axis.set_ylim(old_ylim)
3430
+
3431
+ class text(Animation):
3432
+ """
3433
+ Add text to the Axes.
3434
+
3435
+ Add the text *s* to the Axes at location *x*, *y* in data coordinates,
3436
+ with a default ``horizontalalignment`` on the ``left`` and
3437
+ ``verticalalignment`` at the ``baseline``.
3438
+
3439
+ Parameters
3440
+ ----------
3441
+ x, y : float
3442
+ The position to place the text. By default, this is in data
3443
+ coordinates. The coordinate system can be changed using the
3444
+ *transform* parameter.
3445
+
3446
+ duration : float
3447
+ Duration of the animation
3448
+ delay : float, default=0
3449
+ Delay before starting
3450
+ easing : callable, optional
3451
+ Easing function
3452
+ axis : matplotlib.axes.Axes, optional
3453
+ Axis to plot on
3454
+
3455
+
3456
+ s : str
3457
+ The text.
3458
+
3459
+ fontdict : dict, default: None
3460
+
3461
+ .. admonition:: Discouraged
3462
+
3463
+ The use of *fontdict* is discouraged. Parameters should be passed as
3464
+ individual keyword arguments or using dictionary-unpacking
3465
+ ``text(..., **fontdict)``.
3466
+
3467
+ A dictionary to override the default text properties. If fontdict
3468
+ is None, the defaults are determined by `.rcParams`.
3469
+
3470
+ Returns
3471
+ -------
3472
+ `.Text`
3473
+ The created `.Text` instance.
3474
+
3475
+ Other Parameters
3476
+ ----------------
3477
+ **kwargs : `~matplotlib.text.Text` properties.
3478
+ Other miscellaneous text parameters.
3479
+
3480
+ Properties:
3481
+ agg_filter: a filter function, which takes a (m, n, 3) float array and a dpi value, and returns a (m, n, 3) array and two offsets from the bottom left corner of the image
3482
+ alpha: float or None
3483
+ animated: bool
3484
+ antialiased: bool
3485
+ backgroundcolor: color
3486
+ bbox: dict with properties for `.patches.FancyBboxPatch`
3487
+ clip_box: unknown
3488
+ clip_on: unknown
3489
+ clip_path: unknown
3490
+ color or c: color
3491
+ figure: `~matplotlib.figure.Figure` or `~matplotlib.figure.SubFigure`
3492
+ fontfamily or family or fontname: {FONTNAME, 'serif', 'sans-serif', 'cursive', 'fantasy', 'monospace'}
3493
+ fontproperties or font or font_properties: `.font_manager.FontProperties` or `str` or `pathlib.Path`
3494
+ fontsize or size: float or {'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'}
3495
+ fontstretch or stretch: {a numeric value in range 0-1000, 'ultra-condensed', 'extra-condensed', 'condensed', 'semi-condensed', 'normal', 'semi-expanded', 'expanded', 'extra-expanded', 'ultra-expanded'}
3496
+ fontstyle or style: {'normal', 'italic', 'oblique'}
3497
+ fontvariant or variant: {'normal', 'small-caps'}
3498
+ fontweight or weight: {a numeric value in range 0-1000, 'ultralight', 'light', 'normal', 'regular', 'book', 'medium', 'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy', 'extra bold', 'black'}
3499
+ gid: str
3500
+ horizontalalignment or ha: {'left', 'center', 'right'}
3501
+ in_layout: bool
3502
+ label: object
3503
+ linespacing: float (multiple of font size)
3504
+ math_fontfamily: str
3505
+ mouseover: bool
3506
+ multialignment or ma: {'left', 'right', 'center'}
3507
+ parse_math: bool
3508
+ path_effects: list of `.AbstractPathEffect`
3509
+ picker: None or bool or float or callable
3510
+ position: (float, float)
3511
+ rasterized: bool
3512
+ rotation: float or {'vertical', 'horizontal'}
3513
+ rotation_mode: {None, 'default', 'anchor'}
3514
+ sketch_params: (scale: float, length: float, randomness: float)
3515
+ snap: bool or None
3516
+ text: object
3517
+ transform: `~matplotlib.transforms.Transform`
3518
+ transform_rotates_text: bool
3519
+ url: str
3520
+ usetex: bool, default: text.usetex
3521
+ verticalalignment or va: {'baseline', 'bottom', 'center', 'center_baseline', 'top'}
3522
+ visible: bool
3523
+ wrap: bool
3524
+ x: float
3525
+ y: float
3526
+ zorder: float
3527
+
3528
+ Notes
3529
+ -----
3530
+
3531
+ .. note::
3532
+
3533
+ This is the :ref:`pyplot wrapper <pyplot_interface>` for `.axes.Axes.text`.
3534
+
3535
+ Examples
3536
+ --------
3537
+ Individual keyword arguments can be used to override any given
3538
+ parameter::
3539
+
3540
+ >>> text(x, y, s, fontsize=12)
3541
+
3542
+ The default transform specifies that text is in data coords,
3543
+ alternatively, you can specify text in axis coords ((0, 0) is
3544
+ lower-left and (1, 1) is upper-right). The example below places
3545
+ text in the center of the Axes::
3546
+
3547
+ >>> text(0.5, 0.5, 'matplotlib', horizontalalignment='center',
3548
+ ... verticalalignment='center', transform=ax.transAxes)
3549
+
3550
+ You can put a rectangular box around the text instance (e.g., to
3551
+ set a background color) by using the keyword *bbox*. *bbox* is
3552
+ a dictionary of `~matplotlib.patches.Rectangle`
3553
+ properties. For example::
3554
+
3555
+ >>> text(x, y, s, bbox=dict(facecolor='red', alpha=0.5))
3556
+ """
3557
+ def __init__(self,x,y,string,easing=None,axis=None, *args, **kwargs):
3558
+ self.mpl_obj_type = mpl.patches.PathPatch
3559
+ self.mpl_plot_type = plt.text
3560
+ super().__init__(easing=easing,axis=axis,*args, **kwargs)
3561
+ self.x = x
3562
+ self.y = y
3563
+ self.string = string
3564
+
3565
+ def anim_function(self,x,kwargs):
3566
+ s = self.string
3567
+ for anim in self.anims:
3568
+ if anim['name'] not in ['draw','sequence']:
3569
+ continue
3570
+ t = self.get_t_from_x(anim,x)
3571
+ if anim['name'] == 'sequence':
3572
+ if x < anim['delay'] or x > anim['delay'] + anim['duration']:
3573
+ continue
3574
+ _i = max(min(round(t*len(s)),len(s)-1),0)
3575
+ s = s[_i]
3576
+ elif anim['name'] == 'draw':
3577
+ if anim['reverse']:
3578
+ i_max = max(round((1-t)*len(s)),0)
3579
+ else:
3580
+ i_max = min(round(t*len(s)),len(s))
3581
+ s = s[:i_max]
3582
+
3583
+ self.function(self.x,self.y,s,kwargs)
3584
+
3585
+ def function(self,data_x,data_y,s,kwargs):
3586
+ if len(s) == 0:
3587
+ return
3588
+ if 'fontsize' in kwargs:
3589
+ fp = FontProperties(size=kwargs['fontsize'])
3590
+ else:
3591
+ fp = FontProperties()
3592
+ tp = TextPath((data_x, data_y), s, prop=fp)
3593
+ patch = PathPatch(tp, **kwargs)
3594
+ self.axis.add_patch(patch)
3595
+
3596
+ px_per_pt = self.axis.get_figure().dpi / 72.0
3597
+ dx_per_px = (self.axis.get_xlim()[1] - self.axis.get_xlim()[0]) / self.axis.bbox.width
3598
+ dy_per_px = (self.axis.get_ylim()[1] - self.axis.get_ylim()[0]) / self.axis.bbox.height
3599
+ sx = px_per_pt * dx_per_px
3600
+ sy = px_per_pt * dy_per_px
3601
+
3602
+ T = (
3603
+ transforms.Affine2D()
3604
+ .scale(sx, sy)
3605
+ )
3606
+ patch.set_path(patch.get_path().transformed(T))
3607
+ self.path = patch.get_path()
3608
+ self.obj = patch
3609
+
3610
+ class svg(Animation):
3611
+ """A vector SVG object.
3612
+
3613
+ Parameters
3614
+ ----------
3615
+ data : str
3616
+ SVG object as a string.
3617
+ fc : color, default=None
3618
+ the face color.
3619
+ ec : color, default='k'
3620
+ the edge color.
3621
+ lw : float, default=2
3622
+ the line width.
3623
+ """
3624
+ def __init__(self, data, fc=None, ec='k', lw=2,easing=None,axis=None, *args, **kwargs):
3625
+ self.mpl_obj_type = mpl.patches.PathPatch
3626
+ kwargs['facecolor'] = fc
3627
+ kwargs['edgecolor'] = ec
3628
+ kwargs['linewidth'] = lw
3629
+ super().__init__(easing=easing,axis=axis,*args, **kwargs)
3630
+ self.data = data
3631
+ self.path = None
3632
+
3633
+ def draw_svg(self,kwargs):
3634
+ self.obj = mpl.patches.PathPatch(self.path,**kwargs)
3635
+ self.axis.add_patch(self.obj)
3636
+
3637
+ def _slice_path(self, path, i0, i1):
3638
+ verts = path.vertices[i0:i1]
3639
+ if len(verts) == 0:
3640
+ return None
3641
+ codes = np.full(len(verts), mpl.path.Path.LINETO, dtype=np.uint8)
3642
+ codes[0] = mpl.path.Path.MOVETO
3643
+ return mpl.path.Path(verts, codes)
3644
+
3645
+ def anim_function(self,x,kwargs):
3646
+ self.path = parse_path(self.data)
3647
+ path = self.path
3648
+ n = len(path.vertices)
3649
+
3650
+ i0, i1 = 0, n
3651
+ has_path_anim = False
3652
+
3653
+ for anim in self.anims:
3654
+ if anim['name'] not in ['draw','sequence']:
3655
+ continue
3656
+ if x < anim['delay'] or x > anim['delay'] + anim['duration']:
3657
+ continue
3658
+
3659
+ has_path_anim = True
3660
+ t = self.get_t_from_x(anim,x)
3661
+
3662
+ if anim['name'] == 'draw':
3663
+ if anim['reverse']:
3664
+ i0, i1 = 0, max(round((1 - t) * n), 0)
3665
+ else:
3666
+ i0, i1 = 0, min(round(t * n), n)
3667
+ elif anim['name'] == 'sequence':
3668
+ i = max(min(round(t * (n - 1)), n - 1), 0)
3669
+ i0, i1 = max(i - 1, 0), min(i + 1, n)
3670
+
3671
+ frame_path = self._slice_path(path, i0, i1) if has_path_anim else path
3672
+ if frame_path is None:
3673
+ return
3674
+ self.path = frame_path
3675
+ self.function(self.x, self.y, None, kwargs)
3676
+
3677
+ def function(self,data_x,data_y,x,kwargs):
3678
+ if isinstance(kwargs['alpha'],np.ndarray):
3679
+ kwargs['alpha'] = kwargs['alpha'][0]
3680
+ if kwargs['facecolor'] is None:
3681
+ kwargs['facecolor'] = self.axis._get_lines.get_next_color()
3682
+
3683
+ if 'color' in kwargs:
3684
+ kwargs.pop('color')
3685
+
3686
+ self.draw_svg(kwargs)