Is it possible to animate Delaunay triangulation using Matplotlib? The following plots vertices grouped by Item
and Time
. I'm hoping to animate this instead of plotting each iteration.
I may also have time points that don't contain enough points to adequately plot the triangulation. For these time points, I'm just hoping to pass that period and move onto the subsequent time point.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import Delaunay
import matplotlib.animation as animation
import matplotlib.gridspec as gridspec
# data frame containing time points without adequate points (3)
#df = pd.DataFrame({
# 'Time' : [1,1,1,1,1,1,1,2,2,2,2,2,2,2,3,3],
# 'Item' : ['A','B','A','B','A','B','A','A','B','A','B','A','B','B','A','B'],
# 'X' : [5, 5, 6, 6, 4, 3, 3, 4, 4, 3, 2, 5, 4, 5, 1, 2],
# 'Y' : [5, 6, 6, 5, 5, 6, 5, 6, 3, 1, 4, 6, 7, 4, 5, 6],
# })
fig = plt.figure(figsize = (8,10))
grid = gridspec.GridSpec(1, 2)
gridsize = (1, 2)
ax = plt.subplot2grid(gridsize, (0, 0))
ax2 = plt.subplot2grid(gridsize, (0, 1))
A_coord = df.loc[df['Item'] == 'A']
B_coord = df.loc[df['Item'] == 'B']
def make_points(x):
return np.array(list(zip(x['X'], x['Y'])))
A_points = A_coord.groupby(['Time']).apply(make_points)
B_points = B_coord.groupby(['Time']).apply(make_points)
for p in A_points:
tri = Delaunay(p)
a_del = ax.triplot(*p.T, tri.simplices, color = 'orange')
for p in B_points:
tri = Delaunay(p)
b_del = ax.triplot(*p.T, tri.simplices, color = 'purple')
#def animate(i) :
#a_del.set_data#()
#b_del.set_data#()
#ani = animation.FuncAnimation(fig, animate, blit = False)
Edit 2:
I'm hoping to keep the figure, axes stable as I plot other objects on it. As such, I just want to animate the change in the triangulation.
df = pd.DataFrame({
'Time' : [1,1,1,1,1,1,1,2,2,2,2,2,2,2],
'Item' : ['A','B','A','B','A','B','A','A','B','A','B','A','B','B'],
'X' : [5, 5, 6, 6, 4, 3, 3, 4, 4, 3, 2, 5, 4, 5],
'Y' : [5, 6, 6, 5, 5, 6, 5, 6, 3, 1, 4, 6, 7, 4],
})
A_coord = df.loc[df['Item'] == 'A']
B_coord = df.loc[df['Item'] == 'B']
def make_points(x):
return np.array(list(zip(x['X'], x['Y'])))
A_points = A_coord.groupby(['Time']).apply(make_points)
B_points = B_coord.groupby(['Time']).apply(make_points)
A_points = A_points.values
B_points = B_points.values
fig = plt.figure(figsize = (8,10))
grid = gridspec.GridSpec(2, 2)
gridsize = (2, 2)
ax = plt.subplot2grid(gridsize, (0, 0), colspan = 2)
ax.set_xlim(0, 20)
ax.set_ylim(0, 20)
ax2 = plt.subplot2grid(gridsize, (1, 0))
ax3 = plt.subplot2grid(gridsize, (1, 1))
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(12,8))
def one_frame(i):
ax[0].clear();ax[1].clear()
try:
a_points = np.unique(A_points[i],axis=0)
tri_a = Delaunay(a_points)
ax[0].triplot(*a_points.T, tri_a.simplices, color = 'orange')
except Exception:
pass
try:
b_points = np.unique(B_points[i],axis=0)
tri_b = Delaunay(b_points)
ax[1].triplot(*b_points.T, tri_b.simplices, color = 'purple')
except Exception:
pass
ani = animation.FuncAnimation(fig,one_frame, blit = False)
It is possible mate, try this code
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import Delaunay
import matplotlib.animation as animation
# data frame containing time points without adequate points (3)
df = pd.DataFrame({
'Time' : [1,1,1,1,1,1,1,2,2,2,2,2,2,2,3,3],
'Item' : ['A','B','A','B','A','B','A','A','B','A','B','A','B','B','A','B'],
'X' : [5, 5, 6, 6, 4, 3, 3, 4, 4, 3, 2, 5, 4, 5, 1, 2],
'Y' : [5, 6, 6, 5, 5, 6, 5, 6, 3, 1, 4, 6, 7, 4, 5, 6],
})
A_coord = df.loc[df['Item'] == 'A']
B_coord = df.loc[df['Item'] == 'B']
def make_points(x):
return np.array(list(zip(x['X'], x['Y'])))
A_points = A_coord.groupby(['Time']).apply(make_points)
B_points = B_coord.groupby(['Time']).apply(make_points)
A_points = A_points.values
B_points = B_points.values
fig, ax = plt.subplots(nrows=1,ncols=2,figsize=(12,8))
def one_frame(i):
ax[0].clear();ax[1].clear()
try:
a_points = np.unique(A_points[i],axis=0)
tri_a = Delaunay(a_points)
ax[0].triplot(*a_points.T, tri_a.simplices, color = 'orange')
except Exception as e:
print("frame %i, point a can't print because of \n%s" % (i,e))
try:
b_points = np.unique(B_points[i],axis=0)
tri_b = Delaunay(b_points)
ax[1].triplot(*b_points.T, tri_b.simplices, color = 'purple')
except Exception as e:
print("frame %i, point b can't print because of \n%s" % (i,e))
ani = animation.FuncAnimation(fig,one_frame,range(3), blit = False)
ani.save('test.gif', writer='pillow', fps=1)
The output is
UPDATE
It is possible to keep the fig
and ax
, the idea is to remove the triangles (Line2D
object) of the last frame at the beginning of each frame by
for item in triangles_a:
try:
item.remove()
except Exception as e:
continue
for item in triangles_b:
try:
item.remove()
except Exception as e:
continue
Remove the triangles won't affect other parts of the fig
and ax
. For example, in the example below, the two circles won't be affected during the animation.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import Delaunay
import matplotlib.animation as animation
# data frame containing time points without adequate points (3)
df = pd.DataFrame({
'Time' : [1,1,1,1,1,1,1,2,2,2,2,2,2,2,3,3],
'Item' : ['A','B','A','B','A','B','A','A','B','A','B','A','B','B','A','B'],
'X' : [5, 5, 6, 6, 4, 3, 3, 4, 4, 3, 2, 5, 4, 5, 1, 2],
'Y' : [5, 6, 6, 5, 5, 6, 5, 6, 3, 1, 4, 6, 7, 4, 5, 6],
})
A_coord = df.loc[df['Item'] == 'A']
B_coord = df.loc[df['Item'] == 'B']
def make_points(x):
return np.array(list(zip(x['X'], x['Y'])))
A_points = A_coord.groupby(['Time']).apply(make_points)
B_points = B_coord.groupby(['Time']).apply(make_points)
A_points = A_points.values
B_points = B_points.values
fig = plt.figure(figsize = (8,10))
grid = gridspec.GridSpec(2, 2)
gridsize = (2, 2)
ax0 = plt.subplot2grid(gridsize, (0, 0), colspan = 2)
ax1 = plt.subplot2grid(gridsize, (1, 0), colspan = 1)
ax2 = plt.subplot2grid(gridsize, (1, 1), colspan = 1)
ax1.set_xlim(0,8);ax1.set_ylim(0,8)
ax2.set_xlim(0,8);ax2.set_ylim(0,8)
# things that won't be affected
circle_0 = plt.Circle((4,4), 2, color='violet',fill=False)
ax1.add_artist(circle_0)
circle_1 = plt.Circle((5,4), 2, color='deepskyblue',fill=False)
ax2.add_artist(circle_1)
triangles_a,triangles_b = [],[]
def one_frame(i):
global triangles_a,triangles_b
for item in triangles_a:
try:
item.remove()
except Exception as e:
continue
for item in triangles_b:
try:
item.remove()
except Exception as e:
continue
try:
a_points = np.unique(A_points[i],axis=0)
tri_a = Delaunay(a_points)
obj_a = ax1.triplot(*a_points.T, tri_a.simplices, color = 'orange')
triangles_a.extend(obj_a)
except Exception as e:
print("frame %i, point a can't print because of \n%s" % (i,e))
try:
b_points = np.unique(B_points[i],axis=0)
tri_b = Delaunay(b_points)
obj_b = ax2.triplot(*b_points.T, tri_b.simplices, color = 'purple')
triangles_b.extend(obj_b)
except Exception as e:
print("frame %i, point b can't print because of \n%s" % (i,e))
ani = animation.FuncAnimation(fig,one_frame,range(3), blit = False)
ani.save('test.gif', writer='pillow', fps=1)
the output