Skip to content

Commit

Permalink
Enhancement/17 write unit tests (#28)
Browse files Browse the repository at this point in the history
- Refactored to improve testability
- Wrote unit tests for getNormalisedCentrePointOf, getPupilDisplacement, and getTrackingTarget
- Fixed fault in getPupilDisplacement
- Changed FOV bound tuning variables to sensitivity, as this is more intuitive.
  • Loading branch information
Adam McDevitt authored Jun 13, 2019
1 parent 74ef100 commit 6873624
Show file tree
Hide file tree
Showing 12 changed files with 284 additions and 95 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
coverage
26 changes: 26 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"name": "vscode-jest-tests",
"request": "launch",
"args": [
"--runInBand"
],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
},
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}\\tests\\framefeed.jest.js"
}
]
}
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,12 @@ You will be asked for the permission to access the webcam - click 'Allow'.

When mouse movement is detected on the screen, the configuration menu will open where certain settings can be adjusted. Following options are currently available:

| Option | Description |
|--------------|------------------------------------------------------------------------------|
| X FOV Bound | X axis eyes sensitivity |
| Y FOV Bound | Y axis eyes sensitivity |
| Swap Eyes | Available when two webcams are detected. Swaps the webcam input for the eyes |
| Toggle Debug | Displays the camera feed with bounding boxes |
| Option | Description |
|----------------|------------------------------------------------------------------------------|
| X Sensitivity | X axis eyes sensitivity |
| Y Sensitivity | Y axis eyes sensitivity |
| Swap Eyes | Available when two webcams are detected. Swaps the webcam input for the eyes |
| Toggle Debug | Displays the camera feed with bounding boxes |

## Testing

Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
"scripts": {
"test": "jest"
},
"devDependencies": {
"jest": "^24.8.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ScottLogic/lookingatyou.git"
Expand Down
11 changes: 6 additions & 5 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,19 @@
onmouseover="setMouseIsInOptionsMenu(true)"
onmouseout="setMouseIsInOptionsMenu(false)"
>
<label>X FOV Bound</label>
<label>X Sensitivity Factor</label>
<input
type="text"
id="optionsMenu_xFovBound" value="1"
id="optionsMenu_xSensitivity"
value=""
>
<br>

<label>Y FOV Bound</label>
<label>Y Sensitivity Factor</label>
<input
type="text"
id="optionsMenu_yFovBound"
value="1"
id="optionsMenu_ySensitivity"
value=""
>
<br>

Expand Down
126 changes: 63 additions & 63 deletions src/static/css/styles.css
Original file line number Diff line number Diff line change
@@ -1,64 +1,64 @@
:root {
--main-background-colour: #222;
--menu-background-colour: #111;
--menu-text-colour: #818181;
--menu-top-padding: 60px;
--menu-z-index: 10;
--canvas-z-index: 20;
--canvas-width: 100%;
}

body {
/* Position */
height: 100%;
margin: 0;
padding: 0;

/* View */
overflow: hidden;
background: var(--main-background-colour);

/* Font */
font-family: sans-serif;
font-style: italic;
font-size: calc(0.6em + 1vw);
}

.sidenav {
/* Position */
top: 0;
left: 0;
height: 100%;
width: 0;
position: fixed;

/* View */
padding-top: var(--menu-top-padding);
background-color: var(--menu-background-colour);
overflow-x: hidden;
z-index: var(--menu-z-index);
display: inline-block;

/* Font */
text-decoration: none;
color: var(--menu-text-colour);
}

.sidenav a,
.sidenav label,
.sidenav input {
padding: 0.4em;
font-size: 1.2em;
width: 8em;
display: inline-block;
}

.sidenav input {
width: 3em;
}

canvas {
width: var(--canvas-width);
position: relative;
z-index: var(--canvas-z-index);
}
--main-background-colour: #222;
--menu-background-colour: #111;
--menu-text-colour: #818181;
--menu-top-padding: 60px;
--menu-z-index: 10;
--canvas-z-index: 20;
--canvas-width: 100%;
}
body {
/* Position */
height: 100%;
margin: 0;
padding: 0;
/* View */
overflow: hidden;
background: var(--main-background-colour);
/* Font */
font-family: sans-serif;
font-style: italic;
font-size: calc(0.6em + 1vw);
}
.sidenav {
/* Position */
top: 0;
left: 0;
height: 100%;
width: 0;
position: fixed;
/* View */
padding-top: var(--menu-top-padding);
background-color: var(--menu-background-colour);
overflow-x: hidden;
z-index: var(--menu-z-index);
display: inline-block;
/* Font */
text-decoration: none;
color: var(--menu-text-colour);
}
.sidenav a,
.sidenav label,
.sidenav input {
padding: 0.4em;
font-size: 1.2em;
width: 8em;
display: inline-block;
}
.sidenav input {
width: 3em;
}
canvas {
width: var(--canvas-width);
position: relative;
z-index: var(--canvas-z-index);
}
10 changes: 6 additions & 4 deletions src/static/js/frame_feed.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,14 @@ function drawBoundingBox(ctx, boundingBox, ratio) {
// Calculates the corresponding eye position for boundingBox from webcam index
function getNormalisedCentrePointOf(boundingBox, video) {
var x = boundingBox[0] + boundingBox[2] / 2; // Coordinates for centre of bounding box
x = x - video.width / 2; // Converts to coordinates centred around 0,0
x = x - (video.width / 2); // Converts to coordinates centred around 0,0
x = x / (video.width / 2); // Converts coordinate to a coefficient between -1 and 1

var y = boundingBox[1] + boundingBox[3] / 2;
y = y - video.height / 2;
y = y / video.height / 2;
y = y - (video.height / 2);
y = y / (video.height / 2);

return [x, y];
}
}

module.exports = {getNormalisedCentrePointOf, getTrackingTarget};
36 changes: 22 additions & 14 deletions src/static/js/gui.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ function makeEyes() {
"distanceFromCentre": screen.width / 4
};
maxPupilDisplacement = (sizes.sclera - sizes.iris);

const colors = {
"sclera": "white",
"iris": "red",
Expand Down Expand Up @@ -70,23 +69,30 @@ function makeEyes() {
}

function setEyesPosition(coords, eye) {
// Scale eye movemen by sensitivity
var xFovBound = parseFloat(document.getElementById("optionsMenu_xFovBound").value) || 1; // defaults to 1 if NaN
var yFovBound = parseFloat(document.getElementById("optionsMenu_yFovBound").value) || 1;
var x = coords[0] / xFovBound;
var y = coords[1] / yFovBound;
var polarDistance= Math.hypot(x, y); // Polar coordinate distance
var theta = Math.atan2(y, x); // Polar coordinate angle
var pupilDisplacementDistance = maxPupilDisplacement * Math.min(1, polarDistance)
var pupilXDisplacement = -pupilDisplacementDistance * Math.cos(theta);
var pupilYDisplacement = pupilDisplacementDistance * Math.sin(theta);
// Scale eye movement by sensitivity
var xSensitivity = parseFloat(document.getElementById("optionsMenu_xSensitivity").value) || 1; // defaults to 1 if NaN
var ySensitivity = parseFloat(document.getElementById("optionsMenu_ySensitivity").value) || 1;

var coords = getPupilDisplacement(coords, maxPupilDisplacement, xSensitivity, ySensitivity);
var transformString = "translate(" + coords[0] + "," + coords[1] + ")";

// Allows user swap camera inputs (left/right)
var doSwapEyes = document.getElementById('optionsMenu_doSwapEyes').checked;
if (xor(doSwapEyes, eye) === eyes.LEFT)
d3.select(".leftInner").transition().duration(1000 / FPS).attr("transform", "translate(" + pupilXDisplacement + "," + pupilYDisplacement + ")");
d3.select(".leftInner").transition().duration(1000 / FPS).attr("transform", transformString);
else if (xor(doSwapEyes, eye) === eyes.RIGHT)
d3.select(".rightInner").transition().duration(1000 / FPS).attr("transform", "translate(" + pupilXDisplacement + "," + pupilYDisplacement + ")");
d3.select(".rightInner").transition().duration(1000 / FPS).attr("transform", transformString);
}

function getPupilDisplacement(coords, maxDisplacement, xSensitivity, ySensitivity) {
var x = coords[0] * xSensitivity;
var y = coords[1] * ySensitivity;
var polarDistance = Math.hypot(x, y); // Polar coordinate distance
var theta = Math.atan2(y, x); // Polar coordinate angle
var pupilDisplacementDistance = maxDisplacement * Math.min(1, polarDistance)
var pupilXDisplacement = -pupilDisplacementDistance * Math.cos(theta);
var pupilYDisplacement = pupilDisplacementDistance * Math.sin(theta);
return [pupilXDisplacement, pupilYDisplacement];
}

function setMouseIsInOptionsMenu(value) {
Expand Down Expand Up @@ -117,4 +123,6 @@ function swapEyeDebugLabels() {

function xor(a, b) {
return (a && !b) || (!a && b)
}
}

module.exports = {getPupilDisplacement};
25 changes: 25 additions & 0 deletions tests/getNormalisedCentrePointOf.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const getNormalisedCentrePointOf = require('../src/static/js/frame_feed').getNormalisedCentrePointOf;
var video_1920_1080 = {width: 1920, height: 1080};
describe('getNormalisedCentrePointof', () => {
it('bounding box is point in bottom left corner, so is centre point', () => {
var coords = [0, 0, 0, 0];
point = getNormalisedCentrePointOf(coords, video_1920_1080);
expect(point).toStrictEqual([-1,-1]);
});
it('bounding box spans screen, so point is in centre of screen', () => {
var coords = [0, 0, 1920, 1080];
point = getNormalisedCentrePointOf(coords, video_1920_1080);
expect(point).toStrictEqual([0,0]);
});
it('bounding box is point in exact middle of screen, so centre point is in centre of screen', () => {
var coords = [960, 540, 0, 0];
point = getNormalisedCentrePointOf(coords, video_1920_1080);
expect(point).toStrictEqual([0,0]);
});
it('centre point does not have integer coordinates', () => {
var coords = [500, 500, 1000, 20];
point = getNormalisedCentrePointOf(coords, video_1920_1080);
expect(point[0]).toBeCloseTo(0.04167);
expect(point[1]).toBeCloseTo(-0.05555555);
});
});
Loading

0 comments on commit 6873624

Please sign in to comment.