cutan.expression¶
Facial expression for the cutout face (an#98, epic #9 Wave 6).
The vocabulary (axes), our presets
(presets), how they reach a character’s slots and which
mouth set a line uses (binding), the provider seam that
turns authored leaves and dialogue sugar into per-frame curves
(provider), and the 52-coefficient import/export
mapping (blendshapes). Renderer-free throughout: the
cutout compiler’s face solver consumes these; an validate and
an character validate share the same resolution.
Functions
|
The descriptor's declared |
|
Clamp every numeric axis to its range; an unknown axis is an error. |
|
|
|
The binding the default rig implies, from the slots it actually has. |
|
Every reason an expression cannot resolve on |
|
Every expression contributor on |
|
Fold unipolar coefficients onto the axes (summed, then clamped). |
The preset names, in declaration order. |
|
|
The eyelid key a lid state selects, degraded to the art the rig declares. |
|
The |
|
The numeric axis offsets an expression asks for: the preset's, with |
|
Which mouth set a line under |
|
The swap-set name for a mouth form ( |
Classes
|
One numeric axis: its range and its rest (neutral) value. |
|
One axis sampled at the frame times |
|
A numeric axis driving one transform property of one slot's node. |
Sum of the shot's expression spans on the entity, ramped, per frame. |
|
|
The seam: whatever produces per-axis curves for one entity of one shot. |
|
One expression contributor on one entity, in absolute shot time. |
|
A named expression: axis offsets, the mouth form it prefers, its anchor. |
|
A lid axis driving one slot's swap set through the ladder. |
Exceptions
|
An expression that cannot resolve on a character; |
- class cutan.expression.Axis(name, lo, hi, rest=0.0)[source]¶
Bases:
objectOne numeric axis: its range and its rest (neutral) value.
- class cutan.expression.AxisCurve(axis, samples)[source]¶
Bases:
objectOne axis sampled at the frame times
0, 1/fps, …, n/fps(offline, deterministic).
- class cutan.expression.ChannelBinding(axis, slot, property, gain, rig_scaled=False)[source]¶
Bases:
objectA numeric axis driving one transform property of one slot’s node.
- class cutan.expression.DefaultExpressionProvider[source]¶
Bases:
objectSum of the shot’s expression spans on the entity, ramped, per frame.
- mouth_preset_at(shot, entity_id, t)[source]¶
The preset whose mouth form is in force at
t: the heaviest span attthat prefers a form, orNone(the neutral set).Whole-line by construction when called at a line’s start — the solver asks once per line, never per frame, so at most one mouth swap property is live per instant.
- class cutan.expression.ExpressionProvider(*args, **kwargs)[source]¶
Bases:
ProtocolThe seam: whatever produces per-axis curves for one entity of one shot.
- exception cutan.expression.ExpressionResolutionError(who, problems)[source]¶
Bases:
ValueErrorAn expression that cannot resolve on a character;
problemssays why.
- class cutan.expression.ExpressionSpan(start, end, preset, axes=dict(), intensity=1.0, blend=0.0, source='action')[source]¶
Bases:
objectOne expression contributor on one entity, in absolute shot time.
- class cutan.expression.Preset(name, axes=dict(), mouth_form=None, anchor='')[source]¶
Bases:
objectA named expression: axis offsets, the mouth form it prefers, its anchor.
- class cutan.expression.SetBinding(axis, slot, set_family='eyelid')[source]¶
Bases:
objectA lid axis driving one slot’s swap set through the ladder.
- cutan.expression.binding_for(desc)[source]¶
The descriptor’s declared
expression_binding(additive field), else the default.A declared binding is a list of dicts in the two dataclasses’ shapes (
{"axis", "slot", "property", "gain"[, "rig_scaled"]}or{"axis", "slot", "set_family"}). An unknown axis in it is an error.- Return type:
- cutan.expression.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.declared_mouth_variants(desc)[source]¶
{form: set name}for everyviseme@<form>set the descriptor declares.>>> declared_mouth_variants(CharacterDescriptor(name="m")) {}
- cutan.expression.default_binding(desc)[source]¶
The binding the default rig implies, from the slots it actually has.
The brow angle’s screen sign: PixiJS rotation is clockwise-positive with y down, so on the LEFT brow (screen-left) a clockwise turn drops the inner end while on the RIGHT brow it lifts it — the axis says “+ = inner end up”, hence
-travelon the left and+travelon the right.- Return type:
- cutan.expression.expression_problems(desc, *, preset, axes=(), who)[source]¶
Every reason an expression cannot resolve on
desc— empty means it can.Shared by
an validate(each becomes an error Finding) and the compiler (which raisesExpressionResolutionErrorwith the same list).>>> expression_problems(CharacterDescriptor(name="m"), preset="joyful", who="m") ["unknown expression preset 'joyful' (known: neutral, happy, sad, angry, surprised, afraid, disgusted, thinking, skeptical, amused)"] >>> expression_problems(CharacterDescriptor(name="m", face_overlay=False), preset="happy", who="m")[0].startswith("'m' has its face baked") True
- cutan.expression.expression_spans(shot, entity_id)[source]¶
Every expression contributor on
entity_id: authored leaves, then dialogue sugar.duration=Noneruns to the shot end (the looping-play rule); a span never extends past the shot.- Return type:
- cutan.expression.from_blendshapes(coefficients)[source]¶
Fold unipolar coefficients onto the axes (summed, then clamped).
Unknown names raise — a misspelt coefficient must not vanish quietly.
- cutan.expression.known_presets()[source]¶
The preset names, in declaration order.
>>> known_presets()[:3] ('neutral', 'happy', 'sad')
- cutan.expression.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:
- cutan.expression.mouth_form_of(preset)[source]¶
The
viseme@<form>a preset prefers, orNonefor the neutral set.>>> mouth_form_of("amused"), mouth_form_of("thinking"), mouth_form_of(None) ('happy', None, None)
- cutan.expression.preset_axes(preset, *, axes=None, intensity=1.0)[source]¶
The numeric axis offsets an expression asks for: the preset’s, with
axeslayered over them, scaled byintensityand clamped. Only non-zero offsets are returned, so a neutral expression is{}.An unknown preset or axis is a
ValueError— validate reports it as an error, the compiler refuses it.
- cutan.expression.resolve_mouth_set(desc, preset, *, keys_used, who=None)[source]¶
Which mouth set a line under
presetuses — the one chain, shared.viseme@<form>if the preset prefers a form the descriptor declares and that set coverskeys_used; elsevisemewith a warning naming what was missing; elseExpressionResolutionError. A descriptor with novisemeset and no covering variant cannot speak at all — that is the error, not a fallback.- Return type:
- cutan.expression.variant_set_name(form)[source]¶
The swap-set name for a mouth form (
@is a legal set-name character).>>> variant_set_name("happy") 'viseme@happy'- Return type:
Modules
The facial expression axes: what a cutout face can be asked to do (an#98). |
|
How the axes reach a character: the binding and the mouth-set resolver (an#98). |
|
The 52-coefficient blendshape vocabulary, as an import/export mapping (an#98). |
|
Expression presets: our art direction on the axes (an#98). |
|
The expression provider: authored leaves + dialogue sugar → per-axis curves (an#98). |
|
The face side of the cut-out genre, as declarations: |