@voqalize/avatar 0.2.2 → 0.3.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 (201) hide show
  1. package/LICENSE +21 -661
  2. package/README.md +144 -663
  3. package/client/Avatar.tsx +57 -0
  4. package/client/AvatarClient.ts +523 -0
  5. package/client/arjun.ts +24 -0
  6. package/client/createAvatar.ts +139 -0
  7. package/client/createCanvasAvatar.ts +72 -0
  8. package/client/index.ts +39 -0
  9. package/client/internal.ts +80 -0
  10. package/client/interviewer-female.ts +4 -0
  11. package/client/interviewer-male.ts +4 -0
  12. package/client/ishita.ts +24 -0
  13. package/client/kabir.ts +24 -0
  14. package/client/meera.ts +24 -0
  15. package/client/naina.ts +24 -0
  16. package/client/professional-female-a.ts +4 -0
  17. package/client/professional-female-b.ts +4 -0
  18. package/client/professional-male-a.ts +4 -0
  19. package/client/professional-male-b.ts +4 -0
  20. package/client/react.ts +13 -0
  21. package/client/types.ts +146 -0
  22. package/client/useAvatar.ts +74 -0
  23. package/client/vikram.ts +24 -0
  24. package/dist/Avatar.d.ts +44 -0
  25. package/dist/Avatar.d.ts.map +1 -0
  26. package/{client/dist → dist}/Avatar.js +2 -2
  27. package/dist/Avatar.js.map +1 -0
  28. package/dist/AvatarClient.d.ts +196 -0
  29. package/dist/AvatarClient.d.ts.map +1 -0
  30. package/dist/AvatarClient.js +450 -0
  31. package/dist/AvatarClient.js.map +1 -0
  32. package/dist/arjun.d.ts +6 -0
  33. package/dist/arjun.d.ts.map +1 -0
  34. package/dist/arjun.js +18 -0
  35. package/dist/arjun.js.map +1 -0
  36. package/dist/createAvatar.d.ts +99 -0
  37. package/dist/createAvatar.d.ts.map +1 -0
  38. package/dist/createAvatar.js +75 -0
  39. package/dist/createAvatar.js.map +1 -0
  40. package/dist/createCanvasAvatar.d.ts +22 -0
  41. package/dist/createCanvasAvatar.d.ts.map +1 -0
  42. package/dist/createCanvasAvatar.js +47 -0
  43. package/dist/createCanvasAvatar.js.map +1 -0
  44. package/dist/index.d.ts +30 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +29 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/internal.d.ts +30 -0
  49. package/dist/internal.d.ts.map +1 -0
  50. package/dist/internal.js +33 -0
  51. package/dist/internal.js.map +1 -0
  52. package/dist/interviewer-female.d.ts +4 -0
  53. package/dist/interviewer-female.d.ts.map +1 -0
  54. package/dist/interviewer-female.js +3 -0
  55. package/dist/interviewer-female.js.map +1 -0
  56. package/dist/interviewer-male.d.ts +4 -0
  57. package/dist/interviewer-male.d.ts.map +1 -0
  58. package/dist/interviewer-male.js +3 -0
  59. package/dist/interviewer-male.js.map +1 -0
  60. package/dist/ishita.d.ts +6 -0
  61. package/dist/ishita.d.ts.map +1 -0
  62. package/dist/ishita.js +18 -0
  63. package/dist/ishita.js.map +1 -0
  64. package/dist/kabir.d.ts +6 -0
  65. package/dist/kabir.d.ts.map +1 -0
  66. package/dist/kabir.js +18 -0
  67. package/dist/kabir.js.map +1 -0
  68. package/dist/meera.d.ts +6 -0
  69. package/dist/meera.d.ts.map +1 -0
  70. package/dist/meera.js +18 -0
  71. package/dist/meera.js.map +1 -0
  72. package/dist/naina.d.ts +6 -0
  73. package/dist/naina.d.ts.map +1 -0
  74. package/dist/naina.js +18 -0
  75. package/dist/naina.js.map +1 -0
  76. package/dist/professional-female-a.d.ts +4 -0
  77. package/dist/professional-female-a.d.ts.map +1 -0
  78. package/dist/professional-female-a.js +3 -0
  79. package/dist/professional-female-a.js.map +1 -0
  80. package/dist/professional-female-b.d.ts +4 -0
  81. package/dist/professional-female-b.d.ts.map +1 -0
  82. package/dist/professional-female-b.js +3 -0
  83. package/dist/professional-female-b.js.map +1 -0
  84. package/dist/professional-male-a.d.ts +4 -0
  85. package/dist/professional-male-a.d.ts.map +1 -0
  86. package/dist/professional-male-a.js +3 -0
  87. package/dist/professional-male-a.js.map +1 -0
  88. package/dist/professional-male-b.d.ts +4 -0
  89. package/dist/professional-male-b.d.ts.map +1 -0
  90. package/dist/professional-male-b.js +3 -0
  91. package/dist/professional-male-b.js.map +1 -0
  92. package/dist/react.d.ts +13 -0
  93. package/dist/react.d.ts.map +1 -0
  94. package/dist/react.js +13 -0
  95. package/dist/react.js.map +1 -0
  96. package/dist/types.d.ts +94 -0
  97. package/dist/types.d.ts.map +1 -0
  98. package/dist/types.js +93 -0
  99. package/dist/types.js.map +1 -0
  100. package/dist/useAvatar.d.ts +39 -0
  101. package/dist/useAvatar.d.ts.map +1 -0
  102. package/dist/useAvatar.js +35 -0
  103. package/dist/useAvatar.js.map +1 -0
  104. package/dist/vikram.d.ts +6 -0
  105. package/dist/vikram.d.ts.map +1 -0
  106. package/dist/vikram.js +18 -0
  107. package/dist/vikram.js.map +1 -0
  108. package/package.json +81 -24
  109. package/src/avatar.d.ts +161 -120
  110. package/src/avatar.js +234 -146
  111. package/src/behavior.d.ts +26 -0
  112. package/src/behavior.js +102 -0
  113. package/src/camera.js +29 -0
  114. package/src/canvas/author/parts/eye.mjs +722 -0
  115. package/src/canvas/author/parts/hand.mjs +1156 -0
  116. package/src/canvas/author/parts/mouth.mjs +741 -0
  117. package/src/canvas/author/parts/nose.mjs +100 -0
  118. package/src/canvas/author/parts/skin-detail.mjs +67 -0
  119. package/src/canvas/author/path.mjs +283 -0
  120. package/src/canvas/author/rig.mjs +405 -0
  121. package/src/canvas/avatars/round/face.d.mts +3 -0
  122. package/src/canvas/avatars/round/face.mjs +1307 -0
  123. package/src/canvas/create-rig.d.ts +15 -0
  124. package/src/canvas/create-rig.js +100 -0
  125. package/src/canvas/data/img/professional-female-a-hair-back.webp +0 -0
  126. package/src/canvas/data/img/professional-female-a-hair-front.webp +0 -0
  127. package/src/canvas/data/img/professional-female-a-top-body.webp +0 -0
  128. package/src/canvas/data/img/professional-female-b-hair-back.webp +0 -0
  129. package/src/canvas/data/img/professional-female-b-hair-front.webp +0 -0
  130. package/src/canvas/data/img/professional-female-b-top-body.webp +0 -0
  131. package/src/canvas/data/img/professional-male-a-hair-back.webp +0 -0
  132. package/src/canvas/data/img/professional-male-a-hair-front.webp +0 -0
  133. package/src/canvas/data/img/professional-male-a-top-body.webp +0 -0
  134. package/src/canvas/data/img/professional-male-b-hair-back.webp +0 -0
  135. package/src/canvas/data/img/professional-male-b-hair-front.webp +0 -0
  136. package/src/canvas/data/img/professional-male-b-top-body.webp +0 -0
  137. package/src/canvas/data/img/round-m3-hair-back.webp +0 -0
  138. package/src/canvas/data/img/round-m3-hair-front.webp +0 -0
  139. package/src/canvas/data/img/round-m3-top-body.webp +0 -0
  140. package/src/canvas/data/img/round-w1-hair-back.webp +0 -0
  141. package/src/canvas/data/img/round-w1-hair-front.webp +0 -0
  142. package/src/canvas/data/img/round-w1-top-body.webp +0 -0
  143. package/src/canvas/data/interviewer-female.rig.json +1 -0
  144. package/src/canvas/data/interviewer-male.rig.json +1 -0
  145. package/src/canvas/data/professional-female-a.rig.json +1 -0
  146. package/src/canvas/data/professional-female-b.rig.json +1 -0
  147. package/src/canvas/data/professional-male-a.rig.json +1 -0
  148. package/src/canvas/data/professional-male-b.rig.json +1 -0
  149. package/src/canvas/src/live.js +508 -0
  150. package/src/canvas/src/render2d.js +218 -0
  151. package/src/canvas/src/rig.js +297 -0
  152. package/src/canvas/src/vocab.js +96 -0
  153. package/src/clips.js +18 -7
  154. package/src/conformance.js +119 -0
  155. package/src/emotions.js +4 -3
  156. package/src/face-core.js +27 -1
  157. package/src/face-myna.d.ts +7 -0
  158. package/src/face-myna.js +192 -131
  159. package/src/face-peep-control-plane.js +167 -0
  160. package/src/face-peep.d.ts +7 -0
  161. package/src/face-peep.js +274 -116
  162. package/src/face-wren.d.ts +7 -0
  163. package/src/face-wren.js +17 -19
  164. package/src/faces.d.ts +12 -0
  165. package/src/faces.js +53 -0
  166. package/src/gaze.js +119 -4
  167. package/src/hand.js +120 -91
  168. package/src/idle.js +18 -101
  169. package/src/interjections.js +156 -31
  170. package/src/params.js +8 -4
  171. package/src/perform.js +7 -9
  172. package/src/rig.d.ts +24 -0
  173. package/src/rig.js +32 -0
  174. package/src/speech-timing.js +23 -0
  175. package/src/visemes.js +28 -12
  176. package/client/dist/Avatar.d.ts +0 -27
  177. package/client/dist/Avatar.d.ts.map +0 -1
  178. package/client/dist/Avatar.js.map +0 -1
  179. package/client/dist/AvatarClient.d.ts +0 -139
  180. package/client/dist/AvatarClient.d.ts.map +0 -1
  181. package/client/dist/AvatarClient.js +0 -223
  182. package/client/dist/AvatarClient.js.map +0 -1
  183. package/client/dist/index.d.ts +0 -22
  184. package/client/dist/index.d.ts.map +0 -1
  185. package/client/dist/index.js +0 -22
  186. package/client/dist/index.js.map +0 -1
  187. package/client/dist/types.d.ts +0 -86
  188. package/client/dist/types.d.ts.map +0 -1
  189. package/client/dist/types.js +0 -31
  190. package/client/dist/types.js.map +0 -1
  191. package/client/dist/useAvatar.d.ts +0 -42
  192. package/client/dist/useAvatar.d.ts.map +0 -1
  193. package/client/dist/useAvatar.js +0 -51
  194. package/client/dist/useAvatar.js.map +0 -1
  195. package/client/src/Avatar.tsx +0 -33
  196. package/client/src/AvatarClient.ts +0 -270
  197. package/client/src/index.ts +0 -22
  198. package/client/src/types.ts +0 -104
  199. package/client/src/useAvatar.ts +0 -85
  200. package/docs/contract-avatar.md +0 -371
  201. package/docs/contract-protocol.md +0 -461
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Peep control-plane study.
3
+ *
4
+ * This is intentionally not a character and is not registered in faces.js.
5
+ * It retains only SVG elements whose geometry is written from apply(params):
6
+ * brows, eyes, mouth interior/lips, teeth and tongue. The other pose-layer
7
+ * groups remain empty because poseTransforms writes their transforms; keeping
8
+ * the groups lets this file exercise the exact same animation contract without
9
+ * silently changing the engine.
10
+ */
11
+
12
+ import { clamp, lerp } from './params.js';
13
+ import { f, createFaceShell, faceApi, poseTransforms, pairedTeeth } from './face-core.js';
14
+ import { taper, taperRing, region } from './line-art.js';
15
+
16
+ export const THEME = {
17
+ ink: '#1b1b1b', paper: '#ffffff', accent: '#f97415',
18
+ mouthIn: '#1b1b1b', teeth: '#ffffff', tongue: '#8d7f79',
19
+ };
20
+
21
+ const VB = { x: 92, y: 76, w: 576, h: 800 };
22
+ export const META = {
23
+ viewBox: { ...VB },
24
+ mouthCrop: { x: 296, y: 434, w: 168, h: 98 },
25
+ };
26
+
27
+ // These are geometry anchors for paths regenerated by apply(), not drawn art.
28
+ const CX = 380;
29
+ const BROW_L = [[CX - 20, 347], [CX - 40, 340], [CX - 60, 336], [CX - 76, 337],
30
+ [CX - 86, 339], [CX - 94, 343], [CX - 99, 348]];
31
+ const BROW_R = [[CX + 22, 344], [CX + 40, 337], [CX + 58, 333], [CX + 73, 335],
32
+ [CX + 83, 337], [CX + 90, 341], [CX + 95, 346]];
33
+ const EYE = { y: 386, dx: 55, rx: 14, ry: 16.5 };
34
+ const MOUTH = { cx: CX, cy: 488 };
35
+ const MOUTH_APERTURE = 38;
36
+
37
+ // The original pose specification is retained verbatim: this study is for
38
+ // observing control planes, not for creating a second motion model.
39
+ const PIVOT = { x: CX, y: 700 };
40
+ const POSE = {
41
+ leanTravel: 23, leanPivot: { x: PIVOT.x, y: 560 },
42
+ shrugLift: 30, shrugTiltDeg: 1.6, shrugPivot: { x: PIVOT.x, y: 800 },
43
+ yawPx: 28, pitchPx: 17, pivot: PIVOT,
44
+ pitch: {
45
+ headLayers: ['skull', 'features', 'hair'], neckLayer: 'neck',
46
+ hinge: { x: CX, y: 620 }, neckBase: { x: CX, y: 720 },
47
+ headTravel: 1.0, neckTravel: 0.22, foreshorten: 0.040, neckCompress: 0.034,
48
+ },
49
+ breathSwell: 0.008, swellPivot: { x: CX, y: 950 }, turnPx: 16,
50
+ layers: ['neck', 'skull', 'body', 'features', 'hair'],
51
+ parallax: { neck: 1.0, skull: 1.0, body: 0.1, features: 1.22, hair: 1.12 },
52
+ torsoLayers: ['body'], units: 1,
53
+ };
54
+
55
+ function mouthGeometry(p) {
56
+ const cx = MOUTH.cx, cy = MOUTH.cy;
57
+ const open = clamp(p.mouthOpen), round = clamp(p.mouthRound), tuck = clamp(p.mouthTuck);
58
+ const w = (26 + clamp(p.mouthWidth) * 32) * (1 - 0.36 * round);
59
+ const t = 1 - 0.4 * clamp(p.mouthPress);
60
+ const profile = [2.5 * t, 10.5 * t, 3 * t, 11.5 * t * (1 + 0.35 * tuck), 2.5 * t];
61
+ const halfUp = profile[1] / 2, halfLo = profile[3] / 2;
62
+ const h = open * MOUTH_APERTURE, k = clamp(open / 0.18);
63
+ const yL = cy - 1.5 - p.mouthCornerL * 24;
64
+ const yR = cy - 1.5 - p.mouthCornerR * 24;
65
+ const apTop = cy - h * 0.25;
66
+ let apBot = cy + h * 0.75;
67
+ if (tuck > 0) apBot = Math.max(apTop + 6, apBot - tuck * (h * 0.6 + 4));
68
+ const cornerMid = (yL + yR) / 8;
69
+ const topY = (apTop - k * halfUp - cornerMid) / 0.75;
70
+ const botY = (apBot + k * halfLo - cornerMid) / 0.75;
71
+ const contour = [
72
+ [cx - w, yL], [cx - w * 0.55, topY], [cx + w * 0.55, topY], [cx + w, yR],
73
+ [cx + w * 0.55, botY], [cx - w * 0.55, botY], [cx - w, yL],
74
+ ];
75
+ return {
76
+ contour, profile, cx, w, open, tuck,
77
+ innerTop: cornerMid + 0.75 * topY + halfUp,
78
+ innerBot: cornerMid + 0.75 * botY - halfLo,
79
+ };
80
+ }
81
+
82
+ function teethPath(m, amt, lower) {
83
+ if (amt < 0.01) return '';
84
+ const gap = m.innerBot - m.innerTop;
85
+ if (gap < 2) return '';
86
+ const tw = m.w * (lower ? 0.6 : 0.76);
87
+ const cap = lower ? 0.5 : 0.5 + 0.35 * m.tuck;
88
+ const th = Math.min(amt * (lower ? 13 : 20), gap * cap);
89
+ if (lower) {
90
+ const base = m.innerBot + 8, edge = m.innerBot - th, end = m.innerBot - th * 0.35;
91
+ return `M${f(m.cx - tw)} ${f(base)}L${f(m.cx + tw)} ${f(base)}`
92
+ + `L${f(m.cx + tw * 0.92)} ${f(end)}Q${f(m.cx)} ${f(2 * edge - end)} ${f(m.cx - tw * 0.92)} ${f(end)}Z`;
93
+ }
94
+ const top = m.innerTop - 8, edge = m.innerTop + th, end = m.innerTop + th * 0.35;
95
+ return `M${f(m.cx - tw)} ${f(top)}L${f(m.cx + tw)} ${f(top)}`
96
+ + `L${f(m.cx + tw * 0.92)} ${f(end)}Q${f(m.cx)} ${f(2 * edge - end)} ${f(m.cx - tw * 0.92)} ${f(end)}Z`;
97
+ }
98
+
99
+ function eyePath(cx, cy, lid, squint, tiltDeg) {
100
+ const L = clamp(lid);
101
+ const topY = lerp(cy - EYE.ry * 1.05, cy - EYE.ry * 0.42, L);
102
+ const botY = lerp(cy + EYE.ry * 1.05, cy - EYE.ry * 0.05, L) - clamp(squint) * EYE.ry * 0.7;
103
+ const a = (tiltDeg * Math.PI) / 180, ca = Math.cos(a), sa = Math.sin(a);
104
+ const R = (x, y) => {
105
+ const dx = x - cx, dy = y - cy;
106
+ return `${f(cx + dx * ca - dy * sa)} ${f(cy + dx * sa + dy * ca)}`;
107
+ };
108
+ return `M${R(cx - EYE.rx, cy)}C${R(cx - EYE.rx * 0.5, topY)} ${R(cx + EYE.rx * 0.5, topY)} ${R(cx + EYE.rx, cy)}`
109
+ + `C${R(cx + EYE.rx * 0.5, botY)} ${R(cx - EYE.rx * 0.5, botY)} ${R(cx - EYE.rx, cy)}Z`;
110
+ }
111
+
112
+ function browPath(pts, raise, angle, inner) {
113
+ const n = pts.length - 1;
114
+ return taper(pts.map(([x, y], i) => {
115
+ const u = i / n;
116
+ return [x, y - raise * 15 - inner * 11 * (1 - u) - angle * 12 * u];
117
+ }), [3.5, 17, 8], 6);
118
+ }
119
+
120
+ function markup(id, t) {
121
+ return `<svg id="${id}" viewBox="${VB.x} ${VB.y} ${VB.w} ${VB.h}" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" style="display:block;width:100%;height:100%">
122
+ <defs><clipPath id="${id}-clipMouth"><path id="${id}-clipMouthP" d=""/></clipPath></defs>
123
+ <g id="${id}-neck"/>
124
+ <g id="${id}-skull"/>
125
+ <g id="${id}-body"/>
126
+ <g id="${id}-features">
127
+ <path id="${id}-browL" fill="${t.ink}"/><path id="${id}-browR" fill="${t.ink}"/>
128
+ <g id="${id}-eyes"><path id="${id}-eyeL" fill="${t.ink}"/><path id="${id}-eyeR" fill="${t.ink}"/></g>
129
+ <g id="${id}-mouth"><path id="${id}-mouthIn" fill="${t.mouthIn}"/>
130
+ <g clip-path="url(#${id}-clipMouth)"><ellipse id="${id}-tongue" fill="${t.tongue}"/><path id="${id}-teeth" fill="${t.teeth}"/><path id="${id}-teethLo" fill="${t.teeth}" opacity=".85"/></g>
131
+ <path id="${id}-lips" fill="${t.ink}"/></g>
132
+ </g>
133
+ <g id="${id}-hair"/>
134
+ </svg>`;
135
+ }
136
+
137
+ let uid = 0;
138
+ export function createFace(mount, theme = {}) {
139
+ const t = Object.assign({}, THEME, theme);
140
+ const id = `peepControlPlane${++uid}`;
141
+ const { svg, $, set } = createFaceShell(mount, id, markup(id, t));
142
+ const el = {
143
+ neck: $('neck'), skull: $('skull'), body: $('body'), features: $('features'), hair: $('hair'),
144
+ browL: $('browL'), browR: $('browR'), eyes: $('eyes'), eyeL: $('eyeL'), eyeR: $('eyeR'),
145
+ mouthIn: $('mouthIn'), lips: $('lips'), clipMouth: $('clipMouthP'),
146
+ teeth: $('teeth'), teethLo: $('teethLo'), tongue: $('tongue'),
147
+ };
148
+ function apply(p) {
149
+ poseTransforms(p, set, el, POSE);
150
+ set(el.eyes, 'transform', `translate(${f(p.pupilX * 11)} ${f(p.pupilY * 8)})`);
151
+ const lidFollow = Math.max(0, p.pupilY) * 0.22;
152
+ set(el.eyeL, 'd', eyePath(CX - EYE.dx, EYE.y + 1, p.lidL + lidFollow, p.squintL, -9));
153
+ set(el.eyeR, 'd', eyePath(CX + EYE.dx, EYE.y - 1, p.lidR + lidFollow, p.squintR, 8));
154
+ set(el.browL, 'd', browPath(BROW_L, p.browRaiseL, p.browAngleL, p.browInnerL));
155
+ set(el.browR, 'd', browPath(BROW_R, p.browRaiseR, p.browAngleR, p.browInnerR));
156
+ const m = mouthGeometry(p), contour = region(m.contour);
157
+ set(el.mouthIn, 'd', contour); set(el.clipMouth, 'd', contour); set(el.lips, 'd', taperRing(m.contour, m.profile, 12));
158
+ set(el.mouthIn, 'opacity', f(clamp((m.innerBot - m.innerTop) / 3)));
159
+ pairedTeeth(p, set, el, teethPath, m);
160
+ const tg = clamp(p.tongue);
161
+ set(el.tongue, 'cx', f(m.cx)); set(el.tongue, 'cy', f(m.innerBot + 6 - tg * ((m.innerBot - m.innerTop) * 0.8 + 6)));
162
+ set(el.tongue, 'rx', f(m.w * 0.58)); set(el.tongue, 'ry', '8'); set(el.tongue, 'opacity', tg > 0.02 ? '1' : '0');
163
+ }
164
+ return faceApi(mount, svg, apply, t);
165
+ }
166
+
167
+ export const peepControlPlane = { create: createFace, meta: META };
@@ -0,0 +1,7 @@
1
+ import type { AvatarMeta, Face, FaceFactory } from "./avatar.js";
2
+
3
+ export const createFace: FaceFactory;
4
+ export const META: AvatarMeta;
5
+ /** Default palette. Passed as `createAvatar({ theme })`; see authoring-a-face.md. */
6
+ export const THEME: Readonly<Record<string, string>>;
7
+ export const peep: Face;