Files

151 lines
4.9 KiB
JavaScript

import { BBoxElement } from "./BBoxElement.js";
/**
* Element that displays bounding box overlays on an image.
*/
export class BBoxOverlayElement extends HTMLElement {
constructor () {
super();
this.el = {
bboxes: []
};
// This element is part of the 'segs-control' feature and should be hidden when that feature is not enabled.
this.classList.add('segs-control');
this.addEventListener('mouseleave', () => {
// Remove hover style from all bboxes.
for (const bbox of this.el.bboxes) {
bbox.classList.remove('hover');
}
});
this.addEventListener('mousemove', event => {
// Remove hover style from all bboxes.
for (const bbox of this.el.bboxes) {
bbox.classList.remove('hover');
}
// Update hover styles for the bbox under the mouse.
this.testPoint(event.clientX, event.clientY, (insideBbox) => {
insideBbox.classList.add('hover');
});
}, { capture: true });
this.addEventListener('mousedown', event => {
// Handle clicks on the bbox under the mouse.
if (event.button === 0 || event.button === 2) {
// Update hover styles for the bbox under the mouse.
this.testPoint(event.clientX, event.clientY, (bbox) => {
this.onBBoxClick({
detail: {
eventName: 'mouseup',
button: event.button,
buttons: event.buttons,
segn: this.el.bboxes.indexOf(bbox),
bbox
}
});
});
event.preventDefault();
event.stopPropagation();
return;
}
}, { capture: true });
// Prevent the clicks from being propagated.
this.addEventListener('click', event => {
this.testPoint(event.clientX, event.clientY, (insideBbox) => {
event.preventDefault();
event.stopPropagation();
});
}, { capture: true });
// Prevent the context menu from being displayed.
this.addEventListener('contextmenu', event => {
event.preventDefault();
event.stopPropagation();
}, { capture: true });
this.segmentChangeHander = this.onSegmentChange.bind(this);
}
testPoint (clientX, clientY, callback) {
for (const bbox of document.elementsFromPoint(clientX, clientY)) {
if (bbox === this) {
// Reached the overlay container.
return;
}
if (bbox instanceof BBoxElement) {
// Bounding box at the top of the stack.
if(bbox.isPointInsideMask(clientX, clientY)) {
callback(bbox);
return;
}
}
}
}
setSegments(segments) {
this.segments = segments;
this.segments.addEventListener('segment-change', this.segmentChangeHander);
this.update();
}
update () {
this.innerText = '';
this.el.bboxes.length = 0;
if (this.segments) {
for (let segn = 0; segn < this.segments.bboxes.length; segn++) {
const sizes = this.segments.sizes[segn],
bbox = this.segments.bboxes[segn],
label = this.segments.label[segn],
unwanted = this.segments.unwanted[segn],
maskUrl = this.segments.getMaskUrl(segn);
const bboxElement = new BBoxElement(segn, sizes, bbox, maskUrl);
bboxElement.addEventListener('bbox-mousedown', this.bboxClickHandler);
bboxElement.setLabel(label);
bboxElement.setUnwanted(unwanted);
this.el.bboxes.push(bboxElement);
this.appendChild(bboxElement);
}
}
}
onSegmentChange(event) {
const detail = event.detail,
bbox = this.el.bboxes[detail.segn];
bbox.setLabel(detail.label);
bbox.setUnwanted(detail.unwanted);
}
onBBoxClick(event) {
const detail = event.detail;
if ((detail.buttons | detail.button) === 3) {
// Left-right or right-left chord.
this.segments.toggleUnwanted(detail.segn);
} else {
// Releasing a single button.
switch (detail.button) {
case 0:
this.segments.nextLabel(detail.segn, 1);
break;
case 2:
this.segments.nextLabel(detail.segn, -1);
break;
}
}
}
}
customElements.define('liebs-picker-bbox-overlay', BBoxOverlayElement);