Colormap Frame Animation¶
Apparent movement of a static geometric object by changing the colormapping reference per frame. Transparent sections of the colormaps are used to ‘hide’ portions of the geometry per frame. Animation control:
Visualization |
Frame Value |
---|---|
Surface geometry |
constant |
Surface position |
constant |
Surface colormap |
colormap position per frame |
Shading and highlighting |
constant |
Axis coordinate |
constant |
Following colormaps were used:
import copy
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
import s3dlib.surface as s3d
import s3dlib.cmap_utilities as cmu
#.. Annimated colormap
# 0. Define animation control parameters ............................
totalTime, f_domain, numFrames = 4.5, (0.0,1.0), 90 # time in seconds
frames=np.linspace(*f_domain, numFrames, endpoint=False)
interval = int(1000.0*totalTime/numFrames) # milliseconds
# 1. Define functions to examine ....................................
wdth, subW = 0.4, 0.4
elev, azim = 90, -30
illum = s3d.rtv([1,-1,1],elev,azim)
def Torus(rez,width=wdth ) :
def fold(rtz,width) :
r,t,z = rtz
Z = width*np.sin(z*np.pi)/2
R = 1 + width*np.cos(z*np.pi)/2
return R,t,Z
surface = s3d.CylindricalSurface(rez,basetype='tri_s')
surface.map_geom_from_op( lambda rtz : fold(rtz,width) )
return surface
def Trefoil(rtz) :
r,t,z = rtz
rw = 1-wdth/2
X = rw*(np.sin(t)+2*np.sin(2*t))
Y = rw*(np.cos(t)-2*np.cos(2*t))
R0,T,Z = s3d.CylindricalSurface.coor_convert([X,Y,z]) # cylindrical coor
R = R0 + r - rw
Z = z - np.sin(3*t)
return R,T,Z
def shift(rtz, os=0) :
r,t,z = rtz
T = np.mod(t+os,np.full(len(t),2*np.pi))
return T
def ballCoor(rtp,os) :
xyz = s3d.SphericalSurface.coor_convert(rtp,True)
rtz = s3d.CylindricalSurface.coor_convert(xyz)
rtz = np.array(rtz)
rtz[1,:] = rtz[1,:] - os
rtz = Trefoil(rtz)
xyz = s3d.CylindricalSurface.coor_convert(rtz,True)
return xyz
# 2. Setup and map surfaces .........................................
rez, offset = 6, 0
cyan2clear = cmu.hsv_cmap_gradient( [.5,1,1,1], [.5,1,1,0] )
clear = cmu.hsv_cmap_gradient( [.5,1,1,0], [.5,1,1,0] )
cmap1 = cmu.stitch_cmap( cyan2clear, clear,bndry=[0.075], name='cmap1' )
cmap2 = cmu.stitch_cmap('inferno_r', clear,bndry=[0.58], name='cmap2' )
wt_cy = cmu.hsv_cmap_gradient([.66,0,1],'cyan', name='wt_cy' )
surface1 = Torus(rez)
surf1 = copy.copy(surface1)
surface1.map_cmap_from_op( lambda rtz : shift(rtz,offset), cmap1)
surface1.map_geom_from_op( Trefoil )
surface2 = Torus(rez, subW*wdth)
surf2 = copy.copy(surface2)
surface2.map_cmap_from_op( lambda rtz : shift(rtz,offset), cmap2)
surface2.map_geom_from_op( Trefoil )
ball = s3d.SphericalSurface(5,'octa',color=[0,1,1,1]).domain(wdth/2)
ball.clip(lambda c : np.less_equal(c[1],[0]), usexyz=True )
ball.map_cmap_from_op(lambda c: s3d.SphericalSurface.coor_convert(c,True)[1], wt_cy)
ball.transform(translate=[1,0,0])
ball0 = copy.copy(ball)
ball0.map_geom_from_op(lambda c: ballCoor(c,offset), returnxyz=True)
ring = surface1 + surface2 + ball0
ring.shade(.2,direction=illum).hilite(.8,direction=illum)
# 3. Construct figure, add surfaces, and plot ......................
fig = plt.figure(figsize=plt.figaspect(1), facecolor='k')
info = str(surface1)+'\n'+str(surface2)
text = fig.text(0.02, 0.02, info, color='grey', fontsize='small' )
ax = plt.axes(projection='3d', facecolor='k', aspect='equal', proj_type='ortho')
minmax = (-2,2)
ax.set(xlim=minmax, ylim=minmax, zlim=minmax )
ax.set_axis_off()
ax.view_init(elev,azim)
ax.add_collection3d(ring)
fig.tight_layout(pad=0)
#plt.show()
# 4. Animation ......................................................
def update_fig(frame):
global ring
ring.remove()
offset = frame*(2*np.pi)
surface1 = copy.copy(surf1)
surface1.map_cmap_from_op( lambda rtz : shift(rtz,offset), cmap1)
surface1.map_geom_from_op( Trefoil )
surface2 = copy.copy(surf2)
surface2.map_cmap_from_op( lambda rtz : shift(rtz,offset), cmap2)
surface2.map_geom_from_op( Trefoil )
ball0 = copy.copy(ball)
ball0.map_geom_from_op(lambda c: ballCoor(c,offset), returnxyz=True)
ring = surface1 + surface2 + ball0
ring.shade(.2,direction=illum).hilite(.8,direction=illum)
ax.add_collection3d(ring)
return
anim = FuncAnimation(fig, update_fig, frames, interval=interval, repeat=True)
anim.save('loop_cmap.html',writer='html')
msg = "saved {} frames, values: [{:.3f} to {:.3f}] @ {} milliseconds/frane"
print(msg.format(numFrames,np.min(frames),np.max(frames),interval))