Search code examples
pythonmatplotlibanimationclass-variables

Matplotlib multiple animations with same class variables


I have a simple version of my code with a "satellite" orbiting a point. I made the "satellite" as class so that I could have multiple "satellites" orbiting the point at the same time. However, I can't seem to make it animate all the two example satellites. I want to make all the animation display on the same figure. The way I animated it was creating a circular path around the central point and have the animation only show the satellite moving along that point. I have put in a break down and a direct copy section of the code so people can understand my logic easier. Thanks for helping !!!!

I know that I could plot multiple class objects at the same time on the same figure but not sure if animations would be the same. I did the plotting on my more detailed code for multiple central points using classes.

import math
import scipy as sp
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as spi
from operator import itemgetter
from matplotlib import animation

fig = plt.figure()
fig.set_dpi(100)
fig.set_size_inches(7, 6.5)

ax = plt.axes(xlim=(0, 10), ylim=(0, 10))

a = plt.Circle((5,5), radius = 0.2, color = 'r') 
plt.gca().add_patch(a)
plt.show()


class Satellite:   #defines the class satellites and the ratio radius 
    def __init__(self, x, y, radius) :
        self.x= x
        self.y= y
        self.radius = radius
        self.forceScale()


    def forceScale(self) :
        z= float(self.radius)
        orbitalRadius = ((1/10) * (z)) + 1
        print(orbitalRadius)

        if orbitalRadius >= 0 :
            Satellite = plt.Circle((5, 5), 0.1, fc='y')        
            def init():
                Satellite.center = (5, 5)
                plt.gca().add_patch(Satellite)
                return Satellite,
            def animate1(n):
                x, y = Satellite.center
                x = self.x + orbitalRadius * np.sin(np.radians(n))
                y = self.y + orbitalRadius * np.cos(np.radians(n))
                Satellite.center = (x, y)
                return Satellite,


        else :
            print("Please check data indices")  

        global anim

        anim = animation.FuncAnimation(fig, animate1, 
                                   init_func= init, 
                                   frames=360, 
                                   interval= 20,
                                   blit=True)
        plt.show()

firstPoint = Satellite(x= 5, y=5, radius = 2) 
secondPoint = Satellite(x=5, y= 5, radius = 4)

###################################################################    
#below is a break down of each section of the code for easier understanding
import math
import scipy as sp
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as spi
from operator import itemgetter
from matplotlib import animation


#creating the figure grid info
fig = plt.figure()
fig.set_dpi(100)
fig.set_size_inches(7, 6.5)


#the central point
ax = plt.axes(xlim=(0, 10), ylim=(0, 10))

a = plt.Circle((5,5), radius = 0.2, color = 'r') #plotting the central point
plt.gca().add_patch(a)
plt.show()


class Satellite:   
#defines the satellite and the radius index(will be used to find actual radius
    def __init__(self, x, y, radius) :
        self.x= x
        self.y= y
        self.radius = radius
        self.forceScale()

#the code which creates the orbit
    def forceScale(self) :
        z= float(self.radius) #making the radius index a float to calculate
        orbitalRadius = ((1/10) * (z)) + 1
        print(orbitalRadius) #checking the calculation works

        if orbitalRadius >= 0 : 
#this part is similar to my more detailed code so it answers can be more easily applied

            Satellite = plt.Circle((5, 5), 0.1, fc='y')
#a base for the how the satellite will look        
            def init():
                Satellite.center = (5, 5) #actual central point of orbit
                plt.gca().add_patch(Satellite)
                return Satellite,
            def animate1(n):
                x, y = Satellite.center
                x = self.x + orbitalRadius * np.sin(np.radians(n))
                y = self.y + orbitalRadius * np.cos(np.radians(n))
                Satellite.center = (x, y)
                return Satellite,
            #creating the path for the satellite

        else :
            print("Please check data indices")  #a checking section


#the actual animation function
        global anim


        anim = animation.FuncAnimation(fig, animate1, 
                                   init_func= init, 
                                   frames=360, 
                                   interval= 20,
                                   blit=True)
        plt.show()






# now making the orbit path for the satellite  
# Satellite information
#the examples
firstPoint = Satellite(x= 5, y=5, radius = 2) 
secondPoint = Satellite(x=5, y= 5, radius = 4)

Solution

  • You need one single animation per figure. So I would create as many satellites as you want, and use a single class to animate them all. This could look like this:

    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib import animation
    
    class Satellite:   #defines the class satellites and the ratio radius 
        def __init__(self, x, y, radius, ax=None) :
            self.ax = ax or plt.gca()
            self.x = x
            self.y = y
            self.radius = radius
            self.forceScale()
    
        def forceScale(self) :
            z= float(self.radius)
            self.orbitalRadius = ((1/10) * (z)) + 1
            print(self.orbitalRadius)
            self.satellite = plt.Circle((5, 5), 0.1, fc='y')
            self.ax.add_patch(self.satellite)
    
        def ani_init(self):
            self.satellite.center = (5, 5)
            return [self.satellite]
    
        def ani_update(self, n):
            x, y = self.satellite.center
            x = self.x + self.orbitalRadius * np.sin(np.radians(n))
            y = self.y + self.orbitalRadius * np.cos(np.radians(n))
            self.satellite.center = (x, y)
            return [self.satellite]
    
    
    
    class AnimatedUniverse():
        def __init__(self, fig, satellites):
            self.fig = fig
            self.satellites = satellites
    
        def ani_init(self):
            artists = []
            for sat in self.satellites:
                artists.extend(sat.ani_init())
            return artists
    
        def animate(self, n):
            artists = []
            for sat in self.satellites:
                artists.extend(sat.ani_update(n))
            return artists
    
        def start_ani(self):
            self.anim = animation.FuncAnimation(fig, self.animate, 
                                       init_func= self.ani_init, 
                                       frames=360, 
                                       interval= 20,
                                       blit=True)
    
    
    fig, ax = plt.subplots()
    ax.set(xlim=(0, 10), ylim=(0, 10))
    ax.set_aspect("equal", adjustable="box")
    
    a = plt.Circle((5,5), radius = 0.2, color = 'r') 
    ax.add_patch(a)
    
    firstPoint = Satellite(x= 5, y=5, radius = 2) 
    secondPoint = Satellite(x=5, y= 5, radius = 4)
    au = AnimatedUniverse(fig, (firstPoint, secondPoint))
    au.start_ani()
    plt.show()