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.
Files changed (202) hide show
  1. package/assets/3d/anchor.glb +0 -0
  2. package/assets/3d/coin.glb +0 -0
  3. package/assets/3d/glasses.glb +0 -0
  4. package/assets/audio/bounceMedium.wav +0 -0
  5. package/assets/audio/bounceStrong.wav +0 -0
  6. package/assets/audio/bounceWeak.wav +0 -0
  7. package/assets/audio/coin.wav +0 -0
  8. package/assets/audio/getUp.wav +0 -0
  9. package/assets/audio/grab.wav +0 -0
  10. package/assets/audio/kick.wav +0 -0
  11. package/assets/audio/platterFadeIn old.wav +0 -0
  12. package/assets/audio/platterFadeIn.wav +0 -0
  13. package/assets/audio/platterFadeOut.wav +0 -0
  14. package/assets/audio/punch.wav +0 -0
  15. package/assets/audio/punchSqueak.wav +0 -0
  16. package/assets/audio/purr.wav +0 -0
  17. package/assets/audio/purrFadeOut.wav +0 -0
  18. package/assets/audio/release.wav +0 -0
  19. package/assets/audio/splat.wav +0 -0
  20. package/assets/audio/stomp.wav +0 -0
  21. package/build/brilliantsole.cjs +3091 -741
  22. package/build/brilliantsole.cjs.map +1 -1
  23. package/build/brilliantsole.js +2759 -709
  24. package/build/brilliantsole.js.map +1 -1
  25. package/build/brilliantsole.ls.js +2602 -543
  26. package/build/brilliantsole.ls.js.map +1 -1
  27. package/build/brilliantsole.min.js +1 -1
  28. package/build/brilliantsole.min.js.map +1 -1
  29. package/build/brilliantsole.module.d.ts +295 -65
  30. package/build/brilliantsole.module.js +2749 -710
  31. package/build/brilliantsole.module.js.map +1 -1
  32. package/build/brilliantsole.module.min.d.ts +295 -65
  33. package/build/brilliantsole.module.min.js +1 -1
  34. package/build/brilliantsole.module.min.js.map +1 -1
  35. package/build/brilliantsole.node.module.d.ts +289 -62
  36. package/build/brilliantsole.node.module.js +3080 -742
  37. package/build/brilliantsole.node.module.js.map +1 -1
  38. package/build/dts/BS-output.d.ts +10 -0
  39. package/build/dts/BS.d.ts +21 -8
  40. package/build/dts/CameraManager.d.ts +72 -0
  41. package/build/dts/Device.d.ts +64 -13
  42. package/build/dts/DeviceInformationManager.d.ts +4 -4
  43. package/build/dts/DeviceManager.d.ts +2 -0
  44. package/build/dts/FileTransferManager.d.ts +18 -8
  45. package/build/dts/InformationManager.d.ts +2 -0
  46. package/build/dts/MicrophoneManager.d.ts +88 -0
  47. package/build/dts/TfliteManager.d.ts +22 -2
  48. package/build/dts/WifiManager.d.ts +61 -0
  49. package/build/dts/connection/BaseConnectionManager.d.ts +35 -3
  50. package/build/dts/connection/ClientConnectionManager.d.ts +7 -2
  51. package/build/dts/connection/bluetooth/NobleConnectionManager.d.ts +2 -1
  52. package/build/dts/connection/bluetooth/WebBluetoothConnectionManager.d.ts +1 -0
  53. package/build/dts/connection/bluetooth/bluetoothUUIDs.d.ts +2 -2
  54. package/build/dts/connection/udp/UDPConnectionManager.d.ts +28 -0
  55. package/build/dts/connection/webSocket/WebSocketConnectionManager.d.ts +25 -0
  56. package/build/dts/devicePair/DevicePair.d.ts +5 -5
  57. package/build/dts/scanner/BaseScanner.d.ts +4 -1
  58. package/build/dts/scanner/NobleScanner.d.ts +2 -1
  59. package/build/dts/sensor/MotionSensorDataManager.d.ts +5 -2
  60. package/build/dts/sensor/SensorDataManager.d.ts +5 -4
  61. package/build/dts/server/BaseClient.d.ts +5 -3
  62. package/build/dts/server/ServerUtils.d.ts +1 -1
  63. package/build/dts/server/websocket/WebSocketUtils.d.ts +1 -1
  64. package/build/dts/utils/AudioUtils.d.ts +2 -0
  65. package/build/dts/utils/Console.d.ts +2 -0
  66. package/build/dts/utils/ThrottleUtils.d.ts +2 -0
  67. package/build/dts/vibration/VibrationManager.d.ts +19 -2
  68. package/build/index.d.ts +292 -62
  69. package/build/index.node.d.ts +286 -59
  70. package/examples/3d/scene.html +19 -5
  71. package/examples/3d-generic/index.html +144 -0
  72. package/examples/3d-generic/script.js +266 -0
  73. package/examples/basic/index.html +267 -17
  74. package/examples/basic/script.js +958 -105
  75. package/examples/camera/barcode-detector.js +109 -0
  76. package/examples/camera/depth-estimation.js +71 -0
  77. package/examples/camera/face-detector.js +119 -0
  78. package/examples/camera/face-landmark.js +111 -0
  79. package/examples/camera/gesture-recognition.js +97 -0
  80. package/examples/camera/hand-landmark.js +74 -0
  81. package/examples/camera/image-segmentation.js +98 -0
  82. package/examples/camera/image-to-text.js +43 -0
  83. package/examples/camera/image-upscale.js +75 -0
  84. package/examples/camera/index.html +129 -0
  85. package/examples/camera/object-detection.js +98 -0
  86. package/examples/camera/pose-landmark.js +60 -0
  87. package/examples/camera/script.js +316 -0
  88. package/examples/camera/utils.js +165 -0
  89. package/examples/camera/yolo-tiny.js +54 -0
  90. package/examples/camera/yolo.js +119 -0
  91. package/examples/edge-impulse/script.js +157 -48
  92. package/examples/edge-impulse-test/README.md +11 -0
  93. package/examples/edge-impulse-test/edge-impulse-standalone.js +7228 -0
  94. package/examples/edge-impulse-test/edge-impulse-standalone.wasm +0 -0
  95. package/examples/edge-impulse-test/index.html +75 -0
  96. package/examples/edge-impulse-test/run-impulse.js +135 -0
  97. package/examples/edge-impulse-test/script.js +200 -0
  98. package/examples/glasses-gestures/README.md +11 -0
  99. package/examples/glasses-gestures/edge-impulse-standalone.js +7228 -0
  100. package/examples/glasses-gestures/edge-impulse-standalone.wasm +0 -0
  101. package/examples/glasses-gestures/index.html +69 -0
  102. package/examples/glasses-gestures/run-impulse.js +135 -0
  103. package/examples/glasses-gestures/script.js +226 -0
  104. package/examples/gloves/edge-impulse-standalone.js +7228 -0
  105. package/examples/gloves/edge-impulse-standalone.wasm +0 -0
  106. package/examples/gloves/index.html +4 -1
  107. package/examples/gloves/run-impulse.js +135 -0
  108. package/examples/gloves/script.js +367 -51
  109. package/examples/graph/script.js +94 -37
  110. package/examples/microphone/gender.js +54 -0
  111. package/examples/microphone/index.html +102 -0
  112. package/examples/microphone/script.js +394 -0
  113. package/examples/microphone/utils.js +45 -0
  114. package/examples/microphone/whisper-realtime.js +166 -0
  115. package/examples/microphone/whisper.js +132 -0
  116. package/examples/punch/index.html +135 -0
  117. package/examples/punch/punch.tflite +0 -0
  118. package/examples/punch/script.js +169 -0
  119. package/examples/server/index.html +98 -22
  120. package/examples/server/script.js +317 -109
  121. package/examples/ukaton-firmware-update/merged-firmware.bin +0 -0
  122. package/examples/utils/aframe/aframe-master.min.js +2 -0
  123. package/examples/utils/aframe/bs-vibration.js +150 -0
  124. package/examples/utils/aframe/force-pushable.js +80 -0
  125. package/examples/utils/aframe/grabbable-anchor.js +46 -0
  126. package/examples/utils/aframe/grabbable-listener.js +31 -0
  127. package/examples/utils/aframe/grabbable-physics-body.js +190 -0
  128. package/examples/utils/aframe/grow-shrink.js +25 -0
  129. package/examples/utils/aframe/hand-punch.js +119 -0
  130. package/examples/utils/aframe/my-obb-collider.js +293 -0
  131. package/examples/utils/aframe/occlude-hand-tracking-controls.js +47 -0
  132. package/examples/utils/aframe/occlude-mesh.js +42 -0
  133. package/examples/utils/aframe/palm-up-detector.js +47 -0
  134. package/examples/utils/aframe/shadow-material.js +20 -0
  135. package/examples/utils/aframe/soft-shadow-light.js +9 -0
  136. package/examples/webxr-2/assets/3d/soccerBall.glb +0 -0
  137. package/examples/webxr-2/assets/audio/shellBounce.wav +0 -0
  138. package/examples/webxr-2/assets/audio/shellHit.wav +0 -0
  139. package/examples/webxr-2/assets/audio/shellKick.wav +0 -0
  140. package/examples/webxr-2/assets/audio/soccerBounce.wav +0 -0
  141. package/examples/webxr-2/assets/audio/soccerKick.mp3 +0 -0
  142. package/examples/webxr-2/assets/images/shellTexture.png +0 -0
  143. package/examples/webxr-2/components/bs-ankle.js +337 -0
  144. package/examples/webxr-2/components/coin.js +84 -0
  145. package/examples/webxr-2/components/custom-wrap.js +17 -0
  146. package/examples/webxr-2/components/goomba.js +3250 -0
  147. package/examples/webxr-2/components/init-shell-material.js +215 -0
  148. package/examples/webxr-2/components/platter.js +172 -0
  149. package/examples/webxr-2/components/shell.js +374 -0
  150. package/examples/webxr-2/components/soccer-ball.js +250 -0
  151. package/examples/webxr-2/components/squashed-goomba.js +249 -0
  152. package/examples/webxr-2/edge-impulse-standalone.js +7228 -0
  153. package/examples/webxr-2/edge-impulse-standalone.wasm +0 -0
  154. package/examples/webxr-2/index.html +996 -0
  155. package/examples/webxr-2/kick.tflite +0 -0
  156. package/examples/webxr-2/kick2.tflite +0 -0
  157. package/examples/webxr-2/run-impulse.js +135 -0
  158. package/examples/webxr-2/script.js +384 -0
  159. package/examples/webxr-3/components/bs-camera.js +65 -0
  160. package/examples/webxr-3/index.html +134 -0
  161. package/examples/webxr-3/script.js +432 -0
  162. package/package.json +2 -1
  163. package/src/.prettierrc +4 -0
  164. package/src/BS.ts +79 -8
  165. package/src/CameraManager.ts +497 -0
  166. package/src/Device.ts +691 -86
  167. package/src/DeviceInformationManager.ts +19 -10
  168. package/src/DeviceManager.ts +85 -25
  169. package/src/FileTransferManager.ts +145 -20
  170. package/src/InformationManager.ts +40 -15
  171. package/src/MicrophoneManager.ts +599 -0
  172. package/src/TfliteManager.ts +171 -25
  173. package/src/WifiManager.ts +323 -0
  174. package/src/connection/BaseConnectionManager.ts +130 -30
  175. package/src/connection/ClientConnectionManager.ts +34 -10
  176. package/src/connection/bluetooth/BluetoothConnectionManager.ts +8 -2
  177. package/src/connection/bluetooth/NobleConnectionManager.ts +147 -41
  178. package/src/connection/bluetooth/WebBluetoothConnectionManager.ts +99 -34
  179. package/src/connection/bluetooth/bluetoothUUIDs.ts +40 -13
  180. package/src/connection/udp/UDPConnectionManager.ts +356 -0
  181. package/src/connection/websocket/WebSocketConnectionManager.ts +282 -0
  182. package/src/devicePair/DevicePair.ts +95 -25
  183. package/src/devicePair/DevicePairPressureSensorDataManager.ts +27 -7
  184. package/src/scanner/BaseScanner.ts +49 -11
  185. package/src/scanner/NobleScanner.ts +76 -14
  186. package/src/sensor/MotionSensorDataManager.ts +21 -6
  187. package/src/sensor/PressureSensorDataManager.ts +37 -8
  188. package/src/sensor/SensorConfigurationManager.ts +73 -22
  189. package/src/sensor/SensorDataManager.ts +109 -23
  190. package/src/server/BaseClient.ts +150 -36
  191. package/src/server/BaseServer.ts +50 -2
  192. package/src/server/ServerUtils.ts +39 -9
  193. package/src/server/udp/UDPServer.ts +73 -22
  194. package/src/server/udp/UDPUtils.ts +9 -2
  195. package/src/server/websocket/WebSocketClient.ts +27 -7
  196. package/src/server/websocket/WebSocketUtils.ts +4 -2
  197. package/src/utils/AudioUtils.ts +65 -0
  198. package/src/utils/Console.ts +62 -9
  199. package/src/utils/ParseUtils.ts +24 -5
  200. package/src/utils/ThrottleUtils.ts +62 -0
  201. package/src/utils/Timer.ts +1 -1
  202. 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("availableDeviceTemplate");
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.cloneNode(true).querySelector(".availableDevice");
24
- availableDeviceContainer.querySelector(".name").innerText = availableDevice.name;
25
- availableDeviceContainer.querySelector(".type").innerText = availableDevice.type;
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 = availableDeviceContainer.querySelector(".toggleConnection");
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 ? "disconnect" : "connect";
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", () => onConnectionStatusUpdate());
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.cloneNode(true).querySelector(".glove");
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(targetEntity.querySelectorAll("[data-pressure]"))
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
- //pressureEntities.forEach((entity) => entity.setAttribute("opacity", "0.0"));
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 = gloveContainer.querySelector(".toggleConnection");
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 ? "disconnect" : "reconnect";
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 ? "disconnect" : "reconnect";
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 = { gameRotation: 0, rotation: 0, gyroscope: 0, orientation: 0 };
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 = sensorRate;
195
+ configuration.gameRotation = pinchSensorRate;
171
196
  break;
172
197
  case "rotation":
173
- configuration.rotation = sensorRate;
198
+ configuration.rotation = pinchSensorRate;
174
199
  break;
175
200
  case "orientation":
176
- configuration.orientation = sensorRate;
201
+ configuration.orientation = pinchSensorRate;
177
202
  break;
178
203
  case "gyroscope":
179
- configuration.gyroscope = sensorRate;
204
+ configuration.gyroscope = pinchSensorRate;
180
205
  break;
181
206
  default:
182
- console.error(`uncaught orientationSelect value "${orientationSelect.value}"`);
207
+ console.error(
208
+ `uncaught orientationSelect value "${orientationSelect.value}"`
209
+ );
183
210
  break;
184
211
  }
185
212
 
186
- devicePair[side].setSensorConfiguration(configuration);
213
+ devicePair[side]?.setSensorConfiguration(configuration);
187
214
  });
188
215
 
189
216
  /** @type {HTMLButtonElement} */
190
- const resetOrientationButton = gloveContainer.querySelector(".resetOrientation");
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 = { acceleration: 0, gravity: 0, linearAcceleration: 0 };
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 = sensorRate;
245
+ configuration.acceleration = pinchSensorRate;
214
246
  break;
215
247
  case "gravity":
216
- configuration.gravity = sensorRate;
248
+ configuration.gravity = pinchSensorRate;
217
249
  break;
218
250
  case "linearAcceleration":
219
- configuration.linearAcceleration = sensorRate;
251
+ configuration.linearAcceleration = pinchSensorRate;
220
252
  break;
221
253
  default:
222
- console.error(`uncaught positionSelect value "${positionSelect.value}"`);
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].setSensorConfiguration(configuration);
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(_position, window.interpolationSmoothing);
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(targetQuaternion, window.interpolationSmoothing);
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.set(orientation.pitch, orientation.heading, orientation.roll).multiplyScalar(Math.PI / 180);
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].setSensorConfiguration(configuration);
440
+ devicePair[side]?.setSensorConfiguration(configuration);
399
441
  };
400
442
  const togglePressure = () => {
401
- const isPressureEnabled = devicePair[side].sensorConfiguration.pressure != 0;
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 = sensorConfiguration.pressure == 0 ? "enable pressure" : "disable pressure";
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].setAttribute("opacity", sensor.normalizedValue);
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 ? "disable cursor" : "enable cursor";
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
- orientationSelect.value = "gyroscope";
550
+ checkCursorIntersectableEntitiesIntervalId = setInterval(
551
+ () => checkCursorIntersectableEntities(),
552
+ checkCursorIntersectableEntitiesInterval
553
+ );
448
554
  } else {
449
- orientationSelect.value = "none";
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.001, pitchDegrees * 0.001, true);
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(cursor2DPosition, cursorCameraEntity.object3D.children[0]);
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(scene.querySelectorAll(".intersectable"));
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(entity.object3D, true);
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("color", isCursorDown ? "black" : cursorMeshEntity.dataset.color);
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("constraint", "target: .cursorHandle; collideConnected: false; type: pointToPoint;");
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("toggleServerConnection");
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 ? "disconnect from server" : "connect to server";
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
+ }