nope/resources/ui/graph/addons/custom.sort.ts

199 lines
5.9 KiB
TypeScript
Raw Normal View History

2020-10-25 20:14:51 +00:00
/**
* @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<N extends IBaseNodeOptions, E extends IBaseEdgeOptions>(network: IUndoRedoGraph<N,E>, 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<N extends IBaseNodeOptions, E extends IBaseEdgeOptions>(
nodeID: string,
network: IUndoRedoGraph<N,E>,
selected: Array<string>,
allowedEdgeTypes = new Array<string>(),
layers: {
[index: number]: {
elements: Array<
{
id: string,
width: number,
height: number,
}
>,
totalWidth: number,
totalHeight: number,
maxHeight: number,
maxWidth: number
}
} = {},
considered = new Array<string>(),
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<N extends IBaseNodeOptions, E extends IBaseEdgeOptions>(
network: IUndoRedoGraph<N,E>,
selected: Array<string>,
direction: 'LR' | 'UD' = 'UD',
allowedEdgeType: Array<string> = ['logic:consume:token', 'logic:produce:token'],
spacing = 100) {
/** Make Shure the Positions are stored */
network.disableStoringContent = true;
network.network.storePositions();
let considered = new Array<string>();
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();
}