HSV MappingΒΆ
Animation control:
Visualization |
Frame Value |
---|---|
Surface geometry |
sectioning parameter per frame |
Surface position |
fixed to the coordinate axis |
Surface color |
constant |
Shading and highlighting |
fixed to the coordinate axis |
Axis coordinate |
constant |
This is a composite surface of Planar, Polar and Cylindrical surfaces. As a result, a single-color mapping function is used instead of defining three separate functions which would be required if treated separately, one for each surface native coordinate system.
Note
Any composite surface uses a xyz coordinate system.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import s3dlib.surface as s3d
#.. HSV Color Space Animation
# 0. Define animation control parameters ............................
frameSetSize = 30 # numb frames in each transition phase
totalTime, numFrames = 12, 4*frameSetSize # time in seconds
frames=range(numFrames)
interval = int(1000.0*totalTime/numFrames) # milliseconds
# 1. Define function to examine .....................................
def xyz2hsv(xyz,rot) :
# hsv color cylinder from -1<z<1, 0<R<1
x,y,z = xyz
theta = np.arctan2(y,x)
theta = np.where(theta<0,theta+2*np.pi,theta)
h = np.mod( rot + theta/(2*np.pi), 1.0 )
s = np.sqrt( x**2 + y**2)
v = ( z + 1 ) /2.0
return h,s,v
def get_surface(Z1TSTE_a) :
z1, ts, te, ag = Z1TSTE_a
# ts, te - theta start, end
def outsideface(rtz, Zm, Ts, Te, isTop=True) :
r,t,z = rtz
T = (Te-Ts)*t + 2*np.pi*Ts
T = np.mod(T,2*np.pi)
if isTop : Z = np.full(len(z),Zm)
else: Z = (Zm+1)*z/2 + (Zm-1)/2
return r,T,Z
def insideface(xyz, Zm, T, back=1) :
x,y,z = xyz
A = ( x + 1.0 )/2.0
Z = back*(Zm+1)*y/2 + (Zm-1)/2
X = A*np.cos(T*2*np.pi)
Y = A*np.sin(T*2*np.pi)
return X,Y,Z
rez=5
front = s3d.PlanarSurface(rez,color='C0')
front.map_geom_from_op(lambda xyz : insideface(xyz,z1,ts))
back = s3d.PlanarSurface(rez,color='C1')
back.map_geom_from_op(lambda xyz : insideface(xyz,z1,te,-1))
top = s3d.PolarSurface(rez,basetype='hex_c',color='C2')
top.map_geom_from_op(lambda rtz : outsideface(rtz,z1,ts,te))
side = s3d.CylindricalSurface(rez,basetype='tri_s',color='C3')
side.map_geom_from_op(lambda rtz : outsideface(rtz,z1,ts,te,False))
surface = front+back+top+side
surface.map_color_from_op(lambda xyz : xyz2hsv(xyz,ag), rgb=False )
surface.shade(0.925)
return surface
def get_frames(n) :
negmin = -0.9999
ts_0 = 1/12
te_0 = 1 + ts_0
ts_f,te_f = 1/3, 5/6
z = np.linspace(negmin, 1.0, n)
z = np.concatenate( ( z, np.full(2*n,1.0) ) )
z = np.concatenate( ( z, np.linspace( 1.0, negmin, n) ) )
ts = np.full(n,ts_0)
ts = np.concatenate( ( ts, np.linspace( ts_0, ts_f, n) ) )
ts = np.concatenate( ( ts, np.full(n,ts_f) ) )
ts = np.concatenate( ( ts, np.linspace( ts_f, ts_0, n) ) )
te = np.full(n,te_0)
te = np.concatenate( ( te, np.linspace( te_0, te_f, n) ) )
te = np.concatenate( ( te, np.full(n,te_f) ) )
te = np.concatenate( ( te, np.linspace( te_f, te_0, n) ) )
a = np.zeros(2*n)
a = np.concatenate( ( a, np.linspace( 0, 1.0, 2*n) ) )
return np.transpose([z,ts,te,a])
framearray = get_frames(frameSetSize)
# 2. Setup and map surfaces .........................................
fID = 0
surface = get_surface(framearray[fID])
floor = s3d.PolarSurface.grid(1,30,color='.9').domain(zcoor=-1.001)
# 3. Construct figure, add surface, plot ............................
fig = plt.figure(figsize=plt.figaspect(1),constrained_layout=True)
fig.text(0.05,0.05,str(surface), ha='left', va='bottom', fontsize='smaller')
ax = plt.axes(projection='3d', aspect='equal', proj_type='ortho')
ax.set(xlim=(-1,1), ylim=(-1,1), zlim=(-1,1) )
ax.set_axis_off()
ax.view_init(azim=30)
ax.add_collection3d(surface)
ax.add_collection3d(floor)
#plt.show()
# 4. Animation ======================================================
def update_fig(frame):
global surface
surface.remove()
surface = get_surface( framearray[frame] )
ax.add_collection3d(surface)
return
anim = FuncAnimation(fig, update_fig, frames, interval=interval, repeat=True)
anim.save('hsv_cylinder.html',writer='html')
msg = "saved {} frames, values: [{:.3f} to {:.3f}] @ {} milliseconds/frane"
print(msg.format(numFrames,np.min(frames),np.max(frames),interval))