Search code examples
pythonmatplotlibmatplotlib-animation

How to move a patch along a path?


I am trying to animate a patch.Rectangle object using matplotlib. I want the said object to move along a path.Arc. A roundabout way to do this would be (approximately) :

import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import matplotlib.patches as mpat

fig, ax = plt.subplots()
ax.set(xlim=(0, 10), ylim=(0, 10))


# generate the patch
patch = mpat.Rectangle((5, 5), 1, 4)
patch.rotation_point = 'center'

# generate the path to follow
path_to_follow = mpat.Arc((5, 5), 2, 2)
ax.add_patch(path_to_follow)


def init():
    patch.set(x=5, y=5)
    ax.add_patch(patch)
    return patch,


def animate(i, ax):
    new_x = 5 + np.sin(np.radians(i)) - 0.5  # parametric form for the circle
    new_y = 5 + np.cos(np.radians(i)) - 2
    patch.set(x=new_x, y=new_y, angle=90-i)
    return patch,


anim = animation.FuncAnimation(fig, animate,
                               init_func=init,
                               fargs=[ax],
                               frames=360,
                               interval=10,
                               blit=True)

plt.show()

The rectangle follows a circle, but a parametric one. Would it be possible to make it follow any path?

In other words, I would like to know if there are other simpler methods to do this (make my patch follow my path, here a circle), and if that could be generalized to other path.

Thanks in advance !

I searched into the matplotlib doc for a methods which gives the parametric form for a given path (but apparently there is not), or for a methods which directly move a patch along a path (obviously, there was not).


Solution

  • Here is one way to use matplotlib.path.Path to generate a path, whose vertices can be obtained using the method cleaned, to move a patch along it.

    I have tried to showcase how blue and red colored Rectangles can be moved along a (blue) linear path and a (red) circular path, respectively:

    import numpy as np
    from matplotlib import pyplot as plt
    from matplotlib import animation, path
    import matplotlib.patches as mpat
    
    fig, ax = plt.subplots()
    ax.set(xlim=(0, 10), ylim=(0, 10))
    
    # generate a linear path
    path1 = np.column_stack((np.arange(500)/50, np.arange(500)/50))
    
    # generate a circular path
    circle = path.Path.circle(center=(5, 5), radius=1)
    path2 = circle.cleaned().vertices[:-3]
    
    # create patches
    patch1 = mpat.Rectangle((0, 0), 1, 3)
    patch2 = mpat.Rectangle((0, 0), 1, 3, color='red', fill=None)
    
    # plot path vertices
    plt.scatter(x=path1[:, 0], y=path1[:, 1], s=2)
    plt.scatter(x=path2[:, 0], y=path2[:, 1], color='red', s=2)
    
    def init():
        patch1.set(x=0, y=0)
        patch2.set(x=5, y=6)
        ax.add_patch(patch1)
        ax.add_patch(patch2)
        return [patch1, patch2]
    
    def animate(i, ax):
        j = i % 500   # path1 has shape (500, 2)
        k = (i % 16)  # path2 has shape (16, 2)
        patch1.set(x=path1[j][0], y=path1[j][1], angle=-j)
        patch2.set(x=path2[k][0], y=path2[k][1], angle=-k)
        return [patch1, patch2]
    
    anim = animation.FuncAnimation(fig, animate,
                                   init_func=init,
                                   fargs=[ax],
                                   frames=360,
                                   interval=100,
                                   blit=True)
    
    plt.show()