HSV MappingΒΆ

../../_images/anim_hsv_cylinder.png

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