cutan.characters.methods¶
The cut-out genre’s methods and aspects, and their compile-time resolution (ADR 0002).
The first two aspects of ADR 0002’s first slice, as registry data:
aspect |
method (spelled) |
requires |
chain |
|---|---|---|---|
locomotion |
|
|
1st |
locomotion |
|
|
by request |
locomotion |
|
|
by request |
locomotion |
|
|
by request |
locomotion |
|
nothing |
by request |
locomotion |
|
nothing |
by request |
locomotion |
|
nothing |
by request |
locomotion |
|
nothing |
by request |
locomotion |
|
nothing |
last link |
speech |
|
|
1st |
speech |
|
nothing |
last link |
expression |
|
|
1st |
expression |
|
nothing |
last link |
Locomotion: the gaits of an#224 (classified in
misc/docs/locomotion_gaits.md). A walk’s gait arg is the author’s
request, the descriptor’s gait a declared override (reported as such);
with neither, the chain picks legs when the character affords a leg pair
and glide when it does not (the rock, the last link before an#224, read as a
metronome on robe figures and is now a gait an author asks for). A requested
gait the rig cannot honour (hem on a legless blob, profile on a
character with no side view) is a recorded substitution: a warning, fatal
under --strict-assets, and why_not names what would enable it.
Speech gains a requirement-free last link. A character whose face is baked
into its art (face_overlay: false) used to speak with a frozen mouth; it now
pulses its head on each syllable (cutan.motion.speech_pulse(), parametrised:
strength, part, attack, release; strength: 0 is a mime).
Expression names what reads when the brows cannot (an#252). A hat the
factory could not seat above the brows (recorded in occluded), or brow
slots without art, leave face.brows unafforded: the face then acts with
the lids, the gaze and the mouth form (expr.without_brows), recorded. Both
methods compile to the same face channels — the solver drives whatever the
rig binds — so the aspect is not consulted by the compiler; an validate
reports the fall (check_brow_acting()) and an character
capabilities shows it with the remedy.
The asset profile the compiler resolves against is the character analyser’s
(an.capabilities.affordances), fed what the compiler actually has: the
descriptor and the art its store holds, or — for a rig drawn from parts or
the placeholder — the parts the builder built. Characters only: other entity
kinds have no analyser yet, and keep the preset’s own rig lookup.
Importing this module registers nothing: cutan.genre.CUTOUT lists
CUTOUT_METHODS and CUTOUT_ASPECTS.
Module Attributes
The aspect names (persisted in substitution records). |
|
The requirement a |
|
"The view in force is not known" — a caller that cannot read the timeline (the extent resolver, which never needs it: see |
|
The genre's methods, as vocabulary entries (kind |
|
each chain ends in a method that requires nothing. |
Functions
|
The expression aspect's fall to |
|
|
|
The cut-out genre's semantic check: an expression that moves the brows of a character whose brows cannot act is reported (a warning) — the expression aspect's recorded fall to |
The cut-out genre's semantic check: each character's declared |
|
|
The cut-out genre's semantic check: a |
|
Why a walk on |
|
|
|
The record for a legged gait asked of an entity that does not resolve on the capability registry (a prop: no analyser) and whose built |
|
The sentence |
|
The gait parameters in |
|
Everything |
The walk's own arguments — where to, how many steps, the limbs, the view — read off its signature: everything it accepts that is not a gait parameter ( |
|
|
The character's profile, from what the compiler has: the analyser, fed honestly. |
|
Why a character's declared |
|
A walk's args with |
|
|
|
Resolve the speech aspect ONCE per speaking character, and say what it adds. |
|
A |
|
Syllable onsets of a dialogue line, in seconds from its start. |
|
What the compiler adds to a |
Classes
|
What the speech aspect decided for a shot: the actions it adds, and the speakers whose lip-sync it switched off (the viseme pass skips them). |
- cutan.characters.methods.CUTOUT_ASPECTS: tuple[Aspect, ...] = (Aspect(name='locomotion', chain=('loco.legged_cycle', 'loco.glide'), applies_to=frozenset(), description='how a character travels when it walks.', declared_by='gait', records_fallback=False), Aspect(name='speech', chain=('speech.mouth_chart', 'speech.pose_only'), applies_to=frozenset(), description='how a character shows that it is speaking.', declared_by='speech', records_fallback=True), Aspect(name='expression', chain=('expr.full_face', 'expr.without_brows'), applies_to=frozenset({'character'}), description="how a character's face shows an emotion.", declared_by='', records_fallback=True))¶
each chain ends in a method that requires nothing.
- Type:
The genre’s aspects
- cutan.characters.methods.CUTOUT_METHODS: tuple[Method, ...] = (Method(id='loco.legged_cycle', kind='method', version='2', name='legs', title='legged walk cycle', description='a legged walk cycle: in profile the legs swing about the hip in opposition, facing the camera the stepping leg lifts; the arms swing against the legs', usage='', params={'type': 'object', 'properties': {'step_s': {'type': 'number', 'default': 0.4}, 'step_length': {'type': 'number', 'default': 80.0, 'description': 'scene px at drawn scale 1; scales with the figure'}, 'stride': {'type': 'number', 'default': 0.35}, 'lift': {'type': 'number', 'default': 10.0}, 'arm_swing': {'type': 'number', 'default': 0.3}, 'bob': {'type': 'number', 'default': 6.0, 'description': 'scene px at drawn scale 1; scales with the figure'}}}, examples=({'kind': 'play', 'target': 'ned', 'animation': 'walk', 'args': {'gait': 'legs'}},), requires=(Requirement(capability='limbs.legs', key=None, at_least=None, any_of=()),), levels=frozenset({'a', 'b-name'}), aspects=(), aspect='locomotion', remedies={'limbs.legs': 'split the legs into two slots named leg_l/leg_r, each with its art, pivoted at the hip (an-art-package skill; `an character new` builds them)'}), Method(id='loco.profile_cycle', kind='method', version='2', name='profile', title='profile walk cycle', description="the four poses of a walk seen in profile (contact, down, passing, up): with a side or three-quarter view showing the legs swing about the hip in opposition and the body sinks after each contact and rises before the next; asked while another view shows, it walks as legs (Reiniger's silhouettes, any figure drawn side-on)", usage='', params={'type': 'object', 'properties': {'step_s': {'type': 'number', 'default': 0.4}, 'step_length': {'type': 'number', 'default': 80.0, 'description': 'scene px at drawn scale 1; scales with the figure'}, 'stride': {'type': 'number', 'default': 0.45}, 'bob': {'type': 'number', 'default': 6.0, 'description': 'scene px at drawn scale 1; scales with the figure'}, 'arm_swing': {'type': 'number', 'default': 0.35}}}, examples=({'kind': 'play', 'target': 'ned', 'animation': 'walk', 'args': {'gait': 'profile'}},), requires=(Requirement(capability='limbs.legs', key=None, at_least=None, any_of=()), Requirement(capability='swap.view', key='side', at_least=None, any_of=())), levels=frozenset({'a', 'b-name'}), aspects=(), aspect='locomotion', remedies={'limbs.legs': 'split the legs into two slots named leg_l/leg_r, each with its art, pivoted at the hip (an-art-package skill; `an character new` builds them)', 'swap.view:side': 'give the character a side view: `an character add-views <name>` (a factory character), or carve its art in profile and declare `rest_view: side` in character.json'}), Method(id='loco.shuffle', kind='method', version='2', name='shuffle', title='shuffle', description='the feet barely leave the ground: short, quick steps with little bob and arms close to the body (the old, the tired, the cautious)', usage='', params={'type': 'object', 'properties': {'step_s': {'type': 'number', 'default': 0.3}, 'step_length': {'type': 'number', 'default': 40.0, 'description': 'scene px at drawn scale 1; scales with the figure'}, 'stride': {'type': 'number', 'default': 0.12}, 'lift': {'type': 'number', 'default': 3.0}, 'arm_swing': {'type': 'number', 'default': 0.1}, 'bob': {'type': 'number', 'default': 1.0, 'description': 'scene px at drawn scale 1; scales with the figure'}}}, examples=({'kind': 'play', 'target': 'ned', 'animation': 'walk', 'args': {'gait': 'shuffle'}},), requires=(Requirement(capability='limbs.legs', key=None, at_least=None, any_of=()),), levels=frozenset({'a', 'b-name'}), aspects=(), aspect='locomotion', remedies={'limbs.legs': 'split the legs into two slots named leg_l/leg_r, each with its art, pivoted at the hip (an-art-package skill; `an character new` builds them)'}), Method(id='loco.hem_sway', kind='method', version='2', name='hem', title='hem sway', description="a robe figure's walk: the leg slots are the two halves of the hem, which tilt about the hip as mirror images (the hem opens and closes) while the body sways and bobs facing the camera; in profile they swing like legs", usage='', params={'type': 'object', 'properties': {'step_s': {'type': 'number', 'default': 0.4}, 'step_length': {'type': 'number', 'default': 80.0, 'description': 'scene px at drawn scale 1; scales with the figure'}, 'hem_tilt': {'type': 'number', 'default': 0.24}, 'rock': {'type': 'number', 'default': 0.06}, 'bob': {'type': 'number', 'default': 6.0, 'description': 'scene px at drawn scale 1; scales with the figure'}, 'stride': {'type': 'number', 'default': 0.35}, 'arm_swing': {'type': 'number', 'default': 0.3}}}, examples=({'kind': 'play', 'target': 'ned', 'animation': 'walk', 'args': {'gait': 'hem'}},), requires=(Requirement(capability='limbs.legs', key=None, at_least=None, any_of=()),), levels=frozenset({'a', 'b-name'}), aspects=(), aspect='locomotion', remedies={'limbs.legs': "carve the robe's hem into two halves on slots leg_l/leg_r, pivoted at the hip, and declare `gait: hem` in character.json"}), Method(id='loco.waddle', kind='method', version='2', name='waddle', title='waddle', description='the body rocks from foot to foot and bobs on each step; legs, if any, lift in turn (a penguin, a toddler, a squat figure)', usage='', params={'type': 'object', 'properties': {'step_s': {'type': 'number', 'default': 0.4}, 'step_length': {'type': 'number', 'default': 80.0, 'description': 'scene px at drawn scale 1; scales with the figure'}, 'rock': {'type': 'number', 'default': 0.12}, 'lift': {'type': 'number', 'default': 5.0}, 'bob': {'type': 'number', 'default': 4.0, 'description': 'scene px at drawn scale 1; scales with the figure'}, 'arm_swing': {'type': 'number', 'default': 0.15}}}, examples=({'kind': 'play', 'target': 'ned', 'animation': 'walk', 'args': {'gait': 'waddle'}},), requires=(), levels=frozenset({'a', 'b-name'}), aspects=(), aspect='locomotion', remedies={}), Method(id='loco.hop', kind='method', version='2', name='hop', title='hop', description='the whole figure jumps on every step while it travels (a bird, a kangaroo, a gleeful character, anything drawable)', usage='', params={'type': 'object', 'properties': {'step_s': {'type': 'number', 'default': 0.4}, 'step_length': {'type': 'number', 'default': 80.0, 'description': 'scene px at drawn scale 1; scales with the figure'}, 'hop_height': {'type': 'number', 'default': 18.0, 'description': 'scene px at drawn scale 1; scales with the figure'}, 'arm_swing': {'type': 'number', 'default': 0.0}}}, examples=({'kind': 'play', 'target': 'ned', 'animation': 'walk', 'args': {'gait': 'hop'}},), requires=(), levels=frozenset({'a', 'b-name'}), aspects=(), aspect='locomotion', remedies={}), Method(id='loco.bounce', kind='method', version='2', name='bounce', title='bounce', description='the body bobs on every step while it slides; legs, if any, only flick (the South Park walk)', usage='', params={'type': 'object', 'properties': {'step_s': {'type': 'number', 'default': 0.4}, 'step_length': {'type': 'number', 'default': 80.0, 'description': 'scene px at drawn scale 1; scales with the figure'}, 'bob': {'type': 'number', 'default': 8.0, 'description': 'scene px at drawn scale 1; scales with the figure'}, 'lift': {'type': 'number', 'default': 4.0}, 'stride': {'type': 'number', 'default': 0.1}, 'arm_swing': {'type': 'number', 'default': 0.15}}}, examples=({'kind': 'play', 'target': 'ned', 'animation': 'walk', 'args': {'gait': 'bounce'}},), requires=(), levels=frozenset({'a', 'b-name'}), aspects=(), aspect='locomotion', remedies={}), Method(id='loco.rock', kind='method', version='2', name='rock', title='rock and bob', description='no leg moves: the body rocks side to side and bobs once per step while it travels (a blob, a sack, anything drawable)', usage='', params={'type': 'object', 'properties': {'step_s': {'type': 'number', 'default': 0.4}, 'step_length': {'type': 'number', 'default': 80.0, 'description': 'scene px at drawn scale 1; scales with the figure'}, 'rock': {'type': 'number', 'default': 0.06}, 'bob': {'type': 'number', 'default': 6.0, 'description': 'scene px at drawn scale 1; scales with the figure'}, 'arm_swing': {'type': 'number', 'default': 0.3}}}, examples=({'kind': 'play', 'target': 'ned', 'animation': 'walk', 'args': {'gait': 'rock'}},), requires=(), levels=frozenset({'a', 'b-name'}), aspects=(), aspect='locomotion', remedies={}), Method(id='loco.glide', kind='method', version='2', name='glide', title='glide', description='the figure slides, leaning into the move with a gentle bob; no limb moves (a robe figure, a ghost, a sack — the default for any figure without legs)', usage='', params={'type': 'object', 'properties': {'step_s': {'type': 'number', 'default': 0.4}, 'step_length': {'type': 'number', 'default': 80.0, 'description': 'scene px at drawn scale 1; scales with the figure'}, 'bob': {'type': 'number', 'default': 1.5, 'description': 'scene px at drawn scale 1; scales with the figure'}, 'lean': {'type': 'number', 'default': 0.04}, 'arm_swing': {'type': 'number', 'default': 0.0}}}, examples=({'kind': 'play', 'target': 'ned', 'animation': 'walk', 'args': {'gait': 'glide'}},), requires=(), levels=frozenset({'a', 'b-name'}), aspects=(), aspect='locomotion', remedies={}), Method(id='speech.mouth_chart', kind='method', version='1', name='mouth_chart', title='mouth chart lip-sync', description="lip-sync on the character's mouth chart: the line's visemes swap the mouth drawings (the nine Rhubarb shapes, or the character's own set)", usage='', params={}, examples=(), requires=(Requirement(capability='face.mouth', key=None, at_least=None, any_of=()),), levels=frozenset({'a', 'b-name'}), aspects=(), aspect='speech', remedies={'face.mouth': "give the character an overlay mouth: a `mouth` slot with the viseme set's drawings (`an character mouths <dir>`) and face_overlay: true"}), Method(id='speech.pose_only', kind='method', version='1', name='pulse', title='speech pulse', description='no lip-sync: the head (or the body) pulses on each syllable, so a baked face or a mime still reads as speaking', usage='', params={'type': 'object', 'properties': {'strength': {'type': 'number', 'default': 0.06}, 'part': {'type': 'string', 'default': 'head'}, 'attack': {'type': 'number', 'default': 0.06}, 'release': {'type': 'number', 'default': 0.1}}}, examples=('a character with face_overlay: false speaks',), requires=(), levels=frozenset({'a', 'b-name'}), aspects=(), aspect='speech', remedies={}), Method(id='expr.full_face', kind='method', version='1', name='full_face', title='full-face expression', description="the expression acts with the whole face: the brows rise, knit and tilt, the lids open and close, the pupils move and the mouth takes the preset's form", usage='', params={}, examples=({'kind': 'expression', 'target': 'ned', 'preset': 'surprised'},), requires=(Requirement(capability='face.brows', key=None, at_least=None, any_of=()),), levels=frozenset({'a', 'b-name'}), aspects=(), aspect='expression', remedies={'face.brows': "keep the brows clear: `an character new` seats a hat above them at most head scales — at this one it could not, so use a larger --head-scale, another --hat or --hat none; for drawn art, redraw what covers the brows and remove the descriptor's `occluded` entry, or give the face brow slots (left_brow/right_brow) with art"}), Method(id='expr.without_brows', kind='method', version='1', name='without_brows', title='expression without brows', description='the brows cannot be seen acting (covered, or not drawn): the lids, the gaze and the mouth form carry the expression', usage='', params={}, examples=('a character whose hat covers its brows takes [surprised]',), requires=(), levels=frozenset({'a', 'b-name'}), aspects=(), aspect='expression', remedies={}))¶
The genre’s methods, as vocabulary entries (kind
method).
- cutan.characters.methods.LOCOMOTION: str = 'locomotion'¶
The aspect names (persisted in substitution records).
- cutan.characters.methods.SIDE_VIEW_IN_FORCE: str = 'swap.view:side in force'¶
The requirement a
profilewalk has beyond the registry’sswap.view:side: the side view must be the one SHOWING when the walk starts, or the legs swing in the front view and scissor (cutan#17).
- class cutan.characters.methods.SpeechPlan(actions=(), no_lip_sync=frozenset({}))[source]¶
Bases:
objectWhat the speech aspect decided for a shot: the actions it adds, and the speakers whose lip-sync it switched off (the viseme pass skips them).
- cutan.characters.methods.UNKNOWN_VIEW: Any = <object object>¶
“The view in force is not known” — a caller that cannot read the timeline (the extent resolver, which never needs it: see
walk_preset_context()); the compiler andan validatealways can. A private object, so no view an author could type (view: "?") stands for it.
- cutan.characters.methods.brow_loss(desc, profile, *, entity)[source]¶
The expression aspect’s fall to
expr.without_browsfor one character, as aResolutioncarrying themissingsubstitution, orNonewhen its brows can act.THE rule for who records it (an#283), shared by the compiler (the record
--strict-assetsrefuses),an validateandbrow_acting_problem(): only a character that HAS brows to lose — an overlay face whose binding moves brow slots — and cannot act with them (covered, or their art missing). A baked face (face_overlay: false, a DiceBear head), a descriptor with no brow slots, or a procedural rig (no descriptor) has nothing to lose and records nothing.profileis the analyser’s answer for what compiled (compile_profile(), with the store’s art probe).
- cutan.characters.methods.brow_moving_entities(shot)[source]¶
{entity: [IR paths]}: the shot’s expressions (anexpressionleaf, or a dialogue line’s[emotion]) that move a character’s brows, where each is written (actions/<k>,dialogue/<j>/emotion).
- cutan.characters.methods.check_brow_acting(ctx)[source]¶
The cut-out genre’s semantic check: an expression that moves the brows of a character whose brows cannot act is reported (a warning) — the expression aspect’s recorded fall to
expr.without_brows(an#252), by the rule the compiler records it with (brow_loss(), an#283), the store’s art probe included.- Return type:
- cutan.characters.methods.check_declared_speech(ctx)[source]¶
The cut-out genre’s semantic check: each character’s declared
speechresolves.- Return type:
- cutan.characters.methods.check_walk_gaits(ctx)[source]¶
The cut-out genre’s semantic check: a
walkwhose requested gait the entity cannot honour is reported (a warning, before any render), with the gait it will walk instead and what would make the asked one apply (an#224, ADR 0002 decision 6): a missing capability, the side view not in force for aprofile(cutan#17), a legged gait asked of a prop with no legs (cutan#22) — and a gait parameter the walk’s gait never reads (cutan#21). The view in force is read off the timeline the way the compiler reads it (cutan.characters.checks._turns_of()), a prop’s limbs off the built stage (cutan.characters.checks._stage_poses_of()).- Return type:
- cutan.characters.methods.compile_profile(descriptor, *, built_parts=(), art_exists=None)[source]¶
The character’s profile, from what the compiler has: the analyser, fed honestly.
descriptoris the migratedCharacterDescriptor(orNonefor a rig drawn fromparts/ the placeholder);art_exists(rel_path)the store’s probe (None: the store cannot say, so every declared drawing counts — the rig builder’s own rule);built_partsthe part names the builder built under the entity (for a non-descriptor rig, they ARE its parts document).
- cutan.characters.methods.gait_problem(doc, *, entity, args, art_exists=None, view=<object object>, policy=None)[source]¶
Why a walk on
entitywill not use the gait it asks for (itsgaitarg, else the descriptor’s), with what would enable it — orNone.docis the character’s stored document;art_existsthe store’s probe;viewthe view in force at the play’s start (cutan#17);policythe scene’s, as the compiler resolves under it. The sentence is the one the compiler records (asset_resolution) when it substitutes the method, plus each missing capability’s remedy.
- cutan.characters.methods.locomotion_args(entity, args, *, descriptor, profile, policy=None, view=<object object>, on_skip=None)[source]¶
(args with its gait resolved, resolution)of a walk onentity: the locomotion method the registry resolves (an#248) — the author’sgait(a spelling, a method id or a pinned choice, spelled out first, its args joining the walk’s), else the descriptor’s, else the chain.ONE function for the compiler’s expansion and for the walk’s EXTENT (what a
sequencewaits for,walk_preset_context()), so the two cannot disagree about which gait runs (cutan#12).policyis the scene’s (cutan#9: the shot’s over the style’s, which the compiler’s policy pass leaves in its products, an#348); it orders the methods when nothing is requested (its entries that do not apply go toon_skip), and the chosen entry’s OWN args join the walk’s under them (never the method’s defaults, which the gait scales to the figure).>>> args, r = locomotion_args("blob", {"gait": "hem", "distance": 80}, descriptor=None, profile={}) >>> args["gait"], r.substitution.reason ('glide', 'missing')
- cutan.characters.methods.normalise_gait_args(args)[source]¶
A walk’s args with
gaitas the walk spells it (one ofcutan.motion.GAITS).gaitmay name a locomotion method by id (loco.rock) or be a choice{method, args, version}— the level-(a) form, and how a scene pins a method’s version (ADR 0003 decision 2). The choice’sargsjoin the walk’s (an explicit arg wins); a pin that no longer holds, or a method of another aspect, raisesVocabularyError.>>> normalise_gait_args({"gait": {"method": "loco.legged_cycle", "args": {"stride": 0.5}, "version": "2"}}) {'stride': 0.5, 'gait': 'legs'} >>> normalise_gait_args({"gait": "hem", "distance": 80}) {'gait': 'hem', 'distance': 80}
- cutan.characters.methods.off_registry_substitution(entity, args, parts=None)[source]¶
The record for a legged gait asked of an entity that does not resolve on the capability registry (a prop: no analyser) and whose built
partshold no leg pair (cutan.motion.WALK_LEG_NAMES): it walks the legless default, and says so (cutan#22).Noneotherwise — a prop rigged with legs walks on them.>>> off_registry_substitution("lamp", {"gait": "hem"}, ["base", "shade"]).sentence() "lamp: locomotion 'loco.hem_sway' does not apply (missing limbs.legs); used 'loco.glide'" >>> off_registry_substitution("lamp", {"gait": "hop"}, []) is None True >>> off_registry_substitution("bot", {"gait": "legs"}, ["leg_l", "leg_r"]) is None True
- cutan.characters.methods.resolve_walk_gait(entity, *, args, descriptor, profile, policy=None, view=<object object>, on_skip=None)[source]¶
(gait, resolution)of a walk onentity: the locomotion method’s spelling.policy(the shot’s over the style’s) orders the methods when nothing is requested; its entries that do not apply are reported toon_skip.The request is the walk’s
gaitarg, else the descriptor’s declaredgait(an override of the derivation, and reported as one). An explicitlegsarg names the limbs itself: a non-empty pair affords legs whatever the derivation says,()affords none.viewis the view the TIMELINE has in force at the play’s start (None: the rig’s default drawing; never the walk’s ownviewarg, which poses the legs without swapping the art): aprofilewhile a view its legs do not swing in is showing would scissor them in front, so the side view is taken off the profile for this resolution — the registry then substitutes as it would for a missing capability (the chain, or a policy’s next choice), and the record namesSIDE_VIEW_IN_FORCEwith its remedy (cutan#17).UNKNOWN_VIEWskips that (a caller with no timeline).>>> gait, r = resolve_walk_gait("blob", args={"gait": "hem"}, descriptor=None, profile={}) >>> gait, r.substitution.reason, r.substitution.missing ('glide', 'missing', ('limbs.legs',)) >>> legged = {"limbs.legs": {"slots": ["leg_l", "leg_r"]}, ... "swap.view": {"keys": ["front", "side"], "rest": "front"}} >>> gait, r = resolve_walk_gait("ned", args={"gait": "profile"}, descriptor=None, profile=legged, view=None) >>> gait, r.substitution.missing ('legs', ('swap.view:side in force',)) >>> resolve_walk_gait("ned", args={"gait": "profile"}, descriptor=None, profile=legged, view="side")[0] 'profile'
- cutan.characters.methods.speech_plan(shot, *, is_character, profile_of, descriptor_of=<function <lambda>>, has_part, record=None, policy=None, record_skip=None)[source]¶
Resolve the speech aspect ONCE per speaking character, and say what it adds.
The request is the character’s declared
speech(a method spelling or a{method, args, version}choice);policyis the shot/style policy. The one resolution decides both halves: a speaker resolved to anything butspeech.mouth_chartgets no viseme channel (SpeechPlan.no_lip_sync), and one resolved tospeech.pose_onlygets aspeech_pulseplay at each syllable onset of each timed line — one play per syllable, each built at the head’s pose at that instant, so it rides an authored head-scale tween rather than overwriting it; a syllable that starts while the speaker’s previous pulse is still running is skipped, so the head always settles back.strength: 0is a mime: no pulse at all. An authoredplay: speech_pulseon the speaker replaces the automatic pulses (nothing is added beside it), but it is not a declaration: the fall from the mouth chart is still recorded, and only a declaredspeechmakes it the request. Substitutions (a declared method the rig cannot honour; the fall from the mouth chart to the pulse) go torecord, once per speaker. A declaredspeechnaming no method of the aspect, or pinned to a stale version, raisesVocabularyErrornaming the aspect’s methods (speech_problems()isan validate’s side of it).- Return type:
- cutan.characters.methods.speech_problems(declared)[source]¶
Why a character’s declared
speechcannot be honoured (empty: it can).An unknown method, a method of another aspect, or a pin to a version the registry no longer has; the message names the speech methods.
>>> speech_problems("pulse"), speech_problems(None) ([], []) >>> speech_problems("flap")[0].startswith("speech 'flap'") True
- cutan.characters.methods.substitution_problem(sub)[source]¶
The sentence
an validatereports for amissingsubstitution: what happened, plus each missing capability’s remedy (the substitution’s own where it carries one, else the registry’s).Nonefor no substitution or a non-fatal one.
- cutan.characters.methods.substitution_record(sub, *, entity_ref=None)[source]¶
A
Substitutionas anasset_resolutionentry.The compiled document’s record generalised (ADR 0002 decision 6):
kind: method,storethe aspect,refwhat was asked for,resolvedwhat was used,fallbackwhether--strict-assetsmakes it fatal.
- cutan.characters.methods.syllable_beats(line, *, min_gap_s=0.18)[source]¶
Syllable onsets of a dialogue line, in seconds from its start.
The line’s word timings, when its provider has them, are the authority: they are measured on the audio, so a beat never falls in a leading breath or a pause (an#272: a pulse dipped 0.35 s before “Hi” was heard). Each word starts a beat, and the viseme track’s syllables (where the mouth opens out of a closed shape) add beats INSIDE a word’s window, never outside every word. With no word timings: the viseme track’s syllables, else one beat at the line’s start. Beats closer than
min_gap_smerge.>>> from types import SimpleNamespace as NS >>> track = NS(keyframes=[NS(time=0.0, viseme="B"), NS(time=0.4, viseme="X"), ... NS(time=0.7, viseme="C")]) >>> syllable_beats(NS(viseme_track=track, word_timings=None, duration=1.2)) [0.0, 0.7] >>> words = [NS(start=0.35, end=1.0)] # a breath before the word >>> syllable_beats(NS(viseme_track=track, word_timings=words, duration=1.2)) [0.35, 0.7]
- cutan.characters.methods.walk_arg_problems(gait, args)[source]¶
The gait parameters in
argsthatgaitnever reads (cutan#21): each is a silent no-op otherwise.gaitis a spelling (one ofcutan.motion.GAITS); the walk’s own arguments (walk_own_args()) are never a problem. An unknown spelling is the walk’s own to refuse.>>> walk_arg_problems("hop", {"bob": 20, "distance": 80, "arm_swing": 0.2}) ["gait 'hop' does not read 'bob' (it reads: arm_swing, hop_height, step_length, step_s)"] >>> walk_arg_problems("glide", {"bob": 2}), walk_arg_problems("noop", {"bob": 2}) ([], [])
- cutan.characters.methods.walk_own_args()[source]¶
The walk’s own arguments — where to, how many steps, the limbs, the view — read off its signature: everything it accepts that is not a gait parameter (
cutan.motion.WALK_PARAM_DEFAULTS), which only the gaits that declare it read (cutan#21).>>> sorted(walk_own_args()) ['arms', 'direction', 'distance', 'gait', 'legs', 'steps', 'to_x', 'view']
- cutan.characters.methods.walk_preset_context(entity, args, *, descriptor, profile, scale, parts=None, policy=None)[source]¶
What the compiler adds to a
walkplay’s args before expanding it, as far as the walk’s LENGTH depends on it: the resolvedgait(when the entity resolves on the registry:profilegiven), the figure’s drawnscale(its stage scale, cutan#13), and — off the registry, where the walk picks its own gait from the limbs it finds — the builtparts. The extent resolver reads this so asequencewaits exactly as long as the walk runs (cutan#12); the expansion itself resolves the same way (and records the substitution). The one thing the extent does not see is the view in force (cutan#17): aprofileit resolves may walk aslegs, which is why those two sharestep_sandstep_length(tests/test_gaits.pypins that).>>> walk_preset_context("b", {"gait": "shuffle"}, descriptor=None, profile={}, scale=2.0) {'scale': 2.0, 'gait': 'glide'} >>> walk_preset_context("b", {"gait": "shuffle"}, descriptor=None, profile=None, scale=1.0) {'scale': 1.0} >>> walk_preset_context("b", {}, descriptor=None, profile=None, scale=1.0, parts=["head"]) {'scale': 1.0, 'parts': {'head': {}}}
- cutan.characters.methods.walk_problems(doc, *, entity, args, art_exists=None, view=<object object>, policy=None)[source]¶
Everything
an validatesays about onewalkon a character, each a warning: the gait it asks for will not be used (gait_problem()), and a gait parameter the gait it WILL use never reads (cutan#21) — when the gait is the descriptor’s or the chain’s; an explicit gait’s (a spelling, a method id or a pinned choice) unread parameter isplay_problems’ error.docis the character’s stored document: aCharacterDescriptor, or apartsrig (resolved on its parts, as the compiler does); anything else iscutout.play’s to report.