@fabi-play/motion 0.4.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 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, exit), reserved, so
234
- * games never get it as a move. In player space, mirrored: the child's left shoulder is on the left of the image, so
235
- * crossing means the left wrist past the midline towards the right shoulder and the right wrist towards the left.
236
- * The side is read from the shoulders, not assumed. Measured in torsos (shoulders to hips; estimated from the
237
- * shoulders when the hips are out of view).
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
- /** The thresholds of armsCrossed, in torsos (t: shoulders to hips). */
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 (between the shoulders)… */
243
- readonly past: 0.12;
244
- /** …and no farther than the opposite shoulder plus this (a wide X; self-hugs are told apart by the elbows). */
245
- readonly pastShoulder: 0.5;
246
- /** Each elbow at least this far from the midline, on its own side (in a self-hug they come in front of the chest). */
247
- readonly elbowOut: 0.38;
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
- * Wrists between this far above the shoulder line (an X up to the chin and mouth; not higher: hands on the head and
250
- * forearms over the eyes, a child hiding the face, are not it)…
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 above: 0.6;
253
- /** …and this far below it (mid-chest). */
254
- readonly below: 0.7;
255
- /** Each wrist at least this far above its own elbow (the forearm rises)… */
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, stretched to the
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 crossedIn(s) {
345
- if (!seen(s.leftShoulder, s.rightShoulder, s.leftElbow, s.rightElbow)) return false;
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 false;
348
- const hipsSeen = seen(s.leftHip, s.rightHip);
349
- const t = hipsSeen ? s.torso : s.shoulderWidth / CROSS.shoulderPerTorso;
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 bottom = hipsSeen ? s.hips.y : top + t;
355
- const crossL = dir * (s.leftWrist.x - m) / t;
356
- const crossR = dir * (m - s.rightWrist.x) / t;
357
- const elbowL = dir * (m - s.leftElbow.x) / t;
358
- const elbowR = dir * (s.rightElbow.x - m) / t;
359
- const reach = 0.5 * s.shoulderWidth / t + CROSS.pastShoulder;
360
- if (crossL < CROSS.past || crossR < CROSS.past || crossL > reach || crossR > reach) return false;
361
- if (elbowL < CROSS.elbowOut || elbowR < CROSS.elbowOut) return false;
362
- for (const w of [s.leftWrist, s.rightWrist]) if (w.y < top - CROSS.above * t || w.y > top + CROSS.below * t) return false;
363
- for (const e of [s.leftElbow, s.rightElbow]) if (e.y < top - CROSS.elbowAbove * t || e.y > bottom + CROSS.elbowBelowHips * t) return false;
364
- const rises = (w, e) => (e.y - w.y) / t >= CROSS.rise;
365
- const clear = (w, otherElbow) => (otherElbow.y - w.y) / t >= CROSS.overElbow;
366
- return rises(s.leftWrist, s.leftElbow) && rises(s.rightWrist, s.rightElbow) && clear(s.leftWrist, s.rightElbow) && clear(s.rightWrist, s.leftElbow);
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 (between the shoulders)… */
370
- past: 0.12,
371
- /** …and no farther than the opposite shoulder plus this (a wide X; self-hugs are told apart by the elbows). */
372
- pastShoulder: 0.5,
373
- /** Each elbow at least this far from the midline, on its own side (in a self-hug they come in front of the chest). */
374
- elbowOut: 0.38,
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
- * Wrists between this far above the shoulder line (an X up to the chin and mouth; not higher: hands on the head and
377
- * forearms over the eyes, a child hiding the face, are not it)…
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
- above: 0.6,
380
- /** …and this far below it (mid-chest). */
381
- below: 0.7,
382
- /** Each wrist at least this far above its own elbow (the forearm rises)… */
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.8
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 - 1.6 * s.torso, y: s.shoulders.y - 1.6 * s.torso, w: 3.2 * s.torso, h: 3 * s.torso };
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.4.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.4.0"
35
+ "@fabi-play/protocol": "^0.7.0"
36
36
  },
37
37
  "devDependencies": {
38
38
  "dts-bundle-generator": "9.5.1",