remote-calibrator 0.3.0-rc.3 → 0.5.0-beta.10
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/CHANGELOG.md +35 -3
- package/README.md +34 -49
- package/homepage/example.css +4 -3
- package/homepage/example.js +42 -22
- package/homepage/index.html +19 -4
- package/i18n/fetch-languages-sheets.js +11 -1
- package/lib/RemoteCalibrator.min.js +1 -1
- package/lib/RemoteCalibrator.min.js.LICENSE.txt +19 -2
- package/lib/RemoteCalibrator.min.js.map +1 -1
- package/netlify.toml +1 -1
- package/package.json +25 -24
- package/src/WebGazer4RC/package-lock.json +198 -143
- package/src/WebGazer4RC/package.json +2 -2
- package/src/WebGazer4RC/src/index.mjs +161 -52
- package/src/WebGazer4RC/test/webgazer_test.js +1 -1
- package/src/check/checkScreenSize.js +84 -0
- package/src/components/buttons.js +21 -4
- package/src/components/input.js +82 -0
- package/src/components/keyBinder.js +5 -6
- package/src/components/language.js +5 -0
- package/src/components/mediaPermission.js +21 -0
- package/src/components/onCanvas.js +2 -2
- package/src/components/sound.js +30 -2
- package/src/components/swalOptions.js +6 -3
- package/src/components/utils.js +27 -1
- package/src/components/video.js +9 -6
- package/src/const.js +15 -0
- package/src/core.js +103 -48
- package/src/css/buttons.scss +34 -7
- package/src/css/components.scss +57 -0
- package/src/css/distance.scss +71 -1
- package/src/css/gaze.css +9 -5
- package/src/css/main.css +22 -6
- package/src/css/panel.scss +33 -3
- package/src/css/screenSize.css +6 -5
- package/src/css/swal.css +1 -1
- package/src/css/video.scss +19 -0
- package/src/distance/distance.js +194 -41
- package/src/distance/distanceCheck.js +241 -0
- package/src/distance/distanceTrack.js +165 -68
- package/src/{interpupillaryDistance.js → distance/interPupillaryDistance.js} +27 -19
- package/src/gaze/gaze.js +4 -7
- package/src/gaze/gazeAccuracy.js +9 -4
- package/src/gaze/gazeCalibration.js +14 -4
- package/src/gaze/gazeTracker.js +40 -9
- package/src/i18n.js +1 -1
- package/src/index.js +7 -2
- package/src/media/two-side-arrow.svg +1 -0
- package/src/media/two-sided-horizontal.svg +1 -0
- package/src/media/two-sided-vertical.svg +3 -0
- package/src/panel.js +130 -65
- package/src/screenSize.js +38 -5
- package/webpack.config.js +7 -4
- package/media/measureDistance.png +0 -0
- package/media/panel.png +0 -0
- package/media/screenSize.png +0 -0
- package/media/trackGaze.png +0 -0
- package/src/displaySize.js +0 -28
@@ -6,9 +6,11 @@ import {
|
|
6
6
|
constructInstructions,
|
7
7
|
blurAll,
|
8
8
|
sleep,
|
9
|
+
safeExecuteFunc,
|
9
10
|
} from '../components/utils'
|
10
11
|
import { iRepeat } from '../components/iRepeat'
|
11
12
|
import { phrases } from '../i18n'
|
13
|
+
import { spaceForLanguage } from '../components/language'
|
12
14
|
|
13
15
|
const originalStyles = {
|
14
16
|
video: false,
|
@@ -41,17 +43,26 @@ RemoteCalibrator.prototype.trackDistance = function (
|
|
41
43
|
{
|
42
44
|
fullscreen: false,
|
43
45
|
repeatTesting: 2,
|
44
|
-
sparkle:
|
46
|
+
sparkle: false,
|
45
47
|
pipWidthPx:
|
46
48
|
this._CONST.N.VIDEO_W[this.isMobile.value ? 'MOBILE' : 'DESKTOP'],
|
47
49
|
showVideo: true,
|
48
50
|
showFaceOverlay: false,
|
49
51
|
decimalPlace: 1,
|
50
|
-
framerate: 3, //
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
framerate: 3, // tracking rate
|
53
|
+
desiredDistanceCm: undefined,
|
54
|
+
desiredDistanceTolerance: 1.2,
|
55
|
+
desiredDistanceMonitor: false,
|
56
|
+
desiredDistanceMonitorCancelable: false,
|
57
|
+
nearPoint: true,
|
58
|
+
showNearPoint: false,
|
59
|
+
headline: '🙂 ' + phrases.RC_distanceTrackingTitle[this.L],
|
60
|
+
description:
|
61
|
+
phrases.RC_distanceTrackingIntroStart[this.L] +
|
62
|
+
spaceForLanguage(this.L) +
|
63
|
+
phrases.RC_viewingDistanceIntro[this.L] +
|
64
|
+
spaceForLanguage(this.L) +
|
65
|
+
phrases.RC_distanceTrackingIntroEnd[this.L],
|
55
66
|
},
|
56
67
|
options
|
57
68
|
)
|
@@ -85,9 +96,7 @@ RemoteCalibrator.prototype.trackDistance = function (
|
|
85
96
|
if (this.gazeTracker.checkInitialized('gaze', false))
|
86
97
|
this.showGazer(originalGazer)
|
87
98
|
|
88
|
-
|
89
|
-
callbackStatic(distData)
|
90
|
-
|
99
|
+
safeExecuteFunc(callbackStatic, distData)
|
91
100
|
stdDist.current = distData
|
92
101
|
}
|
93
102
|
|
@@ -112,6 +121,12 @@ RemoteCalibrator.prototype.trackDistance = function (
|
|
112
121
|
trackingOptions.nearPoint = options.nearPoint
|
113
122
|
trackingOptions.showNearPoint = options.showNearPoint
|
114
123
|
|
124
|
+
trackingOptions.desiredDistanceCm = options.desiredDistanceCm
|
125
|
+
trackingOptions.desiredDistanceTolerance = options.desiredDistanceTolerance
|
126
|
+
trackingOptions.desiredDistanceMonitor = options.desiredDistanceMonitor
|
127
|
+
trackingOptions.desiredDistanceMonitorCancelable =
|
128
|
+
options.desiredDistanceMonitorCancelable
|
129
|
+
|
115
130
|
originalStyles.video = options.showVideo
|
116
131
|
|
117
132
|
this.gazeTracker._init(
|
@@ -123,41 +138,52 @@ RemoteCalibrator.prototype.trackDistance = function (
|
|
123
138
|
'distance'
|
124
139
|
)
|
125
140
|
|
141
|
+
this._trackingSetupFinishedStatus.distance = false
|
142
|
+
|
143
|
+
const trackingConfig = {
|
144
|
+
options: options,
|
145
|
+
callbackStatic: callbackStatic,
|
146
|
+
callbackTrack: callbackTrack,
|
147
|
+
}
|
148
|
+
|
126
149
|
if (options.nearPoint) {
|
127
150
|
startTrackingPupils(
|
128
151
|
this,
|
129
152
|
() => {
|
130
|
-
this.
|
153
|
+
this._measurePD({}, _)
|
131
154
|
},
|
132
|
-
callbackTrack
|
155
|
+
callbackTrack,
|
156
|
+
trackingConfig
|
133
157
|
)
|
134
158
|
} else {
|
135
|
-
startTrackingPupils(this, _, callbackTrack)
|
159
|
+
startTrackingPupils(this, _, callbackTrack, trackingConfig)
|
136
160
|
}
|
137
161
|
}
|
138
162
|
|
139
163
|
/* -------------------------------------------------------------------------- */
|
140
164
|
|
141
|
-
const startTrackingPupils = async (
|
165
|
+
const startTrackingPupils = async (
|
166
|
+
RC,
|
167
|
+
beforeCallbackTrack,
|
168
|
+
callbackTrack,
|
169
|
+
trackingConfig
|
170
|
+
) => {
|
142
171
|
RC.gazeTracker.beginVideo({ pipWidthPx: trackingOptions.pipWidthPx }, () => {
|
143
172
|
RC._removeFloatInstructionElement()
|
144
|
-
beforeCallbackTrack
|
145
|
-
_tracking(RC, trackingOptions, callbackTrack)
|
173
|
+
safeExecuteFunc(beforeCallbackTrack)
|
174
|
+
_tracking(RC, trackingOptions, callbackTrack, trackingConfig)
|
146
175
|
})
|
147
176
|
}
|
148
177
|
|
149
178
|
const eyeDist = (a, b) => {
|
150
|
-
return Math.
|
151
|
-
Math.pow(a[0] - b[0], 2) +
|
152
|
-
Math.pow(a[1] - b[1], 2) +
|
153
|
-
Math.pow(a[2] - b[2], 2)
|
154
|
-
)
|
179
|
+
return Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2])
|
155
180
|
}
|
156
181
|
|
157
182
|
const cyclopean = (video, a, b) => {
|
158
|
-
|
159
|
-
|
160
|
-
|
183
|
+
return [
|
184
|
+
(-a[0] - b[0] + video.videoWidth) / 2,
|
185
|
+
(-a[1] - b[1] + video.videoHeight) / 2,
|
186
|
+
]
|
161
187
|
}
|
162
188
|
|
163
189
|
/* -------------------------------------------------------------------------- */
|
@@ -167,6 +193,10 @@ const trackingOptions = {
|
|
167
193
|
framerate: 3,
|
168
194
|
nearPoint: true,
|
169
195
|
showNearPoint: false,
|
196
|
+
desiredDistanceCm: undefined,
|
197
|
+
desiredDistanceTolerance: 1.2,
|
198
|
+
desiredDistanceMonitor: false,
|
199
|
+
desiredDistanceMonitorCancelable: false,
|
170
200
|
}
|
171
201
|
|
172
202
|
const stdDist = { current: null }
|
@@ -177,22 +207,31 @@ let iRepeatOptions = { framerate: 20, break: true }
|
|
177
207
|
let nearPointDot = null
|
178
208
|
/* -------------------------------------------------------------------------- */
|
179
209
|
|
180
|
-
|
210
|
+
let readyToGetFirstData = false
|
211
|
+
let averageDist = 0
|
212
|
+
let distCount = 1
|
213
|
+
|
214
|
+
const _tracking = async (
|
215
|
+
RC,
|
216
|
+
trackingOptions,
|
217
|
+
callbackTrack,
|
218
|
+
trackingConfig
|
219
|
+
) => {
|
181
220
|
const video = document.querySelector('#webgazerVideoFeed')
|
182
221
|
|
183
222
|
const _ = async () => {
|
184
223
|
// const canvas = RC.gazeTracker.webgazer.videoCanvas
|
185
224
|
let model, faces
|
186
225
|
|
187
|
-
// Get the average of
|
188
|
-
|
189
|
-
|
226
|
+
// Get the average of 5 estimates for one measure
|
227
|
+
averageDist = 0
|
228
|
+
distCount = 1
|
190
229
|
const targetCount = 5
|
191
230
|
|
192
231
|
model = await RC.gazeTracker.webgazer.getTracker().model
|
193
232
|
|
194
233
|
// Near point
|
195
|
-
let ppi = RC.screenPpi ? RC.screenPpi.value :
|
234
|
+
let ppi = RC.screenPpi ? RC.screenPpi.value : RC._CONST.N.PPI_DONT_USE
|
196
235
|
if (!RC.screenPpi && trackingOptions.nearPoint)
|
197
236
|
console.error(
|
198
237
|
'Screen size measurement is required to get accurate near point tracking.'
|
@@ -215,29 +254,54 @@ const _tracking = async (RC, trackingOptions, callbackTrack) => {
|
|
215
254
|
})
|
216
255
|
}
|
217
256
|
|
257
|
+
readyToGetFirstData = false
|
258
|
+
const {
|
259
|
+
desiredDistanceCm,
|
260
|
+
desiredDistanceTolerance,
|
261
|
+
desiredDistanceMonitor,
|
262
|
+
desiredDistanceMonitorCancelable,
|
263
|
+
} = trackingOptions
|
264
|
+
|
265
|
+
// Always enable correct on a fresh start
|
266
|
+
RC._distanceTrackNudging.distanceCorrectEnabled = true
|
267
|
+
RC._distanceTrackNudging.distanceDesired = desiredDistanceCm
|
268
|
+
RC._distanceTrackNudging.distanceAllowedRatio = desiredDistanceTolerance
|
269
|
+
|
218
270
|
viewingDistanceTrackingFunction = async () => {
|
271
|
+
//
|
272
|
+
const videoTimestamp = new Date().getTime()
|
273
|
+
//
|
219
274
|
faces = await model.estimateFaces(video)
|
220
275
|
if (faces.length) {
|
221
276
|
// There's at least one face in video
|
222
|
-
|
277
|
+
RC._tackingVideoFrameTimestamps.distance += videoTimestamp
|
223
278
|
// https://github.com/tensorflow/tfjs-models/blob/master/facemesh/mesh_map.jpg
|
279
|
+
const mesh = faces[0].scaledMesh
|
280
|
+
|
224
281
|
if (targetCount === distCount) {
|
225
282
|
averageDist += eyeDist(mesh[133], mesh[362])
|
226
|
-
averageDist /=
|
283
|
+
averageDist /= targetCount
|
284
|
+
RC._tackingVideoFrameTimestamps.distance /= targetCount
|
227
285
|
|
286
|
+
// TODO Add more samples for the first estimate
|
228
287
|
if (stdDist.current !== null) {
|
229
288
|
if (!stdFactor) {
|
230
289
|
// ! First time estimate
|
231
290
|
// Face_Known_Px * Distance_Known_Cm = Face_Now_Px * Distance_x_Cm
|
232
291
|
// Get the factor to be used for future predictions
|
233
292
|
stdFactor = averageDist * stdDist.current.value
|
234
|
-
// FINISH
|
293
|
+
// ! FINISH
|
235
294
|
RC._removeBackground()
|
295
|
+
RC._trackingSetupFinishedStatus.distance = true
|
296
|
+
readyToGetFirstData = true
|
236
297
|
}
|
237
298
|
|
238
299
|
/* -------------------------------------------------------------------------- */
|
239
300
|
|
240
301
|
const timestamp = new Date()
|
302
|
+
const latency = Math.round(
|
303
|
+
timestamp.getTime() - RC._tackingVideoFrameTimestamps.distance
|
304
|
+
)
|
241
305
|
|
242
306
|
const data = (RC.newViewingDistanceData = {
|
243
307
|
value: toFixedNumber(
|
@@ -245,9 +309,20 @@ const _tracking = async (RC, trackingOptions, callbackTrack) => {
|
|
245
309
|
trackingOptions.decimalPlace
|
246
310
|
),
|
247
311
|
timestamp: timestamp,
|
248
|
-
method:
|
312
|
+
method: RC._CONST.VIEW_METHOD.F,
|
313
|
+
latencyMs: latency,
|
249
314
|
})
|
250
315
|
|
316
|
+
if (readyToGetFirstData || desiredDistanceMonitor) {
|
317
|
+
// ! Check distance
|
318
|
+
if (desiredDistanceCm)
|
319
|
+
RC.checkDistance(
|
320
|
+
desiredDistanceMonitorCancelable,
|
321
|
+
trackingConfig
|
322
|
+
)
|
323
|
+
readyToGetFirstData = false
|
324
|
+
}
|
325
|
+
|
251
326
|
/* -------------------------------------------------------------------------- */
|
252
327
|
|
253
328
|
// Near point
|
@@ -260,7 +335,8 @@ const _tracking = async (RC, trackingOptions, callbackTrack) => {
|
|
260
335
|
mesh,
|
261
336
|
averageDist,
|
262
337
|
timestamp,
|
263
|
-
ppi
|
338
|
+
ppi,
|
339
|
+
latency
|
264
340
|
)
|
265
341
|
}
|
266
342
|
|
@@ -272,15 +348,18 @@ const _tracking = async (RC, trackingOptions, callbackTrack) => {
|
|
272
348
|
value: {
|
273
349
|
viewingDistanceCm: data.value,
|
274
350
|
nearPointCm: nPData ? nPData.value : [null, null],
|
351
|
+
latencyMs: latency,
|
275
352
|
},
|
276
353
|
timestamp: timestamp,
|
277
|
-
method:
|
354
|
+
method: RC._CONST.VIEW_METHOD.F,
|
278
355
|
})
|
279
356
|
}
|
280
357
|
}
|
281
358
|
|
282
359
|
averageDist = 0
|
283
360
|
distCount = 1
|
361
|
+
|
362
|
+
RC._tackingVideoFrameTimestamps.distance = 0
|
284
363
|
} else {
|
285
364
|
averageDist += eyeDist(mesh[133], mesh[362])
|
286
365
|
++distCount
|
@@ -289,11 +368,11 @@ const _tracking = async (RC, trackingOptions, callbackTrack) => {
|
|
289
368
|
}
|
290
369
|
|
291
370
|
iRepeatOptions.break = false
|
292
|
-
iRepeatOptions.framerate = targetCount * trackingOptions.framerate // Default
|
371
|
+
iRepeatOptions.framerate = targetCount * trackingOptions.framerate // Default 5 * 3
|
293
372
|
iRepeat(viewingDistanceTrackingFunction, iRepeatOptions)
|
294
373
|
}
|
295
374
|
|
296
|
-
sleep(
|
375
|
+
sleep(1000).then(_)
|
297
376
|
}
|
298
377
|
|
299
378
|
const _getNearPoint = (
|
@@ -303,45 +382,42 @@ const _getNearPoint = (
|
|
303
382
|
mesh,
|
304
383
|
averageDist,
|
305
384
|
timestamp,
|
306
|
-
ppi
|
385
|
+
ppi,
|
386
|
+
latency
|
307
387
|
) => {
|
308
|
-
let
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
const videoFactor = video.videoHeight / video.clientHeight
|
315
|
-
offsetToVideoMid.forEach((e, i) => {
|
316
|
-
// Average interpupillary distance - 6.4cm
|
317
|
-
offsetToVideoMid[i] =
|
318
|
-
((RC.PDCm ? RC.PDCm.value : 6.4) * e) /
|
319
|
-
(averageDist * (videoFactor / 2)) /* Should this be videoFactor? */
|
388
|
+
let offsetToVideoCenter = cyclopean(video, mesh[133], mesh[362])
|
389
|
+
offsetToVideoCenter.forEach((offset, i) => {
|
390
|
+
// Average inter-pupillary distance - 6.4cm
|
391
|
+
offsetToVideoCenter[i] =
|
392
|
+
((RC.PDCm ? RC.PDCm.value : RC._CONST.N.PD_DONT_USE) * offset) /
|
393
|
+
averageDist
|
320
394
|
})
|
321
395
|
|
322
396
|
let nPData = (RC.newNearPointData = {
|
323
397
|
value: {
|
324
|
-
x: toFixedNumber(
|
398
|
+
x: toFixedNumber(offsetToVideoCenter[0], trackingOptions.decimalPlace),
|
325
399
|
y: toFixedNumber(
|
326
|
-
|
400
|
+
offsetToVideoCenter[1] + ((screen.height / 2) * 2.54) / ppi, // Commonly the webcam is 0.5cm above the screen
|
327
401
|
trackingOptions.decimalPlace
|
328
402
|
),
|
403
|
+
latencyMs: latency,
|
329
404
|
},
|
330
405
|
timestamp: timestamp,
|
331
406
|
})
|
332
407
|
|
333
408
|
// SHOW
|
409
|
+
const dotR = 5
|
334
410
|
if (trackingOptions.showNearPoint) {
|
335
411
|
let offsetX = (nPData.value.x * ppi) / 2.54
|
336
412
|
let offsetY = (nPData.value.y * ppi) / 2.54
|
337
413
|
Object.assign(nearPointDot.style, {
|
338
|
-
left: `${screen.width / 2 - window.screenLeft
|
414
|
+
left: `${screen.width / 2 - window.screenLeft + offsetX - dotR}px`,
|
339
415
|
top: `${
|
340
416
|
screen.height / 2 -
|
341
417
|
window.screenTop -
|
342
|
-
|
343
|
-
|
344
|
-
|
418
|
+
(window.outerHeight - window.innerHeight) -
|
419
|
+
offsetY -
|
420
|
+
dotR
|
345
421
|
}px`,
|
346
422
|
})
|
347
423
|
}
|
@@ -353,6 +429,7 @@ RemoteCalibrator.prototype.pauseDistance = function () {
|
|
353
429
|
if (this.gazeTracker.checkInitialized('distance', true)) {
|
354
430
|
iRepeatOptions.break = true
|
355
431
|
if (nearPointDot) nearPointDot.style.display = 'none'
|
432
|
+
this._tackingVideoFrameTimestamps.distance = 0
|
356
433
|
return this
|
357
434
|
}
|
358
435
|
return null
|
@@ -362,6 +439,11 @@ RemoteCalibrator.prototype.resumeDistance = function () {
|
|
362
439
|
if (this.gazeTracker.checkInitialized('distance', true)) {
|
363
440
|
iRepeatOptions.break = false
|
364
441
|
if (nearPointDot) nearPointDot.style.display = 'block'
|
442
|
+
|
443
|
+
averageDist = 0
|
444
|
+
distCount = 1
|
445
|
+
this._tackingVideoFrameTimestamps.distance = 0
|
446
|
+
|
365
447
|
iRepeat(viewingDistanceTrackingFunction, iRepeatOptions)
|
366
448
|
return this
|
367
449
|
}
|
@@ -379,21 +461,30 @@ RemoteCalibrator.prototype.endDistance = function (endAll = false, _r = true) {
|
|
379
461
|
trackingOptions.nearPoint = true
|
380
462
|
trackingOptions.showNearPoint = false
|
381
463
|
|
464
|
+
trackingOptions.desiredDistanceCm = undefined
|
465
|
+
trackingOptions.desiredDistanceTolerance = 1.2
|
466
|
+
trackingOptions.desiredDistanceMonitor = false
|
467
|
+
trackingOptions.desiredDistanceMonitorCancelable = false
|
468
|
+
|
382
469
|
stdDist.current = null
|
383
470
|
stdFactor = null
|
384
471
|
viewingDistanceTrackingFunction = null
|
385
472
|
|
473
|
+
readyToGetFirstData = false
|
474
|
+
this._tackingVideoFrameTimestamps.distance = 0
|
475
|
+
|
386
476
|
// Near point
|
387
477
|
if (nearPointDot) {
|
388
478
|
document.body.removeChild(nearPointDot)
|
389
479
|
nearPointDot = null
|
390
480
|
}
|
391
481
|
|
392
|
-
|
482
|
+
// Nudger
|
483
|
+
this.endNudger()
|
393
484
|
|
485
|
+
if (_r) this.gazeTracker.end('distance', endAll)
|
394
486
|
return this
|
395
487
|
}
|
396
|
-
|
397
488
|
return null
|
398
489
|
}
|
399
490
|
|
@@ -409,6 +500,7 @@ RemoteCalibrator.prototype.getDistanceNow = async function (callback = null) {
|
|
409
500
|
|
410
501
|
let v = document.querySelector('#webgazerVideoFeed')
|
411
502
|
let m = await this.gazeTracker.webgazer.getTracker().model
|
503
|
+
const videoTimestamp = new Date().getTime()
|
412
504
|
let f = await m.estimateFaces(v)
|
413
505
|
|
414
506
|
if (f.length) {
|
@@ -416,11 +508,15 @@ RemoteCalibrator.prototype.getDistanceNow = async function (callback = null) {
|
|
416
508
|
const dist = eyeDist(mesh[133], mesh[362])
|
417
509
|
|
418
510
|
let timestamp = new Date()
|
511
|
+
//
|
512
|
+
const latency = timestamp.getTime() - videoTimestamp
|
513
|
+
//
|
419
514
|
|
420
515
|
const data = (this.newViewingDistanceData = {
|
421
516
|
value: toFixedNumber(stdFactor / dist, trackingOptions.decimalPlace),
|
422
517
|
timestamp: timestamp,
|
423
|
-
method:
|
518
|
+
method: this._CONST.VIEW_METHOD.F,
|
519
|
+
latencyMs: latency,
|
424
520
|
})
|
425
521
|
|
426
522
|
let nPData
|
@@ -432,19 +528,20 @@ RemoteCalibrator.prototype.getDistanceNow = async function (callback = null) {
|
|
432
528
|
mesh,
|
433
529
|
dist,
|
434
530
|
timestamp,
|
435
|
-
this.screenPpi ? this.screenPpi.value :
|
531
|
+
this.screenPpi ? this.screenPpi.value : this._CONST.N.PPI_DONT_USE,
|
532
|
+
latency
|
436
533
|
)
|
437
534
|
}
|
438
535
|
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
536
|
+
safeExecuteFunc(c, {
|
537
|
+
value: {
|
538
|
+
viewingDistanceCm: data.value,
|
539
|
+
nearPointCm: nPData ? nPData.value : null,
|
540
|
+
latencyMs: latency,
|
541
|
+
},
|
542
|
+
timestamp: timestamp,
|
543
|
+
method: this._CONST.VIEW_METHOD.F,
|
544
|
+
})
|
448
545
|
return data
|
449
546
|
}
|
450
547
|
|
@@ -1,19 +1,20 @@
|
|
1
1
|
import Swal from 'sweetalert2'
|
2
2
|
|
3
|
-
import RemoteCalibrator from '
|
3
|
+
import RemoteCalibrator from '../core'
|
4
4
|
|
5
5
|
import {
|
6
6
|
blurAll,
|
7
7
|
constructInstructions,
|
8
|
+
safeExecuteFunc,
|
8
9
|
toFixedNumber,
|
9
|
-
} from '
|
10
|
-
import { swalInfoOptions } from '
|
11
|
-
import Arrow from '
|
12
|
-
import PD from '
|
13
|
-
import { bindKeys, unbindKeys } from '
|
14
|
-
import { addButtons } from '
|
15
|
-
import { setDefaultVideoPosition } from '
|
16
|
-
import { phrases } from '
|
10
|
+
} from '../components/utils'
|
11
|
+
import { swalInfoOptions } from '../components/swalOptions'
|
12
|
+
import Arrow from '../media/arrow.svg'
|
13
|
+
import PD from '../media/pd.png?width=480&height=240'
|
14
|
+
import { bindKeys, unbindKeys } from '../components/keyBinder'
|
15
|
+
import { addButtons } from '../components/buttons'
|
16
|
+
import { setDefaultVideoPosition } from '../components/video'
|
17
|
+
import { phrases } from '../i18n'
|
17
18
|
|
18
19
|
// let selfVideo = false // No WebGazer video available and an extra video element needs to be created
|
19
20
|
|
@@ -29,7 +30,7 @@ const originalStyles = {
|
|
29
30
|
const videoWidthFactor = 0.9
|
30
31
|
const videoHeightFactor = 0.3
|
31
32
|
|
32
|
-
RemoteCalibrator.prototype.
|
33
|
+
RemoteCalibrator.prototype._measurePD = function (options = {}, callback) {
|
33
34
|
////
|
34
35
|
if (!this.checkInitialized()) return
|
35
36
|
blurAll()
|
@@ -39,7 +40,7 @@ RemoteCalibrator.prototype.measurePD = function (options = {}, callback) {
|
|
39
40
|
{
|
40
41
|
fullscreen: false,
|
41
42
|
headline: '👁️ ' + phrases.RC_nearPointTitle[this.L],
|
42
|
-
description: phrases.
|
43
|
+
description: phrases.RC_nearPointIntro[this.L],
|
43
44
|
shortDescription: phrases.RC_nearPointIntro[this.L],
|
44
45
|
},
|
45
46
|
options
|
@@ -50,9 +51,11 @@ RemoteCalibrator.prototype.measurePD = function (options = {}, callback) {
|
|
50
51
|
this._replaceBackground()
|
51
52
|
|
52
53
|
this._replaceBackground(
|
53
|
-
constructInstructions(options.headline, options.shortDescription)
|
54
|
+
constructInstructions(options.headline, options.shortDescription, true)
|
54
55
|
)
|
55
|
-
const screenPpi = this.screenPpi
|
56
|
+
const screenPpi = this.screenPpi
|
57
|
+
? this.screenPpi.value
|
58
|
+
: this._CONST.N.PPI_DONT_USE
|
56
59
|
|
57
60
|
let [videoWidth, videoHeight] = setupVideo(this)
|
58
61
|
let [ruler, rulerListener] = setupRuler(
|
@@ -64,7 +67,7 @@ RemoteCalibrator.prototype.measurePD = function (options = {}, callback) {
|
|
64
67
|
|
65
68
|
const RC = this
|
66
69
|
|
67
|
-
const breakFunction = () => {
|
70
|
+
const breakFunction = (toBreak = true) => {
|
68
71
|
ruler.removeEventListener('mousedown', rulerListener)
|
69
72
|
this._removeBackground()
|
70
73
|
|
@@ -77,7 +80,7 @@ RemoteCalibrator.prototype.measurePD = function (options = {}, callback) {
|
|
77
80
|
height: originalStyles.videoHeight,
|
78
81
|
width: originalStyles.videoWidth,
|
79
82
|
opacity: originalStyles.opacity,
|
80
|
-
borderRadius: '
|
83
|
+
borderRadius: '5px',
|
81
84
|
})
|
82
85
|
setDefaultVideoPosition(
|
83
86
|
RC,
|
@@ -99,6 +102,11 @@ RemoteCalibrator.prototype.measurePD = function (options = {}, callback) {
|
|
99
102
|
originalStyles.gaze = false
|
100
103
|
originalStyles.faceOverlay = false
|
101
104
|
|
105
|
+
if (!RC._trackingSetupFinishedStatus.distance && toBreak) {
|
106
|
+
RC._trackingSetupFinishedStatus.distance = true
|
107
|
+
RC.endDistance()
|
108
|
+
}
|
109
|
+
|
102
110
|
unbindKeys(bindKeysFunction)
|
103
111
|
}
|
104
112
|
|
@@ -110,14 +118,14 @@ RemoteCalibrator.prototype.measurePD = function (options = {}, callback) {
|
|
110
118
|
}
|
111
119
|
this.newPDData = newPDData
|
112
120
|
|
113
|
-
breakFunction()
|
114
|
-
callback
|
121
|
+
breakFunction(false)
|
122
|
+
safeExecuteFunc(callback, newPDData)
|
115
123
|
}
|
116
124
|
}
|
117
125
|
|
118
|
-
// if (callback && typeof callback === 'function') callback()
|
119
126
|
const bindKeysFunction = bindKeys({
|
120
127
|
Escape: breakFunction,
|
128
|
+
Enter: finishFunction,
|
121
129
|
' ': finishFunction,
|
122
130
|
})
|
123
131
|
addButtons(
|
@@ -133,7 +141,7 @@ RemoteCalibrator.prototype.measurePD = function (options = {}, callback) {
|
|
133
141
|
// TODO To be removed
|
134
142
|
setTimeout(() => {
|
135
143
|
Swal.fire({
|
136
|
-
...swalInfoOptions(this),
|
144
|
+
...swalInfoOptions(this, { showIcon: false }),
|
137
145
|
icon: undefined,
|
138
146
|
imageUrl: PD,
|
139
147
|
imageWidth: 480,
|
package/src/gaze/gaze.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import RemoteCalibrator from '../core'
|
2
2
|
|
3
|
-
import { blurAll } from '../components/utils'
|
3
|
+
import { blurAll, safeExecuteFunc } from '../components/utils'
|
4
4
|
import { gazeCalibrationPrepare } from './gazeCalibration'
|
5
5
|
import { phrases } from '../i18n'
|
6
6
|
|
@@ -97,17 +97,14 @@ RemoteCalibrator.prototype.trackGaze = function (
|
|
97
97
|
description: options.description,
|
98
98
|
}
|
99
99
|
this.gazeTracker.begin(gazeTrackerBeginOptions, () => {
|
100
|
+
this._trackingSetupFinishedStatus.gaze = false
|
100
101
|
this.calibrateGaze(calibrateGazeOptions, onCalibrationEnded)
|
101
102
|
})
|
102
103
|
|
103
104
|
// Calibration
|
104
105
|
|
105
|
-
const onCalibrationEnded =
|
106
|
-
|
107
|
-
callbackOnCalibrationEnd &&
|
108
|
-
typeof callbackOnCalibrationEnd === 'function'
|
109
|
-
)
|
110
|
-
callbackOnCalibrationEnd()
|
106
|
+
const onCalibrationEnded = data => {
|
107
|
+
safeExecuteFunc(callbackOnCalibrationEnd, data)
|
111
108
|
|
112
109
|
// ! greedyLearner
|
113
110
|
if (!this.gazeTracker.webgazer.params.greedyLearner) {
|
package/src/gaze/gazeAccuracy.js
CHANGED
@@ -3,7 +3,12 @@ import Swal from 'sweetalert2/dist/sweetalert2.js'
|
|
3
3
|
import RemoteCalibrator from '../core'
|
4
4
|
|
5
5
|
import { _cross } from '../components/onCanvas'
|
6
|
-
import {
|
6
|
+
import {
|
7
|
+
blurAll,
|
8
|
+
safeExecuteFunc,
|
9
|
+
sleep,
|
10
|
+
toFixedNumber,
|
11
|
+
} from '../components/utils'
|
7
12
|
import { swalInfoOptions } from '../components/swalOptions'
|
8
13
|
|
9
14
|
let inGetAccuracy = false
|
@@ -57,7 +62,7 @@ RemoteCalibrator.prototype.getGazeAccuracy = function (
|
|
57
62
|
_resizeCanvas()
|
58
63
|
|
59
64
|
Swal.fire({
|
60
|
-
...swalInfoOptions(this),
|
65
|
+
...swalInfoOptions(this, { showIcon: true }),
|
61
66
|
// title: text.getGazeAccuracy.headline,
|
62
67
|
html: `We will measure your gaze accuracy. Please do not move the mouse and look at the fixation at the middle of the screen for the next 5 seconds.`,
|
63
68
|
}).then(() => {
|
@@ -88,8 +93,8 @@ RemoteCalibrator.prototype.getGazeAccuracy = function (
|
|
88
93
|
|
89
94
|
if (averageDegree < options.thresholdDeg)
|
90
95
|
// Success
|
91
|
-
callbackSuccess
|
92
|
-
else callbackFail
|
96
|
+
safeExecuteFunc(callbackSuccess)
|
97
|
+
else safeExecuteFunc(callbackFail)
|
93
98
|
|
94
99
|
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
95
100
|
resizeObserver.unobserve(this.background)
|