@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 (full) | 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); });
|
|
@@ -119,7 +119,77 @@ var defaultAvatarFor = (slot, ageRange) => {
|
|
|
119
119
|
return list[Number.isInteger(slot) && slot >= 0 ? slot % list.length : 0];
|
|
120
120
|
};
|
|
121
121
|
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 };
|
|
122
|
+
var AIM_VISIBLE = 0.5;
|
|
123
|
+
var HAND_SPACE = { left: 1.6, top: 1.6, w: 3.2, h: 3 };
|
|
124
|
+
var TORSO_PER_SHOULDER_WIDTH = 1.2;
|
|
125
|
+
function aimPoint(who, side, opts = {}) {
|
|
126
|
+
const body = Array.isArray(who) ? who : who.body;
|
|
127
|
+
if (!Array.isArray(body)) return null;
|
|
128
|
+
const w2 = side === "left" ? P.leftWrist : P.rightWrist;
|
|
129
|
+
const at = (i) => isNumber(body[i * 3]) && isNumber(body[i * 3 + 1]) && (body[i * 3 + 2] ?? 0) >= AIM_VISIBLE ? i : -1;
|
|
130
|
+
if (at(w2) < 0) return null;
|
|
131
|
+
const wx = body[w2 * 3];
|
|
132
|
+
const wy = body[w2 * 3 + 1];
|
|
133
|
+
if (opts.orientation !== "portrait") return toScreen(opts.view, { x: wx, y: wy });
|
|
134
|
+
if (at(P.leftShoulder) < 0 || at(P.rightShoulder) < 0) return null;
|
|
135
|
+
const ls = P.leftShoulder * 3;
|
|
136
|
+
const rs = P.rightShoulder * 3;
|
|
137
|
+
const sx = (body[ls] + body[rs]) / 2;
|
|
138
|
+
const sy = (body[ls + 1] + body[rs + 1]) / 2;
|
|
139
|
+
const lh = P.leftHip * 3;
|
|
140
|
+
const rh = P.rightHip * 3;
|
|
141
|
+
const hx = (body[lh] + body[rh]) / 2;
|
|
142
|
+
const hy = (body[lh + 1] + body[rh + 1]) / 2;
|
|
143
|
+
let torso = isNumber(hx) && isNumber(hy) ? Math.hypot(hx - sx, hy - sy) : 0;
|
|
144
|
+
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]));
|
|
145
|
+
torso = Math.max(torso, 0.01);
|
|
146
|
+
return {
|
|
147
|
+
x: clamp01((wx - sx + HAND_SPACE.left * torso) / (HAND_SPACE.w * torso)),
|
|
148
|
+
y: clamp01((wy - sy + HAND_SPACE.top * torso) / (HAND_SPACE.h * torso))
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
var AIM_HOLD_MS = 300;
|
|
152
|
+
var AimHold = class {
|
|
153
|
+
constructor(holdMs = AIM_HOLD_MS) {
|
|
154
|
+
__publicField(this, "holdMs", holdMs);
|
|
155
|
+
__publicField(this, "last", /* @__PURE__ */ new Map());
|
|
156
|
+
}
|
|
157
|
+
/** A number for a player's hand. */
|
|
158
|
+
static key(player, side) {
|
|
159
|
+
return player * 2 + (side === "right" ? 1 : 0);
|
|
160
|
+
}
|
|
161
|
+
/** The aim to use at time t (ms) for the hand key, given what aimPoint said now (p, or null if lost). */
|
|
162
|
+
at(key, p, t) {
|
|
163
|
+
const l = this.last.get(key);
|
|
164
|
+
if (p) {
|
|
165
|
+
if (l) {
|
|
166
|
+
l.x = p.x;
|
|
167
|
+
l.y = p.y;
|
|
168
|
+
l.t = t;
|
|
169
|
+
} else this.last.set(key, { x: p.x, y: p.y, t });
|
|
170
|
+
return p;
|
|
171
|
+
}
|
|
172
|
+
if (!l) return null;
|
|
173
|
+
if (t - l.t <= this.holdMs) return { x: l.x, y: l.y };
|
|
174
|
+
this.last.delete(key);
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
/** Forgets every hand. */
|
|
178
|
+
clear() {
|
|
179
|
+
this.last.clear();
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
var MAX_SAFE_INSET = 0.25;
|
|
183
|
+
var NO_SAFE_AREA = Object.freeze({ top: 0, bottom: 0, left: 0, right: 0 });
|
|
184
|
+
function parseSafeArea(v) {
|
|
185
|
+
if (!isObject(v)) return void 0;
|
|
186
|
+
const f2 = (n) => isNumber(n) ? Math.min(Math.max(n, 0), MAX_SAFE_INSET) : 0;
|
|
187
|
+
return { top: f2(v.top), bottom: f2(v.bottom), left: f2(v.left), right: f2(v.right) };
|
|
188
|
+
}
|
|
122
189
|
var COVER_ASPECT = 16 / 9;
|
|
190
|
+
var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
191
|
+
var isNumber = (v) => typeof v === "number" && Number.isFinite(v);
|
|
192
|
+
var clamp01 = (n) => Math.min(Math.max(n, 0), 1);
|
|
123
193
|
var POSE_HEADER = 12;
|
|
124
194
|
var POSE_BODY_BYTES = LANDMARKS * 4;
|
|
125
195
|
var poseBytes = (bodies) => POSE_HEADER + bodies * POSE_BODY_BYTES;
|
|
@@ -6763,11 +6833,20 @@ function wrists(body) {
|
|
|
6763
6833
|
return out;
|
|
6764
6834
|
}
|
|
6765
6835
|
var playerColor = (player) => PLAYER_COLORS[((player - 1) % PLAYER_COLORS.length + PLAYER_COLORS.length) % PLAYER_COLORS.length];
|
|
6766
|
-
|
|
6836
|
+
var SIDES2 = ["left", "right"];
|
|
6837
|
+
function handsOf(frames, toScreen2, width, height, bodyOf = () => null, aim = {}) {
|
|
6767
6838
|
const out = [];
|
|
6768
6839
|
for (const f2 of frames) {
|
|
6769
6840
|
const body = bodyOf(f2) ?? f2.body;
|
|
6770
6841
|
if (!Array.isArray(body)) continue;
|
|
6842
|
+
if (aim.orientation === "portrait") {
|
|
6843
|
+
for (const side of SIDES2) {
|
|
6844
|
+
let p = aimPoint(body, side, { orientation: "portrait" });
|
|
6845
|
+
if (aim.hold) p = aim.hold.at(AimHold.key(f2.id, side), p, aim.t ?? 0);
|
|
6846
|
+
if (p) out.push({ player: f2.id, side, x: p.x * width, y: p.y * height, color: playerColor(f2.id) });
|
|
6847
|
+
}
|
|
6848
|
+
continue;
|
|
6849
|
+
}
|
|
6771
6850
|
for (const w2 of wrists(body)) {
|
|
6772
6851
|
const s = toScreen2(w2);
|
|
6773
6852
|
out.push({ player: f2.id, side: w2.side, x: s.x * width, y: s.y * height, color: playerColor(f2.id) });
|
|
@@ -7429,6 +7508,10 @@ var HandMarkers = class extends Node {
|
|
|
7429
7508
|
}
|
|
7430
7509
|
};
|
|
7431
7510
|
var now2 = () => performance.now();
|
|
7511
|
+
var frozenSafeArea = (v) => {
|
|
7512
|
+
const sa = parseSafeArea(v);
|
|
7513
|
+
return sa && Object.freeze(sa);
|
|
7514
|
+
};
|
|
7432
7515
|
async function createEngine(opts = {}) {
|
|
7433
7516
|
const play = opts.play ?? null;
|
|
7434
7517
|
let canvas = opts.canvas ?? null;
|
|
@@ -7563,6 +7646,8 @@ async function createEngine(opts = {}) {
|
|
|
7563
7646
|
ro?.observe(canvas);
|
|
7564
7647
|
let backdrop = play?.backdrop === "video" ? "video" : "none";
|
|
7565
7648
|
let view = play?.view ?? null;
|
|
7649
|
+
let orientation = play?.orientation === "portrait" ? "portrait" : "landscape";
|
|
7650
|
+
let safeArea = frozenSafeArea(play?.safeArea) ?? NO_SAFE_AREA;
|
|
7566
7651
|
let livePlayers = [];
|
|
7567
7652
|
let lastPlayersAt = -Infinity;
|
|
7568
7653
|
let hostPaused = false;
|
|
@@ -7590,7 +7675,7 @@ async function createEngine(opts = {}) {
|
|
|
7590
7675
|
lastPlayersAt = now2();
|
|
7591
7676
|
}
|
|
7592
7677
|
};
|
|
7593
|
-
const
|
|
7678
|
+
const hold2 = (on) => {
|
|
7594
7679
|
hostPaused = on;
|
|
7595
7680
|
syncSound();
|
|
7596
7681
|
};
|
|
@@ -7604,6 +7689,11 @@ async function createEngine(opts = {}) {
|
|
|
7604
7689
|
};
|
|
7605
7690
|
unlisten.push(
|
|
7606
7691
|
play.on("start", (m) => lateHint(m?.device)),
|
|
7692
|
+
play.on("start", (m) => {
|
|
7693
|
+
if (m?.orientation === "portrait" || m?.orientation === "landscape") orientation = m.orientation;
|
|
7694
|
+
const sa = frozenSafeArea(m?.safeArea);
|
|
7695
|
+
if (sa) safeArea = sa;
|
|
7696
|
+
}),
|
|
7607
7697
|
// The simple API knows muted only from the start (its getter is false before): read it again then.
|
|
7608
7698
|
play.on("start", (m) => {
|
|
7609
7699
|
if (typeof m?.muted === "boolean") sound.muted = m.muted;
|
|
@@ -7612,11 +7702,11 @@ async function createEngine(opts = {}) {
|
|
|
7612
7702
|
play.on("players", (m) => setPlayers(m?.players)),
|
|
7613
7703
|
play.on("backdrop", (m) => setBackdrop(m?.backdrop, m?.view)),
|
|
7614
7704
|
play.on("start", (m) => setBackdrop(m?.backdrop, m?.view ?? play.view)),
|
|
7615
|
-
play.on("pause", () =>
|
|
7616
|
-
play.on("resume", () =>
|
|
7617
|
-
play.on("break-start", () =>
|
|
7618
|
-
play.on("break-done", () =>
|
|
7619
|
-
play.on("break-end", () =>
|
|
7705
|
+
play.on("pause", () => hold2(true)),
|
|
7706
|
+
play.on("resume", () => hold2(false)),
|
|
7707
|
+
play.on("break-start", () => hold2(true)),
|
|
7708
|
+
play.on("break-done", () => hold2(false)),
|
|
7709
|
+
play.on("break-end", () => hold2(false))
|
|
7620
7710
|
);
|
|
7621
7711
|
sound.muted = play.muted === true;
|
|
7622
7712
|
}
|
|
@@ -7627,11 +7717,13 @@ async function createEngine(opts = {}) {
|
|
|
7627
7717
|
let tick = 0;
|
|
7628
7718
|
const live = () => now2() - lastPlayersAt < 1e3 ? livePlayers : [];
|
|
7629
7719
|
const toScreen2 = (p) => play ? play.toScreen(p) : toScreen(view, p);
|
|
7720
|
+
const hold = new AimHold();
|
|
7630
7721
|
const allHands = () => {
|
|
7631
7722
|
if (handsAt === tick) return handsCache;
|
|
7632
7723
|
handsAt = tick;
|
|
7633
|
-
const
|
|
7634
|
-
|
|
7724
|
+
const portrait = orientation === "portrait";
|
|
7725
|
+
const bodyOf = backdrop === "video" && !portrait || !play?.smoothedBody ? () => null : (f2) => play.smoothedBody(f2);
|
|
7726
|
+
handsCache = handsOf(live(), toScreen2, stageW, STAGE_HEIGHT, bodyOf, portrait ? { orientation, hold, t: now2() } : void 0);
|
|
7635
7727
|
return handsCache;
|
|
7636
7728
|
};
|
|
7637
7729
|
const box = { x: 0, y: 0, w: 1, h: 1 };
|
|
@@ -7794,6 +7886,12 @@ async function createEngine(opts = {}) {
|
|
|
7794
7886
|
get view() {
|
|
7795
7887
|
return view;
|
|
7796
7888
|
},
|
|
7889
|
+
get orientation() {
|
|
7890
|
+
return orientation;
|
|
7891
|
+
},
|
|
7892
|
+
get safeArea() {
|
|
7893
|
+
return safeArea;
|
|
7894
|
+
},
|
|
7797
7895
|
get paused() {
|
|
7798
7896
|
return hostPaused || userPaused;
|
|
7799
7897
|
},
|
|
@@ -8653,7 +8751,7 @@ ${msg}`);
|
|
|
8653
8751
|
};
|
|
8654
8752
|
|
|
8655
8753
|
// src/filters.ts
|
|
8656
|
-
var
|
|
8754
|
+
var clamp012 = (v) => Math.min(Math.max(v, 0), 1);
|
|
8657
8755
|
var pos = (v) => Math.max(v, 0);
|
|
8658
8756
|
var Base = class {
|
|
8659
8757
|
constructor() {
|
|
@@ -8705,11 +8803,11 @@ var Contrast = class extends Amount {
|
|
|
8705
8803
|
};
|
|
8706
8804
|
var Invert = class extends Amount {
|
|
8707
8805
|
toMatrix(m) {
|
|
8708
|
-
const a =
|
|
8806
|
+
const a = clamp012(this.amount);
|
|
8709
8807
|
linear(m, 1 - 2 * a, a);
|
|
8710
8808
|
}
|
|
8711
8809
|
css() {
|
|
8712
|
-
return `invert(${
|
|
8810
|
+
return `invert(${clamp012(this.amount)})`;
|
|
8713
8811
|
}
|
|
8714
8812
|
};
|
|
8715
8813
|
var Saturate = class extends Amount {
|
|
@@ -8732,7 +8830,7 @@ var Saturate = class extends Amount {
|
|
|
8732
8830
|
};
|
|
8733
8831
|
var Grayscale = class extends Amount {
|
|
8734
8832
|
toMatrix(m) {
|
|
8735
|
-
const k = 1 -
|
|
8833
|
+
const k = 1 - clamp012(this.amount);
|
|
8736
8834
|
m9[0] = 0.2126 + 0.7874 * k;
|
|
8737
8835
|
m9[1] = 0.7152 - 0.7152 * k;
|
|
8738
8836
|
m9[2] = 0.0722 - 0.0722 * k;
|
|
@@ -8745,12 +8843,12 @@ var Grayscale = class extends Amount {
|
|
|
8745
8843
|
rgb3(m, m9);
|
|
8746
8844
|
}
|
|
8747
8845
|
css() {
|
|
8748
|
-
return `grayscale(${
|
|
8846
|
+
return `grayscale(${clamp012(this.amount)})`;
|
|
8749
8847
|
}
|
|
8750
8848
|
};
|
|
8751
8849
|
var Sepia = class extends Amount {
|
|
8752
8850
|
toMatrix(m) {
|
|
8753
|
-
const k = 1 -
|
|
8851
|
+
const k = 1 - clamp012(this.amount);
|
|
8754
8852
|
m9[0] = 0.393 + 0.607 * k;
|
|
8755
8853
|
m9[1] = 0.769 - 0.769 * k;
|
|
8756
8854
|
m9[2] = 0.189 - 0.189 * k;
|
|
@@ -8763,7 +8861,7 @@ var Sepia = class extends Amount {
|
|
|
8763
8861
|
rgb3(m, m9);
|
|
8764
8862
|
}
|
|
8765
8863
|
css() {
|
|
8766
|
-
return `sepia(${
|
|
8864
|
+
return `sepia(${clamp012(this.amount)})`;
|
|
8767
8865
|
}
|
|
8768
8866
|
};
|
|
8769
8867
|
var HueRotate = class extends Amount {
|
|
@@ -8812,7 +8910,7 @@ var Tint = class extends Amount {
|
|
|
8812
8910
|
return i + 2;
|
|
8813
8911
|
}
|
|
8814
8912
|
toMatrix(m) {
|
|
8815
|
-
const a =
|
|
8913
|
+
const a = clamp012(this.amount);
|
|
8816
8914
|
linear(m, 1 - a, 0);
|
|
8817
8915
|
m[4] = (this.c >> 16 & 255) / 255 * a;
|
|
8818
8916
|
m[9] = (this.c >> 8 & 255) / 255 * a;
|
|
@@ -8823,7 +8921,7 @@ var Tint = class extends Amount {
|
|
|
8823
8921
|
return null;
|
|
8824
8922
|
}
|
|
8825
8923
|
paint2d(ctx, x, y, w2, h) {
|
|
8826
|
-
const a =
|
|
8924
|
+
const a = clamp012(this.amount);
|
|
8827
8925
|
if (a <= 0) return;
|
|
8828
8926
|
ctx.save();
|
|
8829
8927
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
@@ -8889,7 +8987,7 @@ var DropShadow = class extends Base {
|
|
|
8889
8987
|
return this.a;
|
|
8890
8988
|
}
|
|
8891
8989
|
set opacity(v) {
|
|
8892
|
-
this.a =
|
|
8990
|
+
this.a = clamp012(v);
|
|
8893
8991
|
}
|
|
8894
8992
|
get color() {
|
|
8895
8993
|
return this.rgb;
|