mochi-avatar 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/BRAND.md +85 -0
  2. package/LICENSE.md +104 -0
  3. package/README.md +135 -0
  4. package/dist/canvas2d/avatar.d.ts +264 -0
  5. package/dist/canvas2d/avatar.d.ts.map +1 -0
  6. package/dist/canvas2d/avatar.js +811 -0
  7. package/dist/canvas2d/avatar.js.map +1 -0
  8. package/dist/canvas2d/face.d.ts +74 -0
  9. package/dist/canvas2d/face.d.ts.map +1 -0
  10. package/dist/canvas2d/face.js +298 -0
  11. package/dist/canvas2d/face.js.map +1 -0
  12. package/dist/canvas2d/paths.d.ts +13 -0
  13. package/dist/canvas2d/paths.d.ts.map +1 -0
  14. package/dist/canvas2d/paths.js +24 -0
  15. package/dist/canvas2d/paths.js.map +1 -0
  16. package/dist/characters/colourways.d.ts +78 -0
  17. package/dist/characters/colourways.d.ts.map +1 -0
  18. package/dist/characters/colourways.js +76 -0
  19. package/dist/characters/colourways.js.map +1 -0
  20. package/dist/characters/index.d.ts +8 -0
  21. package/dist/characters/index.d.ts.map +1 -0
  22. package/dist/characters/index.js +8 -0
  23. package/dist/characters/index.js.map +1 -0
  24. package/dist/characters/mochi.d.ts +19 -0
  25. package/dist/characters/mochi.d.ts.map +1 -0
  26. package/dist/characters/mochi.js +66 -0
  27. package/dist/characters/mochi.js.map +1 -0
  28. package/dist/core/colour.d.ts +25 -0
  29. package/dist/core/colour.d.ts.map +1 -0
  30. package/dist/core/colour.js +37 -0
  31. package/dist/core/colour.js.map +1 -0
  32. package/dist/core/envelope.d.ts +153 -0
  33. package/dist/core/envelope.d.ts.map +1 -0
  34. package/dist/core/envelope.js +141 -0
  35. package/dist/core/envelope.js.map +1 -0
  36. package/dist/core/geometry.d.ts +106 -0
  37. package/dist/core/geometry.d.ts.map +1 -0
  38. package/dist/core/geometry.js +157 -0
  39. package/dist/core/geometry.js.map +1 -0
  40. package/dist/core/idle.d.ts +163 -0
  41. package/dist/core/idle.d.ts.map +1 -0
  42. package/dist/core/idle.js +262 -0
  43. package/dist/core/idle.js.map +1 -0
  44. package/dist/core/layout.d.ts +142 -0
  45. package/dist/core/layout.d.ts.map +1 -0
  46. package/dist/core/layout.js +172 -0
  47. package/dist/core/layout.js.map +1 -0
  48. package/dist/core/lens.d.ts +47 -0
  49. package/dist/core/lens.d.ts.map +1 -0
  50. package/dist/core/lens.js +67 -0
  51. package/dist/core/lens.js.map +1 -0
  52. package/dist/core/looks.d.ts +77 -0
  53. package/dist/core/looks.d.ts.map +1 -0
  54. package/dist/core/looks.js +168 -0
  55. package/dist/core/looks.js.map +1 -0
  56. package/dist/core/motion.d.ts +168 -0
  57. package/dist/core/motion.d.ts.map +1 -0
  58. package/dist/core/motion.js +527 -0
  59. package/dist/core/motion.js.map +1 -0
  60. package/dist/core/mouth.d.ts +93 -0
  61. package/dist/core/mouth.d.ts.map +1 -0
  62. package/dist/core/mouth.js +92 -0
  63. package/dist/core/mouth.js.map +1 -0
  64. package/dist/core/plain.d.ts +23 -0
  65. package/dist/core/plain.d.ts.map +1 -0
  66. package/dist/core/plain.js +63 -0
  67. package/dist/core/plain.js.map +1 -0
  68. package/dist/core/spec.d.ts +156 -0
  69. package/dist/core/spec.d.ts.map +1 -0
  70. package/dist/core/spec.js +236 -0
  71. package/dist/core/spec.js.map +1 -0
  72. package/dist/core/spring.d.ts +53 -0
  73. package/dist/core/spring.d.ts.map +1 -0
  74. package/dist/core/spring.js +68 -0
  75. package/dist/core/spring.js.map +1 -0
  76. package/dist/core/vocabulary.d.ts +182 -0
  77. package/dist/core/vocabulary.d.ts.map +1 -0
  78. package/dist/core/vocabulary.js +66 -0
  79. package/dist/core/vocabulary.js.map +1 -0
  80. package/dist/element/dough-avatar.d.ts +34 -0
  81. package/dist/element/dough-avatar.d.ts.map +1 -0
  82. package/dist/element/dough-avatar.js +189 -0
  83. package/dist/element/dough-avatar.js.map +1 -0
  84. package/dist/element/index.d.ts +3 -0
  85. package/dist/element/index.d.ts.map +1 -0
  86. package/dist/element/index.js +35 -0
  87. package/dist/element/index.js.map +1 -0
  88. package/dist/index.d.ts +38 -0
  89. package/dist/index.d.ts.map +1 -0
  90. package/dist/index.js +42 -0
  91. package/dist/index.js.map +1 -0
  92. package/dist/svg/silhouette.d.ts +97 -0
  93. package/dist/svg/silhouette.d.ts.map +1 -0
  94. package/dist/svg/silhouette.js +143 -0
  95. package/dist/svg/silhouette.js.map +1 -0
  96. package/package.json +90 -0
@@ -0,0 +1,168 @@
1
+ /**
2
+ * How each emotion deforms the neutral face.
3
+ *
4
+ * Every Emotion gets an entry, enforced by `Record<Emotion, Look>`. Letting one
5
+ * fall through to neutral would make `caps.presetExpressions` a lie: the
6
+ * backend would claim to support an expression it renders identically to no
7
+ * expression at all, and the caller has no way to discover that.
8
+ *
9
+ * Values are multipliers on the tuned base geometry rather than absolute sizes,
10
+ * so retuning the face in the tuner does not invalidate the table.
11
+ */
12
+ import { clamp01 } from './vocabulary.js';
13
+ export const NEUTRAL = {
14
+ eyeUpper: 1,
15
+ eyeLower: 1,
16
+ eyeWidth: 1,
17
+ eyeTilt: 0,
18
+ mouthUpper: 1,
19
+ mouthLower: 1,
20
+ mouthWidth: 1,
21
+ mouthAlpha: 1,
22
+ cheek: 0,
23
+ sparkle: 0,
24
+ gazeX: 0,
25
+ gazeY: 0,
26
+ squash: 0,
27
+ lean: 0,
28
+ };
29
+ export const LOOKS = {
30
+ neutral: NEUTRAL,
31
+ happy: {
32
+ ...NEUTRAL,
33
+ eyeUpper: 1.1,
34
+ // Negative: the bottom edge lifts past the baseline and the eye becomes ^.
35
+ eyeLower: -0.62,
36
+ eyeWidth: 1.06,
37
+ mouthUpper: 0,
38
+ mouthLower: 2.8,
39
+ mouthWidth: 1.35,
40
+ cheek: 0.6,
41
+ sparkle: 1,
42
+ squash: 0.13,
43
+ },
44
+ shy: {
45
+ ...NEUTRAL,
46
+ eyeUpper: 0.72,
47
+ eyeLower: -0.28,
48
+ eyeWidth: 0.96,
49
+ mouthUpper: 0,
50
+ mouthLower: 0.7,
51
+ mouthWidth: 0.66,
52
+ cheek: 1,
53
+ gazeX: -0.38,
54
+ gazeY: 0.28,
55
+ squash: 0.07,
56
+ lean: -0.06,
57
+ },
58
+ sad: {
59
+ ...NEUTRAL,
60
+ eyeUpper: 0.82,
61
+ eyeLower: 1.14,
62
+ eyeTilt: -0.17,
63
+ mouthUpper: 3,
64
+ mouthLower: -0.3,
65
+ mouthWidth: 0.9,
66
+ gazeY: 0.34,
67
+ squash: -0.11,
68
+ },
69
+ angry: {
70
+ ...NEUTRAL,
71
+ eyeUpper: 0.55,
72
+ eyeLower: 0.92,
73
+ eyeWidth: 1.06,
74
+ eyeTilt: 0.28,
75
+ mouthUpper: 2,
76
+ mouthLower: 0,
77
+ mouthWidth: 0.84,
78
+ cheek: 0.28,
79
+ gazeY: -0.14,
80
+ squash: 0.16,
81
+ },
82
+ surprised: {
83
+ ...NEUTRAL,
84
+ eyeUpper: 1.7,
85
+ eyeLower: 1.7,
86
+ eyeWidth: 1.16,
87
+ mouthUpper: 0,
88
+ mouthLower: 2.6,
89
+ mouthWidth: 0.62,
90
+ cheek: 0.12,
91
+ sparkle: 1,
92
+ gazeY: -0.2,
93
+ // -0.16, down from -0.24. This is the waking perk: `main.ts` wears
94
+ // `surprised` the instant she is told to wake, on top of a poke. At -0.24
95
+ // the pair drove the squash target past -0.34, which stretches her to
96
+ // roughly one and a half times her height -- a launch rather than an "oh!".
97
+ squash: -0.16,
98
+ },
99
+ thinking: {
100
+ ...NEUTRAL,
101
+ eyeUpper: 0.9,
102
+ eyeLower: 0.72,
103
+ eyeTilt: 0.09,
104
+ mouthUpper: 1,
105
+ mouthLower: 0.3,
106
+ mouthWidth: 0.58,
107
+ gazeX: -0.52,
108
+ gazeY: -0.42,
109
+ lean: 0.07,
110
+ },
111
+ sleepy: {
112
+ ...NEUTRAL,
113
+ eyeUpper: 0.16,
114
+ eyeLower: 0.16,
115
+ eyeTilt: -0.11,
116
+ mouthUpper: 0,
117
+ mouthLower: 1.5,
118
+ mouthWidth: 0.52,
119
+ // Gone. She is asleep; a mouth is the difference between resting and quiet.
120
+ mouthAlpha: 0,
121
+ cheek: 0.22,
122
+ gazeY: 0.34,
123
+ /*
124
+ 0.11, up from 0.09, which had itself been cut from 0.19.
125
+
126
+ `sleepy` is what she wears for the whole time she is asleep, which is most
127
+ of her life, so it is the pose she is judged on -- and at 0.19 plus a
128
+ breath she spread to 1.24 times her width and read as a puddle rather than
129
+ as something resting. The cut was applied HERE, to the resting pose,
130
+ because the breath it was summed with reached +0.045 and could not be
131
+ argued with.
132
+
133
+ Back to 0.09, and the round trip is worth recording. It was raised to 0.11
134
+ when the breath became one-sided and small, on the argument that the same
135
+ 0.135 ceiling bought a fuller resting pose. That argument was sound and the
136
+ premise did not survive: at the waking amplitude she moved 2px asleep and
137
+ read as switched off, so the asleep breath is twice as deep again (see
138
+ `ASLEEP_BREATH_GAIN`). With an excursion of 0.048 the base has to come back
139
+ down for the ceiling to stay where it was — 0.09 + 0.048 is 0.138, against
140
+ the 0.135 the pose plus breath already peaked at before any of this.
141
+
142
+ The pose is the same as it ever was, then. What changed is that the
143
+ movement on top of it is now all swell and no stretch.
144
+ */
145
+ squash: 0.09,
146
+ },
147
+ };
148
+ const KEYS = Object.keys(NEUTRAL);
149
+ /**
150
+ * Neutral blended toward the named emotion by intensity.
151
+ *
152
+ * Intensity 0 is exactly neutral for every emotion, which is what lets a signal
153
+ * decay smoothly rather than snapping when it expires.
154
+ */
155
+ export function blendLook(emotion, intensity) {
156
+ const target = LOOKS[emotion];
157
+ const t = clamp01(intensity);
158
+ const out = {};
159
+ // `a*(1-t) + b*t`, not `a + (b-a)*t`. The two are equivalent in real
160
+ // arithmetic and not in floating point: the second form returns
161
+ // 1.1000000000000001 rather than 1.1 at t = 1, so a fully applied emotion is
162
+ // never quite the look that was tuned. The endpoints are the values a human
163
+ // chose in the tuner, and they should survive the trip exactly.
164
+ for (const key of KEYS)
165
+ out[key] = NEUTRAL[key] * (1 - t) + target[key] * t;
166
+ return out;
167
+ }
168
+ //# sourceMappingURL=looks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"looks.js","sourceRoot":"","sources":["../../src/core/looks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAgB,MAAM,iBAAiB,CAAA;AA0DvD,MAAM,CAAC,MAAM,OAAO,GAAS;IAC3B,QAAQ,EAAE,CAAC;IACX,QAAQ,EAAE,CAAC;IACX,QAAQ,EAAE,CAAC;IACX,OAAO,EAAE,CAAC;IACV,UAAU,EAAE,CAAC;IACb,UAAU,EAAE,CAAC;IACb,UAAU,EAAE,CAAC;IACb,UAAU,EAAE,CAAC;IACb,KAAK,EAAE,CAAC;IACR,OAAO,EAAE,CAAC;IACV,KAAK,EAAE,CAAC;IACR,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC;IACT,IAAI,EAAE,CAAC;CACR,CAAA;AAED,MAAM,CAAC,MAAM,KAAK,GAAoC;IACpD,OAAO,EAAE,OAAO;IAChB,KAAK,EAAE;QACL,GAAG,OAAO;QACV,QAAQ,EAAE,GAAG;QACb,2EAA2E;QAC3E,QAAQ,EAAE,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI;QACd,UAAU,EAAE,CAAC;QACb,UAAU,EAAE,GAAG;QACf,UAAU,EAAE,IAAI;QAChB,KAAK,EAAE,GAAG;QACV,OAAO,EAAE,CAAC;QACV,MAAM,EAAE,IAAI;KACb;IACD,GAAG,EAAE;QACH,GAAG,OAAO;QACV,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI;QACd,UAAU,EAAE,CAAC;QACb,UAAU,EAAE,GAAG;QACf,UAAU,EAAE,IAAI;QAChB,KAAK,EAAE,CAAC;QACR,KAAK,EAAE,CAAC,IAAI;QACZ,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,IAAI;QACZ,IAAI,EAAE,CAAC,IAAI;KACZ;IACD,GAAG,EAAE;QACH,GAAG,OAAO;QACV,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE,CAAC,IAAI;QACd,UAAU,EAAE,CAAC;QACb,UAAU,EAAE,CAAC,GAAG;QAChB,UAAU,EAAE,GAAG;QACf,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,CAAC,IAAI;KACd;IACD,KAAK,EAAE;QACL,GAAG,OAAO;QACV,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE,IAAI;QACb,UAAU,EAAE,CAAC;QACb,UAAU,EAAE,CAAC;QACb,UAAU,EAAE,IAAI;QAChB,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,CAAC,IAAI;QACZ,MAAM,EAAE,IAAI;KACb;IACD,SAAS,EAAE;QACT,GAAG,OAAO;QACV,QAAQ,EAAE,GAAG;QACb,QAAQ,EAAE,GAAG;QACb,QAAQ,EAAE,IAAI;QACd,UAAU,EAAE,CAAC;QACb,UAAU,EAAE,GAAG;QACf,UAAU,EAAE,IAAI;QAChB,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,CAAC;QACV,KAAK,EAAE,CAAC,GAAG;QACX,mEAAmE;QACnE,0EAA0E;QAC1E,sEAAsE;QACtE,4EAA4E;QAC5E,MAAM,EAAE,CAAC,IAAI;KACd;IACD,QAAQ,EAAE;QACR,GAAG,OAAO;QACV,QAAQ,EAAE,GAAG;QACb,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE,IAAI;QACb,UAAU,EAAE,CAAC;QACb,UAAU,EAAE,GAAG;QACf,UAAU,EAAE,IAAI;QAChB,KAAK,EAAE,CAAC,IAAI;QACZ,KAAK,EAAE,CAAC,IAAI;QACZ,IAAI,EAAE,IAAI;KACX;IACD,MAAM,EAAE;QACN,GAAG,OAAO;QACV,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE,CAAC,IAAI;QACd,UAAU,EAAE,CAAC;QACb,UAAU,EAAE,GAAG;QACf,UAAU,EAAE,IAAI;QAChB,4EAA4E;QAC5E,UAAU,EAAE,CAAC;QACb,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,IAAI;QACX;;;;;;;;;;;;;;;;;;;;;UAqBE;QACF,MAAM,EAAE,IAAI;KACb;CACF,CAAA;AAED,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAA8B,CAAA;AAE9D;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,OAAgB,EAAE,SAAiB;IAC3D,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAA;IAC7B,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAA;IAC5B,MAAM,GAAG,GAAG,EAAgC,CAAA;IAC5C,qEAAqE;IACrE,gEAAgE;IAChE,6EAA6E;IAC7E,4EAA4E;IAC5E,gEAAgE;IAChE,KAAK,MAAM,GAAG,IAAI,IAAI;QAAE,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAC3E,OAAO,GAAG,CAAA;AACZ,CAAC"}
@@ -0,0 +1,168 @@
1
+ /**
2
+ * A motion: the layer between idle and expression.
3
+ *
4
+ * ## Why this format exists now, having been refused twice
5
+ *
6
+ * The argument for not writing it was that a format designed against ONE
7
+ * motion set comes out shaped like that set. That was right, and it made the
8
+ * missing input sound external when it was not: the answer is to author a
9
+ * second motion, not to wait for one. So there are two below, deliberately
10
+ * unalike — `nod` is short, one-shot and postural; `sway` loops and moves her
11
+ * gaze. Everything here is a thing both of them needed.
12
+ *
13
+ * What that shook out, which one clip would not have:
14
+ *
15
+ * - `loop`, because one of them does and one must not
16
+ * - keys with OPTIONAL channels, because `nod` never touches gaze and a
17
+ * format forcing it to write zeros would make every clip claim every
18
+ * channel, and then "unset" and "zero" would be the same thing
19
+ * - normalised time, because the two differ in duration by 6x and keys
20
+ * written in milliseconds cannot be retimed without rewriting them all
21
+ *
22
+ * ## Four channels, and the mouth is not among them
23
+ *
24
+ * The layer order is idle -> motion -> expression -> mouth, and no layer may
25
+ * overwrite the mouth. A motion that could write `mouthOpen` would be able to
26
+ * hold her jaw shut while audio is playing, which reads as broken. The
27
+ * channels here are POSTURAL -- squash, lean, and where she is looking -- so
28
+ * the rule is a property of the vocabulary rather than something each clip has
29
+ * to respect.
30
+ */
31
+ /**
32
+ * Everything a motion is allowed to move. See the header.
33
+ *
34
+ * ## The three that were added, and why they are not more
35
+ *
36
+ * `squash`, `lean`, `gazeX`, `gazeY` are postural and could not express a
37
+ * character who goes anywhere: a hop with no vertical channel is a crouch, and
38
+ * a wander with no horizontal one is a lean. So:
39
+ *
40
+ * - `lift` — up, in fractions of her body HEIGHT. Positive leaves the ground.
41
+ * - `shift` — right, in fractions of her body WIDTH.
42
+ * - `turn` — -1..1, how far her features have slid toward one side.
43
+ *
44
+ * `turn` is NOT a rotation and cannot become one. One front-facing silhouette
45
+ * is drawn; there is no second view and no yaw. What it does is slide the
46
+ * features across the body, and everything is clipped to the silhouette
47
+ * already, so the far eye passes out of sight around her edge. That reads as
48
+ * turning to look at something and does not read as turning around. Anyone
49
+ * wanting the second thing has to draw her from behind, which is art rather
50
+ * than a channel.
51
+ *
52
+ * The mouth is still not among these, for the reason the header gives.
53
+ *
54
+ * ## Translation is a channel, not a window move
55
+ *
56
+ * `lift` and `shift` move her INSIDE her own window, and `paint` applies them
57
+ * by offsetting the origin every other coordinate is derived from -- so the
58
+ * outline, the features and the hit-test silhouette all travel together, and
59
+ * click-through keeps following her painted pixels for free.
60
+ *
61
+ * Moving the WINDOW was the alternative and is worse: `setPosition` from main
62
+ * on every frame of a renderer-driven animation is two processes disagreeing
63
+ * about where she is sixty times a second, which is the problem `drag.ts`
64
+ * already needs a cursor poll to solve.
65
+ */
66
+ export declare const MOTION_CHANNELS: readonly ['squash', 'lean', 'gazeX', 'gazeY', 'lift', 'shift', 'turn'];
67
+ export type MotionChannel = (typeof MOTION_CHANNELS)[number];
68
+ /** What the layer contributes at one instant. Absent means "not moved". */
69
+ export type MotionPose = Partial<Record<MotionChannel, number>>;
70
+ export interface MotionKey extends MotionPose {
71
+ /**
72
+ * Where in the clip, 0..1.
73
+ *
74
+ * NORMALISED rather than milliseconds. The two built-ins differ in length by
75
+ * six times, and a clip timed in absolute units cannot be slowed down
76
+ * without rewriting every key in it.
77
+ */
78
+ readonly t: number;
79
+ }
80
+ export interface MotionClip {
81
+ readonly durationMs: number;
82
+ /** Whether it starts again. `sway` does; `nod` must not. */
83
+ readonly loop: boolean;
84
+ /** Sorted by `t`, which `parseMotionClip` checks rather than assumes. */
85
+ readonly keys: readonly MotionKey[];
86
+ }
87
+ /**
88
+ * How far through a clip we are, or null once a one-shot has finished.
89
+ *
90
+ * Null rather than 1: a finished one-shot contributes NOTHING, and holding its
91
+ * last key would leave her permanently leaning if a clip happened to end away
92
+ * from neutral. The caller stops mixing the layer in at all.
93
+ */
94
+ export declare function progress(clip: MotionClip, elapsedMs: number): number | null;
95
+ /**
96
+ * The pose at a moment, interpolated between the keys around it.
97
+ *
98
+ * Per CHANNEL, not per key. A key that omits `gazeX` is not saying "zero", it
99
+ * is saying nothing -- so the interpolation for that channel runs between the
100
+ * nearest keys that DO mention it, and a channel no key mentions is absent
101
+ * from the result. Blending toward an implied zero instead would make every
102
+ * clip fight every other layer over channels it never meant to touch.
103
+ */
104
+ export declare function poseAt(clip: MotionClip, at: number): MotionPose;
105
+ /**
106
+ * A short agreement, and a long one that repeats.
107
+ *
108
+ * Two, and unalike on purpose -- see the header. They are also the only
109
+ * motions this app has, which is why `caps.supportsMotions` can finally be
110
+ * true: a backend claiming the capability with an empty library would be the
111
+ * flag lying, and `playMotion` would report success over a face that never
112
+ * moved.
113
+ */
114
+ export declare const BUILT_IN_MOTIONS: Readonly<Record<string, MotionClip>>;
115
+ /**
116
+ * How far a clip takes her from where she stands, as fractions of her body.
117
+ *
118
+ * The room for a translation has to exist in her window BEFORE she uses it, or
119
+ * she walks into the edge of a transparent rectangle and is clipped by it. This
120
+ * is what `face.ts` reserves against, and it is derived from the keys rather
121
+ * than declared beside them: a number an author has to keep in step with the
122
+ * clip is a number that goes stale the first time somebody retimes it.
123
+ *
124
+ * `up` only, for `lift`. She hops; she does not sink through the floor, and
125
+ * reserving room below her would push her down inside her own window for a
126
+ * clearance nothing uses.
127
+ */
128
+ export declare function motionReach(clip: MotionClip): {
129
+ readonly x: number;
130
+ readonly up: number;
131
+ };
132
+ /**
133
+ * The worst case across every built-in, PLUS the drift, which is what a window
134
+ * has to hold.
135
+ *
136
+ * The drift is continuous and additive -- she is always a little off her spot,
137
+ * and a clip starts from wherever that is rather than from centre. So the two
138
+ * reaches SUM: reserving only the clip's would clip her by a couple of pixels
139
+ * at whichever extreme the two happened to agree on, intermittently, which is
140
+ * the least debuggable kind of wrong.
141
+ */
142
+ export declare function builtInReach(): {
143
+ readonly x: number;
144
+ readonly up: number;
145
+ };
146
+ export type MotionParse = {
147
+ readonly ok: true;
148
+ readonly clip: MotionClip;
149
+ } | {
150
+ readonly ok: false;
151
+ readonly problems: readonly string[];
152
+ };
153
+ /**
154
+ * Turn something off disk into a clip, or say what is wrong with all of it.
155
+ *
156
+ * A package may carry motions, so this is a trust boundary and takes the same
157
+ * treatment `parseFaceSpec` gets: every problem reported at once
158
+ * rather than the first, unknown keys refused rather than dropped, and every
159
+ * number range-checked -- `t: 5` and `squash: 400` are both valid JSON numbers
160
+ * that throw nothing and produce a mochi somewhere off the screen.
161
+ *
162
+ * The ordering and loop-closure rules are checked HERE rather than trusted,
163
+ * because `poseAt` is written against them: keys out of order make the
164
+ * interpolation pick the wrong pair, and a loop whose ends differ jumps once
165
+ * per cycle for as long as it plays.
166
+ */
167
+ export declare function parseMotionClip(value: unknown): MotionParse;
168
+ //# sourceMappingURL=motion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"motion.d.ts","sourceRoot":"","sources":["../../src/core/motion.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAIH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,eAAe,YAC1B,QAAQ,EACR,MAAM,EACN,OAAO,EACP,OAAO,EACP,MAAM,EACN,OAAO,EACP,MAAM,CACE,CAAA;AACV,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAE5D,2EAA2E;AAC3E,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAA;AAE/D,MAAM,WAAW,SAAU,SAAQ,UAAU;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,yEAAyE;IACzE,QAAQ,CAAC,IAAI,EAAE,SAAS,SAAS,EAAE,CAAA;CACpC;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK3E;AAED;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,CAO/D;AAwID;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAyJjE,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG;IAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAQzF;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,IAAI;IAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAS1E;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GAChD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAA;AAEhE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CA0F3D"}