Search code examples
pythonanimationnumpymatplotliborbital-mechanics

Python Matplotlib: Centering figure around a moving artist


I've asked this question before very poorly, so I'm going to try to go into a little more detail.

I have made some planetary orbit programs, and I would like to know if I could possibly center the figure around an artist that changes position, so that the figure window moves, instead of staying at (0,0)

So if the center of the artist is defined in simData like so:

planet1.center = x1,y1

I assume somewhere there'd have to be something something like this:

ax = plt.axes(xlim=(x1-430000000, x1+430000000), ylim=(y1-430000000, y1+430000000))

Which I cannot do because I can't call out those variables. Plus, the axes and figure are only defined once at the beginning of the program.

Here's my code, 4 planets in orbital motion:

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

er = 6378100#m
mr = 1737400#m
sr = 696000000
em = 2*5.97219*10**24#kg
mm = 7.34767309*10**22#kg
sm = 1.989*10**30
sed = 150000000000
emd = 384400000

m1 = em
m2 = mm*4
m3 = mm*2
m4 = mm*3
r1 = mr*10*6
r2 = mr*10*3
r3 = mr*10*1.2
r4 = mr*10*2
nts = 10000
ts = 10000
G = 6.67384*10**(-11)


def simData():
    tmax = ts*nts
    t = 0
    x = 0
    x1 = 384400000*0.8*0
    y1 = 384400000*0.8*0
    x2 = 384400000*0.8
    y2 = -384400000*0.8*0
    x3 = -384400000*0.8
    y3 = 384400000*0.8*0
    x4 = -384400000*0.8*0
    y4 = -384400000*0.8
    Vx1 = 0
    Vy1 = 0
    Vx2 = 800
    Vy2 = 1700
    Vx3 = 0
    Vy3 = -1500
    Vx4 = 2000
    Vy4 = 0
    d12 = math.sqrt((x1-x2)**2+(y1-y2)**2)
    d23 = math.sqrt((x2-x3)**2+(y2-y3)**2)
    d13 = math.sqrt((x1-x3)**2+(y1-y3)**2)
    d14 = math.sqrt((x1-x4)**2+(y1-y4)**2)
    d24 = math.sqrt((x2-x4)**2+(y2-y4)**2)
    d34 = math.sqrt((x3-x4)**2+(y3-y4)**2)
    while t < tmax:
        Fg12 = G*(m1*m2)/d12**2
        Fgx12 = -Fg12*((x1-x2))/d12
        Fgy12 = -Fg12*((y1-y2))/d12

        Fgx21 = -Fg12*((x2-x1))/d12
        Fgy21 = -Fg12*((y2-y1))/d12

        Fg13 = G*(m1*m3)/d13**2
        Fgx13 = -Fg13*((x1-x3))/d13
        Fgy13 = -Fg13*((y1-y3))/d13

        Fgx31 = -Fg13*((x3-x1))/d13
        Fgy31 = -Fg13*((y3-y1))/d13

        Fg23 = G*(m3*m2)/d23**2
        Fgx23 = -Fg23*((x2-x3))/d23
        Fgy23 = -Fg23*((y2-y3))/d23

        Fgx32 = -Fg23*((x3-x2))/d23
        Fgy32 = -Fg23*((y3-y2))/d23

        Fg14 = G*(m1*m4)/d14**2
        Fgx14 = -Fg14*((x1-x4))/d14
        Fgy14 = -Fg14*((y1-y4))/d14

        Fgx41 = -Fg14*((x4-x1))/d14
        Fgy41 = -Fg14*((y4-y1))/d14

        Fg24 = G*(m2*m4)/d24**2
        Fgx24 = -Fg24*((x2-x4))/d24
        Fgy24 = -Fg24*((y2-y4))/d24

        Fgx42 = -Fg24*((x4-x2))/d24
        Fgy42 = -Fg24*((x4-x2))/d24

        Fg34 = G*(m3*m4)/d34**2
        Fgx34 = -Fg34*((x3-x4))/d34
        Fgy34 = -Fg34*((y3-y4))/d34

        Fgx43 = -Fg34*((x4-x3))/d34
        Fgy43 = -Fg34*((y4-y3))/d34

        Ax1 = Fgx12/m1 + Fgx13/m1 + Fgx14/m1
        Ay1 = Fgy12/m1 + Fgy13/m1 + Fgy14/m1

        Ax2 = Fgx21/m2 + Fgx23/m2 + Fgx24/m2
        Ay2 = Fgy21/m2 + Fgy23/m2 + Fgy24/m2

        Ax3 = Fgx32/m3 + Fgx31/m3 + Fgx34/m3
        Ay3 = Fgy32/m3 + Fgy31/m3 + Fgy34/m3

        Ax4 = Fgx41/m4 + Fgx42/m4 + Fgx43/m4
        Ay4 = Fgy41/m4 + Fgy42/m4 + Fgx43/m4

        Vx1 = Vx1+Ax1*ts
        Vy1 = Vy1+Ay1*ts

        Vx2 = Vx2+Ax2*ts
        Vy2 = Vy2+Ay2*ts

        Vx3 = Vx3+Ax3*ts
        Vy3 = Vy3+Ay3*ts

        Vx4 = Vx4+Ax4*ts
        Vy4 = Vy4+Ay4*ts

        x1 = x1 + Vx1*ts
        y1 = y1 + Vy1*ts

        x2 = x2 + Vx2*ts
        y2 = y2 + Vy2*ts

        x3 = x3 + Vx3*ts
        y3 = y3 + Vy3*ts

        x4 = x4 + Vx4*ts
        y4 = y4 + Vy4*ts

        planet1.center = x1,y1
        planet2.center = x2,y2
        planet3.center = x3,y3
        planet4.center = x4,y4

        d12 = math.sqrt((x1-x2)**2+(y1-y2)**2)
        d23 = math.sqrt((x2-x3)**2+(y2-y3)**2)
        d13 = math.sqrt((x1-x3)**2+(y1-y3)**2)
        d14 = math.sqrt((x1-x4)**2+(y1-y4)**2)
        d24 = math.sqrt((x2-x4)**2+(y2-y4)**2)
        d34 = math.sqrt((x3-x4)**2+(y3-y4)**2)

        t = t+ts

        yield x, t

def simPoints(simData):
    x, t = simData[0], simData[1]
    time_text.set_text(time_template%(t))
    line.set_data(t, x)
    return line, time_text

fig = plt.figure()
ax = plt.axes(xlim=(-430000000, 430000000), ylim=(-430000000, 430000000))
ax.set_aspect("equal")
line, = ax.plot([], [], '', ms=10)



planet1 = plt.Circle((0,0), r1, facecolor=(0,0,1))
ax.add_artist(planet1)

planet2 = plt.Circle((0,0), r2, facecolor=(1,0,0))
ax.add_artist(planet2)

planet3 = plt.Circle((0,0), r3, facecolor=(1,1,0))
ax.add_artist(planet3)

planet4 = plt.Circle((0,0), r4, facecolor=(0,1,0))
ax.add_artist(planet4)

time_template = 'Time = %.1f s'    # prints running simulation time
time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes)

ani = animation.FuncAnimation(fig, simPoints, simData, blit=False,\
     interval=10, repeat=True)

plt.show()

Solution

  • Assuming that you actually just want to adjust the Axes limits to "follow" one of your planets, you can access the planet's center just like you did to assign it - the planets and the axes are all global variables.

    These modifications to simPoints keeps the blue planet at the center of your axes.

    def simPoints(simData):
        x, t = simData[0], simData[1]
        time_text.set_text(time_template%(t))
        line.set_data(t, x)
        ctr = planet1.center
        ax.set_xlim(ctr[0]-430000000, ctr[0]+430000000)
        ax.set_ylim(ctr[1]-430000000, ctr[1]+430000000)
        return line, time_text