remote-calibrator 0.3.0 → 0.5.0-beta.3
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +12 -0
- package/README.md +29 -19
- package/homepage/example.js +9 -3
- package/i18n/fetch-languages-sheets.js +5 -4
- package/lib/RemoteCalibrator.min.js +1 -1
- package/lib/RemoteCalibrator.min.js.LICENSE.txt +1 -1
- package/lib/RemoteCalibrator.min.js.map +1 -1
- package/package.json +15 -15
- package/src/WebGazer4RC/.gitattributes +10 -0
- package/src/WebGazer4RC/LICENSE.md +15 -0
- package/src/WebGazer4RC/README.md +142 -0
- package/src/WebGazer4RC/gnu-lgpl-v3.0.md +163 -0
- package/src/WebGazer4RC/gplv3.md +636 -0
- package/src/WebGazer4RC/package-lock.json +1133 -0
- package/src/WebGazer4RC/package.json +28 -0
- package/src/WebGazer4RC/src/dom_util.mjs +27 -0
- package/src/WebGazer4RC/src/facemesh.mjs +150 -0
- package/src/WebGazer4RC/src/index.mjs +1235 -0
- package/src/WebGazer4RC/src/mat.mjs +301 -0
- package/src/WebGazer4RC/src/params.mjs +29 -0
- package/src/WebGazer4RC/src/pupil.mjs +109 -0
- package/src/WebGazer4RC/src/ridgeReg.mjs +104 -0
- package/src/WebGazer4RC/src/ridgeRegThreaded.mjs +161 -0
- package/src/WebGazer4RC/src/ridgeWeightedReg.mjs +125 -0
- package/src/WebGazer4RC/src/ridgeWorker.mjs +135 -0
- package/src/WebGazer4RC/src/util.mjs +348 -0
- package/src/WebGazer4RC/src/util_regression.mjs +240 -0
- package/src/WebGazer4RC/src/worker_scripts/mat.js +306 -0
- package/src/WebGazer4RC/src/worker_scripts/util.js +398 -0
- package/src/WebGazer4RC/test/regression_test.js +182 -0
- package/src/WebGazer4RC/test/run_tests_and_server.sh +24 -0
- package/src/WebGazer4RC/test/util_test.js +60 -0
- package/src/WebGazer4RC/test/webgazerExtract_test.js +40 -0
- package/src/WebGazer4RC/test/webgazer_test.js +160 -0
- package/src/WebGazer4RC/test/www_page_test.js +41 -0
- package/src/const.js +3 -0
- package/src/core.js +8 -0
- package/src/css/distance.scss +40 -0
- package/src/css/panel.scss +32 -1
- package/src/distance/distance.js +4 -4
- package/src/distance/distanceCheck.js +115 -0
- package/src/distance/distanceTrack.js +99 -41
- package/src/{interpupillaryDistance.js → distance/interPupillaryDistance.js} +14 -12
- package/src/gaze/gazeTracker.js +16 -1
- package/src/i18n.js +1 -1
- package/src/index.js +2 -1
- package/src/panel.js +32 -3
- package/webpack.config.js +4 -4
@@ -0,0 +1,40 @@
|
|
1
|
+
const puppeteer = require('puppeteer');
|
2
|
+
const { assert } = require('chai');
|
3
|
+
const TFFaceMesh = require('@tensorflow-models/facemesh');
|
4
|
+
|
5
|
+
describe('webgazerExtract functions', async () => {
|
6
|
+
let browser,page;
|
7
|
+
before(async () => {
|
8
|
+
browser = await puppeteer.launch()
|
9
|
+
page = await browser.newPage();
|
10
|
+
await page.goto('http://localhost:8000/webgazerExtractClient.html');
|
11
|
+
})
|
12
|
+
after(async () => {
|
13
|
+
await browser.close();
|
14
|
+
})
|
15
|
+
it('should load elements', async() =>{
|
16
|
+
const elements = await page.evaluate(() => {
|
17
|
+
return {myMouse:document.getElementById('myMouse'),
|
18
|
+
tobiiGP:document.getElementById('tobiiGP'),
|
19
|
+
wsCanvas: document.getElementById('wsCanvas'),
|
20
|
+
screencap: document.getElementById('screencap'),
|
21
|
+
showScreenCap: document.getElementById('showScreenCap'),
|
22
|
+
scTimeOffsetDiv: document.getElementById('scTimeOffsetDiv'),
|
23
|
+
diagDiv: document.getElementById('diagDiv')}
|
24
|
+
})
|
25
|
+
for(const [k,v] of Object.entries(elements)){
|
26
|
+
assert.equal((Object.keys(v).length === 0
|
27
|
+
&& v.constructor === Object), true)
|
28
|
+
}
|
29
|
+
});
|
30
|
+
|
31
|
+
it('webgazer properties should be set correctly', async() =>{
|
32
|
+
await page.waitForSelector('#overlay');
|
33
|
+
let model = await page.evaluate(async() => {
|
34
|
+
let tracker = webgazer.getTracker();
|
35
|
+
return tracker.name
|
36
|
+
})
|
37
|
+
assert.equal(model,'TFFaceMesh')
|
38
|
+
})
|
39
|
+
});
|
40
|
+
|
@@ -0,0 +1,160 @@
|
|
1
|
+
const puppeteer = require('puppeteer');
|
2
|
+
const { assert } = require('chai');
|
3
|
+
const TFFaceMesh = require('@tensorflow-models/facemesh');
|
4
|
+
|
5
|
+
before(async () => {
|
6
|
+
const parent_dir = __dirname.substring(0,__dirname.length-4)
|
7
|
+
let my_y4m_video = parent_dir + 'www/data/src/P_01/dot.y4m'
|
8
|
+
browser = await puppeteer.launch({args:['--use-file-for-fake-video-capture='+my_y4m_video,
|
9
|
+
'--allow-file-access', '--use-fake-device-for-media-stream','--use-fake-ui-for-media-stream',
|
10
|
+
'--no-sandbox','--disable-setuid-sandbox',
|
11
|
+
]
|
12
|
+
//,devtools:true //enable for debugging
|
13
|
+
});
|
14
|
+
page = await browser.newPage();
|
15
|
+
await page.goto('http://localhost:3000/calibration.html?');
|
16
|
+
page.coverage.startJSCoverage();
|
17
|
+
await page.goto('http://localhost:3000/calibration.html?');
|
18
|
+
await page.waitFor(1500)
|
19
|
+
await page.waitForSelector('#start_calibration')
|
20
|
+
//calibration button is not immediately clickable due to css transition
|
21
|
+
await page.waitFor(2500)
|
22
|
+
|
23
|
+
await page.evaluate(async() => {
|
24
|
+
document.querySelector("#start_calibration").click()
|
25
|
+
})
|
26
|
+
await page.waitFor(1500)
|
27
|
+
await page.evaluate(async() =>{
|
28
|
+
document.querySelector("body > div.swal-overlay.swal-overlay--show-modal > div > div.swal-footer > div > button").click()
|
29
|
+
})
|
30
|
+
})
|
31
|
+
describe('webgazer function', async() => {
|
32
|
+
after(async () => {
|
33
|
+
const jsCoverage = await page.coverage.stopJSCoverage();
|
34
|
+
let usedBytes = 0;
|
35
|
+
let webgazer_coverage;
|
36
|
+
jsCoverage.forEach(item => {if (item.url == "http://localhost:3000/webgazer.js")
|
37
|
+
{webgazer_coverage = item}
|
38
|
+
})
|
39
|
+
webgazer_coverage.ranges.forEach(range => (usedBytes += range.end - range.start - 1));
|
40
|
+
console.log((100*usedBytes/webgazer_coverage.text.length).toFixed(4), "% Code Coverage on webgazer.js")
|
41
|
+
await browser.close();
|
42
|
+
})
|
43
|
+
describe('top level functions', async() =>{
|
44
|
+
it('should be able to recognize video input', async() =>{
|
45
|
+
const videoAvailable = await page.evaluate(async() => {
|
46
|
+
return await webgazer.params.showFaceFeedbackBox;
|
47
|
+
});
|
48
|
+
const isReady = await page.evaluate(async() => {
|
49
|
+
return await webgazer.isReady()
|
50
|
+
});
|
51
|
+
assert.equal(videoAvailable,true);
|
52
|
+
assert.equal(isReady,true);
|
53
|
+
});
|
54
|
+
// modifying visibility params
|
55
|
+
it('webgazerVideoFeed should display', async() => {
|
56
|
+
let video_display = await page.evaluate(async() => {
|
57
|
+
return document.getElementById('webgazerVideoFeed').style.display
|
58
|
+
})
|
59
|
+
assert.notEqual(video_display,"none");
|
60
|
+
})
|
61
|
+
it('webgazerFaceFeedbackBox should display', async() => {
|
62
|
+
await page.waitForSelector('#webgazerFaceFeedbackBox')
|
63
|
+
let face_overlay = await page.evaluate(async() => {
|
64
|
+
return document.getElementById('webgazerFaceFeedbackBox').style.display
|
65
|
+
})
|
66
|
+
assert.notEqual(face_overlay,"none");
|
67
|
+
})
|
68
|
+
it('webgazerGazeDot should display', async() => {
|
69
|
+
let webgazer_gazedot = await page.evaluate(async() => {
|
70
|
+
return document.getElementById('webgazerGazeDot').style.display
|
71
|
+
})
|
72
|
+
assert.notEqual(webgazer_gazedot,"none");
|
73
|
+
})
|
74
|
+
it('faceoverlay should hide when showFaceOverlay is false', async() => {
|
75
|
+
face_overlay = await page.evaluate(async() => {
|
76
|
+
await webgazer.showFaceFeedbackBox(false)
|
77
|
+
return document.getElementById('webgazerFaceFeedbackBox').style.display
|
78
|
+
})
|
79
|
+
assert.equal(face_overlay,"none");
|
80
|
+
})
|
81
|
+
it('webgazerGazeDot should hide when showPredictionPoints is false', async() =>{
|
82
|
+
let webgazer_gazedot = await page.evaluate(async() => {
|
83
|
+
await webgazer.showPredictionPoints(false)
|
84
|
+
return document.getElementById('webgazerGazeDot').style.display
|
85
|
+
})
|
86
|
+
assert.equal(webgazer_gazedot,"none");
|
87
|
+
})
|
88
|
+
it('webgazerVideoFeed should hide when showVideo is false', async() => {
|
89
|
+
video_display = await page.evaluate(async() => {
|
90
|
+
await webgazer.showVideo(false)
|
91
|
+
return document.getElementById('webgazerVideoFeed').style.display
|
92
|
+
});
|
93
|
+
assert.equal(video_display,"none");
|
94
|
+
})
|
95
|
+
it('getVideoElementCanvas should exist and be a canvas element', async() => {
|
96
|
+
let video_element_canvas_type = await page.evaluate(async() => {
|
97
|
+
return await webgazer.getVideoElementCanvas().nodeName
|
98
|
+
})
|
99
|
+
assert.equal(video_element_canvas_type,'CANVAS')
|
100
|
+
})
|
101
|
+
it('preview to camera resolution ratio should be [0.5,0.5]', async() =>{
|
102
|
+
let preview_to_camera_resolution_ratio = await page.evaluate(async() => {
|
103
|
+
return await webgazer.getVideoPreviewToCameraResolutionRatio()
|
104
|
+
})
|
105
|
+
assert.equal(preview_to_camera_resolution_ratio[0],0.5)
|
106
|
+
assert.equal(preview_to_camera_resolution_ratio[1],0.5)
|
107
|
+
})
|
108
|
+
it('should be able to change video viewer size', async()=>{
|
109
|
+
const video_dimensions = await page.evaluate(async()=>{
|
110
|
+
return [webgazer.params.videoViewerWidth,webgazer.params.videoViewerHeight]
|
111
|
+
})
|
112
|
+
const new_dimensions = [video_dimensions[0],video_dimensions[1]]
|
113
|
+
const new_video_dimensions = await page.evaluate(async(new_dimensions)=>{
|
114
|
+
await webgazer.setVideoViewerSize(new_dimensions[0],new_dimensions[1])
|
115
|
+
return [webgazer.params.videoViewerWidth,webgazer.params.videoViewerHeight]
|
116
|
+
},new_dimensions)
|
117
|
+
assert.equal(new_video_dimensions[0],new_dimensions[0])
|
118
|
+
assert.equal(new_video_dimensions[1],new_dimensions[1])
|
119
|
+
})
|
120
|
+
it('top level, non-video no arguments webgazer functions should work', async() =>{
|
121
|
+
let basic_functions = await page.evaluate(async() => {
|
122
|
+
return {getCurrentPrediction: JSON.stringify(await webgazer.getCurrentPrediction()),
|
123
|
+
addMouseEventListeners: JSON.stringify(await webgazer.addMouseEventListeners()),
|
124
|
+
getStoredPoints:JSON.stringify(await webgazer.getStoredPoints()),
|
125
|
+
removeMouseEventListeners:JSON.stringify(await webgazer.removeMouseEventListeners()),
|
126
|
+
isReady:JSON.stringify(await webgazer.isReady()),
|
127
|
+
detectCompatibility:JSON.stringify(await webgazer.detectCompatibility()),
|
128
|
+
clearGazeListener:JSON.stringify(await webgazer.clearGazeListener()),
|
129
|
+
getRegression:JSON.stringify(await webgazer.getRegression()),
|
130
|
+
getStoredPoints:JSON.stringify(await webgazer.getStoredPoints()),
|
131
|
+
pause:JSON.stringify(await webgazer.pause())
|
132
|
+
}
|
133
|
+
})
|
134
|
+
|
135
|
+
|
136
|
+
for(const [k,v] of Object.entries(basic_functions)){
|
137
|
+
assert.notEqual(Object.keys(v),null)
|
138
|
+
assert.notEqual(Object.keys(v),{})
|
139
|
+
}
|
140
|
+
|
141
|
+
assert.equal(basic_functions.isReady,"true")
|
142
|
+
assert.equal(basic_functions.detectCompatibility,"true")
|
143
|
+
})
|
144
|
+
it('can record screen position, set tracker and regression and set static video', async() =>{
|
145
|
+
const screen_functions = page.evaluate(async() => {
|
146
|
+
return {setStaticVideo: await webgazer.setStaticVideo('../www/data/src/P_02/1491487691210_2_-study-dot_test_instructions.webm'),
|
147
|
+
setTracker: await webgazer.setTracker('TFFacemesh'),
|
148
|
+
setRegression: await webgazer.setRegression('ridge')}
|
149
|
+
})
|
150
|
+
for(const [k,v] of Object.entries(screen_functions)){
|
151
|
+
assert.notEqual(Object.keys(v),null)
|
152
|
+
assert.notEqual(Object.keys(v),{})
|
153
|
+
}
|
154
|
+
})
|
155
|
+
//checkEyesInValidationBox exists in code but the comment above says it's wrong and it returns nothing
|
156
|
+
})
|
157
|
+
require('./regression_test')
|
158
|
+
require('./util_test')
|
159
|
+
|
160
|
+
})
|
@@ -0,0 +1,41 @@
|
|
1
|
+
const puppeteer = require('puppeteer');
|
2
|
+
const { assert } = require('chai');
|
3
|
+
|
4
|
+
describe('Main Page Basics', async () => {
|
5
|
+
let browser,page,response;
|
6
|
+
before(async () => {
|
7
|
+
browser = await puppeteer.launch();
|
8
|
+
page = await browser.newPage();
|
9
|
+
response = await page.goto('http://localhost:3000');
|
10
|
+
})
|
11
|
+
|
12
|
+
after(async () => {
|
13
|
+
await browser.close();
|
14
|
+
})
|
15
|
+
|
16
|
+
it('Page response should be 200', async() =>{
|
17
|
+
assert.equal(response.status(),200)
|
18
|
+
})
|
19
|
+
it('clicking the collision button should send you to new page' , async() =>{
|
20
|
+
const collision_button = "#collision_button";
|
21
|
+
const [response] = await Promise.all([
|
22
|
+
page.waitForNavigation(),
|
23
|
+
page.click(collision_button),
|
24
|
+
]);
|
25
|
+
assert.equal(page.url(),'http://localhost:3000/collision.html?')
|
26
|
+
assert.equal(response.status(),200)
|
27
|
+
});
|
28
|
+
it('clicking the calibration button should send you to new page' , async() =>{
|
29
|
+
await page.goto('http://localhost:3000');
|
30
|
+
const calibration_button = "#calibration_button";
|
31
|
+
const [response] = await Promise.all([
|
32
|
+
page.waitForNavigation(),
|
33
|
+
page.click(calibration_button),
|
34
|
+
]);
|
35
|
+
assert.equal(page.url(),'http://localhost:3000/calibration.html?')
|
36
|
+
assert.equal(response.status(),200)
|
37
|
+
});
|
38
|
+
});
|
39
|
+
|
40
|
+
|
41
|
+
|
package/src/const.js
CHANGED
@@ -12,6 +12,8 @@ RemoteCalibrator.prototype._CONST = Object.freeze({
|
|
12
12
|
MARGIN: 10,
|
13
13
|
BORDER: 8,
|
14
14
|
},
|
15
|
+
PPI_DONT_USE: 127.7,
|
16
|
+
PD_DONT_USE: 6.4,
|
15
17
|
},
|
16
18
|
S: {
|
17
19
|
AUTO: 'AUTO',
|
@@ -31,4 +33,5 @@ RemoteCalibrator.prototype._CONST = Object.freeze({
|
|
31
33
|
B: 'BlindSpot',
|
32
34
|
F: 'FaceMesh',
|
33
35
|
},
|
36
|
+
IN_TO_CM: 2.54,
|
34
37
|
})
|
package/src/core.js
CHANGED
@@ -44,10 +44,18 @@ class RemoteCalibrator {
|
|
44
44
|
panelResolve: null,
|
45
45
|
}
|
46
46
|
|
47
|
+
// Are we calibrating for setting up gaze or distance tracking?
|
47
48
|
this._trackingSetupFinishedStatus = {
|
48
49
|
gaze: true,
|
49
50
|
distance: true,
|
50
51
|
}
|
52
|
+
this._trackingStatus = {
|
53
|
+
distanceCorrecting: null, // setInterval
|
54
|
+
}
|
55
|
+
this._tackingVideoFrameTimestamps = {
|
56
|
+
gaze: 0,
|
57
|
+
distance: 0,
|
58
|
+
}
|
51
59
|
|
52
60
|
this._environmentData = []
|
53
61
|
|
package/src/css/distance.scss
CHANGED
@@ -87,3 +87,43 @@
|
|
87
87
|
height: 16px;
|
88
88
|
}
|
89
89
|
}
|
90
|
+
|
91
|
+
/* ---------------------------------- Check --------------------------------- */
|
92
|
+
|
93
|
+
#rc-distance-correct {
|
94
|
+
text-align: center;
|
95
|
+
width: 100%;
|
96
|
+
margin: 3rem auto auto auto;
|
97
|
+
padding: 2rem;
|
98
|
+
overflow-wrap: break-word;
|
99
|
+
|
100
|
+
#rc-distance-correct-instruction {
|
101
|
+
font-weight: 700;
|
102
|
+
font-size: 7rem;
|
103
|
+
margin: 2rem auto;
|
104
|
+
}
|
105
|
+
|
106
|
+
#rc-distance-correct-guide {
|
107
|
+
font-weight: 500;
|
108
|
+
font-size: 3rem;
|
109
|
+
line-height: 150%;
|
110
|
+
|
111
|
+
.rc-distance-num {
|
112
|
+
padding: 0.5rem;
|
113
|
+
border-radius: 7px !important;
|
114
|
+
font-weight: 700;
|
115
|
+
font-family: monospace !important;
|
116
|
+
vertical-align: middle;
|
117
|
+
}
|
118
|
+
|
119
|
+
.rc-distance-now {
|
120
|
+
border: 2px solid #ff9a00;
|
121
|
+
background-color: #ff9a0066;
|
122
|
+
}
|
123
|
+
|
124
|
+
.rc-distance-desired {
|
125
|
+
border: 2px solid #3490de;
|
126
|
+
background-color: #3490de66;
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
package/src/css/panel.scss
CHANGED
@@ -29,6 +29,7 @@
|
|
29
29
|
user-select: none;
|
30
30
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
31
31
|
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
32
|
+
scrollbar-width: none;
|
32
33
|
}
|
33
34
|
|
34
35
|
.rc-panel-title {
|
@@ -45,8 +46,27 @@
|
|
45
46
|
font-weight: 500 !important;
|
46
47
|
}
|
47
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
|
+
|
48
68
|
.rc-panel-steps {
|
49
|
-
margin:
|
69
|
+
margin: 1rem 0 0 0 !important;
|
50
70
|
|
51
71
|
&.rc-panel-steps-l,
|
52
72
|
&.rc-panel-steps-s {
|
@@ -90,6 +110,8 @@
|
|
90
110
|
|
91
111
|
&.rc-panel-steps-l {
|
92
112
|
flex-flow: row nowrap;
|
113
|
+
max-width: 100%;
|
114
|
+
overflow-x: scroll;
|
93
115
|
|
94
116
|
.rc-panel-step-name {
|
95
117
|
margin: 1.5rem 0.5rem !important;
|
@@ -99,6 +121,10 @@
|
|
99
121
|
&.rc-panel-steps-s {
|
100
122
|
flex-flow: column nowrap;
|
101
123
|
|
124
|
+
.rc-panel-step {
|
125
|
+
overflow-x: hidden;
|
126
|
+
}
|
127
|
+
|
102
128
|
.rc-panel-step-name {
|
103
129
|
margin: 1.2rem 1.5rem !important;
|
104
130
|
}
|
@@ -186,4 +212,9 @@
|
|
186
212
|
cursor: pointer;
|
187
213
|
}
|
188
214
|
}
|
215
|
+
|
216
|
+
::-webkit-scrollbar {
|
217
|
+
width: 0;
|
218
|
+
display: none;
|
219
|
+
}
|
189
220
|
}
|
package/src/distance/distance.js
CHANGED
@@ -24,7 +24,7 @@ const blindSpotHTML = `<canvas id="blind-spot-canvas"></canvas>`
|
|
24
24
|
/* -------------------------------------------------------------------------- */
|
25
25
|
|
26
26
|
export function blindSpotTest(RC, options, toTrackDistance = false, callback) {
|
27
|
-
let ppi =
|
27
|
+
let ppi = RC._CONST.N.PPI_DONT_USE // Dangerous! Arbitrary value
|
28
28
|
if (RC.screenPpi) ppi = RC.screenPpi.value
|
29
29
|
else
|
30
30
|
console.error(
|
@@ -41,7 +41,7 @@ export function blindSpotTest(RC, options, toTrackDistance = false, callback) {
|
|
41
41
|
RC.background.appendChild(blindSpotDiv)
|
42
42
|
RC._constructFloatInstructionElement(
|
43
43
|
'blind-spot-instruction',
|
44
|
-
phrases.
|
44
|
+
phrases.RC_distanceTrackingCloseL[RC.L]
|
45
45
|
)
|
46
46
|
RC._addCreditOnBackground(phrases.RC_viewingBlindSpotCredit[RC.L])
|
47
47
|
|
@@ -125,10 +125,10 @@ export function blindSpotTest(RC, options, toTrackDistance = false, callback) {
|
|
125
125
|
if (eyeSide === 'left') {
|
126
126
|
// Change to RIGHT
|
127
127
|
eyeSide = 'right'
|
128
|
-
eyeSideEle.innerHTML = phrases.
|
128
|
+
eyeSideEle.innerHTML = phrases.RC_distanceTrackingCloseR[RC.L]
|
129
129
|
} else {
|
130
130
|
eyeSide = 'left'
|
131
|
-
eyeSideEle.innerHTML = phrases.
|
131
|
+
eyeSideEle.innerHTML = phrases.RC_distanceTrackingCloseL[RC.L]
|
132
132
|
}
|
133
133
|
RC._setFloatInstructionElementPos(eyeSide, 16)
|
134
134
|
|
@@ -0,0 +1,115 @@
|
|
1
|
+
import RemoteCalibrator from '../core'
|
2
|
+
import { bindKeys, unbindKeys } from '../components/keyBinder'
|
3
|
+
import { phrases } from '../i18n'
|
4
|
+
import { addButtons } from '../components/buttons'
|
5
|
+
|
6
|
+
RemoteCalibrator.prototype.checkDistance = function (
|
7
|
+
desiredCm,
|
8
|
+
errorTolerance
|
9
|
+
) {
|
10
|
+
////
|
11
|
+
if (!this.checkInitialized()) return
|
12
|
+
////
|
13
|
+
|
14
|
+
if (!desiredCm) return
|
15
|
+
|
16
|
+
if (
|
17
|
+
this.viewingDistanceCm &&
|
18
|
+
this.viewingDistanceCm.method === this._CONST.VIEW_METHOD.F
|
19
|
+
) {
|
20
|
+
if (!withinRange(this.viewingDistanceCm.value, desiredCm, errorTolerance)) {
|
21
|
+
// ! Out of range
|
22
|
+
if (this._trackingStatus.distanceCorrecting === null) {
|
23
|
+
const breakFunction = () => {
|
24
|
+
this._removeBackground()
|
25
|
+
clearInterval(this._trackingStatus.distanceCorrecting)
|
26
|
+
this._trackingStatus.distanceCorrecting = null
|
27
|
+
|
28
|
+
unbindKeys(bindKeysFunction)
|
29
|
+
}
|
30
|
+
|
31
|
+
// Bind keys
|
32
|
+
const bindKeysFunction = bindKeys({
|
33
|
+
Escape: breakFunction,
|
34
|
+
})
|
35
|
+
|
36
|
+
// ! Start
|
37
|
+
const [moveElement, guideNumNow, guideNumDesired] =
|
38
|
+
startCorrecting(this)
|
39
|
+
|
40
|
+
addButtons(
|
41
|
+
this.L,
|
42
|
+
this.background,
|
43
|
+
{
|
44
|
+
cancel: breakFunction,
|
45
|
+
},
|
46
|
+
this.params.showCancelButton
|
47
|
+
)
|
48
|
+
|
49
|
+
const _update = () => {
|
50
|
+
moveElement.innerHTML = getMoveInner(
|
51
|
+
this,
|
52
|
+
this.viewingDistanceCm.value,
|
53
|
+
desiredCm
|
54
|
+
)
|
55
|
+
guideNumNow.innerHTML = Math.round(this.viewingDistanceCm.value)
|
56
|
+
guideNumDesired.innerHTML = Math.round(desiredCm)
|
57
|
+
}
|
58
|
+
_update()
|
59
|
+
|
60
|
+
this._trackingStatus.distanceCorrecting = setInterval(() => {
|
61
|
+
_update()
|
62
|
+
|
63
|
+
// Check again
|
64
|
+
if (
|
65
|
+
withinRange(this.viewingDistanceCm.value, desiredCm, errorTolerance)
|
66
|
+
) {
|
67
|
+
breakFunction()
|
68
|
+
unbindKeys(bindKeysFunction)
|
69
|
+
}
|
70
|
+
}, 250)
|
71
|
+
}
|
72
|
+
return false
|
73
|
+
} else {
|
74
|
+
// ! In range
|
75
|
+
return true
|
76
|
+
}
|
77
|
+
} else {
|
78
|
+
console.error(
|
79
|
+
'You need to start tracking viewing distance before checking it.'
|
80
|
+
)
|
81
|
+
return false
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
const withinRange = (value, target, tolerance) => {
|
86
|
+
tolerance = Math.max(Math.min(Number(tolerance), 1), 0.1)
|
87
|
+
return value <= target * (1 + tolerance) && value >= target * (1 - tolerance)
|
88
|
+
}
|
89
|
+
|
90
|
+
const startCorrecting = RC => {
|
91
|
+
RC._addBackground(`<div id="rc-distance-correct">
|
92
|
+
<p id="rc-distance-correct-instruction"></p>
|
93
|
+
<p id="rc-distance-correct-guide">${phrases.RC_distanceTrackingGuide[RC.L]
|
94
|
+
.replace(
|
95
|
+
'xx1',
|
96
|
+
`<span class="rc-distance-num rc-distance-now" id="rc-distance-now"></span>`
|
97
|
+
)
|
98
|
+
.replace(
|
99
|
+
'xx2',
|
100
|
+
`<span class="rc-distance-num rc-distance-desired" id="rc-distance-desired"></span>`
|
101
|
+
)}</p>
|
102
|
+
</div>
|
103
|
+
`)
|
104
|
+
|
105
|
+
return [
|
106
|
+
document.querySelector('#rc-distance-correct-instruction'),
|
107
|
+
document.querySelector('#rc-distance-now'),
|
108
|
+
document.querySelector('#rc-distance-desired'),
|
109
|
+
]
|
110
|
+
}
|
111
|
+
|
112
|
+
const getMoveInner = (RC, value, target) => {
|
113
|
+
if (value >= target) return phrases.RC_distanceTrackingMoveCloser[RC.L]
|
114
|
+
else return phrases.RC_distanceTrackingMoveFurther[RC.L]
|
115
|
+
}
|