I'm trying to create an animation to show the elliptical orbits of the four inner most planets around the sun, however, when I try this, it effectively just removes each planet and makes them reappear. Furthermore, the planets are depicted as lines rather than single points despite me specifically saying that they should be points. When I plot each orbit in separate files, the orbit works fine (other than the line point problem). Something like this is what my final goal should look like https://www.youtube.com/watch?v=ED-_xN9Jufs. Any help would be appreciated.
Here is my code
fig, ax = plt.subplots()
ax.axis([-0.015,0.015,-0.015,0.015])
#plotting the initial points
sun_x = 0
sun_y = 0
plt.plot(sun_x,sun_y, marker=".")
point_mercury, = plt.plot([r_zerox_mercury,r_zeroy_mercury], marker=".")
point_venus, = plt.plot(r_zerox_venus,r_zeroy_venus, marker=".")
point_earth, = plt.plot(r_zerox_earth,r_zeroy_earth, marker=".")
point_mars, = plt.plot(r_zerox_mars,r_zeroy_mars, marker=".")
#showing the orbital path of each planet
t = linspace(0,360,360)
x1 = a_mercury*cos(radians(t))
y1 = b_mercury*sin(radians(t))
plt.plot(x1,y1)
t = linspace(0,360,360)
x2 = a_venus*cos(radians(t))
y2 = b_venus*sin(radians(t))
plt.plot(x2,y2)
t = linspace(0,360,360)
x3 = a_earth*cos(radians(t))
y3 = b_earth*sin(radians(t))
plt.plot(x3,y3)
t = linspace(0,360,360)
x4 = a_mars*cos(radians(t))
y4 = b_mars*sin(radians(t))
plt.plot(x4,y4)
#obtaining and setting coordinates
def update_mercury(phi):
# obtain point coordinates
x,y = ellipse_mercury(phi)
# set point's coordinates
point_mercury. set_data([x],[y])
return point_mercury,
def update_venus(phi):
# obtain point coordinates
x,y = ellipse_venus(phi)
# set point's coordinates
point_venus.set_data([x], [y])
return point_venus,
def update_earth(phi):
# obtain point coordinates
x,y = ellipse_earth(phi)
# set point's coordinates
point_earth.set_data([x],[y])
return point_earth,
def update_mars(phi):
# obtain point coordinates
x,y = ellipse_mars(phi)
# set point's coordinates
point_mars.set_data([x],[y])
return point_mars,
#animating each planet
ani = FuncAnimation(fig, update_mercury, interval=10, blit=True, repeat=True,
frames=np.linspace(0,2*np.pi,360, endpoint=False))
#ani1 = FuncAnimation(fig, update_venus, interval=20, blit=True, repeat=True,
# frames=np.linspace(0,2*np.pi,360, endpoint=False))
#ani2 = FuncAnimation(fig, update_earth, interval=30, blit=True, repeat=True,
# frames=np.linspace(0,2*np.pi,360, endpoint=False))
#ani3 = FuncAnimation(fig, update_mars, interval=40, blit=True, repeat=True,
# frames=np.linspace(0,2*np.pi,360, endpoint=False))
plt.show()
There are several unrelated issues in the code you have provided. I'll try and address a few :
The reason your planets are showing up as lines is because you're passing a line (or rather a series of points all very close to each other) to the plot
function. Your ellipse_planet
functions return arrays, not points, since the r_planet
variables you're using are arrays.
It's been a while since I did orbital mechanics, so I'm not 100% sure on the math, but in any case I think that should probably be a function instead of an array. so you would probably want something like :
def r(theta):
'''orbital radius as a function of progression'''
return (a*(1-(e**2)))/(1-e*np.cos(theta))
def ellipse(phi):
'''sun-centric cartesian coordinates of the planet'''
return r(phi)*np.cos(phi), r(phi)*np.sin(phi)
def update(phi):
'''update the plot'''
x, y = ellipse(phi)
point.set_data([x], [y])
return point,
#showing the orbital path of the planet
t = linspace(0,2*pi,360)
x, y = ellipse(t)
plt.plot(x, y)
Your planets are flickering because you have several animations that are fighting for control over the figure. The solution is to have a single animation function for all the planets at the same time.
def update_planets(phi):
return (
update_mercury(phi)[0],
update_venus(phi)[0],
update_earth(phi)[0],
update_mars(phi)[0]
)
ani = FuncAnimation(fig, update_planets, interval=1, blit=True, repeat=True,
frames=np.linspace(0,2*np.pi,360, endpoint=False))
Your planets are all orbiting at the exact same rate. They should be orbiting with their orbital periods instead. The solution is to use time as your animation variable, rather than angle. Unfortunately, this means that things won't loop quite so nicely once animation hits the end of the timeline. I you want it to run idefinitely, you'll need to do some fancy stuff with generators or something.
...
def update_mars(t):
# calculate angle (NOTE: only for illustration - this is not the real formula!!)
phi = 2*pi * (t % P_mars)/P_mars
# obtain point coordinates
x,y = ellipse_mars(phi)
point_mars.set_data([x], [y])
return point_mars,
def update_planets(t):
return (
update_mercury(t)[0],
update_venus(t)[0],
update_earth(t)[0],
update_mars(t)[0]
)
#animating each planet
ani = FuncAnimation(fig, update_planets, interval=1, blit=True, repeat=False,
frames=np.linspace(0, 2, 1000))
You might want to look into object-oriented programming to make your life easier. that way you won't need to define the functions multiple times for different planet, and it will make it much simpler to understand (and debug!) your code.