@obliczeniowo/elementary
Version:
Library made in Angular version 20
214 lines (208 loc) • 10.3 kB
JavaScript
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