@syncfusion/ej2-diagrams
Version:
Feature-rich diagram control to create diagrams like flow charts, organizational charts, mind maps, and BPMN diagrams. Its rich feature set includes built-in shapes, editing, serializing, exporting, printing, overview, data binding, and automatic layouts.
1,019 lines (1,018 loc) • 172 kB
JavaScript
import { OrthogonalSegment } from '../objects/connector';
import { Rect as RectModel } from '../primitives/rect';
import { Point } from '../primitives/point';
import { findDistance, getConnectorDirection } from '../utility/diagram-util';
/**
* Connects diagram objects with layout algorithm
*/
var ComplexHierarchicalTree = /** @class */ (function () {
/**
* Constructor for the hierarchical tree layout module
*
* @private
*/
function ComplexHierarchicalTree() {
//constructs the layout module
}
/**
* To destroy the hierarchical tree module
*
* @returns {void}
* @private
*/
ComplexHierarchicalTree.prototype.destroy = function () {
/**
* Destroy method performed here
*/
};
/**
* Core method to return the component name.
*
* @returns {string} Core method to return the component name.
* @private
*/
ComplexHierarchicalTree.prototype.getModuleName = function () {
/**
* Returns the module name of the layout
*
*/
return 'ComplexHierarchicalTree';
};
/**
* doLayout method\
*
* @returns { void } doLayout method .\
* @param {INode[]} nodes - provide the nodes value.
* @param {{}} nameTable - provide the nameTable value.
* @param {Layout} layout - provide the layout value.
* @param {PointModel} viewPort - provide the viewPort value.
* @param {Diagram} diagram - provide the diagram model.
* @private
*/
ComplexHierarchicalTree.prototype.doLayout = function (nodes, nameTable, layout, viewPort, diagram) {
new HierarchicalLayoutUtil().doLayout(nodes, nameTable, layout, viewPort, diagram);
};
ComplexHierarchicalTree.prototype.getLayoutNodesCollection = function (nodes) {
var nodesCollection = [];
var node;
var parentId = 'parentId';
var processId = 'processId';
for (var i = 0; i < nodes.length; i++) {
node = nodes[parseInt(i.toString(), 10)];
//885697:Position of root node without the child node in complex hierarchical layout is not proper
// 941582: ExcludeFromLayout Option Not Supported in Complex Hierarchical Tree
if (!node.excludeFromLayout) {
if (((node.inEdges.length + node.outEdges.length > 0) || (node.offsetX === 0 && node.offsetY === 0)) &&
!node['' + parentId] && !node['' + processId]) {
nodesCollection.push(node);
}
}
}
return nodesCollection;
};
return ComplexHierarchicalTree;
}());
export { ComplexHierarchicalTree };
/**
* Utility that arranges the nodes in hierarchical structure
*/
var HierarchicalLayoutUtil = /** @class */ (function () {
function HierarchicalLayoutUtil() {
this.nameTable = {};
this.vertexTable = {};
this.verticesMap = new Map();
this.crossReduction = new CrossReduction();
/**
* The preferred vertical offset between edges exiting a vertex Default is 2.
*/
this.previousEdgeOffset = 6;
/**
* The preferred horizontal distance between edges exiting a vertex Default is 5.
*/
this.previousEdgeDistance = 5;
/**
* Holds the collection vertices, that are equivalent to nodes to be arranged
*/
this.jettyPositions = {};
/**
* Internal cache of bottom-most value of Y for each rank
*/
this.rankBottomY = null;
/**
* Internal cache of bottom-most value of X for each rank
*/
this.limitX = null;
/**
* Internal cache of top-most values of Y for each rank
*/
this.rankTopY = null;
/**
* The minimum parallelEdgeSpacing value is 12.
*/
this.parallelEdgeSpacing = 10;
/**
* The minimum distance for an edge jetty from a vertex Default is 12.
*/
this.minEdgeJetty = 12;
}
//Defines a vertex that is equivalent to a node object
HierarchicalLayoutUtil.prototype.createVertex = function (node, value, x, y, width, height) {
var geometry = { x: x, y: y, width: width, height: height };
var vertex = {
value: value, geometry: geometry, name: value, vertex: true,
inEdges: node.inEdges.slice(), outEdges: node.outEdges.slice(),
maxRowWidth: undefined, maxRowHeight: undefined
};
return vertex;
};
/**
* Initializes the edges collection of the vertices\
*
* @returns { IConnector[] } Initializes the edges collection of the vertices\
* @param {Vertex} node - provide the node value.
* @private
*/
HierarchicalLayoutUtil.prototype.getEdges = function (node) {
var edges = [];
var node1 = this.nameTable[node.name];
// 941582: ExcludeFromLayout Option Not Supported in Complex Hierarchical Tree
if (node && !node1.excludeFromLayout) {
for (var i = 0; node.inEdges.length > 0 && i < node.inEdges.length; i++) {
var connector = this.nameTable[node.inEdges[parseInt(i.toString(), 10)]];
if (!this.nameTable[connector.sourceID].excludeFromLayout) {
edges.push(connector);
}
}
for (var i = 0; node.outEdges.length > 0 && i < node.outEdges.length; i++) {
var connector = this.nameTable[node.outEdges[parseInt(i.toString(), 10)]];
if (!this.nameTable[connector.targetID].excludeFromLayout) {
edges.push(connector);
}
}
}
return edges;
};
//Finds the root nodes of the layout
HierarchicalLayoutUtil.prototype.findRoots = function (vertices, isMatrixArrangment) {
var roots = [];
var best = null;
var maxDiff = -100000;
for (var _i = 0, _a = Object.keys(vertices); _i < _a.length; _i++) {
var i = _a[_i];
var cell = vertices["" + i];
var conns = this.getEdges(cell);
var outEdges = 0;
var inEdges = 0;
for (var k = 0; k < conns.length; k++) {
var src = this.getVisibleTerminal(conns[parseInt(k.toString(), 10)], true);
if (src.name === cell.name) {
outEdges++;
}
else {
inEdges++;
}
}
//951668 - Independent nodes get overlapped in complex hierarchical tree layout
if (inEdges === 0 && (isMatrixArrangment || outEdges > 0)) {
roots.push(cell);
}
var diff = outEdges - inEdges;
if (diff > maxDiff) {
maxDiff = diff;
best = cell;
}
}
if (roots.length === 0 && best != null) {
roots.push(best);
}
return roots;
};
/**
* Returns the source/target vertex of the given connector \
*
* @returns { Vertex } Returns the source/target vertex of the given connector \
* @param {IConnector} edge - provide the node value.
* @param {boolean} source - provide the node value.
* @private
*/
HierarchicalLayoutUtil.prototype.getVisibleTerminal = function (edge, source) {
var terminalCache = this.nameTable[edge.targetID];
if (source) {
terminalCache = this.nameTable[edge.sourceID];
}
//Removed loop and used Map
return this.verticesMap.get(terminalCache.id) || null;
};
/**
* Traverses each sub tree, ensures there is no cycle in traversing \
*
* @returns { {} } Traverses each sub tree, ensures there is no cycle in traversing .\
* @param {Vertex} vertex - provide the vertex value.
* @param {boolean} directed - provide the directed value.
* @param {IConnector} edge - provide the edge value.
* @param {{}} currentComp - provide the currentComp value.
* @param {{}[]} hierarchyVertices - provide the hierarchyVertices value.
* @param {{}} filledVertices - provide the filledVertices value.
* @private
*/
HierarchicalLayoutUtil.prototype.traverse = function (vertex, directed, edge, currentComp, hierarchyVertices, filledVertices) {
if (vertex != null) {
var vertexID = vertex.name;
if ((filledVertices == null ? true : filledVertices["" + vertexID] != null)) {
if (currentComp["" + vertexID] == null) {
currentComp["" + vertexID] = vertex;
}
if (filledVertices != null) {
delete filledVertices["" + vertexID];
}
var edges = this.getEdges(vertex);
var edgeIsSource = [];
for (var i = 0; i < edges.length; i++) {
edgeIsSource[parseInt(i.toString(), 10)] = this.getVisibleTerminal(edges[parseInt(i.toString(), 10)], true) === vertex;
}
for (var i = 0; i < edges.length; i++) {
if (!directed || edgeIsSource[parseInt(i.toString(), 10)]) {
var next = this.getVisibleTerminal(edges[parseInt(i.toString(), 10)], !edgeIsSource[parseInt(i.toString(), 10)]);
var netCount = 1;
for (var j = 0; j < edges.length; j++) {
if (j === i) {
continue;
}
else {
var isSource2 = edgeIsSource[parseInt(j.toString(), 10)];
var otherTerm = this.getVisibleTerminal(edges[parseInt(j.toString(), 10)], !isSource2);
if (otherTerm === next) {
if (isSource2) {
netCount++;
}
else {
netCount--;
}
}
}
}
if (netCount >= 0) {
currentComp = this.traverse(next, directed, edges[parseInt(i.toString(), 10)], currentComp, hierarchyVertices, filledVertices);
}
}
}
}
else {
if (currentComp["" + vertexID] == null) {
// We've seen this vertex before, but not in the current component This component and the one it's in need to be merged
for (var i = 0; i < hierarchyVertices.length; i++) {
var comp = hierarchyVertices[parseInt(i.toString(), 10)];
if (comp["" + vertexID] != null) {
for (var _i = 0, _a = Object.keys(comp); _i < _a.length; _i++) {
var key = _a[_i];
currentComp["" + key] = comp["" + key];
}
// Remove the current component from the hierarchy set
hierarchyVertices.splice(i, 1);
return currentComp;
}
}
}
}
}
return currentComp;
};
//Returns the bounds of the given vertices
HierarchicalLayoutUtil.prototype.getModelBounds = function (nodes) {
nodes = nodes.slice();
var rect = null;
var rect1 = null;
for (var i = 0; i < nodes.length; i++) {
rect = nodes[parseInt(i.toString(), 10)].geometry;
if (rect1) {
var right = Math.max(rect1.x + rect1.width, rect.x + rect.width);
var bottom = Math.max(rect1.y + rect1.height, rect.y + rect.height);
rect1.x = Math.min(rect1.x, rect.x);
rect1.y = Math.min(rect1.y, rect.y);
rect1.width = right - rect1.x;
rect1.height = bottom - rect1.y;
}
else {
rect1 = { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
}
}
return rect1;
};
/* tslint:disable */
/**
* Initializes the layouting process \
*
* @returns { Vertex } Initializes the layouting process \
* @param {INode[]} nodes - provide the node value.
* @param {{}} nameTable - provide the nameTable value.
* @param {Layout} layoutProp - provide the layoutProp value.
* @param {PointModel} viewPort - provide the viewPort value.
* @param {Diagram} diagram - provide the diagram model.
* @private
*/
HierarchicalLayoutUtil.prototype.doLayout = function (nodes, nameTable, layoutProp, viewPort, diagram) {
this.nameTable = nameTable;
var canEnableRouting = layoutProp.enableRouting;
var layout = {
horizontalSpacing: layoutProp.horizontalSpacing, verticalSpacing: layoutProp.verticalSpacing,
orientation: layoutProp.orientation, marginX: layoutProp.margin.left, marginY: layoutProp.margin.top,
enableLayoutRouting: canEnableRouting
};
var model;
var matrixModel = new MatrixModel();
matrixModel.edgeMapper = [];
matrixModel.diagram = diagram;
var nodeWithMultiEdges = [];
this.vertices = [];
var filledVertexSet = {};
for (var i = 0; i < nodes.length; i++) {
var node1 = this.nameTable[nodes[parseInt(i.toString(), 10)].id];
// 941582: ExcludeFromLayout Option Not Supported in Complex Hierarchical Tree
if (!node1.excludeFromLayout) {
var node = this.createVertex(nodes[parseInt(i.toString(), 10)], nodes[parseInt(i.toString(), 10)].id, 0, 0, nodes[parseInt(i.toString(), 10)].actualSize.width, nodes[parseInt(i.toString(), 10)].actualSize.height);
this.vertices.push(node);
this.verticesMap.set(node.name, node);
this.vertexTable[node1.id] = node;
if (nodes[parseInt(i.toString(), 10)].inEdges.length > 0
|| nodes[parseInt(i.toString(), 10)].outEdges.length > 0) {
nodeWithMultiEdges.push(nodes[parseInt(i.toString(), 10)]);
}
filledVertexSet[node.name] = node;
if (matrixModel) {
var outEdges = nodes[parseInt(i.toString(), 10)].outEdges.slice();
for (var j = 0; j < outEdges.length; j++) {
var outEdge = nameTable[outEdges[parseInt(j.toString(), 10)]];
matrixModel.setEdgeMapper({ key: outEdge, value: [] });
}
}
}
}
var hierarchyVertices = [];
//let candidateRoots: Vertex[];
var isMatrixArrangement = layoutProp.arrangement === 'Linear' || canEnableRouting;
isMatrixArrangement = isMatrixArrangement || (matrixModel && layoutProp.connectionPointOrigin === 'DifferentPoint');
var candidateRoots = this.findRoots(filledVertexSet, isMatrixArrangement);
for (var i = 0; i < candidateRoots.length; i++) {
var vertexSet = {};
hierarchyVertices.push(vertexSet);
this.traverse(candidateRoots[parseInt(i.toString(), 10)], true, null, vertexSet, hierarchyVertices, filledVertexSet);
}
var limit = { marginX: 0, marginY: 0 };
var tmp = [];
var checkLinear = false;
var matrixModelObject;
for (var i = 0; i < hierarchyVertices.length; i++) {
var vertexSet = hierarchyVertices[parseInt(i.toString(), 10)];
// eslint-disable-next-line
for (var _i = 0, _a = Object.keys(vertexSet); _i < _a.length; _i++) {
var key = _a[_i];
tmp.push(vertexSet["" + key]);
}
if ((layoutProp.arrangement === 'Linear' && i === hierarchyVertices.length - 1) || canEnableRouting) {
checkLinear = true;
}
model = new MultiParentModel(this, tmp, candidateRoots, layout);
this.cycleStage(model);
this.layeringStage(model);
var ranks = model.ranks;
for (var x = ranks.length - 1; x >= 0; x--) {
var maxWidth = 0;
var maxHeight = 0;
for (var _b = 0, _c = ranks[parseInt(x.toString(), 10)]; _b < _c.length; _b++) {
var rank = _c[_b];
if (this.getType(rank.type) === 'internalVertex') {
var geometry = rank.cell.geometry;
maxWidth = Math.max(maxWidth, geometry.width);
maxHeight = Math.max(maxHeight, geometry.height);
}
}
for (var _d = 0, _e = ranks[parseInt(x.toString(), 10)]; _d < _e.length; _d++) {
var rank = _e[_d];
if (this.getType(rank.type) === 'internalVertex') {
var cell = rank.cell;
cell.maxRowWidth = maxWidth;
cell.maxRowHeight = maxHeight;
}
}
}
//897503: Child Nodes position in ComplexHierarchicalTree updated wrongly results in connector overlap
if ((matrixModel && layoutProp.connectionPointOrigin === 'DifferentPoint') || checkLinear) {
matrixModelObject = { model: model, matrix: [], rowOffset: [], roots: [] };
matrixModel.arrangeElements(matrixModelObject, layoutProp);
layoutProp.ranks = matrixModelObject.model.ranks;
}
else {
if (layoutProp.arrangement === 'Nonlinear') {
this.crossingStage(model);
limit = this.placementStage(model, limit.marginX, limit.marginY);
tmp = [];
}
}
}
var modelBounds = this.getModelBounds(this.vertices);
this.updateMargin(layoutProp, layout, modelBounds, viewPort);
for (var i = 0; i < this.vertices.length; i++) {
var clnode = this.vertices[parseInt(i.toString(), 10)];
if (clnode) { //Check what is node.source/node.target - && !clnode.source && !clnode.target) {
var dnode = this.nameTable[clnode.name];
dnode.offsetX = 0;
dnode.offsetY = 0;
//initialize layout
var dx = (clnode.geometry.x - (dnode.offsetX - (dnode.actualSize.width / 2))) + layout.marginX;
var dy = (clnode.geometry.y - (dnode.offsetY - (dnode.actualSize.height / 2))) + layout.marginY;
var x = dx;
var y = dy;
if (layout.orientation === 'BottomToTop') {
if (canEnableRouting) {
clnode.geometry.y = modelBounds.height - dy - dnode.actualSize.height / 2;
}
y = modelBounds.height - dy;
}
else if (layout.orientation === 'RightToLeft') {
x = modelBounds.width - dx;
}
dnode.offsetX += x - dnode.offsetX;
dnode.offsetY += y - dnode.offsetY;
if (layoutProp.horizontalAlignment !== 'Stretch' || layoutProp.verticalAlignment !== 'Stretch') {
matrixModel.nodePropertyChange(dnode);
}
}
}
if (!checkLinear) {
for (var i = 0; i < this.vertices.length; i++) {
this.isNodeOverLap(this.nameTable[this.vertices[parseInt(i.toString(), 10)].name], layoutProp);
}
}
matrixModel.updateLayout(viewPort, modelBounds, layoutProp, layout, nodeWithMultiEdges, nameTable, this.vertexTable);
if (canEnableRouting) {
var vertices = {};
var matrixrow1 = void 0;
for (var p = 0; p < matrixModelObject.matrix.length; p++) {
matrixrow1 = matrixModelObject.matrix[parseInt(p.toString(), 10)].value;
for (var q = 0; q < matrixrow1.length; q++) {
var matrixCell = matrixrow1[parseInt(q.toString(), 10)];
for (var r = 0; r < matrixCell.cells.length; r++) {
var cell = matrixCell.cells[parseInt(r.toString(), 10)];
var type = this.getType(cell.type);
if (type === 'internalVertex') {
var internalVertex = cell;
vertices[internalVertex.id] = internalVertex;
}
}
}
}
this.updateRankValuess(model);
for (var i = 0, a = Object.keys(vertices); i < a.length; i++) {
var key = a[parseInt(i.toString(), 10)];
this.setVertexLocationValue(vertices["" + key], layoutProp.orientation, modelBounds);
}
this.localEdgeProcessing(model, vertices);
this.assignRankOffset(model);
this.updateEdgeSetXYValue(model);
var edges = this.getValues(model.edgeMapper);
for (var i = 0; i < edges.length; i++) {
if ((edges[parseInt(i.toString(), 10)]).x.length > 0) {
for (var j = 0; j < (edges[parseInt(i.toString(), 10)]).x.length; j++) {
if (layoutProp.orientation !== 'RightToLeft' && layoutProp.orientation !== 'LeftToRight') {
(edges[parseInt(i.toString(), 10)]).x[parseInt(j.toString(), 10)]
= (edges[parseInt(i.toString(), 10)]).x[parseInt(j.toString(), 10)] + layout.marginX;
}
else if (layoutProp.orientation === 'LeftToRight') {
(edges[parseInt(i.toString(), 10)]).x[parseInt(j.toString(), 10)]
= (edges[parseInt(i.toString(), 10)]).x[parseInt(j.toString(), 10)] + layoutProp.verticalSpacing / 2;
}
else {
(edges[parseInt(i.toString(), 10)]).x[parseInt(j.toString(), 10)]
= (edges[parseInt(i.toString(), 10)]).x[parseInt(j.toString(), 10)] + layoutProp.verticalSpacing / 2;
}
}
}
this.setEdgePosition(edges[parseInt(i.toString(), 10)], model, layout);
}
for (var p = 0; p < this.vertices.length; p++) {
var clnode = this.vertices[parseInt(p.toString(), 10)];
if (clnode.outEdges.length > 1) {
this.updateMultiOutEdgesPoints(clnode);
}
}
}
};
HierarchicalLayoutUtil.prototype.setEdgeXY = function (ranks, node, spacing, layer) {
if (ranks && node.source.id) {
var targetValue = void 0;
var sourceValue = void 0;
for (var i = 0; i < ranks.length; i++) {
for (var k = 0; k < ranks[parseInt(i.toString(), 10)].length; k++) {
if (ranks[parseInt(i.toString(), 10)][parseInt(k.toString(), 10)].id === node.target.id
|| ranks[parseInt(i.toString(), 10)][parseInt(k.toString(), 10)].id === node.source.id) {
if (ranks[parseInt(i.toString(), 10)][parseInt(k.toString(), 10)].id === node.target.id
&& targetValue === undefined) {
targetValue = i;
}
if (ranks[parseInt(i.toString(), 10)][parseInt(k.toString(), 10)].id === node.source.id
&& sourceValue === undefined) {
sourceValue = i;
}
}
}
}
var rankOffsetValue = void 0;
for (var m = targetValue; m <= sourceValue; m++) {
if (rankOffsetValue === undefined) {
rankOffsetValue = this[m + '_RankOffset'];
}
if (rankOffsetValue !== undefined && rankOffsetValue < this[m + '_RankOffset']) {
rankOffsetValue = this[m + '_RankOffset'];
}
}
if (this['edges'] === undefined) {
this['edges'] = {};
}
this['edges'][(node).ids[0]] = { x: node.x, y: 0 };
var value = this.resetOffsetXValue(rankOffsetValue, spacing / 10);
node.x[layer - node.minRank - 1] = value;
for (var k = 0; k < (node).edges.length; k++) {
(node).edges[parseInt(k.toString(), 10)]['levelSkip'] = true;
}
}
};
HierarchicalLayoutUtil.prototype.resetOffsetXValue = function (value, spacing) {
for (var i = 0, a = Object.keys(this['edges']); i < a.length; i++) {
var key = a[parseInt(i.toString(), 10)];
var length_1 = this['edges']["" + key].x;
for (var j = 0; j < length_1.length; j++) {
var offsetValue = void 0;
if (this['edges']["" + key].x[parseInt(j.toString(), 10)] === value) {
offsetValue = value + spacing;
offsetValue = this.resetOffsetXValue(offsetValue, spacing);
return offsetValue;
}
}
}
return value;
};
HierarchicalLayoutUtil.prototype.setEdgePosition = function (cell, model, layout) {
// For parallel edges we need to seperate out the points a
// little
var offsetX = 0;
// Only set the edge control points once
if (cell.temp[0] !== 101207) {
if (cell.maxRank === undefined) {
cell.maxRank = -1;
}
if (cell.minRank === undefined) {
cell.minRank = -1;
}
var maxRank = cell.maxRank;
var minRank = cell.minRank;
if (maxRank === minRank) {
maxRank = cell.source.maxRank;
minRank = cell.target.minRank;
}
var parallelEdgeCount = 0;
var jettys = this.jettyPositions[cell.ids[0]];
if (cell.isReversed === undefined) {
cell.isReversed = false;
}
else {
cell.isReversed = true;
}
var source = cell.isReversed ? cell.target.cell : cell.source.cell;
var layoutReversed = false;
if (model.layout.orientation === 'TopToBottom' || model.layout.orientation === 'LeftToRight') {
if (model.layout.orientation === 'TopToBottom') {
layoutReversed = false;
}
if (model.layout.orientation === 'LeftToRight') {
if (!cell.isReversed) {
layoutReversed = false;
}
else {
layoutReversed = false;
}
}
}
else {
if (!cell.isReversed) {
layoutReversed = true;
}
}
for (var i = 0; i < cell.edges.length; i++) {
var realEdge = cell.edges[parseInt(i.toString(), 10)];
var realSource = this.getVisibleTerminal(realEdge, true);
//List oldPoints = graph.getPoints(realEdge);
var newPoints = [];
// Single length reversed edges end up with the jettys in the wrong
// places. Since single length edges only have jettys, not segment
// control points, we just say the edge isn't reversed in this section
var reversed = cell.isReversed;
// if(cell.isReversed===undefined){
// reversed = false
// }else{
// reversed =cell.isReversed
// }
if (realSource !== source) {
// The real edges include all core model edges and these can go
// in both directions. If the source of the hierarchical model edge
// isn't the source of the specific real edge in this iteration
// treat if as reversed
reversed = !reversed;
}
// First jetty of edge
if (jettys != null) {
var arrayOffset = reversed ? 2 : 0;
var y = reversed ?
(layoutReversed ? this.rankBottomY[parseInt(minRank.toString(), 10)]
: this.rankTopY[parseInt(minRank.toString(), 10)])
: (layoutReversed ? this.rankTopY[parseInt(maxRank.toString(), 10)]
: this.rankBottomY[parseInt(maxRank.toString(), 10)]);
var jetty = jettys[parallelEdgeCount * 4 + 1 + arrayOffset];
if (reversed !== layoutReversed) {
jetty = -jetty;
}
if (layout.orientation === 'TopToBottom' || layout.orientation === 'BottomToTop') {
y += jetty;
}
var x = jettys[parallelEdgeCount * 4 + arrayOffset];
if (layout.orientation === 'TopToBottom' || layout.orientation === 'BottomToTop') {
newPoints.push(this.getPointvalue(x, y + layout.marginY));
}
else {
if (layout.orientation === 'LeftToRight') {
newPoints.push(this.getPointvalue(y + jetty, x + layout.marginY));
}
else {
newPoints.push(this.getPointvalue(y, x + layout.marginY));
}
}
}
var loopStart = cell.x.length - 1;
var loopLimit = -1;
var loopDelta = -1;
var currentRank = cell.maxRank - 1;
if (reversed) {
loopStart = 0;
loopLimit = cell.x.length;
loopDelta = 1;
currentRank = cell.minRank + 1;
}
// Reversed edges need the points inserted in
// reverse order
for (var j = loopStart; (cell.maxRank !== cell.minRank) && j !== loopLimit; j += loopDelta) {
// The horizontal position in a vertical layout
var positionX = cell.x[parseInt(j.toString(), 10)] + offsetX;
// This cell.x determines the deviated points of the connectors and jetty positions
//determine the src and targetgeo points .
// Work out the vertical positions in a vertical layout
// in the edge buffer channels above and below this rank
var topChannelY = (this.rankTopY[parseInt(currentRank.toString(), 10)]
+ this.rankBottomY[currentRank + 1]) / 2.0;
var bottomChannelY = (this.rankTopY[currentRank - 1]
+ this.rankBottomY[parseInt(currentRank.toString(), 10)]) / 2.0;
if (reversed) {
var tmp = topChannelY;
topChannelY = bottomChannelY;
bottomChannelY = tmp;
}
if (layout.orientation === 'TopToBottom' || layout.orientation === 'BottomToTop') {
newPoints.push(this.getPointvalue(positionX, topChannelY + layout.marginY));
newPoints.push(this.getPointvalue(positionX, bottomChannelY + layout.marginY));
}
else {
newPoints.push(this.getPointvalue(topChannelY, positionX + layout.marginY));
newPoints.push(this.getPointvalue(bottomChannelY, positionX + layout.marginY));
}
this.limitX = Math.max(this.limitX, positionX);
currentRank += loopDelta;
}
// Second jetty of edge
if (jettys != null) {
var arrayOffset = reversed ? 2 : 0;
var rankY = reversed ?
(layoutReversed ? this.rankTopY[parseInt(maxRank.toString(), 10)]
: this.rankBottomY[parseInt(maxRank.toString(), 10)])
: (layoutReversed ? this.rankBottomY[parseInt(minRank.toString(), 10)]
: this.rankTopY[parseInt(minRank.toString(), 10)]);
var jetty = jettys[parallelEdgeCount * 4 + 3 - arrayOffset];
if (reversed !== layoutReversed) {
jetty = -jetty;
}
var y = rankY - jetty;
var x = jettys[parallelEdgeCount * 4 + 2 - arrayOffset];
if (layout.orientation === 'TopToBottom' || layout.orientation === 'BottomToTop') {
newPoints.push(this.getPointvalue(x, y + layout.marginY));
}
else {
newPoints.push(this.getPointvalue(y, x + layout.marginY));
}
}
this.setEdgePoints(realEdge, newPoints, model);
// Increase offset so next edge is drawn next to
// this one
if (offsetX === 0.0) {
offsetX = this.parallelEdgeSpacing;
}
else if (offsetX > 0) {
offsetX = -offsetX;
}
else {
offsetX = -offsetX + this.parallelEdgeSpacing;
}
parallelEdgeCount++;
}
cell.temp[0] = 101207;
}
};
/* tslint:enable */
// eslint-disable-next-line
HierarchicalLayoutUtil.prototype.getPointvalue = function (x, y) {
return { 'x': Number(x) || 0, 'y': Number(y) || 0 };
};
HierarchicalLayoutUtil.prototype.updateEdgeSetXYValue = function (model) {
if (model.layout.enableLayoutRouting) {
var isHorizontal = false;
if (model.layout.orientation === 'LeftToRight' || model.layout.orientation === 'RightToLeft') {
isHorizontal = true;
}
for (var i = 0; i < model.ranks.length; i++) {
var rank = model.ranks[parseInt(i.toString(), 10)];
for (var k = 0; k < rank.length; k++) {
var cell = rank[parseInt(k.toString(), 10)];
if ((cell).edges && (cell).edges.length > 0) {
var spacing = model.layout.horizontalSpacing > 0 ? (model.layout.horizontalSpacing / 2) : 15;
var check = true;
if (!(cell.minRank === i - 1 || cell.maxRank === i - 1)) {
check = false;
}
if (check) {
this.setXY(cell, i, undefined, isHorizontal ? true : false, model.ranks, spacing);
}
}
}
}
}
};
HierarchicalLayoutUtil.prototype.getPreviousLayerConnectedCells = function (layer, cell) {
if (cell.previousLayerConnectedCells == null) {
cell.previousLayerConnectedCells = [];
cell.previousLayerConnectedCells[0] = [];
for (var i = 0; i < cell.connectsAsSource.length; i++) {
var edge = cell.connectsAsSource[parseInt(i.toString(), 10)];
if (edge.minRank === -1 || edge.minRank === layer - 1) {
// No dummy nodes in edge, add node of other side of edge
cell.previousLayerConnectedCells[0].push(edge.target);
}
else {
// Edge spans at least two layers, add edge
cell.previousLayerConnectedCells[0].push(edge);
}
}
}
return cell.previousLayerConnectedCells[0];
};
HierarchicalLayoutUtil.prototype.compare = function (a, b) {
if (a != null && b != null) {
if (b.weightedValue > a.weightedValue) {
return -1;
}
else if (b.weightedValue < a.weightedValue) {
return 1;
}
}
return 0;
};
/* tslint:disable */
// eslint-disable-next-line
HierarchicalLayoutUtil.prototype.localEdgeProcessing = function (model, vertices) {
// Iterate through each vertex, look at the edges connected in
// both directions.
for (var rankIndex = 0; rankIndex < model.ranks.length; rankIndex++) {
var rank = model.ranks[parseInt(rankIndex.toString(), 10)];
for (var cellIndex = 0; cellIndex < rank.length; cellIndex++) {
var cell = rank[parseInt(cellIndex.toString(), 10)];
if (this.crossReduction.isVertex(cell)) {
var currentCells = this.getPreviousLayerConnectedCells(rankIndex, cell);
var currentRank = rankIndex - 1;
// Two loops, last connected cells, and next
for (var k = 0; k < 2; k++) {
if (currentRank > -1
&& currentRank < model.ranks.length
&& currentCells != null
&& currentCells.length > 0) {
var sortedCells = [];
for (var j = 0; j < currentCells.length; j++) {
var sorter = this.weightedCellSorter(currentCells[parseInt(j.toString(), 10)], this.getX(currentRank, currentCells[parseInt(j.toString(), 10)]));
sortedCells.push(sorter);
}
sortedCells.sort(this.compare);
cell.width = vertices[cell.id].cell.geometry.width;
cell.height = vertices[cell.id].cell.geometry.height;
var leftLimit = void 0;
if (model.layout.orientation === 'TopToBottom' || model.layout.orientation === 'BottomToTop') {
cell.x[0] = vertices[cell.id].cell.geometry.x + vertices[cell.id].cell.geometry.width / 2;
leftLimit = cell.x[0] - cell.width / 2 + vertices[cell.id].cell.geometry.height / 2;
}
else {
cell.x[0] = vertices[cell.id].cell.geometry.y;
leftLimit = cell.x[0];
}
var rightLimit = leftLimit + cell.width;
// Connected edge count starts at 1 to allow for buffer
// with edge of vertex
var connectedEdgeCount = 0;
var connectedEdgeGroupCount = 0;
var connectedEdges = [];
// Calculate width requirements for all connected edges
for (var j = 0; j < sortedCells.length; j++) {
var innerCell = sortedCells[parseInt(j.toString(), 10)].cell;
var connections = void 0;
if (this.crossReduction.isVertex(innerCell)) {
// Get the connecting edge
if (k === 0) {
connections = cell.connectsAsSource;
}
else {
connections = cell.connectsAsTarget;
}
for (var connIndex = 0; connIndex < connections.length; connIndex++) {
if (connections[parseInt(connIndex.toString(), 10)].source === innerCell
|| connections[parseInt(connIndex.toString(), 10)].target === innerCell) {
connectedEdgeCount += connections[parseInt(connIndex.toString(), 10)].edges
.length;
connectedEdgeGroupCount++;
connectedEdges.push(connections[parseInt(connIndex.toString(), 10)]);
}
}
}
else {
connectedEdgeCount += innerCell.edges.length;
// eslint-disable-next-line
connectedEdgeGroupCount++;
connectedEdges.push(innerCell);
}
}
var requiredWidth = (connectedEdgeCount + 1)
* this.previousEdgeDistance;
// Add a buffer on the edges of the vertex if the edge count allows
if (cell.width > requiredWidth
+ (2 * this.previousEdgeDistance)) {
leftLimit += this.previousEdgeDistance;
rightLimit -= this.previousEdgeDistance;
}
var availableWidth = rightLimit - leftLimit;
var edgeSpacing = availableWidth / connectedEdgeCount;
var currentX = leftLimit + edgeSpacing / 2.0;
var currentYOffset = this.minEdgeJetty - this.previousEdgeOffset;
var maxYOffset = 0;
for (var j = 0; j < connectedEdges.length; j++) {
var numActualEdges = connectedEdges[parseInt(j.toString(), 10)].edges
.length;
if (this.jettyPositions === undefined) {
this.jettyPositions = {};
}
var pos = this.jettyPositions[connectedEdges[parseInt(j.toString(), 10)].ids[0]];
if (pos == null) {
pos = [];
this.jettyPositions[connectedEdges[parseInt(j.toString(), 10)].ids[0]] = pos;
}
if (j < connectedEdgeCount / 2) {
currentYOffset += this.previousEdgeOffset;
}
else if (j > connectedEdgeCount / 2) {
currentYOffset -= this.previousEdgeOffset;
}
// Ignore the case if equals, this means the second of 2
// jettys with the same y (even number of edges)
for (var m = 0; m < numActualEdges; m++) {
pos[m * 4 + k * 2] = currentX;
currentX += edgeSpacing;
pos[m * 4 + k * 2 + 1] = currentYOffset;
}
maxYOffset = Math.max(maxYOffset, currentYOffset);
}
}
currentCells = this.getNextLayerConnectedCells(rankIndex, cell);
currentRank = rankIndex + 1;
}
}
}
}
};
/* tslint:enable */
HierarchicalLayoutUtil.prototype.updateMultiOutEdgesPoints = function (clnode) {
for (var i = 0; i < clnode.outEdges.length / 2; i++) {
var connector1 = this.nameTable[clnode.outEdges[parseInt(i.toString(), 10)]];
var connector2 = this.nameTable[clnode.outEdges[clnode.outEdges.length - (i + 1)]];
var geometry = 'geometry';
//900930: To exclude self-loop in layouts
if (connector1.sourceID !== connector2.targetID && connector1.targetID !== connector2.sourceID) {
connector2["" + geometry].points[0].y = connector1["" + geometry].points[0].y;
}
}
};
HierarchicalLayoutUtil.prototype.getNextLayerConnectedCells = function (layer, cell) {
if (cell.nextLayerConnectedCells == null) {
cell.nextLayerConnectedCells = [];
cell.nextLayerConnectedCells[0] = [];
for (var i = 0; i < cell.connectsAsTarget.length; i++) {
var edge = cell.connectsAsTarget[parseInt(i.toString(), 10)];
if (edge.maxRank === -1 || edge.maxRank === layer + 1) {
// Either edge is not in any rank or
// no dummy nodes in edge, add node of other side of edge
cell.nextLayerConnectedCells[0].push(edge.source);
}
else {
// Edge spans at least two layers, add edge
cell.nextLayerConnectedCells[0].push(edge);
}
}
}
return cell.nextLayerConnectedCells[0];
};
HierarchicalLayoutUtil.prototype.getX = function (layer, cell) {
if (this.crossReduction.isVertex(cell)) {
return cell.x[0];
}
else if (!this.crossReduction.isVertex(cell)) {
return cell.x[layer - cell.minRank - 1] || cell.temp[layer - cell.minRank - 1];
}
return 0.0;
};
HierarchicalLayoutUtil.prototype.getGeometry = function (edge) {
var geometry = 'geometry';
return edge["" + geometry];
};
HierarchicalLayoutUtil.prototype.setEdgePoints = function (edge, points, model) {
if (edge != null) {
var geometryValue = 'geometry';
var geometry = this.getGeometry(edge);
if (points != null) {
for (var i = 0; i < points.length; i++) {
// eslint-disable-next-line
points[i].x = points[i].x;
// eslint-disable-next-line
points[i].y = points[i].y;
}
}
geometry.points = points;
edge["" + geometryValue] = geometry;
}
};
HierarchicalLayoutUtil.prototype.assignRankOffset = function (model) {
if (model) {
for (var i = 0; i < model.ranks.length; i++) {
this.rankCoordinatesAssigment(i, model);
}
}
};
HierarchicalLayoutUtil.prototype.rankCoordinatesAssigment = function (rankValue, model) {
var rank = model.ranks[parseInt(rankValue.toString(), 10)];
var spacing = model.layout.horizontalSpacing;
var localOffset;
for (var i = 0; i < rank.length; i++) {
if (this[rankValue + '_' + 'RankOffset'] === undefined) {
this[rankValue + '_' + 'RankOffset'] = 0;
}
localOffset = rank[parseInt(i.toString(), 10)].x[0];
if (this[rankValue + '_' + 'RankOffset'] < localOffset) {
this[rankValue + '_' + 'RankOffset'] = localOffset + rank[parseInt(i.toString(), 10)].width / 2 + spacing;
}
}
};
HierarchicalLayoutUtil.prototype.getType = function (type) {
if (type === 'internalVertex') {
return 'internalVertex';
}
else {
return 'internalEdge';
}
};
HierarchicalLayoutUtil.prototype.updateRankValuess = function (model) {
this.rankTopY = [];
this.rankBottomY = [];
for (var i = 0; i < model.ranks.length; i++) {
this.rankTopY[parseInt(i.toString(), 10)] = Number.MAX_VALUE;
this.rankBottomY[parseInt(i.toString(), 10)] = -Number.MAX_VALUE;
}
};
HierarchicalLayoutUtil.prototype.setVertexLocationValue = function (cell, orientation, modelBounds) {
var cellGeomtry = cell.cell.geometry;
var positionX;
var positionY;
if (orientation === 'TopToBottom' || orientation === 'BottomToTop') {
positionX = cellGeomtry.x;
positionY = cellGeomtry.y;
}
else {
positionX = cellGeomtry.y;
positionY = cellGeomtry.x;
}
if (orientation === 'RightToLeft') {
// eslint-disable-next-line
positionX = cellGeomtry.y;
positionY = modelBounds.width - cellGeomtry.x - cellGeomtry.height;
this.rankBottomY[cell.minRank] = Math.max(this.rankBottomY[cell.minRank], positionY);
this.rankTopY[cell.minRank] = Math.min(this.rankTopY[cell.minRank], positionY + cellGeomtry.height);
}
else {
this.rankTopY[cell.minRank] = Math.min(this.rankTopY[cell.minRank], positionY);