@fabi-play/motion 0.5.0 → 0.7.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/README.md +15 -0
- package/dist/index.d.ts +83 -29
- package/dist/index.js +120 -42
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -16,6 +16,21 @@ const shape = shapeOf(body) // 'star', 'arms-up', … or null
|
|
|
16
16
|
const { left, right } = hands(body)
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
## System gestures and menus
|
|
20
|
+
|
|
21
|
+
- `armsCrossed(body)`: the arms crossed in an X, Fabi Play's own gesture (held 1.5 s by the app: pause, exit), so
|
|
22
|
+
games never get it as a move and `shapeOf` is null while it is made. It counts in front of the chest and as a big
|
|
23
|
+
X above the head: each wrist past the middle of the body to the other side, the two forearms (elbow to wrist)
|
|
24
|
+
crossing, rising from the elbows; above the head, with both arms stretched. Clapping, arms up, folded arms, a
|
|
25
|
+
self-hug and hands on the head are not it. `crossOf(body)` says which X: `'chest'`, `'overhead'` or null. The
|
|
26
|
+
thresholds are in `CROSS`, tuned on a child's proportions. It reads one frame: when holding it, also want it still
|
|
27
|
+
(arms waving above the head cross now and then).
|
|
28
|
+
- `armOut(body)`: one arm stretched out sideways at shoulder height, `'left'` or `'right'` as seen on the screen
|
|
29
|
+
(player space is a mirror: the side of the stretched wrist), for previous / next in menus. Null when both arms
|
|
30
|
+
are out (a star, arms wide, also with one drooping) and while the arms are crossed. The thresholds are in
|
|
31
|
+
`ARM_OUT`, in arm lengths.
|
|
32
|
+
- `menuPointer(body)`: the raised hand as a cursor; `Hold` and `Dwell` for holding a gesture or staying on a button.
|
|
33
|
+
|
|
19
34
|
Games get these movements as events from [`@fabi-play/sdk`](https://www.npmjs.com/package/@fabi-play/sdk); this
|
|
20
35
|
package is for tools and for the Fabi Play apps. ESM only, types included; depends on `@fabi-play/protocol`.
|
|
21
36
|
Docs: https://docs.fabiplay.app.
|
package/dist/index.d.ts
CHANGED
|
@@ -229,43 +229,96 @@ export declare function skeleton(body: Body$1): {
|
|
|
229
229
|
export type Skeleton = ReturnType<typeof skeleton>;
|
|
230
230
|
/** The shape the arms (and legs) make, or null when it's none of them. */
|
|
231
231
|
export declare function shapeOf(body: Body$1): Shape | null;
|
|
232
|
+
/** Where the arms make the X: in front of the chest, or big above the head. */
|
|
233
|
+
export type Cross = "chest" | "overhead";
|
|
232
234
|
/**
|
|
233
|
-
* Arms crossed in an X in front of the chest: Fabi Play's system gesture (held 1.5 s: pause,
|
|
234
|
-
* games never get it as a move. In player space, mirrored: the child's left shoulder is on the
|
|
235
|
-
* crossing means the left wrist past the midline towards the right shoulder and the right
|
|
236
|
-
* The side is read from the shoulders, not assumed.
|
|
237
|
-
*
|
|
235
|
+
* Arms crossed in an X, in front of the chest or big above the head: Fabi Play's system gesture (held 1.5 s: pause,
|
|
236
|
+
* exit), reserved, so games never get it as a move. In player space, mirrored: the child's left shoulder is on the
|
|
237
|
+
* left of the image, so crossing means the left wrist past the midline towards the right shoulder and the right
|
|
238
|
+
* wrist towards the left. The side is read from the shoulders, not assumed. See crossOf for the rule.
|
|
239
|
+
*
|
|
240
|
+
* One frame at a time: arms waving above the head cross now and then. Whoever holds the gesture (1.5 s) should also
|
|
241
|
+
* want it still.
|
|
238
242
|
*/
|
|
239
243
|
export declare function armsCrossed(body: Body$1): boolean;
|
|
240
|
-
/**
|
|
244
|
+
/**
|
|
245
|
+
* Which X the arms make: 'chest', 'overhead', or null when they are not crossed (armsCrossed is crossOf !== null).
|
|
246
|
+
* The rule: the body faces the camera, each wrist went past the midline to the other side, and the two forearms
|
|
247
|
+
* (elbow to wrist) cross, rising from the elbows. Where they cross tells the X: from mid-chest up to the chin it is
|
|
248
|
+
* the chest X (elbows by the sides, not in front of the chest: a self-hug; hands not on the other arm); with both
|
|
249
|
+
* arms stretched up, the wrists clearly above the top of the head and the elbows above the nose, the overhead X
|
|
250
|
+
* (hands on the head have the elbows bent out and the wrists at the top of the head). Anything else is neither.
|
|
251
|
+
*
|
|
252
|
+
* Heights are in torsos (shoulders to hips; estimated from the shoulders when the hips are out of view), sideways
|
|
253
|
+
* distances in half shoulder widths, and the forearm's rise in forearms, so long and short arms read the same.
|
|
254
|
+
*/
|
|
255
|
+
export declare function crossOf(body: Body$1): Cross | null;
|
|
256
|
+
/**
|
|
257
|
+
* The thresholds of crossOf and armsCrossed. Heights in torsos (t: shoulders to hips), sideways distances in half
|
|
258
|
+
* shoulder widths (h: shoulder to midline). A child (DISTANCE): h ≈ 0.38 t, nose ≈ 0.3 t and the top of the head
|
|
259
|
+
* ≈ 0.7 t above the shoulder line, upper arm ≈ 0.6 t, forearm ≈ 0.5 t.
|
|
260
|
+
*/
|
|
241
261
|
export declare const CROSS: {
|
|
242
|
-
/** Each wrist at least this far past the midline
|
|
243
|
-
readonly past: 0.
|
|
244
|
-
/**
|
|
245
|
-
readonly
|
|
246
|
-
/**
|
|
247
|
-
readonly
|
|
262
|
+
/** Each wrist at least this far past the midline, on the other side (h). */
|
|
263
|
+
readonly past: 0.2;
|
|
264
|
+
/** Each wrist above its own elbow by at least this fraction of the forearm (about 15°: folded arms are level). */
|
|
265
|
+
readonly rise: 0.25;
|
|
266
|
+
/** The forearms count this much longer past the wrists (t: the hands), so wrists that only just cross still do. */
|
|
267
|
+
readonly hand: 0.08;
|
|
268
|
+
/** The chest X: the forearms cross from this far above the shoulder line (t: under the chin; not the face)… */
|
|
269
|
+
readonly chestTop: 0.1;
|
|
270
|
+
/** …down to this far below it (t: wrists at mid-chest, the forearms crossing a little lower). */
|
|
271
|
+
readonly chestBottom: 0.75;
|
|
272
|
+
/** The chest X: each elbow at least this far from the midline, on its own side (h)… */
|
|
273
|
+
readonly elbowOut: 0.3;
|
|
248
274
|
/**
|
|
249
|
-
*
|
|
250
|
-
*
|
|
275
|
+
* …and no wrist farther past the midline than this many times the nearer elbow is out (a self-hug: elbows together
|
|
276
|
+
* in front of the chest, hands out on the other shoulder; in an X the wrists only just pass each other).
|
|
251
277
|
*/
|
|
252
|
-
readonly
|
|
253
|
-
/**
|
|
254
|
-
readonly
|
|
255
|
-
/**
|
|
256
|
-
readonly rise: 0.15;
|
|
257
|
-
/** …and this far above the other arm's elbow. */
|
|
258
|
-
readonly overElbow: 0.1;
|
|
259
|
-
/** Elbows no higher than this above the shoulder line (arms crossed over the head are not it)… */
|
|
260
|
-
readonly elbowAbove: 0.15;
|
|
261
|
-
/** …and no lower than this below the hip line. */
|
|
278
|
+
readonly hugRatio: 1.5;
|
|
279
|
+
/** The chest X: no wrist farther than this past the midline (h: the opposite shoulder; hands on the upper arms are a hug). */
|
|
280
|
+
readonly reach: 1;
|
|
281
|
+
/** The chest X: elbows no lower than this below the hip line (t). */
|
|
262
282
|
readonly elbowBelowHips: 0.3;
|
|
283
|
+
/** The overhead X: the forearms cross at least this far above the shoulder line (t: about the top of the head)… */
|
|
284
|
+
readonly overShoulders: 0.55;
|
|
285
|
+
/** …both wrists at least this far above the shoulder line (t)… */
|
|
286
|
+
readonly wristsOverShoulders: 0.8;
|
|
287
|
+
/** …and this far above the nose (t), the elbows above the nose… */
|
|
288
|
+
readonly wristsOverNose: 0.45;
|
|
289
|
+
/** …and both arms this straight: shoulder to wrist over shoulder to elbow to wrist (hands on the head: ≈ 0.88). */
|
|
290
|
+
readonly straight: 0.93;
|
|
291
|
+
/** The nose above the shoulder line when it is not seen (t). */
|
|
292
|
+
readonly noseAbove: 0.33;
|
|
263
293
|
/** The visibility the wrist hidden behind the other needs; the other wrist, elbows and shoulders need the usual 0.5. */
|
|
264
294
|
readonly hiddenWristVisible: 0.25;
|
|
265
|
-
/** Shoulders at least this wide: facing the camera. */
|
|
295
|
+
/** Shoulders at least this wide (t): facing the camera. */
|
|
296
|
+
readonly minShoulderWidth: 0.3;
|
|
297
|
+
/** Shoulder width over torso (DISTANCE.shoulders / DISTANCE.torso), to estimate the torso when the hips are out of view. */
|
|
298
|
+
readonly shoulderPerTorso: 0.76;
|
|
299
|
+
};
|
|
300
|
+
/**
|
|
301
|
+
* One arm stretched out sideways, about level with the shoulder (for menus: previous / next, a left or right
|
|
302
|
+
* button). The side is the screen side of the stretched wrist, in player space (mirrored, as the child sees it): a
|
|
303
|
+
* wrist left of the shoulders' middle is 'left'. The arm must be on its own side (an arm reaching across the body is
|
|
304
|
+
* not out), its wrist far past its shoulder and near the shoulder's height, and fairly straight (the elbow between
|
|
305
|
+
* shoulder and wrist, near the line from one to the other). Null when no arm is out, when the other wrist is out
|
|
306
|
+
* sideways too (a star, arms wide, also with one arm drooping), and while the arms are crossed. Measured in the arm's
|
|
307
|
+
* own length (shoulder to elbow to wrist, as the camera sees it), so long and short arms read the same (see ARM_OUT).
|
|
308
|
+
*/
|
|
309
|
+
export declare function armOut(body: Body$1): "left" | "right" | null;
|
|
310
|
+
/** The thresholds of armOut, in arm lengths (shoulder to elbow to wrist, as seen; a child's is ≈ 1.1 torsos). */
|
|
311
|
+
export declare const ARM_OUT: {
|
|
312
|
+
/** The wrist at least this far out past its shoulder, sideways (straight out: 1; 45° up or down: 0.7). */
|
|
313
|
+
readonly reach: 0.6;
|
|
314
|
+
/** The wrist no farther than this above or below its shoulder (about 25°). */
|
|
315
|
+
readonly height: 0.4;
|
|
316
|
+
/** The elbow no farther than this above or below the line from shoulder to wrist (fairly straight). */
|
|
317
|
+
readonly bend: 0.2;
|
|
318
|
+
/** The other wrist reaching less than this out past its shoulder, at any height: not a star with one arm drooping. */
|
|
319
|
+
readonly otherReach: 0.35;
|
|
320
|
+
/** Shoulders at least this wide, in torsos: facing the camera. */
|
|
266
321
|
readonly minShoulderWidth: 0.3;
|
|
267
|
-
/** Shoulder width over torso, to estimate the torso when the hips are out of view. */
|
|
268
|
-
readonly shoulderPerTorso: 0.8;
|
|
269
322
|
};
|
|
270
323
|
/**
|
|
271
324
|
* Something held for a while: progress 0–1 while active, fires once when full. Brief drop-outs (under graceMs, e.g.
|
|
@@ -328,8 +381,9 @@ export type Placement = "no-body" | "too-close" | "too-far" | "go-left" | "go-ri
|
|
|
328
381
|
*/
|
|
329
382
|
export declare function placement(body: Body$1 | null, center?: number, radius?: number): Placement;
|
|
330
383
|
/**
|
|
331
|
-
* The hands as pointers on the screen (0–1): the space the arms reach, around the shoulders
|
|
332
|
-
* whole screen, so a child reaches every corner without walking.
|
|
384
|
+
* The hands as pointers on the screen (0–1): the space the arms reach, around the shoulders (HAND_SPACE of
|
|
385
|
+
* @fabi-play/protocol), stretched to the whole screen, so a child reaches every corner without walking. The same
|
|
386
|
+
* stretch as the protocol's aimPoint in portrait (which games get from play.aim and engine.hands()).
|
|
333
387
|
*/
|
|
334
388
|
export declare function hands(body: Body$1): {
|
|
335
389
|
left: Point | null;
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
|
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
|
|
5
5
|
// src/index.ts
|
|
6
|
-
import { P as P2 } from "@fabi-play/protocol";
|
|
6
|
+
import { HAND_SPACE, P as P2 } from "@fabi-play/protocol";
|
|
7
7
|
|
|
8
8
|
// src/distance.ts
|
|
9
9
|
import { P } from "@fabi-play/protocol";
|
|
@@ -341,58 +341,133 @@ function shapeIn(s) {
|
|
|
341
341
|
function armsCrossed(body) {
|
|
342
342
|
return crossedIn(skeleton(body));
|
|
343
343
|
}
|
|
344
|
-
function
|
|
345
|
-
|
|
344
|
+
function crossOf(body) {
|
|
345
|
+
return crossIn(skeleton(body));
|
|
346
|
+
}
|
|
347
|
+
var crossedIn = (s) => crossIn(s) !== null;
|
|
348
|
+
var torsoOf = (s) => seen(s.leftHip, s.rightHip) ? s.torso : s.shoulderWidth / CROSS.shoulderPerTorso;
|
|
349
|
+
var straightness = (shoulder, elbow, wrist) => dist(shoulder, wrist) / Math.max(dist(shoulder, elbow) + dist(elbow, wrist), 1e-6);
|
|
350
|
+
function crossIn(s) {
|
|
351
|
+
if (!seen(s.leftShoulder, s.rightShoulder, s.leftElbow, s.rightElbow)) return null;
|
|
346
352
|
const wv = [s.leftWrist.visibility, s.rightWrist.visibility];
|
|
347
|
-
if (Math.min(...wv) < CROSS.hiddenWristVisible || Math.max(...wv) < VISIBLE) return
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
if (s.shoulderWidth < CROSS.minShoulderWidth * t) return false;
|
|
353
|
+
if (Math.min(...wv) < CROSS.hiddenWristVisible || Math.max(...wv) < VISIBLE) return null;
|
|
354
|
+
const t = torsoOf(s);
|
|
355
|
+
if (s.shoulderWidth < CROSS.minShoulderWidth * t) return null;
|
|
351
356
|
const dir = Math.sign(s.rightShoulder.x - s.leftShoulder.x);
|
|
352
357
|
const m = s.shoulders.x;
|
|
358
|
+
const half = s.shoulderWidth / 2;
|
|
359
|
+
const pastL = dir * (s.leftWrist.x - m) / half;
|
|
360
|
+
const pastR = dir * (m - s.rightWrist.x) / half;
|
|
361
|
+
if (pastL < CROSS.past || pastR < CROSS.past) return null;
|
|
362
|
+
const rises = (e, w) => e.y - w.y >= CROSS.rise * dist(e, w);
|
|
363
|
+
if (!rises(s.leftElbow, s.leftWrist) || !rises(s.rightElbow, s.rightWrist)) return null;
|
|
364
|
+
const at = forearmsCross(s.leftElbow, s.leftWrist, s.rightElbow, s.rightWrist, CROSS.hand * t);
|
|
365
|
+
if (!at) return null;
|
|
353
366
|
const top = s.shoulders.y;
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
+
const up = (top - at.y) / t;
|
|
368
|
+
if (up <= CROSS.chestTop && up >= -CROSS.chestBottom) {
|
|
369
|
+
const elbowOut = Math.min(dir * (m - s.leftElbow.x), dir * (s.rightElbow.x - m)) / half;
|
|
370
|
+
const past = Math.max(pastL, pastR);
|
|
371
|
+
const bottom = seen(s.leftHip, s.rightHip) ? s.hips.y : top + t;
|
|
372
|
+
const elbowsLow = Math.max(s.leftElbow.y, s.rightElbow.y) > bottom + CROSS.elbowBelowHips * t;
|
|
373
|
+
return elbowOut >= CROSS.elbowOut && past <= CROSS.hugRatio * elbowOut && past <= CROSS.reach && !elbowsLow ? "chest" : null;
|
|
374
|
+
}
|
|
375
|
+
if (up < CROSS.overShoulders) return null;
|
|
376
|
+
const nose = s.nose.visibility >= VISIBLE ? s.nose.y : top - CROSS.noseAbove * t;
|
|
377
|
+
const wristsY = Math.max(s.leftWrist.y, s.rightWrist.y);
|
|
378
|
+
if ((top - wristsY) / t < CROSS.wristsOverShoulders || (nose - wristsY) / t < CROSS.wristsOverNose) return null;
|
|
379
|
+
if (Math.max(s.leftElbow.y, s.rightElbow.y) > nose) return null;
|
|
380
|
+
const straight = Math.min(straightness(s.leftShoulder, s.leftElbow, s.leftWrist), straightness(s.rightShoulder, s.rightElbow, s.rightWrist));
|
|
381
|
+
return straight >= CROSS.straight ? "overhead" : null;
|
|
382
|
+
}
|
|
383
|
+
function forearmsCross(e1, w1, e2, w2, hand) {
|
|
384
|
+
const reach = (e, w) => {
|
|
385
|
+
const k = 1 + hand / Math.max(dist(e, w), 1e-6);
|
|
386
|
+
return { x: (w.x - e.x) * k, y: (w.y - e.y) * k };
|
|
387
|
+
};
|
|
388
|
+
const r = reach(e1, w1);
|
|
389
|
+
const q = reach(e2, w2);
|
|
390
|
+
const den = r.x * q.y - r.y * q.x;
|
|
391
|
+
if (Math.abs(den) < 1e-12) return null;
|
|
392
|
+
const dx = e2.x - e1.x;
|
|
393
|
+
const dy = e2.y - e1.y;
|
|
394
|
+
const a = (dx * q.y - dy * q.x) / den;
|
|
395
|
+
const b = (dx * r.y - dy * r.x) / den;
|
|
396
|
+
if (a < 0 || a > 1 || b < 0 || b > 1) return null;
|
|
397
|
+
return { x: e1.x + a * r.x, y: e1.y + a * r.y };
|
|
367
398
|
}
|
|
368
399
|
var CROSS = {
|
|
369
|
-
/** Each wrist at least this far past the midline
|
|
370
|
-
past: 0.
|
|
371
|
-
/**
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
|
|
400
|
+
/** Each wrist at least this far past the midline, on the other side (h). */
|
|
401
|
+
past: 0.2,
|
|
402
|
+
/** Each wrist above its own elbow by at least this fraction of the forearm (about 15°: folded arms are level). */
|
|
403
|
+
rise: 0.25,
|
|
404
|
+
/** The forearms count this much longer past the wrists (t: the hands), so wrists that only just cross still do. */
|
|
405
|
+
hand: 0.08,
|
|
406
|
+
/** The chest X: the forearms cross from this far above the shoulder line (t: under the chin; not the face)… */
|
|
407
|
+
chestTop: 0.1,
|
|
408
|
+
/** …down to this far below it (t: wrists at mid-chest, the forearms crossing a little lower). */
|
|
409
|
+
chestBottom: 0.75,
|
|
410
|
+
/** The chest X: each elbow at least this far from the midline, on its own side (h)… */
|
|
411
|
+
elbowOut: 0.3,
|
|
375
412
|
/**
|
|
376
|
-
*
|
|
377
|
-
*
|
|
413
|
+
* …and no wrist farther past the midline than this many times the nearer elbow is out (a self-hug: elbows together
|
|
414
|
+
* in front of the chest, hands out on the other shoulder; in an X the wrists only just pass each other).
|
|
378
415
|
*/
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
rise: 0.15,
|
|
384
|
-
/** …and this far above the other arm's elbow. */
|
|
385
|
-
overElbow: 0.1,
|
|
386
|
-
/** Elbows no higher than this above the shoulder line (arms crossed over the head are not it)… */
|
|
387
|
-
elbowAbove: 0.15,
|
|
388
|
-
/** …and no lower than this below the hip line. */
|
|
416
|
+
hugRatio: 1.5,
|
|
417
|
+
/** The chest X: no wrist farther than this past the midline (h: the opposite shoulder; hands on the upper arms are a hug). */
|
|
418
|
+
reach: 1,
|
|
419
|
+
/** The chest X: elbows no lower than this below the hip line (t). */
|
|
389
420
|
elbowBelowHips: 0.3,
|
|
421
|
+
/** The overhead X: the forearms cross at least this far above the shoulder line (t: about the top of the head)… */
|
|
422
|
+
overShoulders: 0.55,
|
|
423
|
+
/** …both wrists at least this far above the shoulder line (t)… */
|
|
424
|
+
wristsOverShoulders: 0.8,
|
|
425
|
+
/** …and this far above the nose (t), the elbows above the nose… */
|
|
426
|
+
wristsOverNose: 0.45,
|
|
427
|
+
/** …and both arms this straight: shoulder to wrist over shoulder to elbow to wrist (hands on the head: ≈ 0.88). */
|
|
428
|
+
straight: 0.93,
|
|
429
|
+
/** The nose above the shoulder line when it is not seen (t). */
|
|
430
|
+
noseAbove: 0.33,
|
|
390
431
|
/** The visibility the wrist hidden behind the other needs; the other wrist, elbows and shoulders need the usual 0.5. */
|
|
391
432
|
hiddenWristVisible: 0.25,
|
|
392
|
-
/** Shoulders at least this wide: facing the camera. */
|
|
433
|
+
/** Shoulders at least this wide (t): facing the camera. */
|
|
393
434
|
minShoulderWidth: 0.3,
|
|
394
|
-
/** Shoulder width over torso, to estimate the torso when the hips are out of view. */
|
|
395
|
-
shoulderPerTorso: 0.
|
|
435
|
+
/** Shoulder width over torso (DISTANCE.shoulders / DISTANCE.torso), to estimate the torso when the hips are out of view. */
|
|
436
|
+
shoulderPerTorso: 0.76
|
|
437
|
+
};
|
|
438
|
+
function armOut(body) {
|
|
439
|
+
const s = skeleton(body);
|
|
440
|
+
if (!seen(s.leftShoulder, s.rightShoulder) || crossedIn(s)) return null;
|
|
441
|
+
if (s.shoulderWidth < ARM_OUT.minShoulderWidth * torsoOf(s)) return null;
|
|
442
|
+
const m = s.shoulders.x;
|
|
443
|
+
const arm = (shoulder, elbow, wrist) => {
|
|
444
|
+
if (!seen(elbow, wrist)) return { reach: 0, out: false };
|
|
445
|
+
const length = Math.max(dist(shoulder, elbow) + dist(elbow, wrist), 1e-6);
|
|
446
|
+
const side = Math.sign(shoulder.x - m);
|
|
447
|
+
const from = (p) => side * (p.x - m);
|
|
448
|
+
const reach = (from(wrist) - from(shoulder)) / length;
|
|
449
|
+
if (reach < ARM_OUT.reach || Math.abs(wrist.y - shoulder.y) / length > ARM_OUT.height) return { reach, out: false };
|
|
450
|
+
const k = (from(elbow) - from(shoulder)) / (reach * length);
|
|
451
|
+
const bend = Math.abs(elbow.y - (shoulder.y + k * (wrist.y - shoulder.y))) / length;
|
|
452
|
+
return { reach, out: k >= 0 && k <= 1 && bend <= ARM_OUT.bend };
|
|
453
|
+
};
|
|
454
|
+
const left = arm(s.leftShoulder, s.leftElbow, s.leftWrist);
|
|
455
|
+
const right = arm(s.rightShoulder, s.rightElbow, s.rightWrist);
|
|
456
|
+
const one = left.out && right.reach < ARM_OUT.otherReach ? s.leftWrist : right.out && left.reach < ARM_OUT.otherReach ? s.rightWrist : null;
|
|
457
|
+
if (!one) return null;
|
|
458
|
+
return one.x < m ? "left" : "right";
|
|
459
|
+
}
|
|
460
|
+
var ARM_OUT = {
|
|
461
|
+
/** The wrist at least this far out past its shoulder, sideways (straight out: 1; 45° up or down: 0.7). */
|
|
462
|
+
reach: 0.6,
|
|
463
|
+
/** The wrist no farther than this above or below its shoulder (about 25°). */
|
|
464
|
+
height: 0.4,
|
|
465
|
+
/** The elbow no farther than this above or below the line from shoulder to wrist (fairly straight). */
|
|
466
|
+
bend: 0.2,
|
|
467
|
+
/** The other wrist reaching less than this out past its shoulder, at any height: not a star with one arm drooping. */
|
|
468
|
+
otherReach: 0.35,
|
|
469
|
+
/** Shoulders at least this wide, in torsos: facing the camera. */
|
|
470
|
+
minShoulderWidth: 0.3
|
|
396
471
|
};
|
|
397
472
|
var HOLD_DRAIN = 0.5;
|
|
398
473
|
var Hold = class {
|
|
@@ -512,7 +587,7 @@ function handSpace(body) {
|
|
|
512
587
|
return spaceIn(skeleton(body));
|
|
513
588
|
}
|
|
514
589
|
function spaceIn(s) {
|
|
515
|
-
const box = { x: s.shoulders.x -
|
|
590
|
+
const box = { x: s.shoulders.x - HAND_SPACE.left * s.torso, y: s.shoulders.y - HAND_SPACE.top * s.torso, w: HAND_SPACE.w * s.torso, h: HAND_SPACE.h * s.torso };
|
|
516
591
|
return (p) => ({ x: (p.x - box.x) / box.w, y: (p.y - box.y) / box.h });
|
|
517
592
|
}
|
|
518
593
|
var MotionTracker = class {
|
|
@@ -684,6 +759,7 @@ function nearest(from, free, anchors, limit, pair) {
|
|
|
684
759
|
}
|
|
685
760
|
}
|
|
686
761
|
export {
|
|
762
|
+
ARM_OUT,
|
|
687
763
|
BodyTracker,
|
|
688
764
|
CROSS,
|
|
689
765
|
DISTANCE,
|
|
@@ -693,8 +769,10 @@ export {
|
|
|
693
769
|
MotionTracker,
|
|
694
770
|
RAISED,
|
|
695
771
|
TooCloseGuard,
|
|
772
|
+
armOut,
|
|
696
773
|
armsCrossed,
|
|
697
774
|
bodyAtDistance,
|
|
775
|
+
crossOf,
|
|
698
776
|
defaultFov,
|
|
699
777
|
estimateDistance,
|
|
700
778
|
handSpace,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fabi-play/motion",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Body movements from pose landmarks: jumps, squats, shapes, lanes, freezing, hands as pointers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fabi-play",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"access": "public"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@fabi-play/protocol": "^0.
|
|
35
|
+
"@fabi-play/protocol": "^0.7.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"dts-bundle-generator": "9.5.1",
|