coco/www/coco.js

375 lines
11 KiB
JavaScript

//////////////////////////
// use texture.js without d3 to create discerning patterns on the email blocks
/////////////////////////////////////
// some faked dom, see https://github.com/riccardoscalco/textures/issues/17
function dom(name) {
this.name = name;
this.els = [];
this.attrs = {};
}
dom.prototype.append = function(name) {
if (name === "defs") return this;
if (this.name === undefined) {
this.name = name;
return this;
}
var el = new dom(name);
this.els.push(el);
return el;
}
dom.prototype.attr = function(key, value) {
this.attrs[key] = value;
return this;
}
dom.prototype.toString = function() {
var attrs = [];
var k;
for (k in this.attrs) {
attrs += " " + k + "='" + this.attrs[k] + "'";
}
if (this.els.length) {
return "<"+this.name+attrs+">"+this.els.map(function(el) { return el.toString()}).join('\n')+"</"+this.name+">";
} else {
return "<"+this.name+attrs+"/>"
}
}
//////////// end of faked dom ///////////////////////////////
let categoryMap = {};
let textureMap = {};
function getColoredTexture(i, color) {
let j = i % 11; // update when adding items to switch:
switch(j) {
case 0:
return textures.lines().size(4).strokeWidth(1).stroke(color);
case 1:
return textures.circles().radius(2).size(5).fill('none').strokeWidth(1).stroke(color).complement();
case 2:
return textures.lines().size(10).orientation("3/8").stroke(color);
case 3:
return textures.lines().heavier(4).thinner(.8).stroke(color);
case 4:
return textures.paths().d("hexagons").size(3).strokeWidth(1).stroke(color);
case 5:
return textures.lines().orientation("vertical", "horizontal").size(4).strokeWidth(1).shapeRendering("crispEdges").stroke(color);
case 6:
return textures.paths().d("hexagons").size(5).strokeWidth(2).stroke("rgba(0,0,0,0)").fill(color);
case 7:
return textures.circles().size(4).fill(color);
case 8:
return textures.circles().thicker().complement().fill(color);
case 9:
return textures.paths().d("caps").lighter().thicker().size(5).stroke(color);
case 10:
return textures.paths().d("hexagons").size(4).strokeWidth(2).stroke(color);
// textures.lines().size(4).strokeWidth(1).orientation("-3/8"),
}
};
const categoryColors = {
"person": "#FA0800",
"vehicle": "#80FF00",
"outdoor": "#00FFFF",
"animal": "#FF00FF",
"accessory": "#FFFF00",
"food": "#00FA85",
"furniture": "#BB00FA",
"indoor": "#8500FA",
"electronic": "#FAD900",
"kitchen": "#FA8500",
"sports": "#0080FF",
"appliance": "#FF0080",
}
function getColorForSuperCategory(name) {
return categoryColors[name];
}
function getTextureForCategory(id) {
let hash = id;
if(!textureMap.hasOwnProperty(hash)) {
let color = categoryColors[categoryMap[id]['supercategory']];
textureMap[hash] = getColoredTexture(id, color);
}
return textureMap[hash];
}
class CocoCanvas {
start(){
this.catNavEl = document.getElementById('catNav');
this.catNav2El = document.getElementById('catNav2');
this.canvas = document.getElementById('svgCanvas');
this.loadImagesBtnEl = document.getElementById("loadImages");
this.loadLabelsBtnEl = document.getElementById("loadLabels");
this.sceneSelectEl = document.getElementById('scene');
this.savedBtnEl = document.getElementById('save');
this.loadNav()
this.annotations = [];
this.loadImagesBtnEl.addEventListener('change', (e) => this.toggleImages(e));
this.loadLabelsBtnEl.addEventListener('change', (e) => this.toggleLabels(e));
this.savedBtnEl.addEventListener('click', function(e){
if(this.savedBtnEl.disabled || this.annotations.length < 1) return;
this.save();
}.bind(this));
}
loadNav() {
let r = new Request('/categories.json');
fetch(r)
.then(response => response.json())
.then(categories => {
for(let cat of categories) {
categoryMap[cat['id']] = cat;
}
this.buildNav(categories);
}).catch(function(e){
console.error(e);
});
}
buildNav(categories) {
let lastSuperCat = null;
let supercategories = []
let supercategoriesEls = {}
// create menu's per supercategory, divide these over left & right
for(let cat of categories) {
if(supercategories.indexOf(cat['supercategory']) < 0) {
supercategories.push(cat['supercategory']);
}
}
for(let supercat of supercategories) {
let ulEl = crel('ul', {'data-name': supercat, 'class':'supercategory-'+supercat});
supercategoriesEls[supercat] = ulEl;
// first half left, other half right side of the page
if(supercategories.indexOf(supercat)/supercategories.length < .5) {
this.catNavEl.appendChild(ulEl);
} else {
this.catNav2El.appendChild(ulEl);
}
}
// entries for the menus
for(let cat of categories) {
// let firstOfType = lastSuperCat != cat['supercategory'] ? ' first-of-super' : '';
// lastSuperCat = cat['supercategory'];
supercategoriesEls[cat['supercategory']].appendChild(crel('li', {
'id': 'category-' + cat['id'],
// 'class': 'supercategory-' + cat['supercategory'],
'on': {
'click': (e) => {
this.requestAnnotation(cat['id']);
}
}
}, cat['name']));
}
this.defsEl = document.createElementNS("http://www.w3.org/2000/svg", 'defs');
for(let cat of categories) {
let texture = getTextureForCategory(cat['id']);
let sel = new dom();
texture(sel);
this.defsEl.innerHTML += sel.toString();
}
this.canvas.appendChild(this.defsEl);
}
requestAnnotation(category_id) {
let r = new Request(`/annotation.json?category=${category_id}&normalise=100`);
return fetch(r)
.then(response => response.json())
.then(annotation => {
this.addAnnotationAsShape(annotation);
}).catch(function(e){
console.error(e);
});
}
pointsToD(points) {
let start = points.shift()
let d = `M${start[0].toPrecision(4)} ${start[1].toPrecision(4)} L `;
points = points.map((p) => `${p[0].toPrecision(4)} ${p[1].toPrecision(4)}`);
d += points.join(' ');
return d;
}
getMousePosition(evt) {
// from http://www.petercollingridge.co.uk/tutorials/svg/interactive/dragging/
let CTM = this.canvas.getScreenCTM();
return {
x: (evt.clientX - CTM.e) / CTM.a,
y: (evt.clientY - CTM.f) / CTM.d
};
}
addAnnotationAsShape(annotation) {
console.log('Add annotation', annotation);
let category = categoryMap[annotation['category_id']]
let texture = getTextureForCategory(category['id']);
let x = 500 - annotation['bbox'][2]/2;
let y = 500 - annotation['bbox'][3]/2;
let annEl = crel(document.createElementNS("http://www.w3.org/2000/svg", 'g'), {
'data-id': annotation['id'],
'transform': `translate(${x}, ${y})`,
'on': {
'mousedown': function(downE) {
// after clicking the element should be the top element
// and the last svg element is drawn on top.
annEl.parentNode.appendChild(annEl);
// console.log(downE);
// console.log(this)
let offset = this.getMousePosition(downE);
// Get initial translation amount
// console.log(annEl.transform.baseVal);
let transform = annEl.transform.baseVal.getItem(0);
offset.x -= transform.matrix.e;
offset.y -= transform.matrix.f;
let moveEvent = (moveE) => {
let coord = this.getMousePosition(moveE);
transform.matrix.e = coord.x - offset.x;
transform.matrix.f = coord.y - offset.y;
// annEl.setAttributeNS(null, "x", coord.x - offset.x);
// annEl.setAttributeNS(null, "y", coord.y - offset.y);
};
document.addEventListener('mousemove', moveEvent);
document.addEventListener('mouseup', (upE) => {
document.removeEventListener('mousemove', moveEvent);
});
}.bind(this)
}
});
for(let segment of annotation['segments']) {
let pathEl = crel(document.createElementNS("http://www.w3.org/2000/svg", 'path'), {
'fill': texture.url(),
'd': this.pointsToD(segment)
});
annEl.appendChild(pathEl);
}
this.canvas.appendChild(annEl);
// keep info on annotation elements in CocoCanvas object
annotation['element'] = annEl;
this.annotations.push(annotation);
this.savedBtnEl.disabled = false;
// based on status of checkboxes, add label or image (bit of a later hack)
this.toggleImages();
this.toggleLabels();
}
// convert annotation shapes on canvas to images, which are masked by the shape
convertToImages() {
console.log('convert');
for(let annotation of this.annotations) {
console.log(annotation);
if(annotation['element'].getElementsByTagName('image').length) {
//already done
continue;
}
let scale = annotation['scale'];
let bbox = annotation['is_normalised'] ? annotation['bbox_original'] : annotation['bbox'];
let imgEl = crel(document.createElementNS("http://www.w3.org/2000/svg", 'image'), {
'href': annotation['image']['coco_url'],
'width': annotation['image']['width']* scale,
'height': annotation['image']['height']* scale,
'x': bbox[0] * -1 * scale,
'y': bbox[1] * -1 * scale,
});
annotation['element'].prepend(imgEl );
}
}
addLabels() {
console.log('labels');
for(let annotation of this.annotations) {
console.log(annotation);
if(annotation['element'].getElementsByTagName('text').length) {
//already done
continue;
}
let textEl = crel(document.createElementNS("http://www.w3.org/2000/svg", 'text'), {
'x': annotation['bbox'][2] + 5,
'y': annotation['bbox'][3],
}, categoryMap[annotation['category_id']]['name']);
annotation['element'].append(textEl );
}
}
toggleImages(e) {
if(this.loadImagesBtnEl.checked) {
this.convertToImages();
document.body.classList.remove('hideImages');
}
else {
document.body.classList.add('hideImages');
}
}
toggleLabels(e) {
if(this.loadLabelsBtnEl.checked) {
this.addLabels();
document.body.classList.remove('hideLabels');
}
else {
document.body.classList.add('hideLabels');
}
}
save() {
let scene = this.sceneSelectEl.value;
let annotations = [];
for (let ann of this.annotations) {
annotations.push({
'id': ann['id'],
'x': ann.element.transform.baseVal.getItem(0).matrix.e,
'y': ann.element.transform.baseVal.getItem(0).matrix.f
});
}
let data = JSON.stringify({
'scene': this.sceneSelectEl.value,
'annotations': annotations
});
let r = new Request('/save', {'method': 'POST', 'body': data});
fetch(r)
.then(response => response.json())
.then(submission => {
// alert("Something went wrong when saving the file");
// todo redirect to submission
console.log('saved', submission);
window.location = window.location + 'saved';
}).catch(function(e){
alert("Something went wrong when saving the file");
console.error(e);
});
}
}
let cc = new CocoCanvas();
cc.start();
//for testing only:
//cc.requestAnnotation(1).then((e) => cc.convertToImages());