Python Cube¶
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.
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.patheffects as path_effects
import s3dlib.surface as s3d
#.. Elev and Azim changes setting illumination direction.
# 0. Define animation control parameters ............................
totalTime, f_domain, numFrames = 5, (0.0,1.0), 90 # time in seconds
frames=np.linspace(*f_domain, numFrames, endpoint=False)
interval = int(1000.0*totalTime/numFrames) # milliseconds
# 2. Setup and map surface .........................................
rez = 6
view_elev, view_azim, illum_dir = 0, 0, [1,1,1]
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', facecolor='black', aspect='equal')
ax.set(xlim=(-1,1), ylim=(-1,1), zlim=(-1,1))
ax.set_axis_off()
info = 'Created with S3Dlib 1.3.0, 2024 - 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()
#plt.show()
# 4. Animation ======================================================
def update_fig(frame):
global cube
cube.remove()
view_elev = 360*frame
view_azim = 360*frame
if view_elev >= 270 : view_elev = view_elev - 360
ax.view_init( elev=view_elev, azim=view_azim )
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)
return
anim = FuncAnimation(fig, update_fig, frames, interval=interval, repeat=True)
anim.save('pycube.html',writer='html')
msg = "saved {} frames, values: [{:.3f} to {:.3f}] @ {} milliseconds/frane"
print(msg.format(numFrames,np.min(frames),np.max(frames),interval))