brilliantsole 0.0.27 → 0.0.29
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/assets/3d/anchor.glb +0 -0
- package/assets/3d/coin.glb +0 -0
- package/assets/3d/glasses.glb +0 -0
- package/assets/audio/bounceMedium.wav +0 -0
- package/assets/audio/bounceStrong.wav +0 -0
- package/assets/audio/bounceWeak.wav +0 -0
- package/assets/audio/coin.wav +0 -0
- package/assets/audio/getUp.wav +0 -0
- package/assets/audio/grab.wav +0 -0
- package/assets/audio/kick.wav +0 -0
- package/assets/audio/platterFadeIn old.wav +0 -0
- package/assets/audio/platterFadeIn.wav +0 -0
- package/assets/audio/platterFadeOut.wav +0 -0
- package/assets/audio/punch.wav +0 -0
- package/assets/audio/punchSqueak.wav +0 -0
- package/assets/audio/purr.wav +0 -0
- package/assets/audio/purrFadeOut.wav +0 -0
- package/assets/audio/release.wav +0 -0
- package/assets/audio/splat.wav +0 -0
- package/assets/audio/stomp.wav +0 -0
- package/build/brilliantsole.cjs +3091 -741
- package/build/brilliantsole.cjs.map +1 -1
- package/build/brilliantsole.js +2759 -709
- package/build/brilliantsole.js.map +1 -1
- package/build/brilliantsole.ls.js +2602 -543
- package/build/brilliantsole.ls.js.map +1 -1
- package/build/brilliantsole.min.js +1 -1
- package/build/brilliantsole.min.js.map +1 -1
- package/build/brilliantsole.module.d.ts +295 -65
- package/build/brilliantsole.module.js +2749 -710
- package/build/brilliantsole.module.js.map +1 -1
- package/build/brilliantsole.module.min.d.ts +295 -65
- package/build/brilliantsole.module.min.js +1 -1
- package/build/brilliantsole.module.min.js.map +1 -1
- package/build/brilliantsole.node.module.d.ts +289 -62
- package/build/brilliantsole.node.module.js +3080 -742
- package/build/brilliantsole.node.module.js.map +1 -1
- package/build/dts/BS-output.d.ts +10 -0
- package/build/dts/BS.d.ts +21 -8
- package/build/dts/CameraManager.d.ts +72 -0
- package/build/dts/Device.d.ts +64 -13
- package/build/dts/DeviceInformationManager.d.ts +4 -4
- package/build/dts/DeviceManager.d.ts +2 -0
- package/build/dts/FileTransferManager.d.ts +18 -8
- package/build/dts/InformationManager.d.ts +2 -0
- package/build/dts/MicrophoneManager.d.ts +88 -0
- package/build/dts/TfliteManager.d.ts +22 -2
- package/build/dts/WifiManager.d.ts +61 -0
- package/build/dts/connection/BaseConnectionManager.d.ts +35 -3
- package/build/dts/connection/ClientConnectionManager.d.ts +7 -2
- package/build/dts/connection/bluetooth/NobleConnectionManager.d.ts +2 -1
- package/build/dts/connection/bluetooth/WebBluetoothConnectionManager.d.ts +1 -0
- package/build/dts/connection/bluetooth/bluetoothUUIDs.d.ts +2 -2
- package/build/dts/connection/udp/UDPConnectionManager.d.ts +28 -0
- package/build/dts/connection/webSocket/WebSocketConnectionManager.d.ts +25 -0
- package/build/dts/devicePair/DevicePair.d.ts +5 -5
- package/build/dts/scanner/BaseScanner.d.ts +4 -1
- package/build/dts/scanner/NobleScanner.d.ts +2 -1
- package/build/dts/sensor/MotionSensorDataManager.d.ts +5 -2
- package/build/dts/sensor/SensorDataManager.d.ts +5 -4
- package/build/dts/server/BaseClient.d.ts +5 -3
- package/build/dts/server/ServerUtils.d.ts +1 -1
- package/build/dts/server/websocket/WebSocketUtils.d.ts +1 -1
- package/build/dts/utils/AudioUtils.d.ts +2 -0
- package/build/dts/utils/Console.d.ts +2 -0
- package/build/dts/utils/ThrottleUtils.d.ts +2 -0
- package/build/dts/vibration/VibrationManager.d.ts +19 -2
- package/build/index.d.ts +292 -62
- package/build/index.node.d.ts +286 -59
- package/examples/3d/scene.html +19 -5
- package/examples/3d-generic/index.html +144 -0
- package/examples/3d-generic/script.js +266 -0
- package/examples/basic/index.html +267 -17
- package/examples/basic/script.js +958 -105
- package/examples/camera/barcode-detector.js +109 -0
- package/examples/camera/depth-estimation.js +71 -0
- package/examples/camera/face-detector.js +119 -0
- package/examples/camera/face-landmark.js +111 -0
- package/examples/camera/gesture-recognition.js +97 -0
- package/examples/camera/hand-landmark.js +74 -0
- package/examples/camera/image-segmentation.js +98 -0
- package/examples/camera/image-to-text.js +43 -0
- package/examples/camera/image-upscale.js +75 -0
- package/examples/camera/index.html +129 -0
- package/examples/camera/object-detection.js +98 -0
- package/examples/camera/pose-landmark.js +60 -0
- package/examples/camera/script.js +316 -0
- package/examples/camera/utils.js +165 -0
- package/examples/camera/yolo-tiny.js +54 -0
- package/examples/camera/yolo.js +119 -0
- package/examples/edge-impulse/script.js +157 -48
- package/examples/edge-impulse-test/README.md +11 -0
- package/examples/edge-impulse-test/edge-impulse-standalone.js +7228 -0
- package/examples/edge-impulse-test/edge-impulse-standalone.wasm +0 -0
- package/examples/edge-impulse-test/index.html +75 -0
- package/examples/edge-impulse-test/run-impulse.js +135 -0
- package/examples/edge-impulse-test/script.js +200 -0
- package/examples/glasses-gestures/README.md +11 -0
- package/examples/glasses-gestures/edge-impulse-standalone.js +7228 -0
- package/examples/glasses-gestures/edge-impulse-standalone.wasm +0 -0
- package/examples/glasses-gestures/index.html +69 -0
- package/examples/glasses-gestures/run-impulse.js +135 -0
- package/examples/glasses-gestures/script.js +226 -0
- package/examples/gloves/edge-impulse-standalone.js +7228 -0
- package/examples/gloves/edge-impulse-standalone.wasm +0 -0
- package/examples/gloves/index.html +4 -1
- package/examples/gloves/run-impulse.js +135 -0
- package/examples/gloves/script.js +367 -51
- package/examples/graph/script.js +94 -37
- package/examples/microphone/gender.js +54 -0
- package/examples/microphone/index.html +102 -0
- package/examples/microphone/script.js +394 -0
- package/examples/microphone/utils.js +45 -0
- package/examples/microphone/whisper-realtime.js +166 -0
- package/examples/microphone/whisper.js +132 -0
- package/examples/punch/index.html +135 -0
- package/examples/punch/punch.tflite +0 -0
- package/examples/punch/script.js +169 -0
- package/examples/server/index.html +98 -22
- package/examples/server/script.js +317 -109
- package/examples/ukaton-firmware-update/merged-firmware.bin +0 -0
- package/examples/utils/aframe/aframe-master.min.js +2 -0
- package/examples/utils/aframe/bs-vibration.js +150 -0
- package/examples/utils/aframe/force-pushable.js +80 -0
- package/examples/utils/aframe/grabbable-anchor.js +46 -0
- package/examples/utils/aframe/grabbable-listener.js +31 -0
- package/examples/utils/aframe/grabbable-physics-body.js +190 -0
- package/examples/utils/aframe/grow-shrink.js +25 -0
- package/examples/utils/aframe/hand-punch.js +119 -0
- package/examples/utils/aframe/my-obb-collider.js +293 -0
- package/examples/utils/aframe/occlude-hand-tracking-controls.js +47 -0
- package/examples/utils/aframe/occlude-mesh.js +42 -0
- package/examples/utils/aframe/palm-up-detector.js +47 -0
- package/examples/utils/aframe/shadow-material.js +20 -0
- package/examples/utils/aframe/soft-shadow-light.js +9 -0
- package/examples/webxr-2/assets/3d/soccerBall.glb +0 -0
- package/examples/webxr-2/assets/audio/shellBounce.wav +0 -0
- package/examples/webxr-2/assets/audio/shellHit.wav +0 -0
- package/examples/webxr-2/assets/audio/shellKick.wav +0 -0
- package/examples/webxr-2/assets/audio/soccerBounce.wav +0 -0
- package/examples/webxr-2/assets/audio/soccerKick.mp3 +0 -0
- package/examples/webxr-2/assets/images/shellTexture.png +0 -0
- package/examples/webxr-2/components/bs-ankle.js +337 -0
- package/examples/webxr-2/components/coin.js +84 -0
- package/examples/webxr-2/components/custom-wrap.js +17 -0
- package/examples/webxr-2/components/goomba.js +3250 -0
- package/examples/webxr-2/components/init-shell-material.js +215 -0
- package/examples/webxr-2/components/platter.js +172 -0
- package/examples/webxr-2/components/shell.js +374 -0
- package/examples/webxr-2/components/soccer-ball.js +250 -0
- package/examples/webxr-2/components/squashed-goomba.js +249 -0
- package/examples/webxr-2/edge-impulse-standalone.js +7228 -0
- package/examples/webxr-2/edge-impulse-standalone.wasm +0 -0
- package/examples/webxr-2/index.html +996 -0
- package/examples/webxr-2/kick.tflite +0 -0
- package/examples/webxr-2/kick2.tflite +0 -0
- package/examples/webxr-2/run-impulse.js +135 -0
- package/examples/webxr-2/script.js +384 -0
- package/examples/webxr-3/components/bs-camera.js +65 -0
- package/examples/webxr-3/index.html +134 -0
- package/examples/webxr-3/script.js +432 -0
- package/package.json +2 -1
- package/src/.prettierrc +4 -0
- package/src/BS.ts +79 -8
- package/src/CameraManager.ts +497 -0
- package/src/Device.ts +691 -86
- package/src/DeviceInformationManager.ts +19 -10
- package/src/DeviceManager.ts +85 -25
- package/src/FileTransferManager.ts +145 -20
- package/src/InformationManager.ts +40 -15
- package/src/MicrophoneManager.ts +599 -0
- package/src/TfliteManager.ts +171 -25
- package/src/WifiManager.ts +323 -0
- package/src/connection/BaseConnectionManager.ts +130 -30
- package/src/connection/ClientConnectionManager.ts +34 -10
- package/src/connection/bluetooth/BluetoothConnectionManager.ts +8 -2
- package/src/connection/bluetooth/NobleConnectionManager.ts +147 -41
- package/src/connection/bluetooth/WebBluetoothConnectionManager.ts +99 -34
- package/src/connection/bluetooth/bluetoothUUIDs.ts +40 -13
- package/src/connection/udp/UDPConnectionManager.ts +356 -0
- package/src/connection/websocket/WebSocketConnectionManager.ts +282 -0
- package/src/devicePair/DevicePair.ts +95 -25
- package/src/devicePair/DevicePairPressureSensorDataManager.ts +27 -7
- package/src/scanner/BaseScanner.ts +49 -11
- package/src/scanner/NobleScanner.ts +76 -14
- package/src/sensor/MotionSensorDataManager.ts +21 -6
- package/src/sensor/PressureSensorDataManager.ts +37 -8
- package/src/sensor/SensorConfigurationManager.ts +73 -22
- package/src/sensor/SensorDataManager.ts +109 -23
- package/src/server/BaseClient.ts +150 -36
- package/src/server/BaseServer.ts +50 -2
- package/src/server/ServerUtils.ts +39 -9
- package/src/server/udp/UDPServer.ts +73 -22
- package/src/server/udp/UDPUtils.ts +9 -2
- package/src/server/websocket/WebSocketClient.ts +27 -7
- package/src/server/websocket/WebSocketUtils.ts +4 -2
- package/src/utils/AudioUtils.ts +65 -0
- package/src/utils/Console.ts +62 -9
- package/src/utils/ParseUtils.ts +24 -5
- package/src/utils/ThrottleUtils.ts +62 -0
- package/src/utils/Timer.ts +1 -1
- package/src/vibration/VibrationManager.ts +166 -40
|
@@ -11,7 +11,9 @@ console.log({ BS });
|
|
|
11
11
|
// GET DEVICES
|
|
12
12
|
|
|
13
13
|
/** @type {HTMLTemplateElement} */
|
|
14
|
-
const availableDeviceTemplate = document.getElementById(
|
|
14
|
+
const availableDeviceTemplate = document.getElementById(
|
|
15
|
+
"availableDeviceTemplate"
|
|
16
|
+
);
|
|
15
17
|
const availableDevicesContainer = document.getElementById("availableDevices");
|
|
16
18
|
/** @param {BS.Device[]} availableDevices */
|
|
17
19
|
function onAvailableDevices(availableDevices) {
|
|
@@ -20,12 +22,17 @@ function onAvailableDevices(availableDevices) {
|
|
|
20
22
|
availableDevicesContainer.innerText = "no devices available";
|
|
21
23
|
} else {
|
|
22
24
|
availableDevices.forEach((availableDevice) => {
|
|
23
|
-
let availableDeviceContainer = availableDeviceTemplate.content
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
let availableDeviceContainer = availableDeviceTemplate.content
|
|
26
|
+
.cloneNode(true)
|
|
27
|
+
.querySelector(".availableDevice");
|
|
28
|
+
availableDeviceContainer.querySelector(".name").innerText =
|
|
29
|
+
availableDevice.name;
|
|
30
|
+
availableDeviceContainer.querySelector(".type").innerText =
|
|
31
|
+
availableDevice.type;
|
|
26
32
|
|
|
27
33
|
/** @type {HTMLButtonElement} */
|
|
28
|
-
const toggleConnectionButton =
|
|
34
|
+
const toggleConnectionButton =
|
|
35
|
+
availableDeviceContainer.querySelector(".toggleConnection");
|
|
29
36
|
toggleConnectionButton.addEventListener("click", () => {
|
|
30
37
|
availableDevice.toggleConnection();
|
|
31
38
|
});
|
|
@@ -34,7 +41,9 @@ function onAvailableDevices(availableDevices) {
|
|
|
34
41
|
case "connected":
|
|
35
42
|
case "notConnected":
|
|
36
43
|
toggleConnectionButton.disabled = false;
|
|
37
|
-
toggleConnectionButton.innerText = availableDevice.isConnected
|
|
44
|
+
toggleConnectionButton.innerText = availableDevice.isConnected
|
|
45
|
+
? "disconnect"
|
|
46
|
+
: "connect";
|
|
38
47
|
break;
|
|
39
48
|
case "connecting":
|
|
40
49
|
case "disconnecting":
|
|
@@ -43,7 +52,9 @@ function onAvailableDevices(availableDevices) {
|
|
|
43
52
|
break;
|
|
44
53
|
}
|
|
45
54
|
};
|
|
46
|
-
availableDevice.addEventListener("connectionStatus", () =>
|
|
55
|
+
availableDevice.addEventListener("connectionStatus", () =>
|
|
56
|
+
onConnectionStatusUpdate()
|
|
57
|
+
);
|
|
47
58
|
onConnectionStatusUpdate();
|
|
48
59
|
availableDevicesContainer.appendChild(availableDeviceContainer);
|
|
49
60
|
});
|
|
@@ -88,7 +99,9 @@ window.positionScalar = 0.1;
|
|
|
88
99
|
|
|
89
100
|
devicePair.sides.forEach((side) => {
|
|
90
101
|
/** @type {HTMLElement} */
|
|
91
|
-
const gloveContainer = gloveTemplate.content
|
|
102
|
+
const gloveContainer = gloveTemplate.content
|
|
103
|
+
.cloneNode(true)
|
|
104
|
+
.querySelector(".glove");
|
|
92
105
|
gloveContainer.classList.add(side);
|
|
93
106
|
gloveContainer.dataset.side = side;
|
|
94
107
|
/** @type {HTMLIFrameElement} */
|
|
@@ -110,10 +123,12 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
110
123
|
const targetPositionEntity = targetEntity.querySelector(".position");
|
|
111
124
|
const targetRotationEntity = targetEntity.querySelector(".rotation");
|
|
112
125
|
const gloveEntity = targetEntity.querySelector(".glove");
|
|
113
|
-
const pressureEntities = Array.from(
|
|
126
|
+
const pressureEntities = Array.from(
|
|
127
|
+
targetEntity.querySelectorAll("[data-pressure]")
|
|
128
|
+
)
|
|
114
129
|
.sort((a, b) => a.dataset.pressure - b.dataset.pressure)
|
|
115
130
|
.map((entity) => entity.querySelector("a-sphere"));
|
|
116
|
-
|
|
131
|
+
pressureEntities.forEach((entity) => entity.setAttribute("opacity", "0.0"));
|
|
117
132
|
scene.addEventListener("loaded", () => {
|
|
118
133
|
if (side == "left") {
|
|
119
134
|
targetEntity.object3D.scale.x *= -1;
|
|
@@ -121,7 +136,8 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
121
136
|
});
|
|
122
137
|
|
|
123
138
|
/** @type {HTMLButtonElement} */
|
|
124
|
-
const toggleConnectionButton =
|
|
139
|
+
const toggleConnectionButton =
|
|
140
|
+
gloveContainer.querySelector(".toggleConnection");
|
|
125
141
|
toggleConnectionButton.addEventListener("click", () => {
|
|
126
142
|
devicePair[side].toggleConnection();
|
|
127
143
|
});
|
|
@@ -134,7 +150,9 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
134
150
|
if (device.isConnected) {
|
|
135
151
|
toggleConnectionButton.disabled = false;
|
|
136
152
|
}
|
|
137
|
-
toggleConnectionButton.innerText = device.isConnected
|
|
153
|
+
toggleConnectionButton.innerText = device.isConnected
|
|
154
|
+
? "disconnect"
|
|
155
|
+
: "reconnect";
|
|
138
156
|
});
|
|
139
157
|
|
|
140
158
|
devicePair.addEventListener("deviceConnectionStatus", (event) => {
|
|
@@ -147,7 +165,9 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
147
165
|
case "connected":
|
|
148
166
|
case "notConnected":
|
|
149
167
|
toggleConnectionButton.disabled = false;
|
|
150
|
-
toggleConnectionButton.innerText = device.isConnected
|
|
168
|
+
toggleConnectionButton.innerText = device.isConnected
|
|
169
|
+
? "disconnect"
|
|
170
|
+
: "reconnect";
|
|
151
171
|
break;
|
|
152
172
|
case "connecting":
|
|
153
173
|
case "disconnecting":
|
|
@@ -161,33 +181,41 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
161
181
|
const orientationSelect = gloveContainer.querySelector(".orientation");
|
|
162
182
|
orientationSelect.addEventListener("input", () => {
|
|
163
183
|
/** @type {BS.SensorConfiguration} */
|
|
164
|
-
const configuration = {
|
|
184
|
+
const configuration = {
|
|
185
|
+
gameRotation: 0,
|
|
186
|
+
rotation: 0,
|
|
187
|
+
gyroscope: 0,
|
|
188
|
+
orientation: 0,
|
|
189
|
+
};
|
|
165
190
|
|
|
166
191
|
switch (orientationSelect.value) {
|
|
167
192
|
case "none":
|
|
168
193
|
break;
|
|
169
194
|
case "gameRotation":
|
|
170
|
-
configuration.gameRotation =
|
|
195
|
+
configuration.gameRotation = pinchSensorRate;
|
|
171
196
|
break;
|
|
172
197
|
case "rotation":
|
|
173
|
-
configuration.rotation =
|
|
198
|
+
configuration.rotation = pinchSensorRate;
|
|
174
199
|
break;
|
|
175
200
|
case "orientation":
|
|
176
|
-
configuration.orientation =
|
|
201
|
+
configuration.orientation = pinchSensorRate;
|
|
177
202
|
break;
|
|
178
203
|
case "gyroscope":
|
|
179
|
-
configuration.gyroscope =
|
|
204
|
+
configuration.gyroscope = pinchSensorRate;
|
|
180
205
|
break;
|
|
181
206
|
default:
|
|
182
|
-
console.error(
|
|
207
|
+
console.error(
|
|
208
|
+
`uncaught orientationSelect value "${orientationSelect.value}"`
|
|
209
|
+
);
|
|
183
210
|
break;
|
|
184
211
|
}
|
|
185
212
|
|
|
186
|
-
devicePair[side]
|
|
213
|
+
devicePair[side]?.setSensorConfiguration(configuration);
|
|
187
214
|
});
|
|
188
215
|
|
|
189
216
|
/** @type {HTMLButtonElement} */
|
|
190
|
-
const resetOrientationButton =
|
|
217
|
+
const resetOrientationButton =
|
|
218
|
+
gloveContainer.querySelector(".resetOrientation");
|
|
191
219
|
resetOrientationButton.addEventListener("click", () => {
|
|
192
220
|
resetOrientation();
|
|
193
221
|
});
|
|
@@ -204,28 +232,34 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
204
232
|
const positionSelect = gloveContainer.querySelector(".position");
|
|
205
233
|
positionSelect.addEventListener("input", () => {
|
|
206
234
|
/** @type {BS.SensorConfiguration} */
|
|
207
|
-
const configuration = {
|
|
235
|
+
const configuration = {
|
|
236
|
+
acceleration: 0,
|
|
237
|
+
gravity: 0,
|
|
238
|
+
linearAcceleration: 0,
|
|
239
|
+
};
|
|
208
240
|
|
|
209
241
|
switch (positionSelect.value) {
|
|
210
242
|
case "none":
|
|
211
243
|
break;
|
|
212
244
|
case "acceleration":
|
|
213
|
-
configuration.acceleration =
|
|
245
|
+
configuration.acceleration = pinchSensorRate;
|
|
214
246
|
break;
|
|
215
247
|
case "gravity":
|
|
216
|
-
configuration.gravity =
|
|
248
|
+
configuration.gravity = pinchSensorRate;
|
|
217
249
|
break;
|
|
218
250
|
case "linearAcceleration":
|
|
219
|
-
configuration.linearAcceleration =
|
|
251
|
+
configuration.linearAcceleration = pinchSensorRate;
|
|
220
252
|
break;
|
|
221
253
|
default:
|
|
222
|
-
console.error(
|
|
254
|
+
console.error(
|
|
255
|
+
`uncaught positionSelect value "${positionSelect.value}"`
|
|
256
|
+
);
|
|
223
257
|
break;
|
|
224
258
|
}
|
|
225
259
|
|
|
226
260
|
console.log({ configuration });
|
|
227
261
|
|
|
228
|
-
devicePair[side]
|
|
262
|
+
devicePair[side]?.setSensorConfiguration(configuration);
|
|
229
263
|
});
|
|
230
264
|
devicePair.addEventListener("deviceIsConnected", (event) => {
|
|
231
265
|
const device = event.message.device;
|
|
@@ -276,7 +310,10 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
276
310
|
/** @param {BS.Vector3} position */
|
|
277
311
|
const updatePosition = (position) => {
|
|
278
312
|
_position.copy(position).multiplyScalar(window.positionScalar);
|
|
279
|
-
targetPositionEntity.object3D.position.lerp(
|
|
313
|
+
targetPositionEntity.object3D.position.lerp(
|
|
314
|
+
_position,
|
|
315
|
+
window.interpolationSmoothing
|
|
316
|
+
);
|
|
280
317
|
};
|
|
281
318
|
|
|
282
319
|
devicePair.addEventListener("deviceAcceleration", (event) => {
|
|
@@ -326,7 +363,10 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
326
363
|
if (applyOffset) {
|
|
327
364
|
targetQuaternion.premultiply(offsetQuaternion);
|
|
328
365
|
}
|
|
329
|
-
targetRotationEntity.object3D.quaternion.slerp(
|
|
366
|
+
targetRotationEntity.object3D.quaternion.slerp(
|
|
367
|
+
targetQuaternion,
|
|
368
|
+
window.interpolationSmoothing
|
|
369
|
+
);
|
|
330
370
|
};
|
|
331
371
|
devicePair.addEventListener("deviceGameRotation", (event) => {
|
|
332
372
|
const device = event.message.device;
|
|
@@ -360,7 +400,9 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
360
400
|
}
|
|
361
401
|
|
|
362
402
|
const orientation = event.message.orientation;
|
|
363
|
-
orientationVector3
|
|
403
|
+
orientationVector3
|
|
404
|
+
.set(orientation.pitch, orientation.heading, orientation.roll)
|
|
405
|
+
.multiplyScalar(Math.PI / 180);
|
|
364
406
|
orientationEuler.setFromVector3(orientationVector3);
|
|
365
407
|
orientationQuaternion.setFromEuler(orientationEuler);
|
|
366
408
|
updateQuaternion(orientationQuaternion);
|
|
@@ -395,10 +437,11 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
395
437
|
const setIsPressureEnabled = (newIsPressureEnabled) => {
|
|
396
438
|
/** @type {BS.SensorConfiguration} */
|
|
397
439
|
const configuration = { pressure: newIsPressureEnabled ? 20 : 0 };
|
|
398
|
-
devicePair[side]
|
|
440
|
+
devicePair[side]?.setSensorConfiguration(configuration);
|
|
399
441
|
};
|
|
400
442
|
const togglePressure = () => {
|
|
401
|
-
const isPressureEnabled =
|
|
443
|
+
const isPressureEnabled =
|
|
444
|
+
devicePair[side].sensorConfiguration.pressure != 0;
|
|
402
445
|
setIsPressureEnabled(!isPressureEnabled);
|
|
403
446
|
};
|
|
404
447
|
devicePair.addEventListener("deviceGetSensorConfiguration", (event) => {
|
|
@@ -406,7 +449,10 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
406
449
|
return;
|
|
407
450
|
}
|
|
408
451
|
const { sensorConfiguration } = event.message;
|
|
409
|
-
togglePressureButton.innerText =
|
|
452
|
+
togglePressureButton.innerText =
|
|
453
|
+
sensorConfiguration.pressure == 0
|
|
454
|
+
? "enable pressure"
|
|
455
|
+
: "disable pressure";
|
|
410
456
|
});
|
|
411
457
|
/** @type {HTMLButtonElement} */
|
|
412
458
|
const resetPressureButton = gloveContainer.querySelector(".resetPressure");
|
|
@@ -429,7 +475,7 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
429
475
|
}
|
|
430
476
|
const { pressure } = event.message;
|
|
431
477
|
pressure.sensors.forEach((sensor, index) => {
|
|
432
|
-
pressureEntities[index]
|
|
478
|
+
pressureEntities[index]?.setAttribute("opacity", sensor.normalizedValue);
|
|
433
479
|
});
|
|
434
480
|
});
|
|
435
481
|
|
|
@@ -440,23 +486,93 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
440
486
|
toggleCursorButton.addEventListener("click", () => {
|
|
441
487
|
setIsCursorEnabled(!isCursorEnabled);
|
|
442
488
|
});
|
|
489
|
+
/** @type {HTMLButtonElement} */
|
|
490
|
+
const resetCursorButton = gloveContainer.querySelector(".resetCursor");
|
|
491
|
+
resetCursorButton.addEventListener("click", () => {
|
|
492
|
+
cursorIntersectableEntities.forEach((entity) => {
|
|
493
|
+
entity.removeAttribute("dynamic-body");
|
|
494
|
+
entity.object3D.position.set(0, 1, -20);
|
|
495
|
+
entity.setAttribute("dynamic-body", "");
|
|
496
|
+
});
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
let checkCursorIntersectableEntitiesIntervalId;
|
|
500
|
+
const checkCursorIntersectableEntitiesInterval = 1000;
|
|
501
|
+
const xThreshold = 4.5;
|
|
502
|
+
const checkCursorIntersectableEntities = () => {
|
|
503
|
+
cursorIntersectableEntities.forEach((entity) => {
|
|
504
|
+
const position = entity.object3D.position;
|
|
505
|
+
if (!isCursorDown && Math.abs(position.x) > xThreshold) {
|
|
506
|
+
entity.removeAttribute("dynamic-body");
|
|
507
|
+
entity.object3D.position.set(0, 1, -20);
|
|
508
|
+
entity.setAttribute("dynamic-body", "");
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
};
|
|
443
512
|
const setIsCursorEnabled = (newIsCursorEnabled) => {
|
|
444
513
|
isCursorEnabled = newIsCursorEnabled;
|
|
445
|
-
toggleCursorButton.innerText = isCursorEnabled
|
|
514
|
+
toggleCursorButton.innerText = isCursorEnabled
|
|
515
|
+
? "disable cursor"
|
|
516
|
+
: "enable cursor";
|
|
517
|
+
|
|
518
|
+
if (devicePair[side]?.isConnected) {
|
|
519
|
+
const device = devicePair[side];
|
|
520
|
+
if (device.isUkaton) {
|
|
521
|
+
if (isCursorEnabled) {
|
|
522
|
+
device.setSensorConfiguration(pinchSensorConfiguration);
|
|
523
|
+
} else {
|
|
524
|
+
device.clearSensorConfiguration();
|
|
525
|
+
}
|
|
526
|
+
} else {
|
|
527
|
+
if (isCursorEnabled) {
|
|
528
|
+
orientationSelect.value = "gyroscope";
|
|
529
|
+
} else {
|
|
530
|
+
orientationSelect.value = "none";
|
|
531
|
+
}
|
|
532
|
+
orientationSelect.dispatchEvent(new Event("input"));
|
|
533
|
+
setIsPressureEnabled(isCursorEnabled);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (devicePair[side]?.isUkaton) {
|
|
537
|
+
if (isCursorEnabled) {
|
|
538
|
+
positionSelect.value = "linearAcceleration";
|
|
539
|
+
} else {
|
|
540
|
+
positionSelect.value = "none";
|
|
541
|
+
}
|
|
542
|
+
positionSelect.dispatchEvent(new Event("input"));
|
|
543
|
+
} else {
|
|
544
|
+
setIsPressureEnabled(isCursorEnabled);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
onCursorIsEnabled();
|
|
548
|
+
|
|
446
549
|
if (isCursorEnabled) {
|
|
447
|
-
|
|
550
|
+
checkCursorIntersectableEntitiesIntervalId = setInterval(
|
|
551
|
+
() => checkCursorIntersectableEntities(),
|
|
552
|
+
checkCursorIntersectableEntitiesInterval
|
|
553
|
+
);
|
|
448
554
|
} else {
|
|
449
|
-
|
|
555
|
+
clearInterval(checkCursorIntersectableEntitiesIntervalId);
|
|
450
556
|
}
|
|
451
|
-
orientationSelect.dispatchEvent(new Event("input"));
|
|
452
|
-
setIsPressureEnabled(isCursorEnabled);
|
|
453
|
-
onCursorIsEnabled();
|
|
454
557
|
};
|
|
558
|
+
window.addEventListener("pinch", () => {
|
|
559
|
+
if (isCursorDown || intersectedEntities[0]) {
|
|
560
|
+
setIsCursorDown(!isCursorDown);
|
|
561
|
+
devicePair[side]?.triggerVibration([
|
|
562
|
+
{
|
|
563
|
+
type: "waveformEffect",
|
|
564
|
+
locations: ["rear"],
|
|
565
|
+
segments: [{ effect: "buzz100" }],
|
|
566
|
+
},
|
|
567
|
+
]);
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
|
|
455
571
|
const onCursorIsEnabled = () => {
|
|
456
572
|
if (isCursorEnabled) {
|
|
457
573
|
targetEntity.setAttribute("visible", "false");
|
|
458
574
|
cursorExample.setAttribute("visible", "true");
|
|
459
|
-
cameraEntity.setAttribute("orbit-controls", { enabled: false });
|
|
575
|
+
//cameraEntity.setAttribute("orbit-controls", { enabled: false });
|
|
460
576
|
cameraEntity.setAttribute("camera", { active: false });
|
|
461
577
|
cursorCameraEntity.setAttribute("camera", { active: true });
|
|
462
578
|
} else {
|
|
@@ -464,7 +580,7 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
464
580
|
cursorExample.setAttribute("visible", "false");
|
|
465
581
|
cursorCameraEntity.setAttribute("camera", { active: false });
|
|
466
582
|
cameraEntity.setAttribute("camera", { active: true });
|
|
467
|
-
cameraEntity.setAttribute("orbit-controls", { enabled: true });
|
|
583
|
+
//cameraEntity.setAttribute("orbit-controls", { enabled: true });
|
|
468
584
|
}
|
|
469
585
|
};
|
|
470
586
|
|
|
@@ -473,7 +589,7 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
473
589
|
if (device.side != side) {
|
|
474
590
|
return;
|
|
475
591
|
}
|
|
476
|
-
toggleCursorButton.disabled = !device.isConnected;
|
|
592
|
+
//toggleCursorButton.disabled = !device.isConnected;
|
|
477
593
|
});
|
|
478
594
|
|
|
479
595
|
const cameraEntity = scene.querySelector(".camera");
|
|
@@ -492,7 +608,7 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
492
608
|
const { gyroscope } = event.message;
|
|
493
609
|
const { x: yawDegrees, y: pitchDegrees, z: rollDegrees } = gyroscope;
|
|
494
610
|
// console.log({ yawDegrees, pitchDegrees, rollDegrees });
|
|
495
|
-
setCursorPosition(yawDegrees * 0.
|
|
611
|
+
setCursorPosition(yawDegrees * 0.002, pitchDegrees * 0.003, true);
|
|
496
612
|
});
|
|
497
613
|
const cursorRaycaster = new THREE.Raycaster();
|
|
498
614
|
const cursor2DPosition = new THREE.Vector2();
|
|
@@ -514,11 +630,16 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
514
630
|
}
|
|
515
631
|
};
|
|
516
632
|
const updateCursorEntity = () => {
|
|
517
|
-
cursorRaycaster.setFromCamera(
|
|
633
|
+
cursorRaycaster.setFromCamera(
|
|
634
|
+
cursor2DPosition,
|
|
635
|
+
cursorCameraEntity.object3D.children[0]
|
|
636
|
+
);
|
|
518
637
|
cursorRaycaster.ray.at(1, cursor3DPosition);
|
|
519
638
|
cursorEntity.object3D.position.copy(cursor3DPosition);
|
|
520
639
|
};
|
|
521
|
-
const cursorIntersectableEntities = Array.from(
|
|
640
|
+
const cursorIntersectableEntities = Array.from(
|
|
641
|
+
scene.querySelectorAll(".intersectable")
|
|
642
|
+
);
|
|
522
643
|
|
|
523
644
|
const dragEntityPosition = new THREE.Vector3();
|
|
524
645
|
const dragEntity = () => {
|
|
@@ -531,7 +652,10 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
531
652
|
const intersectEntities = () => {
|
|
532
653
|
intersectedEntities.length = 0;
|
|
533
654
|
cursorIntersectableEntities.forEach((entity) => {
|
|
534
|
-
const intersections = cursorRaycaster.intersectObject(
|
|
655
|
+
const intersections = cursorRaycaster.intersectObject(
|
|
656
|
+
entity.object3D,
|
|
657
|
+
true
|
|
658
|
+
);
|
|
535
659
|
const intersection = intersections[0];
|
|
536
660
|
if (intersection) {
|
|
537
661
|
intersectedEntities.push(entity);
|
|
@@ -559,13 +683,19 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
559
683
|
let draggingEntity;
|
|
560
684
|
const setIsCursorDown = (newIsCursorDown) => {
|
|
561
685
|
isCursorDown = newIsCursorDown;
|
|
562
|
-
cursorMeshEntity.setAttribute(
|
|
686
|
+
cursorMeshEntity.setAttribute(
|
|
687
|
+
"color",
|
|
688
|
+
isCursorDown ? "black" : cursorMeshEntity.dataset.color
|
|
689
|
+
);
|
|
563
690
|
if (isCursorDown && intersectedEntities[0]) {
|
|
564
691
|
draggingEntity = intersectedEntities[0];
|
|
565
692
|
console.log("dragging entity");
|
|
566
693
|
draggingEntity.setAttribute("color", "green");
|
|
567
694
|
cursorHandleEntity.setAttribute("static-body", "");
|
|
568
|
-
draggingEntity.setAttribute(
|
|
695
|
+
draggingEntity.setAttribute(
|
|
696
|
+
"constraint",
|
|
697
|
+
"target: .cursorHandle; collideConnected: false; type: pointToPoint;"
|
|
698
|
+
);
|
|
569
699
|
}
|
|
570
700
|
if (!isCursorDown && draggingEntity) {
|
|
571
701
|
console.log("removing draggingEntity");
|
|
@@ -592,12 +722,16 @@ function onIFrameLoaded(gloveContainer) {
|
|
|
592
722
|
|
|
593
723
|
const websocketClient = new BS.WebSocketClient();
|
|
594
724
|
/** @type {HTMLButtonElement} */
|
|
595
|
-
const toggleServerConnectionButton = document.getElementById(
|
|
725
|
+
const toggleServerConnectionButton = document.getElementById(
|
|
726
|
+
"toggleServerConnection"
|
|
727
|
+
);
|
|
596
728
|
toggleServerConnectionButton.addEventListener("click", () => {
|
|
597
729
|
websocketClient.toggleConnection();
|
|
598
730
|
});
|
|
599
731
|
websocketClient.addEventListener("isConnected", () => {
|
|
600
|
-
toggleServerConnectionButton.innerText = websocketClient.isConnected
|
|
732
|
+
toggleServerConnectionButton.innerText = websocketClient.isConnected
|
|
733
|
+
? "disconnect from server"
|
|
734
|
+
: "connect to server";
|
|
601
735
|
});
|
|
602
736
|
websocketClient.addEventListener("connectionStatus", () => {
|
|
603
737
|
let disabled;
|
|
@@ -613,3 +747,185 @@ websocketClient.addEventListener("connectionStatus", () => {
|
|
|
613
747
|
}
|
|
614
748
|
toggleServerConnectionButton.disabled = disabled;
|
|
615
749
|
});
|
|
750
|
+
|
|
751
|
+
// PINCH CONFIG
|
|
752
|
+
|
|
753
|
+
const pinchSensorRate = 20;
|
|
754
|
+
/** @type {BS.TfliteSensorType[]} */
|
|
755
|
+
const pinchSensorTypes = ["linearAcceleration", "gyroscope"];
|
|
756
|
+
/** @type {BS.SensorConfiguration} */
|
|
757
|
+
const pinchSensorConfiguration = {};
|
|
758
|
+
pinchSensorTypes.forEach((sensorType) => {
|
|
759
|
+
pinchSensorConfiguration[sensorType] = pinchSensorRate;
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
/** @param {BS.DeviceEventMap["sensorData"]} event */
|
|
763
|
+
const onDeviceSensorData = (event) => {
|
|
764
|
+
let data = [];
|
|
765
|
+
switch (event.message.sensorType) {
|
|
766
|
+
case "pressure":
|
|
767
|
+
data = event.message.pressure.sensors.map((sensor) => sensor.rawValue);
|
|
768
|
+
break;
|
|
769
|
+
case "linearAcceleration":
|
|
770
|
+
{
|
|
771
|
+
const { x, y, z } = event.message.linearAcceleration;
|
|
772
|
+
data = [x, y, z];
|
|
773
|
+
}
|
|
774
|
+
break;
|
|
775
|
+
case "gyroscope":
|
|
776
|
+
{
|
|
777
|
+
const { x, y, z } = event.message.gyroscope;
|
|
778
|
+
data = [x, y, z];
|
|
779
|
+
}
|
|
780
|
+
break;
|
|
781
|
+
case "magnetometer":
|
|
782
|
+
{
|
|
783
|
+
const { x, y, z } = event.message.magnetometer;
|
|
784
|
+
data = [x, y, z];
|
|
785
|
+
}
|
|
786
|
+
break;
|
|
787
|
+
}
|
|
788
|
+
data = data.map(
|
|
789
|
+
(value) => value * pinchSensorScalars[event.message.sensorType]
|
|
790
|
+
);
|
|
791
|
+
appendData(event.message.timestamp, event.message.sensorType, data);
|
|
792
|
+
};
|
|
793
|
+
devicePair.addEventListener("deviceIsConnected", (event) => {
|
|
794
|
+
const { device, isConnected, side } = event.message;
|
|
795
|
+
if (side != "right") {
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
if (isConnected) {
|
|
799
|
+
device.addEventListener("sensorData", onDeviceSensorData);
|
|
800
|
+
} else {
|
|
801
|
+
device.removeEventListener("sensorData", onDeviceSensorData);
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
const pinchSensorScalars = {
|
|
806
|
+
pressure: 1 / (2 ** 16 - 1),
|
|
807
|
+
linearAcceleration: 1 / 4,
|
|
808
|
+
gyroscope: 1 / 720,
|
|
809
|
+
magnetometer: 1 / 2500,
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
// MODEL
|
|
813
|
+
|
|
814
|
+
let classifier;
|
|
815
|
+
async function loadClassifier() {
|
|
816
|
+
if (classifier) {
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
classifier = new EdgeImpulseClassifier();
|
|
820
|
+
await classifier.init();
|
|
821
|
+
|
|
822
|
+
let project = classifier.getProjectInfo();
|
|
823
|
+
console.log("loaded classifier", project);
|
|
824
|
+
|
|
825
|
+
window.classifier = classifier;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
devicePair.addEventListener("deviceIsConnected", (event) => {
|
|
829
|
+
if (event.message.device.isUkaton) {
|
|
830
|
+
loadClassifier();
|
|
831
|
+
}
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
/** @param {number[]} features */
|
|
835
|
+
function classify(features) {
|
|
836
|
+
try {
|
|
837
|
+
let res = classifier.classify(features);
|
|
838
|
+
// console.log(res);
|
|
839
|
+
const didPinch = res.results[1].value > 0.5;
|
|
840
|
+
if (didPinch) {
|
|
841
|
+
console.log("pinch");
|
|
842
|
+
window.dispatchEvent(new Event("pinch"));
|
|
843
|
+
lastTimeGestureRecognized = Date.now();
|
|
844
|
+
}
|
|
845
|
+
} catch (ex) {
|
|
846
|
+
console.error("Failed to classify", ex);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// PINCH MODEL BUFFER
|
|
851
|
+
const time = 600; // ms
|
|
852
|
+
const numberOfSamples = time / pinchSensorRate;
|
|
853
|
+
const numberOfFeaturesInEachSensorType = {};
|
|
854
|
+
BS.TfliteSensorTypes.forEach((sensorType) => {
|
|
855
|
+
switch (sensorType) {
|
|
856
|
+
case "pressure":
|
|
857
|
+
numberOfFeaturesInEachSensorType[sensorType] = 8; // change to 16 for ukaton
|
|
858
|
+
break;
|
|
859
|
+
case "linearAcceleration":
|
|
860
|
+
case "gyroscope":
|
|
861
|
+
case "magnetometer":
|
|
862
|
+
numberOfFeaturesInEachSensorType[sensorType] = 3;
|
|
863
|
+
break;
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
let numberOfFeaturesInOneSample = 0;
|
|
867
|
+
pinchSensorTypes.forEach((sensorType) => {
|
|
868
|
+
numberOfFeaturesInOneSample += numberOfFeaturesInEachSensorType[sensorType];
|
|
869
|
+
});
|
|
870
|
+
const numberOfFeatures = numberOfFeaturesInOneSample * numberOfSamples;
|
|
871
|
+
console.log({
|
|
872
|
+
time,
|
|
873
|
+
numberOfSamples,
|
|
874
|
+
numberOfFeaturesInOneSample,
|
|
875
|
+
numberOfFeatures,
|
|
876
|
+
});
|
|
877
|
+
const samples = [];
|
|
878
|
+
let pendingSample;
|
|
879
|
+
let lastTimeClassified = 0;
|
|
880
|
+
let lastTimeGestureRecognized = 0;
|
|
881
|
+
let classificationDelay = 0;
|
|
882
|
+
let gestureDelay = 1000;
|
|
883
|
+
let isClassifying = false;
|
|
884
|
+
/**
|
|
885
|
+
* @param {number} timestamp
|
|
886
|
+
* @param {BS.TfliteSensorType} sensorType
|
|
887
|
+
* @param {number[]} data
|
|
888
|
+
*/
|
|
889
|
+
function appendData(timestamp, sensorType, data) {
|
|
890
|
+
//console.log({ timestamp, sensorType, data });
|
|
891
|
+
if (!pendingSample || timestamp != pendingSample.timestamp) {
|
|
892
|
+
pendingSample = { timestamp };
|
|
893
|
+
//console.log("pendingSample", pendingSample);
|
|
894
|
+
}
|
|
895
|
+
pendingSample[sensorType] = data;
|
|
896
|
+
const gotAllSensorSamples = pinchSensorTypes.every(
|
|
897
|
+
(sensorType) => sensorType in pendingSample
|
|
898
|
+
);
|
|
899
|
+
if (gotAllSensorSamples) {
|
|
900
|
+
//console.log("got all samples");
|
|
901
|
+
samples.push(pendingSample);
|
|
902
|
+
pendingSample = undefined;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
while (samples.length > numberOfSamples) {
|
|
906
|
+
samples.shift();
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
if (!isClassifying && samples.length == numberOfSamples) {
|
|
910
|
+
const now = Date.now();
|
|
911
|
+
if (
|
|
912
|
+
now - lastTimeGestureRecognized < gestureDelay ||
|
|
913
|
+
now - lastTimeClassified < classificationDelay
|
|
914
|
+
) {
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
const features = [];
|
|
918
|
+
samples.forEach((sample) => {
|
|
919
|
+
const _features = [];
|
|
920
|
+
pinchSensorTypes.forEach((sensorType) => {
|
|
921
|
+
_features.push(...sample[sensorType]);
|
|
922
|
+
features.push(..._features);
|
|
923
|
+
});
|
|
924
|
+
});
|
|
925
|
+
isClassifying = true;
|
|
926
|
+
//console.log("classifying", features);
|
|
927
|
+
classify(features);
|
|
928
|
+
isClassifying = false;
|
|
929
|
+
lastTimeClassified = now;
|
|
930
|
+
}
|
|
931
|
+
}
|