cutan.expression.axes¶
The facial expression axes: what a cutout face can be asked to do (an#98).
Ten axes ship in Wave 6 of epic #9 — eight numeric, one selection
(mouth_form, which picks a viseme@<preset> set and is not a number),
one scalar (intensity). Every numeric value is an **offset over the built
rest** of the node it drives; rest is neutral. The ranges and the eyelid
ladder below are the only numbers in the vocabulary (research
misc/docs/wave6_research.md §4, §6).
Deferred, named so nobody re-invents them: brow_squeeze, squint,
head_yaw / head_pitch (Wave 7), mouth_open (the viseme set already
opens monotonically X → A → B → C → D).
>>> AXES["brow_height_l"].clamp(3.0)
1.0
>>> lid_key(-0.9, available={"OPEN", "CLOSED"})
'CLOSED'
>>> lid_key(-0.5, available={"OPEN", "CLOSED"}) # no `half` art: stays open
'OPEN'
>>> lid_key(-0.5, available={"OPEN", "CLOSED", "HALF"})
'HALF'
Module Attributes
which |
|
The scalar on every offset (MPEG-4 "excitation"); the blend ramp is a curve on it. |
|
The eyelid ladder — one rule, stated once (research §6). |
|
Eyelid set keys the ladder can name, by openness. |
Functions
|
Clamp every numeric axis to its range; an unknown axis is an error. |
|
The eyelid key a lid state selects, degraded to the art the rig declares. |
Classes
|
One numeric axis: its range and its rest (neutral) value. |
- cutan.expression.axes.AXES: dict[str, Axis] = {'brow_angle_l': Axis(name='brow_angle_l', lo=-1.0, hi=1.0, rest=0.0), 'brow_angle_r': Axis(name='brow_angle_r', lo=-1.0, hi=1.0, rest=0.0), 'brow_height_l': Axis(name='brow_height_l', lo=-1.0, hi=1.0, rest=0.0), 'brow_height_r': Axis(name='brow_height_r', lo=-1.0, hi=1.0, rest=0.0), 'gaze_x': Axis(name='gaze_x', lo=-1.0, hi=1.0, rest=0.0), 'gaze_y': Axis(name='gaze_y', lo=-1.0, hi=1.0, rest=0.0), 'lid_open_l': Axis(name='lid_open_l', lo=-1.0, hi=0.5, rest=0.0), 'lid_open_r': Axis(name='lid_open_r', lo=-1.0, hi=0.5, rest=0.0)}¶
raises the brow, scaled by the rig’s eye height.
Brow angle, per side: + inner end up (worry), − inner end down (furrow); the binding’s per-side gain carries the screen sign. Lid openness, per side: − closes (
half, thenclosed), + widens (wide). Gaze: pupil travel inside the eye, clamped by the rig’s declared travel.- Type:
Brow height, per side
- class cutan.expression.axes.Axis(name, lo, hi, rest=0.0)[source]¶
Bases:
objectOne numeric axis: its range and its rest (neutral) value.
- cutan.expression.axes.INTENSITY_AXIS: str = 'intensity'¶
The scalar on every offset (MPEG-4 “excitation”); the blend ramp is a curve on it.
- cutan.expression.axes.LID_WIDE_ABOVE: float = 0.25¶
The eyelid ladder — one rule, stated once (research §6). A lid state
min(lid_expr, lid_blink)reads off these thresholds; a rig without the intermediate art degrades to the key it has.
- cutan.expression.axes.MOUTH_FORM_AXIS: str = 'mouth_form'¶
which
viseme@<form>set the mouth’s key indexes.- Type:
The selection axis
- cutan.expression.axes.clamp_axes(values)[source]¶
Clamp every numeric axis to its range; an unknown axis is an error.
>>> clamp_axes({"brow_height_l": 2.0, "lid_open_r": -3.0}) {'brow_height_l': 1.0, 'lid_open_r': -1.0} >>> clamp_axes({"eyebrow": 1.0}) Traceback (most recent call last): ... ValueError: unknown expression axis 'eyebrow' (known: brow_angle_l, ...)
- cutan.expression.axes.lid_key(value, *, available)[source]¶
The eyelid key a lid state selects, degraded to the art the rig declares.
wideabove +0.25,open,halfbelow −0.35,closedbelow −0.85; a rig withouthalfstays open until the lower threshold and one withoutwidestays open above the upper one — never a blend of two drawings.- Return type: