@voqalize/avatar 0.2.2 → 0.4.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.
- package/LICENSE +21 -661
- package/LICENSE-CC-BY-4.0 +396 -0
- package/README.md +191 -662
- package/assets/README.md +48 -0
- package/assets/tanya.glb +0 -0
- package/assets/tara.glb +0 -0
- package/assets/tushar.glb +0 -0
- package/client/Avatar.tsx +57 -0
- package/client/AvatarClient.ts +818 -0
- package/client/arjun.ts +26 -0
- package/client/createAvatar.ts +177 -0
- package/client/createCanvasAvatar.ts +72 -0
- package/client/index.ts +44 -0
- package/client/internal.ts +108 -0
- package/client/interviewer-female.ts +4 -0
- package/client/interviewer-male.ts +4 -0
- package/client/ishita.ts +26 -0
- package/client/kabir.ts +26 -0
- package/client/meera.ts +26 -0
- package/client/naina.ts +26 -0
- package/client/playout.ts +95 -0
- package/client/professional-female-a.ts +4 -0
- package/client/professional-female-b.ts +4 -0
- package/client/professional-male-a.ts +4 -0
- package/client/professional-male-b.ts +4 -0
- package/client/react.ts +13 -0
- package/client/supports.ts +28 -0
- package/client/three/assets.ts +24 -0
- package/client/three/budgets.ts +27 -0
- package/client/three/holds.ts +64 -0
- package/client/three/internal.ts +54 -0
- package/client/three/motion-limits.json +116 -0
- package/client/three/sequences.ts +281 -0
- package/client/three/tanya.ts +69 -0
- package/client/three/tara-rig.ts +1562 -0
- package/client/three/tara.ts +76 -0
- package/client/three/tushar.ts +65 -0
- package/client/types.ts +235 -0
- package/client/useAvatar.ts +74 -0
- package/client/vikram.ts +26 -0
- package/dist/Avatar.d.ts +44 -0
- package/dist/Avatar.d.ts.map +1 -0
- package/{client/dist → dist}/Avatar.js +2 -2
- package/dist/Avatar.js.map +1 -0
- package/dist/AvatarClient.d.ts +274 -0
- package/dist/AvatarClient.d.ts.map +1 -0
- package/dist/AvatarClient.js +712 -0
- package/dist/AvatarClient.js.map +1 -0
- package/dist/arjun.d.ts +7 -0
- package/dist/arjun.d.ts.map +1 -0
- package/dist/arjun.js +20 -0
- package/dist/arjun.js.map +1 -0
- package/dist/createAvatar.d.ts +135 -0
- package/dist/createAvatar.d.ts.map +1 -0
- package/dist/createAvatar.js +75 -0
- package/dist/createAvatar.js.map +1 -0
- package/dist/createCanvasAvatar.d.ts +22 -0
- package/dist/createCanvasAvatar.d.ts.map +1 -0
- package/dist/createCanvasAvatar.js +47 -0
- package/dist/createCanvasAvatar.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +34 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +41 -0
- package/dist/internal.js.map +1 -0
- package/dist/interviewer-female.d.ts +4 -0
- package/dist/interviewer-female.d.ts.map +1 -0
- package/dist/interviewer-female.js +3 -0
- package/dist/interviewer-female.js.map +1 -0
- package/dist/interviewer-male.d.ts +4 -0
- package/dist/interviewer-male.d.ts.map +1 -0
- package/dist/interviewer-male.js +3 -0
- package/dist/interviewer-male.js.map +1 -0
- package/dist/ishita.d.ts +7 -0
- package/dist/ishita.d.ts.map +1 -0
- package/dist/ishita.js +20 -0
- package/dist/ishita.js.map +1 -0
- package/dist/kabir.d.ts +7 -0
- package/dist/kabir.d.ts.map +1 -0
- package/dist/kabir.js +20 -0
- package/dist/kabir.js.map +1 -0
- package/dist/meera.d.ts +7 -0
- package/dist/meera.d.ts.map +1 -0
- package/dist/meera.js +20 -0
- package/dist/meera.js.map +1 -0
- package/dist/naina.d.ts +7 -0
- package/dist/naina.d.ts.map +1 -0
- package/dist/naina.js +20 -0
- package/dist/naina.js.map +1 -0
- package/dist/playout.d.ts +42 -0
- package/dist/playout.d.ts.map +1 -0
- package/dist/playout.js +85 -0
- package/dist/playout.js.map +1 -0
- package/dist/professional-female-a.d.ts +4 -0
- package/dist/professional-female-a.d.ts.map +1 -0
- package/dist/professional-female-a.js +3 -0
- package/dist/professional-female-a.js.map +1 -0
- package/dist/professional-female-b.d.ts +4 -0
- package/dist/professional-female-b.d.ts.map +1 -0
- package/dist/professional-female-b.js +3 -0
- package/dist/professional-female-b.js.map +1 -0
- package/dist/professional-male-a.d.ts +4 -0
- package/dist/professional-male-a.d.ts.map +1 -0
- package/dist/professional-male-a.js +3 -0
- package/dist/professional-male-a.js.map +1 -0
- package/dist/professional-male-b.d.ts +4 -0
- package/dist/professional-male-b.d.ts.map +1 -0
- package/dist/professional-male-b.js +3 -0
- package/dist/professional-male-b.js.map +1 -0
- package/dist/react.d.ts +13 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +13 -0
- package/dist/react.js.map +1 -0
- package/dist/supports.d.ts +20 -0
- package/dist/supports.d.ts.map +1 -0
- package/dist/supports.js +26 -0
- package/dist/supports.js.map +1 -0
- package/dist/three/assets.d.ts +24 -0
- package/dist/three/assets.d.ts.map +1 -0
- package/dist/three/assets.js +22 -0
- package/dist/three/assets.js.map +1 -0
- package/dist/three/budgets.d.ts +17 -0
- package/dist/three/budgets.d.ts.map +1 -0
- package/dist/three/budgets.js +21 -0
- package/dist/three/budgets.js.map +1 -0
- package/dist/three/holds.d.ts +33 -0
- package/dist/three/holds.d.ts.map +1 -0
- package/dist/three/holds.js +56 -0
- package/dist/three/holds.js.map +1 -0
- package/dist/three/internal.d.ts +52 -0
- package/dist/three/internal.d.ts.map +1 -0
- package/dist/three/internal.js +49 -0
- package/dist/three/internal.js.map +1 -0
- package/dist/three/motion-limits.json +116 -0
- package/dist/three/sequences.d.ts +71 -0
- package/dist/three/sequences.d.ts.map +1 -0
- package/dist/three/sequences.js +262 -0
- package/dist/three/sequences.js.map +1 -0
- package/dist/three/tanya.d.ts +35 -0
- package/dist/three/tanya.d.ts.map +1 -0
- package/dist/three/tanya.js +51 -0
- package/dist/three/tanya.js.map +1 -0
- package/dist/three/tara-rig.d.ts +403 -0
- package/dist/three/tara-rig.d.ts.map +1 -0
- package/dist/three/tara-rig.js +1500 -0
- package/dist/three/tara-rig.js.map +1 -0
- package/dist/three/tara.d.ts +35 -0
- package/dist/three/tara.d.ts.map +1 -0
- package/dist/three/tara.js +58 -0
- package/dist/three/tara.js.map +1 -0
- package/dist/three/tushar.d.ts +31 -0
- package/dist/three/tushar.d.ts.map +1 -0
- package/dist/three/tushar.js +47 -0
- package/dist/three/tushar.js.map +1 -0
- package/dist/types.d.ts +149 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +140 -0
- package/dist/types.js.map +1 -0
- package/dist/useAvatar.d.ts +39 -0
- package/dist/useAvatar.d.ts.map +1 -0
- package/dist/useAvatar.js +35 -0
- package/dist/useAvatar.js.map +1 -0
- package/dist/vikram.d.ts +7 -0
- package/dist/vikram.d.ts.map +1 -0
- package/dist/vikram.js +20 -0
- package/dist/vikram.js.map +1 -0
- package/package.json +110 -25
- package/src/avatar.d.ts +218 -120
- package/src/avatar.js +950 -213
- package/src/behavior.d.ts +42 -0
- package/src/behavior.js +114 -0
- package/src/camera.js +29 -0
- package/src/canvas/author/parts/eye.mjs +722 -0
- package/src/canvas/author/parts/hand.mjs +1156 -0
- package/src/canvas/author/parts/mouth.mjs +741 -0
- package/src/canvas/author/parts/nose.mjs +100 -0
- package/src/canvas/author/parts/skin-detail.mjs +67 -0
- package/src/canvas/author/path.mjs +283 -0
- package/src/canvas/author/rig.mjs +405 -0
- package/src/canvas/avatars/round/face.d.mts +3 -0
- package/src/canvas/avatars/round/face.mjs +1307 -0
- package/src/canvas/create-rig.d.ts +15 -0
- package/src/canvas/create-rig.js +100 -0
- package/src/canvas/data/img/professional-female-a-hair-back.webp +0 -0
- package/src/canvas/data/img/professional-female-a-hair-front.webp +0 -0
- package/src/canvas/data/img/professional-female-a-top-body.webp +0 -0
- package/src/canvas/data/img/professional-female-b-hair-back.webp +0 -0
- package/src/canvas/data/img/professional-female-b-hair-front.webp +0 -0
- package/src/canvas/data/img/professional-female-b-top-body.webp +0 -0
- package/src/canvas/data/img/professional-male-a-hair-back.webp +0 -0
- package/src/canvas/data/img/professional-male-a-hair-front.webp +0 -0
- package/src/canvas/data/img/professional-male-a-top-body.webp +0 -0
- package/src/canvas/data/img/professional-male-b-hair-back.webp +0 -0
- package/src/canvas/data/img/professional-male-b-hair-front.webp +0 -0
- package/src/canvas/data/img/professional-male-b-top-body.webp +0 -0
- package/src/canvas/data/img/round-m3-hair-back.webp +0 -0
- package/src/canvas/data/img/round-m3-hair-front.webp +0 -0
- package/src/canvas/data/img/round-m3-top-body.webp +0 -0
- package/src/canvas/data/img/round-w1-hair-back.webp +0 -0
- package/src/canvas/data/img/round-w1-hair-front.webp +0 -0
- package/src/canvas/data/img/round-w1-top-body.webp +0 -0
- package/src/canvas/data/interviewer-female.rig.json +1 -0
- package/src/canvas/data/interviewer-male.rig.json +1 -0
- package/src/canvas/data/professional-female-a.rig.json +1 -0
- package/src/canvas/data/professional-female-b.rig.json +1 -0
- package/src/canvas/data/professional-male-a.rig.json +1 -0
- package/src/canvas/data/professional-male-b.rig.json +1 -0
- package/src/canvas/src/live.js +508 -0
- package/src/canvas/src/render2d.js +218 -0
- package/src/canvas/src/rig.js +297 -0
- package/src/canvas/src/vocab.js +96 -0
- package/src/clips.js +18 -7
- package/src/conformance.js +119 -0
- package/src/emotions.js +8 -5
- package/src/face-core.js +27 -1
- package/src/face-myna.d.ts +7 -0
- package/src/face-myna.js +192 -131
- package/src/face-peep-control-plane.js +167 -0
- package/src/face-peep.d.ts +7 -0
- package/src/face-peep.js +274 -116
- package/src/face-wren.d.ts +7 -0
- package/src/face-wren.js +17 -19
- package/src/faces.d.ts +12 -0
- package/src/faces.js +53 -0
- package/src/gaze.js +434 -58
- package/src/hand.js +120 -91
- package/src/head.js +134 -0
- package/src/idle.js +227 -119
- package/src/interjections.js +200 -31
- package/src/params.js +8 -4
- package/src/perform.js +7 -9
- package/src/prosody.js +647 -0
- package/src/rig.d.ts +24 -0
- package/src/rig.js +32 -0
- package/src/speech-timing.js +23 -0
- package/src/visemes.js +110 -24
- package/client/dist/Avatar.d.ts +0 -27
- package/client/dist/Avatar.d.ts.map +0 -1
- package/client/dist/Avatar.js.map +0 -1
- package/client/dist/AvatarClient.d.ts +0 -139
- package/client/dist/AvatarClient.d.ts.map +0 -1
- package/client/dist/AvatarClient.js +0 -223
- package/client/dist/AvatarClient.js.map +0 -1
- package/client/dist/index.d.ts +0 -22
- package/client/dist/index.d.ts.map +0 -1
- package/client/dist/index.js +0 -22
- package/client/dist/index.js.map +0 -1
- package/client/dist/types.d.ts +0 -86
- package/client/dist/types.d.ts.map +0 -1
- package/client/dist/types.js +0 -31
- package/client/dist/types.js.map +0 -1
- package/client/dist/useAvatar.d.ts +0 -42
- package/client/dist/useAvatar.d.ts.map +0 -1
- package/client/dist/useAvatar.js +0 -51
- package/client/dist/useAvatar.js.map +0 -1
- package/client/src/Avatar.tsx +0 -33
- package/client/src/AvatarClient.ts +0 -270
- package/client/src/index.ts +0 -22
- package/client/src/types.ts +0 -104
- package/client/src/useAvatar.ts +0 -85
- package/docs/contract-avatar.md +0 -371
- 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;
|