ngx-image-annotation
Version:
A comprehensive Angular component for tagging images. Check out [the demo](https://pdkds7-4200.csb.app/) for some examples.
200 lines (195 loc) • 8.11 kB
JavaScript
import { Director, Rectangle, Polygon, Circle, Ellipse, Dot, Util, ActType, ImageEl, SVGSVGEl } from 'image-labeling';
export { Circle, Dot, Ellipse, Polygon, Rectangle, Shape } from 'image-labeling';
import * as i0 from '@angular/core';
import { EventEmitter, Input, Output, Component } from '@angular/core';
class AnnotatorComponent {
elRef;
getDirector = () => Director.instance;
onReady = new EventEmitter();
onAdded = new EventEmitter();
onEdited = new EventEmitter();
onSelected = new EventEmitter();
onContextMenu = new EventEmitter();
set imageUrl(value) {
Director.clear(this.elRef.nativeElement.querySelector('svg'));
if (value)
this.onload(value);
}
shapes;
naturalSize;
width;
height;
discRadius;
hideBorder;
shortcut;
categoryOpt;
constructor(elRef) {
this.elRef = elRef;
}
stopAll = () => {
let director = this.getDirector();
director.stopDraw();
director.stopEdit();
};
drawRectangle() {
this.stopAll();
this.getDirector().startDraw(new Rectangle());
}
drawPolygon() {
this.stopAll();
this.getDirector().startDraw(new Polygon());
}
drawCircle() {
this.stopAll();
this.getDirector().startDraw(new Circle());
}
drawEllipse() {
this.stopAll();
this.getDirector().startDraw(new Ellipse());
}
drawDot() {
this.stopAll();
this.getDirector().startDraw(new Dot());
}
stop = this.stopAll;
stopEdit = () => this.getDirector().stopEdit();
edit = (id) => this.getDirector().edit(id);
delete = (id) => this.getDirector().removeById(id);
updateCategories = (id, categories, color) => this.getDirector().updateCategories(id, categories, color);
zoom = (factor, relative = true) => {
let director = this.getDirector();
factor = director.setSizeAndRatio(factor, relative);
director.zoom(factor);
};
getShapes = () => this.getDirector().getShapes();
onload(imageUrl) {
let svg = this.getWrapper(), container = this.getContainer();
let onloaded = (target) => {
let bb = target.bbox();
let naturalWidth = bb.width, naturalHeight = bb.height, ratio = 1, sw = container.parentElement?.scrollWidth ?? 0, width = Util.toPx(sw, this.width), height = Util.toPx(sw, this.height);
svg.addClass('il-svg');
Object.assign(container.style, {
width: (width || naturalWidth) + 'px',
height: (height || naturalHeight) + 'px',
overflow: 'hidden',
backgroundColor: '#e6e6e6'
});
if (!this.naturalSize) {
if (!width)
width = container.scrollWidth;
if (!height)
height = container.scrollHeight;
if (width / height > bb.width / bb.height)
ratio = Math.min(height, bb.height) / naturalHeight;
else
ratio = Math.min(width, bb.width) / naturalWidth;
}
target.size('100%', '100%');
let statics = {
width: naturalWidth,
height: naturalHeight,
ratio,
discRadius: this.discRadius || 5,
hb: this.hideBorder,
shortcut: this.shortcut,
categoryOpt: this.categoryOpt || { vertical: 'top' }
};
Director.init(svg, statics, container);
this.drawShapes(this.shapes);
this.onReady.emit();
let actions = [
{ type: ActType.Added, func: (shape) => this.onAdded.emit(shape) },
{ type: ActType.Edited, func: (shape) => this.onEdited.emit(shape) },
{ type: ActType.Selected, func: (shape) => this.onSelected.emit(shape) },
{ type: ActType.CtxMenu, func: (shape) => this.onContextMenu.emit(shape) }
];
Director.setActions(actions);
};
var image = svg.image(imageUrl, onloaded).size('', '').attr('onmousedown', 'return false').attr('oncontextmenu', 'return false');
image.on('testEvent', (ev) => onloaded(new ImageEl(ev.detail.testTarget)));
}
drawShapes(shapes) {
let director = this.getDirector();
if (!shapes)
return;
director.setMaxId(Math.max(...shapes.map(c => c.id ?? 0)));
let rectangles = shapes.filter(s => s instanceof Rectangle || s.type === 'rectangle')
.map(s => new Rectangle([...s.points], s.categories, s.color, s.id));
let polygons = shapes.filter(s => s instanceof Polygon || s.type === 'polygon')
.map(s => new Polygon([...s.points], s.categories, s.color, s.id));
let circles = shapes.filter(s => s instanceof Circle || s.type === 'circle')
.map(s => new Circle(s.centre, s.radius, s.categories, s.color, s.id));
let ellipses = shapes.filter(s => s instanceof Ellipse || s.type === 'ellipse')
.map(s => new Ellipse(s.centre, s.radiusX, s.radiusY, s.categories, s.phi || 0, s.color, s.id));
let dots = shapes.filter(s => s instanceof Dot || s.type === 'dot')
.map(s => new Dot(s.position, s.categories, s.color, s.id));
if (rectangles.length > 0)
director.plot(rectangles);
if (polygons.length > 0)
director.plot(polygons);
if (circles.length > 0)
director.plot(circles);
if (ellipses.length > 0)
director.plot(ellipses);
if (dots.length > 0)
director.plot(dots);
}
getWrapper() {
return new SVGSVGEl(this.elRef.nativeElement.querySelector('svg'));
}
getContainer() {
return this.elRef.nativeElement.querySelector('svg').parentElement;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AnnotatorComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: AnnotatorComponent, isStandalone: true, selector: "annotator", inputs: { imageUrl: "imageUrl", shapes: "shapes", naturalSize: "naturalSize", width: "width", height: "height", discRadius: "discRadius", hideBorder: "hideBorder", shortcut: "shortcut", categoryOpt: "categoryOpt" }, outputs: { onReady: "onReady", onAdded: "onAdded", onEdited: "onEdited", onSelected: "onSelected", onContextMenu: "onContextMenu" }, ngImport: i0, template: `
<div>
<svg>
</svg>
</div>
`, isInline: true, styles: [""] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AnnotatorComponent, decorators: [{
type: Component,
args: [{ selector: 'annotator', imports: [], template: `
<div>
<svg>
</svg>
</div>
` }]
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { onReady: [{
type: Output
}], onAdded: [{
type: Output
}], onEdited: [{
type: Output
}], onSelected: [{
type: Output
}], onContextMenu: [{
type: Output
}], imageUrl: [{
type: Input
}], shapes: [{
type: Input
}], naturalSize: [{
type: Input
}], width: [{
type: Input
}], height: [{
type: Input
}], discRadius: [{
type: Input
}], hideBorder: [{
type: Input
}], shortcut: [{
type: Input
}], categoryOpt: [{
type: Input
}] } });
/*
* Public API Surface of annotator
*/
/**
* Generated bundle index. Do not edit.
*/
export { AnnotatorComponent };
//# sourceMappingURL=ngx-image-annotation.mjs.map