Home > Enterprise >  Matplotlib animation.FuncAnimation() animation miss first frame?
Matplotlib animation.FuncAnimation() animation miss first frame?

Time:12-08

I am new to both Python and Matplotlib. I'have this code to plot a trajectory in a 3d Matplotlib subplot:

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

from itertools import islice

import sympy as sym
from sympy.parsing.sympy_parser import parse_expr

from sympy import Eq, solve




class FunZ:
    
    def __init__(self, f):  
  
        self.x, self.y  = sym.symbols('x y')
        
        self.f = parse_expr(f)
        
        # print('f : ', self.f)
        
    def evalu(self, xx, yy):
    
        return float(self.f.subs({self.x: xx, self.y: yy}).evalf())
    

    def derX(self, xx, yy):        
        
        self.dx = sym.diff(self.f, self.x)
        
        # print('dx : ', self.dx)
        
        return float(self.dx.subs({self.x: xx, self.y: yy}).evalf())
    
    def derY(self, xx, yy):
        
        self.dy = sym.diff(self.f, self.y)
        
        # print('dy :', self.dy)
        
        return float(self.dy.subs({self.x: xx, self.y: yy}).evalf())
    
    def derXY(self, xx, yy):
        
        return [float(self.derX(xx, yy)), float(self.derY(xx, yy))]
    
    def minim(self):
        
        self.dx = sym.diff(self.f, self.x)
        self.dy = sym.diff(self.f, self.y)
        print('dx : ', self.dx)
        print('dy : ', self.dy)
        
        eq1 = Eq(self.dx ,0)
        
        eq2 = Eq(self.dy ,0)
        
        solu = solve((eq1,eq2), (self.x, self.y), dict= False)
        
        print(solu, type(solu))
        
        return solu

XX = np.linspace(-4, 4, 100)

YY = np.linspace(-4, 4, 100)

funz = FunZ('x**2   y**2   2*y   2')

ij = [(x, y, funz.evalu(x, y)) for x in XX for y in YY]


arr = np.array(ij, dtype=float)

# print(arr, arr.size, arr.shape, arr.dtype)


der_x = [(a, b, funz.derX(a, b)) for a in XX for b in YY] 

derX = np.array(der_x)

# print(derX, derX.size, derX.shape, derX.dtype)


der_y = [(a, b, funz.derY(a, b)) for a in XX for b in YY] 

derY = np.array(der_y)

# print(derY, derY.size, derY.shape, derY.dtype)


x = arr[:, 0]
y = arr[:, 1]

data = arr[:, 2]




fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)


### devo duplicare primo elemento della lista altrimenti skippa a visualizzare i primi due sul video

# trajectory =  [(2.5, 3.5, 27.5),(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]

trajectory =  [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]

print('len trajectory : ', len(trajectory))


i = islice(trajectory, 0 , len(trajectory), 1)


pippo = None
def animate(j):  #funziona senza stop iteration with animation.save
    
    global pippo
    
    try: 
        coord = next(i)
        
    except:
        print('coord empty')
        return
    
    if pippo:
        pippo.remove()
    
    print(coord)
                
    x = coord[0]
    y = coord[1]
    z = coord[2]
      
    pippo = ax.scatter(x,y,z)          
    
    return pippo

ani = animation.FuncAnimation(fig, animate, interval=1000, frames=len(trajectory), repeat=False)  # e' necessario



plt.show()
plt.close('all')


fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)


print('#####################')

i = islice(trajectory, 0 , len(trajectory), 1)

ani2 = animation.FuncAnimation(fig, animate, interval=1000, frames=len(trajectory), repeat=False)

# writervideo = animation.FFMpegWriter(fps=1, codec="h264") 
# ani2.save('video_steeper.mp4', writer=writervideo)

ani2.save("movie.gif", writer=animation.PillowWriter(fps=1))

I am struggling trying to understand why to have a proper animation and saved animation I need to duplicate the first element of my trajectory list ((2.5, 3.5, 27.5)).

If I don't do so the animation and my gif show only 9 points instead of 10, see resulting output:

movie from 10 element list trajectory :

enter image description here

movie from 11 element list trajectory (first one duplicated):

enter image description here

I've tried a lot to figure out what is going on but can't understand, it is strange that my print statement inside animate function prints exactly 11 and 10 points for the two different list but both of them miss a frame in the animation and file. Any clue ?

CodePudding user response:

It seems to me that your issue comes from the iterable i you are creating and the use of the function next. I am not exactly sure why it's happening but if instead of creating your iterable i you just index your trajectory directly then your animation works as expected.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from itertools import islice
import sympy as sym
from sympy.parsing.sympy_parser import parse_expr
from sympy import Eq, solve

class FunZ:    
    def __init__(self, f):  
        self.x, self.y  = sym.symbols('x y')
        self.f = parse_expr(f)
        
    def evalu(self, xx, yy):   
        return float(self.f.subs({self.x: xx, self.y: yy}).evalf())
  
    def derX(self, xx, yy):        
        self.dx = sym.diff(self.f, self.x)
        return float(self.dx.subs({self.x: xx, self.y: yy}).evalf())
    
    def derY(self, xx, yy):
        self.dy = sym.diff(self.f, self.y)
        return float(self.dy.subs({self.x: xx, self.y: yy}).evalf())
    
    def derXY(self, xx, yy):
        return [float(self.derX(xx, yy)), float(self.derY(xx, yy))]
    
    def minim(self):     
        self.dx = sym.diff(self.f, self.x)
        self.dy = sym.diff(self.f, self.y)
        print('dx : ', self.dx)
        print('dy : ', self.dy)      
        eq1 = Eq(self.dx ,0)   
        eq2 = Eq(self.dy ,0)
        solu = solve((eq1,eq2), (self.x, self.y), dict= False)
        print(solu, type(solu))
        return solu

XX = np.linspace(-4, 4, 100)
YY = np.linspace(-4, 4, 100)

funz = FunZ('x**2   y**2   2*y   2')
ij = [(x, y, funz.evalu(x, y)) for x in XX for y in YY]
arr = np.array(ij, dtype=float)

der_x = [(a, b, funz.derX(a, b)) for a in XX for b in YY] 
derX = np.array(der_x)
der_y = [(a, b, funz.derY(a, b)) for a in XX for b in YY] 
derY = np.array(der_y)

x = arr[:, 0]
y = arr[:, 1]
data = arr[:, 2]

fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)

trajectory =  [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]
print('len trajectory : ', len(trajectory))

pippo = None
def animate(j):  #funziona senza stop iteration with animation.save
    
    global pippo

    x = trajectory[j][0]
    y = trajectory[j][1]
    z = trajectory[j][2]

    if pippo is not None:
        pippo.remove()
    pippo = ax.scatter(x,y,z)          
    return pippo

ani = animation.FuncAnimation(fig, animate, interval=1000, frames=len(trajectory), repeat=False)  # e' necessario
plt.show()

The output gives:

enter image description here

CodePudding user response:

Well fiddled a little bit with FuncAnimation adding an init_func :

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

from itertools import islice

import sympy as sym
from sympy.parsing.sympy_parser import parse_expr

from sympy import Eq, solve




class FunZ:
    
    def __init__(self, f):  
  
        self.x, self.y  = sym.symbols('x y')
        
        self.f = parse_expr(f)
        
        # print('f : ', self.f)
        
    def evalu(self, xx, yy):
    
        return float(self.f.subs({self.x: xx, self.y: yy}).evalf())
    

    def derX(self, xx, yy):        
        
        self.dx = sym.diff(self.f, self.x)
        
        # print('dx : ', self.dx)
        
        return float(self.dx.subs({self.x: xx, self.y: yy}).evalf())
    
    def derY(self, xx, yy):
        
        self.dy = sym.diff(self.f, self.y)
        
        # print('dy :', self.dy)
        
        return float(self.dy.subs({self.x: xx, self.y: yy}).evalf())
    
    def derXY(self, xx, yy):
        
        return [float(self.derX(xx, yy)), float(self.derY(xx, yy))]
    
    def minim(self):
        
        self.dx = sym.diff(self.f, self.x)
        self.dy = sym.diff(self.f, self.y)
        print('dx : ', self.dx)
        print('dy : ', self.dy)
        
        eq1 = Eq(self.dx ,0)
        
        eq2 = Eq(self.dy ,0)
        
        solu = solve((eq1,eq2), (self.x, self.y), dict= False)
        
        print(solu, type(solu))
        
        return solu

XX = np.linspace(-4, 4, 100)

YY = np.linspace(-4, 4, 100)

funz = FunZ('x**2   y**2   2*y   2')

ij = [(x, y, funz.evalu(x, y)) for x in XX for y in YY]


arr = np.array(ij, dtype=float)

# print(arr, arr.size, arr.shape, arr.dtype)


der_x = [(a, b, funz.derX(a, b)) for a in XX for b in YY] 

derX = np.array(der_x)

# print(derX, derX.size, derX.shape, derX.dtype)


der_y = [(a, b, funz.derY(a, b)) for a in XX for b in YY] 

derY = np.array(der_y)

# print(derY, derY.size, derY.shape, derY.dtype)


x = arr[:, 0]
y = arr[:, 1]

data = arr[:, 2]

fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)

trajectory =  [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]

print('len trajectory : ', len(trajectory))
i = islice(trajectory, 0 , len(trajectory), 1)

def init_animate():
    ax.plot_trisurf(x, y, data, color="green", alpha=0.5)
    
pippo = None
def animate(j):  #funziona senza stop iteration with animation.save
    
    global pippo
    
    try: 
        coord = next(i)
        
    except:
        print('coord empty')
        return
    
    if pippo:
        pippo.remove()
    
    print(coord)
                
    x = coord[0]
    y = coord[1]
    z = coord[2]
      
    pippo = ax.scatter(x,y,z)          
    
    return pippo

ani = animation.FuncAnimation(fig, animate, interval=1000, frames=len(trajectory),init_func=init_animate, repeat=False)  

plt.show()
plt.close('all')

print('#####################')

was able to get all the 10 points moving on gif:

enter image description here

was able to remove the frames arg too but just for the visualization not the saving of my animation, see:

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

from itertools import islice

import sympy as sym
from sympy.parsing.sympy_parser import parse_expr

from sympy import Eq, solve




class FunZ:
    
    def __init__(self, f):  
  
        self.x, self.y  = sym.symbols('x y')
        
        self.f = parse_expr(f)
        
        # print('f : ', self.f)
        
    def evalu(self, xx, yy):
    
        return float(self.f.subs({self.x: xx, self.y: yy}).evalf())
    

    def derX(self, xx, yy):        
        
        self.dx = sym.diff(self.f, self.x)
        
        # print('dx : ', self.dx)
        
        return float(self.dx.subs({self.x: xx, self.y: yy}).evalf())
    
    def derY(self, xx, yy):
        
        self.dy = sym.diff(self.f, self.y)
        
        # print('dy :', self.dy)
        
        return float(self.dy.subs({self.x: xx, self.y: yy}).evalf())
    
    def derXY(self, xx, yy):
        
        return [float(self.derX(xx, yy)), float(self.derY(xx, yy))]
    
    def minim(self):
        
        self.dx = sym.diff(self.f, self.x)
        self.dy = sym.diff(self.f, self.y)
        print('dx : ', self.dx)
        print('dy : ', self.dy)
        
        eq1 = Eq(self.dx ,0)
        
        eq2 = Eq(self.dy ,0)
        
        solu = solve((eq1,eq2), (self.x, self.y), dict= False)
        
        print(solu, type(solu))
        
        return solu

XX = np.linspace(-4, 4, 100)

YY = np.linspace(-4, 4, 100)

funz = FunZ('x**2   y**2   2*y   2')

ij = [(x, y, funz.evalu(x, y)) for x in XX for y in YY]


arr = np.array(ij, dtype=float)

# print(arr, arr.size, arr.shape, arr.dtype)


der_x = [(a, b, funz.derX(a, b)) for a in XX for b in YY] 

derX = np.array(der_x)

# print(derX, derX.size, derX.shape, derX.dtype)


der_y = [(a, b, funz.derY(a, b)) for a in XX for b in YY] 

derY = np.array(der_y)

# print(derY, derY.size, derY.shape, derY.dtype)


x = arr[:, 0]
y = arr[:, 1]

data = arr[:, 2]




fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)


### devo duplicare primo elemento della lista altrimenti skippa a visualizzare i primi due sul video

trajectory =  [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]

# trajectory =  [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]

print('len trajectory : ', len(trajectory))


i = islice(trajectory, 0 , len(trajectory), 1)





def init_animate():
    ax.plot_trisurf(x, y, data, color="blue", alpha=0.5)
    
pippo = None
def animate(j):  #funziona senza stop iteration with animation.save

    global pippo

    try: 
        
        coord = next(i)
        print(coord)
        
    except:
        print('coord empty')
        global ani
        global ani2
        ani = None
        ani2 = None
        return 
    
    if pippo:
        pippo.remove()
    
    # print(coord)
                
    x = coord[0]
    y = coord[1]
    z = coord[2]
      
    pippo = ax.scatter(x,y,z)          
    
    return pippo


# ani = animation.FuncAnimation(fig, animate ,frames=len(trajectory),init_func=init_animate, interval=1000, repeat=False) 
ani = animation.FuncAnimation(fig, animate, init_func=init_animate, interval=1000, repeat=False) 

plt.show()

print('test to see if everything is fine')

fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)


print('#####################')

i = islice(trajectory, 0 , len(trajectory), 1)

ani2 = animation.FuncAnimation(fig, animate, init_func=init_animate, interval=1000, repeat=False).save("movie_init_no-frames.gif", writer=animation.PillowWriter(fps=1)) 

dont know what is going on but alghoritm keeps running, output:

len trajectory :  10
(2.5, 3.5, 27.5)
(2.0, 3.0, 21.0)
(1.5, 2.5, 15.5)
(1.0, 2.0, 11.0)
(0.5, 1.5, 7.5)
(0.0, 1.0, 5.0)
(0.0, 0.5, 3.25)
(0.0, 0.0, 2.0)
(0.0, -0.5, 1.25)
(0.0, -1.0, 1.0)
coord empty
test to see if everything is fine
#####################
(2.5, 3.5, 27.5)
(2.0, 3.0, 21.0)
(1.5, 2.5, 15.5)
(1.0, 2.0, 11.0)
(0.5, 1.5, 7.5)
(0.0, 1.0, 5.0)
(0.0, 0.5, 3.25)
(0.0, 0.0, 2.0)
(0.0, -0.5, 1.25)
(0.0, -1.0, 1.0)
coord empty
coord empty
coord empty
^Z
[9]   Stopped 

I suppose it relates to the inners of FuncAnimation. Nevertheless I should have took the time to try to understant j Documentation as per help(matplotlib.animation)

......

class FuncAnimation(TimedAnimation)
     |  FuncAnimation(fig, func, frames=None, init_func=None, fargs=None, save_count=None, *, cache_frame_data=True, **kwargs)
     |  
     |  Makes an animation by repeatedly calling a function *func*.
     |  
     |  Parameters
     |  ----------
     |  fig : `~matplotlib.figure.Figure`
     |      The figure object used to get needed events, such as draw or resize.
     |  
     |  func : callable
     |      The function to call at each frame.  The first argument will
     |      be the next value in *frames*.   Any additional positional
     |      arguments can be supplied via the *fargs* parameter.
     |  
     |      The required signature is::
     |  
     |          def func(frame, *fargs) -> iterable_of_artists
     |  
     |      If ``blit == True``, *func* must return an iterable of all artists
     |      that were modified or created. This information is used by the blitting
     |      algorithm to determine which parts of the figure have to be updated.
     |      The return value is unused if ``blit == False`` and may be omitted in
     |      that case.

......

  • Related