@fabi-play/engine 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 +14 -0
- package/dist/fabi-play-engine.d.ts +68 -8
- package/dist/fabi-play-engine.js +13 -13
- package/dist/fabi-play-engine.lite.d.ts +68 -8
- package/dist/fabi-play-engine.lite.js +13 -13
- package/dist/fabi-play-engine.lite.umd.js +119 -20
- package/dist/fabi-play-engine.lite.umd.min.js +13 -13
- package/dist/fabi-play-engine.umd.js +119 -20
- package/dist/fabi-play-engine.umd.min.js +13 -13
- package/dist/index.d.ts +68 -8
- package/dist/index.development.js +118 -19
- package/dist/index.js +13 -13
- package/dist/integrity.json +9 -9
- package/package.json +5 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fabi-play/engine 0.
|
|
1
|
+
/*! @fabi-play/engine 0.7.0 (lite: load fabi-play.js first) | Fabi Play Kit License | https://docs.fabiplay.app */
|
|
2
2
|
(function (root, factory) {
|
|
3
3
|
var script = typeof document !== 'undefined' ? document.currentScript : null;
|
|
4
4
|
if (typeof define === 'function' && define.amd) define([], function () { return factory(script); });
|
|
@@ -113,6 +113,77 @@ var DEFAULT_AVATARS_BY_AGE = {
|
|
|
113
113
|
};
|
|
114
114
|
var DEFAULT_AVATARS = DEFAULT_AVATARS_BY_AGE["6-8"];
|
|
115
115
|
var toScreen = (view, p) => view ? { x: view.x + p.x * view.w, y: view.y + p.y * view.h } : { x: p.x, y: p.y };
|
|
116
|
+
var AIM_VISIBLE = 0.5;
|
|
117
|
+
var HAND_SPACE = { left: 1.6, top: 1.6, w: 3.2, h: 3 };
|
|
118
|
+
var TORSO_PER_SHOULDER_WIDTH = 1.2;
|
|
119
|
+
function aimPoint(who, side, opts = {}) {
|
|
120
|
+
const body = Array.isArray(who) ? who : who.body;
|
|
121
|
+
if (!Array.isArray(body)) return null;
|
|
122
|
+
const w2 = side === "left" ? P.leftWrist : P.rightWrist;
|
|
123
|
+
const at = (i) => isNumber(body[i * 3]) && isNumber(body[i * 3 + 1]) && (body[i * 3 + 2] ?? 0) >= AIM_VISIBLE ? i : -1;
|
|
124
|
+
if (at(w2) < 0) return null;
|
|
125
|
+
const wx = body[w2 * 3];
|
|
126
|
+
const wy = body[w2 * 3 + 1];
|
|
127
|
+
if (opts.orientation !== "portrait") return toScreen(opts.view, { x: wx, y: wy });
|
|
128
|
+
if (at(P.leftShoulder) < 0 || at(P.rightShoulder) < 0) return null;
|
|
129
|
+
const ls = P.leftShoulder * 3;
|
|
130
|
+
const rs = P.rightShoulder * 3;
|
|
131
|
+
const sx = (body[ls] + body[rs]) / 2;
|
|
132
|
+
const sy = (body[ls + 1] + body[rs + 1]) / 2;
|
|
133
|
+
const lh = P.leftHip * 3;
|
|
134
|
+
const rh = P.rightHip * 3;
|
|
135
|
+
const hx = (body[lh] + body[rh]) / 2;
|
|
136
|
+
const hy = (body[lh + 1] + body[rh + 1]) / 2;
|
|
137
|
+
let torso = isNumber(hx) && isNumber(hy) ? Math.hypot(hx - sx, hy - sy) : 0;
|
|
138
|
+
if (at(P.leftHip) < 0 || at(P.rightHip) < 0) torso = Math.max(torso, TORSO_PER_SHOULDER_WIDTH * Math.hypot(body[ls] - body[rs], body[ls + 1] - body[rs + 1]));
|
|
139
|
+
torso = Math.max(torso, 0.01);
|
|
140
|
+
return {
|
|
141
|
+
x: clamp01((wx - sx + HAND_SPACE.left * torso) / (HAND_SPACE.w * torso)),
|
|
142
|
+
y: clamp01((wy - sy + HAND_SPACE.top * torso) / (HAND_SPACE.h * torso))
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
var AIM_HOLD_MS = 300;
|
|
146
|
+
var AimHold = class {
|
|
147
|
+
constructor(holdMs = AIM_HOLD_MS) {
|
|
148
|
+
__publicField(this, "holdMs", holdMs);
|
|
149
|
+
__publicField(this, "last", /* @__PURE__ */ new Map());
|
|
150
|
+
}
|
|
151
|
+
/** A number for a player's hand. */
|
|
152
|
+
static key(player, side) {
|
|
153
|
+
return player * 2 + (side === "right" ? 1 : 0);
|
|
154
|
+
}
|
|
155
|
+
/** The aim to use at time t (ms) for the hand key, given what aimPoint said now (p, or null if lost). */
|
|
156
|
+
at(key, p, t) {
|
|
157
|
+
const l = this.last.get(key);
|
|
158
|
+
if (p) {
|
|
159
|
+
if (l) {
|
|
160
|
+
l.x = p.x;
|
|
161
|
+
l.y = p.y;
|
|
162
|
+
l.t = t;
|
|
163
|
+
} else this.last.set(key, { x: p.x, y: p.y, t });
|
|
164
|
+
return p;
|
|
165
|
+
}
|
|
166
|
+
if (!l) return null;
|
|
167
|
+
if (t - l.t <= this.holdMs) return { x: l.x, y: l.y };
|
|
168
|
+
this.last.delete(key);
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
/** Forgets every hand. */
|
|
172
|
+
clear() {
|
|
173
|
+
this.last.clear();
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
var MAX_SAFE_INSET = 0.25;
|
|
177
|
+
var NO_SAFE_AREA = Object.freeze({ top: 0, bottom: 0, left: 0, right: 0 });
|
|
178
|
+
function parseSafeArea(v) {
|
|
179
|
+
if (!isObject(v)) return void 0;
|
|
180
|
+
const f = (n) => isNumber(n) ? Math.min(Math.max(n, 0), MAX_SAFE_INSET) : 0;
|
|
181
|
+
return { top: f(v.top), bottom: f(v.bottom), left: f(v.left), right: f(v.right) };
|
|
182
|
+
}
|
|
183
|
+
var COVER_ASPECT = 16 / 9;
|
|
184
|
+
var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
185
|
+
var isNumber = (v) => typeof v === "number" && Number.isFinite(v);
|
|
186
|
+
var clamp01 = (n) => Math.min(Math.max(n, 0), 1);
|
|
116
187
|
var POSE_HEADER = 12;
|
|
117
188
|
var POSE_BODY_BYTES = LANDMARKS * 4;
|
|
118
189
|
var poseBytes = (bodies) => POSE_HEADER + bodies * POSE_BODY_BYTES;
|
|
@@ -4427,11 +4498,20 @@ function wrists(body) {
|
|
|
4427
4498
|
return out;
|
|
4428
4499
|
}
|
|
4429
4500
|
var playerColor = (player) => PLAYER_COLORS[((player - 1) % PLAYER_COLORS.length + PLAYER_COLORS.length) % PLAYER_COLORS.length];
|
|
4430
|
-
|
|
4501
|
+
var SIDES = ["left", "right"];
|
|
4502
|
+
function handsOf(frames, toScreen2, width, height, bodyOf = () => null, aim = {}) {
|
|
4431
4503
|
const out = [];
|
|
4432
4504
|
for (const f of frames) {
|
|
4433
4505
|
const body = bodyOf(f) ?? f.body;
|
|
4434
4506
|
if (!Array.isArray(body)) continue;
|
|
4507
|
+
if (aim.orientation === "portrait") {
|
|
4508
|
+
for (const side of SIDES) {
|
|
4509
|
+
let p = aimPoint(body, side, { orientation: "portrait" });
|
|
4510
|
+
if (aim.hold) p = aim.hold.at(AimHold.key(f.id, side), p, aim.t ?? 0);
|
|
4511
|
+
if (p) out.push({ player: f.id, side, x: p.x * width, y: p.y * height, color: playerColor(f.id) });
|
|
4512
|
+
}
|
|
4513
|
+
continue;
|
|
4514
|
+
}
|
|
4435
4515
|
for (const w2 of wrists(body)) {
|
|
4436
4516
|
const s = toScreen2(w2);
|
|
4437
4517
|
out.push({ player: f.id, side: w2.side, x: s.x * width, y: s.y * height, color: playerColor(f.id) });
|
|
@@ -5093,6 +5173,10 @@ var HandMarkers = class extends Node {
|
|
|
5093
5173
|
}
|
|
5094
5174
|
};
|
|
5095
5175
|
var now = () => performance.now();
|
|
5176
|
+
var frozenSafeArea = (v) => {
|
|
5177
|
+
const sa = parseSafeArea(v);
|
|
5178
|
+
return sa && Object.freeze(sa);
|
|
5179
|
+
};
|
|
5096
5180
|
async function createEngine(opts = {}) {
|
|
5097
5181
|
const play = opts.play ?? null;
|
|
5098
5182
|
let canvas = opts.canvas ?? null;
|
|
@@ -5227,6 +5311,8 @@ async function createEngine(opts = {}) {
|
|
|
5227
5311
|
ro?.observe(canvas);
|
|
5228
5312
|
let backdrop = play?.backdrop === "video" ? "video" : "none";
|
|
5229
5313
|
let view = play?.view ?? null;
|
|
5314
|
+
let orientation = play?.orientation === "portrait" ? "portrait" : "landscape";
|
|
5315
|
+
let safeArea = frozenSafeArea(play?.safeArea) ?? NO_SAFE_AREA;
|
|
5230
5316
|
let livePlayers = [];
|
|
5231
5317
|
let lastPlayersAt = -Infinity;
|
|
5232
5318
|
let hostPaused = false;
|
|
@@ -5254,7 +5340,7 @@ async function createEngine(opts = {}) {
|
|
|
5254
5340
|
lastPlayersAt = now();
|
|
5255
5341
|
}
|
|
5256
5342
|
};
|
|
5257
|
-
const
|
|
5343
|
+
const hold2 = (on) => {
|
|
5258
5344
|
hostPaused = on;
|
|
5259
5345
|
syncSound();
|
|
5260
5346
|
};
|
|
@@ -5268,6 +5354,11 @@ async function createEngine(opts = {}) {
|
|
|
5268
5354
|
};
|
|
5269
5355
|
unlisten.push(
|
|
5270
5356
|
play.on("start", (m) => lateHint(m?.device)),
|
|
5357
|
+
play.on("start", (m) => {
|
|
5358
|
+
if (m?.orientation === "portrait" || m?.orientation === "landscape") orientation = m.orientation;
|
|
5359
|
+
const sa = frozenSafeArea(m?.safeArea);
|
|
5360
|
+
if (sa) safeArea = sa;
|
|
5361
|
+
}),
|
|
5271
5362
|
// The simple API knows muted only from the start (its getter is false before): read it again then.
|
|
5272
5363
|
play.on("start", (m) => {
|
|
5273
5364
|
if (typeof m?.muted === "boolean") sound.muted = m.muted;
|
|
@@ -5276,11 +5367,11 @@ async function createEngine(opts = {}) {
|
|
|
5276
5367
|
play.on("players", (m) => setPlayers(m?.players)),
|
|
5277
5368
|
play.on("backdrop", (m) => setBackdrop(m?.backdrop, m?.view)),
|
|
5278
5369
|
play.on("start", (m) => setBackdrop(m?.backdrop, m?.view ?? play.view)),
|
|
5279
|
-
play.on("pause", () =>
|
|
5280
|
-
play.on("resume", () =>
|
|
5281
|
-
play.on("break-start", () =>
|
|
5282
|
-
play.on("break-done", () =>
|
|
5283
|
-
play.on("break-end", () =>
|
|
5370
|
+
play.on("pause", () => hold2(true)),
|
|
5371
|
+
play.on("resume", () => hold2(false)),
|
|
5372
|
+
play.on("break-start", () => hold2(true)),
|
|
5373
|
+
play.on("break-done", () => hold2(false)),
|
|
5374
|
+
play.on("break-end", () => hold2(false))
|
|
5284
5375
|
);
|
|
5285
5376
|
sound.muted = play.muted === true;
|
|
5286
5377
|
}
|
|
@@ -5291,11 +5382,13 @@ async function createEngine(opts = {}) {
|
|
|
5291
5382
|
let tick = 0;
|
|
5292
5383
|
const live = () => now() - lastPlayersAt < 1e3 ? livePlayers : [];
|
|
5293
5384
|
const toScreen2 = (p) => play ? play.toScreen(p) : toScreen(view, p);
|
|
5385
|
+
const hold = new AimHold();
|
|
5294
5386
|
const allHands = () => {
|
|
5295
5387
|
if (handsAt === tick) return handsCache;
|
|
5296
5388
|
handsAt = tick;
|
|
5297
|
-
const
|
|
5298
|
-
|
|
5389
|
+
const portrait = orientation === "portrait";
|
|
5390
|
+
const bodyOf = backdrop === "video" && !portrait || !play?.smoothedBody ? () => null : (f) => play.smoothedBody(f);
|
|
5391
|
+
handsCache = handsOf(live(), toScreen2, stageW, STAGE_HEIGHT, bodyOf, portrait ? { orientation, hold, t: now() } : void 0);
|
|
5299
5392
|
return handsCache;
|
|
5300
5393
|
};
|
|
5301
5394
|
const box = { x: 0, y: 0, w: 1, h: 1 };
|
|
@@ -5458,6 +5551,12 @@ async function createEngine(opts = {}) {
|
|
|
5458
5551
|
get view() {
|
|
5459
5552
|
return view;
|
|
5460
5553
|
},
|
|
5554
|
+
get orientation() {
|
|
5555
|
+
return orientation;
|
|
5556
|
+
},
|
|
5557
|
+
get safeArea() {
|
|
5558
|
+
return safeArea;
|
|
5559
|
+
},
|
|
5461
5560
|
get paused() {
|
|
5462
5561
|
return hostPaused || userPaused;
|
|
5463
5562
|
},
|
|
@@ -6317,7 +6416,7 @@ ${msg}`);
|
|
|
6317
6416
|
};
|
|
6318
6417
|
|
|
6319
6418
|
// src/filters.ts
|
|
6320
|
-
var
|
|
6419
|
+
var clamp012 = (v) => Math.min(Math.max(v, 0), 1);
|
|
6321
6420
|
var pos = (v) => Math.max(v, 0);
|
|
6322
6421
|
var Base = class {
|
|
6323
6422
|
constructor() {
|
|
@@ -6369,11 +6468,11 @@ var Contrast = class extends Amount {
|
|
|
6369
6468
|
};
|
|
6370
6469
|
var Invert = class extends Amount {
|
|
6371
6470
|
toMatrix(m) {
|
|
6372
|
-
const a =
|
|
6471
|
+
const a = clamp012(this.amount);
|
|
6373
6472
|
linear(m, 1 - 2 * a, a);
|
|
6374
6473
|
}
|
|
6375
6474
|
css() {
|
|
6376
|
-
return `invert(${
|
|
6475
|
+
return `invert(${clamp012(this.amount)})`;
|
|
6377
6476
|
}
|
|
6378
6477
|
};
|
|
6379
6478
|
var Saturate = class extends Amount {
|
|
@@ -6396,7 +6495,7 @@ var Saturate = class extends Amount {
|
|
|
6396
6495
|
};
|
|
6397
6496
|
var Grayscale = class extends Amount {
|
|
6398
6497
|
toMatrix(m) {
|
|
6399
|
-
const k = 1 -
|
|
6498
|
+
const k = 1 - clamp012(this.amount);
|
|
6400
6499
|
m9[0] = 0.2126 + 0.7874 * k;
|
|
6401
6500
|
m9[1] = 0.7152 - 0.7152 * k;
|
|
6402
6501
|
m9[2] = 0.0722 - 0.0722 * k;
|
|
@@ -6409,12 +6508,12 @@ var Grayscale = class extends Amount {
|
|
|
6409
6508
|
rgb3(m, m9);
|
|
6410
6509
|
}
|
|
6411
6510
|
css() {
|
|
6412
|
-
return `grayscale(${
|
|
6511
|
+
return `grayscale(${clamp012(this.amount)})`;
|
|
6413
6512
|
}
|
|
6414
6513
|
};
|
|
6415
6514
|
var Sepia = class extends Amount {
|
|
6416
6515
|
toMatrix(m) {
|
|
6417
|
-
const k = 1 -
|
|
6516
|
+
const k = 1 - clamp012(this.amount);
|
|
6418
6517
|
m9[0] = 0.393 + 0.607 * k;
|
|
6419
6518
|
m9[1] = 0.769 - 0.769 * k;
|
|
6420
6519
|
m9[2] = 0.189 - 0.189 * k;
|
|
@@ -6427,7 +6526,7 @@ var Sepia = class extends Amount {
|
|
|
6427
6526
|
rgb3(m, m9);
|
|
6428
6527
|
}
|
|
6429
6528
|
css() {
|
|
6430
|
-
return `sepia(${
|
|
6529
|
+
return `sepia(${clamp012(this.amount)})`;
|
|
6431
6530
|
}
|
|
6432
6531
|
};
|
|
6433
6532
|
var HueRotate = class extends Amount {
|
|
@@ -6476,7 +6575,7 @@ var Tint = class extends Amount {
|
|
|
6476
6575
|
return i + 2;
|
|
6477
6576
|
}
|
|
6478
6577
|
toMatrix(m) {
|
|
6479
|
-
const a =
|
|
6578
|
+
const a = clamp012(this.amount);
|
|
6480
6579
|
linear(m, 1 - a, 0);
|
|
6481
6580
|
m[4] = (this.c >> 16 & 255) / 255 * a;
|
|
6482
6581
|
m[9] = (this.c >> 8 & 255) / 255 * a;
|
|
@@ -6487,7 +6586,7 @@ var Tint = class extends Amount {
|
|
|
6487
6586
|
return null;
|
|
6488
6587
|
}
|
|
6489
6588
|
paint2d(ctx, x, y, w2, h) {
|
|
6490
|
-
const a =
|
|
6589
|
+
const a = clamp012(this.amount);
|
|
6491
6590
|
if (a <= 0) return;
|
|
6492
6591
|
ctx.save();
|
|
6493
6592
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
@@ -6553,7 +6652,7 @@ var DropShadow = class extends Base {
|
|
|
6553
6652
|
return this.a;
|
|
6554
6653
|
}
|
|
6555
6654
|
set opacity(v) {
|
|
6556
|
-
this.a =
|
|
6655
|
+
this.a = clamp012(v);
|
|
6557
6656
|
}
|
|
6558
6657
|
get color() {
|
|
6559
6658
|
return this.rgb;
|