brilliantsole 0.0.25 → 0.0.27

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 (110) hide show
  1. package/README.md +16 -10
  2. package/assets/3d/rightHand.glb +0 -0
  3. package/assets/images/ukaton-pressure-0.svg +9 -0
  4. package/assets/images/ukaton-pressure-1.svg +9 -0
  5. package/assets/images/ukaton-pressure-10.svg +9 -0
  6. package/assets/images/ukaton-pressure-11.svg +9 -0
  7. package/assets/images/ukaton-pressure-12.svg +9 -0
  8. package/assets/images/ukaton-pressure-13.svg +9 -0
  9. package/assets/images/ukaton-pressure-14.svg +9 -0
  10. package/assets/images/ukaton-pressure-15.svg +9 -0
  11. package/assets/images/ukaton-pressure-2.svg +9 -0
  12. package/assets/images/ukaton-pressure-3.svg +9 -0
  13. package/assets/images/ukaton-pressure-4.svg +9 -0
  14. package/assets/images/ukaton-pressure-5.svg +9 -0
  15. package/assets/images/ukaton-pressure-6.svg +9 -0
  16. package/assets/images/ukaton-pressure-7.svg +9 -0
  17. package/assets/images/ukaton-pressure-8.svg +9 -0
  18. package/assets/images/ukaton-pressure-9.svg +9 -0
  19. package/assets/images/ukaton-right-insole.svg +798 -0
  20. package/build/brilliantsole.cjs +255 -119
  21. package/build/brilliantsole.cjs.map +1 -1
  22. package/build/brilliantsole.js +283 -140
  23. package/build/brilliantsole.js.map +1 -1
  24. package/build/brilliantsole.ls.js +210 -103
  25. package/build/brilliantsole.ls.js.map +1 -1
  26. package/build/brilliantsole.min.js +1 -1
  27. package/build/brilliantsole.min.js.map +1 -1
  28. package/build/brilliantsole.module.d.ts +76 -60
  29. package/build/brilliantsole.module.js +282 -140
  30. package/build/brilliantsole.module.js.map +1 -1
  31. package/build/brilliantsole.module.min.d.ts +76 -60
  32. package/build/brilliantsole.module.min.js +1 -1
  33. package/build/brilliantsole.module.min.js.map +1 -1
  34. package/build/brilliantsole.node.module.d.ts +75 -60
  35. package/build/brilliantsole.node.module.js +254 -119
  36. package/build/brilliantsole.node.module.js.map +1 -1
  37. package/build/dts/BS.d.ts +2 -2
  38. package/build/dts/Device.d.ts +11 -7
  39. package/build/dts/DeviceManager.d.ts +1 -0
  40. package/build/dts/InformationManager.d.ts +6 -5
  41. package/build/dts/connection/BaseConnectionManager.d.ts +2 -0
  42. package/build/dts/connection/ClientConnectionManager.d.ts +4 -0
  43. package/build/dts/connection/bluetooth/BluetoothConnectionManager.d.ts +1 -0
  44. package/build/dts/connection/bluetooth/NobleConnectionManager.d.ts +1 -0
  45. package/build/dts/connection/bluetooth/WebBluetoothConnectionManager.d.ts +1 -0
  46. package/build/dts/devicePair/DevicePair.d.ts +14 -10
  47. package/build/dts/devicePair/DevicePairPressureSensorDataManager.d.ts +8 -4
  48. package/build/dts/devicePair/DevicePairSensorDataManager.d.ts +2 -2
  49. package/build/dts/server/BaseClient.d.ts +1 -0
  50. package/build/dts/utils/CenterOfPressureHelper.d.ts +2 -2
  51. package/build/dts/utils/MathUtils.d.ts +2 -0
  52. package/build/index.d.ts +76 -60
  53. package/build/index.node.d.ts +75 -60
  54. package/examples/3d/script.js +90 -17
  55. package/examples/balance/script.js +2 -1
  56. package/examples/basic/index.html +21 -2
  57. package/examples/basic/script.js +17 -3
  58. package/examples/bottango/index.html +11 -1
  59. package/examples/bottango/script.js +2 -2
  60. package/examples/center-of-pressure/index.html +114 -114
  61. package/examples/center-of-pressure/script.js +1 -1
  62. package/examples/device-pair/index.html +58 -58
  63. package/examples/device-pair/script.js +12 -8
  64. package/examples/gloves/index.html +116 -0
  65. package/examples/gloves/scene.html +124 -0
  66. package/examples/gloves/script.js +615 -0
  67. package/examples/graph/index.html +11 -1
  68. package/examples/graph/script.js +2 -2
  69. package/examples/pressure/index.html +180 -12
  70. package/examples/pressure/script.js +144 -7
  71. package/examples/recording/index.html +191 -183
  72. package/examples/server/index.html +11 -1
  73. package/examples/server/script.js +6 -3
  74. package/examples/ukaton-firmware-update/index.html +20 -0
  75. package/examples/ukaton-firmware-update/manifest.json +11 -0
  76. package/examples/ukaton-firmware-update/merged-firmware.bin +0 -0
  77. package/examples/webxr/script.js +3 -3
  78. package/package.json +1 -1
  79. package/src/BS.ts +3 -8
  80. package/src/Device.ts +41 -8
  81. package/src/DeviceInformationManager.ts +4 -2
  82. package/src/DeviceManager.ts +10 -1
  83. package/src/FileTransferManager.ts +1 -1
  84. package/src/FirmwareManager.ts +1 -1
  85. package/src/InformationManager.ts +24 -7
  86. package/src/TfliteManager.ts +1 -1
  87. package/src/connection/BaseConnectionManager.ts +23 -6
  88. package/src/connection/ClientConnectionManager.ts +13 -1
  89. package/src/connection/bluetooth/BluetoothConnectionManager.ts +6 -1
  90. package/src/connection/bluetooth/NobleConnectionManager.ts +8 -1
  91. package/src/connection/bluetooth/WebBluetoothConnectionManager.ts +5 -1
  92. package/src/devicePair/DevicePair.ts +53 -27
  93. package/src/devicePair/DevicePairPressureSensorDataManager.ts +51 -23
  94. package/src/devicePair/DevicePairSensorDataManager.ts +5 -5
  95. package/src/scanner/NobleScanner.ts +5 -3
  96. package/src/sensor/BarometerSensorDataManager.ts +1 -1
  97. package/src/sensor/MotionSensorDataManager.ts +1 -1
  98. package/src/sensor/PressureSensorDataManager.ts +13 -8
  99. package/src/sensor/SensorConfigurationManager.ts +3 -3
  100. package/src/sensor/SensorDataManager.ts +1 -1
  101. package/src/server/BaseClient.ts +43 -2
  102. package/src/server/BaseServer.ts +149 -39
  103. package/src/server/udp/UDPServer.ts +1 -1
  104. package/src/server/websocket/WebSocketClient.ts +3 -2
  105. package/src/server/websocket/WebSocketServer.ts +1 -1
  106. package/src/utils/CenterOfPressureHelper.ts +5 -5
  107. package/src/utils/MathUtils.ts +31 -1
  108. package/src/utils/ParseUtils.ts +1 -1
  109. package/src/utils/checksum.ts +1 -1
  110. package/src/utils/mcumgr.js +1 -1
@@ -0,0 +1,615 @@
1
+ import * as BS from "../../build/brilliantsole.module.js";
2
+ window.BS = BS;
3
+ console.log({ BS });
4
+ //BS.setAllConsoleLevelFlags({ log: false });
5
+
6
+ /** @typedef {import("../utils/three/three.module.min").Vector2} TVector2 */
7
+ /** @typedef {import("../utils/three/three.module.min").Vector3} TVector3 */
8
+ /** @typedef {import("../utils/three/three.module.min").Quaternion} TQuaternion */
9
+ /** @typedef {import("../utils/three/three.module.min").Euler} TEuler */
10
+
11
+ // GET DEVICES
12
+
13
+ /** @type {HTMLTemplateElement} */
14
+ const availableDeviceTemplate = document.getElementById("availableDeviceTemplate");
15
+ const availableDevicesContainer = document.getElementById("availableDevices");
16
+ /** @param {BS.Device[]} availableDevices */
17
+ function onAvailableDevices(availableDevices) {
18
+ availableDevicesContainer.innerHTML = "";
19
+ if (availableDevices.length == 0) {
20
+ availableDevicesContainer.innerText = "no devices available";
21
+ } else {
22
+ 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;
26
+
27
+ /** @type {HTMLButtonElement} */
28
+ const toggleConnectionButton = availableDeviceContainer.querySelector(".toggleConnection");
29
+ toggleConnectionButton.addEventListener("click", () => {
30
+ availableDevice.toggleConnection();
31
+ });
32
+ const onConnectionStatusUpdate = () => {
33
+ switch (availableDevice.connectionStatus) {
34
+ case "connected":
35
+ case "notConnected":
36
+ toggleConnectionButton.disabled = false;
37
+ toggleConnectionButton.innerText = availableDevice.isConnected ? "disconnect" : "connect";
38
+ break;
39
+ case "connecting":
40
+ case "disconnecting":
41
+ toggleConnectionButton.disabled = true;
42
+ toggleConnectionButton.innerText = availableDevice.connectionStatus;
43
+ break;
44
+ }
45
+ };
46
+ availableDevice.addEventListener("connectionStatus", () => onConnectionStatusUpdate());
47
+ onConnectionStatusUpdate();
48
+ availableDevicesContainer.appendChild(availableDeviceContainer);
49
+ });
50
+ }
51
+ }
52
+ async function getDevices() {
53
+ const availableDevices = await BS.DeviceManager.GetDevices();
54
+ if (!availableDevices) {
55
+ return;
56
+ }
57
+ onAvailableDevices(availableDevices);
58
+ }
59
+
60
+ BS.DeviceManager.AddEventListener("availableDevices", (event) => {
61
+ const devices = event.message.availableDevices;
62
+ onAvailableDevices(devices);
63
+ });
64
+ getDevices();
65
+
66
+ // ADD DEVICE
67
+
68
+ /** @type {HTMLButtonElement} */
69
+ const addDeviceButton = document.getElementById("addDevice");
70
+ addDeviceButton.addEventListener("click", () => {
71
+ BS.Device.Connect();
72
+ });
73
+
74
+ const devicePair = BS.DevicePair.gloves;
75
+ devicePair.addEventListener("isConnected", () => {
76
+ addDeviceButton.disabled = devicePair.isConnected;
77
+ });
78
+
79
+ // 3D VISUALIZATION
80
+
81
+ const glovesContainer = document.getElementById("gloves");
82
+ /** @type {HTMLTemplateElement} */
83
+ const gloveTemplate = document.getElementById("gloveTemplate");
84
+
85
+ window.sensorRate = 20;
86
+ window.interpolationSmoothing = 0.4;
87
+ window.positionScalar = 0.1;
88
+
89
+ devicePair.sides.forEach((side) => {
90
+ /** @type {HTMLElement} */
91
+ const gloveContainer = gloveTemplate.content.cloneNode(true).querySelector(".glove");
92
+ gloveContainer.classList.add(side);
93
+ gloveContainer.dataset.side = side;
94
+ /** @type {HTMLIFrameElement} */
95
+ const iframe = gloveContainer.querySelector("iframe");
96
+ iframe.addEventListener("load", () => {
97
+ onIFrameLoaded(gloveContainer);
98
+ });
99
+ glovesContainer.appendChild(gloveContainer);
100
+ });
101
+
102
+ /** @param {HTMLElement} gloveContainer */
103
+ function onIFrameLoaded(gloveContainer) {
104
+ /** @type {BS.Side} */
105
+ const side = gloveContainer.dataset.side;
106
+ /** @type {HTMLIFrameElement} */
107
+ const iframe = gloveContainer.querySelector("iframe");
108
+ const scene = iframe.contentDocument.querySelector("a-scene");
109
+ const targetEntity = scene.querySelector(".target");
110
+ const targetPositionEntity = targetEntity.querySelector(".position");
111
+ const targetRotationEntity = targetEntity.querySelector(".rotation");
112
+ const gloveEntity = targetEntity.querySelector(".glove");
113
+ const pressureEntities = Array.from(targetEntity.querySelectorAll("[data-pressure]"))
114
+ .sort((a, b) => a.dataset.pressure - b.dataset.pressure)
115
+ .map((entity) => entity.querySelector("a-sphere"));
116
+ //pressureEntities.forEach((entity) => entity.setAttribute("opacity", "0.0"));
117
+ scene.addEventListener("loaded", () => {
118
+ if (side == "left") {
119
+ targetEntity.object3D.scale.x *= -1;
120
+ }
121
+ });
122
+
123
+ /** @type {HTMLButtonElement} */
124
+ const toggleConnectionButton = gloveContainer.querySelector(".toggleConnection");
125
+ toggleConnectionButton.addEventListener("click", () => {
126
+ devicePair[side].toggleConnection();
127
+ });
128
+ devicePair.addEventListener("deviceIsConnected", (event) => {
129
+ const device = event.message.device;
130
+ if (device.side != side) {
131
+ return;
132
+ }
133
+
134
+ if (device.isConnected) {
135
+ toggleConnectionButton.disabled = false;
136
+ }
137
+ toggleConnectionButton.innerText = device.isConnected ? "disconnect" : "reconnect";
138
+ });
139
+
140
+ devicePair.addEventListener("deviceConnectionStatus", (event) => {
141
+ const device = event.message.device;
142
+ if (device.side != side) {
143
+ return;
144
+ }
145
+
146
+ switch (device.connectionStatus) {
147
+ case "connected":
148
+ case "notConnected":
149
+ toggleConnectionButton.disabled = false;
150
+ toggleConnectionButton.innerText = device.isConnected ? "disconnect" : "reconnect";
151
+ break;
152
+ case "connecting":
153
+ case "disconnecting":
154
+ toggleConnectionButton.disabled = true;
155
+ toggleConnectionButton.innerText = device.connectionStatus;
156
+ break;
157
+ }
158
+ });
159
+
160
+ /** @type {HTMLSelectElement} */
161
+ const orientationSelect = gloveContainer.querySelector(".orientation");
162
+ orientationSelect.addEventListener("input", () => {
163
+ /** @type {BS.SensorConfiguration} */
164
+ const configuration = { gameRotation: 0, rotation: 0, gyroscope: 0, orientation: 0 };
165
+
166
+ switch (orientationSelect.value) {
167
+ case "none":
168
+ break;
169
+ case "gameRotation":
170
+ configuration.gameRotation = sensorRate;
171
+ break;
172
+ case "rotation":
173
+ configuration.rotation = sensorRate;
174
+ break;
175
+ case "orientation":
176
+ configuration.orientation = sensorRate;
177
+ break;
178
+ case "gyroscope":
179
+ configuration.gyroscope = sensorRate;
180
+ break;
181
+ default:
182
+ console.error(`uncaught orientationSelect value "${orientationSelect.value}"`);
183
+ break;
184
+ }
185
+
186
+ devicePair[side].setSensorConfiguration(configuration);
187
+ });
188
+
189
+ /** @type {HTMLButtonElement} */
190
+ const resetOrientationButton = gloveContainer.querySelector(".resetOrientation");
191
+ resetOrientationButton.addEventListener("click", () => {
192
+ resetOrientation();
193
+ });
194
+ devicePair.addEventListener("deviceIsConnected", (event) => {
195
+ const device = event.message.device;
196
+ if (device.side != side) {
197
+ return;
198
+ }
199
+
200
+ resetOrientationButton.disabled = !device.isConnected;
201
+ });
202
+
203
+ /** @type {HTMLSelectElement} */
204
+ const positionSelect = gloveContainer.querySelector(".position");
205
+ positionSelect.addEventListener("input", () => {
206
+ /** @type {BS.SensorConfiguration} */
207
+ const configuration = { acceleration: 0, gravity: 0, linearAcceleration: 0 };
208
+
209
+ switch (positionSelect.value) {
210
+ case "none":
211
+ break;
212
+ case "acceleration":
213
+ configuration.acceleration = sensorRate;
214
+ break;
215
+ case "gravity":
216
+ configuration.gravity = sensorRate;
217
+ break;
218
+ case "linearAcceleration":
219
+ configuration.linearAcceleration = sensorRate;
220
+ break;
221
+ default:
222
+ console.error(`uncaught positionSelect value "${positionSelect.value}"`);
223
+ break;
224
+ }
225
+
226
+ console.log({ configuration });
227
+
228
+ devicePair[side].setSensorConfiguration(configuration);
229
+ });
230
+ devicePair.addEventListener("deviceIsConnected", (event) => {
231
+ const device = event.message.device;
232
+ if (device.side != side) {
233
+ return;
234
+ }
235
+
236
+ orientationSelect.disabled = !device.isConnected;
237
+ positionSelect.disabled = !device.isConnected;
238
+ });
239
+
240
+ devicePair.addEventListener("deviceGetSensorConfiguration", (event) => {
241
+ const device = event.message.device;
242
+ if (device.side != side) {
243
+ return;
244
+ }
245
+
246
+ let newOrientationSelectValue = "none";
247
+ let newPositionSelectValue = "none";
248
+
249
+ for (const key in device.sensorConfiguration) {
250
+ /** @type {BS.SensorType} */
251
+ const sensorType = key;
252
+ if (device.sensorConfiguration[sensorType] > 0) {
253
+ switch (sensorType) {
254
+ case "gameRotation":
255
+ case "rotation":
256
+ case "orientation":
257
+ case "gyroscope":
258
+ newOrientationSelectValue = sensorType;
259
+ break;
260
+ case "acceleration":
261
+ case "gravity":
262
+ case "linearAcceleration":
263
+ newPositionSelectValue = sensorType;
264
+ break;
265
+ }
266
+ }
267
+ }
268
+
269
+ orientationSelect.value = newOrientationSelectValue;
270
+ positionSelect.value = newPositionSelectValue;
271
+ });
272
+
273
+ /** @type {TVector3} */
274
+ const _position = new THREE.Vector3();
275
+
276
+ /** @param {BS.Vector3} position */
277
+ const updatePosition = (position) => {
278
+ _position.copy(position).multiplyScalar(window.positionScalar);
279
+ targetPositionEntity.object3D.position.lerp(_position, window.interpolationSmoothing);
280
+ };
281
+
282
+ devicePair.addEventListener("deviceAcceleration", (event) => {
283
+ const device = event.message.device;
284
+ if (device.side != side) {
285
+ return;
286
+ }
287
+ const acceleration = event.message.acceleration;
288
+ updatePosition(acceleration);
289
+ });
290
+ devicePair.addEventListener("deviceGravity", (event) => {
291
+ const device = event.message.device;
292
+ if (device.side != side) {
293
+ return;
294
+ }
295
+
296
+ const gravity = event.message.gravity;
297
+ updatePosition(gravity);
298
+ });
299
+ devicePair.addEventListener("deviceLinearAcceleration", (event) => {
300
+ const device = event.message.device;
301
+ if (device.side != side) {
302
+ return;
303
+ }
304
+
305
+ const linearAcceleration = event.message.linearAcceleration;
306
+ updatePosition(linearAcceleration);
307
+ });
308
+
309
+ /** @type {TQuaternion} */
310
+ const offsetQuaternion = new THREE.Quaternion();
311
+ const resetOrientation = () => {
312
+ offsetQuaternion.copy(_quaternion).invert();
313
+ };
314
+
315
+ /** @type {TQuaternion} */
316
+ const _quaternion = new THREE.Quaternion();
317
+ /** @type {TQuaternion} */
318
+ const targetQuaternion = new THREE.Quaternion();
319
+ /**
320
+ * @param {BS.Quaternion} quaternion
321
+ * @param {boolean} applyOffset
322
+ */
323
+ const updateQuaternion = (quaternion, applyOffset = false) => {
324
+ _quaternion.copy(quaternion);
325
+ targetQuaternion.copy(_quaternion);
326
+ if (applyOffset) {
327
+ targetQuaternion.premultiply(offsetQuaternion);
328
+ }
329
+ targetRotationEntity.object3D.quaternion.slerp(targetQuaternion, window.interpolationSmoothing);
330
+ };
331
+ devicePair.addEventListener("deviceGameRotation", (event) => {
332
+ const device = event.message.device;
333
+ if (device.side != side) {
334
+ return;
335
+ }
336
+
337
+ const gameRotation = event.message.gameRotation;
338
+ updateQuaternion(gameRotation, true);
339
+ });
340
+ devicePair.addEventListener("deviceRotation", (event) => {
341
+ const device = event.message.device;
342
+ if (device.side != side) {
343
+ return;
344
+ }
345
+
346
+ const rotation = event.message.rotation;
347
+ updateQuaternion(rotation, true);
348
+ });
349
+
350
+ /** @type {TVector3} */
351
+ const orientationVector3 = new THREE.Vector3();
352
+ /** @type {TEuler} */
353
+ const orientationEuler = new THREE.Euler(0, 0, 0, "YXZ");
354
+ /** @type {TQuaternion} */
355
+ const orientationQuaternion = new THREE.Quaternion();
356
+ devicePair.addEventListener("deviceOrientation", (event) => {
357
+ const device = event.message.device;
358
+ if (device.side != side) {
359
+ return;
360
+ }
361
+
362
+ const orientation = event.message.orientation;
363
+ orientationVector3.set(orientation.pitch, orientation.heading, orientation.roll).multiplyScalar(Math.PI / 180);
364
+ orientationEuler.setFromVector3(orientationVector3);
365
+ orientationQuaternion.setFromEuler(orientationEuler);
366
+ updateQuaternion(orientationQuaternion);
367
+ });
368
+
369
+ /** @type {TVector3} */
370
+ const gyroscopeVector3 = new THREE.Vector3();
371
+ /** @type {TEuler} */
372
+ const gyroscopeEuler = new THREE.Euler();
373
+ /** @type {TQuaternion} */
374
+ const gyroscopeQuaternion = new THREE.Quaternion();
375
+ devicePair.addEventListener("deviceGyroscope", (event) => {
376
+ const device = event.message.device;
377
+ if (device.side != side) {
378
+ return;
379
+ }
380
+
381
+ const gyroscope = event.message.gyroscope;
382
+ gyroscopeVector3.copy(gyroscope).multiplyScalar(Math.PI / 180);
383
+ gyroscopeEuler.setFromVector3(gyroscopeVector3);
384
+ gyroscopeQuaternion.setFromEuler(gyroscopeEuler);
385
+ updateQuaternion(gyroscopeQuaternion);
386
+ });
387
+
388
+ // PRESSURE
389
+
390
+ /** @type {HTMLButtonElement} */
391
+ const togglePressureButton = gloveContainer.querySelector(".togglePressure");
392
+ togglePressureButton.addEventListener("click", () => {
393
+ togglePressure();
394
+ });
395
+ const setIsPressureEnabled = (newIsPressureEnabled) => {
396
+ /** @type {BS.SensorConfiguration} */
397
+ const configuration = { pressure: newIsPressureEnabled ? 20 : 0 };
398
+ devicePair[side].setSensorConfiguration(configuration);
399
+ };
400
+ const togglePressure = () => {
401
+ const isPressureEnabled = devicePair[side].sensorConfiguration.pressure != 0;
402
+ setIsPressureEnabled(!isPressureEnabled);
403
+ };
404
+ devicePair.addEventListener("deviceGetSensorConfiguration", (event) => {
405
+ if (event.message.side != side) {
406
+ return;
407
+ }
408
+ const { sensorConfiguration } = event.message;
409
+ togglePressureButton.innerText = sensorConfiguration.pressure == 0 ? "enable pressure" : "disable pressure";
410
+ });
411
+ /** @type {HTMLButtonElement} */
412
+ const resetPressureButton = gloveContainer.querySelector(".resetPressure");
413
+ resetPressureButton.addEventListener("click", () => {
414
+ devicePair[side].resetPressureRange();
415
+ });
416
+ devicePair.addEventListener("deviceIsConnected", (event) => {
417
+ const device = event.message.device;
418
+ if (device.side != side) {
419
+ return;
420
+ }
421
+
422
+ togglePressureButton.disabled = !device.isConnected;
423
+ resetPressureButton.disabled = !device.isConnected;
424
+ });
425
+
426
+ devicePair.addEventListener("devicePressure", (event) => {
427
+ if (event.message.side != side) {
428
+ return;
429
+ }
430
+ const { pressure } = event.message;
431
+ pressure.sensors.forEach((sensor, index) => {
432
+ pressureEntities[index].setAttribute("opacity", sensor.normalizedValue);
433
+ });
434
+ });
435
+
436
+ // CURSOR MODE
437
+ let isCursorEnabled = false;
438
+ /** @type {HTMLButtonElement} */
439
+ const toggleCursorButton = gloveContainer.querySelector(".toggleCursor");
440
+ toggleCursorButton.addEventListener("click", () => {
441
+ setIsCursorEnabled(!isCursorEnabled);
442
+ });
443
+ const setIsCursorEnabled = (newIsCursorEnabled) => {
444
+ isCursorEnabled = newIsCursorEnabled;
445
+ toggleCursorButton.innerText = isCursorEnabled ? "disable cursor" : "enable cursor";
446
+ if (isCursorEnabled) {
447
+ orientationSelect.value = "gyroscope";
448
+ } else {
449
+ orientationSelect.value = "none";
450
+ }
451
+ orientationSelect.dispatchEvent(new Event("input"));
452
+ setIsPressureEnabled(isCursorEnabled);
453
+ onCursorIsEnabled();
454
+ };
455
+ const onCursorIsEnabled = () => {
456
+ if (isCursorEnabled) {
457
+ targetEntity.setAttribute("visible", "false");
458
+ cursorExample.setAttribute("visible", "true");
459
+ cameraEntity.setAttribute("orbit-controls", { enabled: false });
460
+ cameraEntity.setAttribute("camera", { active: false });
461
+ cursorCameraEntity.setAttribute("camera", { active: true });
462
+ } else {
463
+ targetEntity.setAttribute("visible", "true");
464
+ cursorExample.setAttribute("visible", "false");
465
+ cursorCameraEntity.setAttribute("camera", { active: false });
466
+ cameraEntity.setAttribute("camera", { active: true });
467
+ cameraEntity.setAttribute("orbit-controls", { enabled: true });
468
+ }
469
+ };
470
+
471
+ devicePair.addEventListener("deviceIsConnected", (event) => {
472
+ const device = event.message.device;
473
+ if (device.side != side) {
474
+ return;
475
+ }
476
+ toggleCursorButton.disabled = !device.isConnected;
477
+ });
478
+
479
+ const cameraEntity = scene.querySelector(".camera");
480
+ const cursorCameraEntity = scene.querySelector(".cursorCamera");
481
+ const cursorEntity = scene.querySelector(".cursor");
482
+ const cursorHandleEntity = scene.querySelector(".cursorHandle");
483
+ const cursorMeshEntity = cursorEntity.querySelector(".mesh");
484
+ const cursorExample = scene.querySelector(".cursorExample");
485
+ devicePair.addEventListener("deviceGyroscope", (event) => {
486
+ if (event.message.side != side) {
487
+ return;
488
+ }
489
+ if (!isCursorEnabled) {
490
+ return;
491
+ }
492
+ const { gyroscope } = event.message;
493
+ const { x: yawDegrees, y: pitchDegrees, z: rollDegrees } = gyroscope;
494
+ // console.log({ yawDegrees, pitchDegrees, rollDegrees });
495
+ setCursorPosition(yawDegrees * 0.001, pitchDegrees * 0.001, true);
496
+ });
497
+ const cursorRaycaster = new THREE.Raycaster();
498
+ const cursor2DPosition = new THREE.Vector2();
499
+ const cursor3DPosition = new THREE.Vector3();
500
+ const setCursorPosition = (x, y, isOffset = false) => {
501
+ if (isOffset) {
502
+ cursor2DPosition.x += x;
503
+ cursor2DPosition.y += y;
504
+ } else {
505
+ cursor2DPosition.set(x, y);
506
+ }
507
+ cursor2DPosition.clampScalar(-1, 1);
508
+ updateCursorEntity();
509
+ if (!isCursorDown) {
510
+ intersectEntities();
511
+ }
512
+ if (isCursorDown && draggingEntity) {
513
+ dragEntity();
514
+ }
515
+ };
516
+ const updateCursorEntity = () => {
517
+ cursorRaycaster.setFromCamera(cursor2DPosition, cursorCameraEntity.object3D.children[0]);
518
+ cursorRaycaster.ray.at(1, cursor3DPosition);
519
+ cursorEntity.object3D.position.copy(cursor3DPosition);
520
+ };
521
+ const cursorIntersectableEntities = Array.from(scene.querySelectorAll(".intersectable"));
522
+
523
+ const dragEntityPosition = new THREE.Vector3();
524
+ const dragEntity = () => {
525
+ cursorRaycaster.ray.at(20, dragEntityPosition);
526
+ //draggingEntity.object3D.position.copy(dragEntityPosition);
527
+ cursorHandleEntity.object3D.position.copy(dragEntityPosition);
528
+ };
529
+
530
+ let intersectedEntities = [];
531
+ const intersectEntities = () => {
532
+ intersectedEntities.length = 0;
533
+ cursorIntersectableEntities.forEach((entity) => {
534
+ const intersections = cursorRaycaster.intersectObject(entity.object3D, true);
535
+ const intersection = intersections[0];
536
+ if (intersection) {
537
+ intersectedEntities.push(entity);
538
+ entity.setAttribute("color", "lightgreen");
539
+ } else {
540
+ entity.setAttribute("color", entity.dataset.color);
541
+ }
542
+ });
543
+ };
544
+
545
+ let isCursorDown = false;
546
+ scene.addEventListener("mousemove", (event) => {
547
+ const canvas = scene.canvas;
548
+ const rect = canvas.getBoundingClientRect();
549
+ const x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
550
+ const y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
551
+ setCursorPosition(x, y);
552
+ });
553
+ scene.addEventListener("mousedown", () => {
554
+ setIsCursorDown(true);
555
+ });
556
+ scene.addEventListener("mouseup", () => {
557
+ setIsCursorDown(false);
558
+ });
559
+ let draggingEntity;
560
+ const setIsCursorDown = (newIsCursorDown) => {
561
+ isCursorDown = newIsCursorDown;
562
+ cursorMeshEntity.setAttribute("color", isCursorDown ? "black" : cursorMeshEntity.dataset.color);
563
+ if (isCursorDown && intersectedEntities[0]) {
564
+ draggingEntity = intersectedEntities[0];
565
+ console.log("dragging entity");
566
+ draggingEntity.setAttribute("color", "green");
567
+ cursorHandleEntity.setAttribute("static-body", "");
568
+ draggingEntity.setAttribute("constraint", "target: .cursorHandle; collideConnected: false; type: pointToPoint;");
569
+ }
570
+ if (!isCursorDown && draggingEntity) {
571
+ console.log("removing draggingEntity");
572
+ draggingEntity.setAttribute("color", draggingEntity.dataset.color);
573
+
574
+ draggingEntity.removeAttribute("constraint");
575
+ cursorHandleEntity.removeAttribute("static-body");
576
+ draggingEntity = undefined;
577
+ }
578
+ };
579
+ devicePair.addEventListener("devicePressure", (event) => {
580
+ if (event.message.side != side) {
581
+ return;
582
+ }
583
+ const { pressure } = event.message;
584
+ const pinchPressure = pressure.sensors[4];
585
+ const isPinching = pinchPressure.normalizedValue > 0.5;
586
+ setIsCursorDown(isPinching);
587
+ });
588
+ onCursorIsEnabled();
589
+ }
590
+
591
+ // SERVER
592
+
593
+ const websocketClient = new BS.WebSocketClient();
594
+ /** @type {HTMLButtonElement} */
595
+ const toggleServerConnectionButton = document.getElementById("toggleServerConnection");
596
+ toggleServerConnectionButton.addEventListener("click", () => {
597
+ websocketClient.toggleConnection();
598
+ });
599
+ websocketClient.addEventListener("isConnected", () => {
600
+ toggleServerConnectionButton.innerText = websocketClient.isConnected ? "disconnect from server" : "connect to server";
601
+ });
602
+ websocketClient.addEventListener("connectionStatus", () => {
603
+ let disabled;
604
+ switch (websocketClient.connectionStatus) {
605
+ case "notConnected":
606
+ case "connected":
607
+ disabled = false;
608
+ break;
609
+ case "connecting":
610
+ case "disconnecting":
611
+ disabled = true;
612
+ break;
613
+ }
614
+ toggleServerConnectionButton.disabled = disabled;
615
+ });
@@ -67,7 +67,17 @@
67
67
  <template id="sensorTypeConfigurationTemplate">
68
68
  <label class="sensorTypeConfiguration">
69
69
  <b class="sensorType"></b>
70
- <input class="sensorRate" type="number" min="0" value="" disabled />
70
+ <!-- <input class="sensorRate" type="number" min="0" value="" disabled /> -->
71
+ <select class="sensorRate input">
72
+ <optgroup label="sensor rate (ms)">
73
+ <option value="0">0ms</option>
74
+ <option value="5">5ms</option>
75
+ <option value="10">10ms</option>
76
+ <option value="20">20ms</option>
77
+ <option value="40">40ms</option>
78
+ <option value="100">100ms</option>
79
+ </optgroup>
80
+ </select>
71
81
  </label>
72
82
  </template>
73
83
  </div>
@@ -125,7 +125,7 @@ function onSensorConfiguration(device) {
125
125
  for (const sensorType in device.sensorConfiguration) {
126
126
  const sensorRate = device.sensorConfiguration[sensorType];
127
127
  /** @type {HTMLInputElement?} */
128
- const input = document.querySelector(`.sensorTypeConfiguration[data-sensor-type="${sensorType}"] input`);
128
+ const input = document.querySelector(`.sensorTypeConfiguration[data-sensor-type="${sensorType}"] .input`);
129
129
  if (input) {
130
130
  input.value = sensorRate;
131
131
  }
@@ -152,7 +152,7 @@ function onSensorConfiguration(device) {
152
152
  function updateSensorRateInputs(device) {
153
153
  for (const sensorType in device.sensorConfiguration) {
154
154
  /** @type {HTMLInputElement?} */
155
- const input = document.querySelector(`[data-sensor-type="${sensorType}"] input`);
155
+ const input = document.querySelector(`[data-sensor-type="${sensorType}"] .input`);
156
156
  if (input) {
157
157
  input.disabled = !device.isConnected;
158
158
  }