@map-gesture-controls/core 0.1.7 → 0.1.9
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 +29 -13
- package/dist/GestureController.d.ts +2 -0
- package/dist/GestureController.js +16 -2
- package/dist/GestureStateMachine.d.ts +17 -7
- package/dist/GestureStateMachine.js +151 -32
- package/dist/GestureStateMachine.test.js +124 -23
- package/dist/WebcamOverlay.js +1 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +7 -0
- package/dist/gestureClassifier.d.ts +18 -3
- package/dist/gestureClassifier.js +73 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +197 -144
- package/dist/types.d.ts +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
import { FilesetResolver as
|
|
5
|
-
const
|
|
1
|
+
var O = Object.defineProperty;
|
|
2
|
+
var x = (i, t, e) => t in i ? O(i, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : i[t] = e;
|
|
3
|
+
var l = (i, t, e) => x(i, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
+
import { FilesetResolver as H } from "@mediapipe/tasks-vision";
|
|
5
|
+
const Y = {
|
|
6
6
|
enabled: !0,
|
|
7
7
|
mode: "corner",
|
|
8
8
|
opacity: 0.85,
|
|
9
9
|
position: "bottom-right",
|
|
10
10
|
width: 320,
|
|
11
11
|
height: 240
|
|
12
|
-
},
|
|
12
|
+
}, Z = {
|
|
13
13
|
actionDwellMs: 80,
|
|
14
14
|
releaseGraceMs: 150,
|
|
15
15
|
panDeadzonePx: 10,
|
|
@@ -18,7 +18,7 @@ const L = {
|
|
|
18
18
|
minDetectionConfidence: 0.65,
|
|
19
19
|
minTrackingConfidence: 0.65,
|
|
20
20
|
minPresenceConfidence: 0.6
|
|
21
|
-
},
|
|
21
|
+
}, r = {
|
|
22
22
|
WRIST: 0,
|
|
23
23
|
THUMB_TIP: 4,
|
|
24
24
|
INDEX_TIP: 8,
|
|
@@ -29,75 +29,97 @@ const L = {
|
|
|
29
29
|
RING_MCP: 13,
|
|
30
30
|
PINKY_TIP: 20,
|
|
31
31
|
PINKY_MCP: 17
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
],
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
],
|
|
32
|
+
}, L = 0.25, z = 0.35, E = [
|
|
33
|
+
r.INDEX_TIP,
|
|
34
|
+
r.MIDDLE_TIP,
|
|
35
|
+
r.RING_TIP,
|
|
36
|
+
r.PINKY_TIP
|
|
37
|
+
], _ = [
|
|
38
|
+
r.INDEX_MCP,
|
|
39
|
+
r.MIDDLE_MCP,
|
|
40
|
+
r.RING_MCP,
|
|
41
|
+
r.PINKY_MCP
|
|
42
|
+
], D = {
|
|
43
43
|
idle: "#888888",
|
|
44
44
|
panning: "#00ccff",
|
|
45
45
|
zooming: "#00ffcc",
|
|
46
|
+
rotating: "#ff9900",
|
|
46
47
|
landmark: "rgba(255,255,255,0.6)",
|
|
47
48
|
connection: "rgba(255,255,255,0.3)",
|
|
48
49
|
fingertipGlow: "#4488ff"
|
|
49
|
-
},
|
|
50
|
-
function
|
|
51
|
-
const e =
|
|
50
|
+
}, G = "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.14/wasm";
|
|
51
|
+
function T(i, t) {
|
|
52
|
+
const e = i.x - t.x, n = i.y - t.y;
|
|
52
53
|
return Math.sqrt(e * e + n * n);
|
|
53
54
|
}
|
|
54
|
-
function
|
|
55
|
-
const t =
|
|
56
|
-
for (let
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
55
|
+
function N(i) {
|
|
56
|
+
const t = i[r.WRIST];
|
|
57
|
+
for (let s = 0; s < E.length; s++) {
|
|
58
|
+
const u = i[E[s]], c = i[_[s]];
|
|
59
|
+
if (T(u, t) < T(c, t) * 0.9)
|
|
59
60
|
return !1;
|
|
60
61
|
}
|
|
61
|
-
const e =
|
|
62
|
+
const e = w(i);
|
|
62
63
|
if (e === 0) return !1;
|
|
63
|
-
const n =
|
|
64
|
-
let
|
|
65
|
-
for (let
|
|
66
|
-
const
|
|
67
|
-
|
|
64
|
+
const n = E.map((s) => i[s]);
|
|
65
|
+
let o = 1 / 0;
|
|
66
|
+
for (let s = 0; s < n.length - 1; s++) {
|
|
67
|
+
const u = T(n[s], n[s + 1]);
|
|
68
|
+
u < o && (o = u);
|
|
68
69
|
}
|
|
69
|
-
return
|
|
70
|
+
return o >= e * 0.18;
|
|
70
71
|
}
|
|
71
|
-
function
|
|
72
|
-
const t =
|
|
72
|
+
function R(i) {
|
|
73
|
+
const t = i[r.WRIST];
|
|
73
74
|
let e = 0;
|
|
74
|
-
for (let n = 0; n <
|
|
75
|
-
const
|
|
76
|
-
|
|
75
|
+
for (let n = 0; n < E.length; n++) {
|
|
76
|
+
const o = i[E[n]], a = i[_[n]];
|
|
77
|
+
T(o, t) < T(a, t) * 1.1 && e++;
|
|
77
78
|
}
|
|
78
79
|
return e >= 3;
|
|
79
80
|
}
|
|
80
|
-
function
|
|
81
|
-
const t =
|
|
82
|
-
return !t || !e ? 0 :
|
|
81
|
+
function w(i) {
|
|
82
|
+
const t = i[r.WRIST], e = i[r.MIDDLE_MCP];
|
|
83
|
+
return !t || !e ? 0 : T(t, e);
|
|
83
84
|
}
|
|
84
|
-
function
|
|
85
|
-
const e =
|
|
86
|
-
return !e || !n ? 0 :
|
|
85
|
+
function j(i, t) {
|
|
86
|
+
const e = i[r.INDEX_TIP], n = t[r.INDEX_TIP];
|
|
87
|
+
return !e || !n ? 0 : T(e, n);
|
|
87
88
|
}
|
|
88
|
-
function
|
|
89
|
-
|
|
89
|
+
function F(i) {
|
|
90
|
+
const t = w(i);
|
|
91
|
+
if (t === 0) return !1;
|
|
92
|
+
const e = i[r.THUMB_TIP], n = i[r.INDEX_TIP];
|
|
93
|
+
return T(e, n) < t * L;
|
|
90
94
|
}
|
|
91
|
-
|
|
95
|
+
function B(i) {
|
|
96
|
+
const t = w(i);
|
|
97
|
+
if (t === 0) return !1;
|
|
98
|
+
const e = i[r.THUMB_TIP], n = i[r.INDEX_TIP];
|
|
99
|
+
return T(e, n) < t * z;
|
|
100
|
+
}
|
|
101
|
+
function k(i) {
|
|
102
|
+
return i.length < 21 ? "none" : R(i) ? "fist" : F(i) ? "pinch" : N(i) ? "openPalm" : "none";
|
|
103
|
+
}
|
|
104
|
+
function C() {
|
|
105
|
+
let i = !1;
|
|
106
|
+
return function(e) {
|
|
107
|
+
return e.length < 21 ? (i = !1, "none") : R(e) ? (i = !1, "fist") : (i ? i = B(e) : i = F(e), i ? "pinch" : N(e) ? "openPalm" : "none");
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
class $ {
|
|
92
111
|
constructor(t, e) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
112
|
+
l(this, "landmarker", null);
|
|
113
|
+
l(this, "videoEl", null);
|
|
114
|
+
l(this, "stream", null);
|
|
115
|
+
l(this, "rafHandle", null);
|
|
116
|
+
l(this, "running", !1);
|
|
117
|
+
l(this, "onFrame");
|
|
118
|
+
l(this, "tuning");
|
|
119
|
+
l(this, "lastVideoTime", -1);
|
|
120
|
+
// One stateful classifier per hand label; persists pinch hysteresis across frames.
|
|
121
|
+
l(this, "leftClassifier", C());
|
|
122
|
+
l(this, "rightClassifier", C());
|
|
101
123
|
this.tuning = t, this.onFrame = e;
|
|
102
124
|
}
|
|
103
125
|
/**
|
|
@@ -105,7 +127,7 @@ class k {
|
|
|
105
127
|
* Returns the video element so the overlay can render it.
|
|
106
128
|
*/
|
|
107
129
|
async init() {
|
|
108
|
-
const t = await
|
|
130
|
+
const t = await H.forVisionTasks(G), { HandLandmarker: e } = await import("@mediapipe/tasks-vision");
|
|
109
131
|
return this.landmarker = await e.createFromOptions(t, {
|
|
110
132
|
baseOptions: {
|
|
111
133
|
modelAssetPath: "https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task",
|
|
@@ -141,30 +163,30 @@ class k {
|
|
|
141
163
|
const n = performance.now();
|
|
142
164
|
if (t.currentTime === this.lastVideoTime) return;
|
|
143
165
|
this.lastVideoTime = t.currentTime;
|
|
144
|
-
let
|
|
166
|
+
let o;
|
|
145
167
|
try {
|
|
146
|
-
|
|
168
|
+
o = e.detectForVideo(t, n);
|
|
147
169
|
} catch {
|
|
148
170
|
return;
|
|
149
171
|
}
|
|
150
|
-
const
|
|
151
|
-
this.onFrame(
|
|
172
|
+
const a = this.buildFrame(o, n);
|
|
173
|
+
this.onFrame(a);
|
|
152
174
|
}
|
|
153
175
|
buildFrame(t, e) {
|
|
154
|
-
const n = t.landmarks.map((
|
|
155
|
-
var
|
|
156
|
-
const c = t.handedness[
|
|
157
|
-
return { handedness:
|
|
158
|
-
}),
|
|
159
|
-
return { timestamp: e, hands: n, leftHand:
|
|
176
|
+
const n = t.landmarks.map((s, u) => {
|
|
177
|
+
var m, p;
|
|
178
|
+
const c = t.handedness[u], d = ((m = c == null ? void 0 : c[0]) == null ? void 0 : m.categoryName) === "Left" ? "Left" : "Right", P = ((p = c == null ? void 0 : c[0]) == null ? void 0 : p.score) ?? 0, f = (d === "Left" ? this.leftClassifier : this.rightClassifier)(s);
|
|
179
|
+
return { handedness: d, score: P, landmarks: s, gesture: f };
|
|
180
|
+
}), o = n.find((s) => s.handedness === "Left") ?? null, a = n.find((s) => s.handedness === "Right") ?? null;
|
|
181
|
+
return { timestamp: e, hands: n, leftHand: o, rightHand: a };
|
|
160
182
|
}
|
|
161
183
|
getVideoElement() {
|
|
162
184
|
return this.videoEl;
|
|
163
185
|
}
|
|
164
186
|
}
|
|
165
|
-
class
|
|
187
|
+
class U {
|
|
166
188
|
constructor(t) {
|
|
167
|
-
|
|
189
|
+
l(this, "value", null);
|
|
168
190
|
this.alpha = t;
|
|
169
191
|
}
|
|
170
192
|
update(t, e) {
|
|
@@ -177,9 +199,9 @@ class R {
|
|
|
177
199
|
this.value = null;
|
|
178
200
|
}
|
|
179
201
|
}
|
|
180
|
-
class
|
|
202
|
+
class M {
|
|
181
203
|
constructor(t) {
|
|
182
|
-
|
|
204
|
+
l(this, "value", null);
|
|
183
205
|
this.alpha = t;
|
|
184
206
|
}
|
|
185
207
|
update(t) {
|
|
@@ -189,65 +211,94 @@ class F {
|
|
|
189
211
|
this.value = null;
|
|
190
212
|
}
|
|
191
213
|
}
|
|
192
|
-
class
|
|
214
|
+
const y = class y {
|
|
193
215
|
constructor(t) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
this
|
|
216
|
+
l(this, "mode", "idle");
|
|
217
|
+
l(this, "actionDwell", null);
|
|
218
|
+
l(this, "releaseTimer", null);
|
|
219
|
+
l(this, "panSmoother");
|
|
220
|
+
l(this, "prevPanPos", null);
|
|
221
|
+
l(this, "zoomSmoother");
|
|
222
|
+
l(this, "prevZoomDist", null);
|
|
223
|
+
l(this, "rotateSmoother");
|
|
224
|
+
l(this, "prevRotateAngle", null);
|
|
225
|
+
// Tracks how many consecutive frames each hand has been active.
|
|
226
|
+
// Used to require the secondary hand to be stable before escalating
|
|
227
|
+
// the mode (e.g. pan → rotate), preventing a single noisy frame from
|
|
228
|
+
// interrupting an ongoing single-hand gesture.
|
|
229
|
+
l(this, "leftActiveFrames", 0);
|
|
230
|
+
l(this, "rightActiveFrames", 0);
|
|
231
|
+
this.tuning = t, this.panSmoother = new U(t.smoothingAlpha), this.zoomSmoother = new M(t.smoothingAlpha), this.rotateSmoother = new M(t.smoothingAlpha);
|
|
202
232
|
}
|
|
203
233
|
getMode() {
|
|
204
234
|
return this.mode;
|
|
205
235
|
}
|
|
206
236
|
update(t) {
|
|
207
|
-
const e = t.timestamp, { actionDwellMs: n, releaseGraceMs:
|
|
237
|
+
const e = t.timestamp, { actionDwellMs: n, releaseGraceMs: o } = this.tuning, { leftHand: a, rightHand: s } = t, u = (f) => f !== null && (f.gesture === "fist" || f.gesture === "pinch"), c = u(s), h = u(a);
|
|
238
|
+
this.leftActiveFrames = h ? this.leftActiveFrames + 1 : 0, this.rightActiveFrames = c ? this.rightActiveFrames + 1 : 0;
|
|
239
|
+
const d = this.leftActiveFrames >= y.ESCALATION_FRAMES && this.rightActiveFrames >= y.ESCALATION_FRAMES, g = d ? "rotating" : h && c && !d && (this.mode === "panning" || this.mode === "zooming") ? this.mode : c ? "zooming" : h ? "panning" : "idle";
|
|
208
240
|
if (this.mode === "idle")
|
|
209
|
-
return
|
|
241
|
+
return g !== "idle" ? this.actionDwell === null || this.actionDwell.gesture !== g ? this.actionDwell = { gesture: g, startMs: e } : e - this.actionDwell.startMs >= n && this.transitionTo(g) : this.actionDwell = null, this.buildOutput(null, null, null);
|
|
210
242
|
if (this.mode === "panning") {
|
|
211
|
-
if (
|
|
212
|
-
return this.
|
|
243
|
+
if (d)
|
|
244
|
+
return this.transitionTo("rotating"), this.buildOutput(null, null, null);
|
|
245
|
+
if (g !== "panning")
|
|
246
|
+
return this.releaseTimer === null ? this.releaseTimer = e : e - this.releaseTimer >= o && this.transitionTo("idle"), this.buildOutput(null, null, null);
|
|
213
247
|
this.releaseTimer = null;
|
|
214
|
-
const
|
|
215
|
-
if (!
|
|
216
|
-
return this.transitionTo("idle"), this.buildOutput(null, null);
|
|
217
|
-
const m =
|
|
218
|
-
let
|
|
248
|
+
const f = u(a) ? a : null;
|
|
249
|
+
if (!f)
|
|
250
|
+
return this.transitionTo("idle"), this.buildOutput(null, null, null);
|
|
251
|
+
const m = f.landmarks[0], p = this.panSmoother.update(m.x, m.y);
|
|
252
|
+
let I = null;
|
|
219
253
|
if (this.prevPanPos !== null) {
|
|
220
|
-
const
|
|
221
|
-
(Math.abs(
|
|
254
|
+
const b = p.x - this.prevPanPos.x, v = p.y - this.prevPanPos.y, A = this.tuning.panDeadzonePx / 640;
|
|
255
|
+
(Math.abs(b) > A || Math.abs(v) > A) && (I = { x: b, y: v });
|
|
222
256
|
}
|
|
223
|
-
return this.prevPanPos = p, this.buildOutput(
|
|
257
|
+
return this.prevPanPos = p, this.buildOutput(I, null, null);
|
|
224
258
|
}
|
|
225
259
|
if (this.mode === "zooming") {
|
|
226
|
-
if (
|
|
227
|
-
return this.
|
|
228
|
-
if (
|
|
229
|
-
return this.transitionTo("idle"), this.buildOutput(null, null);
|
|
230
|
-
|
|
260
|
+
if (d)
|
|
261
|
+
return this.transitionTo("rotating"), this.buildOutput(null, null, null);
|
|
262
|
+
if (g !== "zooming")
|
|
263
|
+
return this.releaseTimer === null ? this.releaseTimer = e : e - this.releaseTimer >= o && this.transitionTo("idle"), this.buildOutput(null, null, null);
|
|
264
|
+
if (this.releaseTimer = null, !s)
|
|
265
|
+
return this.transitionTo("idle"), this.buildOutput(null, null, null);
|
|
266
|
+
const f = s.landmarks[0], m = this.zoomSmoother.update(f.y);
|
|
231
267
|
let p = null;
|
|
232
268
|
if (this.prevZoomDist !== null) {
|
|
233
|
-
const
|
|
234
|
-
Math.abs(
|
|
269
|
+
const I = m - this.prevZoomDist;
|
|
270
|
+
Math.abs(I) > this.tuning.zoomDeadzoneRatio && (p = -I);
|
|
235
271
|
}
|
|
236
|
-
return this.prevZoomDist = m, this.buildOutput(null, p);
|
|
272
|
+
return this.prevZoomDist = m, this.buildOutput(null, p, null);
|
|
237
273
|
}
|
|
238
|
-
|
|
274
|
+
if (this.mode === "rotating") {
|
|
275
|
+
if (g !== "rotating")
|
|
276
|
+
return this.releaseTimer === null ? this.releaseTimer = e : e - this.releaseTimer >= o && this.transitionTo("idle"), this.buildOutput(null, null, null);
|
|
277
|
+
if (this.releaseTimer = null, !a || !s)
|
|
278
|
+
return this.transitionTo("idle"), this.buildOutput(null, null, null);
|
|
279
|
+
const f = a.landmarks[0], m = s.landmarks[0], p = Math.atan2(m.y - f.y, m.x - f.x), I = this.rotateSmoother.update(p);
|
|
280
|
+
let b = null;
|
|
281
|
+
if (this.prevRotateAngle !== null) {
|
|
282
|
+
let v = I - this.prevRotateAngle;
|
|
283
|
+
v > Math.PI && (v -= 2 * Math.PI), v < -Math.PI && (v += 2 * Math.PI), Math.abs(v) > 5e-3 && (b = v);
|
|
284
|
+
}
|
|
285
|
+
return this.prevRotateAngle = I, this.buildOutput(null, null, b);
|
|
286
|
+
}
|
|
287
|
+
return this.buildOutput(null, null, null);
|
|
239
288
|
}
|
|
240
289
|
transitionTo(t) {
|
|
241
|
-
this.mode = t, this.releaseTimer = null, this.actionDwell = null, t !== "panning" && (this.panSmoother.reset(), this.prevPanPos = null), t !== "zooming" && (this.zoomSmoother.reset(), this.prevZoomDist = null);
|
|
290
|
+
this.mode = t, this.releaseTimer = null, this.actionDwell = null, t !== "panning" && t !== "rotating" && (this.leftActiveFrames = 0), t !== "zooming" && t !== "rotating" && (this.rightActiveFrames = 0), t !== "panning" && (this.panSmoother.reset(), this.prevPanPos = null), t !== "zooming" && (this.zoomSmoother.reset(), this.prevZoomDist = null), t !== "rotating" && (this.rotateSmoother.reset(), this.prevRotateAngle = null);
|
|
242
291
|
}
|
|
243
|
-
buildOutput(t, e) {
|
|
244
|
-
return { mode: this.mode, panDelta: t, zoomDelta: e };
|
|
292
|
+
buildOutput(t, e, n) {
|
|
293
|
+
return { mode: this.mode, panDelta: t, zoomDelta: e, rotateDelta: n };
|
|
245
294
|
}
|
|
246
295
|
reset() {
|
|
247
296
|
this.transitionTo("idle");
|
|
248
297
|
}
|
|
249
|
-
}
|
|
250
|
-
|
|
298
|
+
};
|
|
299
|
+
l(y, "ESCALATION_FRAMES", 3);
|
|
300
|
+
let S = y;
|
|
301
|
+
const X = [
|
|
251
302
|
[0, 1],
|
|
252
303
|
[1, 2],
|
|
253
304
|
[2, 3],
|
|
@@ -277,20 +328,20 @@ const O = [
|
|
|
277
328
|
[9, 13],
|
|
278
329
|
[13, 17]
|
|
279
330
|
// palm cross
|
|
280
|
-
],
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
331
|
+
], V = [
|
|
332
|
+
r.THUMB_TIP,
|
|
333
|
+
r.INDEX_TIP,
|
|
334
|
+
r.MIDDLE_TIP,
|
|
335
|
+
r.RING_TIP,
|
|
336
|
+
r.PINKY_TIP
|
|
286
337
|
];
|
|
287
|
-
class
|
|
338
|
+
class q {
|
|
288
339
|
constructor(t) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
340
|
+
l(this, "container");
|
|
341
|
+
l(this, "canvas");
|
|
342
|
+
l(this, "ctx");
|
|
343
|
+
l(this, "badge");
|
|
344
|
+
l(this, "config");
|
|
294
345
|
this.config = t, this.container = document.createElement("div"), this.container.className = "ol-gesture-overlay", this.applyContainerStyles(), this.canvas = document.createElement("canvas"), this.canvas.className = "ol-gesture-canvas", this.canvas.style.cssText = "position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;", this.badge = document.createElement("div"), this.badge.className = "ol-gesture-badge ol-gesture-badge--idle", this.badge.textContent = "Idle", this.container.appendChild(this.canvas), this.container.appendChild(this.badge);
|
|
295
346
|
const e = this.canvas.getContext("2d");
|
|
296
347
|
if (!e) throw new Error("Cannot get 2D canvas context");
|
|
@@ -311,35 +362,36 @@ class U {
|
|
|
311
362
|
/** Called each frame with the latest gesture frame and mode. */
|
|
312
363
|
render(t, e) {
|
|
313
364
|
this.updateBadge(e);
|
|
314
|
-
const n = this.config.width,
|
|
315
|
-
if (this.canvas.width = n, this.canvas.height =
|
|
316
|
-
for (const
|
|
317
|
-
this.drawSkeleton(
|
|
365
|
+
const n = this.config.width, o = this.config.height;
|
|
366
|
+
if (this.canvas.width = n, this.canvas.height = o, this.ctx.clearRect(0, 0, n, o), t !== null)
|
|
367
|
+
for (const a of t.hands)
|
|
368
|
+
this.drawSkeleton(a.landmarks, e, a.gesture === "fist");
|
|
318
369
|
}
|
|
319
370
|
drawSkeleton(t, e, n) {
|
|
320
|
-
const { ctx:
|
|
321
|
-
|
|
322
|
-
for (const [
|
|
323
|
-
!t[
|
|
324
|
-
for (let
|
|
325
|
-
const
|
|
326
|
-
|
|
371
|
+
const { ctx: o } = this, a = this.config.width, s = this.config.height, u = (h) => (1 - h.x) * a, c = (h) => h.y * s;
|
|
372
|
+
o.strokeStyle = D.connection, o.lineWidth = 1.5;
|
|
373
|
+
for (const [h, d] of X)
|
|
374
|
+
!t[h] || !t[d] || (o.beginPath(), o.moveTo(u(t[h]), c(t[h])), o.lineTo(u(t[d]), c(t[d])), o.stroke());
|
|
375
|
+
for (let h = 0; h < t.length; h++) {
|
|
376
|
+
const d = t[h], P = V.includes(h), g = e !== "idle" && P ? D.fingertipGlow : D.landmark;
|
|
377
|
+
o.beginPath(), o.arc(u(d), c(d), P ? 5 : 3, 0, Math.PI * 2), o.fillStyle = g, o.fill(), e !== "idle" && P && (o.shadowBlur = n ? 12 : 6, o.shadowColor = D.fingertipGlow, o.fill(), o.shadowBlur = 0);
|
|
327
378
|
}
|
|
328
379
|
}
|
|
329
380
|
updateBadge(t) {
|
|
330
381
|
const e = {
|
|
331
382
|
idle: "Idle",
|
|
332
383
|
panning: "Pan",
|
|
333
|
-
zooming: "Zoom"
|
|
384
|
+
zooming: "Zoom",
|
|
385
|
+
rotating: "Rotate"
|
|
334
386
|
};
|
|
335
387
|
this.badge.textContent = e[t], this.badge.className = `ol-gesture-badge ol-gesture-badge--${t}`;
|
|
336
388
|
}
|
|
337
389
|
applyContainerStyles() {
|
|
338
|
-
const { mode: t, position: e, width: n, height:
|
|
339
|
-
if (this.container.style.cssText = "", this.container.style.position = "fixed", this.container.style.zIndex = "9999", this.container.style.overflow = "hidden", this.container.style.borderRadius = "8px", this.container.style.opacity = String(
|
|
340
|
-
this.container.style.width = `${n}px`, this.container.style.height = `${
|
|
341
|
-
const
|
|
342
|
-
e === "bottom-right" ? (this.container.style.bottom =
|
|
390
|
+
const { mode: t, position: e, width: n, height: o, opacity: a } = this.config;
|
|
391
|
+
if (this.container.style.cssText = "", this.container.style.position = "fixed", this.container.style.zIndex = "9999", this.container.style.overflow = "hidden", this.container.style.borderRadius = "8px", this.container.style.opacity = String(a), this.container.style.display = t === "hidden" ? "none" : "block", t === "corner") {
|
|
392
|
+
this.container.style.width = `${n}px`, this.container.style.height = `${o}px`;
|
|
393
|
+
const s = "16px";
|
|
394
|
+
e === "bottom-right" ? (this.container.style.bottom = s, this.container.style.right = s) : e === "bottom-left" ? (this.container.style.bottom = s, this.container.style.left = s) : e === "top-right" ? (this.container.style.top = s, this.container.style.right = s) : (this.container.style.top = s, this.container.style.left = s);
|
|
343
395
|
} else t === "full" && (this.container.style.top = "0", this.container.style.left = "0", this.container.style.width = "100vw", this.container.style.height = "100vh", this.container.style.borderRadius = "0");
|
|
344
396
|
}
|
|
345
397
|
updateConfig(t) {
|
|
@@ -347,14 +399,15 @@ class U {
|
|
|
347
399
|
}
|
|
348
400
|
}
|
|
349
401
|
export {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
402
|
+
D as COLORS,
|
|
403
|
+
Z as DEFAULT_TUNING_CONFIG,
|
|
404
|
+
Y as DEFAULT_WEBCAM_CONFIG,
|
|
405
|
+
$ as GestureController,
|
|
406
|
+
S as GestureStateMachine,
|
|
407
|
+
r as LANDMARKS,
|
|
408
|
+
q as WebcamOverlay,
|
|
409
|
+
k as classifyGesture,
|
|
410
|
+
C as createHandClassifier,
|
|
411
|
+
w as getHandSize,
|
|
412
|
+
j as getTwoHandDistance
|
|
360
413
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export type GestureMode = 'idle' | 'panning' | 'zooming';
|
|
1
|
+
export type GestureMode = 'idle' | 'panning' | 'zooming' | 'rotating';
|
|
2
2
|
export type HandednessLabel = 'Left' | 'Right';
|
|
3
|
-
export type GestureType = 'openPalm' | 'fist' | 'none';
|
|
3
|
+
export type GestureType = 'openPalm' | 'fist' | 'pinch' | 'none';
|
|
4
4
|
export interface Point2D {
|
|
5
5
|
x: number;
|
|
6
6
|
y: number;
|