UNPKG

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
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