o
    j                     @  s   d Z ddlmZ ddlZddlZddlmZ ed Zed Z	ed Z
ed	 Zed
 Zed Zed Zed Zed Zed Zed Zed Zed Zed Zd<ddZ	d=d>d#d$Zd?d%d&Z		'	(	(d@dAd-d.Z		/	0	1dBdCd5d6Z		'	/	0dDdEd:d;ZdS )Fac  Natural eye motion: blinks + subtle iris tremor.

Final-pass post-processor for compiled blendshape targets. Adds:
  1. Periodic blinks (Poisson-distributed, suppressed during emotional eye peaks)
  2. Subtle iris drift (low-frequency smoothed noise on eye-look channels)

Deterministic per scenario: same scenario_id always produces the same eye motion.
    )annotationsN   )NAME_TO_IDXeyeBlinkLefteyeBlinkRighteyeLookDownLefteyeLookDownRighteyeLookInLefteyeLookInRighteyeLookOutLefteyeLookOutRighteyeLookUpLefteyeLookUpRighteyeSquintLefteyeSquintRighteyeWideLefteyeWideRightseed_strstrreturnnp.random.Generatorc                 C  s.   t | d }tjt|dd dS )z;Deterministic RNG from arbitrary string (e.g. scenario_id).zutf-8N   )hashlibmd5encode	hexdigestnprandomdefault_rngint)r   h r!   S/dataset/kemix-engine/package/face/animasync-face-v3/scripts/compiler/eye_motion.py_seeded_rng!   s   r#         ?   Tr   rngfreq_hzfloatfps
np.ndarrayc           	      C  s   t dt|| }|| | }tt|dd}||d |d|   | }|d|  }| }|dkr;|| }|tjS )u   Generate length-T noise band-limited around freq_hz.

    Method: white noise → moving average whose window matches the period.
    Result drifts smoothly at ~freq_hz, no high-frequency jitter.
       r   Ngư>)	maxr   standard_normalr   cumsuminsertstdastypefloat32)	r&   r'   r(   r*   windowrawcssmoothedsr!   r!   r"   _smoothed_noise'   s   r9   c                   C  s   t jg dt jdS )uS  5-frame triangular blink shape, peak 0.70 at center frame.

    Was 0.95 (ARKit-standard "fully closed eye"). Visual review on the
    Ready Player Me / Wolf3D avatar showed 0.95 looked like squeezing the
    eyes shut tightly — not a natural blink. The avatar's visible blink
    lives on the `eyesClosed` morph; at 0.95 that morph is near its tight-
    closure end of travel. Peak 0.70 gives a relaxed natural blink shape
    that reads correctly on this avatar.

    Values scaled proportionally from the original [0.30, 0.75, 0.95,
    0.75, 0.30] by 0.70/0.95 = 0.737, then rounded.
    ))\(?皙?gffffff?r;   r:   )dtype)r   arrayr3   r!   r!   r!   r"   _blink_kernel;   s   r>         @333333?targetmean_interval_swide_suppresssquint_suppressc              	     s<  |   jd  t|d }t t}|d || }ttd  td| }	t|td t  d |	}
d}d fdd}|
  k ry||
r[|d7 }|	|}t
t|d| d| }|
t|7 }
|
  k sS|dkr td| kr|t  D ]}|t|r S qS )zAdd Poisson-distributed blinks. Only suppressed when the preset is
    holding the eyes very wide (strong surprise) or hard-squinted (peak
    anger/laughter). Moderate squint (anger ~0.6) still allows blinks.r   z::blink   333333?g       @r   centerr   r   boolc                   s   t d|  t |  d }}t ||tf   ||tf   }t ||tf   ||tf   }|k rz|k rztD ]0\}}|  | }d|  kr[ k rwn qGt |tf ||tf< t |tf ||tf< qGdS dS )Nr   r   TF)	r-   min
IDX_WIDE_L
IDX_WIDE_RIDX_SQUINT_LIDX_SQUINT_R	enumerateIDX_BLINK_LIDX_BLINK_R)rG   lohiwidesquintikfir&   halfkerneloutrD   rC   r!   r"   
_try_blinka   s   "**zadd_blinks.<locals>._try_blinkr$   g       @N)rG   r   r   rH   )copyshaper#   r>   lenr-   r   integersrI   exponentialr)   r   clippermutationarange)rA   r   r*   rB   rC   rD   r'   klenmean_frames	first_maxtblink_countr\   gaprG   r!   rX   r"   
add_blinksK   s2   
(

rk   ~jt?Mb?rF   	horiz_ampvert_ampdrift_hzc                 C  s  |   }|jd }t|d }t||||d| }	t||||d| }
t|	dd}t|	 dd}t|
dd}t|
 dd}t|ddtf ||ddtf< t|ddtf ||ddtf< t|ddt	f ||ddt	f< t|ddt
f ||ddt
f< t|ddtf ||ddtf< t|ddtf ||ddtf< t|ddtf ||ddtf< t|ddtf ||ddtf< t|ddtjS )zAdd subtle continuous iris drift on the eyeLook channels.

    Both eyes stay synchronized (no cross-eyed). Amplitude is intentionally
    small (~0.04 of [0,1] range) so it reads as life, not as looking around.
    r   z::iris)r(   r*   Ng        g      ?)r]   r^   r#   r9   r   rb   maximumIDX_LOOK_IN_LIDX_LOOK_OUT_RIDX_LOOK_OUT_LIDX_LOOK_IN_RIDX_LOOK_UP_LIDX_LOOK_UP_RIDX_LOOK_DOWN_LIDX_LOOK_DOWN_Rr2   r3   )rA   r   r*   rn   ro   rp   r[   r&   r'   r    vh_posh_negv_posv_negr!   r!   r"   add_iris_tremor   s$   	
$$$$$$$$r   blink_interval_siris_horiz_ampiris_vert_ampc                 C  s&   t | |||d}t|||||d}|S )z6Convenience wrapper: blinks + iris tremor in one pass.)r*   rB   )r*   rn   ro   )rk   r   )rA   r   r*   r   r   r   r[   r!   r!   r"   apply_eye_motion   s
   r   )r   r   r   r   )r$   r%   )
r&   r   r'   r   r(   r)   r*   r   r   r+   )r   r+   )r%   r?   r@   r@   )rA   r+   r   r   r*   r   rB   r)   rC   r)   rD   r)   r   r+   )r%   rl   rm   rF   )rA   r+   r   r   r*   r   rn   r)   ro   r)   rp   r)   r   r+   )r%   r?   rl   rm   )rA   r+   r   r   r*   r   r   r)   r   r)   r   r)   r   r+   )__doc__
__future__r   r   numpyr   	constantsr   rO   rP   rx   ry   rr   ru   rt   rs   rv   rw   rL   rM   rJ   rK   r#   r9   r>   rk   r   r   r!   r!   r!   r"   <module>   sL    

6&