Programmatically generate SVG (vector) images, animations, and interactive Jupyter widgets

Add Drawing.as_spritesheet and save_spritesheet

Changed files
+69 -6
drawsvg
examples
+2 -1
README.md
···
#d.display_image() # Display SVG as an image (will not be interactive)
#d.display_iframe() # Display as interactive SVG (alternative)
#d.as_gif('orbit.gif', fps=10) # Render as a GIF image, optionally save to file
-
#d.as_mp4('orbig.mp4', fps=60) # Render as an MP4 video, optionally save to file
+
#d.as_mp4('orbig.mp4', fps=60, verbose=True) # Render as an MP4 video, optionally save to file
+
#d.as_spritesheet('orbit-spritesheet.png', row_length=10, fps=3) # Render as a spritesheet
d.display_inline() # Display as interactive SVG
```
+12
drawsvg/drawing.py
···
self.as_mp4(
fname, fps=fps, duration=duration, context=context,
verbose=verbose)
+
def save_spritesheet(self, fname, fps=10, duration=None, context=None,
+
row_length=None, verbose=False):
+
self.as_spritesheet(
+
fname, fps=fps, duration=duration, context=context,
+
row_length=row_length, verbose=verbose)
def as_video(self, to_file=None, fps=10, duration=None,
mime_type=None, file_type=None, context=None, verbose=False):
if file_type is None and mime_type is None:
···
return self.as_video(
to_file=to_file, fps=fps, duration=duration, context=context,
mime_type='video/mp4', file_type='mp4', verbose=verbose)
+
def as_spritesheet(self, to_file=None, fps=10, duration=None, context=None,
+
row_length=None, verbose=False):
+
frames = self.as_animation_frames(
+
fps=fps, duration=duration, context=context)
+
sheet = video.render_spritesheet(
+
frames, row_length=row_length, verbose=verbose)
+
return raster.Raster.from_arr(sheet, out_file=to_file)
def _repr_svg_(self):
'''Display in Jupyter notebook.'''
return self.as_svg(randomize_ids=True)
+24
drawsvg/raster.py
···
) from e
return cairosvg
+
def delay_import_imageio():
+
try:
+
import imageio
+
except ImportError as e:
+
raise ImportError(
+
'Optional dependencies not installed. '
+
'Install with `python3 -m pip install "drawsvg[all]"` '
+
'or `python3 -m pip install "drawsvg[raster]"`. '
+
'See https://github.com/cduck/drawsvg#full-feature-install '
+
'for more details.'
+
) from e
+
return imageio
+
class Raster:
def __init__(self, png_data=None, png_file=None):
···
cairosvg = delay_import_cairo()
cairosvg.svg2png(bytestring=svg_data, write_to=out_file)
return Raster(None, png_file=out_file)
+
@staticmethod
+
def from_arr(arr, out_file=None):
+
imageio = delay_import_imageio()
+
if out_file is None:
+
with io.BytesIO() as f:
+
imageio.imwrite(f, arr, format='png')
+
f.seek(0)
+
return Raster(f.read())
+
else:
+
imageio.imwrite(out_file, arr, format='png')
+
return Raster(None, png_file=out_file)
def _repr_png_(self):
if self.png_data:
return self.png_data
+31 -5
drawsvg/video.py
···
print(f'Converting to video')
imageio.mimsave(file, frames, **kwargs)
-
def save_spritesheet(frames, file, row_length=None, verbose=False, **kwargs):
+
def render_spritesheet(frames, row_length=None, verbose=False, **kwargs):
'''
Save a series of drawings as a bitmap spritesheet
Arguments:
frames: A list of `Drawing`s or a list of `numpy.array`s.
-
file: File name or file like object to write the spritesheet to. The
-
extension determines the output format.
row_length: The length (in frames) of one row in the spritesheet.
If not provided, all frames go on one row.
align_bottom: If frames are different sizes, align the bottoms of each
···
**kwargs: Other arguments to imageio.imsave().
'''
-
np, imageio = delay_import_np_imageio()
+
np, _ = delay_import_np_imageio()
if not isinstance(frames[0], np.ndarray):
frames = render_svg_frames(frames, verbose=verbose, **kwargs)
kwargs.pop('align_bottom', None)
kwargs.pop('align_right', None)
-
kwargs.pop('bg', None)
+
bg = kwargs.pop('bg', (255, 255, 255, 255))
cols = row_length if row_length is not None else len(frames)
rows = (len(frames) - 1) // cols + 1
if rows * cols > len(frames): # Unfilled final row
empty_frame = np.zeros(frames[0].shape, dtype=frames[0].dtype)
+
empty_frame[..., :] = bg[:empty_frame.shape[-1]]
frames.extend([empty_frame] * (rows * cols - len(frames)))
block_arrangement = []
···
])
spritesheet = np.block(block_arrangement)
+
return spritesheet
+
+
def save_spritesheet(frames, file, row_length=None, verbose=False, **kwargs):
+
'''
+
Save a series of drawings as a bitmap spritesheet
+
+
Arguments:
+
frames: A list of `Drawing`s or a list of `numpy.array`s.
+
file: File name or file like object to write the spritesheet to. The
+
extension determines the output format.
+
row_length: The length (in frames) of one row in the spritesheet.
+
If not provided, all frames go on one row.
+
align_bottom: If frames are different sizes, align the bottoms of each
+
frame in the video.
+
align_right: If frames are different sizes, align the right edge of each
+
frame in the video.
+
bg: If frames are different sizes, fill the background with this color.
+
(default is white: (255, 255, 255, 255))
+
**kwargs: Other arguments to imageio.imsave().
+
+
'''
+
_, imageio = delay_import_np_imageio()
+
spritesheet = render_spritesheet(
+
frames, row_length=row_length, verbose=verbose, **kwargs)
+
kwargs.pop('align_bottom', None)
+
kwargs.pop('align_right', None)
+
kwargs.pop('bg', None)
imageio.imsave(file, spritesheet, **kwargs)
examples/orbit-spritesheet.png

This is a binary file and will not be displayed.