sigma
sigma

Reputation: 247

How to update the texts in each frame in this code?

I have written a code on Python for animating the 'Stereographic Projection' from the Cicle onto the Real line. I have defined three functions named update_plot(),update_texts() and create_animation().(As shown in the code below). Now for the animation purpose I have to pass the former two functions in the animation.FuncAnimation(). So I try to create and empty list named anim = [] and append() two animation object to it. Now the animation corresponding to the function update_plot() is okay but the update_texts() didn't work as I expected. Actually I want to update the coordinates of the points (R_x, R_y) (given in the definition of update_text) in every frame.

I cannot understand how to recover this problem. Please help. Thank you.

I also attached a picture of a moment of the output of the following code.

My Code:

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation

W = 3
H = 1.2


mpl.rc('text', usetex=True)
mpl.rc('font', family='serif')

plt.style.use(['ggplot', 'dark_background'])


def create_circle(x, y):
    circle = plt.Circle((x, y), radius=1, fill=False,
                        edgecolor='skyblue', lw=2)
    return circle


def stereo_project(p):
    x = p[0]
    y = p[1]

    # transformation
    x1 = x / (1 - y)
    y1 = 0

    return x1, y1


def rotate(p, angle):
    vec = np.array([p[0], p[1]]).reshape(2, 1)

    theta = angle
    R = np.array([[np.cos(theta), -np.sin(theta)],
                  [np.sin(theta), np.cos(theta)]])

    point = R.dot(vec)

    return point[0][0], point[1][0]


def update_plot(angle, plot, plot1, plot2, plot3):
    p = (0, 1)
    R_x, R_y = rotate(p, angle)
    SR_x, SR_y = stereo_project((R_x, R_y))
    x = [p[0], R_x]
    y = [p[1], R_y]
    x1 = [R_x, SR_x]
    y1 = [R_y, SR_y]
    plot.set_data(x, y)
    plot1.set_data(x1, y1)
    plot2.set_data([R_x], [R_y])
    plot3.set_data([SR_x], [SR_y])

    return plot, plot1


def update_texts(angle):
    p = (0, 1)
    R_x, R_y = rotate(p, angle)
    plt.text(R_x, R_y, '$({0:.2f}, {1:.2f})$'.format(R_x, R_y),
             horizontalalignment='right',
             verticalalignment='center', fontsize=15, color='orange')



def create_animation():

    fig = plt.figure()
    ax = plt.axes(xlim=(-W, W), ylim=(-H, H))

    plot = plt.plot([], [], '-o', markersize=5, color='c')[0]
    plot1 = plt.plot([], [], '-o', markersize=5, color='c')[0]
    plot2 = plt.plot([], [], '-o', markersize=5, color='m')[0]
    plot3 = plt.plot([], [], '-o', markersize=5, color='lime')[0]


    ax.set_aspect('equal')
    circle = create_circle(0, 0)
    ax.add_patch(circle)

    l1 = [-3, 3]
    l2 = [0, 0]
    plt.plot(l1, l2)

    title = 'Stereographic Projection: $\mathbf{S^{1}} \setminus \{(0, 1)\}$ ' \
            'to $\mathbf{R}$'
    plt.title(title, color='orange', y=1.09)
    plt.grid(False)
    plt.gca().spines['left'].set_visible(False)
    plt.gca().spines['top'].set_visible(False)
    plt.gca().spines['right'].set_visible(False)
    plt.gca().spines['bottom'].set_visible(False)
    plt.gca().set_xticks([])
    plt.gca().set_yticks([])

    anim = []
    anim.append(animation.FuncAnimation(fig, update_plot,
                                        fargs=(plot, plot1, plot2, plot3),
                                        frames=np.arange(0, 2 * np.pi, 0.01),
                                        interval=50, repeat=True))

    anim.append(animation.FuncAnimation(fig, update_texts,
                                        frames=np.arange(0, 2 * np.pi, 0.01),
                                        interval=50, repeat=True))



    plt.show()


if __name__ == '__main__':

    create_animation()

Output. Output

Upvotes: 1

Views: 293

Answers (1)

jwalton
jwalton

Reputation: 5686

Text is an artist, so you can animate it like any other artist.

So, just as in your code you get a handle on the Line2D object returned from plt.plot and then update the lines with set_data, you can get a handle on the text instance created by plt.text and update its position and text with text.set_text and text.set_position.

I also altered your code to handle updating the text and the lines in a single function.

With this I got

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation

W = 3
H = 1.2


mpl.rc('text', usetex=True)
mpl.rc('font', family='serif')

plt.style.use(['ggplot', 'dark_background'])


def create_circle(x, y):
    circle = plt.Circle((x, y), radius=1, fill=False,
                        edgecolor='skyblue', lw=2)
    return circle


def stereo_project(p):
    x = p[0]
    y = p[1]

    # transformation
    x1 = x / (1 - y)
    y1 = 0

    return x1, y1


def rotate(p, angle):
    vec = np.array([p[0], p[1]]).reshape(2, 1)

    theta = angle
    R = np.array([[np.cos(theta), -np.sin(theta)],
                  [np.sin(theta), np.cos(theta)]])

    point = R.dot(vec)

    return point[0][0], point[1][0]


def update_plot(angle, text, plot, plot1, plot2, plot3):
    p = (0, 1)
    R_x, R_y = rotate(p, angle)
    SR_x, SR_y = stereo_project((R_x, R_y))
    x = [p[0], R_x]
    y = [p[1], R_y]
    x1 = [R_x, SR_x]
    y1 = [R_y, SR_y]
    plot.set_data(x, y)
    plot1.set_data(x1, y1)
    plot2.set_data([R_x], [R_y])
    plot3.set_data([SR_x], [SR_y])

    R_x, R_y = rotate(p, angle)
    text.set_text('$({0:.2f}, {1:.2f})$'.format(R_x, R_y))
    text.set_position((R_x, R_y))

    return text, plot, plot1



def create_animation():

    fig = plt.figure()
    ax = plt.axes(xlim=(-W, W), ylim=(-H, H))

    plot = plt.plot([], [], '-o', markersize=5, color='c')[0]
    plot1 = plt.plot([], [], '-o', markersize=5, color='c')[0]
    plot2 = plt.plot([], [], '-o', markersize=5, color='m')[0]
    plot3 = plt.plot([], [], '-o', markersize=5, color='lime')[0]


    ax.set_aspect('equal')
    circle = create_circle(0, 0)
    ax.add_patch(circle)

    l1 = [-3, 3]
    l2 = [0, 0]
    plt.plot(l1, l2)

    p = (0, 1)
    R_x, R_y = rotate(p, 0)
    text = plt.text(R_x, R_y, '$({0:.2f}, {1:.2f})$'.format(R_x, R_y),
             horizontalalignment='right',
             verticalalignment='center', fontsize=15, color='orange')

    title = 'Stereographic Projection: $\mathbf{S^{1}} \setminus \{(0, 1)\}$ ' \
            'to $\mathbf{R}$'
    plt.title(title, color='orange', y=1.09)
    plt.grid(False)
    plt.gca().spines['left'].set_visible(False)
    plt.gca().spines['top'].set_visible(False)
    plt.gca().spines['right'].set_visible(False)
    plt.gca().spines['bottom'].set_visible(False)
    plt.gca().set_xticks([])
    plt.gca().set_yticks([])

    anim = []
    anim.append(animation.FuncAnimation(fig, update_plot,
                                        fargs=(text, plot, plot1, plot2, plot3),
                                        frames=np.arange(0, 2 * np.pi, 0.01),
                                        interval=50, repeat=True))




    plt.show()


if __name__ == '__main__':

    create_animation()

Upvotes: 1

Related Questions