agent-robot-avatar 0.3.1 → 0.5.2
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/CHANGELOG.md +170 -111
- package/LICENSE +21 -21
- package/README.md +186 -446
- package/agent-robot-avatar.js +28 -25
- package/docs/README.de.md +186 -166
- package/docs/README.es.md +186 -225
- package/docs/README.fr.md +186 -225
- package/docs/README.ja.md +186 -225
- package/docs/README.ko.md +186 -225
- package/docs/README.pt.md +186 -225
- package/docs/README.ru.md +186 -0
- package/docs/README.zh-CN.md +190 -396
- package/docs/README.zh-TW.md +186 -227
- package/index.d.ts +128 -108
- package/package.json +77 -77
- package/src/agent-robot-avatar-actions.js +400 -400
- package/src/agent-robot-avatar-antenna-flash.js +127 -127
- package/src/agent-robot-avatar-antenna.js +152 -152
- package/src/agent-robot-avatar-core.js +1155 -1135
- package/src/agent-robot-avatar-extension-host.js +78 -78
- package/src/agent-robot-avatar-failure.js +131 -131
- package/src/agent-robot-avatar-geometry.js +40 -40
- package/src/agent-robot-avatar-gestures.js +161 -0
- package/src/agent-robot-avatar-head-roundness.js +103 -103
- package/src/agent-robot-avatar-inspect.js +242 -242
- package/src/agent-robot-avatar-love.js +226 -0
- package/src/agent-robot-avatar-random.js +272 -0
- package/src/agent-robot-avatar-runtime.js +905 -875
- package/src/agent-robot-avatar-version.js +3 -3
- package/src/agent-robot-avatar-waiting.js +130 -121
- package/src/agent-robot-avatar-wrap.js +66 -0
|
@@ -1,400 +1,400 @@
|
|
|
1
|
-
import AgentRobotAvatar, { registerAvatarExtension } from './agent-robot-avatar-extension-host.js';
|
|
2
|
-
|
|
3
|
-
if (!AgentRobotAvatar) {
|
|
4
|
-
throw new Error('Agent Robot Avatar core failed to load.');
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
const proto = AgentRobotAvatar.prototype;
|
|
8
|
-
const baseUpdateLook = proto._updateLook;
|
|
9
|
-
const baseOnPointerMove = proto._onPointerMove;
|
|
10
|
-
|
|
11
|
-
const clamp01 = value => Math.max(0, Math.min(1, value));
|
|
12
|
-
const lerp = (a, b, t) => a + (b - a) * t;
|
|
13
|
-
const outQuint = t => 1 - Math.pow(1 - t, 5);
|
|
14
|
-
const smooth = t => {
|
|
15
|
-
t = clamp01(t);
|
|
16
|
-
return t * t * (3 - 2 * t);
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const WARNING_TIMING = Object.freeze({
|
|
20
|
-
W1: 480,
|
|
21
|
-
W2: 400,
|
|
22
|
-
W4: 600,
|
|
23
|
-
W5: 200,
|
|
24
|
-
W5P: 300,
|
|
25
|
-
W6: 200,
|
|
26
|
-
W6P: 300,
|
|
27
|
-
W7: 100,
|
|
28
|
-
W8: 800,
|
|
29
|
-
W9: 227,
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
function buildWarningPhases() {
|
|
33
|
-
let cursor = 0;
|
|
34
|
-
const phases = {};
|
|
35
|
-
for (const key of ['W2', 'W4', 'W5', 'W5P', 'W6', 'W6P', 'W7', 'W8', 'W9']) {
|
|
36
|
-
phases[`${key.toLowerCase()}Start`] = cursor;
|
|
37
|
-
cursor += WARNING_TIMING[key];
|
|
38
|
-
phases[`${key.toLowerCase()}End`] = cursor;
|
|
39
|
-
}
|
|
40
|
-
phases.duration = cursor;
|
|
41
|
-
return Object.freeze(phases);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const WARNING_PHASES = buildWarningPhases();
|
|
45
|
-
|
|
46
|
-
function segment(t, a, b, from, to) {
|
|
47
|
-
if (t <= a) return from;
|
|
48
|
-
if (t >= b) return to;
|
|
49
|
-
return lerp(from, to, smooth((t - a) / (b - a)));
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function errorEyeSweep(t) {
|
|
53
|
-
if (t < 0.16) return segment(t, 0, 0.16, 0, -1);
|
|
54
|
-
if (t < 0.40) return segment(t, 0.16, 0.40, -1, 1);
|
|
55
|
-
if (t < 0.64) return segment(t, 0.40, 0.64, 1, -0.88);
|
|
56
|
-
if (t < 0.84) return segment(t, 0.64, 0.84, -0.88, 0.58);
|
|
57
|
-
return segment(t, 0.84, 1, 0.58, 0);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function dispatchActionState(instance, state) {
|
|
61
|
-
instance.dispatchEvent(new CustomEvent('face-state', { detail: { state } }));
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function centerHeadTransform(instance) {
|
|
65
|
-
if (!instance._headMotion) return;
|
|
66
|
-
instance._headMotion.style.transformBox = 'view-box';
|
|
67
|
-
instance._headMotion.style.transformOrigin = '120px 120px';
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function cancelCustomMotion(instance) {
|
|
71
|
-
instance._systemErrorShake = null;
|
|
72
|
-
instance._warningFx = null;
|
|
73
|
-
instance._look.x = 0;
|
|
74
|
-
instance._look.y = 0;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function interruptRunningAction(instance) {
|
|
78
|
-
++instance._transitionToken;
|
|
79
|
-
instance._inputWanted = false;
|
|
80
|
-
instance._boredRoutine = null;
|
|
81
|
-
instance._boredLookSpeed = null;
|
|
82
|
-
instance._eyeNod = null;
|
|
83
|
-
instance._eyeBob = null;
|
|
84
|
-
instance._angryEyeDrop = null;
|
|
85
|
-
instance._surpriseShake = null;
|
|
86
|
-
cancelCustomMotion(instance);
|
|
87
|
-
if (instance._headMotion?.getAnimations) {
|
|
88
|
-
instance._headMotion.getAnimations().forEach(animation => animation.cancel());
|
|
89
|
-
}
|
|
90
|
-
if (instance._headMotion) instance._headMotion.style.transform = '';
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function playBuiltInAction(action) {
|
|
94
|
-
if (action === 'idle') return this.reset();
|
|
95
|
-
if (action === 'input' && (this._inputWanted || this._state === 'input')) return this;
|
|
96
|
-
if (action === 'wake' && !this._sleeping && this._state !== 'sleep') return this;
|
|
97
|
-
if (action === 'sleep' && this._sleeping) return this;
|
|
98
|
-
|
|
99
|
-
interruptRunningAction(this);
|
|
100
|
-
|
|
101
|
-
if (action === 'warning') return this.warning();
|
|
102
|
-
if (action === 'system-error' || action === 'connection-error') return this.error();
|
|
103
|
-
if (action === 'blocked' || action === 'policy-blocked') return this.angry();
|
|
104
|
-
return this[action]();
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const BUILT_IN_ACTIONS = [
|
|
108
|
-
'idle',
|
|
109
|
-
'bored',
|
|
110
|
-
'input',
|
|
111
|
-
'send',
|
|
112
|
-
'success',
|
|
113
|
-
'warning',
|
|
114
|
-
'error',
|
|
115
|
-
'system-error',
|
|
116
|
-
'connection-error',
|
|
117
|
-
'angry',
|
|
118
|
-
'blocked',
|
|
119
|
-
'policy-blocked',
|
|
120
|
-
'surprise',
|
|
121
|
-
'sleep',
|
|
122
|
-
'wake',
|
|
123
|
-
];
|
|
124
|
-
|
|
125
|
-
const actionHandlers = Object.fromEntries(BUILT_IN_ACTIONS.map(action => [
|
|
126
|
-
action,
|
|
127
|
-
function() { return playBuiltInAction.call(this, action); },
|
|
128
|
-
]));
|
|
129
|
-
|
|
130
|
-
proto.setPointerFollow = function(enabled = true) {
|
|
131
|
-
this._pointerFollowEnabled = enabled !== false;
|
|
132
|
-
if (!this._pointerFollowEnabled) {
|
|
133
|
-
this._pointer.active = false;
|
|
134
|
-
this._pointer.influence = 0;
|
|
135
|
-
this._pointer.lastMove = 0;
|
|
136
|
-
this._headCenteringUntil = performance.now() + 320;
|
|
137
|
-
}
|
|
138
|
-
return this;
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
proto._onPointerMove = function(event) {
|
|
142
|
-
if (this._pointerFollowEnabled === false) return;
|
|
143
|
-
return baseOnPointerMove.call(this, event);
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
proto._returnToIdle = async function(duration = 320, ease = outQuint) {
|
|
147
|
-
const token = this._transitionToken;
|
|
148
|
-
this.setState('idle', { duration, ease, keepGazeLock: true });
|
|
149
|
-
this._headCenteringUntil = Math.max(this._headCenteringUntil || 0, performance.now() + duration);
|
|
150
|
-
await this._wait(duration);
|
|
151
|
-
if (token === this._transitionToken && this._state === 'idle') this._releaseExpressionLock();
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
proto.send = async function() {
|
|
155
|
-
this.noteActivity();
|
|
156
|
-
this._inputWanted = false;
|
|
157
|
-
if (!(await this._prepareExpression({ normalizePose: true, duration: 180, pause: 320 }))) return;
|
|
158
|
-
const token = this._transitionToken;
|
|
159
|
-
this._eyeNod = { start: performance.now(), duration: 760 };
|
|
160
|
-
this._animateHead([
|
|
161
|
-
{ transform: 'translateY(0px)' },
|
|
162
|
-
{ transform: 'translateY(5px)', offset: .52 },
|
|
163
|
-
{ transform: 'translateY(0px)' }
|
|
164
|
-
], 920);
|
|
165
|
-
await this._wait(860);
|
|
166
|
-
if (token !== this._transitionToken) return;
|
|
167
|
-
this._eyeNod = null;
|
|
168
|
-
this._releaseExpressionLock();
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
proto.wake = async function() {
|
|
172
|
-
if (!this._sleeping && this._state !== 'sleep') return;
|
|
173
|
-
this._resumeFrames?.();
|
|
174
|
-
const token = ++this._transitionToken;
|
|
175
|
-
this._expressionLock = true;
|
|
176
|
-
this._sleeping = false;
|
|
177
|
-
this._animateHead([
|
|
178
|
-
{ transform: 'translateY(4px) scale(1)' },
|
|
179
|
-
{ transform: 'translateY(-2px) scale(1.015)', offset: .55 },
|
|
180
|
-
{ transform: 'translateY(0) scale(1)' }
|
|
181
|
-
], 500);
|
|
182
|
-
this.setState('sleepy', { duration: 240, keepGazeLock: true });
|
|
183
|
-
await this._wait(260);
|
|
184
|
-
if (token !== this._transitionToken) return;
|
|
185
|
-
this.setState('surprise', { duration: 120, keepGazeLock: true });
|
|
186
|
-
await this._wait(190);
|
|
187
|
-
if (token !== this._transitionToken) return;
|
|
188
|
-
await this._returnToIdle(300);
|
|
189
|
-
if (token !== this._transitionToken) return;
|
|
190
|
-
this._lastActivity = performance.now();
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
proto.error = async function() {
|
|
194
|
-
this.noteActivity();
|
|
195
|
-
this._inputWanted = false;
|
|
196
|
-
cancelCustomMotion(this);
|
|
197
|
-
if (!(await this._prepareExpression({ normalizePose: true, duration: 180, pause: 240 }))) return;
|
|
198
|
-
|
|
199
|
-
const token = this._transitionToken;
|
|
200
|
-
const eyeDuration = 980;
|
|
201
|
-
const headDuration = 1380;
|
|
202
|
-
this._systemErrorShake = { start: performance.now(), duration: eyeDuration };
|
|
203
|
-
dispatchActionState(this, 'error');
|
|
204
|
-
|
|
205
|
-
centerHeadTransform(this);
|
|
206
|
-
if (this._headMotion?.animate) {
|
|
207
|
-
const animationToken = this._transitionToken;
|
|
208
|
-
const anim = this._headMotion.animate([
|
|
209
|
-
{ transform: 'translateX(0px)', offset: 0, easing: 'cubic-bezier(.38,0,.28,1)' },
|
|
210
|
-
{ transform: 'translateX(-4px)', offset: 0.34, easing: 'cubic-bezier(.35,0,.22,1)' },
|
|
211
|
-
{ transform: 'translateX(4px)', offset: 0.72, easing: 'cubic-bezier(.35,0,.22,1)' },
|
|
212
|
-
{ transform: 'translateX(0px)', offset: 1 }
|
|
213
|
-
], { duration: headDuration, easing: 'linear', fill: 'forwards' });
|
|
214
|
-
anim.onfinish = () => {
|
|
215
|
-
if (animationToken === this._transitionToken) this._headMotion.style.transform = 'translateX(0px)';
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
await this._wait(headDuration + 40);
|
|
220
|
-
if (token !== this._transitionToken) return;
|
|
221
|
-
this._systemErrorShake = null;
|
|
222
|
-
this._look.x = 0;
|
|
223
|
-
this._look.y = 0;
|
|
224
|
-
this._releaseExpressionLock();
|
|
225
|
-
dispatchActionState(this, 'idle');
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
proto.warning = async function() {
|
|
229
|
-
this.noteActivity();
|
|
230
|
-
this._inputWanted = false;
|
|
231
|
-
cancelCustomMotion(this);
|
|
232
|
-
if (!(await this._prepareExpression({ normalizePose: true, duration: 200, pause: WARNING_TIMING.W1 - 200 }))) return;
|
|
233
|
-
|
|
234
|
-
const token = this._transitionToken;
|
|
235
|
-
const duration = WARNING_PHASES.duration;
|
|
236
|
-
this._warningFx = { start: performance.now(), duration };
|
|
237
|
-
dispatchActionState(this, 'warning');
|
|
238
|
-
|
|
239
|
-
centerHeadTransform(this);
|
|
240
|
-
if (this._headMotion?.animate) {
|
|
241
|
-
const animationToken = this._transitionToken;
|
|
242
|
-
const anim = this._headMotion.animate([
|
|
243
|
-
{ transform: 'translateY(0px)', offset: 0 },
|
|
244
|
-
{ transform: 'translateY(0px)', offset: WARNING_PHASES.w5Start / duration, easing: 'cubic-bezier(.38,0,.25,1)' },
|
|
245
|
-
{ transform: 'translateY(5px)', offset: WARNING_PHASES.w5End / duration, easing: 'cubic-bezier(.35,0,.22,1)' },
|
|
246
|
-
{ transform: 'translateY(5px)', offset: WARNING_PHASES.w5pEnd / duration, easing: 'cubic-bezier(.35,0,.22,1)' },
|
|
247
|
-
{ transform: 'translateY(-4px)', offset: WARNING_PHASES.w6End / duration },
|
|
248
|
-
{ transform: 'translateY(-4px)', offset: WARNING_PHASES.w6pEnd / duration, easing: 'cubic-bezier(.35,0,.22,1)' },
|
|
249
|
-
{ transform: 'translateY(0px)', offset: WARNING_PHASES.w7End / duration },
|
|
250
|
-
{ transform: 'translateY(0px)', offset: 1 }
|
|
251
|
-
], { duration, easing: 'linear', fill: 'forwards' });
|
|
252
|
-
anim.onfinish = () => {
|
|
253
|
-
if (animationToken === this._transitionToken) this._headMotion.style.transform = 'translateY(0px)';
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
await this._wait(duration + 40);
|
|
258
|
-
if (token !== this._transitionToken) return;
|
|
259
|
-
this._warningFx = null;
|
|
260
|
-
this._look.x = 0;
|
|
261
|
-
this._look.y = 0;
|
|
262
|
-
this._releaseExpressionLock();
|
|
263
|
-
dispatchActionState(this, 'idle');
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
proto._startBoredRoutine = function(now) {
|
|
267
|
-
this._boredRoutine = {
|
|
268
|
-
index: 0,
|
|
269
|
-
nextAt: now,
|
|
270
|
-
steps: [
|
|
271
|
-
{ x: -18, y: -16, hold: 1400, lookSpeed: 0.5 },
|
|
272
|
-
{ x: 18, y: -16, hold: 1400, lookSpeed: 0.5 },
|
|
273
|
-
{ x: 0, y: 0, hold: 320, lookSpeed: 0.5 },
|
|
274
|
-
]
|
|
275
|
-
};
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
proto._updateLook = function(now, dt) {
|
|
279
|
-
baseUpdateLook.call(this, now, dt);
|
|
280
|
-
|
|
281
|
-
if (this._systemErrorShake) {
|
|
282
|
-
const t = clamp01((now - this._systemErrorShake.start) / this._systemErrorShake.duration);
|
|
283
|
-
this._look.x = errorEyeSweep(t) * 30;
|
|
284
|
-
this._look.y = 0;
|
|
285
|
-
if (t >= 1) this._systemErrorShake = null;
|
|
286
|
-
}
|
|
287
|
-
};
|
|
288
|
-
|
|
289
|
-
function drawWarning(now) {
|
|
290
|
-
const fx = this._warningFx;
|
|
291
|
-
if (!fx) return;
|
|
292
|
-
|
|
293
|
-
const elapsed = Math.max(0, Math.min(fx.duration, now - fx.start));
|
|
294
|
-
|
|
295
|
-
let proximity = elapsed < WARNING_PHASES.w2End
|
|
296
|
-
? smooth(elapsed / WARNING_TIMING.W2)
|
|
297
|
-
: 1;
|
|
298
|
-
|
|
299
|
-
let lids = elapsed < WARNING_PHASES.w2End
|
|
300
|
-
? smooth(elapsed / WARNING_TIMING.W2)
|
|
301
|
-
: 1;
|
|
302
|
-
|
|
303
|
-
const release = elapsed > WARNING_PHASES.w8End
|
|
304
|
-
? 1 - smooth((elapsed - WARNING_PHASES.w8End) / WARNING_TIMING.W9)
|
|
305
|
-
: 1;
|
|
306
|
-
proximity *= release;
|
|
307
|
-
lids *= release;
|
|
308
|
-
|
|
309
|
-
let lidShift = 0;
|
|
310
|
-
const lightPress = 4.2;
|
|
311
|
-
const w4PressEnd = Math.min(WARNING_PHASES.w4Start + 120, WARNING_PHASES.w4End);
|
|
312
|
-
if (elapsed >= WARNING_PHASES.w4Start && elapsed < WARNING_PHASES.w4End) {
|
|
313
|
-
lidShift = elapsed < w4PressEnd
|
|
314
|
-
? segment(elapsed, WARNING_PHASES.w4Start, w4PressEnd, 0, lightPress)
|
|
315
|
-
: lightPress;
|
|
316
|
-
} else if (elapsed >= WARNING_PHASES.w5Start && elapsed < WARNING_PHASES.w5End) {
|
|
317
|
-
lidShift = segment(elapsed, WARNING_PHASES.w5Start, WARNING_PHASES.w5End, lightPress, 20.5);
|
|
318
|
-
} else if (elapsed >= WARNING_PHASES.w5pStart && elapsed < WARNING_PHASES.w5pEnd) {
|
|
319
|
-
lidShift = 20.5;
|
|
320
|
-
} else if (elapsed >= WARNING_PHASES.w6Start && elapsed < WARNING_PHASES.w6End) {
|
|
321
|
-
lidShift = segment(elapsed, WARNING_PHASES.w6Start, WARNING_PHASES.w6End, 20.5, -6.5);
|
|
322
|
-
} else if (elapsed >= WARNING_PHASES.w6pStart && elapsed < WARNING_PHASES.w6pEnd) {
|
|
323
|
-
lidShift = -6.5;
|
|
324
|
-
} else if (elapsed >= WARNING_PHASES.w7Start && elapsed < WARNING_PHASES.w7End) {
|
|
325
|
-
lidShift = segment(elapsed, WARNING_PHASES.w7Start, WARNING_PHASES.w7End, -6.5, lightPress);
|
|
326
|
-
} else if (elapsed >= WARNING_PHASES.w8Start && elapsed < WARNING_PHASES.w8End) {
|
|
327
|
-
lidShift = lightPress;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
let farLowOffset = 0;
|
|
331
|
-
if (elapsed >= WARNING_PHASES.w5Start && elapsed < WARNING_PHASES.w5End) {
|
|
332
|
-
farLowOffset = segment(elapsed, WARNING_PHASES.w5Start, WARNING_PHASES.w5End, 0, 2.4);
|
|
333
|
-
} else if (elapsed >= WARNING_PHASES.w5pStart && elapsed < WARNING_PHASES.w5pEnd) {
|
|
334
|
-
farLowOffset = 2.4;
|
|
335
|
-
} else if (elapsed >= WARNING_PHASES.w6Start && elapsed < WARNING_PHASES.w6End) {
|
|
336
|
-
farLowOffset = segment(elapsed, WARNING_PHASES.w6Start, WARNING_PHASES.w6End, 2.4, 0);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
let farDeepLidDrop = 0;
|
|
340
|
-
if (elapsed >= WARNING_PHASES.w5Start && elapsed < WARNING_PHASES.w5End) {
|
|
341
|
-
farDeepLidDrop = segment(elapsed, WARNING_PHASES.w5Start, WARNING_PHASES.w5End, 0, 3.5);
|
|
342
|
-
} else if (elapsed >= WARNING_PHASES.w5pStart && elapsed < WARNING_PHASES.w5pEnd) {
|
|
343
|
-
farDeepLidDrop = 3.5;
|
|
344
|
-
} else if (elapsed >= WARNING_PHASES.w6Start && elapsed < WARNING_PHASES.w6End) {
|
|
345
|
-
farDeepLidDrop = segment(elapsed, WARNING_PHASES.w6Start, WARNING_PHASES.w6End, 3.5, 0);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const nearSX = lerp(1, 1.48, proximity);
|
|
349
|
-
const nearSY = lerp(1, 1.40, proximity);
|
|
350
|
-
const farSX = lerp(1, 0.78, proximity);
|
|
351
|
-
const farSY = lerp(1, 0.97, proximity);
|
|
352
|
-
|
|
353
|
-
const nearX = lerp(86, 102.0, proximity);
|
|
354
|
-
const farX = lerp(154, 178.5, proximity);
|
|
355
|
-
const nearY = lerp(126, 125.5, proximity);
|
|
356
|
-
const farY = lerp(126, 134.0, proximity) + farLowOffset;
|
|
357
|
-
|
|
358
|
-
this._leftEye.setAttribute(
|
|
359
|
-
'transform',
|
|
360
|
-
`translate(${nearX.toFixed(2)} ${nearY.toFixed(2)}) scale(${nearSX.toFixed(3)} ${nearSY.toFixed(3)})`
|
|
361
|
-
);
|
|
362
|
-
this._rightEye.setAttribute(
|
|
363
|
-
'transform',
|
|
364
|
-
`translate(${farX.toFixed(2)} ${farY.toFixed(2)}) scale(${farSX.toFixed(3)} ${farSY.toFixed(3)})`
|
|
365
|
-
);
|
|
366
|
-
|
|
367
|
-
const nearTopY = lerp(-36, -10.0, lids) + lidShift;
|
|
368
|
-
const farTopY = lerp(-36, -18.0, lids) + lidShift * 0.95 + farDeepLidDrop;
|
|
369
|
-
const nearTopRot = lerp(0, 10.0, lids);
|
|
370
|
-
const farTopRot = lerp(0, -7.5, lids);
|
|
371
|
-
|
|
372
|
-
this._leftTop.setAttribute('y', (nearTopY - 90).toFixed(2));
|
|
373
|
-
this._leftTop.setAttribute('height', '90');
|
|
374
|
-
this._leftTop.setAttribute('transform', `rotate(${nearTopRot.toFixed(2)} 0 ${nearTopY.toFixed(2)})`);
|
|
375
|
-
|
|
376
|
-
this._rightTop.setAttribute('y', (farTopY - 90).toFixed(2));
|
|
377
|
-
this._rightTop.setAttribute('height', '90');
|
|
378
|
-
this._rightTop.setAttribute('transform', `rotate(${farTopRot.toFixed(2)} 0 ${farTopY.toFixed(2)})`);
|
|
379
|
-
|
|
380
|
-
const nearBottomY = lerp(36, 35.3, lids);
|
|
381
|
-
const farBottomY = lerp(36, 35.6, lids);
|
|
382
|
-
this._leftBottom.setAttribute('y', nearBottomY.toFixed(2));
|
|
383
|
-
this._leftBottom.setAttribute('height', '90');
|
|
384
|
-
this._leftBottom.setAttribute('transform', `rotate(0 0 ${nearBottomY.toFixed(2)})`);
|
|
385
|
-
this._rightBottom.setAttribute('y', farBottomY.toFixed(2));
|
|
386
|
-
this._rightBottom.setAttribute('height', '90');
|
|
387
|
-
this._rightBottom.setAttribute('transform', `rotate(0 0 ${farBottomY.toFixed(2)})`);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
registerAvatarExtension({
|
|
391
|
-
name: 'actions',
|
|
392
|
-
actions: actionHandlers,
|
|
393
|
-
reset() {
|
|
394
|
-
interruptRunningAction(this);
|
|
395
|
-
},
|
|
396
|
-
draw: drawWarning,
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
export { AgentRobotAvatar };
|
|
400
|
-
export default AgentRobotAvatar;
|
|
1
|
+
import AgentRobotAvatar, { registerAvatarExtension } from './agent-robot-avatar-extension-host.js';
|
|
2
|
+
|
|
3
|
+
if (!AgentRobotAvatar) {
|
|
4
|
+
throw new Error('Agent Robot Avatar core failed to load.');
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const proto = AgentRobotAvatar.prototype;
|
|
8
|
+
const baseUpdateLook = proto._updateLook;
|
|
9
|
+
const baseOnPointerMove = proto._onPointerMove;
|
|
10
|
+
|
|
11
|
+
const clamp01 = value => Math.max(0, Math.min(1, value));
|
|
12
|
+
const lerp = (a, b, t) => a + (b - a) * t;
|
|
13
|
+
const outQuint = t => 1 - Math.pow(1 - t, 5);
|
|
14
|
+
const smooth = t => {
|
|
15
|
+
t = clamp01(t);
|
|
16
|
+
return t * t * (3 - 2 * t);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const WARNING_TIMING = Object.freeze({
|
|
20
|
+
W1: 480,
|
|
21
|
+
W2: 400,
|
|
22
|
+
W4: 600,
|
|
23
|
+
W5: 200,
|
|
24
|
+
W5P: 300,
|
|
25
|
+
W6: 200,
|
|
26
|
+
W6P: 300,
|
|
27
|
+
W7: 100,
|
|
28
|
+
W8: 800,
|
|
29
|
+
W9: 227,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
function buildWarningPhases() {
|
|
33
|
+
let cursor = 0;
|
|
34
|
+
const phases = {};
|
|
35
|
+
for (const key of ['W2', 'W4', 'W5', 'W5P', 'W6', 'W6P', 'W7', 'W8', 'W9']) {
|
|
36
|
+
phases[`${key.toLowerCase()}Start`] = cursor;
|
|
37
|
+
cursor += WARNING_TIMING[key];
|
|
38
|
+
phases[`${key.toLowerCase()}End`] = cursor;
|
|
39
|
+
}
|
|
40
|
+
phases.duration = cursor;
|
|
41
|
+
return Object.freeze(phases);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const WARNING_PHASES = buildWarningPhases();
|
|
45
|
+
|
|
46
|
+
function segment(t, a, b, from, to) {
|
|
47
|
+
if (t <= a) return from;
|
|
48
|
+
if (t >= b) return to;
|
|
49
|
+
return lerp(from, to, smooth((t - a) / (b - a)));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function errorEyeSweep(t) {
|
|
53
|
+
if (t < 0.16) return segment(t, 0, 0.16, 0, -1);
|
|
54
|
+
if (t < 0.40) return segment(t, 0.16, 0.40, -1, 1);
|
|
55
|
+
if (t < 0.64) return segment(t, 0.40, 0.64, 1, -0.88);
|
|
56
|
+
if (t < 0.84) return segment(t, 0.64, 0.84, -0.88, 0.58);
|
|
57
|
+
return segment(t, 0.84, 1, 0.58, 0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function dispatchActionState(instance, state) {
|
|
61
|
+
instance.dispatchEvent(new CustomEvent('face-state', { detail: { state } }));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function centerHeadTransform(instance) {
|
|
65
|
+
if (!instance._headMotion) return;
|
|
66
|
+
instance._headMotion.style.transformBox = 'view-box';
|
|
67
|
+
instance._headMotion.style.transformOrigin = '120px 120px';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function cancelCustomMotion(instance) {
|
|
71
|
+
instance._systemErrorShake = null;
|
|
72
|
+
instance._warningFx = null;
|
|
73
|
+
instance._look.x = 0;
|
|
74
|
+
instance._look.y = 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function interruptRunningAction(instance) {
|
|
78
|
+
++instance._transitionToken;
|
|
79
|
+
instance._inputWanted = false;
|
|
80
|
+
instance._boredRoutine = null;
|
|
81
|
+
instance._boredLookSpeed = null;
|
|
82
|
+
instance._eyeNod = null;
|
|
83
|
+
instance._eyeBob = null;
|
|
84
|
+
instance._angryEyeDrop = null;
|
|
85
|
+
instance._surpriseShake = null;
|
|
86
|
+
cancelCustomMotion(instance);
|
|
87
|
+
if (instance._headMotion?.getAnimations) {
|
|
88
|
+
instance._headMotion.getAnimations().forEach(animation => animation.cancel());
|
|
89
|
+
}
|
|
90
|
+
if (instance._headMotion) instance._headMotion.style.transform = '';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function playBuiltInAction(action) {
|
|
94
|
+
if (action === 'idle') return this.reset();
|
|
95
|
+
if (action === 'input' && (this._inputWanted || this._state === 'input')) return this;
|
|
96
|
+
if (action === 'wake' && !this._sleeping && this._state !== 'sleep') return this;
|
|
97
|
+
if (action === 'sleep' && this._sleeping) return this;
|
|
98
|
+
|
|
99
|
+
interruptRunningAction(this);
|
|
100
|
+
|
|
101
|
+
if (action === 'warning') return this.warning();
|
|
102
|
+
if (action === 'system-error' || action === 'connection-error') return this.error();
|
|
103
|
+
if (action === 'blocked' || action === 'policy-blocked') return this.angry();
|
|
104
|
+
return this[action]();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const BUILT_IN_ACTIONS = [
|
|
108
|
+
'idle',
|
|
109
|
+
'bored',
|
|
110
|
+
'input',
|
|
111
|
+
'send',
|
|
112
|
+
'success',
|
|
113
|
+
'warning',
|
|
114
|
+
'error',
|
|
115
|
+
'system-error',
|
|
116
|
+
'connection-error',
|
|
117
|
+
'angry',
|
|
118
|
+
'blocked',
|
|
119
|
+
'policy-blocked',
|
|
120
|
+
'surprise',
|
|
121
|
+
'sleep',
|
|
122
|
+
'wake',
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
const actionHandlers = Object.fromEntries(BUILT_IN_ACTIONS.map(action => [
|
|
126
|
+
action,
|
|
127
|
+
function() { return playBuiltInAction.call(this, action); },
|
|
128
|
+
]));
|
|
129
|
+
|
|
130
|
+
proto.setPointerFollow = function(enabled = true) {
|
|
131
|
+
this._pointerFollowEnabled = enabled !== false;
|
|
132
|
+
if (!this._pointerFollowEnabled) {
|
|
133
|
+
this._pointer.active = false;
|
|
134
|
+
this._pointer.influence = 0;
|
|
135
|
+
this._pointer.lastMove = 0;
|
|
136
|
+
this._headCenteringUntil = performance.now() + 320;
|
|
137
|
+
}
|
|
138
|
+
return this;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
proto._onPointerMove = function(event) {
|
|
142
|
+
if (this._pointerFollowEnabled === false) return;
|
|
143
|
+
return baseOnPointerMove.call(this, event);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
proto._returnToIdle = async function(duration = 320, ease = outQuint) {
|
|
147
|
+
const token = this._transitionToken;
|
|
148
|
+
this.setState('idle', { duration, ease, keepGazeLock: true });
|
|
149
|
+
this._headCenteringUntil = Math.max(this._headCenteringUntil || 0, performance.now() + duration);
|
|
150
|
+
await this._wait(duration);
|
|
151
|
+
if (token === this._transitionToken && this._state === 'idle') this._releaseExpressionLock();
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
proto.send = async function() {
|
|
155
|
+
this.noteActivity();
|
|
156
|
+
this._inputWanted = false;
|
|
157
|
+
if (!(await this._prepareExpression({ normalizePose: true, duration: 180, pause: 320 }))) return;
|
|
158
|
+
const token = this._transitionToken;
|
|
159
|
+
this._eyeNod = { start: performance.now(), duration: 760 };
|
|
160
|
+
this._animateHead([
|
|
161
|
+
{ transform: 'translateY(0px)' },
|
|
162
|
+
{ transform: 'translateY(5px)', offset: .52 },
|
|
163
|
+
{ transform: 'translateY(0px)' }
|
|
164
|
+
], 920);
|
|
165
|
+
await this._wait(860);
|
|
166
|
+
if (token !== this._transitionToken) return;
|
|
167
|
+
this._eyeNod = null;
|
|
168
|
+
this._releaseExpressionLock();
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
proto.wake = async function() {
|
|
172
|
+
if (!this._sleeping && this._state !== 'sleep') return;
|
|
173
|
+
this._resumeFrames?.();
|
|
174
|
+
const token = ++this._transitionToken;
|
|
175
|
+
this._expressionLock = true;
|
|
176
|
+
this._sleeping = false;
|
|
177
|
+
this._animateHead([
|
|
178
|
+
{ transform: 'translateY(4px) scale(1)' },
|
|
179
|
+
{ transform: 'translateY(-2px) scale(1.015)', offset: .55 },
|
|
180
|
+
{ transform: 'translateY(0) scale(1)' }
|
|
181
|
+
], 500);
|
|
182
|
+
this.setState('sleepy', { duration: 240, keepGazeLock: true });
|
|
183
|
+
await this._wait(260);
|
|
184
|
+
if (token !== this._transitionToken) return;
|
|
185
|
+
this.setState('surprise', { duration: 120, keepGazeLock: true });
|
|
186
|
+
await this._wait(190);
|
|
187
|
+
if (token !== this._transitionToken) return;
|
|
188
|
+
await this._returnToIdle(300);
|
|
189
|
+
if (token !== this._transitionToken) return;
|
|
190
|
+
this._lastActivity = performance.now();
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
proto.error = async function() {
|
|
194
|
+
this.noteActivity();
|
|
195
|
+
this._inputWanted = false;
|
|
196
|
+
cancelCustomMotion(this);
|
|
197
|
+
if (!(await this._prepareExpression({ normalizePose: true, duration: 180, pause: 240 }))) return;
|
|
198
|
+
|
|
199
|
+
const token = this._transitionToken;
|
|
200
|
+
const eyeDuration = 980;
|
|
201
|
+
const headDuration = 1380;
|
|
202
|
+
this._systemErrorShake = { start: performance.now(), duration: eyeDuration };
|
|
203
|
+
dispatchActionState(this, 'error');
|
|
204
|
+
|
|
205
|
+
centerHeadTransform(this);
|
|
206
|
+
if (this._headMotion?.animate) {
|
|
207
|
+
const animationToken = this._transitionToken;
|
|
208
|
+
const anim = this._headMotion.animate([
|
|
209
|
+
{ transform: 'translateX(0px)', offset: 0, easing: 'cubic-bezier(.38,0,.28,1)' },
|
|
210
|
+
{ transform: 'translateX(-4px)', offset: 0.34, easing: 'cubic-bezier(.35,0,.22,1)' },
|
|
211
|
+
{ transform: 'translateX(4px)', offset: 0.72, easing: 'cubic-bezier(.35,0,.22,1)' },
|
|
212
|
+
{ transform: 'translateX(0px)', offset: 1 }
|
|
213
|
+
], { duration: headDuration, easing: 'linear', fill: 'forwards' });
|
|
214
|
+
anim.onfinish = () => {
|
|
215
|
+
if (animationToken === this._transitionToken) this._headMotion.style.transform = 'translateX(0px)';
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
await this._wait(headDuration + 40);
|
|
220
|
+
if (token !== this._transitionToken) return;
|
|
221
|
+
this._systemErrorShake = null;
|
|
222
|
+
this._look.x = 0;
|
|
223
|
+
this._look.y = 0;
|
|
224
|
+
this._releaseExpressionLock();
|
|
225
|
+
dispatchActionState(this, 'idle');
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
proto.warning = async function() {
|
|
229
|
+
this.noteActivity();
|
|
230
|
+
this._inputWanted = false;
|
|
231
|
+
cancelCustomMotion(this);
|
|
232
|
+
if (!(await this._prepareExpression({ normalizePose: true, duration: 200, pause: WARNING_TIMING.W1 - 200 }))) return;
|
|
233
|
+
|
|
234
|
+
const token = this._transitionToken;
|
|
235
|
+
const duration = WARNING_PHASES.duration;
|
|
236
|
+
this._warningFx = { start: performance.now(), duration };
|
|
237
|
+
dispatchActionState(this, 'warning');
|
|
238
|
+
|
|
239
|
+
centerHeadTransform(this);
|
|
240
|
+
if (this._headMotion?.animate) {
|
|
241
|
+
const animationToken = this._transitionToken;
|
|
242
|
+
const anim = this._headMotion.animate([
|
|
243
|
+
{ transform: 'translateY(0px)', offset: 0 },
|
|
244
|
+
{ transform: 'translateY(0px)', offset: WARNING_PHASES.w5Start / duration, easing: 'cubic-bezier(.38,0,.25,1)' },
|
|
245
|
+
{ transform: 'translateY(5px)', offset: WARNING_PHASES.w5End / duration, easing: 'cubic-bezier(.35,0,.22,1)' },
|
|
246
|
+
{ transform: 'translateY(5px)', offset: WARNING_PHASES.w5pEnd / duration, easing: 'cubic-bezier(.35,0,.22,1)' },
|
|
247
|
+
{ transform: 'translateY(-4px)', offset: WARNING_PHASES.w6End / duration },
|
|
248
|
+
{ transform: 'translateY(-4px)', offset: WARNING_PHASES.w6pEnd / duration, easing: 'cubic-bezier(.35,0,.22,1)' },
|
|
249
|
+
{ transform: 'translateY(0px)', offset: WARNING_PHASES.w7End / duration },
|
|
250
|
+
{ transform: 'translateY(0px)', offset: 1 }
|
|
251
|
+
], { duration, easing: 'linear', fill: 'forwards' });
|
|
252
|
+
anim.onfinish = () => {
|
|
253
|
+
if (animationToken === this._transitionToken) this._headMotion.style.transform = 'translateY(0px)';
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
await this._wait(duration + 40);
|
|
258
|
+
if (token !== this._transitionToken) return;
|
|
259
|
+
this._warningFx = null;
|
|
260
|
+
this._look.x = 0;
|
|
261
|
+
this._look.y = 0;
|
|
262
|
+
this._releaseExpressionLock();
|
|
263
|
+
dispatchActionState(this, 'idle');
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
proto._startBoredRoutine = function(now) {
|
|
267
|
+
this._boredRoutine = {
|
|
268
|
+
index: 0,
|
|
269
|
+
nextAt: now,
|
|
270
|
+
steps: [
|
|
271
|
+
{ x: -18, y: -16, hold: 1400, lookSpeed: 0.5 },
|
|
272
|
+
{ x: 18, y: -16, hold: 1400, lookSpeed: 0.5 },
|
|
273
|
+
{ x: 0, y: 0, hold: 320, lookSpeed: 0.5 },
|
|
274
|
+
]
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
proto._updateLook = function(now, dt) {
|
|
279
|
+
baseUpdateLook.call(this, now, dt);
|
|
280
|
+
|
|
281
|
+
if (this._systemErrorShake) {
|
|
282
|
+
const t = clamp01((now - this._systemErrorShake.start) / this._systemErrorShake.duration);
|
|
283
|
+
this._look.x = errorEyeSweep(t) * 30;
|
|
284
|
+
this._look.y = 0;
|
|
285
|
+
if (t >= 1) this._systemErrorShake = null;
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
function drawWarning(now) {
|
|
290
|
+
const fx = this._warningFx;
|
|
291
|
+
if (!fx) return;
|
|
292
|
+
|
|
293
|
+
const elapsed = Math.max(0, Math.min(fx.duration, now - fx.start));
|
|
294
|
+
|
|
295
|
+
let proximity = elapsed < WARNING_PHASES.w2End
|
|
296
|
+
? smooth(elapsed / WARNING_TIMING.W2)
|
|
297
|
+
: 1;
|
|
298
|
+
|
|
299
|
+
let lids = elapsed < WARNING_PHASES.w2End
|
|
300
|
+
? smooth(elapsed / WARNING_TIMING.W2)
|
|
301
|
+
: 1;
|
|
302
|
+
|
|
303
|
+
const release = elapsed > WARNING_PHASES.w8End
|
|
304
|
+
? 1 - smooth((elapsed - WARNING_PHASES.w8End) / WARNING_TIMING.W9)
|
|
305
|
+
: 1;
|
|
306
|
+
proximity *= release;
|
|
307
|
+
lids *= release;
|
|
308
|
+
|
|
309
|
+
let lidShift = 0;
|
|
310
|
+
const lightPress = 4.2;
|
|
311
|
+
const w4PressEnd = Math.min(WARNING_PHASES.w4Start + 120, WARNING_PHASES.w4End);
|
|
312
|
+
if (elapsed >= WARNING_PHASES.w4Start && elapsed < WARNING_PHASES.w4End) {
|
|
313
|
+
lidShift = elapsed < w4PressEnd
|
|
314
|
+
? segment(elapsed, WARNING_PHASES.w4Start, w4PressEnd, 0, lightPress)
|
|
315
|
+
: lightPress;
|
|
316
|
+
} else if (elapsed >= WARNING_PHASES.w5Start && elapsed < WARNING_PHASES.w5End) {
|
|
317
|
+
lidShift = segment(elapsed, WARNING_PHASES.w5Start, WARNING_PHASES.w5End, lightPress, 20.5);
|
|
318
|
+
} else if (elapsed >= WARNING_PHASES.w5pStart && elapsed < WARNING_PHASES.w5pEnd) {
|
|
319
|
+
lidShift = 20.5;
|
|
320
|
+
} else if (elapsed >= WARNING_PHASES.w6Start && elapsed < WARNING_PHASES.w6End) {
|
|
321
|
+
lidShift = segment(elapsed, WARNING_PHASES.w6Start, WARNING_PHASES.w6End, 20.5, -6.5);
|
|
322
|
+
} else if (elapsed >= WARNING_PHASES.w6pStart && elapsed < WARNING_PHASES.w6pEnd) {
|
|
323
|
+
lidShift = -6.5;
|
|
324
|
+
} else if (elapsed >= WARNING_PHASES.w7Start && elapsed < WARNING_PHASES.w7End) {
|
|
325
|
+
lidShift = segment(elapsed, WARNING_PHASES.w7Start, WARNING_PHASES.w7End, -6.5, lightPress);
|
|
326
|
+
} else if (elapsed >= WARNING_PHASES.w8Start && elapsed < WARNING_PHASES.w8End) {
|
|
327
|
+
lidShift = lightPress;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
let farLowOffset = 0;
|
|
331
|
+
if (elapsed >= WARNING_PHASES.w5Start && elapsed < WARNING_PHASES.w5End) {
|
|
332
|
+
farLowOffset = segment(elapsed, WARNING_PHASES.w5Start, WARNING_PHASES.w5End, 0, 2.4);
|
|
333
|
+
} else if (elapsed >= WARNING_PHASES.w5pStart && elapsed < WARNING_PHASES.w5pEnd) {
|
|
334
|
+
farLowOffset = 2.4;
|
|
335
|
+
} else if (elapsed >= WARNING_PHASES.w6Start && elapsed < WARNING_PHASES.w6End) {
|
|
336
|
+
farLowOffset = segment(elapsed, WARNING_PHASES.w6Start, WARNING_PHASES.w6End, 2.4, 0);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
let farDeepLidDrop = 0;
|
|
340
|
+
if (elapsed >= WARNING_PHASES.w5Start && elapsed < WARNING_PHASES.w5End) {
|
|
341
|
+
farDeepLidDrop = segment(elapsed, WARNING_PHASES.w5Start, WARNING_PHASES.w5End, 0, 3.5);
|
|
342
|
+
} else if (elapsed >= WARNING_PHASES.w5pStart && elapsed < WARNING_PHASES.w5pEnd) {
|
|
343
|
+
farDeepLidDrop = 3.5;
|
|
344
|
+
} else if (elapsed >= WARNING_PHASES.w6Start && elapsed < WARNING_PHASES.w6End) {
|
|
345
|
+
farDeepLidDrop = segment(elapsed, WARNING_PHASES.w6Start, WARNING_PHASES.w6End, 3.5, 0);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const nearSX = lerp(1, 1.48, proximity);
|
|
349
|
+
const nearSY = lerp(1, 1.40, proximity);
|
|
350
|
+
const farSX = lerp(1, 0.78, proximity);
|
|
351
|
+
const farSY = lerp(1, 0.97, proximity);
|
|
352
|
+
|
|
353
|
+
const nearX = lerp(86, 102.0, proximity);
|
|
354
|
+
const farX = lerp(154, 178.5, proximity);
|
|
355
|
+
const nearY = lerp(126, 125.5, proximity);
|
|
356
|
+
const farY = lerp(126, 134.0, proximity) + farLowOffset;
|
|
357
|
+
|
|
358
|
+
this._leftEye.setAttribute(
|
|
359
|
+
'transform',
|
|
360
|
+
`translate(${nearX.toFixed(2)} ${nearY.toFixed(2)}) scale(${nearSX.toFixed(3)} ${nearSY.toFixed(3)})`
|
|
361
|
+
);
|
|
362
|
+
this._rightEye.setAttribute(
|
|
363
|
+
'transform',
|
|
364
|
+
`translate(${farX.toFixed(2)} ${farY.toFixed(2)}) scale(${farSX.toFixed(3)} ${farSY.toFixed(3)})`
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
const nearTopY = lerp(-36, -10.0, lids) + lidShift;
|
|
368
|
+
const farTopY = lerp(-36, -18.0, lids) + lidShift * 0.95 + farDeepLidDrop;
|
|
369
|
+
const nearTopRot = lerp(0, 10.0, lids);
|
|
370
|
+
const farTopRot = lerp(0, -7.5, lids);
|
|
371
|
+
|
|
372
|
+
this._leftTop.setAttribute('y', (nearTopY - 90).toFixed(2));
|
|
373
|
+
this._leftTop.setAttribute('height', '90');
|
|
374
|
+
this._leftTop.setAttribute('transform', `rotate(${nearTopRot.toFixed(2)} 0 ${nearTopY.toFixed(2)})`);
|
|
375
|
+
|
|
376
|
+
this._rightTop.setAttribute('y', (farTopY - 90).toFixed(2));
|
|
377
|
+
this._rightTop.setAttribute('height', '90');
|
|
378
|
+
this._rightTop.setAttribute('transform', `rotate(${farTopRot.toFixed(2)} 0 ${farTopY.toFixed(2)})`);
|
|
379
|
+
|
|
380
|
+
const nearBottomY = lerp(36, 35.3, lids);
|
|
381
|
+
const farBottomY = lerp(36, 35.6, lids);
|
|
382
|
+
this._leftBottom.setAttribute('y', nearBottomY.toFixed(2));
|
|
383
|
+
this._leftBottom.setAttribute('height', '90');
|
|
384
|
+
this._leftBottom.setAttribute('transform', `rotate(0 0 ${nearBottomY.toFixed(2)})`);
|
|
385
|
+
this._rightBottom.setAttribute('y', farBottomY.toFixed(2));
|
|
386
|
+
this._rightBottom.setAttribute('height', '90');
|
|
387
|
+
this._rightBottom.setAttribute('transform', `rotate(0 0 ${farBottomY.toFixed(2)})`);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
registerAvatarExtension({
|
|
391
|
+
name: 'actions',
|
|
392
|
+
actions: actionHandlers,
|
|
393
|
+
reset() {
|
|
394
|
+
interruptRunningAction(this);
|
|
395
|
+
},
|
|
396
|
+
draw: drawWarning,
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
export { AgentRobotAvatar };
|
|
400
|
+
export default AgentRobotAvatar;
|