cutan.expression.registration

The face side of the cut-out genre, as declarations: expression and [emotion].

What the cut-out genre (cutan.genre) registers from here:

  • the ``expression`` action kind — ExpressionAction (hold a facial expression, an#98): zero-width in a sequence when it runs to the shot end, and its scene.md spelling;

  • the ``[emotion]`` dialogue sugar — maya [happy]: Hi! fills an.ir.schema.Dialogue.emotion, which the expression provider turns

    into an expression over the line, in memory only.

Plain declarations: importing this module registers nothing.

>>> EXPRESSION.name, EMOTION.opener, EMOTION.parse(" Happy ")
('expression', '[', {'emotion': 'happy'})

Module Attributes

DFLT_EXPRESSION_BLEND_S

Default ramp in/out of an expression, seconds (0 = cut).

EMOTION_NAME_RE

a preset name (happy, wry-smile).

EMOTION_INTENSITY_FIELD

The Dialogue field an [emotion level] sets beside the emotion (an#253).

Functions

expression(target[, preset, axes, ...])

Hold a facial expression on an entity (an#98).

expression_duration(action, extent)

An expression's span: its duration, else zero (it runs to the shot end).

format_emotion(line)

The […] content for line (angry 0.4 with an intensity), or None when it carries no emotion.

parse_emotion(content)

[happy]'s content to the line's emotion, lower-cased, and [angry 0.4]'s to the emotion and its intensity (0..1, an#253); refuse anything else.

read_expression_md(item, *, index)

{kind: expression, target, [preset], [axes], [intensity], [duration], [blend]}.

write_expression_md(leaf)

The scene.md entry for leaf (read_expression_md's inverse).

Classes

ExpressionAction(*[, name, kind, preset, ...])

Hold a facial expression on an entity (an#98, epic #9 Wave 6).

cutan.expression.registration.DFLT_EXPRESSION_BLEND_S: float = 0.15

Default ramp in/out of an expression, seconds (0 = cut). The dialogue [emotion] sugar uses its own in an.expression.provider.

cutan.expression.registration.EMOTION_INTENSITY_FIELD: str = 'emotion_intensity'

The Dialogue field an [emotion level] sets beside the emotion (an#253).

cutan.expression.registration.EMOTION_NAME_RE = re.compile('[\\w-]+')

a preset name (happy, wry-smile).

Type:

What an emotion name may be

class cutan.expression.registration.ExpressionAction(*, name=None, kind='expression', target, preset=None, axes=<factory>, intensity=1.0, duration=None, blend=0.15, **extra_data)[source]

Bases: ExtensionAction

Hold a facial expression on an entity (an#98, epic #9 Wave 6).

preset names one of an.expression.presets.PRESETS; axes are per-axis overrides layered on it (axis units, see an.expression.axes); None + no axes is a cheap “return to rest”. duration=None runs to the shot end (the looping-play rule) and is zero-width in a sequence, like a looping play. blend ramps the intensity in and out; two overlapping expressions cross-fade because the face solver sums offsets. The dialogue speaker [emotion]: … bracket is sugar for one of these over the line, desugared in memory only.

A leaf action, flattened like play: the compiler resolves it in the face solver (one channel per (node, property)), never per action.

The ramp is a min over the two ends, so a span shorter than 2·blend never reaches full intensity (a 0.2 s expression at the default 0.15 s blend peaks at 0.67) and a duration=0 expression shows only where a frame lands on it with blend=0 — cut the blend for a flash.

model_config: ClassVar[ConfigDict] = {'extra': 'allow', 'populate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

cutan.expression.registration.expression(target, preset=None, *, axes=None, intensity=1.0, duration=None, blend=0.15)[source]

Hold a facial expression on an entity (an#98).

duration=None runs to the shot end and counts as zero in a sequence, as a looping play does:

>>> [f.start for f in flatten(sequence(expression("a", "happy"), delay(1.0), expression("a", "sad")))]
[0.0, 1.0]
>>> flatten(expression("a", "angry", duration=2.0))[0].end
2.0
Return type:

ExpressionAction

cutan.expression.registration.expression_duration(action, extent)[source]

An expression’s span: its duration, else zero (it runs to the shot end).

>>> expression_duration(ExpressionAction(target="a", duration=2.0), None)
2.0
>>> expression_duration(ExpressionAction(target="a"), None)
0.0
Return type:

float

cutan.expression.registration.format_emotion(line)[source]

The […] content for line (angry 0.4 with an intensity), or None when it carries no emotion.

Return type:

str | None

cutan.expression.registration.parse_emotion(content)[source]

[happy]’s content to the line’s emotion, lower-cased, and [angry 0.4]’s to the emotion and its intensity (0..1, an#253); refuse anything else.

>>> parse_emotion("Happy"), parse_emotion(" angry 0.4 ")
({'emotion': 'happy'}, {'emotion': 'angry', 'emotion_intensity': 0.4})
Return type:

dict[str, Any]

cutan.expression.registration.read_expression_md(item, *, index)[source]

{kind: expression, target, [preset], [axes], [intensity], [duration], [blend]}.

Landed with its writer and round trip in one commit (an#98): the writer skips unknown leaves, so a parser-only entry would vanish from scene.md on the next sync and then from the JSON on the next md edit.

Return type:

ExpressionAction

cutan.expression.registration.write_expression_md(leaf)[source]

The scene.md entry for leaf (read_expression_md’s inverse).

Return type:

dict[str, Any]