Vector Magnitude within a DomainΒΆ

../../_images/anim_vmag_surface.webp

Animation control:

Visualization

Frame Value

Surface geometry

sectioning parameter per frame

Surface position

fixed to the coordinate axis

Surface color

color per frame

Shading and highlighting

fixed to the coordinate axis

Axis coordinate

constant

This example illustrates the magnitude of a vector field within a domain, as also shown in the static Vector Surfaces example for constant values.

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

# 0. Define animation control parameters ............................

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

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

def vector_mag(xyz) :
    x,y,z = xyz
    u =    np.sin(np.pi*x) * np.cos(np.pi*z)
    v = -2*np.sin(np.pi*y) * np.cos(2*np.pi*z)
    w = np.cos(np.pi*x)*np.sin(np.pi*z) + np.cos(np.pi*y)*np.sin(2*np.pi*z)
    vect = np.array([u,v,w]).T
    return np.linalg.norm(vect,axis=3).T

def frame_to_vals(f,cloud,zeta=0.85) :
    min_cldV, max_cldV = cloud.bounds['vlim']
    del_cldV = max_cldV - min_cldV
    mid_cldV = (max_cldV + min_cldV)/2
    frame_to_A = lambda t : 2*t if t<.5 else 2*(1-t)  # 0 <= A <= 1
    A = frame_to_A(f)
    A_to_Fo = lambda A : (A-.5)*zeta*del_cldV + mid_cldV
    A_to_cB = lambda A : zeta*A + (1-zeta)/2
    return A_to_cB(A), A_to_Fo(A)

def indicator_by_A(fig, A, vOld=None) :
    '''   0 <= A <= 1 '''
    symbol, blank = r'$\blacktriangleright$', r'$\blacksquare$'
    horz, vBot, vRng = 0.80, 0.22, 0.56
    horz, vBot, vRng = 0.825, 0.22, 0.56
    vert = vBot + vRng*A
    bkgrd = fig.get_facecolor()
    if vOld is not None: #.. cover current indicator with a blank symbol before showing indicator.
        fig.text(horz,vOld,blank, ha='right', va='center', fontsize='x-large', color=bkgrd)
    fig.text(horz,vert,symbol, ha='right', va='center', fontsize='large', color='k')
    return vert

# 2. Setup and map surfaces .........................................
domain = [ [0,1], [0,1], [0,1]  ]
xlim,ylim,zlim = domain
drez, cmap = 5, cmu.hue_cmap('b','r',2.0,name='BlRd')

cloudObj = ptc.Point3DCloud(drez, domain=domain)
cloudObj.map_vals_from_op(vector_mag)
cloudObj.map_cmap_from_cloudvals(cmap)

t=.25
vbar,Vo = frame_to_vals(t,cloudObj)
surface = cloudObj.valsurf(Vo)

# 3. Construct figures, add surfaces, and plot ....................

fig = plt.figure(figsize=(5,4))
fig.text(.5,0.9,'Magnitude',ha='center')
ax = fig.add_subplot(111, projection='3d', aspect='equal', focal_length=0.25)
ax.view_init(0,-120)
ax.set(xlim=xlim, ylim=ylim, zlim=zlim, xlabel='x',ylabel='y',zlabel='z')

prevIndicator = indicator_by_A(fig, vbar)
surface = surface.shade(.4,ax=ax).hilite(.5,ax=ax)
ax.add_collection3d(surface)
s3d.add_boxCorner(ax,domain)

cbar =fig.colorbar(cloudObj.cBar_ScalarMappable, ax=ax,shrink=0.6, pad=.12)
cbar.set_label('cloud values', rotation=270, labelpad = 15)

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

def update_fig(frame):
    global surface,prevIndicator
    surface.remove()
    
    vbar,Vo = frame_to_vals(frame,cloudObj)
    surface = cloudObj.valsurf(Vo)
    prevIndicator = indicator_by_A(fig, vbar, prevIndicator)
    surface.shade(.4,ax=ax).hilite(.5,ax=ax)
    ax.add_collection3d(surface)

    return

anim = FuncAnimation(fig, update_fig, frames, interval=interval, repeat=True)
anim.save('anim_vmag.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')