mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-01-31 16:36:22 +00:00
New selection manager and graph data manager
This commit is contained in:
parent
e4e9c671c7
commit
0e04e85e53
@ -47,7 +47,7 @@ import { ProjectWebServiceHandler } from "./handlers/project-web-service-handler
|
|||||||
import { LinksDataSource } from "./cartography/datasources/links-datasource";
|
import { LinksDataSource } from "./cartography/datasources/links-datasource";
|
||||||
import { NodesDataSource } from "./cartography/datasources/nodes-datasource";
|
import { NodesDataSource } from "./cartography/datasources/nodes-datasource";
|
||||||
import { SymbolsDataSource } from "./cartography/datasources/symbols-datasource";
|
import { SymbolsDataSource } from "./cartography/datasources/symbols-datasource";
|
||||||
import { SelectionManager } from "./cartography/managers/selection-manager";
|
import { SelectionManager, SelectionStore } from "./cartography/managers/selection-manager";
|
||||||
import { InRectangleHelper } from "./cartography/helpers/in-rectangle-helper";
|
import { InRectangleHelper } from "./cartography/helpers/in-rectangle-helper";
|
||||||
import { DrawingsDataSource } from "./cartography/datasources/drawings-datasource";
|
import { DrawingsDataSource } from "./cartography/datasources/drawings-datasource";
|
||||||
import { MoveLayerDownActionComponent } from './components/project-map/node-context-menu/actions/move-layer-down-action/move-layer-down-action.component';
|
import { MoveLayerDownActionComponent } from './components/project-map/node-context-menu/actions/move-layer-down-action/move-layer-down-action.component';
|
||||||
@ -149,6 +149,7 @@ if (environment.production) {
|
|||||||
LinksDataSource,
|
LinksDataSource,
|
||||||
NodesDataSource,
|
NodesDataSource,
|
||||||
SymbolsDataSource,
|
SymbolsDataSource,
|
||||||
|
SelectionStore,
|
||||||
SelectionManager,
|
SelectionManager,
|
||||||
InRectangleHelper,
|
InRectangleHelper,
|
||||||
DrawingsDataSource,
|
DrawingsDataSource,
|
||||||
|
@ -17,7 +17,6 @@ import { Context } from './models/context';
|
|||||||
import { D3_MAP_IMPORTS } from './d3-map.imports';
|
import { D3_MAP_IMPORTS } from './d3-map.imports';
|
||||||
import { CanvasSizeDetector } from './helpers/canvas-size-detector';
|
import { CanvasSizeDetector } from './helpers/canvas-size-detector';
|
||||||
import { MapListeners } from './listeners/map-listeners';
|
import { MapListeners } from './listeners/map-listeners';
|
||||||
import { DrawingsDraggableListener } from './listeners/drawings-draggable-listener';
|
|
||||||
import { NodesDraggableListener } from './listeners/nodes-draggable-listener';
|
import { NodesDraggableListener } from './listeners/nodes-draggable-listener';
|
||||||
import { DrawingsEventSource } from './events/drawings-event-source';
|
import { DrawingsEventSource } from './events/drawings-event-source';
|
||||||
import { NodesEventSource } from './events/nodes-event-source';
|
import { NodesEventSource } from './events/nodes-event-source';
|
||||||
@ -35,6 +34,9 @@ import { NodeToMapNodeConverter } from './converters/map/node-to-map-node-conver
|
|||||||
import { PortToMapPortConverter } from './converters/map/port-to-map-port-converter';
|
import { PortToMapPortConverter } from './converters/map/port-to-map-port-converter';
|
||||||
import { SymbolToMapSymbolConverter } from './converters/map/symbol-to-map-symbol-converter';
|
import { SymbolToMapSymbolConverter } from './converters/map/symbol-to-map-symbol-converter';
|
||||||
import { LinkNodeToMapLinkNodeConverter } from './converters/map/link-node-to-map-link-node-converter';
|
import { LinkNodeToMapLinkNodeConverter } from './converters/map/link-node-to-map-link-node-converter';
|
||||||
|
import { GraphDataManager } from './managers/graph-data-manager';
|
||||||
|
import { SelectionListener } from './listeners/selection-listener';
|
||||||
|
import { MapNodesDataSource, MapLinksDataSource, MapDrawingsDataSource, MapSymbolsDataSource } from './datasources/map-datasource';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -58,8 +60,8 @@ import { LinkNodeToMapLinkNodeConverter } from './converters/map/link-node-to-ma
|
|||||||
MapChangeDetectorRef,
|
MapChangeDetectorRef,
|
||||||
CanvasSizeDetector,
|
CanvasSizeDetector,
|
||||||
Context,
|
Context,
|
||||||
|
SelectionListener,
|
||||||
MapListeners,
|
MapListeners,
|
||||||
DrawingsDraggableListener,
|
|
||||||
NodesDraggableListener,
|
NodesDraggableListener,
|
||||||
DrawingsEventSource,
|
DrawingsEventSource,
|
||||||
NodesEventSource,
|
NodesEventSource,
|
||||||
@ -77,6 +79,11 @@ import { LinkNodeToMapLinkNodeConverter } from './converters/map/link-node-to-ma
|
|||||||
NodeToMapNodeConverter,
|
NodeToMapNodeConverter,
|
||||||
PortToMapPortConverter,
|
PortToMapPortConverter,
|
||||||
SymbolToMapSymbolConverter,
|
SymbolToMapSymbolConverter,
|
||||||
|
GraphDataManager,
|
||||||
|
MapNodesDataSource,
|
||||||
|
MapLinksDataSource,
|
||||||
|
MapDrawingsDataSource,
|
||||||
|
MapSymbolsDataSource,
|
||||||
...D3_MAP_IMPORTS
|
...D3_MAP_IMPORTS
|
||||||
],
|
],
|
||||||
exports: [ MapComponent ]
|
exports: [ MapComponent ]
|
||||||
|
@ -2,7 +2,7 @@ import { Component, OnInit, Output, EventEmitter, OnDestroy, ViewChild } from '@
|
|||||||
import { DrawingLineWidget } from '../../widgets/drawing-line';
|
import { DrawingLineWidget } from '../../widgets/drawing-line';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { NodeSelectInterfaceComponent } from '../node-select-interface/node-select-interface.component';
|
import { NodeSelectInterfaceComponent } from '../node-select-interface/node-select-interface.component';
|
||||||
import { LinkCreated } from '../../events/links';
|
import { MapLinkCreated } from '../../events/links';
|
||||||
import { NodeClicked } from '../../events/nodes';
|
import { NodeClicked } from '../../events/nodes';
|
||||||
import { NodeWidget } from '../../widgets/node';
|
import { NodeWidget } from '../../widgets/node';
|
||||||
import { MapNode } from '../../models/map/map-node';
|
import { MapNode } from '../../models/map/map-node';
|
||||||
@ -17,7 +17,7 @@ import { MapPort } from '../../models/map/map-port';
|
|||||||
export class DrawLinkToolComponent implements OnInit, OnDestroy {
|
export class DrawLinkToolComponent implements OnInit, OnDestroy {
|
||||||
@ViewChild(NodeSelectInterfaceComponent) nodeSelectInterfaceMenu: NodeSelectInterfaceComponent;
|
@ViewChild(NodeSelectInterfaceComponent) nodeSelectInterfaceMenu: NodeSelectInterfaceComponent;
|
||||||
|
|
||||||
@Output('linkCreated') linkCreated = new EventEmitter<LinkCreated>();
|
@Output('linkCreated') linkCreated = new EventEmitter<MapLinkCreated>();
|
||||||
|
|
||||||
private onNodeClicked: Subscription;
|
private onNodeClicked: Subscription;
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ export class DrawLinkToolComponent implements OnInit, OnDestroy {
|
|||||||
const port: MapPort = event.port;
|
const port: MapPort = event.port;
|
||||||
if (this.drawingLineTool.isDrawing()) {
|
if (this.drawingLineTool.isDrawing()) {
|
||||||
const data = this.drawingLineTool.stop();
|
const data = this.drawingLineTool.stop();
|
||||||
this.linkCreated.emit(new LinkCreated(data['node'], data['port'], node, port));
|
this.linkCreated.emit(new MapLinkCreated(data['node'], data['port'], node, port));
|
||||||
} else {
|
} else {
|
||||||
this.drawingLineTool.start(node.x + node.width / 2., node.y + node.height / 2., {
|
this.drawingLineTool.start(node.x + node.width / 2., node.y + node.height / 2., {
|
||||||
'node': node,
|
'node': node,
|
||||||
|
@ -26,10 +26,10 @@ import { Link } from '../../../models/link';
|
|||||||
import { Drawing } from '../../models/drawing';
|
import { Drawing } from '../../models/drawing';
|
||||||
import { Symbol } from '../../../models/symbol';
|
import { Symbol } from '../../../models/symbol';
|
||||||
import { MapNodeToNodeConverter } from '../../converters/map/map-node-to-node-converter';
|
import { MapNodeToNodeConverter } from '../../converters/map/map-node-to-node-converter';
|
||||||
import { NodeToMapNodeConverter } from '../../converters/map/node-to-map-node-converter';
|
import { MapPortToPortConverter } from '../../converters/map/map-port-to-port-converter';
|
||||||
import { DrawingToMapDrawingConverter } from '../../converters/map/drawing-to-map-drawing-converter';
|
import { GraphDataManager } from '../../managers/graph-data-manager';
|
||||||
import { LinkToMapLinkConverter } from '../../converters/map/link-to-map-link-converter';
|
import { SelectionManager } from '../../managers/selection-manager';
|
||||||
import { SymbolToMapSymbolConverter } from '../../converters/map/symbol-to-map-symbol-converter';
|
import { MapDrawingToDrawingConverter } from '../../converters/map/map-drawing-to-drawing-converter';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -46,8 +46,8 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
@Input() width = 1500;
|
@Input() width = 1500;
|
||||||
@Input() height = 600;
|
@Input() height = 600;
|
||||||
|
|
||||||
@Output() nodeDragged = new EventEmitter<DraggedDataEvent<Node>>();
|
@Output() nodeDragged = new EventEmitter<DraggedDataEvent<Node[]>>();
|
||||||
@Output() drawingDragged = new EventEmitter<DraggedDataEvent<Drawing>>();
|
@Output() drawingDragged = new EventEmitter<DraggedDataEvent<Drawing[]>>();
|
||||||
@Output() onLinkCreated = new EventEmitter<LinkCreated>();
|
@Output() onLinkCreated = new EventEmitter<LinkCreated>();
|
||||||
|
|
||||||
private parentNativeElement: any;
|
private parentNativeElement: any;
|
||||||
@ -56,21 +56,22 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
private onChangesDetected: Subscription;
|
private onChangesDetected: Subscription;
|
||||||
private nodeDraggedSub: Subscription;
|
private nodeDraggedSub: Subscription;
|
||||||
private drawingDraggedSub: Subscription;
|
private drawingDraggedSub: Subscription;
|
||||||
|
private selectionChanged: Subscription;
|
||||||
|
|
||||||
protected settings = {
|
protected settings = {
|
||||||
'show_interface_labels': true
|
'show_interface_labels': true
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private graphDataManager: GraphDataManager,
|
||||||
private context: Context,
|
private context: Context,
|
||||||
private mapChangeDetectorRef: MapChangeDetectorRef,
|
private mapChangeDetectorRef: MapChangeDetectorRef,
|
||||||
private canvasSizeDetector: CanvasSizeDetector,
|
private canvasSizeDetector: CanvasSizeDetector,
|
||||||
private mapListeners: MapListeners,
|
private mapListeners: MapListeners,
|
||||||
private mapNodeToNode: MapNodeToNodeConverter,
|
private mapNodeToNode: MapNodeToNodeConverter,
|
||||||
private nodeToMapNode: NodeToMapNodeConverter,
|
private mapPortToPort: MapPortToPortConverter,
|
||||||
private linkToMapLink: LinkToMapLinkConverter,
|
private mapDrawingToDrawing: MapDrawingToDrawingConverter,
|
||||||
private drawingToMapDrawing: DrawingToMapDrawingConverter,
|
private selectionManager: SelectionManager,
|
||||||
private symbolToMapSymbol: SymbolToMapSymbolConverter,
|
|
||||||
protected element: ElementRef,
|
protected element: ElementRef,
|
||||||
protected nodesWidget: NodesWidget,
|
protected nodesWidget: NodesWidget,
|
||||||
protected nodeWidget: NodeWidget,
|
protected nodeWidget: NodeWidget,
|
||||||
@ -122,12 +123,6 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
(changes['symbols'] && !changes['symbols'].isFirstChange())
|
(changes['symbols'] && !changes['symbols'].isFirstChange())
|
||||||
) {
|
) {
|
||||||
if (this.svg.empty && !this.svg.empty()) {
|
if (this.svg.empty && !this.svg.empty()) {
|
||||||
if (changes['nodes']) {
|
|
||||||
this.onNodesChange(changes['nodes']);
|
|
||||||
}
|
|
||||||
if (changes['links']) {
|
|
||||||
this.onLinksChange(changes['links']);
|
|
||||||
}
|
|
||||||
if (changes['symbols']) {
|
if (changes['symbols']) {
|
||||||
this.onSymbolsChange(changes['symbols']);
|
this.onSymbolsChange(changes['symbols']);
|
||||||
}
|
}
|
||||||
@ -144,15 +139,22 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
|
|
||||||
this.onChangesDetected = this.mapChangeDetectorRef.changesDetected.subscribe(() => {
|
this.onChangesDetected = this.mapChangeDetectorRef.changesDetected.subscribe(() => {
|
||||||
if (this.mapChangeDetectorRef.hasBeenDrawn) {
|
if (this.mapChangeDetectorRef.hasBeenDrawn) {
|
||||||
this.reload();
|
this.redraw();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.nodeDraggedSub = this.nodesEventSource.dragged.subscribe((evt) => {
|
this.nodeDraggedSub = this.nodesEventSource.dragged.subscribe((evt) => {
|
||||||
const converted = this.mapNodeToNode.convert(evt.datum);
|
const converted = evt.datum.map((e) => this.mapNodeToNode.convert(e));
|
||||||
this.nodeDragged.emit(new DraggedDataEvent<Node>(converted));
|
this.nodeDragged.emit(new DraggedDataEvent<Node[]>(converted));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.drawingDraggedSub = this.drawingsEventSource.dragged.subscribe((evt) => {
|
||||||
|
const converted = evt.datum.map((e) => this.mapDrawingToDrawing.convert(e));
|
||||||
|
this.drawingDragged.emit(new DraggedDataEvent<Drawing[]>(converted));
|
||||||
|
});
|
||||||
|
|
||||||
|
this.selectionChanged = this.selectionManager.subscribe(this.selectionToolWidget.rectangleSelected);
|
||||||
|
|
||||||
this.mapListeners.onInit(this.svg);
|
this.mapListeners.onInit(this.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,6 +163,7 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.onChangesDetected.unsubscribe();
|
this.onChangesDetected.unsubscribe();
|
||||||
this.mapListeners.onDestroy();
|
this.mapListeners.onDestroy();
|
||||||
this.nodeDraggedSub.unsubscribe();
|
this.nodeDraggedSub.unsubscribe();
|
||||||
|
this.selectionChanged.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
public createGraph(domElement: HTMLElement) {
|
public createGraph(domElement: HTMLElement) {
|
||||||
@ -176,7 +179,14 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected linkCreated(evt) {
|
protected linkCreated(evt) {
|
||||||
this.onLinkCreated.emit(evt);
|
const linkCreated = new LinkCreated(
|
||||||
|
this.mapNodeToNode.convert(evt.sourceNode),
|
||||||
|
this.mapPortToPort.convert(evt.sourcePort),
|
||||||
|
this.mapNodeToNode.convert(evt.targetNode),
|
||||||
|
this.mapPortToPort.convert(evt.targetPort)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.onLinkCreated.emit(linkCreated);
|
||||||
}
|
}
|
||||||
|
|
||||||
private changeLayout() {
|
private changeLayout() {
|
||||||
@ -184,69 +194,22 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.context.size = this.getSize();
|
this.context.size = this.getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setNodes(this.nodes);
|
|
||||||
this.setLinks(this.links);
|
|
||||||
this.setDrawings(this.drawings);
|
|
||||||
|
|
||||||
this.redraw();
|
this.redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
private setNodes(nodes: Node[]) {
|
|
||||||
this.graphLayout.setNodes(nodes.map((n) => this.nodeToMapNode.convert(n)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private setLinks(links: Link[]) {
|
|
||||||
this.graphLayout.setLinks(links.map((l) => this.linkToMapLink.convert(l)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private setDrawings(drawings: Drawing[]) {
|
|
||||||
this.graphLayout.setDrawings(drawings.map((d) => this.drawingToMapDrawing.convert(d)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private setSymbols(symbols: Symbol[]) {
|
|
||||||
this.nodeWidget.setSymbols(symbols.map((s) => this.symbolToMapSymbol.convert(s)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private onLinksChange(change: SimpleChange) {
|
|
||||||
const nodes_by_id = {};
|
|
||||||
this.nodes.forEach((n: Node) => {
|
|
||||||
nodes_by_id[n.node_id] = n;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.links.forEach((link: Link) => {
|
|
||||||
const source_id = link.nodes[0].node_id;
|
|
||||||
const target_id = link.nodes[1].node_id;
|
|
||||||
if (source_id in nodes_by_id) {
|
|
||||||
link.source = nodes_by_id[source_id];
|
|
||||||
}
|
|
||||||
if (target_id in nodes_by_id) {
|
|
||||||
link.target = nodes_by_id[target_id];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (link.source && link.target) {
|
|
||||||
link.x = link.source.x + (link.target.x - link.source.x) * 0.5;
|
|
||||||
link.y = link.source.y + (link.target.y - link.source.y) * 0.5;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private onNodesChange(change: SimpleChange) {
|
|
||||||
this.onLinksChange(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private onSymbolsChange(change: SimpleChange) {
|
private onSymbolsChange(change: SimpleChange) {
|
||||||
this.setSymbols(this.symbols);
|
this.graphDataManager.setSymbols(this.symbols);
|
||||||
}
|
}
|
||||||
|
|
||||||
public redraw() {
|
private redraw() {
|
||||||
|
this.graphDataManager.setNodes(this.nodes);
|
||||||
|
this.graphDataManager.setLinks(this.links);
|
||||||
|
this.graphDataManager.setDrawings(this.drawings);
|
||||||
|
|
||||||
this.graphLayout.draw(this.svg, this.context);
|
this.graphLayout.draw(this.svg, this.context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public reload() {
|
|
||||||
this.onLinksChange(null);
|
|
||||||
this.redraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
@HostListener('window:resize', ['$event'])
|
@HostListener('window:resize', ['$event'])
|
||||||
onResize(event) {
|
onResize(event) {
|
||||||
this.changeLayout();
|
this.changeLayout();
|
||||||
|
@ -15,12 +15,26 @@ export abstract class DataSource<T> {
|
|||||||
this.update(item);
|
this.update(item);
|
||||||
} else {
|
} else {
|
||||||
this.data.push(item);
|
this.data.push(item);
|
||||||
|
console.log("Item added");
|
||||||
this.dataChange.next(this.data);
|
this.dataChange.next(this.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public set(data: T[]) {
|
public set(data: T[]) {
|
||||||
this.data = data;
|
data.forEach((item) => {
|
||||||
|
const index = this.findIndex(item);
|
||||||
|
if (index >= 0) {
|
||||||
|
const updated = Object.assign(this.data[index], item);
|
||||||
|
this.data[index] = updated;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.data.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const toRemove = this.data.filter((item) => data.filter((i) => this.getItemKey(i) === this.getItemKey(item)).length === 0);
|
||||||
|
toRemove.forEach((item) => this.remove(item));
|
||||||
|
|
||||||
this.dataChange.next(this.data);
|
this.dataChange.next(this.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,6 +44,7 @@ export abstract class DataSource<T> {
|
|||||||
const updated = Object.assign(this.data[index], item);
|
const updated = Object.assign(this.data[index], item);
|
||||||
this.data[index] = updated;
|
this.data[index] = updated;
|
||||||
this.dataChange.next(this.data);
|
this.dataChange.next(this.data);
|
||||||
|
console.log("Item updated");
|
||||||
this.itemUpdated.next(updated);
|
this.itemUpdated.next(updated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,5 +70,9 @@ export abstract class DataSource<T> {
|
|||||||
this.dataChange.next(this.data);
|
this.dataChange.next(this.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract findIndex(item: T): number;
|
private findIndex(item: T) {
|
||||||
|
return this.data.findIndex((i: T) => this.getItemKey(i) === this.getItemKey(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract getItemKey(item: T): any;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import { Drawing } from "../models/drawing";
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DrawingsDataSource extends DataSource<Drawing> {
|
export class DrawingsDataSource extends DataSource<Drawing> {
|
||||||
protected findIndex(drawing: Drawing) {
|
protected getItemKey(drawing: Drawing) {
|
||||||
return this.data.findIndex((d: Drawing) => d.drawing_id === drawing.drawing_id);
|
return drawing.drawing_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import { Link } from "../../models/link";
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LinksDataSource extends DataSource<Link> {
|
export class LinksDataSource extends DataSource<Link> {
|
||||||
protected findIndex(link: Link) {
|
protected getItemKey(link: Link) {
|
||||||
return this.data.findIndex((l: Link) => l.link_id === link.link_id);
|
return link.link_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
28
src/app/cartography/datasources/map-datasource.ts
Normal file
28
src/app/cartography/datasources/map-datasource.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { DataSource } from "./datasource";
|
||||||
|
import { MapNode } from "../models/map/map-node";
|
||||||
|
import { MapLink } from "../models/map/map-link";
|
||||||
|
import { MapDrawing } from "../models/map/map-drawing";
|
||||||
|
import { MapSymbol } from "../models/map/map-symbol";
|
||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
|
export interface Indexed {
|
||||||
|
id: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MapDataSource<T extends Indexed> extends DataSource<T> {
|
||||||
|
protected getItemKey(item: Indexed) {
|
||||||
|
return item.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MapNodesDataSource extends MapDataSource<MapNode> {}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MapLinksDataSource extends MapDataSource<MapLink> {}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MapDrawingsDataSource extends MapDataSource<MapDrawing> {}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MapSymbolsDataSource extends MapDataSource<MapSymbol> {}
|
@ -6,7 +6,7 @@ import { DataSource } from "./datasource";
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class NodesDataSource extends DataSource<Node> {
|
export class NodesDataSource extends DataSource<Node> {
|
||||||
protected findIndex(node: Node) {
|
protected getItemKey(node: Node) {
|
||||||
return this.data.findIndex((n: Node) => n.node_id === node.node_id);
|
return node.node_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import { Symbol } from "../../models/symbol";
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SymbolsDataSource extends DataSource<Symbol> {
|
export class SymbolsDataSource extends DataSource<Symbol> {
|
||||||
protected findIndex(symbol: Symbol) {
|
protected getItemKey(symbol: Symbol) {
|
||||||
return this.data.findIndex((s: Symbol) => s.symbol_id === symbol.symbol_id);
|
return symbol.symbol_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,5 +5,5 @@ import { MapDrawing } from "../models/map/map-drawing";
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DrawingsEventSource {
|
export class DrawingsEventSource {
|
||||||
public dragged = new EventEmitter<DraggedDataEvent<MapDrawing>>();
|
public dragged = new EventEmitter<DraggedDataEvent<MapDrawing[]>>();
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,4 @@ export class DataEventSource<T> {
|
|||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// class CreatedDataEvent<T> extends DataEventSource<T> {}
|
|
||||||
export class DraggedDataEvent<T> extends DataEventSource<T> {}
|
export class DraggedDataEvent<T> extends DataEventSource<T> {}
|
||||||
|
@ -1,12 +1,23 @@
|
|||||||
|
import { Port } from "../../models/port";
|
||||||
|
import { Node } from "../models/node";
|
||||||
import { MapNode } from "../models/map/map-node";
|
import { MapNode } from "../models/map/map-node";
|
||||||
import { MapPort } from "../models/map/map-port";
|
import { MapPort } from "../models/map/map-port";
|
||||||
|
|
||||||
|
|
||||||
export class LinkCreated {
|
export class LinkCreated {
|
||||||
constructor(
|
constructor(
|
||||||
public sourceNode: MapNode,
|
public sourceNode: Node,
|
||||||
public sourcePort: MapPort,
|
public sourcePort: Port,
|
||||||
public targetNode: MapNode,
|
public targetNode: Node,
|
||||||
public targetPort: MapPort
|
public targetPort: Port
|
||||||
) {}
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MapLinkCreated {
|
||||||
|
constructor(
|
||||||
|
public sourceNode: MapNode,
|
||||||
|
public sourcePort: MapPort,
|
||||||
|
public targetNode: MapNode,
|
||||||
|
public targetPort: MapPort
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
|
@ -5,5 +5,5 @@ import { MapNode } from "../models/map/map-node";
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class NodesEventSource {
|
export class NodesEventSource {
|
||||||
public dragged = new EventEmitter<DraggedDataEvent<MapNode>>();
|
public dragged = new EventEmitter<DraggedDataEvent<MapNode[]>>();
|
||||||
}
|
}
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
import { Injectable } from "@angular/core";
|
|
||||||
import { DrawingsWidget } from "../widgets/drawings";
|
|
||||||
import { DraggableStart } from "../events/draggable";
|
|
||||||
import { Subscription } from "rxjs";
|
|
||||||
import { SelectionManager } from "../managers/selection-manager";
|
|
||||||
import { DrawingsEventSource } from "../events/drawings-event-source";
|
|
||||||
import { DraggedDataEvent } from "../events/event-source";
|
|
||||||
import { MapDrawing } from "../models/map/map-drawing";
|
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class DrawingsDraggableListener {
|
|
||||||
private start: Subscription;
|
|
||||||
private drag: Subscription;
|
|
||||||
private end: Subscription;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private drawingsWidget: DrawingsWidget,
|
|
||||||
private selectionManager: SelectionManager,
|
|
||||||
private drawingsEventSource: DrawingsEventSource
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public onInit(svg: any) {
|
|
||||||
this.start = this.drawingsWidget.draggable.start.subscribe((evt: DraggableStart<MapDrawing>) => {
|
|
||||||
const drawings = this.selectionManager.getSelectedDrawings();
|
|
||||||
if (drawings.filter((n: MapDrawing) => n.id === evt.datum.id).length === 0) {
|
|
||||||
this.selectionManager.setSelectedDrawings([evt.datum]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.drag = this.drawingsWidget.draggable.drag.subscribe((evt: DraggableStart<MapDrawing>) => {
|
|
||||||
const drawings = this.selectionManager.getSelectedDrawings();
|
|
||||||
drawings.forEach((drawing: MapDrawing) => {
|
|
||||||
drawing.x += evt.dx;
|
|
||||||
drawing.y += evt.dy;
|
|
||||||
this.drawingsWidget.redrawDrawing(svg, drawing);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.end = this.drawingsWidget.draggable.end.subscribe((evt: DraggableStart<MapDrawing>) => {
|
|
||||||
const drawings = this.selectionManager.getSelectedDrawings();
|
|
||||||
drawings.forEach((drawing: MapDrawing) => {
|
|
||||||
this.drawingsEventSource.dragged.emit(new DraggedDataEvent<MapDrawing>(drawing));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public onDestroy() {
|
|
||||||
this.start.unsubscribe();
|
|
||||||
this.drag.unsubscribe();
|
|
||||||
this.end.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +1,18 @@
|
|||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
import { MapListener } from "./map-listener";
|
import { MapListener } from "./map-listener";
|
||||||
import { DrawingsDraggableListener } from "./drawings-draggable-listener";
|
|
||||||
import { NodesDraggableListener } from "./nodes-draggable-listener";
|
import { NodesDraggableListener } from "./nodes-draggable-listener";
|
||||||
|
import { SelectionListener } from "./selection-listener";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MapListeners {
|
export class MapListeners {
|
||||||
private listeners: MapListener[] = [];
|
private listeners: MapListener[] = [];
|
||||||
constructor(
|
constructor(
|
||||||
private drawingsDraggableListener: DrawingsDraggableListener,
|
private nodesDraggableListener: NodesDraggableListener,
|
||||||
private nodesDraggableListener: NodesDraggableListener
|
private selectionListener: SelectionListener
|
||||||
) {
|
) {
|
||||||
this.listeners.push(this.drawingsDraggableListener);
|
|
||||||
this.listeners.push(this.nodesDraggableListener);
|
this.listeners.push(this.nodesDraggableListener);
|
||||||
|
this.listeners.push(this.selectionListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onInit(svg: any) {
|
public onInit(svg: any) {
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
import { NodesWidget } from "../widgets/nodes";
|
import { NodesWidget } from "../widgets/nodes";
|
||||||
import { DraggableStart } from "../events/draggable";
|
import { DraggableStart, DraggableDrag, DraggableEnd } from "../events/draggable";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { SelectionManager } from "../managers/selection-manager";
|
import { SelectionManager } from "../managers/selection-manager";
|
||||||
import { LinksWidget } from "../widgets/links";
|
import { LinksWidget } from "../widgets/links";
|
||||||
import { GraphLayout } from "../widgets/graph-layout";
|
|
||||||
import { NodesEventSource } from "../events/nodes-event-source";
|
import { NodesEventSource } from "../events/nodes-event-source";
|
||||||
import { DraggedDataEvent } from "../events/event-source";
|
import { DraggedDataEvent } from "../events/event-source";
|
||||||
import { MapNode } from "../models/map/map-node";
|
import { MapNode } from "../models/map/map-node";
|
||||||
|
import { GraphDataManager } from "../managers/graph-data-manager";
|
||||||
|
import { DrawingsWidget } from "../widgets/drawings";
|
||||||
|
import { merge } from "rxjs";
|
||||||
|
import { MapDrawing } from "../models/map/map-drawing";
|
||||||
|
import { DrawingsEventSource } from "../events/drawings-event-source";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -18,42 +22,79 @@ export class NodesDraggableListener {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private nodesWidget: NodesWidget,
|
private nodesWidget: NodesWidget,
|
||||||
|
private drawingsWidget: DrawingsWidget,
|
||||||
private linksWidget: LinksWidget,
|
private linksWidget: LinksWidget,
|
||||||
private selectionManager: SelectionManager,
|
private selectionManager: SelectionManager,
|
||||||
private graphLayout: GraphLayout,
|
private nodesEventSource: NodesEventSource,
|
||||||
private nodesEventSource: NodesEventSource
|
private drawingsEventSource: DrawingsEventSource,
|
||||||
|
private graphDataManager: GraphDataManager
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public onInit(svg: any) {
|
public onInit(svg: any) {
|
||||||
this.start = this.nodesWidget.draggable.start.subscribe((evt: DraggableStart<MapNode>) => {
|
|
||||||
const nodes = this.selectionManager.getSelectedNodes();
|
this.start = merge(
|
||||||
if (nodes.filter((n: MapNode) => n.id === evt.datum.id).length === 0) {
|
this.nodesWidget.draggable.start,
|
||||||
this.selectionManager.setSelectedNodes([evt.datum]);
|
this.drawingsWidget.draggable.start
|
||||||
|
).subscribe((evt: DraggableStart<any>) => {
|
||||||
|
const selected = this.selectionManager.getSelected();
|
||||||
|
|
||||||
|
if (evt.datum instanceof MapNode) {
|
||||||
|
if (selected.filter((item) => item instanceof MapNode && item.id === evt.datum.id).length === 0) {
|
||||||
|
this.selectionManager.setSelected([evt.datum]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evt.datum instanceof MapDrawing) {
|
||||||
|
if (selected.filter((item) => item instanceof MapDrawing && item.id === evt.datum.id).length === 0) {
|
||||||
|
this.selectionManager.setSelected([evt.datum]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.drag = this.nodesWidget.draggable.drag.subscribe((evt: DraggableStart<MapNode>) => {
|
|
||||||
const nodes = this.selectionManager.getSelectedNodes();
|
this.drag = merge(
|
||||||
nodes.forEach((node: MapNode) => {
|
this.nodesWidget.draggable.drag,
|
||||||
|
this.drawingsWidget.draggable.drag
|
||||||
|
).subscribe((evt: DraggableDrag<any>) => {
|
||||||
|
const selected = this.selectionManager.getSelected();
|
||||||
|
|
||||||
|
// update nodes
|
||||||
|
selected.filter((item) => item instanceof MapNode).forEach((node: MapNode) => {
|
||||||
node.x += evt.dx;
|
node.x += evt.dx;
|
||||||
node.y += evt.dy;
|
node.y += evt.dy;
|
||||||
|
|
||||||
this.nodesWidget.redrawNode(svg, node);
|
this.nodesWidget.redrawNode(svg, node);
|
||||||
|
|
||||||
const links = this.graphLayout.getLinks().filter(
|
const links = this.graphDataManager.getLinks().filter(
|
||||||
(link) => link.target.id === node.id || link.source.id === node.id);
|
(link) => link.target.id === node.id || link.source.id === node.id);
|
||||||
links.forEach((link) => {
|
links.forEach((link) => {
|
||||||
this.linksWidget.redrawLink(svg, link);
|
this.linksWidget.redrawLink(svg, link);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// update drawings
|
||||||
|
selected.filter((item) => item instanceof MapDrawing).forEach((drawing: MapDrawing) => {
|
||||||
|
drawing.x += evt.dx;
|
||||||
|
drawing.y += evt.dy;
|
||||||
|
this.drawingsWidget.redrawDrawing(svg, drawing);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.end = this.nodesWidget.draggable.end.subscribe((evt: DraggableStart<MapNode>) => {
|
this.end = merge(
|
||||||
const nodes = this.selectionManager.getSelectedNodes();
|
this.nodesWidget.draggable.end,
|
||||||
nodes.forEach((node: MapNode) => {
|
this.drawingsWidget.draggable.end
|
||||||
this.nodesEventSource.dragged.emit(new DraggedDataEvent<MapNode>(node));
|
).subscribe((evt: DraggableEnd<any>) => {
|
||||||
});
|
const selected = this.selectionManager.getSelected();
|
||||||
|
|
||||||
|
const updatedNodes = selected.filter((item) => item instanceof MapNode).map((item: MapNode) => item);
|
||||||
|
this.nodesEventSource.dragged.emit(new DraggedDataEvent<MapNode[]>(updatedNodes))
|
||||||
|
|
||||||
|
const updatedDrawings = selected.filter((item) => item instanceof MapDrawing).map((item: MapDrawing) => item);
|
||||||
|
this.drawingsEventSource.dragged.emit(new DraggedDataEvent<MapDrawing[]>(updatedDrawings));
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onDestroy() {
|
public onDestroy() {
|
||||||
|
31
src/app/cartography/listeners/selection-listener.ts
Normal file
31
src/app/cartography/listeners/selection-listener.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { Subscription } from "rxjs";
|
||||||
|
import { SelectionStore } from "../managers/selection-manager";
|
||||||
|
import { MapChangeDetectorRef } from "../services/map-change-detector-ref";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SelectionListener {
|
||||||
|
private onSelected: Subscription;
|
||||||
|
private onUnselected: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private selectionStore: SelectionStore,
|
||||||
|
private mapChangeDetectorRef: MapChangeDetectorRef
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public onInit(svg: any) {
|
||||||
|
this.onSelected = this.selectionStore.selected.subscribe(() => {
|
||||||
|
this.mapChangeDetectorRef.detectChanges();
|
||||||
|
});
|
||||||
|
this.onUnselected = this.selectionStore.unselected.subscribe(() => {
|
||||||
|
this.mapChangeDetectorRef.detectChanges();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public onDestroy() {
|
||||||
|
this.onSelected.unsubscribe();
|
||||||
|
this.onUnselected.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
103
src/app/cartography/managers/graph-data-manager.ts
Normal file
103
src/app/cartography/managers/graph-data-manager.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { Node } from "../models/node";
|
||||||
|
import { NodeToMapNodeConverter } from "../converters/map/node-to-map-node-converter";
|
||||||
|
import { LinkToMapLinkConverter } from "../converters/map/link-to-map-link-converter";
|
||||||
|
import { DrawingToMapDrawingConverter } from "../converters/map/drawing-to-map-drawing-converter";
|
||||||
|
import { SymbolToMapSymbolConverter } from "../converters/map/symbol-to-map-symbol-converter";
|
||||||
|
import { MapNode } from "../models/map/map-node";
|
||||||
|
import { MapLink } from "../models/map/map-link";
|
||||||
|
import { Link } from "../../models/link";
|
||||||
|
import { Drawing } from "../models/drawing";
|
||||||
|
import { Symbol } from "../../models/symbol";
|
||||||
|
import { LayersManager } from "./layers-manager";
|
||||||
|
import { MapNodesDataSource, MapLinksDataSource, MapDrawingsDataSource, MapSymbolsDataSource } from "../datasources/map-datasource";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GraphDataManager {
|
||||||
|
constructor(
|
||||||
|
private mapNodesDataSource: MapNodesDataSource,
|
||||||
|
private mapLinksDataSource: MapLinksDataSource,
|
||||||
|
private mapDrawingsDataSource: MapDrawingsDataSource,
|
||||||
|
private mapSymbolsDataSource: MapSymbolsDataSource,
|
||||||
|
private nodeToMapNode: NodeToMapNodeConverter,
|
||||||
|
private linkToMapLink: LinkToMapLinkConverter,
|
||||||
|
private drawingToMapDrawing: DrawingToMapDrawingConverter,
|
||||||
|
private symbolToMapSymbol: SymbolToMapSymbolConverter,
|
||||||
|
private layersManager: LayersManager
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public setNodes(nodes: Node[]) {
|
||||||
|
const mapNodes = nodes.map((n) => this.nodeToMapNode.convert(n));
|
||||||
|
this.mapNodesDataSource.set(mapNodes);
|
||||||
|
|
||||||
|
this.assignDataToLinks();
|
||||||
|
this.onDataUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setLinks(links: Link[]) {
|
||||||
|
const mapLinks = links.map((l) => this.linkToMapLink.convert(l));
|
||||||
|
this.mapLinksDataSource.set(mapLinks);
|
||||||
|
|
||||||
|
this.assignDataToLinks();
|
||||||
|
this.onDataUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setDrawings(drawings: Drawing[]) {
|
||||||
|
const mapDrawings = drawings.map((d) => this.drawingToMapDrawing.convert(d));
|
||||||
|
this.mapDrawingsDataSource.set(mapDrawings);
|
||||||
|
|
||||||
|
this.onDataUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setSymbols(symbols: Symbol[]) {
|
||||||
|
const mapSymbols = symbols.map((s) => this.symbolToMapSymbol.convert(s));
|
||||||
|
this.mapSymbolsDataSource.set(mapSymbols);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNodes() {
|
||||||
|
return this.mapNodesDataSource.getItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLinks() {
|
||||||
|
return this.mapLinksDataSource.getItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getDrawings() {
|
||||||
|
return this.mapDrawingsDataSource.getItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSymbols() {
|
||||||
|
return this.mapSymbolsDataSource.getItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
private onDataUpdate() {
|
||||||
|
this.layersManager.clear();
|
||||||
|
this.layersManager.setNodes(this.getNodes());
|
||||||
|
this.layersManager.setLinks(this.getLinks());
|
||||||
|
this.layersManager.setDrawings(this.getDrawings());
|
||||||
|
}
|
||||||
|
|
||||||
|
private assignDataToLinks() {
|
||||||
|
const nodes_by_id = {};
|
||||||
|
this.getNodes().forEach((n: MapNode) => {
|
||||||
|
nodes_by_id[n.id] = n;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.getLinks().forEach((link: MapLink) => {
|
||||||
|
const source_id = link.nodes[0].nodeId;
|
||||||
|
const target_id = link.nodes[1].nodeId;
|
||||||
|
if (source_id in nodes_by_id) {
|
||||||
|
link.source = nodes_by_id[source_id];
|
||||||
|
}
|
||||||
|
if (target_id in nodes_by_id) {
|
||||||
|
link.target = nodes_by_id[target_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (link.source && link.target) {
|
||||||
|
link.x = link.source.x + (link.target.x - link.source.x) * 0.5;
|
||||||
|
link.y = link.source.y + (link.target.y - link.source.y) * 0.5;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,40 +1,74 @@
|
|||||||
import { Injectable } from "@angular/core";
|
import { Injectable, EventEmitter } from "@angular/core";
|
||||||
|
|
||||||
import { Subject } from "rxjs";
|
import { Subject } from "rxjs";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
|
|
||||||
import { NodesDataSource } from "../datasources/nodes-datasource";
|
|
||||||
import { LinksDataSource } from "../datasources/links-datasource";
|
|
||||||
import { InRectangleHelper } from "../helpers/in-rectangle-helper";
|
import { InRectangleHelper } from "../helpers/in-rectangle-helper";
|
||||||
import { Rectangle } from "../models/rectangle";
|
import { Rectangle } from "../models/rectangle";
|
||||||
import { DataSource } from "../datasources/datasource";
|
import { GraphDataManager } from "./graph-data-manager";
|
||||||
import { InterfaceLabel } from "../models/interface-label";
|
import { Indexed } from "../datasources/map-datasource";
|
||||||
import { DrawingsDataSource } from "../datasources/drawings-datasource";
|
|
||||||
import { MapNode } from "../models/map/map-node";
|
|
||||||
import { MapLink } from "../models/map/map-link";
|
|
||||||
import { MapDrawing } from "../models/map/map-drawing";
|
|
||||||
|
|
||||||
|
|
||||||
export interface Selectable {
|
@Injectable()
|
||||||
x: number;
|
export class SelectionStore {
|
||||||
y: number;
|
private selection: {[id:string]: any} = {};
|
||||||
is_selected: boolean;
|
|
||||||
|
public selected = new EventEmitter<any[]>();
|
||||||
|
public unselected = new EventEmitter<any[]>();
|
||||||
|
|
||||||
|
public set(items: Indexed[]) {
|
||||||
|
const dictItems = this.convertToKeyDict(items);
|
||||||
|
|
||||||
|
const selected = Object.keys(dictItems).filter((key) => {
|
||||||
|
return !this.isSelectedByKey(key);
|
||||||
|
}).map(key => dictItems[key]);
|
||||||
|
|
||||||
|
const unselected = Object.keys(this.selection).filter((key) => {
|
||||||
|
return !(key in dictItems);
|
||||||
|
}).map((key) => this.selection[key]);
|
||||||
|
|
||||||
|
this.selection = dictItems;
|
||||||
|
|
||||||
|
this.selected.emit(selected);
|
||||||
|
this.unselected.emit(unselected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(): Indexed[] {
|
||||||
|
return Object.keys(this.selection).map(key => this.selection[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isSelected(item): boolean {
|
||||||
|
const key = this.getKey(item);
|
||||||
|
return this.isSelectedByKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private isSelectedByKey(key): boolean {
|
||||||
|
return key in this.selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getKey(item: Indexed): string {
|
||||||
|
const type = item.constructor.name;
|
||||||
|
return `${type}-${item.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private convertToKeyDict(items: Indexed[]) {
|
||||||
|
const dict = {};
|
||||||
|
items.forEach((item) => {
|
||||||
|
dict[this.getKey(item)] = item;
|
||||||
|
});
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SelectionManager {
|
export class SelectionManager {
|
||||||
private selectedNodes: MapNode[] = [];
|
|
||||||
private selectedLinks: MapLink[] = [];
|
|
||||||
private selectedDrawings: MapDrawing[] = [];
|
|
||||||
private selectedInterfaceLabels: InterfaceLabel[] = [];
|
|
||||||
|
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private nodesDataSource: NodesDataSource,
|
private graphDataManager: GraphDataManager,
|
||||||
private linksDataSource: LinksDataSource,
|
private inRectangleHelper: InRectangleHelper,
|
||||||
private drawingsDataSource: DrawingsDataSource,
|
private selectionStore: SelectionStore
|
||||||
private inRectangleHelper: InRectangleHelper
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public subscribe(subject: Subject<Rectangle>) {
|
public subscribe(subject: Subject<Rectangle>) {
|
||||||
@ -45,104 +79,31 @@ export class SelectionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onSelection(rectangle: Rectangle) {
|
public onSelection(rectangle: Rectangle) {
|
||||||
// this.selectedNodes = this.getSelectedItemsInRectangle<MapNode>(this.nodesDataSource, rectangle);
|
const selectedNodes = this.graphDataManager.getNodes().filter((node) => {
|
||||||
// this.selectedLinks = this.getSelectedItemsInRectangle<MapLink>(this.linksDataSource, rectangle);
|
return this.inRectangleHelper.inRectangle(rectangle, node.x, node.y)
|
||||||
// this.selectedDrawings = this.getSelectedItemsInRectangle<MapDrawing>(this.drawingsDataSource, rectangle);
|
});
|
||||||
// don't select interfaces for now
|
|
||||||
// this.selectedInterfaceLabels = this.getSelectedInterfaceLabelsInRectangle(rectangle);
|
const selectedLinks = this.graphDataManager.getLinks().filter((link) => {
|
||||||
|
return this.inRectangleHelper.inRectangle(rectangle, link.x, link.y)
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedDrawings = this.graphDataManager.getDrawings().filter((drawing) => {
|
||||||
|
return this.inRectangleHelper.inRectangle(rectangle, drawing.x, drawing.y)
|
||||||
|
});
|
||||||
|
|
||||||
|
const selected = [...selectedNodes, ...selectedLinks, ...selectedDrawings];
|
||||||
|
|
||||||
|
this.selectionStore.set(selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSelectedNodes() {
|
public getSelected() {
|
||||||
return this.selectedNodes;
|
return this.selectionStore.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSelectedLinks() {
|
public setSelected(value: Indexed[]) {
|
||||||
return this.selectedLinks;
|
this.selectionStore.set(value);
|
||||||
}
|
|
||||||
|
|
||||||
public getSelectedDrawings() {
|
|
||||||
return this.selectedDrawings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setSelectedNodes(nodes: MapNode[]) {
|
|
||||||
// this.selectedNodes = this.setSelectedItems<MapNode>(this.nodesDataSource, (node: MapNode) => {
|
|
||||||
// return !!nodes.find((n: MapNode) => node.id === n.id);
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
public setSelectedLinks(links: MapLink[]) {
|
|
||||||
// this.selectedLinks = this.setSelectedItems<MapLink>(this.linksDataSource, (link: MapLink) => {
|
|
||||||
// return !!links.find((l: MapLink) => link.link_id === l.link_id);
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
public setSelectedDrawings(drawings: MapDrawing[]) {
|
|
||||||
// this.selectedDrawings = this.setSelectedItems<MapDrawing>(this.drawingsDataSource, (drawing: MapDrawing) => {
|
|
||||||
// return !!drawings.find((d: MapDrawing) => drawing.drawing_id === d.drawing_id);
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public clearSelection() {
|
public clearSelection() {
|
||||||
this.setSelectedDrawings([]);
|
|
||||||
this.setSelectedLinks([]);
|
|
||||||
this.setSelectedNodes([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getSelectedItemsInRectangle<T extends Selectable>(dataSource: DataSource<T>, rectangle: Rectangle) {
|
|
||||||
return this.setSelectedItems<T>(dataSource, (item: T) => {
|
|
||||||
return this.inRectangleHelper.inRectangle(rectangle, item.x, item.y);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private getSelectedInterfaceLabelsInRectangle(rectangle: Rectangle) {
|
|
||||||
// this.linksDataSource.getItems().forEach((link: MapLink) => {
|
|
||||||
// if (!(link.source && link.target && link.nodes.length > 1)) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let updated = false;
|
|
||||||
|
|
||||||
// let x = link.source.x + link.nodes[0].label.x;
|
|
||||||
// let y = link.source.y + link.nodes[0].label.y;
|
|
||||||
|
|
||||||
// if (this.inRectangleHelper.inRectangle(rectangle, x, y)) {
|
|
||||||
// link.nodes[0].label.is_selected = true;
|
|
||||||
// updated = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// x = link.target.x + link.nodes[1].label.x;
|
|
||||||
// y = link.target.y + link.nodes[1].label.y;
|
|
||||||
|
|
||||||
// if (this.inRectangleHelper.inRectangle(rectangle, x, y)) {
|
|
||||||
// link.nodes[1].label.is_selected = true;
|
|
||||||
// updated = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (updated) {
|
|
||||||
// this.linksDataSource.update(link);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
private setSelected<T extends Selectable>(item: T, isSelected: boolean, dataSource: DataSource<T>): boolean {
|
|
||||||
if (item.is_selected !== isSelected) {
|
|
||||||
item.is_selected = isSelected;
|
|
||||||
dataSource.update(item);
|
|
||||||
}
|
|
||||||
return item.is_selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
private setSelectedItems<T extends Selectable>(dataSource: DataSource<T>, discriminator: (item: T) => boolean) {
|
|
||||||
const selected: T[] = [];
|
|
||||||
dataSource.getItems().forEach((item: T) => {
|
|
||||||
const isSelected = discriminator(item);
|
|
||||||
this.setSelected<T>(item, isSelected, dataSource);
|
|
||||||
if (isSelected) {
|
|
||||||
selected.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return selected;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { Selectable } from "../managers/selection-manager";
|
|
||||||
import { DrawingElement } from "./drawings/drawing-element";
|
import { DrawingElement } from "./drawings/drawing-element";
|
||||||
|
|
||||||
export class Drawing implements Selectable {
|
export class Drawing {
|
||||||
drawing_id: string;
|
drawing_id: string;
|
||||||
project_id: string;
|
project_id: string;
|
||||||
rotation: number;
|
rotation: number;
|
||||||
@ -9,6 +8,5 @@ export class Drawing implements Selectable {
|
|||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
z: number;
|
z: number;
|
||||||
is_selected = false;
|
|
||||||
element: DrawingElement; // @todo; move to context
|
element: DrawingElement; // @todo; move to context
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import { Selectable } from "../managers/selection-manager";
|
export class InterfaceLabel {
|
||||||
|
|
||||||
export class InterfaceLabel implements Selectable {
|
|
||||||
constructor(
|
constructor(
|
||||||
public link_id: string,
|
public link_id: string,
|
||||||
public direction: string,
|
public direction: string,
|
||||||
@ -9,6 +7,5 @@ export class InterfaceLabel implements Selectable {
|
|||||||
public text: string,
|
public text: string,
|
||||||
public style: string,
|
public style: string,
|
||||||
public rotation = 0,
|
public rotation = 0,
|
||||||
public is_selected = false
|
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import { Selectable } from "../managers/selection-manager";
|
export class Label {
|
||||||
|
|
||||||
export class Label implements Selectable {
|
|
||||||
rotation: number;
|
rotation: number;
|
||||||
style: string;
|
style: string;
|
||||||
text: string;
|
text: string;
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
is_selected: boolean;
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { DrawingElement } from "../drawings/drawing-element";
|
import { DrawingElement } from "../drawings/drawing-element";
|
||||||
|
import { Indexed } from "../../datasources/map-datasource";
|
||||||
|
|
||||||
export class MapDrawing {
|
export class MapDrawing implements Indexed {
|
||||||
id: string;
|
id: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
rotation: number;
|
rotation: number;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { MapLinkNode } from "./map-link-node";
|
import { MapLinkNode } from "./map-link-node";
|
||||||
import { MapNode } from "./map-node";
|
import { MapNode } from "./map-node";
|
||||||
|
import { Indexed } from "../../datasources/map-datasource";
|
||||||
|
|
||||||
export class MapLink {
|
export class MapLink implements Indexed {
|
||||||
id: string;
|
id: string;
|
||||||
captureFileName: string;
|
captureFileName: string;
|
||||||
captureFilePath: string;
|
captureFilePath: string;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { MapLabel } from "./map-label";
|
import { MapLabel } from "./map-label";
|
||||||
import { MapPort } from "./map-port";
|
import { MapPort } from "./map-port";
|
||||||
|
import { Indexed } from "../../datasources/map-datasource";
|
||||||
|
|
||||||
export class MapNode {
|
export class MapNode implements Indexed {
|
||||||
id: string;
|
id: string;
|
||||||
commandLine: string;
|
commandLine: string;
|
||||||
computeId: string;
|
computeId: string;
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
export class MapSymbol {
|
import { Indexed } from "../../datasources/map-datasource";
|
||||||
|
|
||||||
|
export class MapSymbol implements Indexed {
|
||||||
id: string;
|
id: string;
|
||||||
builtin: boolean;
|
builtin: boolean;
|
||||||
filename: string;
|
filename: string;
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import { Selectable } from "../managers/selection-manager";
|
|
||||||
import { Label } from "./label";
|
import { Label } from "./label";
|
||||||
import { Port } from "../../models/port";
|
import { Port } from "../../models/port";
|
||||||
|
|
||||||
|
|
||||||
export class Node implements Selectable {
|
export class Node {
|
||||||
command_line: string;
|
command_line: string;
|
||||||
compute_id: string;
|
compute_id: string;
|
||||||
console: number;
|
console: number;
|
||||||
@ -26,5 +25,4 @@ export class Node implements Selectable {
|
|||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
z: number;
|
z: number;
|
||||||
is_selected = false;
|
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,11 @@ import { Injectable, EventEmitter } from "@angular/core";
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MapChangeDetectorRef {
|
export class MapChangeDetectorRef {
|
||||||
public changesDetected = new EventEmitter<boolean>();
|
public changesDetected = new EventEmitter<boolean>();
|
||||||
|
|
||||||
public hasBeenDrawn = false;
|
public hasBeenDrawn = false;
|
||||||
|
|
||||||
public detectChanges() {
|
public detectChanges() {
|
||||||
this.changesDetected.emit(true);
|
this.changesDetected.emit(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,42 +8,20 @@ import { MovingTool } from "../tools/moving-tool";
|
|||||||
import { LayersWidget } from "./layers";
|
import { LayersWidget } from "./layers";
|
||||||
import { LayersManager } from "../managers/layers-manager";
|
import { LayersManager } from "../managers/layers-manager";
|
||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
import { MapNode } from "../models/map/map-node";
|
|
||||||
import { MapLink } from "../models/map/map-link";
|
|
||||||
import { MapDrawing } from "../models/map/map-drawing";
|
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class GraphLayout implements Widget {
|
export class GraphLayout implements Widget {
|
||||||
private nodes: MapNode[] = [];
|
|
||||||
private links: MapLink[] = [];
|
|
||||||
private drawings: MapDrawing[] = [];
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private nodesWidget: NodesWidget,
|
private nodesWidget: NodesWidget,
|
||||||
private drawingLineTool: DrawingLineWidget,
|
private drawingLineTool: DrawingLineWidget,
|
||||||
private selectionTool: SelectionTool,
|
private selectionTool: SelectionTool,
|
||||||
private movingTool: MovingTool,
|
private movingTool: MovingTool,
|
||||||
private layersWidget: LayersWidget
|
private layersWidget: LayersWidget,
|
||||||
|
private layersManager: LayersManager
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public setNodes(nodes: MapNode[]) {
|
|
||||||
this.nodes = nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setLinks(links: MapLink[]) {
|
|
||||||
this.links = links;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getLinks() {
|
|
||||||
return this.links;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setDrawings(drawings: MapDrawing[]) {
|
|
||||||
this.drawings = drawings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getNodesWidget() {
|
public getNodesWidget() {
|
||||||
return this.nodesWidget;
|
return this.nodesWidget;
|
||||||
}
|
}
|
||||||
@ -84,13 +62,7 @@ export class GraphLayout implements Widget {
|
|||||||
return `translate(${xTrans}, ${yTrans}) scale(${kTrans})`;
|
return `translate(${xTrans}, ${yTrans}) scale(${kTrans})`;
|
||||||
});
|
});
|
||||||
|
|
||||||
// @fix me
|
this.layersWidget.draw(canvas, this.layersManager.getLayersList());
|
||||||
const layersManager = new LayersManager();
|
|
||||||
layersManager.setNodes(this.nodes);
|
|
||||||
layersManager.setDrawings(this.drawings);
|
|
||||||
layersManager.setLinks(this.links);
|
|
||||||
|
|
||||||
this.layersWidget.draw(canvas, layersManager.getLayersList());
|
|
||||||
|
|
||||||
this.drawingLineTool.draw(view, context);
|
this.drawingLineTool.draw(view, context);
|
||||||
this.selectionTool.draw(view, context);
|
this.selectionTool.draw(view, context);
|
||||||
|
@ -33,8 +33,7 @@ export class InterfaceLabelWidget {
|
|||||||
Math.round(l.source.y + l.nodes[0].label.y),
|
Math.round(l.source.y + l.nodes[0].label.y),
|
||||||
l.nodes[0].label.text,
|
l.nodes[0].label.text,
|
||||||
l.nodes[0].label.style,
|
l.nodes[0].label.style,
|
||||||
l.nodes[0].label.rotation,
|
l.nodes[0].label.rotation
|
||||||
l.nodes[0].label.isSelected
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const targetInterface = new InterfaceLabel(
|
const targetInterface = new InterfaceLabel(
|
||||||
@ -44,8 +43,7 @@ export class InterfaceLabelWidget {
|
|||||||
Math.round( l.target.y + l.nodes[1].label.y),
|
Math.round( l.target.y + l.nodes[1].label.y),
|
||||||
l.nodes[1].label.text,
|
l.nodes[1].label.text,
|
||||||
l.nodes[1].label.style,
|
l.nodes[1].label.style,
|
||||||
l.nodes[1].label.rotation,
|
l.nodes[1].label.rotation
|
||||||
l.nodes[1].label.isSelected
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.enabled) {
|
if (this.enabled) {
|
||||||
@ -81,7 +79,7 @@ export class InterfaceLabelWidget {
|
|||||||
const y = l.y + bbox.height;
|
const y = l.y + bbox.height;
|
||||||
return `translate(${x}, ${y}) rotate(${l.rotation}, ${x}, ${y})`;
|
return `translate(${x}, ${y}) rotate(${l.rotation}, ${x}, ${y})`;
|
||||||
})
|
})
|
||||||
.classed('selected', (l: InterfaceLabel) => l.is_selected);
|
.classed('selected', (l: InterfaceLabel) => false);
|
||||||
|
|
||||||
// update label
|
// update label
|
||||||
merge
|
merge
|
||||||
@ -95,7 +93,7 @@ export class InterfaceLabelWidget {
|
|||||||
// update surrounding rect
|
// update surrounding rect
|
||||||
merge
|
merge
|
||||||
.select<SVGRectElement>('rect.interface_label_border')
|
.select<SVGRectElement>('rect.interface_label_border')
|
||||||
.attr('visibility', (l: InterfaceLabel) => l.is_selected ? 'visible' : 'hidden')
|
.attr('visibility', (l: InterfaceLabel) => false ? 'visible' : 'hidden')
|
||||||
.attr('stroke-dasharray', '3,3')
|
.attr('stroke-dasharray', '3,3')
|
||||||
.attr('stroke-width', '0.5')
|
.attr('stroke-width', '0.5')
|
||||||
.each(function (this: SVGRectElement, l: InterfaceLabel) {
|
.each(function (this: SVGRectElement, l: InterfaceLabel) {
|
||||||
|
@ -8,6 +8,7 @@ import { MultiLinkCalculatorHelper } from "../helpers/multi-link-calculator-help
|
|||||||
import { InterfaceLabelWidget } from "./interface-label";
|
import { InterfaceLabelWidget } from "./interface-label";
|
||||||
import { InterfaceStatusWidget } from "./interface-status";
|
import { InterfaceStatusWidget } from "./interface-status";
|
||||||
import { MapLink } from "../models/map/map-link";
|
import { MapLink } from "../models/map/map-link";
|
||||||
|
import { SelectionStore } from "../managers/selection-manager";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -16,7 +17,8 @@ export class LinkWidget implements Widget {
|
|||||||
constructor(
|
constructor(
|
||||||
private multiLinkCalculatorHelper: MultiLinkCalculatorHelper,
|
private multiLinkCalculatorHelper: MultiLinkCalculatorHelper,
|
||||||
private interfaceLabelWidget: InterfaceLabelWidget,
|
private interfaceLabelWidget: InterfaceLabelWidget,
|
||||||
private interfaceStatusWidget: InterfaceStatusWidget
|
private interfaceStatusWidget: InterfaceStatusWidget,
|
||||||
|
private selectionStore: SelectionStore
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public draw(view: SVGSelection) {
|
public draw(view: SVGSelection) {
|
||||||
@ -41,7 +43,7 @@ export class LinkWidget implements Widget {
|
|||||||
|
|
||||||
link_body_merge
|
link_body_merge
|
||||||
.select<SVGPathElement>('path')
|
.select<SVGPathElement>('path')
|
||||||
.classed('selected', (l: MapLink) => l.isSelected);
|
.classed('selected', (l: MapLink) => this.selectionStore.isSelected(l));
|
||||||
|
|
||||||
this.interfaceLabelWidget.draw(link_body_merge);
|
this.interfaceLabelWidget.draw(link_body_merge);
|
||||||
this.interfaceStatusWidget.draw(link_body_merge);
|
this.interfaceStatusWidget.draw(link_body_merge);
|
||||||
|
@ -8,6 +8,8 @@ import { FontFixer } from "../helpers/font-fixer";
|
|||||||
import { select, event } from "d3-selection";
|
import { select, event } from "d3-selection";
|
||||||
import { MapSymbol } from "../models/map/map-symbol";
|
import { MapSymbol } from "../models/map/map-symbol";
|
||||||
import { MapNode } from "../models/map/map-node";
|
import { MapNode } from "../models/map/map-node";
|
||||||
|
import { GraphDataManager } from "../managers/graph-data-manager";
|
||||||
|
import { SelectionStore } from "../managers/selection-manager";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -19,17 +21,13 @@ export class NodeWidget implements Widget {
|
|||||||
public onNodeDragged = new EventEmitter<NodeDragged>();
|
public onNodeDragged = new EventEmitter<NodeDragged>();
|
||||||
public onNodeDragging = new EventEmitter<NodeDragging>();
|
public onNodeDragging = new EventEmitter<NodeDragging>();
|
||||||
|
|
||||||
private symbols: MapSymbol[] = [];
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private cssFixer: CssFixer,
|
private cssFixer: CssFixer,
|
||||||
private fontFixer: FontFixer,
|
private fontFixer: FontFixer,
|
||||||
|
private graphDataManager: GraphDataManager,
|
||||||
|
private selectionStore: SelectionStore
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public setSymbols(symbols: MapSymbol[]) {
|
|
||||||
this.symbols = symbols;
|
|
||||||
}
|
|
||||||
|
|
||||||
public draw(view: SVGSelection) {
|
public draw(view: SVGSelection) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
@ -49,7 +47,7 @@ export class NodeWidget implements Widget {
|
|||||||
.attr('class', 'label');
|
.attr('class', 'label');
|
||||||
|
|
||||||
const node_body_merge = node_body.merge(node_body_enter)
|
const node_body_merge = node_body.merge(node_body_enter)
|
||||||
.classed('selected', (n: MapNode) => n.isSelected)
|
.classed('selected', (n: MapNode) => this.selectionStore.isSelected(n))
|
||||||
.on("contextmenu", function (n: MapNode, i: number) {
|
.on("contextmenu", function (n: MapNode, i: number) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
self.onContextMenu.emit(new NodeContextMenu(event, n));
|
self.onContextMenu.emit(new NodeContextMenu(event, n));
|
||||||
@ -62,7 +60,7 @@ export class NodeWidget implements Widget {
|
|||||||
node_body_merge
|
node_body_merge
|
||||||
.select<SVGImageElement>('image')
|
.select<SVGImageElement>('image')
|
||||||
.attr('xnode:href', (n: MapNode) => {
|
.attr('xnode:href', (n: MapNode) => {
|
||||||
const symbol = this.symbols.find((s: MapSymbol) => s.id === n.symbol);
|
const symbol = this.graphDataManager.getSymbols().find((s: MapSymbol) => s.id === n.symbol);
|
||||||
if (symbol) {
|
if (symbol) {
|
||||||
return 'data:image/svg+xml;base64,' + btoa(symbol.raw);
|
return 'data:image/svg+xml;base64,' + btoa(symbol.raw);
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,6 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
private projectWebServiceHandler: ProjectWebServiceHandler,
|
private projectWebServiceHandler: ProjectWebServiceHandler,
|
||||||
private mapChangeDetectorRef: MapChangeDetectorRef,
|
private mapChangeDetectorRef: MapChangeDetectorRef,
|
||||||
private nodeWidget: NodeWidget,
|
private nodeWidget: NodeWidget,
|
||||||
private selectionManager: SelectionManager,
|
|
||||||
private mapNodeToNode: MapNodeToNodeConverter,
|
private mapNodeToNode: MapNodeToNodeConverter,
|
||||||
protected nodesDataSource: NodesDataSource,
|
protected nodesDataSource: NodesDataSource,
|
||||||
protected linksDataSource: LinksDataSource,
|
protected linksDataSource: LinksDataSource,
|
||||||
@ -137,6 +136,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
this.subscriptions.push(
|
this.subscriptions.push(
|
||||||
this.nodesDataSource.changes.subscribe((nodes: Node[]) => {
|
this.nodesDataSource.changes.subscribe((nodes: Node[]) => {
|
||||||
this.nodes = nodes;
|
this.nodes = nodes;
|
||||||
|
console.log("update nodes");
|
||||||
this.mapChangeDetectorRef.detectChanges();
|
this.mapChangeDetectorRef.detectChanges();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -198,12 +198,6 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.subscriptions.push(onContextMenu);
|
this.subscriptions.push(onContextMenu);
|
||||||
|
|
||||||
this.subscriptions.push(
|
|
||||||
this.selectionManager.subscribe(
|
|
||||||
this.mapChild.graphLayout.getSelectionTool().rectangleSelected)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.mapChangeDetectorRef.detectChanges();
|
this.mapChangeDetectorRef.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,22 +213,26 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onNodeDragged(draggedEvent: DraggedDataEvent<Node>) {
|
onNodeDragged(draggedEvent: DraggedDataEvent<Node[]>) {
|
||||||
this.nodesDataSource.update(draggedEvent.datum);
|
draggedEvent.datum.forEach((node: Node) => {
|
||||||
this.nodeService
|
this.nodesDataSource.update(node);
|
||||||
.updatePosition(this.server, draggedEvent.datum, draggedEvent.datum.x, draggedEvent.datum.y)
|
this.nodeService
|
||||||
.subscribe((node: Node) => {
|
.updatePosition(this.server, node, node.x, node.y)
|
||||||
this.nodesDataSource.update(node);
|
.subscribe((serverNode: Node) => {
|
||||||
});
|
this.nodesDataSource.update(serverNode);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onDrawingDragged(draggedEvent: DraggedDataEvent<Drawing>) {
|
onDrawingDragged(draggedEvent: DraggedDataEvent<Drawing[]>) {
|
||||||
this.drawingsDataSource.update(draggedEvent.datum);
|
draggedEvent.datum.forEach((drawing: Drawing) => {
|
||||||
this.drawingService
|
this.drawingsDataSource.update(drawing);
|
||||||
.updatePosition(this.server, draggedEvent.datum, draggedEvent.datum.x, draggedEvent.datum.y)
|
this.drawingService
|
||||||
.subscribe((drawing: Drawing) => {
|
.updatePosition(this.server, drawing, drawing.x, drawing.y)
|
||||||
this.drawingsDataSource.update(drawing);
|
.subscribe((serverDrawing: Drawing) => {
|
||||||
});
|
this.drawingsDataSource.update(serverDrawing);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public set readonly(value) {
|
public set readonly(value) {
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import { Node } from "../cartography/models/node";
|
import { Node } from "../cartography/models/node";
|
||||||
import { LinkNode } from "./link-node";
|
import { LinkNode } from "./link-node";
|
||||||
import { Selectable } from "../cartography/managers/selection-manager";
|
|
||||||
|
|
||||||
|
|
||||||
export class Link implements Selectable {
|
export class Link {
|
||||||
capture_file_name: string;
|
capture_file_name: string;
|
||||||
capture_file_path: string;
|
capture_file_path: string;
|
||||||
capturing: boolean;
|
capturing: boolean;
|
||||||
@ -18,7 +17,6 @@ export class Link implements Selectable {
|
|||||||
source: Node; // this is not from server
|
source: Node; // this is not from server
|
||||||
target: Node; // this is not from server
|
target: Node; // this is not from server
|
||||||
|
|
||||||
is_selected = false; // this is not from server
|
|
||||||
x: number; // this is not from server
|
x: number; // this is not from server
|
||||||
y: number; // this is not from server
|
y: number; // this is not from server
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user