UNPKG

@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
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);