@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 (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,6 +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
|
+
}
|
|
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);
|
|
122
193
|
var POSE_HEADER = 12;
|
|
123
194
|
var POSE_BODY_BYTES = LANDMARKS * 4;
|
|
124
195
|
var poseBytes = (bodies) => POSE_HEADER + bodies * POSE_BODY_BYTES;
|
|
@@ -6762,11 +6833,20 @@ function wrists(body) {
|
|
|
6762
6833
|
return out;
|
|
6763
6834
|
}
|
|
6764
6835
|
var playerColor = (player) => PLAYER_COLORS[((player - 1) % PLAYER_COLORS.length + PLAYER_COLORS.length) % PLAYER_COLORS.length];
|
|
6765
|
-
|
|
6836
|
+
var SIDES2 = ["left", "right"];
|
|
6837
|
+
function handsOf(frames, toScreen2, width, height, bodyOf = () => null, aim = {}) {
|
|
6766
6838
|
const out = [];
|
|
6767
6839
|
for (const f2 of frames) {
|
|
6768
6840
|
const body = bodyOf(f2) ?? f2.body;
|
|
6769
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
|
+
}
|
|
6770
6850
|
for (const w2 of wrists(body)) {
|
|
6771
6851
|
const s = toScreen2(w2);
|
|
6772
6852
|
out.push({ player: f2.id, side: w2.side, x: s.x * width, y: s.y * height, color: playerColor(f2.id) });
|
|
@@ -7428,6 +7508,10 @@ var HandMarkers = class extends Node {
|
|
|
7428
7508
|
}
|
|
7429
7509
|
};
|
|
7430
7510
|
var now2 = () => performance.now();
|
|
7511
|
+
var frozenSafeArea = (v) => {
|
|
7512
|
+
const sa = parseSafeArea(v);
|
|
7513
|
+
return sa && Object.freeze(sa);
|
|
7514
|
+
};
|
|
7431
7515
|
async function createEngine(opts = {}) {
|
|
7432
7516
|
const play = opts.play ?? null;
|
|
7433
7517
|
let canvas = opts.canvas ?? null;
|
|
@@ -7562,6 +7646,8 @@ async function createEngine(opts = {}) {
|
|
|
7562
7646
|
ro?.observe(canvas);
|
|
7563
7647
|
let backdrop = play?.backdrop === "video" ? "video" : "none";
|
|
7564
7648
|
let view = play?.view ?? null;
|
|
7649
|
+
let orientation = play?.orientation === "portrait" ? "portrait" : "landscape";
|
|
7650
|
+
let safeArea = frozenSafeArea(play?.safeArea) ?? NO_SAFE_AREA;
|
|
7565
7651
|
let livePlayers = [];
|
|
7566
7652
|
let lastPlayersAt = -Infinity;
|
|
7567
7653
|
let hostPaused = false;
|
|
@@ -7589,7 +7675,7 @@ async function createEngine(opts = {}) {
|
|
|
7589
7675
|
lastPlayersAt = now2();
|
|
7590
7676
|
}
|
|
7591
7677
|
};
|
|
7592
|
-
const
|
|
7678
|
+
const hold2 = (on) => {
|
|
7593
7679
|
hostPaused = on;
|
|
7594
7680
|
syncSound();
|
|
7595
7681
|
};
|
|
@@ -7603,6 +7689,11 @@ async function createEngine(opts = {}) {
|
|
|
7603
7689
|
};
|
|
7604
7690
|
unlisten.push(
|
|
7605
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
|
+
}),
|
|
7606
7697
|
// The simple API knows muted only from the start (its getter is false before): read it again then.
|
|
7607
7698
|
play.on("start", (m) => {
|
|
7608
7699
|
if (typeof m?.muted === "boolean") sound.muted = m.muted;
|
|
@@ -7611,11 +7702,11 @@ async function createEngine(opts = {}) {
|
|
|
7611
7702
|
play.on("players", (m) => setPlayers(m?.players)),
|
|
7612
7703
|
play.on("backdrop", (m) => setBackdrop(m?.backdrop, m?.view)),
|
|
7613
7704
|
play.on("start", (m) => setBackdrop(m?.backdrop, m?.view ?? play.view)),
|
|
7614
|
-
play.on("pause", () =>
|
|
7615
|
-
play.on("resume", () =>
|
|
7616
|
-
play.on("break-start", () =>
|
|
7617
|
-
play.on("break-done", () =>
|
|
7618
|
-
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))
|
|
7619
7710
|
);
|
|
7620
7711
|
sound.muted = play.muted === true;
|
|
7621
7712
|
}
|
|
@@ -7626,11 +7717,13 @@ async function createEngine(opts = {}) {
|
|
|
7626
7717
|
let tick = 0;
|
|
7627
7718
|
const live = () => now2() - lastPlayersAt < 1e3 ? livePlayers : [];
|
|
7628
7719
|
const toScreen2 = (p) => play ? play.toScreen(p) : toScreen(view, p);
|
|
7720
|
+
const hold = new AimHold();
|
|
7629
7721
|
const allHands = () => {
|
|
7630
7722
|
if (handsAt === tick) return handsCache;
|
|
7631
7723
|
handsAt = tick;
|
|
7632
|
-
const
|
|
7633
|
-
|
|
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);
|
|
7634
7727
|
return handsCache;
|
|
7635
7728
|
};
|
|
7636
7729
|
const box = { x: 0, y: 0, w: 1, h: 1 };
|
|
@@ -7793,6 +7886,12 @@ async function createEngine(opts = {}) {
|
|
|
7793
7886
|
get view() {
|
|
7794
7887
|
return view;
|
|
7795
7888
|
},
|
|
7889
|
+
get orientation() {
|
|
7890
|
+
return orientation;
|
|
7891
|
+
},
|
|
7892
|
+
get safeArea() {
|
|
7893
|
+
return safeArea;
|
|
7894
|
+
},
|
|
7796
7895
|
get paused() {
|
|
7797
7896
|
return hostPaused || userPaused;
|
|
7798
7897
|
},
|
|
@@ -8652,7 +8751,7 @@ ${msg}`);
|
|
|
8652
8751
|
};
|
|
8653
8752
|
|
|
8654
8753
|
// src/filters.ts
|
|
8655
|
-
var
|
|
8754
|
+
var clamp012 = (v) => Math.min(Math.max(v, 0), 1);
|
|
8656
8755
|
var pos = (v) => Math.max(v, 0);
|
|
8657
8756
|
var Base = class {
|
|
8658
8757
|
constructor() {
|
|
@@ -8704,11 +8803,11 @@ var Contrast = class extends Amount {
|
|
|
8704
8803
|
};
|
|
8705
8804
|
var Invert = class extends Amount {
|
|
8706
8805
|
toMatrix(m) {
|
|
8707
|
-
const a =
|
|
8806
|
+
const a = clamp012(this.amount);
|
|
8708
8807
|
linear(m, 1 - 2 * a, a);
|
|
8709
8808
|
}
|
|
8710
8809
|
css() {
|
|
8711
|
-
return `invert(${
|
|
8810
|
+
return `invert(${clamp012(this.amount)})`;
|
|
8712
8811
|
}
|
|
8713
8812
|
};
|
|
8714
8813
|
var Saturate = class extends Amount {
|
|
@@ -8731,7 +8830,7 @@ var Saturate = class extends Amount {
|
|
|
8731
8830
|
};
|
|
8732
8831
|
var Grayscale = class extends Amount {
|
|
8733
8832
|
toMatrix(m) {
|
|
8734
|
-
const k = 1 -
|
|
8833
|
+
const k = 1 - clamp012(this.amount);
|
|
8735
8834
|
m9[0] = 0.2126 + 0.7874 * k;
|
|
8736
8835
|
m9[1] = 0.7152 - 0.7152 * k;
|
|
8737
8836
|
m9[2] = 0.0722 - 0.0722 * k;
|
|
@@ -8744,12 +8843,12 @@ var Grayscale = class extends Amount {
|
|
|
8744
8843
|
rgb3(m, m9);
|
|
8745
8844
|
}
|
|
8746
8845
|
css() {
|
|
8747
|
-
return `grayscale(${
|
|
8846
|
+
return `grayscale(${clamp012(this.amount)})`;
|
|
8748
8847
|
}
|
|
8749
8848
|
};
|
|
8750
8849
|
var Sepia = class extends Amount {
|
|
8751
8850
|
toMatrix(m) {
|
|
8752
|
-
const k = 1 -
|
|
8851
|
+
const k = 1 - clamp012(this.amount);
|
|
8753
8852
|
m9[0] = 0.393 + 0.607 * k;
|
|
8754
8853
|
m9[1] = 0.769 - 0.769 * k;
|
|
8755
8854
|
m9[2] = 0.189 - 0.189 * k;
|
|
@@ -8762,7 +8861,7 @@ var Sepia = class extends Amount {
|
|
|
8762
8861
|
rgb3(m, m9);
|
|
8763
8862
|
}
|
|
8764
8863
|
css() {
|
|
8765
|
-
return `sepia(${
|
|
8864
|
+
return `sepia(${clamp012(this.amount)})`;
|
|
8766
8865
|
}
|
|
8767
8866
|
};
|
|
8768
8867
|
var HueRotate = class extends Amount {
|
|
@@ -8811,7 +8910,7 @@ var Tint = class extends Amount {
|
|
|
8811
8910
|
return i + 2;
|
|
8812
8911
|
}
|
|
8813
8912
|
toMatrix(m) {
|
|
8814
|
-
const a =
|
|
8913
|
+
const a = clamp012(this.amount);
|
|
8815
8914
|
linear(m, 1 - a, 0);
|
|
8816
8915
|
m[4] = (this.c >> 16 & 255) / 255 * a;
|
|
8817
8916
|
m[9] = (this.c >> 8 & 255) / 255 * a;
|
|
@@ -8822,7 +8921,7 @@ var Tint = class extends Amount {
|
|
|
8822
8921
|
return null;
|
|
8823
8922
|
}
|
|
8824
8923
|
paint2d(ctx, x, y, w2, h) {
|
|
8825
|
-
const a =
|
|
8924
|
+
const a = clamp012(this.amount);
|
|
8826
8925
|
if (a <= 0) return;
|
|
8827
8926
|
ctx.save();
|
|
8828
8927
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
@@ -8888,7 +8987,7 @@ var DropShadow = class extends Base {
|
|
|
8888
8987
|
return this.a;
|
|
8889
8988
|
}
|
|
8890
8989
|
set opacity(v) {
|
|
8891
|
-
this.a =
|
|
8990
|
+
this.a = clamp012(v);
|
|
8892
8991
|
}
|
|
8893
8992
|
get color() {
|
|
8894
8993
|
return this.rgb;
|