Implicit SurfaceΒΆ

../../_images/anim_func3D_XYZ.webp

Animation control:

Visualization

Frame Value

Surface geometry

functional parameter per frame

Surface position

constant

Surface color

color per frame

Shading and highlighting

fixed to the coordinate axis

Axis coordinate

constant

The above animation uses the same function as the static plot from the Contour Surface within a Volume example. For this example, a cloud is first constructed and then used to generate multiple cloud surfaces for the animation. This improves computational efficiency as opposed to using multiple implsurf method calls.

The frame function dualcusp was used to increase the density of functional values near zero.

../../_images/plot_dualcusp.png

As the functional value, Fo, changes sign, the outward normal of the surface reverses. For consistent shading, the surface evert method is used, as highlighted if the following script.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import s3dlib.surface as s3d
import s3dlib.pntcloud as ptc

totalTime, f_domain, numFrames = 10, (0.0,1.0), 121   # time in seconds
frames=np.linspace(*f_domain, numFrames, endpoint=True)
interval = int(1000.0*totalTime/numFrames)            # milliseconds

def dualcusp(t) :
    # used to increase density of Fo values near zero.
    y = -np.cos(2*t*np.pi)
    y = np.where(y>0.0,-y,y)
    y = np.where(t>0.5,-2-y,y)
    return y+1

frame_to_A  = lambda t : (1.0 + dualcusp(t))/2
frame_to_Fo = lambda t : -0.001 + 0.42*dualcusp(t)

def indicator_by_A(fig, A, vOld=None) :
    symbol, blank = r'$\blacktriangleright$', r'$\blacksquare$'
    horz, vBot, vRng = 0.80, 0.22, 0.56
    vert = vBot + vRng*A
    if vOld is not None: #.. cover current > symbol
        fig.text(horz,vOld,blank, ha='right', va='center', fontsize='x-large', color='w')
    fig.text(horz,vert,symbol, ha='right', va='center', fontsize='large')
    return vert

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

def func3D(xyz) :
    x,y,z = xyz
    f = (x+y+z)*np.exp(-3*(x**2 + y**2 + z**2) )
    return f

# 2. Setup and map cloud and surface ................................
drez, dmn, cmap = 5, [-1,1], 'jet'
t=0.15

Fo = frame_to_Fo(t)
cloudObj = ptc.Point3DCloud(drez,domain=dmn,cmap=cmap)
cloudObj.map_vals_from_op(func3D)
cloudObj.map_cmap_from_cloudvals()

surface = cloudObj.valsurf(Fo)
if Fo > 0.0 : surface.evert()

# 3. Construct figures, add surfaces, and plot ....................
ticks = [-1,-.5,0,.5,1]
info = r"Fo = f(x,y,z) = (x+y+z)$e^{-3(x^2+y^2+z^2)}$ "
fig = plt.figure(figsize=(5,4))
fig.text(0.5,0.98,info, ha='center', va='top', fontsize='x-large')
ax = plt.axes(projection='3d', aspect='equal')
ax.set(xticks=ticks, yticks=ticks, zticks=ticks,
       xlabel='x',ylabel='y',zlabel='z')
scmp = cloudObj.cBar_ScalarMappable
cbar = plt.colorbar(scmp, ax=ax,  shrink=0.6, pad=.12 )
cbar.set_label('Fo', rotation=0, labelpad = 5, fontsize='x-large')
prevIndicator = indicator_by_A(fig, frame_to_A(t))

ax.add_collection3d(surface.shade().hilite(0.5))
s3d.add_boxCorner(ax,dmn)

fig.tight_layout(pad=1)
plt.show()
# 4. Animation ======================================================

def update_fig(frame):
    global surface,prevIndicator
    surface.remove()
    
    Fo = frame_to_Fo(frame)
    surface = cloudObj.valsurf(Fo)
    if Fo > 0.0 : surface.evert()
    prevIndicator = indicator_by_A(fig, frame_to_A(frame), prevIndicator)
    ax.add_collection3d(surface.shade().hilite(.5))
    return

anim = FuncAnimation(fig, update_fig, frames, interval=interval, repeat=True)
anim.save('func3D_XYZ.html',writer='html')

msg = "saved {} frames, values: [{:.3f} to {:.3f}] @ {} milliseconds/frane"
print(msg.format(numFrames,np.min(frames),np.max(frames),interval))

print('done')