···2525 def save_video(self, file, **kwargs):
2626 video.save_video(self.frames, file, **kwargs)
27272828+ def save_spritesheet(self, file, **kwargs):
2929+ video.save_spritesheet(self.frames, file, **kwargs)
3030+28312932class FrameAnimationContext:
3033 def __init__(self, draw_func=None, out_file=None,
3131- jupyter=False, pause=False, clear=True, delay=0, disable=False,
3232- video_args=None, _patch_delay=0.05):
3434+ jupyter=False, spritesheet=False, pause=False,
3535+ clear=True, delay=0, disable=False, video_args=None,
3636+ _patch_delay=0.05):
3337 self.jupyter = jupyter
3838+ self.spritesheet = spritesheet
3439 self.disable = disable
3540 if self.jupyter and not self.disable:
3641 from IPython import display
···6772 if exc_value is None:
6873 # No error
6974 if self.out_file is not None and not self.disable:
7070- self.anim.save_video(self.out_file, **self.video_args)
7575+ if self.spritesheet:
7676+ self.anim.save_spritesheet(self.out_file, **self.video_args)
7777+ else:
7878+ self.anim.save_video(self.out_file, **self.video_args)
717972807381def frame_animate_video(out_file, draw_func=None, jupyter=False, **video_args):
···77857886 Example:
7987 ```
8080- with animate_video('video.mp4') as anim:
8888+ with frame_animate_video('video.mp4') as anim:
8189 while True:
8290 ...
8391 anim.draw_frame(...)
···8694 return FrameAnimationContext(draw_func=draw_func, out_file=out_file,
8795 jupyter=jupyter, video_args=video_args)
88969797+def frame_animate_spritesheet(out_file, draw_func=None, jupyter=False,
9898+ **video_args):
9999+ '''
100100+ Returns a context manager that stores frames and saves a spritesheet when
101101+ the context exits.
102102+103103+ Example:
104104+ ```
105105+ with frame_animate_spritesheet('sheet.png', row_length=10) as anim:
106106+ while True:
107107+ ...
108108+ anim.draw_frame(...)
109109+ ```
110110+ '''
111111+ return FrameAnimationContext(draw_func=draw_func, out_file=out_file,
112112+ jupyter=jupyter, spritesheet=True,
113113+ video_args=video_args)
114114+8911590116def frame_animate_jupyter(draw_func=None, pause=False, clear=True, delay=0.1,
91117 **kwargs):
···9412095121 Example:
96122 ```
9797- with animate_jupyter(delay=0.5) as anim:
123123+ with frame_animate_jupyter(delay=0.5) as anim:
98124 while True:
99125 ...
100126 anim.draw_frame(...)
+43
drawsvg/video.py
···148148 print()
149149 print(f'Converting to video')
150150 imageio.mimsave(file, frames, **kwargs)
151151+152152+def save_spritesheet(frames, file, row_length=None, verbose=False, **kwargs):
153153+ '''
154154+ Save a series of drawings as a bitmap spritesheet
155155+156156+ Arguments:
157157+ frames: A list of `Drawing`s or a list of `numpy.array`s.
158158+ file: File name or file like object to write the spritesheet to. The
159159+ extension determines the output format.
160160+ row_length: The length (in frames) of one row in the spritesheet.
161161+ If not provided, all frames go on one row.
162162+ align_bottom: If frames are different sizes, align the bottoms of each
163163+ frame in the video.
164164+ align_right: If frames are different sizes, align the right edge of each
165165+ frame in the video.
166166+ bg: If frames are different sizes, fill the background with this color.
167167+ (default is white: (255, 255, 255, 255))
168168+ **kwargs: Other arguments to imageio.imsave().
169169+170170+ '''
171171+ np, imageio = delay_import_np_imageio()
172172+ if not isinstance(frames[0], np.ndarray):
173173+ frames = render_svg_frames(frames, verbose=verbose, **kwargs)
174174+ kwargs.pop('align_bottom', None)
175175+ kwargs.pop('align_right', None)
176176+ kwargs.pop('bg', None)
177177+178178+ cols = row_length if row_length is not None else len(frames)
179179+ rows = (len(frames) - 1) // cols + 1
180180+181181+ if rows * cols > len(frames): # Unfilled final row
182182+ empty_frame = np.zeros(frames[0].shape, dtype=frames[0].dtype)
183183+ frames.extend([empty_frame] * (rows * cols - len(frames)))
184184+185185+ block_arrangement = []
186186+ for row in range(rows):
187187+ next_row_end = (row+1)*cols
188188+ block_arrangement.append([
189189+ [frame] for frame in frames[row*cols:next_row_end]
190190+ ])
191191+192192+ spritesheet = np.block(block_arrangement)
193193+ imageio.imsave(file, spritesheet, **kwargs)