Dual Surface Face ColorΒΆ

../../_images/anim_fig_8_knot.png

Animation control:

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

Surface construction is based on the Inner and Outer Surface Coloring example. Color mapping based on the view in the map_cmap_from_normals method, as in the Inner and Outer Surface Coloring example, provides front and back surface coloring (direction=ax). Now, both the elev and azim change the shading per frame, same as the Python Cube animation, with the addition of the view information provided by the ax=ax parameter changing surface color.

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

#.. Elev and Azim changes setting illumination direction and surface coloring.

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

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

def fig_8_knot(t,isInner) :
    T = 2*np.pi*t
    # https://en.wikipedia.org/wiki/Figure-eight_knot_(mathematics)#Description
    x = (2 + np.cos(2*T))*np.cos(3*T)
    y = (2 + np.cos(2*T))*np.sin(3*T)
    z = 3*np.sin(4*T) 
    f = 0.8 if isInner else 1.2
    return f*x,f*y,f*z

inn_eight = lambda t : fig_8_knot(t,True)
out_eight = lambda t : fig_8_knot(t,False)

# 2. Setup and map surface .........................................
rez=8
bcmap = cmu.binary_cmap('orange', 'yellowgreen', name='orng_yg' )
line_1 = s3d.ParametricLine(rez,inn_eight)
line_2 = s3d.ParametricLine(rez,out_eight)
knot = line_1.get_surface_to_line(line_2)

orig_knot = copy.copy(knot)

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

minmax = (-3,3)
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()
ax.set(xlim=minmax,ylim=minmax,zlim=minmax)

info = 'Created with S3Dlib 1.1.0, 2022 - https://s3dlib.org'
kgreen, korange = colors.to_rgba('green'), colors.to_rgba('orange')
text = fig.text(0.02, 0.02, info, color=korange, fontsize=9  )
text.set_path_effects([path_effects.withSimplePatchShadow(linewidth=3, foreground=kgreen)])

knot.map_cmap_from_normals(direction=ax,cmap=bcmap)
knot.shade(0.1,direction=illum_dir,ax=ax).hilite(.5,direction=illum_dir,ax=ax)

ax.add_collection3d(knot)

fig.tight_layout(pad=0)

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

def init_fig():
    return knot,

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

    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 )

    knot = copy.copy(orig_knot)
    knot.map_cmap_from_normals(direction=ax,cmap=bcmap)
    knot.shade(0.1,direction=illum_dir,ax=ax).hilite(.5,direction=illum_dir,ax=ax)

    ax.add_collection3d(knot)

    return knot,

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")