Python Cube

../../_images/anim_pycube.png

Animation control:

Visualization Frame Value
Surface geometry constant
Surface position fixed to the coordinate axis
Surface color constant
Shading and highlighting illumination direction per frame
Axis coordinate elev and azim per frame using view_init

Surface construction is based on the Composite of Copies example. Now, both the elev and azim are changed per frame. Changing the axis coordinate and shading/highlighting results in ‘stationary viewer’ perception. The view is rotated about a single surface object. The illumination direction path as the elev and azim changes is shown in the following plot. The starting direction [1,1,1] is shown in red.

../../_images/illum_path.png

In the script that follows, note the following line:

if view_elev >= 270 : view_elev = view_elev - 360

This was needed since the elev argument in the call to ‘view_init’ only provides a unique solution in the domain of -90° to 270°.

import copy
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import matplotlib.animation as animation
import matplotlib.patheffects as path_effects
import s3dlib.surface as s3d

#.. Elev and Azim changes setting illumination direction.

# 1. Define functions to examine ....................................

view_elev, view_azim = 0, 0
illum_dir = [1,1,1]

# 2. Setup and map surface .........................................
rez = 6

top = s3d.PlanarSurface(rez, basetype='oct1')
top.map_color_from_image('data/python.png')
top.map_geom_from_image('data/python_elevation.png',0.07)

front = copy.copy(top)
side = copy.copy(top)
bottom = copy.copy(top)
backside = copy.copy(top)
top.transform(translate=[0,0,1])
backfront = copy.copy(top)
bottom.transform(rotate=s3d.eulerRot(0,180),  translate=[0,0,-1])
front.transform(rotate=s3d.eulerRot(0,90),  translate=[0,-1,0])
backfront.transform(rotate=s3d.eulerRot(180,90),  translate=[0,0,0])
side.transform(rotate=s3d.eulerRot(90,90),  translate=[1,0,0])
backside.transform(rotate=s3d.eulerRot(-90,90),  translate=[-1,0,0])

cube = (top+front+side+bottom+backfront+backside)
orig_cube = copy.copy(cube)
illum = s3d.rtv(illum_dir, view_elev, view_azim)
cube.shade(direction=illum).hilite(.8,direction=illum)

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

fig = plt.figure(figsize=plt.figaspect(1), facecolor='black')
ax = plt.axes(projection='3d')
ax.set(xlim=(-1,1), ylim=(-1,1), zlim=(-1,1))
ax.set_facecolor('black')
ax.set_axis_off()

info = 'Created with S3Dlib 1.0.0, 2020 - https://s3dlib.org'
pblue, pyellow = [0.216,0.443,0.635] , [ 1.0,0.827,0.263]
text = fig.text(0.05, 0.05, info, color=pyellow, fontsize=9  )
text.set_path_effects([path_effects.withSimplePatchShadow(linewidth=3, foreground=pblue)])

ax.add_collection3d(cube)

fig.tight_layout()


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

def init_fig():
    return cube,

def update_fig(frame):
    global cube
  
    ax.collections.remove(cube)

    view_elev = 360*frame
    view_azim = 360*frame
    if view_elev >= 270 : view_elev = view_elev - 360
    illum = s3d.rtv(illum_dir, view_elev, view_azim)

    cube = copy.copy(orig_cube)
    cube.shade(direction=illum).hilite(.8,direction=illum)
    ax.add_collection3d(cube)

    ax.view_init( elev=view_elev, azim=view_azim )

    return cube,

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

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()
print(">>>>>>>>>>>>>>> process completed")