A. Neo
A. Neo

Reputation: 113

Make a figure rotate with the rotation matrix using matplotlib

I want to rotate a figure (made of lines) in a 2D space. I use this technique : (1) I translate the figure at the origin. The rotation point is at (0,0) (2) I execute the rotation (3) I put the figure back

The problem comes from my rotation function. No idea why it doesnt work since I used the rotation matrix. You dont need to necessarely look the entire code, just look at the "rotation" function and the "animate" function. You'll see I put comments to help you.

If you execute the whole thing, you'll see a line moving slowly. I dont know what it is suppose to be but it is not the result expected. I should see my figure rotate.

If you dont make the rotation and just translate twice (step (1) and step (3) of my procedure), you'll see the entire figure not moving (it is expected since I move it to the center and put it back where it was) and you can see what the figure looks like. So the only problem comes from the rotation function.

import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import pylab as pl
import math

#Deplacement de la base pour pouvoir utiliser la matrice de rotation
def center(x,y,c):
    for i in range(len(x)):
        x[i] = x[i] - c[0]
    for i in range(len(y)):
        y[i] = y[i] - c[1]
    return None

#Remettre la base à sa place
def recenter(x,y,c):
    for i in range(len(x)):
        x[i] = x[i] + c[0]
    for i in range(len(y)):
        y[i] = y[i] + c[1]
    return None

#Rotation
def rotation(x,y,angle):
    my_list = [[],[]]
    for i in range(len(x)):
        my_list[0].append(x[i]*math.cos(angle) + y[i]*math.sin(angle))
    for j in range(len(y)):
        my_list[1].append(x[i]*math.sin(angle)*(-1) + y[i]*math.cos(angle))
    return my_list

#Here is the rotation matrix
#cos sin
#-sin cos

# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(-125, 125), ylim=(-250, 250))
line, = ax.plot([], [], lw=2)

#Les lignes du tableau
plt.plot([0, 125], [50, 50], color='k', linestyle='-', linewidth=2)
plt.plot([0, 125], [200, 200], color='k', linestyle='-', linewidth=2)
plt.plot([17.5, 107.5], [80, 80], color='k', linestyle='-', linewidth=2)
plt.plot([17.5, 107.5], [110, 110], color='k', linestyle='-', linewidth=2)
plt.plot([17.5, 107.5], [140, 140], color='k', linestyle='-', linewidth=2)
plt.plot([17.5, 107.5], [170, 170], color='k', linestyle='-', linewidth=2)
plt.plot([17.5, 17.5], [80, 170], color='k', linestyle='-', linewidth=2)
plt.plot([17.5, 17.5], [80, 170], color='k', linestyle='-', linewidth=2)
plt.plot([47.5, 47.5], [80, 170], color='k', linestyle='-', linewidth=2)
plt.plot([77.5, 77.5], [80, 170], color='k', linestyle='-', linewidth=2)
plt.plot([107.5, 107.5], [80, 170], color='k', linestyle='-', linewidth=2)

# initialization function: plot the background of each frame
def init():
    line.set_data([], [])
    return line,

# animation function. This is called sequentially
def animate(i):
    c1 = 62.5
    c2 = 10
    pt_rot = (c1, c2) #Point from which I want to rotate
    x = [47.5, 47.5, 77.5, 77.5, 47.5, 62.5, c1, 57.5, 67.5]
    y = [15, 45, 45, 15, 15, 15, c2, 10, 10]

    center(x,y,pt_rot)
    #Uncomment these 3 following lines to test the double translation
    my_list = rotation(x,y,(i/180))
    x = my_list[0]
    y = my_list[1]
    recenter(x, y, pt_rot)

    line.set_data(x,y)

    return line,

# call the animator.  blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
                               frames=200, interval=20, blit=True)

plt.show()

Upvotes: 2

Views: 2408

Answers (1)

ImportanceOfBeingErnest
ImportanceOfBeingErnest

Reputation: 339120

There are two main problems in your script:

  1. In the rotation function, you let j loop but set x and y in terms of i, which is a constant after the first loop.

  2. You set the angle of rotation in degrees. However, the trigonometric functions require the angle set in radiants. So basically you need to multiply the angle by 2pi.

A third point which is not really a problem but makes the script hard to read is that you return None in the translation functions. I find it easier to read if each function returns some values and one would put those values back to the next function.

Here is a complete runnable script.

import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import math


def center(x,y,c):
    for i in range(len(x)):
        x[i] = x[i] - c[0]
    for i in range(len(y)):
        y[i] = y[i] - c[1]
    # return x,y  <--------------------- here 
    return x,y

def recenter(x,y,c):
    for i in range(len(x)):
        x[i] = x[i] + c[0]
    for i in range(len(y)):
        y[i] = y[i] + c[1]
    # return x,y  <--------------------- here 
    return x,y

def rotation(x,y,angle):
    my_list = [[],[]]
    for i in range(len(x)):
        my_list[0].append(x[i]*math.cos(angle) + y[i]*math.sin(angle))
    for j in range(len(y)):
        #Big mistake here, replace i by j <--------------------- here 
        my_list[1].append(x[j]*math.sin(angle)*(-1.) + y[j]*math.cos(angle))
    return my_list[0], my_list[1]



fig = plt.figure()
ax = plt.axes(xlim=(10, 110), ylim=(-40, 60))
ax.set_aspect('equal')
line, = ax.plot([],[], lw=2)

def init():
    line.set_data([],[])
    return line,

def animate(i):
    c1 = 62.5
    c2 = 10
    pt_rot = (c1, c2)
    x = [47.5, 47.5, 77.5, 77.5, 47.5, 62.5, c1, 57.5, 67.5]
    y = [15, 45, 45, 15, 15, 15, c2, 10, 10]

    x,y = center(x,y,pt_rot)  # <--------------------- here 
    x,y = rotation(x,y,(i/180.)*np.pi) # <------------ here 
    x,y = recenter(x, y, pt_rot) # <------------------ here 

    line.set_data(x,y)
    return line,


anim = animation.FuncAnimation(fig, animate, init_func=init,
                               frames=359, interval=20, blit=True)

plt.show()

Upvotes: 2

Related Questions