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.
- package/README.md +16 -10
- package/assets/3d/rightHand.glb +0 -0
- package/assets/images/ukaton-pressure-0.svg +9 -0
- package/assets/images/ukaton-pressure-1.svg +9 -0
- package/assets/images/ukaton-pressure-10.svg +9 -0
- package/assets/images/ukaton-pressure-11.svg +9 -0
- package/assets/images/ukaton-pressure-12.svg +9 -0
- package/assets/images/ukaton-pressure-13.svg +9 -0
- package/assets/images/ukaton-pressure-14.svg +9 -0
- package/assets/images/ukaton-pressure-15.svg +9 -0
- package/assets/images/ukaton-pressure-2.svg +9 -0
- package/assets/images/ukaton-pressure-3.svg +9 -0
- package/assets/images/ukaton-pressure-4.svg +9 -0
- package/assets/images/ukaton-pressure-5.svg +9 -0
- package/assets/images/ukaton-pressure-6.svg +9 -0
- package/assets/images/ukaton-pressure-7.svg +9 -0
- package/assets/images/ukaton-pressure-8.svg +9 -0
- package/assets/images/ukaton-pressure-9.svg +9 -0
- package/assets/images/ukaton-right-insole.svg +798 -0
- package/build/brilliantsole.cjs +255 -119
- package/build/brilliantsole.cjs.map +1 -1
- package/build/brilliantsole.js +283 -140
- package/build/brilliantsole.js.map +1 -1
- package/build/brilliantsole.ls.js +210 -103
- 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 +76 -60
- package/build/brilliantsole.module.js +282 -140
- package/build/brilliantsole.module.js.map +1 -1
- package/build/brilliantsole.module.min.d.ts +76 -60
- package/build/brilliantsole.module.min.js +1 -1
- package/build/brilliantsole.module.min.js.map +1 -1
- package/build/brilliantsole.node.module.d.ts +75 -60
- package/build/brilliantsole.node.module.js +254 -119
- package/build/brilliantsole.node.module.js.map +1 -1
- package/build/dts/BS.d.ts +2 -2
- package/build/dts/Device.d.ts +11 -7
- package/build/dts/DeviceManager.d.ts +1 -0
- package/build/dts/InformationManager.d.ts +6 -5
- package/build/dts/connection/BaseConnectionManager.d.ts +2 -0
- package/build/dts/connection/ClientConnectionManager.d.ts +4 -0
- package/build/dts/connection/bluetooth/BluetoothConnectionManager.d.ts +1 -0
- package/build/dts/connection/bluetooth/NobleConnectionManager.d.ts +1 -0
- package/build/dts/connection/bluetooth/WebBluetoothConnectionManager.d.ts +1 -0
- package/build/dts/devicePair/DevicePair.d.ts +14 -10
- package/build/dts/devicePair/DevicePairPressureSensorDataManager.d.ts +8 -4
- package/build/dts/devicePair/DevicePairSensorDataManager.d.ts +2 -2
- package/build/dts/server/BaseClient.d.ts +1 -0
- package/build/dts/utils/CenterOfPressureHelper.d.ts +2 -2
- package/build/dts/utils/MathUtils.d.ts +2 -0
- package/build/index.d.ts +76 -60
- package/build/index.node.d.ts +75 -60
- package/examples/3d/script.js +90 -17
- package/examples/balance/script.js +2 -1
- package/examples/basic/index.html +21 -2
- package/examples/basic/script.js +17 -3
- package/examples/bottango/index.html +11 -1
- package/examples/bottango/script.js +2 -2
- package/examples/center-of-pressure/index.html +114 -114
- package/examples/center-of-pressure/script.js +1 -1
- package/examples/device-pair/index.html +58 -58
- package/examples/device-pair/script.js +12 -8
- package/examples/gloves/index.html +116 -0
- package/examples/gloves/scene.html +124 -0
- package/examples/gloves/script.js +615 -0
- package/examples/graph/index.html +11 -1
- package/examples/graph/script.js +2 -2
- package/examples/pressure/index.html +180 -12
- package/examples/pressure/script.js +144 -7
- package/examples/recording/index.html +191 -183
- package/examples/server/index.html +11 -1
- package/examples/server/script.js +6 -3
- package/examples/ukaton-firmware-update/index.html +20 -0
- package/examples/ukaton-firmware-update/manifest.json +11 -0
- package/examples/ukaton-firmware-update/merged-firmware.bin +0 -0
- package/examples/webxr/script.js +3 -3
- package/package.json +1 -1
- package/src/BS.ts +3 -8
- package/src/Device.ts +41 -8
- package/src/DeviceInformationManager.ts +4 -2
- package/src/DeviceManager.ts +10 -1
- package/src/FileTransferManager.ts +1 -1
- package/src/FirmwareManager.ts +1 -1
- package/src/InformationManager.ts +24 -7
- package/src/TfliteManager.ts +1 -1
- package/src/connection/BaseConnectionManager.ts +23 -6
- package/src/connection/ClientConnectionManager.ts +13 -1
- package/src/connection/bluetooth/BluetoothConnectionManager.ts +6 -1
- package/src/connection/bluetooth/NobleConnectionManager.ts +8 -1
- package/src/connection/bluetooth/WebBluetoothConnectionManager.ts +5 -1
- package/src/devicePair/DevicePair.ts +53 -27
- package/src/devicePair/DevicePairPressureSensorDataManager.ts +51 -23
- package/src/devicePair/DevicePairSensorDataManager.ts +5 -5
- package/src/scanner/NobleScanner.ts +5 -3
- package/src/sensor/BarometerSensorDataManager.ts +1 -1
- package/src/sensor/MotionSensorDataManager.ts +1 -1
- package/src/sensor/PressureSensorDataManager.ts +13 -8
- package/src/sensor/SensorConfigurationManager.ts +3 -3
- package/src/sensor/SensorDataManager.ts +1 -1
- package/src/server/BaseClient.ts +43 -2
- package/src/server/BaseServer.ts +149 -39
- package/src/server/udp/UDPServer.ts +1 -1
- package/src/server/websocket/WebSocketClient.ts +3 -2
- package/src/server/websocket/WebSocketServer.ts +1 -1
- package/src/utils/CenterOfPressureHelper.ts +5 -5
- package/src/utils/MathUtils.ts +31 -1
- package/src/utils/ParseUtils.ts +1 -1
- package/src/utils/checksum.ts +1 -1
- 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>
|
package/examples/graph/script.js
CHANGED
|
@@ -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
|
}
|