From 6ed91397de3025ba4b567713103e217332a5029e Mon Sep 17 00:00:00 2001 From: Mihails Akimenko Date: Sun, 7 Feb 2021 00:24:52 +0200 Subject: [PATCH 1/6] Dagre dynamic add/remove --- .../layout/dagre-composition.component.ts | 4 -- .../layout/dagre-layout.component.ts | 60 +++++++++++++++--- .../atft/src/lib/object/abstract-object-3d.ts | 3 +- .../object/helper/axes-helper.component.ts | 2 +- projects/atft/src/lib/util/removeFromArray.ts | 6 ++ src/stories/layout/dagre-dynamic.stories.ts | 62 +++++++++++++++++++ src/stories/layout/dagre.stories.ts | 5 +- 7 files changed, 125 insertions(+), 17 deletions(-) create mode 100644 projects/atft/src/lib/util/removeFromArray.ts create mode 100644 src/stories/layout/dagre-dynamic.stories.ts diff --git a/projects/atft/src/lib/actor/data-center/layout/dagre-composition.component.ts b/projects/atft/src/lib/actor/data-center/layout/dagre-composition.component.ts index fb744cc2..68941378 100644 --- a/projects/atft/src/lib/actor/data-center/layout/dagre-composition.component.ts +++ b/projects/atft/src/lib/actor/data-center/layout/dagre-composition.component.ts @@ -24,15 +24,11 @@ export class DagreCompositionComponent extends EmptyComponent { this._height = hight; this.translateLabelY = this._height / 2 - 5; } - get height(): number { return this._height; } @Input() width: number; - - - @Output() render = new EventEmitter(); @Output() selected = new EventEmitter(); @Output() deselected = new EventEmitter(); diff --git a/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts b/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts index d568458c..15a62b57 100644 --- a/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts +++ b/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts @@ -1,4 +1,4 @@ -import {AfterViewInit, Component, Input, OnChanges, Optional, SimpleChanges, SkipSelf} from '@angular/core'; +import {AfterViewInit, Component, Input, OnChanges, OnDestroy, Optional, SimpleChanges, SkipSelf} from '@angular/core'; import {EmptyComponent} from '../../../object/helper'; import {provideParent} from '../../../util'; import {DagreUtils, GraphModel} from './dagre-utils'; @@ -17,7 +17,7 @@ import {DagreCompositionComponent} from './dagre-composition.component'; ` }) -export class DagreLayoutComponent extends EmptyComponent implements AfterViewInit, OnChanges { +export class DagreLayoutComponent extends EmptyComponent implements AfterViewInit, OnChanges, OnDestroy { @Input() align = 'DR'; @Input() rankdir = 'TB'; @@ -30,6 +30,7 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni protected graphModel: GraphModel; + protected graph: dagre.graphlib.Graph; constructor( protected rendererService: RendererService, @@ -48,10 +49,22 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni public addChild(object: AbstractObject3D): void { super.addChild(object); - this.processDagreChild(object); + this.addDagreChild(object); + if (this.graph) { + this.layout(); + } + } + + public removeChild(object: AbstractObject3D) { + super.removeChild(object); + console.log('DagreLayoutComponent.removeChild'); + this.removeDagreChild(object); + if (this.graph) { + this.layout(); + } } - protected processDagreChild(object: AbstractObject3D) { + protected addDagreChild(object: AbstractObject3D) { if (object instanceof DagreEdgeComponent) { this.addEdge(object); } else { @@ -59,6 +72,26 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni } } + protected removeDagreChild(object: AbstractObject3D) { + if (object instanceof DagreEdgeComponent) { + console.log('DagreLayoutComponent.removeDagreChild: Edge'); + const edgeObject: DagreEdgeComponent = object; + this.graphModel.edges = this.graphModel.edges.filter(i => i.uuid !== edgeObject.getObject().uuid); + } else { + console.log('DagreLayoutComponent.removeDagreChild: Node'); + this.graphModel.nodes = this.graphModel.nodes.filter(i => i.id !== object.getObject().uuid); + + if (object instanceof DagreNodeComponent) { + const node: DagreNodeComponent = object; + if (node.composition) { + this.graphModel.composition = this.graphModel.composition.filter(i => i.child !== node.getObject().uuid); + } + } + + } + } + + protected addEdge(edge: DagreEdgeComponent) { // console.log('DagreLayoutComponent.addEdge', edge); const edgeObject: DagreEdgeComponent = edge; @@ -111,10 +144,11 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni marginy: this.marginy, ranker: this.ranker }; - const g = DagreUtils.modelToGraph(this.graphModel); + this.graph = DagreUtils.modelToGraph(this.graphModel); // console.log('DagreLayoutComponent.layout: g', g); - this.syncGraphNodes(g); - this.syncGraphEdges(g); + this.syncGraphNodes(this.graph); + this.syncGraphEdges(this.graph); + this.syncGraphContainer(this.graph); this.rendererService.render(); } @@ -170,6 +204,13 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni }); } + protected syncGraphContainer(g: dagre.graphlib.Graph) { + // console.log('DagreLayoutComponent.syncGraphContainer'); + this.translateX = -(g.graph().width / 2); + this.translateY = -(g.graph().height / 2); + this.applyTranslation(); + + } public ngOnChanges(changes: SimpleChanges) { // console.log('AbstractObject3D.ngOnChanges', this.uuid); @@ -192,5 +233,10 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni } + ngOnDestroy() { + super.ngOnDestroy(); + this.graph = undefined; + this.graphModel = undefined; + } } diff --git a/projects/atft/src/lib/object/abstract-object-3d.ts b/projects/atft/src/lib/object/abstract-object-3d.ts index 81d9c93e..7f6b8bc6 100644 --- a/projects/atft/src/lib/object/abstract-object-3d.ts +++ b/projects/atft/src/lib/object/abstract-object-3d.ts @@ -86,7 +86,8 @@ export abstract class AbstractObject3D implements Afte public ngOnDestroy() { // console.log('AbstractObject3D.OnDestroy', this.uuid); if (this.object && this.object.parent) { - this.object.parent.remove(this.object); + this.parent.removeChild(this); + // this.object.parent.remove(this.object); if (this.rendererService) { this.rendererService.render(); } diff --git a/projects/atft/src/lib/object/helper/axes-helper.component.ts b/projects/atft/src/lib/object/helper/axes-helper.component.ts index 59adf215..72c7f91f 100644 --- a/projects/atft/src/lib/object/helper/axes-helper.component.ts +++ b/projects/atft/src/lib/object/helper/axes-helper.component.ts @@ -11,7 +11,7 @@ import { AbstractObject3D } from '../abstract-object-3d'; }) export class AxesHelperComponent extends AbstractObject3D { - @Input() size: number; + @Input() size = 50; constructor( protected rendererService: RendererService, diff --git a/projects/atft/src/lib/util/removeFromArray.ts b/projects/atft/src/lib/util/removeFromArray.ts new file mode 100644 index 00000000..e249d0c4 --- /dev/null +++ b/projects/atft/src/lib/util/removeFromArray.ts @@ -0,0 +1,6 @@ +export function removeFromArray(array: Array, element: T) { + const index = array.indexOf(element, 0); + if (index > -1) { + array.splice(index, 1); + } +} diff --git a/src/stories/layout/dagre-dynamic.stories.ts b/src/stories/layout/dagre-dynamic.stories.ts new file mode 100644 index 00000000..21d6bc78 --- /dev/null +++ b/src/stories/layout/dagre-dynamic.stories.ts @@ -0,0 +1,62 @@ +import {Component} from '@angular/core'; +import {moduleMetadata} from '@storybook/angular'; +import {AtftDataCenterActorModule} from '../../../projects/atft/src/lib/actor/data-center'; +// NOTE: Do direct import instead of library (allows to watch component and easy to develop) +import {AtftModule} from '../../../projects/atft/src/lib/atft.module'; +import {worldSceneWrapper} from '../scene-wrapper/world-scene-wrapper'; +import {AnimationService} from '../../../projects/atft/src/lib/animation'; + + +@Component({ + template: worldSceneWrapper(` + + + + + + + + + +`) +}) +class StorybookDagreComponent { + + constructor(private animationService: AnimationService) { + this.animationService.start(); + } + + numDatabases = 1; + + fakeArray(length: number): Array { + if (length >= 0) { + return new Array(length); + } + } + +} + + +export default { + title: 'Layout/Dagre Dynamic', + decorators: [ + moduleMetadata({ + imports: [ + AtftModule, + AtftDataCenterActorModule + ] + }) + ], + args: { + numDatabases: 1 + } + +}; + + +export const DynamicNodes = (args) => ({ + component: StorybookDagreComponent, + props: args +}); + + diff --git a/src/stories/layout/dagre.stories.ts b/src/stories/layout/dagre.stories.ts index 3ff665bc..19c861cc 100644 --- a/src/stories/layout/dagre.stories.ts +++ b/src/stories/layout/dagre.stories.ts @@ -9,7 +9,6 @@ import {AnimationService} from '../../../projects/atft/src/lib/animation'; @Component({ template: worldSceneWrapper(` - @@ -26,7 +25,6 @@ import {AnimationService} from '../../../projects/atft/src/lib/animation'; - `) }) class StorybookDagreComponent { @@ -39,7 +37,6 @@ class StorybookDagreComponent { @Component({ template: worldSceneWrapper(` - @@ -69,7 +66,7 @@ class StorybookDagreComponent { - + `) }) class StorybookDagreCompositionComponent { From a58b7765c351ce14d0e10d88615039ddf26aa098 Mon Sep 17 00:00:00 2001 From: Mihails Akimenko Date: Sun, 7 Feb 2021 00:31:58 +0200 Subject: [PATCH 2/6] Dagre NgFor --- .../layout/dagre-composition.component.ts | 2 +- src/stories/layout/dagre-dynamic.stories.ts | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/projects/atft/src/lib/actor/data-center/layout/dagre-composition.component.ts b/projects/atft/src/lib/actor/data-center/layout/dagre-composition.component.ts index 68941378..4d41cb73 100644 --- a/projects/atft/src/lib/actor/data-center/layout/dagre-composition.component.ts +++ b/projects/atft/src/lib/actor/data-center/layout/dagre-composition.component.ts @@ -22,7 +22,7 @@ export class DagreCompositionComponent extends EmptyComponent { @Input() set height(hight: number) { this._height = hight; - this.translateLabelY = this._height / 2 - 5; + this.translateLabelY = this._height / 2 - 3; } get height(): number { return this._height; diff --git a/src/stories/layout/dagre-dynamic.stories.ts b/src/stories/layout/dagre-dynamic.stories.ts index 21d6bc78..01cd20d5 100644 --- a/src/stories/layout/dagre-dynamic.stories.ts +++ b/src/stories/layout/dagre-dynamic.stories.ts @@ -10,17 +10,15 @@ import {AnimationService} from '../../../projects/atft/src/lib/animation'; @Component({ template: worldSceneWrapper(` - - - + `) }) -class StorybookDagreComponent { +class StorybookNgForComponent { constructor(private animationService: AnimationService) { this.animationService.start(); @@ -54,9 +52,7 @@ export default { }; -export const DynamicNodes = (args) => ({ - component: StorybookDagreComponent, +export const NgFor = (args) => ({ + component: StorybookNgForComponent, props: args }); - - From a8b0171ac447ce2bc8e6bd1122714cecc2c87a19 Mon Sep 17 00:00:00 2001 From: Mihails Akimenko Date: Sun, 7 Feb 2021 14:21:28 +0200 Subject: [PATCH 3/6] Name to label --- .../layer/layer-actor.component.ts | 4 +- .../layout/dagre-composition.component.ts | 4 +- .../layout/dagre-layout.component.ts | 5 +- .../server/abstract-server-actor.ts | 7 +- .../server/server-stand-actor.component.ts | 3 +- .../all-in-one/infrastructure.stories.ts | 10 +- src/stories/layout/dagre-dynamic.stories.ts | 150 +++++++++++++++++- src/stories/layout/dagre.stories.ts | 6 +- 8 files changed, 169 insertions(+), 20 deletions(-) diff --git a/projects/atft/src/lib/actor/data-center/layer/layer-actor.component.ts b/projects/atft/src/lib/actor/data-center/layer/layer-actor.component.ts index f52b078f..3fccf04f 100644 --- a/projects/atft/src/lib/actor/data-center/layer/layer-actor.component.ts +++ b/projects/atft/src/lib/actor/data-center/layer/layer-actor.component.ts @@ -9,14 +9,14 @@ import { provideParent } from '../../../util'; template: ` - ` }) export class LayerActorComponent extends EmptyComponent { - @Input() name: string; + @Input() label: string; @Input() set width(width: number) { diff --git a/projects/atft/src/lib/actor/data-center/layout/dagre-composition.component.ts b/projects/atft/src/lib/actor/data-center/layout/dagre-composition.component.ts index 4d41cb73..59ff909e 100644 --- a/projects/atft/src/lib/actor/data-center/layout/dagre-composition.component.ts +++ b/projects/atft/src/lib/actor/data-center/layout/dagre-composition.component.ts @@ -8,7 +8,7 @@ import {EmptyComponent} from '../../../object'; template: ` - @@ -16,7 +16,7 @@ import {EmptyComponent} from '../../../object'; }) export class DagreCompositionComponent extends EmptyComponent { - @Input() name: string; + @Input() label: string; private _height: number; @Input() diff --git a/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts b/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts index 15a62b57..75ac446f 100644 --- a/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts +++ b/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts @@ -13,9 +13,7 @@ import {DagreCompositionComponent} from './dagre-composition.component'; @Component({ selector: 'atft-dagre-layout', providers: [provideParent(DagreLayoutComponent)], - template: ` - - ` + template: `` }) export class DagreLayoutComponent extends EmptyComponent implements AfterViewInit, OnChanges, OnDestroy { @@ -65,6 +63,7 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni } protected addDagreChild(object: AbstractObject3D) { + console.log('DagreLayoutComponent.addDagreChild'); if (object instanceof DagreEdgeComponent) { this.addEdge(object); } else { diff --git a/projects/atft/src/lib/actor/data-center/server/abstract-server-actor.ts b/projects/atft/src/lib/actor/data-center/server/abstract-server-actor.ts index b12c6d8b..657cdbe3 100644 --- a/projects/atft/src/lib/actor/data-center/server/abstract-server-actor.ts +++ b/projects/atft/src/lib/actor/data-center/server/abstract-server-actor.ts @@ -15,6 +15,10 @@ export abstract class AbstractServerActor extends EmptyComponent { @Output() deselected = new EventEmitter(); + @Output() + actorClick = new EventEmitter(); + + @Input() svgName: string; @@ -34,8 +38,9 @@ export abstract class AbstractServerActor extends EmptyComponent { } public onClick() { - // console.log('ServerActorComponent.onClick'); + console.log('ServerActorComponent.onClick'); this.color = 0xffa0a0; + this.actorClick.emit(); } } diff --git a/projects/atft/src/lib/actor/data-center/server/server-stand-actor.component.ts b/projects/atft/src/lib/actor/data-center/server/server-stand-actor.component.ts index 47d99f59..86be852d 100644 --- a/projects/atft/src/lib/actor/data-center/server/server-stand-actor.component.ts +++ b/projects/atft/src/lib/actor/data-center/server/server-stand-actor.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import {Component, EventEmitter, Output} from '@angular/core'; import { provideParent } from '../../../util'; import { AbstractServerActor } from './abstract-server-actor'; @@ -30,4 +30,5 @@ import { AbstractServerActor } from './abstract-server-actor'; }) export class ServerStandActorComponent extends AbstractServerActor { + } diff --git a/src/stories/all-in-one/infrastructure.stories.ts b/src/stories/all-in-one/infrastructure.stories.ts index 8f5e070a..23eecce3 100644 --- a/src/stories/all-in-one/infrastructure.stories.ts +++ b/src/stories/all-in-one/infrastructure.stories.ts @@ -11,15 +11,15 @@ import {worldSceneWrapper} from '../scene-wrapper/world-scene-wrapper'; template: worldSceneWrapper(` - + - + - + - + - + diff --git a/src/stories/layout/dagre-dynamic.stories.ts b/src/stories/layout/dagre-dynamic.stories.ts index 01cd20d5..e4a79539 100644 --- a/src/stories/layout/dagre-dynamic.stories.ts +++ b/src/stories/layout/dagre-dynamic.stories.ts @@ -1,10 +1,16 @@ -import {Component} from '@angular/core'; +import {Component, NgModule, Optional, SkipSelf} from '@angular/core'; import {moduleMetadata} from '@storybook/angular'; import {AtftDataCenterActorModule} from '../../../projects/atft/src/lib/actor/data-center'; // NOTE: Do direct import instead of library (allows to watch component and easy to develop) import {AtftModule} from '../../../projects/atft/src/lib/atft.module'; import {worldSceneWrapper} from '../scene-wrapper/world-scene-wrapper'; import {AnimationService} from '../../../projects/atft/src/lib/animation'; +import {provideParent} from '../../../projects/atft/src/lib/util'; +import {AbstractObject3D, EmptyComponent} from '../../../projects/atft/src/lib/object'; +import {RendererService} from '../../../projects/atft/src/lib/renderer'; +import {Router, RouterModule, Routes} from '@angular/router'; +import {BrowserModule} from '@angular/platform-browser'; +import {APP_BASE_HREF} from '@angular/common'; @Component({ @@ -14,7 +20,7 @@ import {AnimationService} from '../../../projects/atft/src/lib/animation'; - + `) }) @@ -35,13 +41,145 @@ class StorybookNgForComponent { } +// ====================================================================== +@Component({ + template: worldSceneWrapper(` + + + + + + +`) +}) +class StorybookRouterMainComponent { + + constructor( + private animationService: AnimationService, + private router: Router + ) { + this.animationService.start(); + } + + public showSpaDetails() { + console.log('*****************************'); + this.router.navigate(['spaDetails']); + } + + public showApiDetails() { + console.log('*****************************'); + this.router.navigate(['apiDetails']); + } + + +} + + +@Component({ + selector: 'app-spa-details-page', + providers: [provideParent(SpaDetailsPageComponent)], + template: ` + + ` +}) +class SpaDetailsPageComponent extends EmptyComponent { + + constructor( + protected rendererService: RendererService, + @SkipSelf() @Optional() protected parent: AbstractObject3D + ) { + super(rendererService, parent); + console.log('SpaDetailsPageComponent.constructor', parent); + } + +} + + +@Component({ + selector: 'app-api-details-page', + providers: [provideParent(ApiDetailsPageComponent)], + template: ` + + + ` +}) +class ApiDetailsPageComponent extends EmptyComponent { + + constructor( + protected rendererService: RendererService, + @SkipSelf() @Optional() protected parent: AbstractObject3D + ) { + super(rendererService, parent); + console.log('ApiDetailsPageComponent.constructor', parent); + } + +} + + +@Component({ + selector: 'app-no-details-page', + providers: [provideParent(NoDetailsPageComponent)], + template: ` + + ` +}) +class NoDetailsPageComponent extends EmptyComponent { + + constructor( + protected rendererService: RendererService, + @SkipSelf() @Optional() protected parent: AbstractObject3D + ) { + super(rendererService, parent); + console.log('NoDetailsPageComponent.constructor', parent); + } + +} + +// ====================================================================== +const routes: Routes = [ + {path: '', redirectTo: '/noDetails', pathMatch: 'full'}, // redirect to `first-component` + {path: 'noDetails', component: NoDetailsPageComponent}, + {path: 'spaDetails', component: SpaDetailsPageComponent}, + {path: 'apiDetails', component: ApiDetailsPageComponent}, + {path: '**', redirectTo: 'noDetails'}, +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule] +}) +class StoryRoutingModule { +} + +@NgModule({ + declarations: [ + StorybookRouterMainComponent, + SpaDetailsPageComponent, + ApiDetailsPageComponent, + NoDetailsPageComponent + ], + imports: [ + BrowserModule, + StoryRoutingModule, + AtftModule, + AtftDataCenterActorModule + ], + providers: [{provide: APP_BASE_HREF, useValue: '/'}], + bootstrap: [StorybookRouterMainComponent] +}) +class StoryModule { +} + + +// ====================================================================== export default { title: 'Layout/Dagre Dynamic', decorators: [ moduleMetadata({ imports: [ AtftModule, - AtftDataCenterActorModule + AtftDataCenterActorModule, + StoryModule ] }) ], @@ -56,3 +194,9 @@ export const NgFor = (args) => ({ component: StorybookNgForComponent, props: args }); + +export const RouterSample = (args) => ({ + component: StorybookRouterMainComponent, + props: args +}); + diff --git a/src/stories/layout/dagre.stories.ts b/src/stories/layout/dagre.stories.ts index 19c861cc..5659c72c 100644 --- a/src/stories/layout/dagre.stories.ts +++ b/src/stories/layout/dagre.stories.ts @@ -41,9 +41,9 @@ class StorybookDagreComponent { [nodesep]="nodesep" [edgesep]="edgesep" [ranksep]="ranksep" [marginx]="marginx" [marginy]="marginy"> - - - + + + From d5371f5ae5ddb9cf502407c1494514a8c6f69300 Mon Sep 17 00:00:00 2001 From: Mihails Akimenko Date: Sun, 7 Feb 2021 17:00:06 +0200 Subject: [PATCH 4/6] Dagre: switch from object.uuid to name --- package-lock.json | 56 ++++++++++- package.json | 1 + .../layout/dagre-edge.component.ts | 11 ++- .../layout/dagre-layout.component.ts | 99 +++++++++---------- .../layout/dagre-node.component.ts | 2 +- .../actor/data-center/layout/dagre-utils.ts | 8 +- .../lib/actor/ux/text/text-actor.component.ts | 2 +- .../atft/src/lib/object/abstract-object-3d.ts | 32 +++--- .../object/connector/abstract-connector.ts | 6 +- .../object/content-projection.component.ts | 2 +- .../lib/object/mesh/plane-mesh.component.ts | 2 +- .../src/lib/raycaster/raycaster.service.ts | 2 +- projects/atft/src/lib/util/provide-parent.ts | 2 +- src/stories/layout/dagre-dynamic.stories.ts | 14 +-- src/stories/layout/dagre.stories.ts | 38 +++---- 15 files changed, 167 insertions(+), 110 deletions(-) diff --git a/package-lock.json b/package-lock.json index aef062e9..fd165aed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2371,6 +2371,12 @@ "requires": { "minimist": "^1.2.0" } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true } } }, @@ -12655,6 +12661,14 @@ "apache-md5": "^1.0.6", "bcryptjs": "^2.3.0", "uuid": "^3.0.0" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } } }, "http-cache-semantics": { @@ -21031,6 +21045,12 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true } } }, @@ -22217,6 +22237,11 @@ "requires": { "ansi-regex": "^4.1.0" } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, @@ -24524,6 +24549,12 @@ "dev": true } } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true } } }, @@ -24594,6 +24625,11 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, @@ -25594,6 +25630,14 @@ "debug": "^4.1.1", "request": "^2.88.2", "uuid": "^3.0.0" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } } }, "universalify": { @@ -25924,9 +25968,9 @@ "dev": true }, "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "validate-npm-package-license": { "version": "3.0.4", @@ -26907,6 +26951,12 @@ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true } } }, diff --git a/package.json b/package.json index 6b371fb8..2254a65c 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "snyk": "^1.445.0", "three": "^0.124.0", "three.meshline": "^1.3.0", + "uuid": "^8.3.2", "zone.js": "~0.11.3" }, "devDependencies": { diff --git a/projects/atft/src/lib/actor/data-center/layout/dagre-edge.component.ts b/projects/atft/src/lib/actor/data-center/layout/dagre-edge.component.ts index a5ea76d1..042741ef 100644 --- a/projects/atft/src/lib/actor/data-center/layout/dagre-edge.component.ts +++ b/projects/atft/src/lib/actor/data-center/layout/dagre-edge.component.ts @@ -1,4 +1,4 @@ -import {Component} from '@angular/core'; +import {Component, Input} from '@angular/core'; import {MeshLineConnectorComponent} from '../../../object'; import {provideParent} from '../../../util'; import * as THREE from 'three'; @@ -14,10 +14,15 @@ export class DagreEdgeComponent extends MeshLineConnectorComponent { public animated = true; + @Input() from: string; + @Input() to: string; protected getLineGeometry(): THREE.BufferGeometry { - if (!this.source || !this.target) { - throw new Error('DagreCompositionComponent: source or target inputs are missing!'); + if (this.source || this.target) { + console.warn('DagreEdgeComponent.getLineGeometry source/target inputs ignored. Please use from/to instead'); + } + if (!this.from || !this.to) { + throw new Error('DagreEdgeComponent: from or to inputs are missing!'); } // console.log('DagreCompositionComponent.getLineGeometry', this.positions); const geometry = new THREE.BufferGeometry(); diff --git a/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts b/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts index 75ac446f..68690188 100644 --- a/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts +++ b/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts @@ -13,7 +13,8 @@ import {DagreCompositionComponent} from './dagre-composition.component'; @Component({ selector: 'atft-dagre-layout', providers: [provideParent(DagreLayoutComponent)], - template: `` + template: ` + ` }) export class DagreLayoutComponent extends EmptyComponent implements AfterViewInit, OnChanges, OnDestroy { @@ -53,15 +54,6 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni } } - public removeChild(object: AbstractObject3D) { - super.removeChild(object); - console.log('DagreLayoutComponent.removeChild'); - this.removeDagreChild(object); - if (this.graph) { - this.layout(); - } - } - protected addDagreChild(object: AbstractObject3D) { console.log('DagreLayoutComponent.addDagreChild'); if (object instanceof DagreEdgeComponent) { @@ -71,46 +63,25 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni } } - protected removeDagreChild(object: AbstractObject3D) { - if (object instanceof DagreEdgeComponent) { - console.log('DagreLayoutComponent.removeDagreChild: Edge'); - const edgeObject: DagreEdgeComponent = object; - this.graphModel.edges = this.graphModel.edges.filter(i => i.uuid !== edgeObject.getObject().uuid); - } else { - console.log('DagreLayoutComponent.removeDagreChild: Node'); - this.graphModel.nodes = this.graphModel.nodes.filter(i => i.id !== object.getObject().uuid); - - if (object instanceof DagreNodeComponent) { - const node: DagreNodeComponent = object; - if (node.composition) { - this.graphModel.composition = this.graphModel.composition.filter(i => i.child !== node.getObject().uuid); - } - } - - } - } - - protected addEdge(edge: DagreEdgeComponent) { - // console.log('DagreLayoutComponent.addEdge', edge); + console.log('DagreLayoutComponent.addEdge', edge); const edgeObject: DagreEdgeComponent = edge; - if (edgeObject.source && edgeObject.source.getObject() && edgeObject.target && edgeObject.target.getObject()) { + if (edgeObject.from && edgeObject.to) { this.graphModel.edges.push({ - uuid: edgeObject.getObject().uuid, - from: edgeObject.source.getObject().uuid, - to: edgeObject.target.getObject().uuid + name: edgeObject.name, + from: edgeObject.from, + to: edgeObject.to }); } else { console.warn('DagreLayoutComponent.addChild: edge source/target is undefined'); } } - protected addNode(object: AbstractObject3D) { - // console.log('DagreLayoutComponent.addNode', object); + console.log('DagreLayoutComponent.addNode', object); this.graphModel.nodes.push({ - id: object.getObject().uuid, - label: object.getObject().uuid, + name: object.name, + label: object.name, }); if (object instanceof DagreNodeComponent) { @@ -118,14 +89,42 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni if (node.composition) { // console.log('DagreLayoutComponent.addNode to composition', node.composition); this.graphModel.composition.push({ - parent: node.composition.getObject().uuid, - child: node.getObject().uuid + parent: node.composition, + child: node.name }); } } } + public removeChild(object: AbstractObject3D) { + super.removeChild(object); + console.log('DagreLayoutComponent.removeChild'); + this.removeDagreChild(object); + if (this.graph) { + this.layout(); + } + } + + protected removeDagreChild(object: AbstractObject3D) { + if (object instanceof DagreEdgeComponent) { + console.log('DagreLayoutComponent.removeDagreChild: Edge'); + const edgeObject: DagreEdgeComponent = object; + this.graphModel.edges = this.graphModel.edges.filter(i => i.name !== edgeObject.name); + } else { + console.log('DagreLayoutComponent.removeDagreChild: Node'); + this.graphModel.nodes = this.graphModel.nodes.filter(i => i.name !== object.name); + + if (object instanceof DagreNodeComponent) { + const node: DagreNodeComponent = object; + if (node.composition) { + this.graphModel.composition = this.graphModel.composition.filter(i => i.child !== node.name); + } + } + + } + } + ngAfterViewInit() { super.ngAfterViewInit(); this.layout(); @@ -152,13 +151,13 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni } protected syncGraphNodes(g: dagre.graphlib.Graph) { - // console.log('DagreLayoutComponent.syncGraphNodes'); - g.nodes().forEach((uuid) => { - // console.log('Node ' + uuid + ': ' + JSON.stringify(g.node(uuid))); - const object: AbstractObject3D = this.findByUuid(uuid); + console.log('DagreLayoutComponent.syncGraphNodes'); + g.nodes().forEach((name) => { + console.log('Node ' + name + ': ' + JSON.stringify(g.node(name))); + const object: AbstractObject3D = this.findByName(name); if (object) { - const node = g.node(uuid); + const node = g.node(name); // console.log('DagreLayoutComponent.layout: Update position', node); object.translateX = node.x; @@ -174,7 +173,7 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni } } else { - console.warn('DagreLayoutComponent.layout: Object not found by uuid', uuid); + console.warn('DagreLayoutComponent.layout: Object not found by name', name); } }); } @@ -184,7 +183,7 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni g.edges().forEach((e) => { const edge: dagre.GraphEdge = g.edge(e); // console.log('DagreLayoutComponent.syncGraphEdges: edge', edge); - const object: AbstractObject3D = this.findByUuid(edge.uuid); + const object: AbstractObject3D = this.findByName(edge.name); if (object && object instanceof DagreEdgeComponent) { const edgeComponent: DagreEdgeComponent = object; edgeComponent.positions = []; @@ -198,7 +197,7 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni }); edgeComponent.updateLineGeometry(); } else { - console.warn('DagreLayoutComponent.layout: Object not found by uuid', e.name); + console.warn('DagreLayoutComponent.layout: Object not found by name', e.name); } }); } @@ -212,7 +211,7 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni } public ngOnChanges(changes: SimpleChanges) { - // console.log('AbstractObject3D.ngOnChanges', this.uuid); + // console.log('AbstractObject3D.ngOnChanges', this.name); if (!this.object) { return; } diff --git a/projects/atft/src/lib/actor/data-center/layout/dagre-node.component.ts b/projects/atft/src/lib/actor/data-center/layout/dagre-node.component.ts index 94dc82a6..bb6ca35b 100644 --- a/projects/atft/src/lib/actor/data-center/layout/dagre-node.component.ts +++ b/projects/atft/src/lib/actor/data-center/layout/dagre-node.component.ts @@ -9,7 +9,7 @@ import {provideParent} from '../../../util'; }) export class DagreNodeComponent extends EmptyComponent { - @Input() composition: AbstractObject3D; + @Input() composition: string; @Input() translateZ = 1; } diff --git a/projects/atft/src/lib/actor/data-center/layout/dagre-utils.ts b/projects/atft/src/lib/actor/data-center/layout/dagre-utils.ts index ec05ccc1..e1b22387 100644 --- a/projects/atft/src/lib/actor/data-center/layout/dagre-utils.ts +++ b/projects/atft/src/lib/actor/data-center/layout/dagre-utils.ts @@ -1,12 +1,12 @@ import * as dagre from 'dagre'; export interface Node { - id: string; + name: string; label: string; } export interface Edge { - uuid: string; + name: string; from: string; to: string; } @@ -52,7 +52,7 @@ export class DagreUtils { public static updateNodes(g: dagre.graphlib.Graph, model: GraphModel) { if (model.nodes) { model.nodes.forEach((node: Node) => { - g.setNode(node.id, {label: node.label, width: 15, height: 15}); + g.setNode(node.name, {label: node.label, width: 15, height: 15}); }); } } @@ -60,7 +60,7 @@ export class DagreUtils { public static updateEdges(g: dagre.graphlib.Graph, model: GraphModel) { if (model.edges) { model.edges.forEach((edge: Edge) => { - g.setEdge(edge.from, edge.to, {uuid: edge.uuid}); + g.setEdge(edge.from, edge.to, {name: edge.name}); }); } } diff --git a/projects/atft/src/lib/actor/ux/text/text-actor.component.ts b/projects/atft/src/lib/actor/ux/text/text-actor.component.ts index 1d9b5a5f..51644f91 100644 --- a/projects/atft/src/lib/actor/ux/text/text-actor.component.ts +++ b/projects/atft/src/lib/actor/ux/text/text-actor.component.ts @@ -116,7 +116,7 @@ export class TextActorComponent extends EmptyComponent implements AfterViewInit, public ngOnChanges(changes: SimpleChanges) { - // console.log('AbstractObject3D.ngOnChanges', this.uuid); + // console.log('AbstractObject3D.ngOnChanges', this.name); if (!this.object) { return; } diff --git a/projects/atft/src/lib/object/abstract-object-3d.ts b/projects/atft/src/lib/object/abstract-object-3d.ts index 7f6b8bc6..42ae24cd 100644 --- a/projects/atft/src/lib/object/abstract-object-3d.ts +++ b/projects/atft/src/lib/object/abstract-object-3d.ts @@ -13,6 +13,7 @@ import { } from '@angular/core'; import * as THREE from 'three'; import {RendererService} from '../renderer/renderer.service'; +import {v4 as uuidv4} from 'uuid'; @Directive() export abstract class AbstractObject3D implements AfterViewInit, OnChanges, OnDestroy, OnInit { @@ -36,8 +37,7 @@ export abstract class AbstractObject3D implements Afte @Input() scaleY = 1; @Input() scaleZ = 1; - - @Input() name: string; + @Input() name: string = uuidv4(); // if not provided, then auto-generate @Input() layer = 0; @@ -51,11 +51,11 @@ export abstract class AbstractObject3D implements Afte protected rendererService: RendererService, @SkipSelf() @Optional() protected parent: AbstractObject3D ) { - // console.log('AbstractObject3D.constructor', this.uuid); + // console.log('AbstractObject3D.constructor', this.name); } public ngOnChanges(changes: SimpleChanges) { - // console.log('AbstractObject3D.ngOnChanges', this.uuid); + // console.log('AbstractObject3D.ngOnChanges', this.name); if (!this.object) { return; } @@ -84,7 +84,7 @@ export abstract class AbstractObject3D implements Afte } public ngOnDestroy() { - // console.log('AbstractObject3D.OnDestroy', this.uuid); + // console.log('AbstractObject3D.OnDestroy', this.name); if (this.object && this.object.parent) { this.parent.removeChild(this); // this.object.parent.remove(this.object); @@ -143,9 +143,9 @@ export abstract class AbstractObject3D implements Afte } public addChild(object: AbstractObject3D): void { - // (this.constructor.uuid + ' addChild ' + object, this.object); + // (this.constructor.name + ' addChild ' + object, this.object); if (this.object) { - // console.log(this.constructor.uuid + ' add child ' + object); + // console.log(this.constructor.name + ' add child ' + object); this.childlren.push(object); this.object.add(object.getObject()); if (this.rendererService) { @@ -181,21 +181,21 @@ export abstract class AbstractObject3D implements Afte this.updateParent(); } - public findByUuid(uuid: string) { - // console.log('AbstractObject3D.findByUuid: Searching uuid', uuid); - // console.log('AbstractObject3D.findByUuid: children', this.childlren); - // const res = this.childlren.filter(i => i.object && i.object.uuid === uuid)[0]; - const res = this.getNodeByUuid(this, uuid); - // console.log('AbstractObject3D.findByUuid: result', res); + public findByName(name: string) { + // console.log('AbstractObject3D.findByName: Searching name', name); + // console.log('AbstractObject3D.findByName: children', this.childlren); + // const res = this.childlren.filter(i => i.object && i.object.name === name)[0]; + const res = this.recursionByName(this, name); + // console.log('AbstractObject3D.findByName: result', res); return res; } - protected getNodeByUuid(currentNode: AbstractObject3D, uuid) { - if (currentNode.object && currentNode.object.uuid === uuid) { + protected recursionByName(currentNode: AbstractObject3D, name) { + if (currentNode.object && currentNode.name === name) { return currentNode; } let node; - currentNode.childlren.some(child => node = this.getNodeByUuid(child, uuid)); + currentNode.childlren.some(child => node = this.recursionByName(child, name)); return node; } diff --git a/projects/atft/src/lib/object/connector/abstract-connector.ts b/projects/atft/src/lib/object/connector/abstract-connector.ts index 3f33ffa6..1f9b150c 100644 --- a/projects/atft/src/lib/object/connector/abstract-connector.ts +++ b/projects/atft/src/lib/object/connector/abstract-connector.ts @@ -1,4 +1,4 @@ -import { Input, Directive } from '@angular/core'; +import {Directive, Input} from '@angular/core'; import * as THREE from 'three'; import {AbstractObject3D} from '../abstract-object-3d'; @@ -13,7 +13,9 @@ export abstract class AbstractConnector extends Abstra protected newObject3DInstance(): T { const mesh = this.createConnectorObject(); - this.watchObjects(); + if (this.source && this.target) { + this.watchObjects(); + } return mesh; } diff --git a/projects/atft/src/lib/object/content-projection.component.ts b/projects/atft/src/lib/object/content-projection.component.ts index 481d7415..70c33278 100644 --- a/projects/atft/src/lib/object/content-projection.component.ts +++ b/projects/atft/src/lib/object/content-projection.component.ts @@ -25,7 +25,7 @@ export class ContentProjectionComponent extends EmptyComponent { if (this.contentProjection) { this.contentProjection.addChild(object); } else { - console.error('ContentProjectionComponent error: #contentProjection id not found! Embedded child object in "ng-content" can not be attached to parentScene object'); + console.error('ContentProjectionComponent error: #contentProjection name not found! Embedded child object in "ng-content" can not be attached to parentScene object'); } } diff --git a/projects/atft/src/lib/object/mesh/plane-mesh.component.ts b/projects/atft/src/lib/object/mesh/plane-mesh.component.ts index d4b8831e..be713e6b 100644 --- a/projects/atft/src/lib/object/mesh/plane-mesh.component.ts +++ b/projects/atft/src/lib/object/mesh/plane-mesh.component.ts @@ -53,7 +53,7 @@ export class PlaneMeshComponent extends AbstractMesh implements OnChanges { public ngOnChanges(changes: SimpleChanges) { - // console.log('AbstractObject3D.ngOnChanges', this.uuid); + // console.log('AbstractObject3D.ngOnChanges', this.name); if (!this.object) { return; } diff --git a/projects/atft/src/lib/raycaster/raycaster.service.ts b/projects/atft/src/lib/raycaster/raycaster.service.ts index 184cb33a..67b981fc 100644 --- a/projects/atft/src/lib/raycaster/raycaster.service.ts +++ b/projects/atft/src/lib/raycaster/raycaster.service.ts @@ -72,7 +72,7 @@ export class RaycasterService implements OnDestroy { } public addGroup(group: AbstractObject3D) { - // console.log('RaycasterService.addGroup', group.uuid, group); + // console.log('RaycasterService.addGroup', group.name, group); this.groups.push(group); } diff --git a/projects/atft/src/lib/util/provide-parent.ts b/projects/atft/src/lib/util/provide-parent.ts index ae7f0de6..c77f9df8 100644 --- a/projects/atft/src/lib/util/provide-parent.ts +++ b/projects/atft/src/lib/util/provide-parent.ts @@ -1,5 +1,5 @@ // https://angular.io/guide/dependency-injection-navtree -// Helper method to provide the current component instance in the uuid of a `parentType`. +// Helper method to provide the current component instance in the name of a `parentType`. // The `parentType` defaults to `Parent` when omitting the second parameter. import { forwardRef } from '@angular/core'; import { AbstractObject3D } from '../object/abstract-object-3d'; diff --git a/src/stories/layout/dagre-dynamic.stories.ts b/src/stories/layout/dagre-dynamic.stories.ts index e4a79539..c6f6dc1b 100644 --- a/src/stories/layout/dagre-dynamic.stories.ts +++ b/src/stories/layout/dagre-dynamic.stories.ts @@ -16,11 +16,11 @@ import {APP_BASE_HREF} from '@angular/common'; @Component({ template: worldSceneWrapper(` - + - + `) }) @@ -45,9 +45,9 @@ class StorybookNgForComponent { @Component({ template: worldSceneWrapper(` - - - + + + `) @@ -99,8 +99,8 @@ class SpaDetailsPageComponent extends EmptyComponent { selector: 'app-api-details-page', providers: [provideParent(ApiDetailsPageComponent)], template: ` - - + + ` }) class ApiDetailsPageComponent extends EmptyComponent { diff --git a/src/stories/layout/dagre.stories.ts b/src/stories/layout/dagre.stories.ts index 5659c72c..943090df 100644 --- a/src/stories/layout/dagre.stories.ts +++ b/src/stories/layout/dagre.stories.ts @@ -13,16 +13,16 @@ import {AnimationService} from '../../../projects/atft/src/lib/animation'; [nodesep]="nodesep" [edgesep]="edgesep" [ranksep]="ranksep" [marginx]="marginx" [marginy]="marginy"> - - - - - + + + + + - - - - + + + + `) @@ -41,29 +41,29 @@ class StorybookDagreComponent { [nodesep]="nodesep" [edgesep]="edgesep" [ranksep]="ranksep" [marginx]="marginx" [marginy]="marginy"> - - - + + + - + - + - + - + - - - + + + From f020599a0f181748f5b815994897e0bf45cac636 Mon Sep 17 00:00:00 2001 From: Mihails Akimenko Date: Sun, 7 Feb 2021 19:17:22 +0200 Subject: [PATCH 5/6] Dagre: deepScan v1 --- .../layout/dagre-layout.component.ts | 48 ++++++++++---- .../layout/dagre-node.component.ts | 17 ++++- .../actor/data-center/layout/dagre-utils.ts | 13 ++-- src/stories/layout/dagre-loop.stories.ts | 60 ++++++++++++++++++ ...amic.stories.ts => dagre-route.stories.ts} | 63 +++++++------------ src/stories/layout/dagre.stories.ts | 20 ++++-- 6 files changed, 159 insertions(+), 62 deletions(-) create mode 100644 src/stories/layout/dagre-loop.stories.ts rename src/stories/layout/{dagre-dynamic.stories.ts => dagre-route.stories.ts} (76%) diff --git a/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts b/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts index 68690188..019ec2e3 100644 --- a/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts +++ b/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts @@ -26,7 +26,7 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni @Input() marginx = 0; @Input() marginy = 0; @Input() ranker = 'network-simplex'; - + @Input() deepScan = false; protected graphModel: GraphModel; protected graph: dagre.graphlib.Graph; @@ -54,34 +54,51 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni } } - protected addDagreChild(object: AbstractObject3D) { + protected addDagreChild(object: AbstractObject3D, deepRoot?: AbstractObject3D) { console.log('DagreLayoutComponent.addDagreChild'); if (object instanceof DagreEdgeComponent) { - this.addEdge(object); - } else { - this.addNode(object); + this.addEdge(object, deepRoot); + } else if (object instanceof DagreNodeComponent + || object instanceof DagreCompositionComponent + ) { + this.addNode(object, deepRoot); + } + + if (this.deepScan) { + if (object.getChildren()) { + for (const i of object.getChildren()) { + if (i instanceof DagreNodeComponent + || i instanceof DagreEdgeComponent + || i instanceof DagreCompositionComponent) { + console.log('DagreLayoutComponent.addDagreChild: ', i.name); + this.addDagreChild(i, object); + } + } + } } } - protected addEdge(edge: DagreEdgeComponent) { + protected addEdge(edge: DagreEdgeComponent, deepRoot?: AbstractObject3D) { console.log('DagreLayoutComponent.addEdge', edge); const edgeObject: DagreEdgeComponent = edge; if (edgeObject.from && edgeObject.to) { this.graphModel.edges.push({ name: edgeObject.name, from: edgeObject.from, - to: edgeObject.to + to: edgeObject.to, + deepRoot: deepRoot?.name }); } else { console.warn('DagreLayoutComponent.addChild: edge source/target is undefined'); } } - protected addNode(object: AbstractObject3D) { + protected addNode(object: AbstractObject3D, deepRoot?: AbstractObject3D) { console.log('DagreLayoutComponent.addNode', object); this.graphModel.nodes.push({ name: object.name, label: object.name, + deepRoot: deepRoot?.name }); if (object instanceof DagreNodeComponent) { @@ -90,7 +107,8 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni // console.log('DagreLayoutComponent.addNode to composition', node.composition); this.graphModel.composition.push({ parent: node.composition, - child: node.name + child: node.name, + deepRoot: deepRoot?.name }); } } @@ -101,6 +119,14 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni super.removeChild(object); console.log('DagreLayoutComponent.removeChild'); this.removeDagreChild(object); + + if (this.deepScan) { + console.log('DagreLayoutComponent.removeChild deepScan for', object.name); + this.graphModel.nodes = this.graphModel.nodes.filter(i => i.deepRoot !== object.name); + this.graphModel.edges = this.graphModel.edges.filter(i => i.deepRoot !== object.name); + this.graphModel.composition = this.graphModel.composition.filter(i => i.deepRoot !== object.name); + } + if (this.graph) { this.layout(); } @@ -108,11 +134,11 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni protected removeDagreChild(object: AbstractObject3D) { if (object instanceof DagreEdgeComponent) { - console.log('DagreLayoutComponent.removeDagreChild: Edge'); + console.log('DagreLayoutComponent.removeDagreChild: Edge', object.name); const edgeObject: DagreEdgeComponent = object; this.graphModel.edges = this.graphModel.edges.filter(i => i.name !== edgeObject.name); } else { - console.log('DagreLayoutComponent.removeDagreChild: Node'); + console.log('DagreLayoutComponent.removeDagreChild: Node', object.name); this.graphModel.nodes = this.graphModel.nodes.filter(i => i.name !== object.name); if (object instanceof DagreNodeComponent) { diff --git a/projects/atft/src/lib/actor/data-center/layout/dagre-node.component.ts b/projects/atft/src/lib/actor/data-center/layout/dagre-node.component.ts index bb6ca35b..64f8adfd 100644 --- a/projects/atft/src/lib/actor/data-center/layout/dagre-node.component.ts +++ b/projects/atft/src/lib/actor/data-center/layout/dagre-node.component.ts @@ -1,6 +1,8 @@ -import {Component, Input} from '@angular/core'; +import {Component, Injector, Input, Optional, SkipSelf} from '@angular/core'; import {AbstractObject3D, EmptyComponent} from '../../../object'; import {provideParent} from '../../../util'; +import {RendererService} from '../../../renderer'; +import {DagreLayoutComponent} from './dagre-layout.component'; @Component({ selector: 'atft-dagre-node', @@ -12,4 +14,17 @@ export class DagreNodeComponent extends EmptyComponent { @Input() composition: string; @Input() translateZ = 1; + + constructor( + protected rendererService: RendererService, + @SkipSelf() @Optional() protected parent: AbstractObject3D, + protected injector: Injector + ) { + super(rendererService, parent); + + const dagreLayout: DagreLayoutComponent = this.injector.get(DagreLayoutComponent); + console.log('DagreNodeComponent.constructor dagreLayout', dagreLayout); + } + + } diff --git a/projects/atft/src/lib/actor/data-center/layout/dagre-utils.ts b/projects/atft/src/lib/actor/data-center/layout/dagre-utils.ts index e1b22387..293cdfa4 100644 --- a/projects/atft/src/lib/actor/data-center/layout/dagre-utils.ts +++ b/projects/atft/src/lib/actor/data-center/layout/dagre-utils.ts @@ -1,17 +1,21 @@ import * as dagre from 'dagre'; -export interface Node { +export interface DeepRoot { + deepRoot?: string; +} + +export interface Node extends DeepRoot { name: string; label: string; } -export interface Edge { +export interface Edge extends DeepRoot { name: string; from: string; to: string; } -export interface Composition { +export interface Composition extends DeepRoot { parent: string; child: string; } @@ -44,7 +48,8 @@ export class DagreUtils { this.updateGraph(g, model); - // console.log('DagreUtils.layout', g); + console.log('DagreUtils.layout model', model); + console.log('DagreUtils.layout', g); dagre.layout(g); return g; } diff --git a/src/stories/layout/dagre-loop.stories.ts b/src/stories/layout/dagre-loop.stories.ts new file mode 100644 index 00000000..0932aace --- /dev/null +++ b/src/stories/layout/dagre-loop.stories.ts @@ -0,0 +1,60 @@ +import {Component} from '@angular/core'; +import {moduleMetadata} from '@storybook/angular'; +import {AtftDataCenterActorModule} from '../../../projects/atft/src/lib/actor/data-center'; +// NOTE: Do direct import instead of library (allows to watch component and easy to develop) +import {AtftModule} from '../../../projects/atft/src/lib/atft.module'; +import {worldSceneWrapper} from '../scene-wrapper/world-scene-wrapper'; +import {AnimationService} from '../../../projects/atft/src/lib/animation'; + + +@Component({ + template: worldSceneWrapper(` + + + + + + + +`) +}) +class StorybookLoopComponent { + + constructor(private animationService: AnimationService) { + this.animationService.start(); + } + + numDatabases = 1; + + fakeArray(length: number): Array { + if (length >= 0) { + return new Array(length); + } + } + +} + +// ====================================================================== +export default { + title: 'Layout/Loop', + decorators: [ + moduleMetadata({ + imports: [ + AtftModule, + AtftDataCenterActorModule + ] + }) + ], + args: { + numDatabases: 1 + } + +}; + + +export const Loop = (args) => ({ + component: StorybookLoopComponent, + props: args +}); + + diff --git a/src/stories/layout/dagre-dynamic.stories.ts b/src/stories/layout/dagre-route.stories.ts similarity index 76% rename from src/stories/layout/dagre-dynamic.stories.ts rename to src/stories/layout/dagre-route.stories.ts index c6f6dc1b..86f88a18 100644 --- a/src/stories/layout/dagre-dynamic.stories.ts +++ b/src/stories/layout/dagre-route.stories.ts @@ -13,41 +13,19 @@ import {BrowserModule} from '@angular/platform-browser'; import {APP_BASE_HREF} from '@angular/common'; -@Component({ - template: worldSceneWrapper(` - - - - - - - -`) -}) -class StorybookNgForComponent { - - constructor(private animationService: AnimationService) { - this.animationService.start(); - } - - numDatabases = 1; - - fakeArray(length: number): Array { - if (length >= 0) { - return new Array(length); - } - } - -} - - // ====================================================================== @Component({ template: worldSceneWrapper(` - - - + + + + + + + + + `) @@ -79,7 +57,10 @@ class StorybookRouterMainComponent { selector: 'app-spa-details-page', providers: [provideParent(SpaDetailsPageComponent)], template: ` - + + + + ` }) class SpaDetailsPageComponent extends EmptyComponent { @@ -99,8 +80,14 @@ class SpaDetailsPageComponent extends EmptyComponent { selector: 'app-api-details-page', providers: [provideParent(ApiDetailsPageComponent)], template: ` - - + + + + + + + + ` }) class ApiDetailsPageComponent extends EmptyComponent { @@ -173,7 +160,7 @@ class StoryModule { // ====================================================================== export default { - title: 'Layout/Dagre Dynamic', + title: 'Layout/Router Sample', decorators: [ moduleMetadata({ imports: [ @@ -189,12 +176,6 @@ export default { }; - -export const NgFor = (args) => ({ - component: StorybookNgForComponent, - props: args -}); - export const RouterSample = (args) => ({ component: StorybookRouterMainComponent, props: args diff --git a/src/stories/layout/dagre.stories.ts b/src/stories/layout/dagre.stories.ts index 943090df..00ea919c 100644 --- a/src/stories/layout/dagre.stories.ts +++ b/src/stories/layout/dagre.stories.ts @@ -13,11 +13,21 @@ import {AnimationService} from '../../../projects/atft/src/lib/animation'; [nodesep]="nodesep" [edgesep]="edgesep" [ranksep]="ranksep" [marginx]="marginx" [marginy]="marginy"> - - - - - + + + + + + + + + + + + + + + From 2bb0095ff86f15a35b54e30c3f497a18bf7247ab Mon Sep 17 00:00:00 2001 From: Mihails Akimenko Date: Sun, 7 Feb 2021 20:33:34 +0200 Subject: [PATCH 6/6] Fix add/remove --- .../layout/dagre-composition.component.ts | 75 +++++++++- .../layout/dagre-edge.component.ts | 82 ++++++++++- .../layout/dagre-layout.component.ts | 130 ++---------------- .../layout/dagre-node.component.ts | 69 +++++++++- .../actor/data-center/layout/dagre-utils.ts | 14 +- .../atft/src/lib/object/abstract-object-3d.ts | 7 +- 6 files changed, 233 insertions(+), 144 deletions(-) diff --git a/projects/atft/src/lib/actor/data-center/layout/dagre-composition.component.ts b/projects/atft/src/lib/actor/data-center/layout/dagre-composition.component.ts index 59ff909e..4cba78ef 100644 --- a/projects/atft/src/lib/actor/data-center/layout/dagre-composition.component.ts +++ b/projects/atft/src/lib/actor/data-center/layout/dagre-composition.component.ts @@ -1,6 +1,8 @@ -import {Component, EventEmitter, Input, Output} from '@angular/core'; +import {Component, EventEmitter, Injector, Input, OnDestroy, OnInit, Optional, Output, SkipSelf} from '@angular/core'; import {provideParent} from '../../../util'; -import {EmptyComponent} from '../../../object'; +import {AbstractObject3D, EmptyComponent} from '../../../object'; +import {RendererService} from '../../../renderer'; +import {DagreLayoutComponent} from './dagre-layout.component'; @Component({ selector: 'atft-dagre-composition', @@ -14,16 +16,17 @@ import {EmptyComponent} from '../../../object'; ` }) -export class DagreCompositionComponent extends EmptyComponent { +export class DagreCompositionComponent extends EmptyComponent implements OnInit, OnDestroy { @Input() label: string; private _height: number; @Input() - set height(hight: number) { - this._height = hight; + set height(height: number) { + this._height = height; this.translateLabelY = this._height / 2 - 3; } + get height(): number { return this._height; } @@ -33,9 +36,22 @@ export class DagreCompositionComponent extends EmptyComponent { @Output() selected = new EventEmitter(); @Output() deselected = new EventEmitter(); - color = 0xA0A0A0; + public color = 0xA0A0A0; + public translateLabelY: number; + protected dagreLayout: DagreLayoutComponent; + + constructor( + protected rendererService: RendererService, + @SkipSelf() @Optional() protected parent: AbstractObject3D, + protected injector: Injector + ) { + super(rendererService, parent); - translateLabelY: number; + this.dagreLayout = this.injector.get(DagreLayoutComponent); + if (!this.dagreLayout) { + console.warn('DagreCompositionComponent.constructor: atft-dagre-layout not found!'); + } + } public onSelected() { this.color = 0xA4A4A4; @@ -49,4 +65,49 @@ export class DagreCompositionComponent extends EmptyComponent { this.color = 0xA0A0A0; } + ngOnInit() { + super.ngOnInit(); + this.addNode(); + } + + + protected addNode() { + if (this.dagreLayout && this.dagreLayout.getGraphModel()) { + // console.log('DagreCompositionComponent.addNode', this.name); + + // Register as layout children + this.dagreLayout.getChildren().push(this); + + // Create Graph Node + this.dagreLayout.getGraphModel().nodes.push({ + name: this.name, + label: this.name + }); + + // Update Graph Layout + this.dagreLayout.refreshLayout(); + } + } + + + ngOnDestroy() { + super.ngOnDestroy(); + this.removeNode(); + } + + protected removeNode() { + if (this.dagreLayout && this.dagreLayout.getGraphModel()) { + // console.log('DagreCompositionComponent.removeNode', this.name); + + // Remove from layout + this.dagreLayout.removeChildByName(this.name); + + // Remove from model + this.dagreLayout.getGraphModel().nodes = this.dagreLayout.getGraphModel().nodes.filter(i => i.name !== this.name); + + // Update Graph Layout + this.dagreLayout.refreshLayout(); + } + } + } diff --git a/projects/atft/src/lib/actor/data-center/layout/dagre-edge.component.ts b/projects/atft/src/lib/actor/data-center/layout/dagre-edge.component.ts index 042741ef..661f9ecb 100644 --- a/projects/atft/src/lib/actor/data-center/layout/dagre-edge.component.ts +++ b/projects/atft/src/lib/actor/data-center/layout/dagre-edge.component.ts @@ -1,21 +1,40 @@ -import {Component, Input} from '@angular/core'; -import {MeshLineConnectorComponent} from '../../../object'; +import {Component, Injector, Input, OnDestroy, OnInit, Optional, SkipSelf} from '@angular/core'; +import {AbstractObject3D, MeshLineConnectorComponent} from '../../../object'; import {provideParent} from '../../../util'; import * as THREE from 'three'; +import {RendererService} from '../../../renderer'; +import {DagreLayoutComponent} from './dagre-layout.component'; +import {AnimationService} from '../../../animation'; @Component({ selector: 'atft-dagre-edge', providers: [provideParent(DagreEdgeComponent)], template: '' }) -export class DagreEdgeComponent extends MeshLineConnectorComponent { +export class DagreEdgeComponent extends MeshLineConnectorComponent implements OnInit, OnDestroy { + + @Input() animated = true; + @Input() from: string; + @Input() to: string; public positions: Array; + protected dagreLayout: DagreLayoutComponent; - public animated = true; - @Input() from: string; - @Input() to: string; + constructor( + protected rendererService: RendererService, + @SkipSelf() @Optional() protected parent: AbstractObject3D, + protected animationService: AnimationService, + protected injector: Injector + ) { + super(rendererService, parent, animationService); + + this.dagreLayout = this.injector.get(DagreLayoutComponent); + if (!this.dagreLayout) { + console.warn('DagreEdgeComponent.constructor: atft-dagre-layout not found!'); + } + } + protected getLineGeometry(): THREE.BufferGeometry { if (this.source || this.target) { @@ -30,4 +49,55 @@ export class DagreEdgeComponent extends MeshLineConnectorComponent { return geometry; } + + ngOnInit() { + super.ngOnInit(); + this.addEdge(); + } + + + protected addEdge() { + if (this.dagreLayout && this.dagreLayout.getGraphModel()) { + // console.log('DagreEdgeComponent.addEdge', this.name); + + // Register as layout children + this.dagreLayout.getChildren().push(this); + + // Create Graph edge: + if (this.from && this.to) { + this.dagreLayout.getGraphModel().edges.push({ + name: this.name, + from: this.from, + to: this.to + }); + } else { + console.warn('DagreEdgeComponent.addChild: edge source/target is undefined'); + } + + // Update Graph Layout + this.dagreLayout.refreshLayout(); + } + } + + ngOnDestroy() { + super.ngOnDestroy(); + this.removeEdge(); + } + + protected removeEdge() { + if (this.dagreLayout && this.dagreLayout.getGraphModel()) { + // console.log('DagreNodeComponent.removeNode', this.name); + + // Remove from layout + this.dagreLayout.removeChildByName(this.name); + + // Remove from model + this.dagreLayout.getGraphModel().edges = this.dagreLayout.getGraphModel().edges.filter(i => i.name !== this.name); + + // Update Graph Layout + this.dagreLayout.refreshLayout(); + } + } + + } diff --git a/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts b/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts index 019ec2e3..b011d475 100644 --- a/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts +++ b/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.ts @@ -6,7 +6,6 @@ import {RendererService} from '../../../renderer'; import {AbstractObject3D} from '../../../object'; import {DagreEdgeComponent} from './dagre-edge.component'; import * as dagre from 'dagre'; -import {DagreNodeComponent} from './dagre-node.component'; import {DagreCompositionComponent} from './dagre-composition.component'; @@ -46,111 +45,6 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni }; } - public addChild(object: AbstractObject3D): void { - super.addChild(object); - this.addDagreChild(object); - if (this.graph) { - this.layout(); - } - } - - protected addDagreChild(object: AbstractObject3D, deepRoot?: AbstractObject3D) { - console.log('DagreLayoutComponent.addDagreChild'); - if (object instanceof DagreEdgeComponent) { - this.addEdge(object, deepRoot); - } else if (object instanceof DagreNodeComponent - || object instanceof DagreCompositionComponent - ) { - this.addNode(object, deepRoot); - } - - if (this.deepScan) { - if (object.getChildren()) { - for (const i of object.getChildren()) { - if (i instanceof DagreNodeComponent - || i instanceof DagreEdgeComponent - || i instanceof DagreCompositionComponent) { - console.log('DagreLayoutComponent.addDagreChild: ', i.name); - this.addDagreChild(i, object); - } - } - } - } - } - - protected addEdge(edge: DagreEdgeComponent, deepRoot?: AbstractObject3D) { - console.log('DagreLayoutComponent.addEdge', edge); - const edgeObject: DagreEdgeComponent = edge; - if (edgeObject.from && edgeObject.to) { - this.graphModel.edges.push({ - name: edgeObject.name, - from: edgeObject.from, - to: edgeObject.to, - deepRoot: deepRoot?.name - }); - } else { - console.warn('DagreLayoutComponent.addChild: edge source/target is undefined'); - } - } - - protected addNode(object: AbstractObject3D, deepRoot?: AbstractObject3D) { - console.log('DagreLayoutComponent.addNode', object); - this.graphModel.nodes.push({ - name: object.name, - label: object.name, - deepRoot: deepRoot?.name - }); - - if (object instanceof DagreNodeComponent) { - const node: DagreNodeComponent = object; - if (node.composition) { - // console.log('DagreLayoutComponent.addNode to composition', node.composition); - this.graphModel.composition.push({ - parent: node.composition, - child: node.name, - deepRoot: deepRoot?.name - }); - } - } - - } - - public removeChild(object: AbstractObject3D) { - super.removeChild(object); - console.log('DagreLayoutComponent.removeChild'); - this.removeDagreChild(object); - - if (this.deepScan) { - console.log('DagreLayoutComponent.removeChild deepScan for', object.name); - this.graphModel.nodes = this.graphModel.nodes.filter(i => i.deepRoot !== object.name); - this.graphModel.edges = this.graphModel.edges.filter(i => i.deepRoot !== object.name); - this.graphModel.composition = this.graphModel.composition.filter(i => i.deepRoot !== object.name); - } - - if (this.graph) { - this.layout(); - } - } - - protected removeDagreChild(object: AbstractObject3D) { - if (object instanceof DagreEdgeComponent) { - console.log('DagreLayoutComponent.removeDagreChild: Edge', object.name); - const edgeObject: DagreEdgeComponent = object; - this.graphModel.edges = this.graphModel.edges.filter(i => i.name !== edgeObject.name); - } else { - console.log('DagreLayoutComponent.removeDagreChild: Node', object.name); - this.graphModel.nodes = this.graphModel.nodes.filter(i => i.name !== object.name); - - if (object instanceof DagreNodeComponent) { - const node: DagreNodeComponent = object; - if (node.composition) { - this.graphModel.composition = this.graphModel.composition.filter(i => i.child !== node.name); - } - } - - } - } - ngAfterViewInit() { super.ngAfterViewInit(); this.layout(); @@ -169,7 +63,7 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni ranker: this.ranker }; this.graph = DagreUtils.modelToGraph(this.graphModel); - // console.log('DagreLayoutComponent.layout: g', g); + // console.log('DagreLayoutComponent.layout: graph', this.graph); this.syncGraphNodes(this.graph); this.syncGraphEdges(this.graph); this.syncGraphContainer(this.graph); @@ -177,9 +71,9 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni } protected syncGraphNodes(g: dagre.graphlib.Graph) { - console.log('DagreLayoutComponent.syncGraphNodes'); + // console.log('DagreLayoutComponent.syncGraphNodes'); g.nodes().forEach((name) => { - console.log('Node ' + name + ': ' + JSON.stringify(g.node(name))); + // console.log('Node ' + name + ': ' + JSON.stringify(g.node(name))); const object: AbstractObject3D = this.findByName(name); if (object) { @@ -195,11 +89,8 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni const composition: DagreCompositionComponent = object; composition.width = node.width; composition.height = node.height; - } - } else { - console.warn('DagreLayoutComponent.layout: Object not found by name', name); } }); } @@ -222,8 +113,6 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni } }); edgeComponent.updateLineGeometry(); - } else { - console.warn('DagreLayoutComponent.layout: Object not found by name', e.name); } }); } @@ -237,12 +126,11 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni } public ngOnChanges(changes: SimpleChanges) { + super.ngOnChanges(changes); // console.log('AbstractObject3D.ngOnChanges', this.name); if (!this.object) { return; } - super.ngOnChanges(changes); - let modified = false; if (['align', 'rankdir', 'ranksep', 'nodesep', 'edgesep', 'marginx', 'marginy', 'ranker'].some(propName => propName in changes)) { @@ -263,4 +151,14 @@ export class DagreLayoutComponent extends EmptyComponent implements AfterViewIni this.graphModel = undefined; } + public getGraphModel() { + return this.graphModel; + } + + public refreshLayout() { + if (this.graph) { + this.layout(); + } + } + } diff --git a/projects/atft/src/lib/actor/data-center/layout/dagre-node.component.ts b/projects/atft/src/lib/actor/data-center/layout/dagre-node.component.ts index 64f8adfd..32818655 100644 --- a/projects/atft/src/lib/actor/data-center/layout/dagre-node.component.ts +++ b/projects/atft/src/lib/actor/data-center/layout/dagre-node.component.ts @@ -1,4 +1,4 @@ -import {Component, Injector, Input, Optional, SkipSelf} from '@angular/core'; +import {Component, Injector, Input, OnDestroy, OnInit, Optional, SkipSelf} from '@angular/core'; import {AbstractObject3D, EmptyComponent} from '../../../object'; import {provideParent} from '../../../util'; import {RendererService} from '../../../renderer'; @@ -9,12 +9,14 @@ import {DagreLayoutComponent} from './dagre-layout.component'; providers: [provideParent(DagreNodeComponent)], template: '' }) -export class DagreNodeComponent extends EmptyComponent { +export class DagreNodeComponent extends EmptyComponent implements OnInit, OnDestroy { @Input() composition: string; @Input() translateZ = 1; + protected dagreLayout: DagreLayoutComponent; + constructor( protected rendererService: RendererService, @SkipSelf() @Optional() protected parent: AbstractObject3D, @@ -22,9 +24,68 @@ export class DagreNodeComponent extends EmptyComponent { ) { super(rendererService, parent); - const dagreLayout: DagreLayoutComponent = this.injector.get(DagreLayoutComponent); - console.log('DagreNodeComponent.constructor dagreLayout', dagreLayout); + this.dagreLayout = this.injector.get(DagreLayoutComponent); + if (!this.dagreLayout) { + console.warn('DagreNodeComponent.constructor: atft-dagre-layout not found!'); + } + } + + ngOnInit() { + super.ngOnInit(); + this.addNode(); + } + + protected addNode() { + if (this.dagreLayout && this.dagreLayout.getGraphModel()) { + // console.log('DagreNodeComponent.addNode', this.name); + + // Register as layout children + this.dagreLayout.getChildren().push(this); + + // Create Graph Node + this.dagreLayout.getGraphModel().nodes.push({ + name: this.name, + label: this.name + }); + + // Create Composition (is exists): + if (this.composition) { + // console.log('DagreNodeComponent.addNode to composition', node.composition); + this.dagreLayout.getGraphModel().composition.push({ + parent: this.composition, + child: this.name + }); + } + + // Update Graph Layout + this.dagreLayout.refreshLayout(); + } } + ngOnDestroy() { + super.ngOnDestroy(); + this.removeNode(); + } + + protected removeNode() { + if (this.dagreLayout && this.dagreLayout.getGraphModel()) { + // console.log('DagreNodeComponent.removeNode', this.name); + + // Remove from layout + this.dagreLayout.removeChildByName(this.name); + + // Remove from model + this.dagreLayout.getGraphModel().nodes = this.dagreLayout.getGraphModel().nodes.filter(i => i.name !== this.name); + + // Remove from composition + if (this.composition) { + this.dagreLayout.getGraphModel().composition = this.dagreLayout.getGraphModel().composition.filter(i => i.child !== this.name); + } + + // Update Graph Layout + this.dagreLayout.refreshLayout(); + } + } + } diff --git a/projects/atft/src/lib/actor/data-center/layout/dagre-utils.ts b/projects/atft/src/lib/actor/data-center/layout/dagre-utils.ts index 293cdfa4..e0330fc8 100644 --- a/projects/atft/src/lib/actor/data-center/layout/dagre-utils.ts +++ b/projects/atft/src/lib/actor/data-center/layout/dagre-utils.ts @@ -1,21 +1,17 @@ import * as dagre from 'dagre'; -export interface DeepRoot { - deepRoot?: string; -} - -export interface Node extends DeepRoot { +export interface Node { name: string; label: string; } -export interface Edge extends DeepRoot { +export interface Edge { name: string; from: string; to: string; } -export interface Composition extends DeepRoot { +export interface Composition { parent: string; child: string; } @@ -48,8 +44,8 @@ export class DagreUtils { this.updateGraph(g, model); - console.log('DagreUtils.layout model', model); - console.log('DagreUtils.layout', g); + // console.log('DagreUtils.layout model', model); + // console.log('DagreUtils.layout', g); dagre.layout(g); return g; } diff --git a/projects/atft/src/lib/object/abstract-object-3d.ts b/projects/atft/src/lib/object/abstract-object-3d.ts index 42ae24cd..6513cad3 100644 --- a/projects/atft/src/lib/object/abstract-object-3d.ts +++ b/projects/atft/src/lib/object/abstract-object-3d.ts @@ -183,8 +183,6 @@ export abstract class AbstractObject3D implements Afte public findByName(name: string) { // console.log('AbstractObject3D.findByName: Searching name', name); - // console.log('AbstractObject3D.findByName: children', this.childlren); - // const res = this.childlren.filter(i => i.object && i.object.name === name)[0]; const res = this.recursionByName(this, name); // console.log('AbstractObject3D.findByName: result', res); return res; @@ -204,4 +202,9 @@ export abstract class AbstractObject3D implements Afte return this.childlren; } + public removeChildByName(name: string) { + this.childlren = this.childlren.filter(i => i.name !== name); + } + + }