Dini Surface Animation

../../_images/anim_dini.png

Animation control:

Visualization Frame Value
Surface geometry multitple functional parameters per frame
Surface position functional z-coordinate axis parameter per frame
Surface color constant
Shading and highlighting fixed to the coordinate axis
Axis coordinate constant

Based on the static plot from the Polar Coordinates to XYZ example.

Multiple ‘constants’ values for the Dini surface parametric equations were varied along the frame. The values were varied in a linear segmented function of the frame using the s3d.frame_to_value function to provide the multiple steps in the animation. In addition, a mapping of the PolorSurface radius was used to provide the effect of the ‘petals.’ The first mapping was the radial distance of the initial polar surface (petals) which was then followed by the dini transformation. Also, a following transformation was used to adjust the vertical length during the animation.

../../_images/dini_xy_plot.png

A custom color map was created to suggest a flower surface.

../../_images/dini_cmap.png
import copy
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
import matplotlib.animation as animation
import s3dlib.surface as s3d
import s3dlib.cmap_utilities as cmu

#.. Animation demo using a parametric frame history.

# 1. Define function to examine .....................................

# parameter = f( frmae position )
mrh,mps,mpd = 0.2, -6, 0.6 # smallest radius, position, petal depth
alf_list = [ 0.0, 1.0, 1.0, 1.0, 1.0, 0.0 ]
bet_list = [ 0.0, 0.0, 0.0, 1.0, 1.0, 1.0 ]
rho_list = [ mrh, mrh, 1.0, 1.0, 1.0, mrh ]
pos_list = [ mps, 0.0, 0.0, 0.0, 0.0, mps ]
pet_list = [ 0.0, 0.0, mpd, mpd, 0.0, 0.0 ]

def getParams(f):
    return s3d.frame_to_value(f,alf_list,bet_list,rho_list,pos_list,pet_list)

def petals(rtz,depth,numb=7) :
    r,t,z = rtz
    sc = 1 - depth*(  0.5*(1+np.cos(numb*t))  )
    return sc*r,t,z

def dinisurf2(rtz,alpha,beta,rho) :
    r,t,z = rtz
    a, b = 1.5, 0.2
    R = rho*r
    b = b*(1-beta)
    T = (1+np.abs(alpha-beta))*t
    x = a*np.cos(T)*np.sin(R)
    y = a*np.sin(T)*np.sin(R)
    z = a*(np.cos(r) + np.log(np.tan(r/2))) + b*T
    return x,y,z*alpha

# 2. Setup and map surfaces .........................................
frame = 0
rez = 6
cmap1 = cmu.hsv_cmap_gradient( 'green','cornsilk',smooth=1 )
cmap2 = cmu.hsv_cmap_gradient( 'cornsilk','lightsalmon',smooth=.5 )
cmap = cmu.stitch_cmap(cmap1,cmap2)

base = s3d.PolarSurface(rez, basetype='hex_c', minrad=0.01)
base.map_cmap_from_op( lambda rtz: rtz[0] , cmap=cmap )
surface = copy.copy(base)
alpha, beta, rho, pos, pet = getParams(frame)
surface.map_geom_from_op( lambda rtz: petals(rtz,pet) )
surface.map_geom_from_op( lambda rtz: dinisurf2(rtz,alpha,beta,rho), returnxyz=True )
surface.shade().hilite(focus=3)
surface.transform(translate=[0,0,pos])

# 3. Construct figures, add surface, plot ...........................

fig = plt.figure(figsize=plt.figaspect(1),facecolor='k')
ax = plt.axes(projection='3d',facecolor='k')
s=1.5
minmax=(-.75*s,.75*s)
ax.set(xlim=minmax, ylim=minmax, zlim=(-3*s,s) )
ax.set_axis_off()
ax.view_init(elev=25)

ax.add_collection3d(surface)

fig.tight_layout()

# 4. Animation ......................................................

def init_fig():
    return surface,

def update_fig(frame):
    global surface

    ax.collections.remove(surface)

    surface = copy.copy(base)
    alpha, beta, rho, pos, pet = getParams(frame)
    surface.map_geom_from_op( lambda rtz: petals(rtz,pet) )
    surface.map_geom_from_op( lambda rtz: dinisurf2(rtz,alpha,beta,rho), returnxyz=True )
    surface.shade().hilite(focus=3)
    surface.transform(translate=[0,0,pos])

    ax.add_collection3d(surface)

    return surface,

ani = FuncAnimation(fig, update_fig, frames=np.linspace(0.0, 1.0, 150),
                    init_func=init_fig, blit=False, repeat=True, interval=50)

print(">>>>>>>>>>>>>>> Animation completed, file save proceeds")
#ani.save('ZZZ.mp4')                                   # use for movie file.
ani.save(None,writer=animation.FFMpegFileWriter())    # use for temp files.
print(">>>>>>>>>>>>>>> Save completed, screen display proceeds")

plt.show()

The script to construct the plot of the parameter values per frame follows:

import numpy as np
import matplotlib.pyplot as plt
import s3dlib.surface as s3d

# 1. Define function to examine ...............

# parameter = f( frmae position )
mrh,mps,mpd = 0.2, -6, 0.6 # smallest radius, position, petal depth
alf_list = [ 0.0, 1.0, 1.0, 1.0, 1.0, 0.0 ]
bet_list = [ 0.0, 0.0, 0.0, 1.0, 1.0, 1.0 ]
rho_list = [ mrh, mrh, 1.0, 1.0, 1.0, mrh ]
pos_list = [ mps, 0.0, 0.0, 0.0, 0.0, mps ]
pet_list = [ 0.0, 0.0, mpd, mpd, 0.0, 0.0 ]

def getParams(f):
    return s3d.frame_to_value(f,alf_list,bet_list,rho_list,pos_list,pet_list)

# 2. Setup and map lines ......................
mps = 0.6  # adjust only for plotting
pos_list = [ mps, 0.0, 0.0, 0.0, 0.0, mps ]

N = 150
frames = np.linspace(0.0, 1.0, N)
pVals = [ getParams(f) for f in frames ]
yVals = [ list(val) for val in list(zip(*pVals)) ]  #transpose

fig = plt.figure(figsize=(7,3))
ax = plt.axes()
plt.xlabel('frame (normalize)')
plt.ylabel('parameter value')
labels = ['alpha','beta','rho','pos*','pet']
steps = [ 'grow\ncurl', 'expand', 'rotate\nuncurl', 'flatten','shrink']

for i in range(5) :
    ax.plot(frames, yVals[i], label=labels[i])
    fig.text(0.24+0.15*i,0.95,steps[i], ha='center', va='center', fontsize='small')
plt.legend()
fig.tight_layout(pad=2)
plt.show()