Animating multiple dots in different orbits in Python using FuncAnimation

Viewed 742

A similar question has already been answered (How to animate multiple dots moving along the circumference of a circle in Python using matplotlib?) but somehow it doesnt work for me.

What Im Trying To Do

Im trying to make little dots which represent planets orbit another dot in an ellipse. It should look something like this: https://www.youtube.com/watch?v=ED-_xN9Jufs&feature=youtu.be

The Issue I Currently Have enter image description here (I dont know how to insert a video but heres a screenshot. The outermost dot is actually moving but the three inner ones are not)

Only the outermost planet is circling in an orbit. I know that this is because the FuncAnimation is within the for j in range(1, planet_count): loop, so I believe the last loop overwrites the previous loops. The code is down below

Please ask if you want me to clarify anything. This is the first time using FuncAnimation so I think there must be a simple solution.

Any help, even if ur unsure if its correct, would be enormously helpful and I thank you in advance.


planet_count = 5

df = pd.read_csv("Wincoll_prog_chall_1/data2.csv")

df["b"] = df["a"]*(1-df["Epsilon"]**2)


def position(theta, a, eps):
    r = a*(1-eps**2)/(1-eps*np.cos(theta))
    x = r*np.cos(theta)
    y = r*np.sin(theta)

    return r, x, y


num_to_color = {
    1: "orange",
    2: "pink",
    3: "green",
    4: "red",
    5: "brown",
    6: "yellow",
    7: "blue",
    8: "purple",
    9: "grey",
}



def animate(i, a, eps):
    theta = i*math.pi/180
    r = a*(1-eps**2)/(1-eps*np.cos(theta))
    x = r*np.cos(theta)
    y = r*np.sin(theta)
    planet.set_data(x, y)
    return planet,


plt.style.use('dark_background')
fig, ax = plt.subplots(figsize=(10, 10))


if planet_count > 5:
    ax.plot(0, 0, 'X', markersize=5, color="yellow")
else:
    ax.plot(0, 0, 'X', markersize=15, color="yellow")

# planet = []

for j in range(1, planet_count):
    theta = np.linspace(0, 2*math.pi, 1000)
    r, x, y = position(theta, df["a"][j], df["Epsilon"][j])
    ax.plot(x, y, color=num_to_color[j], linewidth=2, label=df["Object"][j])

    planet, = ax.plot(x[0], y[0], '.', markersize=15, color="white")
    
    # Animation
    animation = FuncAnimation(fig, animate, frames=360, interval=5, fargs=(
        df["a"][j], df["Epsilon"][j],), blit=True)

plt.xlabel("x", size=20)
plt.ylabel("y", size=20)
plt.legend(fontsize=12, loc="upper right", numpoints=1)
plt.grid(True, lw=0.3)
plt.show()

UPDATE: With the help of r-beginners, I somewhat solved this. Although I cannot set the colors of the dots to the colors of the planet, it now animates nicely.

What I did was add two extra arrays, dataX and dataY, which I update every time and feed into the ax.plot. It now works very well. Thank you for that.

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import math
from matplotlib.animation import FuncAnimation

AU = 149597870700
P = 5

df = pd.read_csv("Wincoll_prog_chall_1/data2.csv")

df["b"] = df["a"]*(1-df["Epsilon"]**2)


def position(theta, a, eps):
    r = a*(1-eps**2)/(1-eps*np.cos(theta))
    x = r*np.cos(theta)
    y = r*np.sin(theta)

    return r, x, y


num_to_color = {
    1: "orange",
    2: "pink",
    3: "green",
    4: "red",
    5: "brown",
    6: "yellow",
    7: "blue",
    8: "purple",
    9: "grey",
}

starting_planet = 1
planet_count = 5

if (planet_count < 6):
    anim_speed = 2.125
else:
    anim_speed = 50


def animate(i):
    dataX = []
    dataY = []

    years = round(1000*i*anim_speed/360)/1000

    for j in range(starting_planet, planet_count):
        theta = (i*anim_speed*np.pi)/(df["P"][j]*180)
        # theta = i*1/df["P"][j]*np.pi/180
        r = df["a"][j]*(1-df["Epsilon"][j]**2) / \
            (1-df["Epsilon"][j]*np.cos(theta))
        x = r*np.cos(theta)
        y = r*np.sin(theta)
        dataX.append(x)
        dataY.append(y)
        # planet.set_color(num_to_color[j])
    planet.set_data(dataX, dataY)
    title.set_text(u"Years: {}".format(years))
    return planet, title,


plt.style.use('dark_background')
fig, ax = plt.subplots(figsize=(10, 10))
# planet, = ax.plot([], [], '.')


if planet_count > 5:
    ax.plot(0, 0, 'X', markersize=5, color="yellow")
else:
    ax.plot(0, 0, 'X', markersize=15, color="yellow")


planet, = ax.plot([], [], '.', markersize=11)


title = ax.text(0.5, 0.97, "", bbox={'facecolor': 'w', 'alpha': 0.0, 'pad': 5},
                transform=ax.transAxes, ha="center")


dataX = []
dataY = []

for j in range(starting_planet, planet_count):
    theta = np.linspace(0, 2*math.pi, 1000)
    r, x, y = position(theta, df["a"][j], df["Epsilon"][j])
    ax.plot(x, y, color=num_to_color[j], linewidth=2, label=df["Object"][j])

    # planet.set_color(num_to_color[j])
    dataX.append(x[0])
    dataY.append(y[0])
animation = FuncAnimation(fig, animate, interval=1, blit=True)


planet.set_data(dataX, dataY)

plt.xlabel("x", size=20)
plt.ylabel("y", size=20)
plt.legend(fontsize=12, loc="upper right", numpoints=1)
plt.grid(True, lw=0.3)
plt.show()
1 Answers

For the data, we created a suitable circle data and animated it. The configuration involves setting up an initial graph for animation and a fixed graph. The animation function draws the orbit information sequentially.

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
# from matplotlib.animation import PillowWriter

plt.style.use('dark_background')

n_points = 100
theta = np.linspace(0, 2 * np.pi, n_points)
e_radius = 5
m_radius = 7

x = e_radius * np.sin(theta)
y = e_radius * np.cos(theta)

xx = m_radius * np.sin(theta)
yy = m_radius * np.cos(theta)

fig, ax = plt.subplots(figsize=(5, 5))
ax = plt.axes(xlim=(-8, 8), ylim=(-8, 8))
earth, = ax.plot([], [], 'g.', markersize=15)
mars, = ax.plot([], [], 'r.', markersize=15)
ax.plot(0, 0, 'X', markersize=5, color="yellow")
plt.grid(True, lw=0.3)
ax.plot(x, y, 'g-')
ax.plot(xx, yy, 'r-')

def animate(i):
    earth.set_data(x[i], y[i])
    mars.set_data(xx[i], yy[i])
    return earth,mars

anim = FuncAnimation(fig, animate, frames=100, interval=200, repeat=False)
# anim.save('cirlce_ani.gif', writer='pillow')
plt.show()

enter image description here

Related