Initial commit
This commit is contained in:
56
static/annotator.html
Normal file
56
static/annotator.html
Normal file
@@ -0,0 +1,56 @@
|
||||
<!doctype HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
* {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.centered {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.column {
|
||||
float: left;
|
||||
width: 50%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.row:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
#annotations {
|
||||
margin-left: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 class="centered">Annotator</h1>
|
||||
<div class="row">
|
||||
<div class="column">
|
||||
<h2 class="centered">Previous frame (<span id="img-1-index">1</span>)</h2>
|
||||
<img id="img-1">
|
||||
</div>
|
||||
<div class="column">
|
||||
<h2 class="centered">Current frame (<span id="img-2-index">2</span>)</h2>
|
||||
<img id="img-2">
|
||||
<h3>Annotations:</h3>
|
||||
<div id="annotations">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/static/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
143
static/main.js
Normal file
143
static/main.js
Normal file
@@ -0,0 +1,143 @@
|
||||
function findGetParameter(parameterName) {
|
||||
var result = null, tmp = [];
|
||||
|
||||
location.search
|
||||
.substr(1)
|
||||
.split("&")
|
||||
.forEach(function(item) {
|
||||
tmp = item.split("=");
|
||||
if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
const Events = [
|
||||
'Transition -> slide',
|
||||
'Transition -> demo',
|
||||
'Transition -> new viewpoint',
|
||||
'Build Up',
|
||||
'Animation',
|
||||
'Pointer',
|
||||
'Live annotation',
|
||||
];
|
||||
|
||||
class Annotator {
|
||||
constructor(root, annotations, img1Id, img2Id, annotationId) {
|
||||
this.root = root;
|
||||
this.annotations = annotations;
|
||||
|
||||
this.maxIndex = Object.keys(this.annotations).length - 1;
|
||||
|
||||
this.img1Element = document.getElementById(img1Id);
|
||||
this.img1IndexElement = document.getElementById(img1Id + '-index');
|
||||
this.img2Element = document.getElementById(img2Id);
|
||||
this.img2IndexElement = document.getElementById(img2Id + '-index');
|
||||
this.annotationElement = document.getElementById(annotationId);
|
||||
|
||||
this.setIndex(0);
|
||||
this.addShortcuts();
|
||||
}
|
||||
|
||||
formatIndex(index) {
|
||||
index += '';
|
||||
return index.padStart(4, '0');
|
||||
}
|
||||
|
||||
getUrl(index) {
|
||||
return this.root + '/' + this.formatIndex(index) + '.jpg';
|
||||
}
|
||||
|
||||
setIndex(index) {
|
||||
|
||||
// If index is undefined, we only want a refresh
|
||||
if (index !== undefined) {
|
||||
this.index = index;
|
||||
this.index = Math.max(0, this.index);
|
||||
this.index = Math.min(this.index, this.maxIndex);
|
||||
|
||||
// Refrsh images
|
||||
this.img1Element.src = this.getUrl(this.index);
|
||||
this.img2Element.src = this.getUrl(this.index + 1);
|
||||
|
||||
// Refresh headers
|
||||
this.img1IndexElement.innerHTML = this.index + '/' + this.maxIndex;
|
||||
this.img2IndexElement.innerHTML = this.index + 1 + '/' + this.maxIndex;
|
||||
}
|
||||
|
||||
// Refresh annotations list
|
||||
let currentAnnotations = this.annotations[this.formatIndex(this.index + 1)];
|
||||
if (currentAnnotations.length === 0) {
|
||||
this.annotationElement.innerHTML = "There is no annotation for this frame";
|
||||
} else {
|
||||
this.annotationElement.innerHTML = '<ul>';
|
||||
for (let annotation of currentAnnotations) {
|
||||
this.annotationElement.innerHTML += '<li>' + annotation + '</li>';
|
||||
}
|
||||
this.annotationElement.innerHTML += '</ul>';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
increment(by = 1) {
|
||||
this.setIndex(this.index + by);
|
||||
}
|
||||
|
||||
decrement(by = 1) {
|
||||
this.setIndex(this.index - by);
|
||||
}
|
||||
|
||||
addShortcuts() {
|
||||
document.addEventListener('keyup', (e) => {
|
||||
switch (e.code) {
|
||||
case 'ArrowRight': this.increment(e.ctrlKey ? 10 : 1); break;
|
||||
case 'ArrowLeft': this.decrement(e.ctrlKey ? 10 : 1); break;
|
||||
}
|
||||
|
||||
if (e.code.startsWith('Numpad') || e.code.startsWith('Digit')) {
|
||||
let key = parseInt(e.code[e.code.length - 1], 10);
|
||||
this.toggle(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
toggle(key) {
|
||||
let event = Events[key];
|
||||
if (event === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
let events = this.annotations[this.formatIndex(this.index + 1)];
|
||||
let search = events.indexOf(event);
|
||||
|
||||
if (search === -1) {
|
||||
events.push(event);
|
||||
} else {
|
||||
events.splice(search, 1);
|
||||
}
|
||||
|
||||
this.setIndex();
|
||||
this.sync();
|
||||
}
|
||||
|
||||
sync() {
|
||||
fetch('/data', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
root: this.root,
|
||||
annotations: this.annotations,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
async function main() {
|
||||
const root = findGetParameter('root');
|
||||
let annotations = await fetch(root + '/annotations.json');
|
||||
annotations = await annotations.json();
|
||||
const annotator = new Annotator(root, annotations, 'img-1', 'img-2', 'annotations');
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user