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
:
movie from 11 element list trajectory
(first one duplicated):
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 ?
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): # it works without 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) # it is required
plt.show()
The output gives: