Sunday, April 23, 2017

Tweened vector animations with python

What?

In this blog post I'll show how to create tweened vector animations with python. People who arrive here probably ought to look at Zulko's excellent blog post first to find the excellent gizeh library, written on top of cairo, that allows for generating vector graphics animations in python. And then there's also Zulko's wonderful moviepy library that allows saving these animations to animated .gif or one of the many video formats supported by ffmpeg.

While experimenting with Zulko's libraries, I was struck by what seemed a missing feature: a convenient way to introduce "easing" in animations. I subsequently found the pytweening library which implements the formulas required to perform easing and created my own vectortween library to wrap pytweening and make it usable in combination with Zulko's libraries (as well as other libraries).

How?

Here's some sample code using plain gizeh. It creates a yellow circle moving from the top left to the middle of the drawing:

if __name__ == "__main__":
    import gizeh
    import moviepy.editor as mpy

    W, H = 250, 250  # width, height, in pixels
    duration = 5 # duration of the clip, in seconds
    fps = 25

    def make_frame(t):
       # prepare a drawing surface
       surface = gizeh.Surface(W, H)

       # as time t evolves from 0 to 5 seconds, calculate new position of
       # the yellow circle
       gizeh.circle(30, xy=((t*22),(t*22)), fill=(1,1,0)).draw(surface)
       return surface.get_npimage()

    clip = mpy.VideoClip(make_frame, duration=duration)
    clip.write_gif("example0-plain.gif", fps=fps, fuzz=10)

And what does it look like?

Boring, right?

Now how about you wanted add some excitement by letting the yellow circle bounce a bit upon arrival in the middle of the picture? Are you ready to write out the math formulas to do so? If yes, by all means go ahead! If no, you could try the vectortweening library to write this:

if __name__ == "__main__":
    import gizeh
    import moviepy.editor as mpy

    from vectortween.PointAnimation import PointAnimation

    W, H = 250, 250  # width, height, in pixels
    duration = 5 # duration of the clip, in seconds
    fps = 25

    def make_frame(t):
        # prepare a drawing surface
        surface = gizeh.Surface(W, H)

        # p animates from position (0,0) to position (110,110) with an
        # easeOutElastic tweening method
        p = PointAnimation((0, 0), (110, 110), tween=['easeOutElastic', 1, 0.2])

        # circle to appear at second 0.2, animate from second 1 to 4,
        # then stay visible until disappearance in second 5
        xy = p.make_frame(t, 0.2, 1, 4, 5)

        # because graphics can disappear, we must check for None
        if None not in xy:
            gizeh.circle(30, xy=xy, fill=(1,1,0)).draw(surface)
            return surface.get_npimage()

    clip = mpy.VideoClip(make_frame, duration=duration)
    clip.write_gif("example0-tween.gif", fps=fps, fuzz=10)

And what does it look like?


We want more, we want more...

Ok, ok... I hear you. Here's another one:


Ooooh I need this now!

Well of course you do... Here's all you need!