UNPKG

@obliczeniowo/elementary

Version:
214 lines (208 loc) 10.3 kB
import * as i0 from '@angular/core'; import { input, EventEmitter, effect, Output, Component, NgModule } from '@angular/core'; import { DrawingSvgInterface, TextAlign } from '@obliczeniowo/elementary/drawing'; import { Point2D, TextTransform } from '@obliczeniowo/elementary/classes'; import { CommonModule } from '@angular/common'; class PagesCircleTreeComponent { elementRef; renderer; /** items to draw */ items = input([], { transform: (value) => { return structuredClone(value); }, }); onClicked = new EventEmitter(); /** drawing interface */ drawing; /** found under mouse item */ item; /** prepared points to redraw diagram */ points = []; /** ray */ graphSize = 175; /** * graphSizeMax = graphSize + dGraphSize */ dGraphSize = 40; /** size of svg view box */ size = 500; /** categories */ categories = []; constructor(elementRef, renderer) { this.elementRef = elementRef; this.renderer = renderer; effect(() => { this.calcPoints(); this.draw(); }); } ngAfterViewInit() { this.drawing = new DrawingSvgInterface(this.elementRef.nativeElement.querySelector('svg'), this.renderer); setTimeout(() => this.draw()); } move(event) { const items = this.items(); if (this.drawing && items.length) { const box = this.drawing.svgContainer.getBoundingClientRect(); let pt = new Point2D(event.x - box.left, event.y - box.top); pt = pt.scale(500 / box.width, 500 / box.width); pt = pt.subtract(new Point2D(250, 250)); pt = pt.scale(1, -1).rotate(Math.PI / 2); let angle = pt.angle(); if (angle < 0) { angle = 2 * Math.PI + angle; } const index = Math.floor((items.length / (Math.PI * 2)) * angle); this.item = items[index]; this.draw(); } } clicked() { if (this.item) { this.onClicked.emit(structuredClone(this.item)); } } calcPoints() { const items = this.items(); if (items) { const max = this.getMax(); const k = this.dGraphSize / (max || 1); this.points = []; items.forEach((item, index) => { const calcRay = this.graphSize + k * item.value; const angle = ((Math.PI * 2) / this.items().length) * index; const pos = new Point2D(calcRay * Math.sin(angle) + this.size / 2, calcRay * Math.cos(angle) + this.size / 2); item.data = { ...item.data, pos, index, angle, }; this.points.push(pos); }); } this.calcCategories(); } calcCategories() { this.categories = []; const center = new Point2D(this.size / 2, this.size / 2); const items = this.items(); const getPos = (index, scale = 0.9) => items[index].data.pos .subtract(center) .setLength(this.graphSize) .multiply(scale) .add(center); for (let i = 1; i < items.length; i++) { if (i === 1) { const { parentName, rootName } = items[0]; this.categories.push({ parentName, rootName, pos: getPos(0, 1.2), endPos: getPos(0), pChanged: true, }); } else if (items[i].rootName !== items[i - 1].rootName || items[i].parentName !== items[i - 1].parentName) { const { parentName, rootName } = items[i]; const pChanged = items[i].rootName !== items[i - 1].rootName; this.categories.push({ parentName, rootName, pos: getPos(i, pChanged ? 1.2 : 1), endPos: getPos(i), pChanged, }); } } } draw() { if (!this.drawing) { return; } const color = '#000000'; const diagramClass = ['diagram']; const categoriesClass = ['categories']; this.drawing.clear(); this.drawing.drawPolyline(this.points, 1, color, { close: true, svgOnly: { classes: diagramClass, }, }); this.categories.forEach((category) => { this.drawing.drawLine(category.pos, category.endPos, category.pChanged ? 0.6 : 0.3, color, { svgOnly: { classes: categoriesClass.concat(category.pChanged ? ['root'] : ['parent']), }, }); }); if (this.item) { this.drawing.drawCircle(this.item.data.pos, 2.5, 1, color, color, { svgOnly: { classes: ['current'] }, }); this.drawing.drawCircle(new Point2D(250, 250), this.item.data.pos.subtract(new Point2D(250, 250)).length(), 0.1, color, undefined, { svgOnly: { classes: ['value'] } }); this.drawing.drawCircle(new Point2D(250, 250), this.graphSize, 0.1, color, undefined, { svgOnly: { classes: ['minimum'] } }); this.drawing.setFontSize(20); this.drawing.setTextAlign(TextAlign.CENTER); this.drawing.drawText(this.item.rootName, new Point2D(250, 230), color); this.drawing.setFontSize(15); this.drawing.drawText(`${this.item.parentName}`, new Point2D(250, 255), color); this.drawing.setFontSize(7); const offset = this.drawSplittedText(this.item.name, new Point2D(250, 270), color, 80); this.drawing.drawText(`${this.item.value}`, new Point2D(250, 270 + offset), color); } } drawSplittedText(text, position, color, maxLength) { const strings = TextTransform.splitByLengthAndWords(text, maxLength); const fontSize = this.drawing.fontSize * 1.4; strings.forEach((text, index) => { this.drawing.drawText(text, position.add(new Point2D(0, index * fontSize)), color); }); return strings.length * fontSize; } getMax() { let max = 0; this.items().forEach((item) => { if (max < item.value) { max = item.value; } }); return max; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: PagesCircleTreeComponent, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.0.4", type: PagesCircleTreeComponent, isStandalone: false, selector: "obl-pages-circle-tree", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onClicked: "onClicked" }, ngImport: i0, template: "<svg width=\"75%\" [attr.viewBox]=\"'0 0 ' + size + ' ' + size\" (mousemove)=\"move($event)\" (click)=\"clicked()\">\n</svg>", styles: [":host{display:flex;width:100%;align-items:center;justify-content:center}:host ::ng-deep polyline.diagram{stroke:var(--default-text-color);stroke-width:1;fill:none}:host ::ng-deep polyline.categories{fill:none}:host ::ng-deep polyline.categories.root{stroke:red;stroke-width:.6}:host ::ng-deep polyline.categories.parent{stroke:#00b400;stroke-width:.3}:host ::ng-deep circle.current{fill:var(--default-text-color)}:host ::ng-deep circle.value,:host ::ng-deep circle.minimum{fill:none;stroke:var(--default-text-color);stroke-width:.1}\n"] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: PagesCircleTreeComponent, decorators: [{ type: Component, args: [{ selector: 'obl-pages-circle-tree', standalone: false, template: "<svg width=\"75%\" [attr.viewBox]=\"'0 0 ' + size + ' ' + size\" (mousemove)=\"move($event)\" (click)=\"clicked()\">\n</svg>", styles: [":host{display:flex;width:100%;align-items:center;justify-content:center}:host ::ng-deep polyline.diagram{stroke:var(--default-text-color);stroke-width:1;fill:none}:host ::ng-deep polyline.categories{fill:none}:host ::ng-deep polyline.categories.root{stroke:red;stroke-width:.6}:host ::ng-deep polyline.categories.parent{stroke:#00b400;stroke-width:.3}:host ::ng-deep circle.current{fill:var(--default-text-color)}:host ::ng-deep circle.value,:host ::ng-deep circle.minimum{fill:none;stroke:var(--default-text-color);stroke-width:.1}\n"] }] }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { onClicked: [{ type: Output }] } }); class PagesCircleTreeModule { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: PagesCircleTreeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.0.4", ngImport: i0, type: PagesCircleTreeModule, declarations: [PagesCircleTreeComponent], imports: [CommonModule], exports: [PagesCircleTreeComponent] }); static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: PagesCircleTreeModule, imports: [CommonModule] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: PagesCircleTreeModule, decorators: [{ type: NgModule, args: [{ declarations: [ PagesCircleTreeComponent ], imports: [ CommonModule ], exports: [ PagesCircleTreeComponent ] }] }] }); ; /** * Generated bundle index. Do not edit. */ export { PagesCircleTreeComponent, PagesCircleTreeModule }; //# sourceMappingURL=obliczeniowo-elementary-pages-circle-tree.mjs.map