3
3
import enum
4
4
import tempfile
5
5
import warnings
6
+ from typing import Literal
6
7
from pathlib import Path
7
8
8
9
import matplotlib as mpl
9
10
import matplotlib .pyplot as plt
11
+ import matplotlib .animation as animation
12
+ import matplotlib .figure as figure
10
13
11
14
from . import _axes
12
15
from . import _image as img
17
20
18
21
19
22
def get_tikz_code (
20
- figure = "gcf" ,
23
+ figure : Literal [ "gcf" ] | figure . Figure | animation . TimedAnimation = "gcf" ,
21
24
filepath : str | Path | None = None ,
22
25
axis_width : str | None = None ,
23
26
axis_height : str | None = None ,
@@ -32,6 +35,11 @@ def get_tikz_code(
32
35
extra_axis_parameters : list | set | None = None ,
33
36
extra_groupstyle_parameters : dict = {},
34
37
extra_tikzpicture_parameters : list | set | None = None ,
38
+ extra_animation_parameters : list | set | None = [
39
+ "autoplay" ,
40
+ "autoresume" ,
41
+ "controls" ,
42
+ ],
35
43
extra_lines_start : list | set | None = None ,
36
44
dpi : int | None = None ,
37
45
show_info : bool = False ,
@@ -44,7 +52,7 @@ def get_tikz_code(
44
52
"""Main function. Here, the recursion into the image starts and the
45
53
contents are picked up. The actual file gets written in this routine.
46
54
47
- :param figure: either a Figure object or 'gcf' (default).
55
+ :param figure: either a Figure object or an animation or 'gcf' (default).
48
56
49
57
:param axis_width: If not ``None``, this will be used as figure width within the
50
58
TikZ/PGFPlots output. If ``axis_height`` is not given,
@@ -109,6 +117,10 @@ def get_tikz_code(
109
117
(as a set) to pgfplots.
110
118
:type extra_tikzpicture_parameters: a set of strings for the pfgplots tikzpicture.
111
119
120
+ :param extra_animation_parameters: Extra animation options to be passed
121
+ (as a set) to animateinline when an animation is passed.
122
+ :type extra_animation_parameters: a set of strings for the animateinline animation.
123
+
112
124
:param dpi: The resolution in dots per inch of the rendered image in case
113
125
of QuadMesh plots. If ``None`` it will default to the value
114
126
``savefig.dpi`` from matplotlib.rcParams. Default is ``None``.
@@ -150,6 +162,7 @@ def get_tikz_code(
150
162
if figure == "gcf" :
151
163
figure = plt .gcf ()
152
164
data = {}
165
+ data ["animation" ] = isinstance (figure , animation .Animation )
153
166
data ["axis width" ] = axis_width
154
167
data ["axis height" ] = axis_height
155
168
data ["rel data path" ] = (
@@ -209,39 +222,95 @@ def get_tikz_code(
209
222
if show_info :
210
223
_print_pgfplot_libs_message (data )
211
224
212
- # gather the file content
213
- data , content = _recurse (data , figure )
225
+ def get_figure_tikz_code (
226
+ data ,
227
+ figure ,
228
+ wrap : bool = wrap ,
229
+ include_disclaimer : bool = include_disclaimer ,
230
+ include_colordefs : bool = True ,
231
+ ):
232
+ # gather the file content
233
+ data , content = _recurse (data , figure )
234
+
235
+ # Check if there is still an open groupplot environment. This occurs if not
236
+ # all of the group plot slots are used.
237
+ if "is_in_groupplot_env" in data and data ["is_in_groupplot_env" ]:
238
+ content .extend (data ["flavor" ].end ("groupplot" ) + "\n \n " )
239
+ data ["is_in_groupplot_env" ] = False
240
+
241
+ code = """"""
242
+
243
+ if include_disclaimer :
244
+ disclaimer = f"This file was created with tikzplotlib v{ __version__ } ."
245
+ code += _tex_comment (disclaimer )
246
+
247
+ # write the contents
248
+ if wrap and add_axis_environment :
249
+ code += data ["flavor" ].start ("tikzpicture" )
250
+ if extra_tikzpicture_parameters :
251
+ code += "[\n " + ",\n " .join (extra_tikzpicture_parameters ) + "\n ]"
252
+ code += "\n "
253
+ if extra_lines_start :
254
+ code += "\n " .join (extra_lines_start ) + "\n "
255
+ code += "\n "
256
+
257
+ coldefs = _get_color_definitions (data )
258
+ if coldefs and include_colordefs :
259
+ code += "\n " .join (coldefs ) + "\n \n "
260
+
261
+ code += "" .join (content )
262
+
263
+ if wrap and add_axis_environment :
264
+ code += data ["flavor" ].end ("tikzpicture" ) + "\n "
265
+
266
+ return data , content , code
267
+
268
+ if isinstance (figure , animation .TimedAnimation ):
269
+ extra_animation_parameters = list (extra_animation_parameters or [])
270
+ if figure ._repeat and "loop" not in extra_animation_parameters :
271
+ extra_animation_parameters .append ("loop" )
272
+
273
+ data ["framerate" ] = 1000 / figure ._interval
274
+
275
+ frames = []
276
+
277
+ for frame in figure .new_frame_seq ():
278
+ figure ._draw_frame (frame )
279
+ data , content , code = get_figure_tikz_code (
280
+ data ,
281
+ figure ._fig ,
282
+ wrap = True ,
283
+ include_disclaimer = False ,
284
+ include_colordefs = False ,
285
+ )
286
+ frames .append (f"% Frame { frame + 1 } \n { code } \n " )
214
287
215
- # Check if there is still an open groupplot environment. This occurs if not
216
- # all of the group plot slots are used.
217
- if "is_in_groupplot_env" in data and data ["is_in_groupplot_env" ]:
218
- content .extend (data ["flavor" ].end ("groupplot" ) + "\n \n " )
288
+ code = """"""
219
289
220
- # write disclaimer to the file header
221
- code = """"""
290
+ if include_disclaimer :
291
+ disclaimer = f"This file was created with tikzplotlib v{ __version__ } ."
292
+ code += _tex_comment (disclaimer )
222
293
223
- if include_disclaimer :
224
- disclaimer = f"This file was created with tikzplotlib v{ __version__ } ."
225
- code += _tex_comment (disclaimer )
294
+ # write the contents
295
+ if wrap :
296
+ code += data ["flavor" ].start ("animateinline" )
297
+ if extra_animation_parameters :
298
+ code += "[\n " + ",\n " .join (extra_animation_parameters ) + "\n ]"
299
+ code += f"{{{ data ['framerate' ]} }}"
300
+ code += "\n "
301
+ code += "\n "
226
302
227
- # write the contents
228
- if wrap and add_axis_environment :
229
- code += data ["flavor" ].start ("tikzpicture" )
230
- if extra_tikzpicture_parameters :
231
- code += "[\n " + ",\n " .join (extra_tikzpicture_parameters ) + "\n ]"
232
- code += "\n "
233
- if extra_lines_start :
234
- code += "\n " .join (extra_lines_start ) + "\n "
235
- code += "\n "
303
+ coldefs = _get_color_definitions (data )
304
+ if coldefs :
305
+ code += "\n " .join (coldefs ) + "\n \n "
236
306
237
- coldefs = _get_color_definitions (data )
238
- if coldefs :
239
- code += "\n " .join (coldefs ) + "\n \n "
307
+ code += "\n \\ newframe\n " .join (frames )
240
308
241
- code += "" .join (content )
309
+ if wrap :
310
+ code += data ["flavor" ].end ("animateinline" ) + "\n "
242
311
243
- if wrap and add_axis_environment :
244
- code += data [ "flavor" ]. end ( "tikzpicture" ) + " \n "
312
+ else :
313
+ data , content , code = get_figure_tikz_code ( data , figure )
245
314
246
315
if standalone :
247
316
# When using pdflatex, \\DeclareUnicodeCharacter is necessary.
@@ -406,6 +475,7 @@ class Flavors(enum.Enum):
406
475
\\ usetikzlibrary{{{tikzlibs}}}
407
476
\\ pgfplotsset{{compat=newest}}
408
477
""" ,
478
+ r"\usepackage{{{}}}" ,
409
479
)
410
480
context = (
411
481
r"\start{}" ,
@@ -422,6 +492,7 @@ class Flavors(enum.Enum):
422
492
\\ unexpanded\\ def\\ startgroupplot{{\\ groupplot}}
423
493
\\ unexpanded\\ def\\ stopgroupplot{{\\ endgroupplot}}
424
494
""" ,
495
+ r"\usemodule[{}]" ,
425
496
)
426
497
427
498
def start (self , what ):
@@ -430,6 +501,9 @@ def start(self, what):
430
501
def end (self , what ):
431
502
return self .value [1 ].format (what )
432
503
504
+ def usepackage (self , * what ):
505
+ return self .value [4 ]
506
+
433
507
def preamble (self , data = None ):
434
508
if data is None :
435
509
data = {
@@ -438,7 +512,13 @@ def preamble(self, data=None):
438
512
}
439
513
pgfplotslibs = "," .join (data ["pgfplots libs" ])
440
514
tikzlibs = "," .join (data ["tikz libs" ])
441
- return self .value [3 ].format (pgfplotslibs = pgfplotslibs , tikzlibs = tikzlibs )
515
+ extra_imports = (
516
+ self .usepackage ("animate" ) + "\n " if data .get ("animation" , False ) else ""
517
+ )
518
+ return (
519
+ self .value [3 ].format (pgfplotslibs = pgfplotslibs , tikzlibs = tikzlibs )
520
+ + extra_imports
521
+ )
442
522
443
523
def standalone (self , code ):
444
524
docenv = self .value [2 ]
0 commit comments