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,007 lines 59.8 kB
import { Rect } from '../primitives/rect'; import { findPoint, getIntersection, getOppositeDirection, getPortDirection } from '../utility/connector'; import { canEnableRouting } from '../utility/constraints-util'; /** * Line Routing */ var LineRouting = /** @class */ (function () { /** * Constructor for the line routing module * * @private */ function LineRouting() { this.size = 20; this.intermediatePoints = []; this.gridCollection = []; this.startArray = []; this.targetGridCollection = []; this.sourceGridCollection = []; this.considerWalkable = []; this.skipObstacleCheck = false; // collection of non-walkable grids this.nonWalkableGrids = []; //constructs the line routing module } /** * lineRouting method \ * * @returns { void } lineRouting method .\ * @param {Diagram} diagram - provide the source value. * * @private */ LineRouting.prototype.lineRouting = function (diagram) { var length = diagram.connectors.length; this.renderVirtualRegion(diagram); if (length > 0) { for (var k = 0; k < length; k++) { var connector = diagram.connectors[parseInt(k.toString(), 10)]; if (connector.type === 'Orthogonal' && connector.visible) { this.refreshConnectorSegments(diagram, connector, true); } } } }; /** @private */ /** * renderVirtualRegion method \ * * @returns { void } renderVirtualRegion method .\ * @param {Diagram} diagram - provide the source value. * @param {boolean} isUpdate - provide the target value. * * @private */ LineRouting.prototype.renderVirtualRegion = function (diagram, isUpdate) { /* tslint:disable */ var extraBounds = this.size; if (diagram.spatialSearch['pageTop'] < 0 || diagram.spatialSearch['pageLeft'] < 0) { extraBounds = this.size + (this.size / 2); } var right = diagram.spatialSearch['pageRight'] + extraBounds; var bottom = diagram.spatialSearch['pageBottom'] + extraBounds; var left = diagram.spatialSearch['pageLeft'] - extraBounds; var top = diagram.spatialSearch['pageTop'] - extraBounds; left = left < 0 ? left - 20 : 0; top = top < 0 ? top - 20 : 0; /* tslint:enable */ if ((isUpdate && (this.width !== (right - left) || this.height !== (bottom - top) || this.diagramStartX !== left || this.diagramStartY !== top)) || isUpdate === undefined) { this.width = right - left; this.height = bottom - top; this.diagramStartX = left; this.diagramStartY = top; this.gridCollection = []; this.noOfRows = this.width / this.size; this.noOfCols = this.height / this.size; var size = this.size; var x = this.diagramStartX < 0 ? this.diagramStartX : 0; var y = this.diagramStartY < 0 ? this.diagramStartY : 0; for (var i = 0; i < this.noOfCols; i++) { for (var j = 0; j < this.noOfRows; j++) { if (i === 0) { // eslint-disable-next-line @typescript-eslint/no-explicit-any this.gridCollection.push([0]); } var grid = { x: x, y: y, width: size, height: size, gridX: j, gridY: i, walkable: true, tested: undefined, nodeId: [] }; this.gridCollection[parseInt(j.toString(), 10)][parseInt(i.toString(), 10)] = grid; x += size; } x = this.diagramStartX < 0 ? this.diagramStartX : 0; y += size; } // Clear the non-walkable grid collection if (this.nonWalkableGrids.length !== 0) { this.nonWalkableGrids.length = 0; } } var nodes = this.findNodes(diagram.nodes); this.updateNodesInVirtualRegion(nodes); }; LineRouting.prototype.findNodes = function (nodes) { var objects = []; var node; for (var i = 0; i < nodes.length; i++) { node = nodes[parseInt(i.toString(), 10)]; if (node.shape.type !== 'SwimLane' && !node.isLane && !node.isPhase && !node.isHeader && node.visible) { objects.push(node); } } return objects; }; LineRouting.prototype.updateNodesInVirtualRegion = function (diagramNodes) { var size = this.size; // Make non-walkable grids again walkable for (var _i = 0, _a = this.nonWalkableGrids; _i < _a.length; _i++) { var grid = _a[_i]; grid.walkable = true; grid.tested = undefined; grid.nodeId = []; grid.parentNodeId = ''; } // Clear the non-walkable grid collection if (this.nonWalkableGrids.length !== 0) { this.nonWalkableGrids.length = 0; } // Mark the grids that are occupied by nodes as non-walkable for (var i = 0; i < diagramNodes.length; i++) { var node = diagramNodes[parseInt(i.toString(), 10)]; var bounds = node.wrapper.bounds; if (bounds) { // Get grid that intersects with node bounds var grids = this.getGridsIntersectBounds(bounds); for (var _b = 0, grids_1 = grids; _b < grids_1.length; _b++) { var grid = grids_1[_b]; var rectangle = new Rect(grid.x, grid.y, size, size); // check whether node bounds intersects with grid bounds var isContains = this.intersectRect(rectangle, bounds); if (isContains) { grid.nodeId.push(node.id); grid.walkable = false; this.nonWalkableGrids.push(grid); if (node.parentId !== '') { grid.parentNodeId = node.parentId; } } } } } }; /** * Gets the grids that intersect with the node bounds. * @param {Rect} bounds - The starting point of the line segment. * @returns {VirtualBoundaries[]} An array of VirtualBoundaries that intersect with the line segment. * @private */ LineRouting.prototype.getGridsIntersectBounds = function (bounds) { var grids = []; var minX = bounds.topLeft.x; var maxX = bounds.bottomRight.x; var minY = bounds.topLeft.y; var maxY = bounds.bottomRight.y; var gridSize = this.size; var minGridX = Math.floor((minX - this.diagramStartX) / gridSize); var minGridY = Math.floor((minY - this.diagramStartY) / gridSize); var maxGridX = Math.floor((maxX - this.diagramStartX) / gridSize); var maxGridY = Math.floor((maxY - this.diagramStartY) / gridSize); for (var x = minGridX; x <= maxGridX; x++) { for (var y = minGridY; y <= maxGridY; y++) { var gridRow = this.gridCollection[parseInt(x.toString(), 10)]; if (gridRow) { var grid = gridRow[parseInt(y.toString(), 10)]; if (grid && grids.indexOf(grid) === -1) { grids.push(grid); } } } } return grids; }; LineRouting.prototype.intersectRect = function (r1, r2) { return !(r2.left >= r1.right || r2.right <= r1.left || r2.top >= r1.bottom || r2.bottom <= r1.top); }; LineRouting.prototype.findEndPoint = function (connector, isSource, isPortBounds) { var endPoint; var portDirection; // EJ2-65876 - Exception occurs on line routing injection module if ((isSource && connector.sourcePortID !== '' && connector.sourcePortWrapper) || (!isSource && connector.targetPortID !== '' && connector.targetPortWrapper)) { endPoint = (isSource) ? { x: connector.sourcePortWrapper.offsetX, y: connector.sourcePortWrapper.offsetY } : { x: connector.targetPortWrapper.offsetX, y: connector.targetPortWrapper.offsetY }; portDirection = getPortDirection(endPoint, undefined, (isSource) ? connector.sourceWrapper.bounds : connector.targetWrapper.bounds, false); var bounds = (isSource) ? connector.sourcePortWrapper.bounds : connector.targetPortWrapper.bounds; if (isPortBounds) { if (portDirection === 'Top') { endPoint = { x: bounds.topCenter.x, y: bounds.topCenter.y }; } else if (portDirection === 'Left') { endPoint = { x: bounds.middleLeft.x, y: bounds.middleLeft.y }; } else if (portDirection === 'Right') { endPoint = { x: bounds.middleRight.x, y: bounds.middleRight.y }; } else { endPoint = { x: bounds.bottomCenter.x, y: bounds.bottomCenter.y }; } } else { endPoint = { x: bounds.center.x, y: bounds.center.y }; } } else { if ((isSource && this.startNode) || (!isSource && this.targetNode)) { endPoint = (isSource) ? { x: this.startNode.wrapper.offsetX, y: this.startNode.wrapper.offsetY } : { x: this.targetNode.wrapper.offsetX, y: this.targetNode.wrapper.offsetY }; } else { endPoint = (isSource) ? { x: connector.sourcePoint.x, y: connector.sourcePoint.y } : { x: connector.targetPoint.x, y: connector.targetPoint.y }; } } return endPoint; }; /** * Gets the grids that intersect with the line segment defined by the start and end points. * @param {PointModel} startPoint - The starting point of the line segment. * @param {PointModel} endPoint - The ending point of the line segment. * @returns {VirtualBoundaries[]} An array of VirtualBoundaries that intersect with the line segment. * @private */ LineRouting.prototype.getGridsIntersect = function (startPoint, endPoint) { var grids = []; var minX = Math.min(startPoint.x, endPoint.x); var minY = Math.min(startPoint.y, endPoint.y); var maxX = Math.max(startPoint.x, endPoint.x); var maxY = Math.max(startPoint.y, endPoint.y); var gridSize = this.size; var minGridX = Math.floor((minX - this.diagramStartX) / gridSize); var minGridY = Math.floor((minY - this.diagramStartY) / gridSize); var maxGridX = Math.floor((maxX - this.diagramStartX) / gridSize); var maxGridY = Math.floor((maxY - this.diagramStartY) / gridSize); var isHorizontal = maxX - minX > maxY - minY; if (isHorizontal) { for (var x = minGridX; x <= maxGridX; x++) { var gridRow = this.gridCollection[parseInt(x.toString(), 10)]; if (gridRow) { var grid = gridRow[parseInt(minGridY.toString(), 10)]; if (grid && grids.indexOf(grid) === -1) { grids.push(grid); } } } } else { for (var y = minGridY; y <= maxGridY; y++) { var gridRow = this.gridCollection[parseInt(minGridX.toString(), 10)]; if (gridRow) { var grid = gridRow[parseInt(y.toString(), 10)]; if (grid && grids.indexOf(grid) === -1) { grids.push(grid); } } } } return grids; }; /** * Checks if the path between the start and end points is walkable. * @param {PointModel} startPoint - The starting point of the path. * @param {PointModel} endPoint - The ending point of the path. * @param {Diagram} diagram - The diagram instance. * @param {Connector} [connector] - The connector to check for obstacles. * @returns {boolean} True if the path is walkable, otherwise false. * @private */ LineRouting.prototype.isPathWalkable = function (startPoint, endPoint, diagram, connector) { var minX = Math.min(startPoint.x, endPoint.x); var minY = Math.min(startPoint.y, endPoint.y); var maxX = Math.max(startPoint.x, endPoint.x); var maxY = Math.max(startPoint.y, endPoint.y); var grids = this.getGridsIntersect(startPoint, endPoint); for (var i = 0; i < grids.length; i++) { var grid = grids[parseInt(i.toString(), 10)]; // Exclude grids that contain the source or target node if (connector && (grid.nodeId.indexOf(connector.sourceID) !== -1 || grid.nodeId.indexOf(connector.targetID) !== -1)) { continue; } if (!grid.walkable) { var isHorizontal = maxX - minX > maxY - minY; for (var _i = 0, _a = grid.nodeId; _i < _a.length; _i++) { var nodeId = _a[_i]; var node = diagram.nameTable["" + nodeId]; if (node) { var bounds = node.wrapper.bounds; var padding = 5; if (isHorizontal) { if (bounds.top - padding < minY && bounds.bottom + padding > maxY) { return false; } } else { if (bounds.left - padding < minX && bounds.right + padding > maxX) { return false; } } } } } } return true; }; LineRouting.prototype.checkObstaclesIntersect = function (segmentPoints, connector, diagram) { for (var i = 1; i < segmentPoints.length; i++) { var start = segmentPoints[i - 1]; var end = segmentPoints[parseInt(i.toString(), 10)]; if (!this.isPathWalkable(start, end, diagram, connector)) { return false; } } return true; }; /** * refreshConnectorSegments method \ * * @returns { void } refreshConnectorSegments method .\ * @param {Diagram} diagram - provide the diagram value. * @param {Connector} connector - provide the connector value. * @param {boolean} isUpdate - provide the diagram value. * @param {boolean} isEnableRouting - provide enableRouting value. * * @private */ LineRouting.prototype.refreshConnectorSegments = function (diagram, connector, isUpdate, isEnableRouting) { if (!connector.sourceID || !connector.targetID || connector.sourceID === connector.targetID) { return; } if (!this.skipObstacleCheck && connector.segments && connector.segments.length < 2 && connector.intermediatePoints && this.checkObstaclesIntersect(connector.intermediatePoints, connector, diagram)) { if (diagram.avoidLineOverlappingModule && isUpdate) { diagram.avoidLineOverlappingModule.addConnector(connector); diagram.avoidLineOverlappingModule.refreshModifiedConnectors(diagram); } return; } var sourceId = connector.sourceID; var targetId = connector.targetID; var sourcePortID = connector.sourcePortID; var targetPortID = connector.targetPortID; var startPoint; var targetPoint; var sourcePortDirection; var targetPortDirection; var grid; var sourceTop; var sourceBottom; var isBreak; var sourceLeft; var sourceRight; var targetRight; var targetTop; var targetBottom; var targetLeft; if (canEnableRouting(connector, diagram) || isEnableRouting) { this.startNode = diagram.nameTable["" + sourceId]; this.targetNode = diagram.nameTable["" + targetId]; this.intermediatePoints = []; this.startArray = []; this.targetGridCollection = []; this.sourceGridCollection = []; this.startGrid = undefined; this.targetGrid = undefined; for (var i = 0; i < this.noOfCols; i++) { for (var j = 0; j < this.noOfRows; j++) { this.gridCollection[parseInt(j.toString(), 10)][parseInt(i.toString(), 10)].tested = this.gridCollection[parseInt(j.toString(), 10)][parseInt(i.toString(), 10)].parent = undefined; this.gridCollection[parseInt(j.toString(), 10)][parseInt(i.toString(), 10)].previousDistance = this.gridCollection[parseInt(j.toString(), 10)][parseInt(i.toString(), 10)].afterDistance = undefined; this.gridCollection[parseInt(j.toString(), 10)][parseInt(i.toString(), 10)].totalDistance = undefined; } } // Set the source point and target point startPoint = this.findEndPoint(connector, true); targetPoint = this.findEndPoint(connector, false); // Find the start grid and target grid for (var i = 0; i < this.noOfRows; i++) { for (var j = 0; j < this.noOfCols; j++) { grid = this.gridCollection[parseInt(i.toString(), 10)][parseInt(j.toString(), 10)]; var rectangle = new Rect(grid.x, grid.y, grid.width, grid.height); if (rectangle.containsPoint(startPoint) && !this.startGrid && (grid.nodeId.indexOf(sourceId) !== -1 || sourceId === '')) { this.startGrid = (sourcePortID && this.startGrid && (sourcePortDirection === 'Left' || sourcePortDirection === 'Top')) ? this.startGrid : grid; } if (rectangle.containsPoint(targetPoint) && !this.targetGrid && (grid.nodeId.indexOf(targetId) !== -1 || targetId === '')) { this.targetGrid = (targetPortID && this.targetGrid && (targetPortDirection === 'Left' || targetPortDirection === 'Top')) ? this.targetGrid : grid; } if (!sourcePortID && this.startNode) { var bounds = this.startNode.wrapper.bounds; if (rectangle.containsPoint(bounds.topCenter) && !sourceTop) { sourceTop = grid; } if (rectangle.containsPoint(bounds.middleLeft) && !sourceLeft) { sourceLeft = grid; } if (rectangle.containsPoint(bounds.middleRight) && !sourceRight) { sourceRight = grid; } if (rectangle.containsPoint(bounds.bottomCenter) && !sourceBottom) { sourceBottom = grid; } } if (!targetPortID && this.targetNode) { var bounds = this.targetNode.wrapper.bounds; if (rectangle.containsPoint(bounds.topCenter) && !targetTop) { targetTop = grid; } if (rectangle.containsPoint(bounds.middleLeft) && !targetLeft) { targetLeft = grid; } if (rectangle.containsPoint(bounds.middleRight) && !targetRight) { targetRight = grid; } if (rectangle.containsPoint({ x: bounds.bottomCenter.x, y: bounds.bottomCenter.y }) && !targetBottom) { targetBottom = grid; } } } } this.findEdgeBoundary(sourcePortID, sourceLeft, sourceRight, sourceTop, sourceBottom, true); this.findEdgeBoundary(targetPortID, targetLeft, targetRight, targetTop, targetBottom, false); this.startGrid.totalDistance = 0; this.startGrid.previousDistance = 0; this.intermediatePoints.push({ x: this.startGrid.gridX, y: this.startGrid.gridY }); this.startArray.push(this.startGrid); if (connector && targetLeft && targetRight && targetTop && targetBottom) { this.checkObstacles(connector, diagram, targetLeft, targetRight, targetTop, targetBottom); } // eslint-disable-next-line no-labels renderPathElement: while (this.startArray.length > 0) { var startGridNode = this.startArray.pop(); //890444:Exception thrown while drag and drop shapes on top of each other repeatedly with Line Routing if (startGridNode) { for (var i = 0; i < this.targetGridCollection.length; i++) { var target = this.targetGridCollection[parseInt(i.toString(), 10)]; if (startGridNode) { if (startGridNode.gridX === target.gridX && startGridNode.gridY === target.gridY) { this.getIntermediatePoints(startGridNode); this.optimizeIntermediatePoints(); if (startGridNode.nodeId && startGridNode.nodeId.length > 1) { connector.segments = []; } isBreak = this.updateConnectorSegments(diagram, this.intermediatePoints, this.gridCollection, connector, isUpdate); if (!isBreak) { this.targetGridCollection.splice(this.targetGridCollection.indexOf(target), 1); startGridNode = this.startArray.pop(); } else { this.considerWalkable = []; // eslint-disable-next-line no-labels break renderPathElement; } } } } if (startGridNode) { this.findPath(startGridNode); } } } } }; LineRouting.prototype.checkChildNodes = function (grid, isSource) { var check = false; var reject = false; if (grid.nodeId.length >= 1 && !isSource) { for (var i = 0; i < grid.nodeId.length; i++) { var id = grid.nodeId[parseInt(i.toString(), 10)]; for (var j = 0; j < grid.nodeId.length; j++) { if (this.targetNode.parentId === grid.nodeId[parseInt(j.toString(), 10)]) { reject = true; } } if (!reject && this.targetNode.id === id) { check = true; } else { check = false; } } } else { if (grid.nodeId.length === 1) { check = true; } } return check; }; LineRouting.prototype.findEdgeBoundary = function (portID, left, right, top, bottom, isSource) { var grid; var collection = (isSource) ? this.sourceGridCollection : this.targetGridCollection; if (!portID && ((isSource) ? this.startNode : this.targetNode)) { for (var i = left.gridX; i <= right.gridX; i++) { grid = this.gridCollection[parseInt(i.toString(), 10)][left.gridY]; if ((this.checkChildNodes(grid, isSource) && (i === left.gridX || i === right.gridX)) || (i !== left.gridX && i !== right.gridX)) { collection.push(grid); } } for (var i = top.gridY; i <= bottom.gridY; i++) { grid = this.gridCollection[top.gridX][parseInt(i.toString(), 10)]; if (((this.checkChildNodes(grid, isSource) && (i === top.gridY || i === bottom.gridY)) || (i !== top.gridY && i !== bottom.gridY)) && collection.indexOf(grid) === -1) { collection.push(grid); } } } else { collection.push((isSource) ? this.startGrid : this.targetGrid); } }; LineRouting.prototype.checkObstacles = function (connector, diagram, targetLeft, targetRight, targetTop, targetBottom) { var neigbours = this.findNearestNeigbours(this.startGrid, this.gridCollection, true); if (neigbours.length === 0) { if (connector.sourcePortID !== '') { var endPoint = { x: connector.sourcePortWrapper.offsetX, y: connector.sourcePortWrapper.offsetY }; var portDirection = getPortDirection(endPoint, undefined, connector.sourceWrapper.bounds, false); if (portDirection === 'Top') { this.resetGridColl(this.startGrid, 'top', true); } else if (portDirection === 'Right') { this.resetGridColl(this.startGrid, 'right', true); } else if (portDirection === 'Bottom') { this.resetGridColl(this.startGrid, 'bottom', true); } else { this.resetGridColl(this.startGrid, 'left', true); } } else { this.resetGridColl(this.startGrid, 'top', true); this.resetGridColl(this.startGrid, 'right', true); this.resetGridColl(this.startGrid, 'bottom', true); this.resetGridColl(this.startGrid, 'left', true); } } neigbours = this.findNearestNeigbours(this.targetGrid, this.gridCollection, false); if (neigbours.length === 0) { if (connector.targetPortID !== '') { var endPoint = { x: connector.targetPortWrapper.offsetX, y: connector.targetPortWrapper.offsetY }; var portDirection = getPortDirection(endPoint, undefined, connector.targetWrapper.bounds, false); if (portDirection === 'Top') { this.resetGridColl(this.targetGrid, 'top', true); } else if (portDirection === 'Right') { this.resetGridColl(this.targetGrid, 'right', true); } else if (portDirection === 'Bottom') { this.resetGridColl(this.targetGrid, 'bottom', true); } else { this.resetGridColl(this.targetGrid, 'left', true); } } else { this.resetGridColl(this.targetGrid, 'top', false); this.resetGridColl(this.targetGrid, 'right', false); this.resetGridColl(this.targetGrid, 'bottom', false); this.resetGridColl(this.targetGrid, 'left', false); } } if (this.targetGridCollection.length > 1 && this.targetGridCollection[0].nodeId.length > 1) { for (var i = 0; i <= 1; i++) { var gridX = this.targetGridCollection[parseInt(i.toString(), 10)].gridX; var gridY = this.targetGridCollection[parseInt(i.toString(), 10)].gridY; var gridNodes = this.targetGridCollection[parseInt(i.toString(), 10)].nodeId; var targetNode = void 0; for (var k = 0; k < gridNodes.length; k++) { if (this.targetNode.id !== gridNodes[parseInt(k.toString(), 10)]) { targetNode = gridNodes[parseInt(k.toString(), 10)]; break; } } var targetNodewrapper = void 0; var overLapNode = void 0; var contains = void 0; if (diagram.nameTable[this.targetNode.id]) { targetNodewrapper = diagram.nameTable[this.targetNode.id].wrapper; } if (diagram.nameTable["" + targetNode]) { overLapNode = diagram.nameTable["" + targetNode].wrapper; } if (targetNodewrapper && overLapNode) { contains = this.contains(overLapNode.bounds, targetNodewrapper.bounds); } var reject = void 0; for (var j = 0; j < gridNodes.length; j++) { if (this.targetNode.parentId === gridNodes[parseInt(j.toString(), 10)]) { reject = true; } } if (!this.gridCollection[parseInt(gridX.toString(), 10)][parseInt(gridY.toString(), 10)].walkable && contains && !reject) { var grid = void 0; var diff = void 0; grid = this.getEndvalue(targetLeft, 'left'); diff = targetLeft.gridX - grid.gridX; this.changeValue(targetLeft, diff, 'left'); grid = this.getEndvalue(targetRight, 'right'); diff = grid.gridX - targetRight.gridX; this.changeValue(targetRight, diff, 'right'); grid = this.getEndvalue(targetTop, 'top'); diff = targetTop.gridY - grid.gridY; this.changeValue(targetTop, diff, 'top'); grid = this.getEndvalue(targetBottom, 'bottom'); diff = targetBottom.gridY - grid.gridY; this.changeValue(targetBottom, diff, 'top'); } } } }; LineRouting.prototype.contains = function (rect1, rect2) { return rect1.left <= rect2.left && rect1.right >= rect2.right && rect1.top <= rect2.top && rect1.bottom >= rect2.bottom; }; LineRouting.prototype.getEndvalue = function (target, direction) { if (!this.gridCollection[target.gridX][target.gridY].walkable) { if (direction === 'left') { return this.getEndvalue(this.gridCollection[target.gridX - 1][target.gridY], direction); } if (direction === 'right') { return this.getEndvalue(this.gridCollection[target.gridX + 1][target.gridY], direction); } if (direction === 'top') { return this.getEndvalue(this.gridCollection[target.gridX][target.gridY - 1], direction); } if (direction === 'bottom') { return this.getEndvalue(this.gridCollection[target.gridX][target.gridY + 1], direction); } } else { return target; } return target; }; LineRouting.prototype.changeValue = function (targetLeft, diff, direction) { if (!targetLeft.walkable) { this.considerWalkable.push(targetLeft); } var grid; for (var i = 0; i <= diff; i++) { if (direction === 'left') { grid = this.gridCollection[targetLeft.gridX - i][targetLeft.gridY]; } else if (direction === 'right') { grid = this.gridCollection[targetLeft.gridX + i][targetLeft.gridY]; } else if (direction === 'top') { grid = this.gridCollection[targetLeft.gridX][targetLeft.gridY - i]; } else if (direction === 'bottom') { grid = this.gridCollection[targetLeft.gridX][targetLeft.gridY + i]; } if (!grid.walkable) { this.considerWalkable.push(grid); } } }; // Get all the intermediated points from target grid LineRouting.prototype.getIntermediatePoints = function (target) { var distance; this.intermediatePoints = []; while (target) { this.intermediatePoints.push({ x: target.gridX, y: target.gridY }); target = target.parent; } this.intermediatePoints.reverse(); //890444: Exception thrown while drag and drop shapes on top of each other repeatedly with Line Routing if (this.intermediatePoints.length >= 1 && this.intermediatePoints[0] !== undefined && this.intermediatePoints[1] !== undefined) { if (this.intermediatePoints[0].x === this.intermediatePoints[1].x) { if (this.intermediatePoints[0].y < this.intermediatePoints[1].y) { distance = this.neigbour(this.startGrid, 'bottom', undefined, true); this.intermediatePoints[0].y += distance - 1; } else { distance = this.neigbour(this.startGrid, 'top', undefined, true); this.intermediatePoints[0].y -= distance - 1; } } else { if (this.intermediatePoints[0].x < this.intermediatePoints[1].x) { distance = this.neigbour(this.startGrid, 'right', undefined, true); this.intermediatePoints[0].x += distance - 1; } else { distance = this.neigbour(this.startGrid, 'left', undefined, true); this.intermediatePoints[0].x -= distance - 1; } } } }; LineRouting.prototype.optimizeIntermediatePoints = function () { this.intermediatePoints = this.removePointsInSameLine(this.intermediatePoints); this.intermediatePoints = this.getValidPoints(this.intermediatePoints); }; LineRouting.prototype.removePointsInSameLine = function (points) { if (points.length < 3) { return points; } var result = [points[0]]; for (var i = 1; i < points.length - 1; i++) { var prevPoint = result[result.length - 1]; var currentPoint = points[parseInt(i.toString(), 10)]; var nextPoint = points[i + 1]; if (!this.arePointsInSameLine(prevPoint, currentPoint, nextPoint)) { result.push(currentPoint); } } result.push(points[points.length - 1]); return result; }; LineRouting.prototype.arePointsInSameLine = function (point1, point2, point3) { return (point2.x - point1.x) * (point3.y - point1.y) === (point3.x - point1.x) * (point2.y - point1.y); }; LineRouting.prototype.getValidPoints = function (points) { if (points.length < 4) { return points; } var i = 1; while (i < points.length - 3) { var lineStart1 = points[parseInt(i.toString(), 10)]; var lineEnd1 = points[i + 1]; var lineStart2 = points[i + 2]; var lineEnd2 = points[i + 3]; if (lineStart1.x === lineEnd1.x) { if ((lineEnd1.y < lineStart1.y && lineEnd2.y < lineStart2.y) || (lineEnd1.y > lineStart1.y && lineEnd2.y > lineStart2.y)) { var dx = lineStart1.x < lineStart2.x ? 1 : -1; var dy = lineEnd1.y < lineStart1.y ? -1 : 1; var neigbourGridX = lineStart1.x + dx; var neigbourGridY = lineStart1.y; var isValid = false; while (neigbourGridX !== lineEnd2.x || neigbourGridY !== lineEnd2.y) { if (!this.isWalkable(neigbourGridX, neigbourGridY)) { isValid = false; break; } else { isValid = true; } if (neigbourGridX !== lineStart2.x) { neigbourGridX += dx; } else { neigbourGridY += dy; } } if (isValid) { lineStart1.x = lineStart2.x; points.splice(i + 1, 2); continue; } } } else if (lineStart1.y === lineEnd1.y) { if ((lineEnd1.x < lineStart1.x && lineEnd2.x < lineStart2.x) || (lineEnd1.x > lineStart1.x && lineEnd2.x > lineStart2.x)) { var dy1 = lineStart1.y < lineStart2.y ? 1 : -1; var dx1 = lineEnd1.x < lineStart1.x ? -1 : 1; var neigbourGridY1 = lineStart1.y + dy1; var neigbourGridX1 = lineStart1.x; var isValid1 = false; while (neigbourGridX1 !== lineEnd2.x || neigbourGridY1 !== lineEnd2.y) { if (!this.isWalkable(neigbourGridX1, neigbourGridY1)) { isValid1 = false; break; } else { isValid1 = true; } if (neigbourGridY1 !== lineStart2.y) { neigbourGridY1 += dy1; } else { neigbourGridX1 += dx1; } } if (isValid1) { lineStart1.y = lineStart2.y; points.splice(i + 1, 2); continue; } } } i++; } return points; }; // Connector rendering /* tslint:disable */ LineRouting.prototype.updateConnectorSegments = function (diagram, intermediatePoints, gridCollection, connector, isUpdate) { var segments = []; var seg; var targetPoint; var pointX; var pointY; var node; var points = []; var direction; var length; var currentdirection; var prevDirection; var targetWrapper = connector.targetWrapper; var sourceWrapper = connector.sourceWrapper; var sourcePoint = this.findEndPoint(connector, true); if (connector.targetPortID !== '' || !connector.targetWrapper) { targetPoint = this.findEndPoint(connector, false, true); } for (var i = 0; i < intermediatePoints.length; i++) { node = gridCollection[intermediatePoints[parseInt(i.toString(), 10)].x][intermediatePoints[parseInt(i.toString(), 10)].y]; if (node) { pointX = node.x + node.width / 2; pointY = node.y + node.height / 2; points.push({ x: pointX, y: pointY }); if (i >= 1 && points.length > 1) { if (points[points.length - 2].x !== points[points.length - 1].x) { currentdirection = (points[points.length - 2].x > points[points.length - 1].x) ? 'Left' : 'Right'; } else { currentdirection = (points[points.length - 2].y > points[points.length - 1].y) ? 'Top' : 'Bottom'; } } if (i >= 2 && prevDirection === currentdirection && points.length > 1) { points.splice(points.length - 2, 1); } prevDirection = currentdirection; } } if (points && points.length > 1) { for (var j = 0; j < points.length - 1; j++) { var currentPoint = points[parseInt(j.toString(), 10)]; var nextPoint = points[j + 1]; if (currentPoint.x !== nextPoint.x) { if (j === 0 && connector.sourcePortID === '' && sourceWrapper) { sourcePoint = (currentPoint.x > nextPoint.x) ? sourceWrapper.bounds.middleLeft : sourceWrapper.bounds.middleRight; } if (j === points.length - 2 && connector.targetPortID === '' && targetWrapper) { targetPoint = (currentPoint.x > nextPoint.x) ? targetWrapper.bounds.middleRight : targetWrapper.bounds.middleLeft; } if (j === 0 && sourcePoint) { currentPoint.x = sourcePoint.x; currentPoint.y = nextPoint.y = sourcePoint.y; //Bug:849859 -set node bounds as source point if intersected point exists inside the node if (connector.sourcePortID === '') { var newDirection = currentPoint.x > nextPoint.x ? 'Left' : 'Right'; var refPoint = findPoint(sourceWrapper.bounds, getOppositeDirection(newDirection)); sourcePoint = getIntersection(connector, sourceWrapper, sourcePoint, refPoint, false); } currentPoint.x = sourcePoint.x; } if (j === points.length - 2 && targetPoint) { if (j > 0 && connector.targetDecorator && ((targetPoint.x - nextPoint.x) < 0) && (Math.abs(targetPoint.x - currentPoint.x) < connector.targetDecorator.width + 1)) { currentPoint.x = points[j - 1].x -= this.size / 2; } if (j > 0 && connector.targetDecorator && ((targetPoint.x - nextPoint.x) > 0) && (Math.abs(targetPoint.x - currentPoint.x) < connector.targetDecorator.width + 1)) { currentPoint.x = points[j - 1].x += this.size / 2; } nextPoint.x = targetPoint.x; currentPoint.y = nextPoint.y = targetPoint.y; } } else { //EJ2-855805 - Connector target decorator is not proper in complexhierarchical layout when we call doLayout with line-routing if (j === 0 && connector.sourcePortID === '' && sourceWrapper) { sourcePoint = (currentPoint.y > nextPoint.y) ? sourceWrapper.bounds.topCenter : sourceWrapper.bounds.bottomCenter; } if (j === points.length - 2 && connector.targetPortID === '' && targetWrapper) { targetPoint = (currentPoint.y > nextPoint.y) ? targetWrapper.bounds.bottomCenter : targetWrapper.bounds.topCenter; } if (j === 0 && sourcePoint) { currentPoint.y = sourcePoint.y; currentPoint.x = nextPoint.x = sourcePoint.x; //Bug:849859 -set node bounds as source point if intersected point exists inside the node if (connector.sourcePortID === '') { var newDirection1 = currentPoint.y > nextPoint.y ? 'Top' : 'Bottom'; var refPoint = findPoint(sourceWrapper.bounds, getOppositeDirection(newDirection1)); sourcePoint = getIntersection(connector, sourceWrapper, sourcePoint, refPoint, false); } currentPoint.y = sourcePoint.y; } if (j === points.length - 2 && targetPoint) { if (j > 0 && connector.targetDecorator && ((targetPoint.y - nextPoint.y) < 0) && (Math.abs(targetPoint.y - currentPoint.y) < connector.targetDecorator.height + 1)) { currentPoint.y = points[j - 1].y -= this.size / 2; } if (j > 0 && connector.targetDecorator && ((targetPoint.y - nextPoint.y) > 0) && (Math.abs(targetPoint.y - currentPoint.y) < connector.targetDecorator.height + 1)) { currentPoint.y = points[j - 1].y += this.size / 2; } nextPoint.y = targetPoint.y; currentPoint.x = nextPoint.x = targetPoint.x; } } } if (diagram.avoidLineOverlappingModule && isUpdate) { diagram.avoidLineOverlappingModule.addConnector(connector, points); var modifiedConnectors = diagram.avoidLineOverlappingModule.getModifiedConnector(); if (modifiedConnectors.has(connector)) { segments = diagram.avoidLineOverlappingModule.getModifiedConnectorSegments(connector); modifiedConnectors.delete(connector); } if (modifiedConnectors.size > 0) { diagram.avoidLineOverlappingModule.refreshModifiedConnectors(diagram); } } if (segments.length === 0) { for (var j = 0; j < points.length - 1; j++) { var currentPoint = points[parseInt(j.toString(), 10)]; var nextPoint = points[j + 1]; if (currentPoint.x !== nextPoint.x) { if (currentPoint.x > nextPoint.x) { direction = 'Left'; length = currentPoint.x - nextPoint.x; } else { direction = 'Right'; length = nextPoint.x - currentPoint.x; } } else { if (currentPoint.y > nextPoint.y) { direction = 'Top'; length = currentPoint.y - nextPoint.y; } else { direction = 'Bottom'; length = nextPoint.y - currentPoint.y; } } seg = { type: 'Orthogonal', length: length, direction: direction }; segments.push(seg); } } } if (segments && segments.length > 0) { var lastSeg = segments[segments.length - 1]; if (segments.length === 1) { lastSeg.length -= 20; } if (lastSeg.length < 10 && segments.length === 2) { segments.pop(); if (segments.length > 0) { segments[0].length -= 20; lastSeg = segments[0]; } } if (connector.targetDecorator && ((lastSeg.direction === 'Top' || lastSeg.direction === 'Bottom') && lastSeg.length > connector.targetDecorator.height + 1) || ((lastSeg.direction === 'Right' || lastSeg.direction === 'Left') && lastSeg.length > connector.targetDecorator.width + 1)) { if (isUpdate || !diagram.avoidLineOverlappingModule) { connector.segments = segments; } if (isUpdate) { diagram.connectorPropertyChange(connector, {}, { type: 'Orthogonal', segments: segments }); } return true; } } return false; }; /* tslint:enable */ // Shortest path LineRouting.prototype.findPath = function (startGrid) { var intermediatePoint; var collection = []; var neigbours = this.findNearestNeigbours(startGrid, this.gridCollection, true); for (var i = 0; i < neigbours.length; i++) { intermediatePoint = this.findIntermediatePoints(neigbours[parseInt(i.toString(), 10)].gridX, neigbours[parseInt(i.toString(), 10)].gridY, startGrid.gridX, startGrid.gridY, this.targetGrid.gridX, this.targetGrid.gridY); if (intermediatePoint !== null) { var grid = this.gridCollection[intermediatePoint.x][intermediatePoint.y]; var h = this.octile(Math.abs(intermediatePoint.x - startGrid.gridX), Math.abs(intermediatePoint.y - startGrid.gridY)); if (startGrid.parent && startGrid.parent.parent) { if (grid.gridX !== startGrid.parent.gridX && grid.gridY !== startGrid.parent.gridY) { h += 0.1; } } var l = startGrid.previousDistance + h; if ((!grid.previousDistance || grid.previousDistance > l) && (!(intermediatePoint.x === startGrid.gridX && intermediatePoint.y === startGrid.gridY))) { collection.push(intermediatePoint); grid.previousDistance = l; grid.afterDistance = grid.afterDistance || this.manhattan(Math.abs(intermediatePoint.x - this.targetGrid.gridX), Math.abs(intermediatePoint.y - this.targetGrid.gridY)); grid