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.
Files changed (58) hide show
  1. package/CHANGELOG.md +35 -3
  2. package/README.md +34 -49
  3. package/homepage/example.css +4 -3
  4. package/homepage/example.js +42 -22
  5. package/homepage/index.html +19 -4
  6. package/i18n/fetch-languages-sheets.js +11 -1
  7. package/lib/RemoteCalibrator.min.js +1 -1
  8. package/lib/RemoteCalibrator.min.js.LICENSE.txt +19 -2
  9. package/lib/RemoteCalibrator.min.js.map +1 -1
  10. package/netlify.toml +1 -1
  11. package/package.json +25 -24
  12. package/src/WebGazer4RC/package-lock.json +198 -143
  13. package/src/WebGazer4RC/package.json +2 -2
  14. package/src/WebGazer4RC/src/index.mjs +161 -52
  15. package/src/WebGazer4RC/test/webgazer_test.js +1 -1
  16. package/src/check/checkScreenSize.js +84 -0
  17. package/src/components/buttons.js +21 -4
  18. package/src/components/input.js +82 -0
  19. package/src/components/keyBinder.js +5 -6
  20. package/src/components/language.js +5 -0
  21. package/src/components/mediaPermission.js +21 -0
  22. package/src/components/onCanvas.js +2 -2
  23. package/src/components/sound.js +30 -2
  24. package/src/components/swalOptions.js +6 -3
  25. package/src/components/utils.js +27 -1
  26. package/src/components/video.js +9 -6
  27. package/src/const.js +15 -0
  28. package/src/core.js +103 -48
  29. package/src/css/buttons.scss +34 -7
  30. package/src/css/components.scss +57 -0
  31. package/src/css/distance.scss +71 -1
  32. package/src/css/gaze.css +9 -5
  33. package/src/css/main.css +22 -6
  34. package/src/css/panel.scss +33 -3
  35. package/src/css/screenSize.css +6 -5
  36. package/src/css/swal.css +1 -1
  37. package/src/css/video.scss +19 -0
  38. package/src/distance/distance.js +194 -41
  39. package/src/distance/distanceCheck.js +241 -0
  40. package/src/distance/distanceTrack.js +165 -68
  41. package/src/{interpupillaryDistance.js → distance/interPupillaryDistance.js} +27 -19
  42. package/src/gaze/gaze.js +4 -7
  43. package/src/gaze/gazeAccuracy.js +9 -4
  44. package/src/gaze/gazeCalibration.js +14 -4
  45. package/src/gaze/gazeTracker.js +40 -9
  46. package/src/i18n.js +1 -1
  47. package/src/index.js +7 -2
  48. package/src/media/two-side-arrow.svg +1 -0
  49. package/src/media/two-sided-horizontal.svg +1 -0
  50. package/src/media/two-sided-vertical.svg +3 -0
  51. package/src/panel.js +130 -65
  52. package/src/screenSize.js +38 -5
  53. package/webpack.config.js +7 -4
  54. package/media/measureDistance.png +0 -0
  55. package/media/panel.png +0 -0
  56. package/media/screenSize.png +0 -0
  57. package/media/trackGaze.png +0 -0
  58. package/src/displaySize.js +0 -28
@@ -69,7 +69,7 @@
69
69
  top: 0;
70
70
  width: 2px;
71
71
  transform: translate(-1px, 0);
72
- z-index: 1;
72
+ z-index: 2;
73
73
  }
74
74
 
75
75
  .rc-ruler-major {
@@ -87,3 +87,73 @@
87
87
  height: 16px;
88
88
  }
89
89
  }
90
+
91
+ /* ---------------------------------- Check --------------------------------- */
92
+
93
+ .hide-nudger {
94
+ .calibration-nudger {
95
+ display: none !important;
96
+ opacity: 0 !important;
97
+ }
98
+ }
99
+
100
+ .calibration-nudger {
101
+ z-index: 999999999;
102
+ position: fixed;
103
+ width: 100%;
104
+ height: 100%;
105
+ top: 0;
106
+ left: 0;
107
+ right: 0;
108
+ bottom: 0;
109
+ margin: 0;
110
+ overflow: hidden;
111
+ user-select: none;
112
+ box-sizing: border-box;
113
+ text-align: center;
114
+ scrollbar-width: none;
115
+
116
+ * {
117
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
118
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
119
+ }
120
+ }
121
+
122
+ #rc-distance-correct {
123
+ text-align: center;
124
+ width: 100%;
125
+ margin: 3rem auto auto auto;
126
+ padding: 2rem;
127
+ overflow-wrap: break-word;
128
+
129
+ #rc-distance-correct-instruction {
130
+ font-weight: 700;
131
+ font-size: 7rem;
132
+ margin: 2rem auto;
133
+ line-height: 100%;
134
+ }
135
+
136
+ #rc-distance-correct-guide {
137
+ font-weight: 500;
138
+ font-size: 3rem;
139
+ line-height: 150%;
140
+
141
+ .rc-distance-num {
142
+ padding: 0.5rem;
143
+ border-radius: 7px !important;
144
+ font-weight: 700;
145
+ font-family: monospace !important;
146
+ vertical-align: middle;
147
+ }
148
+
149
+ .rc-distance-now {
150
+ border: 2px solid #ff9a00;
151
+ background-color: #ff9a0066;
152
+ }
153
+
154
+ .rc-distance-desired {
155
+ border: 2px solid #3490de;
156
+ background-color: #3490de66;
157
+ }
158
+ }
159
+ }
package/src/css/gaze.css CHANGED
@@ -8,7 +8,7 @@
8
8
  position: fixed !important;
9
9
  display: block;
10
10
  cursor: initial;
11
- z-index: 9998;
11
+ z-index: 999999998;
12
12
  transition-timing-function: ease-in-out;
13
13
  transition: left 0.5s, right 0.5s, top 0.5s, bottom 0.5s;
14
14
  }
@@ -41,7 +41,7 @@
41
41
 
42
42
  #webgazerGazeDot {
43
43
  position: fixed;
44
- z-index: 999999;
44
+ z-index: 9999999999;
45
45
  opacity: 0.5;
46
46
  background-color: #111d5e;
47
47
  border-radius: 5px;
@@ -50,14 +50,18 @@
50
50
  }
51
51
 
52
52
  #webgazerVideoContainer {
53
- z-index: 9997;
53
+ z-index: 999999997;
54
54
  display: block;
55
55
  position: fixed !important;
56
56
  transform-origin: bottom left;
57
- /* border-radius: 7px; */
57
+ border-radius: 5px;
58
58
  /* opacity: 0.8; */
59
59
  overflow: hidden;
60
- pointer-events: none;
60
+ /* pointer-events: none; */
61
+ user-select: none;
62
+ }
63
+
64
+ #webgazerVideoContainer * {
61
65
  user-select: none;
62
66
  }
63
67
 
package/src/css/main.css CHANGED
@@ -1,6 +1,6 @@
1
1
  /* background div */
2
2
  #calibration-background {
3
- z-index: 999;
3
+ z-index: 999999990;
4
4
  position: fixed;
5
5
  width: 100%;
6
6
  height: 100%;
@@ -15,6 +15,12 @@
15
15
  user-select: none;
16
16
  box-sizing: border-box;
17
17
  text-align: center;
18
+ scrollbar-width: none;
19
+ }
20
+
21
+ #calibration-background::-webkit-scrollbar {
22
+ width: 0;
23
+ display: none;
18
24
  }
19
25
 
20
26
  #calibration-background * {
@@ -28,7 +34,6 @@
28
34
 
29
35
  .calibration-instruction {
30
36
  position: absolute;
31
- text-align: left;
32
37
  user-select: none;
33
38
  }
34
39
 
@@ -121,13 +126,13 @@
121
126
  border-radius: 10px;
122
127
  font-size: 1.2rem;
123
128
  font-weight: 500;
124
- z-index: 9999;
129
+ z-index: 999999991;
125
130
  }
126
131
 
127
132
  /* -------------------------------------------------------------------------- */
128
133
 
129
134
  .swal2-container {
130
- z-index: 999997 !important;
135
+ z-index: 999999999 !important;
131
136
  }
132
137
 
133
138
  /* -------------------------------------------------------------------------- */
@@ -136,12 +141,11 @@
136
141
  position: fixed !important;
137
142
  width: 100% !important;
138
143
  bottom: 3px !important;
139
- left: 50% !important;
140
- transform: translate(-50%, 0) !important;
141
144
  color: #999 !important;
142
145
  margin: 0 !important;
143
146
  padding: 0 !important;
144
147
  line-height: 100% !important;
148
+ text-align: center !important;
145
149
  }
146
150
 
147
151
  /* -------------------------------------------------------------------------- */
@@ -149,3 +153,15 @@
149
153
  .lock-view {
150
154
  overflow: hidden !important;
151
155
  }
156
+
157
+ /* -------------------------------------------------------------------------- */
158
+
159
+ .rc-lang-ltr {
160
+ direction: ltr !important;
161
+ text-align: left !important;
162
+ }
163
+
164
+ .rc-lang-rtl {
165
+ direction: rtl !important;
166
+ text-align: right !important;
167
+ }
@@ -17,7 +17,6 @@
17
17
  border-radius: 10px !important;
18
18
  box-shadow: var(--rc-panel-darken-color-semi) 0px 50px 100px -20px,
19
19
  var(--rc-panel-theme-color-semi) 0px 30px 60px -30px !important;
20
- text-align: left !important;
21
20
 
22
21
  * {
23
22
  outline: none;
@@ -30,6 +29,7 @@
30
29
  user-select: none;
31
30
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
32
31
  Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
32
+ scrollbar-width: none;
33
33
  }
34
34
 
35
35
  .rc-panel-title {
@@ -46,8 +46,27 @@
46
46
  font-weight: 500 !important;
47
47
  }
48
48
 
49
+ #rc-panel-language-parent {
50
+ display: flex;
51
+ flex-direction: row-reverse;
52
+ margin: 0;
53
+ padding: 0;
54
+
55
+ #rc-panel-lang-picker {
56
+ display: block;
57
+ background-color: #ffffffcc !important;
58
+ border: none !important;
59
+ outline: none !important;
60
+ margin: 0.25rem 0.25rem 0 0.25rem !important;
61
+ padding: 0.25rem !important;
62
+ font-size: 1rem !important;
63
+ font-weight: 500 !important;
64
+ border-radius: 5px !important;
65
+ }
66
+ }
67
+
49
68
  .rc-panel-steps {
50
- margin: 1.5rem 0 0 0 !important;
69
+ margin: 1rem 0 0 0 !important;
51
70
 
52
71
  &.rc-panel-steps-l,
53
72
  &.rc-panel-steps-s {
@@ -63,6 +82,10 @@
63
82
  // overflow: hidden !important;
64
83
  background: #ffffffcc;
65
84
 
85
+ // &:focus {
86
+ // outline: 3px solid black !important;
87
+ // }
88
+
66
89
  .rc-panel-step-use {
67
90
  position: absolute;
68
91
  right: 0;
@@ -91,6 +114,8 @@
91
114
 
92
115
  &.rc-panel-steps-l {
93
116
  flex-flow: row nowrap;
117
+ max-width: 100%;
118
+ overflow-x: scroll;
94
119
 
95
120
  .rc-panel-step-name {
96
121
  margin: 1.5rem 0.5rem !important;
@@ -101,7 +126,7 @@
101
126
  flex-flow: column nowrap;
102
127
 
103
128
  .rc-panel-step {
104
- text-align: left;
129
+ overflow-x: hidden;
105
130
  }
106
131
 
107
132
  .rc-panel-step-name {
@@ -191,4 +216,9 @@
191
216
  cursor: pointer;
192
217
  }
193
218
  }
219
+
220
+ ::-webkit-scrollbar {
221
+ width: 0;
222
+ display: none;
223
+ }
194
224
  }
@@ -10,10 +10,11 @@
10
10
  /* top: max(45%, 200px); */
11
11
  left: 2rem;
12
12
  border-radius: 5px;
13
- z-index: 1;
13
+ z-index: 10;
14
14
  cursor: grab;
15
15
  -webkit-transition: opacity 0.3s;
16
16
  transition: opacity 0.3s;
17
+ direction: ltr !important;
17
18
  }
18
19
 
19
20
  .rc-slider:hover {
@@ -59,18 +60,18 @@
59
60
  /* display: none; */
60
61
  width: 70px;
61
62
  height: auto;
62
- z-index: 3;
63
+ z-index: 1;
63
64
  }
64
65
 
65
66
  #size-arrow-fill {
66
- transition: fill 0.1s;
67
+ transition: fill 0.3s;
67
68
  }
68
69
 
69
70
  .minor {
70
- transition: opacity 0.1s;
71
+ transition: opacity 0.25s;
71
72
  }
72
73
 
73
- .rc-slider:hover ~ .size-obj .minor,
74
+ /* .rc-slider:hover ~ .size-obj .minor, */
74
75
  .rc-slider:active ~ .size-obj .minor {
75
76
  opacity: 0;
76
77
  }
package/src/css/swal.css CHANGED
@@ -20,12 +20,12 @@
20
20
  }
21
21
 
22
22
  .my__swal2__html {
23
+ margin: 1rem 1.6rem;
23
24
  color: #444 !important;
24
25
  font-size: 1.2rem !important;
25
26
  line-height: 150% !important;
26
27
  font-weight: normal !important;
27
28
  user-select: none !important;
28
- text-align: left !important;
29
29
  }
30
30
 
31
31
  .animate__animated.animate__fadeInUp,
@@ -0,0 +1,19 @@
1
+ #webgazerVideoContainer {
2
+ .webgazer-videoinput-select {
3
+ z-index: 9;
4
+ position: absolute;
5
+ top: 0;
6
+ left: 0;
7
+ margin: 0.3rem;
8
+ padding: 0.2rem 0.3rem;
9
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
10
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
11
+ font-size: 0.6rem;
12
+ line-height: 100%;
13
+ border: none !important;
14
+ outline: none !important;
15
+ border-radius: 0.6rem !important;
16
+ background: #ffffffcc !important;
17
+ color: #666;
18
+ }
19
+ }
@@ -5,11 +5,13 @@ import {
5
5
  toFixedNumber,
6
6
  median,
7
7
  blurAll,
8
+ safeExecuteFunc,
9
+ average,
8
10
  } from '../components/utils'
9
11
  import {
10
12
  _getCrossX,
11
13
  _cross,
12
- circleDeltaX,
14
+ // circleDeltaX,
13
15
  _getCircleBounds,
14
16
  _circle,
15
17
  } from '../components/onCanvas'
@@ -23,7 +25,7 @@ const blindSpotHTML = `<canvas id="blind-spot-canvas"></canvas>`
23
25
  /* -------------------------------------------------------------------------- */
24
26
 
25
27
  export function blindSpotTest(RC, options, toTrackDistance = false, callback) {
26
- let ppi = 108 // Dangerous! Arbitrary value
28
+ let ppi = RC._CONST.N.PPI_DONT_USE // Dangerous! Arbitrary value
27
29
  if (RC.screenPpi) ppi = RC.screenPpi.value
28
30
  else
29
31
  console.error(
@@ -40,7 +42,7 @@ export function blindSpotTest(RC, options, toTrackDistance = false, callback) {
40
42
  RC.background.appendChild(blindSpotDiv)
41
43
  RC._constructFloatInstructionElement(
42
44
  'blind-spot-instruction',
43
- phrases.RC_headTrackingCloseL[RC.L]
45
+ phrases.RC_distanceTrackingCloseL[RC.L]
44
46
  )
45
47
  RC._addCreditOnBackground(phrases.RC_viewingBlindSpotCredit[RC.L])
46
48
 
@@ -74,63 +76,186 @@ export function blindSpotTest(RC, options, toTrackDistance = false, callback) {
74
76
  let v = eyeSide === 'left' ? 1 : -1
75
77
 
76
78
  // ! KEY
77
- const breakFunction = () => {
79
+ const breakFunction = (toBreakTracking = true) => {
78
80
  // ! BREAK
79
81
  inTest = false
80
82
  resizeObserver.unobserve(RC.background)
81
83
  RC._removeBackground()
82
84
 
85
+ if (!RC._trackingSetupFinishedStatus.distance && toBreakTracking) {
86
+ RC._trackingSetupFinishedStatus.distance = true
87
+ if (RC.gazeTracker.checkInitialized('distance', false)) RC.endDistance()
88
+ }
89
+
83
90
  unbindKeys(bindKeysFunction)
91
+ unbindKeys(bindKeyUpsFunction, 'keyup')
84
92
  }
85
93
 
86
94
  // SPACE
87
95
  const finishFunction = () => {
96
+ customButton.disabled = false
88
97
  soundFeedback()
89
98
 
90
99
  tested += 1
91
100
  // Average
92
- dist.push(
93
- toFixedNumber(_getDist(circleX, crossX, ppi), options.decimalPlace)
94
- )
101
+ dist.push({
102
+ dist: toFixedNumber(_getDist(circleX, crossX, ppi), options.decimalPlace),
103
+ v: v,
104
+ closedEyeSide: eyeSide,
105
+ crossX: crossX,
106
+ circleX: circleX,
107
+ ppi: ppi,
108
+ timestamp: new Date(),
109
+ })
95
110
 
96
111
  // Enough tests?
97
112
  if (Math.floor(tested / options.repeatTesting) === 2) {
98
- // ! Put dist into data and callback function
99
- const data = (RC.newViewingDistanceData = {
100
- value: toFixedNumber(median(dist), options.decimalPlace),
101
- timestamp: new Date(),
102
- method: 'Blind Spot',
103
- })
104
- if (callback) callback(data)
105
-
106
- // Break
107
- if (!toTrackDistance) {
108
- breakFunction()
113
+ // Check if these data are acceptable
114
+ if (checkDataRepeatability(dist)) {
115
+ // ! Put dist into data and callback function
116
+ const data = (RC.newViewingDistanceData = {
117
+ value: toFixedNumber(
118
+ median(_getDistValues(dist)),
119
+ options.decimalPlace
120
+ ),
121
+ timestamp: new Date(),
122
+ method: RC._CONST.VIEW_METHOD.B,
123
+ raw: { ...dist },
124
+ })
125
+ safeExecuteFunc(callback, data)
126
+
127
+ // Break
128
+ if (!toTrackDistance) {
129
+ breakFunction(false)
130
+ } else {
131
+ // ! For tracking
132
+ // Stop test
133
+ inTest = false
134
+ // Clear observer and keys
135
+ resizeObserver.unobserve(RC.background)
136
+ unbindKeys(bindKeysFunction)
137
+ unbindKeys(bindKeyUpsFunction, 'keyup')
138
+ }
109
139
  } else {
110
- // ! For tracking
111
- // Stop test
112
- inTest = false
113
- // Clear observer and keys
114
- resizeObserver.unobserve(RC.background)
115
- unbindKeys(bindKeysFunction)
140
+ // ! Reset
141
+ tested = 0
142
+ customButton.disabled = true
143
+ // Get first response
144
+ const firstResponse = dist[0]
145
+ _resetCanvasLayout(
146
+ firstResponse.v,
147
+ firstResponse.closedEyeSide,
148
+ firstResponse.crossX
149
+ )
116
150
  }
117
151
  } else if (tested % options.repeatTesting === 0) {
118
152
  // Switch eye side
119
153
  if (eyeSide === 'left') {
120
154
  // Change to RIGHT
121
155
  eyeSide = 'right'
122
- eyeSideEle.innerHTML = phrases.RC_headTrackingCloseR[RC.L]
156
+ eyeSideEle.innerHTML = phrases.RC_distanceTrackingCloseR[RC.L]
123
157
  } else {
124
158
  eyeSide = 'left'
125
- eyeSideEle.innerHTML = phrases.RC_headTrackingCloseL[RC.L]
159
+ eyeSideEle.innerHTML = phrases.RC_distanceTrackingCloseL[RC.L]
160
+ }
161
+ RC._setFloatInstructionElementPos(eyeSide, 16)
162
+
163
+ _resetCanvasLayout(
164
+ // eyeSide === 'left' ? 1 : -1, // v
165
+ 1, // v
166
+ eyeSide, // eyeSide
167
+ _getCrossX(eyeSide, c.width), // crossX
168
+ false,
169
+ true
170
+ )
171
+ } else {
172
+ // Shift circle
173
+ v = -v
174
+ if (v > 0)
175
+ // Going to the right
176
+ circleX = circleBounds[0]
177
+ else if (v < 0) circleX = circleBounds[1]
178
+ }
179
+ }
180
+
181
+ const redoFunction = () => {
182
+ if (!tested) return
183
+ tested--
184
+ customButton.disabled = true
185
+
186
+ soundFeedback(3)
187
+
188
+ const lastResponse = dist.pop()
189
+ _resetCanvasLayout(
190
+ lastResponse.v,
191
+ lastResponse.closedEyeSide,
192
+ lastResponse.crossX,
193
+ true,
194
+ true
195
+ )
196
+ }
197
+
198
+ let arrowKeyDown = false
199
+ let arrowIntervalFunction = null
200
+ const arrowDownFunction = e => {
201
+ if (arrowKeyDown) return
202
+
203
+ arrowUpFunction()
204
+ arrowKeyDown = true
205
+
206
+ arrowIntervalFunction = setInterval(() => {
207
+ if (e.key === 'ArrowLeft') {
208
+ circleX -= 10
209
+ helpMoveCircleX()
210
+ } else if (e.key === 'ArrowRight') {
211
+ circleX += 10
212
+ helpMoveCircleX()
126
213
  }
214
+ }, 30)
215
+ }
216
+
217
+ const arrowUpFunction = () => {
218
+ arrowKeyDown = false
219
+ if (arrowIntervalFunction) {
220
+ clearInterval(arrowIntervalFunction)
221
+ arrowIntervalFunction = null
222
+ }
223
+ }
224
+
225
+ const helpMoveCircleX = () => {
226
+ tempX = constrain(circleX, ...circleBounds)
227
+ if (circleX !== tempX) {
228
+ circleX = tempX
229
+ for (let b of circleBounds)
230
+ if (circleX !== b) {
231
+ circleX = b
232
+ break
233
+ }
234
+ }
235
+ }
236
+
237
+ const _resetCanvasLayout = (
238
+ nextV,
239
+ nextEyeSide,
240
+ nextCrossX,
241
+ shiftFloatingElement = true,
242
+ shiftCircle = true
243
+ ) => {
244
+ v = nextV
245
+ eyeSide = nextEyeSide
246
+ crossX = nextCrossX
247
+ circleBounds = _getCircleBounds(eyeSide, crossX, c.width)
248
+
249
+ if (shiftFloatingElement) {
250
+ if (eyeSide === 'left')
251
+ eyeSideEle.innerHTML = phrases.RC_distanceTrackingCloseL[RC.L]
252
+ else eyeSideEle.innerHTML = phrases.RC_distanceTrackingCloseR[RC.L]
127
253
  RC._setFloatInstructionElementPos(eyeSide, 16)
254
+ }
128
255
 
129
- circleBounds = _getCircleBounds(eyeSide, crossX, c.width)
130
- circleX = circleBounds[eyeSide === 'left' ? 0 : 1]
131
- v = eyeSide === 'left' ? 1 : -1
132
- crossX = _getCrossX(eyeSide, c.width)
133
- circleBounds = _getCircleBounds(eyeSide, crossX, c.width)
256
+ if (shiftCircle) {
257
+ if (v > 0) circleX = circleBounds[0]
258
+ else circleX = circleBounds[1]
134
259
  }
135
260
  }
136
261
 
@@ -138,17 +263,33 @@ export function blindSpotTest(RC, options, toTrackDistance = false, callback) {
138
263
  const bindKeysFunction = bindKeys({
139
264
  Escape: breakFunction,
140
265
  ' ': finishFunction,
266
+ ArrowLeft: arrowDownFunction,
267
+ ArrowRight: arrowDownFunction,
141
268
  })
142
- addButtons(
269
+ const bindKeyUpsFunction = bindKeys(
270
+ {
271
+ ArrowLeft: arrowUpFunction,
272
+ ArrowRight: arrowUpFunction,
273
+ },
274
+ 'keyup'
275
+ )
276
+ const addedButtons = addButtons(
143
277
  RC.L,
144
278
  RC.background,
145
279
  {
146
280
  go: finishFunction,
147
281
  cancel: breakFunction,
282
+ custom: {
283
+ callback: redoFunction,
284
+ content: phrases.RC_viewingDistanceRedo[RC.L],
285
+ },
148
286
  },
149
287
  RC.params.showCancelButton
150
288
  )
151
289
 
290
+ const customButton = addedButtons[3]
291
+ customButton.disabled = true
292
+
152
293
  // ! ACTUAL TEST
153
294
  let frameCount = 0
154
295
  const runTest = () => {
@@ -160,12 +301,7 @@ export function blindSpotTest(RC, options, toTrackDistance = false, callback) {
160
301
  _cross(ctx, crossX, c.height / 2)
161
302
 
162
303
  _circle(RC, ctx, circleX, c.height / 2, frameCount, options.sparkle)
163
- circleX += v * circleDeltaX
164
- tempX = constrain(circleX, ...circleBounds)
165
- if (circleX !== tempX) {
166
- circleX = tempX
167
- v = -v
168
- }
304
+ // circleX += v * circleDeltaX
169
305
 
170
306
  if (inTest) {
171
307
  frameCount++
@@ -183,7 +319,6 @@ RemoteCalibrator.prototype.measureDistance = function (options = {}, callback) {
183
319
  * options -
184
320
  *
185
321
  * fullscreen: [Boolean]
186
- * quitFullscreenOnFinished: [Boolean] // TODO
187
322
  * repeatTesting: 2
188
323
  * sparkle: true
189
324
  * decimalPlace: 1
@@ -200,9 +335,8 @@ RemoteCalibrator.prototype.measureDistance = function (options = {}, callback) {
200
335
  options = Object.assign(
201
336
  {
202
337
  fullscreen: false,
203
- quitFullscreenOnFinished: false,
204
338
  repeatTesting: 2,
205
- sparkle: true,
339
+ sparkle: false,
206
340
  decimalPlace: 1,
207
341
  headline: '📏 ' + phrases.RC_viewingDistanceTitle[this.L],
208
342
  description: phrases.RC_viewingDistanceIntro[this.L],
@@ -230,3 +364,22 @@ function _getDist(x, crossX, ppi) {
230
364
  function _getTanDeg(deg) {
231
365
  return Math.tan((deg * Math.PI) / 180)
232
366
  }
367
+
368
+ function checkDataRepeatability(dist) {
369
+ let lefts = []
370
+ let rights = []
371
+ for (let d of dist) {
372
+ if (d.closedEyeSide === 'left') lefts.push(d.dist)
373
+ else rights.push(d.dist)
374
+ }
375
+ const leftMean = average(lefts)
376
+ const rightMean = average(rights)
377
+
378
+ return Math.abs(leftMean - rightMean) < 0.2 * Math.min(leftMean, rightMean)
379
+ }
380
+
381
+ function _getDistValues(dist) {
382
+ const v = []
383
+ for (let d of dist) v.push(d.dist)
384
+ return v
385
+ }