/** * @author Martin Karkowski * @email m.karkowski@zema.de * @create date 2019-06-19 09:31:08 * @modify date 2019-06-19 09:31:08 * @desc [description] */ import { IUndoRedoGraph } from '../interfaces/IGraph'; import { IBaseNodeOptions } from '../interfaces/IBaseNodeOptions'; import { IBaseEdgeOptions } from '../interfaces/IBaseEdgeOptions'; function getEdgeData(network: IUndoRedoGraph, from: string, to: string) { for (const edge of network.edges) { if (edge.from === from && edge.to === to) { return edge; } } return null; } /** * * @param nodeID Node ID which should be checked * @param network The Network Type * @param selected The Elements, which are selected to start sorting the Nodes on this Elements * @param allowedEdgeTypes Edge-Types, that will be included * @param layers * @param considered * @param layer * @param maxWidth * @param maxHeight */ function recursiveAnalyse( nodeID: string, network: IUndoRedoGraph, selected: Array, allowedEdgeTypes = new Array(), layers: { [index: number]: { elements: Array< { id: string, width: number, height: number, } >, totalWidth: number, totalHeight: number, maxHeight: number, maxWidth: number } } = {}, considered = new Array(), layer = 0, spacing = 50) { /** Get the Edges */ const childs = network.network.getConnectedNodes(nodeID, 'to'); const node = network.data.nodes.get(nodeID); if (!layers[layer]) { layers[layer] = { elements: new Array< { id: string, width: number, height: number } >(), totalWidth: 0, totalHeight: 0, maxHeight: Number.MIN_SAFE_INTEGER, maxWidth: Number.MIN_SAFE_INTEGER } } const width = Math.max(node.width || 0, node.size || 0); const height = Math.max(node.height || 0, node.size || 0); /** Update the Layer Information */ layers[layer].elements.push({ id: nodeID, width, height }); layers[layer].totalWidth += width + spacing; layers[layer].totalHeight += height + spacing; layers[layer].maxHeight = Math.max(layers[layer].maxHeight, height) layers[layer].maxWidth = Math.max(layers[layer].maxWidth, width) considered.push(nodeID); const elementsToAdd = []; for (const childID of childs) { if (!considered.includes(childID) && !selected.includes(childID)) { const edgeData = getEdgeData(network, nodeID, childID); if (edgeData && ((edgeData.type && allowedEdgeTypes.length > 0 && allowedEdgeTypes.includes(edgeData.type) )|| (!edgeData.type))) { considered.push(childID) elementsToAdd.push(childID) } } } for (const childID of elementsToAdd) { layers = recursiveAnalyse(childID, network, selected, allowedEdgeTypes, layers, considered, layer + 1, spacing); } return layers } /** * * @param network Network * @param selected Elements to Start with * @param direction The Direction of the Orientation * @param allowedEdgeType Edges, which should not be considerd * @param spacing Spacing between the Elements */ export function placeNodes( network: IUndoRedoGraph, selected: Array, direction: 'LR' | 'UD' = 'UD', allowedEdgeType: Array = ['logic:consume:token', 'logic:produce:token'], spacing = 100) { /** Make Shure the Positions are stored */ network.disableStoringContent = true; network.network.storePositions(); let considered = new Array(); let layers: { [index: number]: { elements: Array< { id: string, width: number, height: number, } >, totalWidth: number, totalHeight: number, maxHeight: number, maxWidth: number } } = {}; for (const nodeID of selected) { layers = recursiveAnalyse(nodeID, network, selected, allowedEdgeType, layers, considered, 0, spacing); } const first = layers[0] ? layers[0].elements[0] || null : null; if (first) { /** Extract the Starting Position */ const pos = network.network.getPositions([first.id])[first.id]; let layerOffset_y = 0; let layerOffset_x = 0; for (const layer of Object.getOwnPropertyNames(layers)) { let inLayerOffset = 0; if (layers[layer].elements.length > 1) { inLayerOffset = layers[layer].elements[0].width / 2 - ((direction === 'UD' ? layers[layer].totalWidth : layers[layer].totalHeight) - spacing) / 2; } for (const element of layers[layer].elements) { switch (direction) { case 'UD': network.network.moveNode(element.id, pos.x + inLayerOffset + layerOffset_x, pos.y + layerOffset_y); inLayerOffset += element.width + spacing; break; default: break; } } switch (direction) { case 'UD': layerOffset_y += (direction === 'UD' ? layers[layer].maxHeight : layers[layer].maxWidth) + spacing break; default: break; } } } network.disableStoringContent = false; network.network.storePositions(); network.save(); }