dushnila
dushnila

Reputation: 1

polar animation

I have this task: create a heart animation using polar coordinates. Start from drawing a heart and then add animation as the next step; use variable increase of the rendering angle. Then i need to build other figures in polar coordinates the same way. I also need to make the drawing function separate from the figure, so that it would be possible to change figure with minimal code changes

I tried running this, put the output looks really weird and the other figures don't look as expected when i change theta and r. I am not sure if it's possible to complete this task using tkinter or pygame or something like that. I would be happy if you could come up with any ideas to complete this.

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

fig = plt.figure()
ax = fig.add_subplot(111, polar=True)

theta = np.linspace(0, 2*np.pi, 100)
r = 1 - (np.abs(theta) - 1) ** 2

line, = ax.plot([], [], color='red')

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

def animate(i):
    current_theta = theta[:i]
    current_r = r[:i]
    x_polar = current_r * np.cos(current_theta)
    y_polar = current_r * np.sin(current_theta)
    line.set_data(x_polar, y_polar)
    return line,

ani = animation.FuncAnimation(fig, animate, init_func=init, frames=len(theta), interval=50, blit=True)

plt.show()

Upvotes: -1

Views: 460

Answers (4)

Lập Trần
Lập Trần

Reputation: 1

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

fig = plt.figure() ax = fig.add_subplot(111, polar=True)

theta = np.linspace(0, 2*np.pi, 100) r = 1 - (np.abs(theta) - 1) ** 2

line, = ax.plot([], [], color='red')

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

def animate(i): current_theta = theta[:i] current_r = r[:i] x_polar = current_r * np.cos(current_theta) y_polar = current_r * np.sin(current_theta) line.set_data(x_polar, y_polar) return line,

ani = animation.FuncAnimation(fig, animate, init_func=init, frames=len(theta), interval=50, blit=True)

plt.show()

Upvotes: 0

jared
jared

Reputation: 9046

If you look at the plot on Desmos (link), you see that up to pi/2, the plot looks fine, but the tail end after pi/2 is not what you want. So, the first thing you need to do is to plot theta from 0 to pi/2.

You also want to plot a second half reflected across the x-axis. A y=f(-x) reflects f(x) across the x-axis. We can achieve this by converting the polar coordinates to cartesian like you do in the loop, and then creating the x vector of points as the x coordinates and the negative x coordinates. The y vector is just the y coordinates twice. To ensure the plot is smooth, you will want to reverse the second set of points (for both x and y) so it doesn't restart at the origin.

The code below does what I describe and I've put in code that plots it polar or cartesian coordinates.

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

plt.close("all")

fig, ax = plt.subplots(subplot_kw=dict(projection="polar"))

theta = np.linspace(0, np.pi/2, 100)
r = 1 - (np.abs(theta) - 1) ** 2
x = r*np.cos(theta)
y = r*np.sin(theta)

x_data = np.concatenate((x, -x[::-1]))
y_data = np.concatenate((y, y[::-1]))

r_data = np.sqrt(x_data**2 + y_data**2)
theta_data = np.arctan2(y_data, x_data)

line, = ax.plot([], [], color='red')

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

def animate(i):
    # line.set_data(x_data[:i], y_data[:i])           # cartesian
    line.set_data(theta_data[:i], r_data[:i])       # polar
    return line,

ani = animation.FuncAnimation(fig, animate, init_func=init, frames=len(x_data), interval=10, blit=True)
plt.show()

Polar animation:

Cartesian animation:

Upvotes: 0

Aradhna Sonia
Aradhna Sonia

Reputation: 91

I wrote another code:

# Import modules
import numpy as np 
import matplotlib.pyplot as plt 
from matplotlib.animation import FuncAnimation
# ------------------------------------------ #
  
# Define the figure
fig = plt.figure(figsize=(16,16))
# Setting the axes projection as polar 
ax = fig.add_subplot(111, projection='polar')

# Define the heart equation from https://pavpanchekha.com/blog/heart-polar-coordinates.html
t = np.linspace(0.0, 2.0*np.pi, 200, endpoint=True)
r = np.sin(t)*np.sqrt(np.abs(np.cos(t))) / (np.sin(t) + 7/5) - 2*np.sin(t) + 2

# Define a list that stores temporary data points
# [[theta], [radius]]
data_points = [[], []]

# Define the index of the last data point that needs to be plotted
# For example, if index = 5, data_points[0][-1] = t[5] and data_points[1][-1] = r[5]
index = 0

def animate(i):
     # Global variables
     global data_points, index

     # Clear the plot
     ax.clear()

     # Remove tick labels
     ax.set_xticklabels([])
     ax.set_yticklabels([])

     # Append a data point to the list
     data_points[0].append(t[index])
     data_points[1].append(r[index])

     # Update the index
     index += 1

     # Reset the index and points 
     # if index is out of range
     if (index == len(t)):
          index = 0
          data_points = [[], []]
          
     # Plot the "heart"
     plt.plot(data_points[0], data_points[1], color="red")

     # Define the limits
     ax.set_rlim(min(r), max(r))

     # Remove border
     #ax.axis("off")

# Calling the animation function
ani1 = FuncAnimation(fig, animate, interval=30)

# Display the plot
plt.show()

This yields: (You must increase the number of points in the theta array for a better smooth line) Heart

Change your way of writing the code to mine... I hope this helps!

Upvotes: 0

Flow
Flow

Reputation: 731

It seems you are using the wrong expression for the radius. Your radius is proportional to the sqare of theta and becoming very negative with increasing theta which cannot be represented in a polar plot. Most likely you are missing sine or cosine. Playing around a bit I found r = 1 - (np.abs(np.cos(theta)) - 1) ** 2 to produce a heart shape.

In general though it seems odd to me to calculate theta and r, which are the coordinates of a polar plot and then doing something like a trasformation to cartesian coordinates (x_polar and y_polar) for your plot.

About the separating issue, move the calculation of the radius out of the animate function. The animate function should be responsible only for updating the plot:

def heart(theta):
    return 1 - (np.abs(np.cos(theta)) - 1) ** 2

def cardiod(theta):
    return 0.5 * (1 - np.sin(theta))

theta = np.linspace(0, 2*np.pi, 100)
r = heart(theta)

# transformation needed for your example
theta, r = r * np.cos(theta), r * np.sin(theta)

# test of the cardiod function; this function does not need any transformation afterwards
# r = cardiod(theta)


def animate(i):
    current_theta = theta[:i]
    current_r = r[:i]
    line.set_data(current_theta, current_r)
    return line,

I added a function for the Cardioid on Wikipedia, the 'heart curve', as an example for having several functions prepared.

Upvotes: 1

Related Questions