@fabi-play/engine 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 +14 -0
- package/dist/fabi-play-engine.d.ts +65 -5
- package/dist/fabi-play-engine.js +13 -13
- package/dist/fabi-play-engine.lite.d.ts +65 -5
- package/dist/fabi-play-engine.lite.js +13 -13
- package/dist/fabi-play-engine.lite.umd.js +118 -20
- package/dist/fabi-play-engine.lite.umd.min.js +13 -13
- package/dist/fabi-play-engine.umd.js +118 -20
- package/dist/fabi-play-engine.umd.min.js +13 -13
- package/dist/index.d.ts +65 -5
- package/dist/index.development.js +117 -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,7 +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
|
+
}
|
|
116
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);
|
|
117
187
|
var POSE_HEADER = 12;
|
|
118
188
|
var POSE_BODY_BYTES = LANDMARKS * 4;
|
|
119
189
|
var poseBytes = (bodies) => POSE_HEADER + bodies * POSE_BODY_BYTES;
|
|
@@ -4428,11 +4498,20 @@ function wrists(body) {
|
|
|
4428
4498
|
return out;
|
|
4429
4499
|
}
|
|
4430
4500
|
var playerColor = (player) => PLAYER_COLORS[((player - 1) % PLAYER_COLORS.length + PLAYER_COLORS.length) % PLAYER_COLORS.length];
|
|
4431
|
-
|
|
4501
|
+
var SIDES = ["left", "right"];
|
|
4502
|
+
function handsOf(frames, toScreen2, width, height, bodyOf = () => null, aim = {}) {
|
|
4432
4503
|
const out = [];
|
|
4433
4504
|
for (const f of frames) {
|
|
4434
4505
|
const body = bodyOf(f) ?? f.body;
|
|
4435
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
|
+
}
|
|
4436
4515
|
for (const w2 of wrists(body)) {
|
|
4437
4516
|
const s = toScreen2(w2);
|
|
4438
4517
|
out.push({ player: f.id, side: w2.side, x: s.x * width, y: s.y * height, color: playerColor(f.id) });
|
|
@@ -5094,6 +5173,10 @@ var HandMarkers = class extends Node {
|
|
|
5094
5173
|
}
|
|
5095
5174
|
};
|
|
5096
5175
|
var now = () => performance.now();
|
|
5176
|
+
var frozenSafeArea = (v) => {
|
|
5177
|
+
const sa = parseSafeArea(v);
|
|
5178
|
+
return sa && Object.freeze(sa);
|
|
5179
|
+
};
|
|
5097
5180
|
async function createEngine(opts = {}) {
|
|
5098
5181
|
const play = opts.play ?? null;
|
|
5099
5182
|
let canvas = opts.canvas ?? null;
|
|
@@ -5228,6 +5311,8 @@ async function createEngine(opts = {}) {
|
|
|
5228
5311
|
ro?.observe(canvas);
|
|
5229
5312
|
let backdrop = play?.backdrop === "video" ? "video" : "none";
|
|
5230
5313
|
let view = play?.view ?? null;
|
|
5314
|
+
let orientation = play?.orientation === "portrait" ? "portrait" : "landscape";
|
|
5315
|
+
let safeArea = frozenSafeArea(play?.safeArea) ?? NO_SAFE_AREA;
|
|
5231
5316
|
let livePlayers = [];
|
|
5232
5317
|
let lastPlayersAt = -Infinity;
|
|
5233
5318
|
let hostPaused = false;
|
|
@@ -5255,7 +5340,7 @@ async function createEngine(opts = {}) {
|
|
|
5255
5340
|
lastPlayersAt = now();
|
|
5256
5341
|
}
|
|
5257
5342
|
};
|
|
5258
|
-
const
|
|
5343
|
+
const hold2 = (on) => {
|
|
5259
5344
|
hostPaused = on;
|
|
5260
5345
|
syncSound();
|
|
5261
5346
|
};
|
|
@@ -5269,6 +5354,11 @@ async function createEngine(opts = {}) {
|
|
|
5269
5354
|
};
|
|
5270
5355
|
unlisten.push(
|
|
5271
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
|
+
}),
|
|
5272
5362
|
// The simple API knows muted only from the start (its getter is false before): read it again then.
|
|
5273
5363
|
play.on("start", (m) => {
|
|
5274
5364
|
if (typeof m?.muted === "boolean") sound.muted = m.muted;
|
|
@@ -5277,11 +5367,11 @@ async function createEngine(opts = {}) {
|
|
|
5277
5367
|
play.on("players", (m) => setPlayers(m?.players)),
|
|
5278
5368
|
play.on("backdrop", (m) => setBackdrop(m?.backdrop, m?.view)),
|
|
5279
5369
|
play.on("start", (m) => setBackdrop(m?.backdrop, m?.view ?? play.view)),
|
|
5280
|
-
play.on("pause", () =>
|
|
5281
|
-
play.on("resume", () =>
|
|
5282
|
-
play.on("break-start", () =>
|
|
5283
|
-
play.on("break-done", () =>
|
|
5284
|
-
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))
|
|
5285
5375
|
);
|
|
5286
5376
|
sound.muted = play.muted === true;
|
|
5287
5377
|
}
|
|
@@ -5292,11 +5382,13 @@ async function createEngine(opts = {}) {
|
|
|
5292
5382
|
let tick = 0;
|
|
5293
5383
|
const live = () => now() - lastPlayersAt < 1e3 ? livePlayers : [];
|
|
5294
5384
|
const toScreen2 = (p) => play ? play.toScreen(p) : toScreen(view, p);
|
|
5385
|
+
const hold = new AimHold();
|
|
5295
5386
|
const allHands = () => {
|
|
5296
5387
|
if (handsAt === tick) return handsCache;
|
|
5297
5388
|
handsAt = tick;
|
|
5298
|
-
const
|
|
5299
|
-
|
|
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);
|
|
5300
5392
|
return handsCache;
|
|
5301
5393
|
};
|
|
5302
5394
|
const box = { x: 0, y: 0, w: 1, h: 1 };
|
|
@@ -5459,6 +5551,12 @@ async function createEngine(opts = {}) {
|
|
|
5459
5551
|
get view() {
|
|
5460
5552
|
return view;
|
|
5461
5553
|
},
|
|
5554
|
+
get orientation() {
|
|
5555
|
+
return orientation;
|
|
5556
|
+
},
|
|
5557
|
+
get safeArea() {
|
|
5558
|
+
return safeArea;
|
|
5559
|
+
},
|
|
5462
5560
|
get paused() {
|
|
5463
5561
|
return hostPaused || userPaused;
|
|
5464
5562
|
},
|
|
@@ -6318,7 +6416,7 @@ ${msg}`);
|
|
|
6318
6416
|
};
|
|
6319
6417
|
|
|
6320
6418
|
// src/filters.ts
|
|
6321
|
-
var
|
|
6419
|
+
var clamp012 = (v) => Math.min(Math.max(v, 0), 1);
|
|
6322
6420
|
var pos = (v) => Math.max(v, 0);
|
|
6323
6421
|
var Base = class {
|
|
6324
6422
|
constructor() {
|
|
@@ -6370,11 +6468,11 @@ var Contrast = class extends Amount {
|
|
|
6370
6468
|
};
|
|
6371
6469
|
var Invert = class extends Amount {
|
|
6372
6470
|
toMatrix(m) {
|
|
6373
|
-
const a =
|
|
6471
|
+
const a = clamp012(this.amount);
|
|
6374
6472
|
linear(m, 1 - 2 * a, a);
|
|
6375
6473
|
}
|
|
6376
6474
|
css() {
|
|
6377
|
-
return `invert(${
|
|
6475
|
+
return `invert(${clamp012(this.amount)})`;
|
|
6378
6476
|
}
|
|
6379
6477
|
};
|
|
6380
6478
|
var Saturate = class extends Amount {
|
|
@@ -6397,7 +6495,7 @@ var Saturate = class extends Amount {
|
|
|
6397
6495
|
};
|
|
6398
6496
|
var Grayscale = class extends Amount {
|
|
6399
6497
|
toMatrix(m) {
|
|
6400
|
-
const k = 1 -
|
|
6498
|
+
const k = 1 - clamp012(this.amount);
|
|
6401
6499
|
m9[0] = 0.2126 + 0.7874 * k;
|
|
6402
6500
|
m9[1] = 0.7152 - 0.7152 * k;
|
|
6403
6501
|
m9[2] = 0.0722 - 0.0722 * k;
|
|
@@ -6410,12 +6508,12 @@ var Grayscale = class extends Amount {
|
|
|
6410
6508
|
rgb3(m, m9);
|
|
6411
6509
|
}
|
|
6412
6510
|
css() {
|
|
6413
|
-
return `grayscale(${
|
|
6511
|
+
return `grayscale(${clamp012(this.amount)})`;
|
|
6414
6512
|
}
|
|
6415
6513
|
};
|
|
6416
6514
|
var Sepia = class extends Amount {
|
|
6417
6515
|
toMatrix(m) {
|
|
6418
|
-
const k = 1 -
|
|
6516
|
+
const k = 1 - clamp012(this.amount);
|
|
6419
6517
|
m9[0] = 0.393 + 0.607 * k;
|
|
6420
6518
|
m9[1] = 0.769 - 0.769 * k;
|
|
6421
6519
|
m9[2] = 0.189 - 0.189 * k;
|
|
@@ -6428,7 +6526,7 @@ var Sepia = class extends Amount {
|
|
|
6428
6526
|
rgb3(m, m9);
|
|
6429
6527
|
}
|
|
6430
6528
|
css() {
|
|
6431
|
-
return `sepia(${
|
|
6529
|
+
return `sepia(${clamp012(this.amount)})`;
|
|
6432
6530
|
}
|
|
6433
6531
|
};
|
|
6434
6532
|
var HueRotate = class extends Amount {
|
|
@@ -6477,7 +6575,7 @@ var Tint = class extends Amount {
|
|
|
6477
6575
|
return i + 2;
|
|
6478
6576
|
}
|
|
6479
6577
|
toMatrix(m) {
|
|
6480
|
-
const a =
|
|
6578
|
+
const a = clamp012(this.amount);
|
|
6481
6579
|
linear(m, 1 - a, 0);
|
|
6482
6580
|
m[4] = (this.c >> 16 & 255) / 255 * a;
|
|
6483
6581
|
m[9] = (this.c >> 8 & 255) / 255 * a;
|
|
@@ -6488,7 +6586,7 @@ var Tint = class extends Amount {
|
|
|
6488
6586
|
return null;
|
|
6489
6587
|
}
|
|
6490
6588
|
paint2d(ctx, x, y, w2, h) {
|
|
6491
|
-
const a =
|
|
6589
|
+
const a = clamp012(this.amount);
|
|
6492
6590
|
if (a <= 0) return;
|
|
6493
6591
|
ctx.save();
|
|
6494
6592
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
@@ -6554,7 +6652,7 @@ var DropShadow = class extends Base {
|
|
|
6554
6652
|
return this.a;
|
|
6555
6653
|
}
|
|
6556
6654
|
set opacity(v) {
|
|
6557
|
-
this.a =
|
|
6655
|
+
this.a = clamp012(v);
|
|
6558
6656
|
}
|
|
6559
6657
|
get color() {
|
|
6560
6658
|
return this.rgb;
|