manual js syntax updates

development
mcyph 2021-03-20 10:37:54 +11:00
parent 0b8e1695f8
commit 5a241d7283
13 changed files with 4509 additions and 5142 deletions

View File

@ -2,22 +2,8 @@
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
* Class: mxGraphAbstractHierarchyCell
*
* An abstraction of an internal hierarchy node or edge
*
* Constructor: mxGraphAbstractHierarchyCell
*
* Constructs a new hierarchical layout algorithm.
*/
function mxGraphAbstractHierarchyCell()
{
this.x = [];
this.y = [];
this.temp = [];
};
class mxGraphAbstractHierarchyCell {
/**
* Variable: maxRank
*
@ -86,13 +72,27 @@ previousLayerConnectedCells = null;
*/
temp = null;
/**
* Class: mxGraphAbstractHierarchyCell
*
* An abstraction of an internal hierarchy node or edge
*
* Constructor: mxGraphAbstractHierarchyCell
*
* Constructs a new hierarchical layout algorithm.
*/
constructor() {
this.x = [];
this.y = [];
this.temp = [];
};
/**
* Function: getNextLayerConnectedCells
*
* Returns the cells this cell connects to on the next layer up
*/
getNextLayerConnectedCells = (layer)=>
{
getNextLayerConnectedCells = (layer) => {
return null;
};
@ -101,8 +101,7 @@ getNextLayerConnectedCells = (layer)=>
*
* Returns the cells this cell connects to on the next layer down
*/
getPreviousLayerConnectedCells = (layer)=>
{
getPreviousLayerConnectedCells = (layer) => {
return null;
};
@ -111,8 +110,7 @@ getPreviousLayerConnectedCells = (layer)=>
*
* Returns whether or not this cell is an edge
*/
isEdge = ()=>
{
isEdge = () => {
return false;
};
@ -121,8 +119,7 @@ isEdge = ()=>
*
* Returns whether or not this cell is a node
*/
isVertex = ()=>
{
isVertex = () => {
return false;
};
@ -131,8 +128,7 @@ isVertex = ()=>
*
* Gets the value of temp for the specified layer
*/
getGeneralPurposeVariable = (layer)=>
{
getGeneralPurposeVariable = (layer) => {
return null;
};
@ -141,8 +137,7 @@ getGeneralPurposeVariable = (layer)=>
*
* Set the value of temp for the specified layer
*/
setGeneralPurposeVariable = (layer, value)=>
{
setGeneralPurposeVariable = (layer, value) => {
return null;
};
@ -151,14 +146,10 @@ setGeneralPurposeVariable = (layer, value)=>
*
* Set the value of x for the specified layer
*/
setX = (layer, value)=>
{
if (this.isVertex())
{
setX = (layer, value) => {
if (this.isVertex()) {
this.x[0] = value;
}
else if (this.isEdge())
{
} else if (this.isEdge()) {
this.x[layer - this.minRank - 1] = value;
}
};
@ -168,14 +159,10 @@ setX = (layer, value)=>
*
* Gets the value of x on the specified layer
*/
getX = (layer)=>
{
if (this.isVertex())
{
getX = (layer) => {
if (this.isVertex()) {
return this.x[0];
}
else if (this.isEdge())
{
} else if (this.isEdge()) {
return this.x[layer - this.minRank - 1];
}
@ -187,14 +174,13 @@ getX = (layer)=>
*
* Set the value of y for the specified layer
*/
setY = (layer, value)=>
{
if (this.isVertex())
{
setY = (layer, value) => {
if (this.isVertex()) {
this.y[0] = value;
}
else if (this.isEdge())
{
} else if (this.isEdge()) {
this.y[layer - this.minRank - 1] = value;
}
};
}
export default mxGraphAbstractHierarchyCell;

View File

@ -2,37 +2,8 @@
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
* Class: mxGraphHierarchyEdge
*
* An abstraction of a hierarchical edge for the hierarchy layout
*
* Constructor: mxGraphHierarchyEdge
*
* Constructs a hierarchy edge
*
* Arguments:
*
* edges - a list of real graph edges this abstraction represents
*/
function mxGraphHierarchyEdge(edges)
{
mxGraphAbstractHierarchyCell.apply(this, arguments);
this.edges = edges;
this.ids = [];
for (var i = 0; i < edges.length; i++)
{
this.ids.push(mxObjectIdentity.get(edges[i]));
}
};
/**
* Extends mxGraphAbstractHierarchyCell.
*/
mxGraphHierarchyEdge.prototype = new mxGraphAbstractHierarchyCell();
constructor = mxGraphHierarchyEdge;
class mxGraphHierarchyEdge extends mxGraphAbstractHierarchyCell {
/**
* Variable: edges
*
@ -70,13 +41,35 @@ target = null;
*/
isReversed = false;
/**
* Class: mxGraphHierarchyEdge
*
* An abstraction of a hierarchical edge for the hierarchy layout
*
* Constructor: mxGraphHierarchyEdge
*
* Constructs a hierarchy edge
*
* Arguments:
*
* edges - a list of real graph edges this abstraction represents
*/
constructor(edges) {
super(edges);
this.edges = edges;
this.ids = [];
for (var i = 0; i < edges.length; i++) {
this.ids.push(mxObjectIdentity.get(edges[i]));
}
};
/**
* Function: invert
*
* Inverts the direction of this internal edge(s)
*/
invert = (layer)=>
{
invert = (layer) => {
var temp = this.source;
this.source = this.target;
this.target = temp;
@ -88,22 +81,16 @@ invert = (layer)=>
*
* Returns the cells this cell connects to on the next layer up
*/
getNextLayerConnectedCells = (layer)=>
{
if (this.nextLayerConnectedCells == null)
{
getNextLayerConnectedCells = (layer) => {
if (this.nextLayerConnectedCells == null) {
this.nextLayerConnectedCells = [];
for (var i = 0; i < this.temp.length; i++)
{
for (var i = 0; i < this.temp.length; i++) {
this.nextLayerConnectedCells[i] = [];
if (i == this.temp.length - 1)
{
if (i === this.temp.length - 1) {
this.nextLayerConnectedCells[i].push(this.source);
}
else
{
} else {
this.nextLayerConnectedCells[i].push(this);
}
}
@ -117,22 +104,16 @@ getNextLayerConnectedCells = (layer)=>
*
* Returns the cells this cell connects to on the next layer down
*/
getPreviousLayerConnectedCells = (layer)=>
{
if (this.previousLayerConnectedCells == null)
{
getPreviousLayerConnectedCells = (layer) => {
if (this.previousLayerConnectedCells == null) {
this.previousLayerConnectedCells = [];
for (var i = 0; i < this.temp.length; i++)
{
for (var i = 0; i < this.temp.length; i++) {
this.previousLayerConnectedCells[i] = [];
if (i == 0)
{
if (i === 0) {
this.previousLayerConnectedCells[i].push(this.target);
}
else
{
} else {
this.previousLayerConnectedCells[i].push(this);
}
}
@ -146,8 +127,7 @@ getPreviousLayerConnectedCells = (layer)=>
*
* Returns true.
*/
isEdge = ()=>
{
isEdge = () => {
return true;
};
@ -156,8 +136,7 @@ isEdge = ()=>
*
* Gets the value of temp for the specified layer
*/
getGeneralPurposeVariable = (layer)=>
{
getGeneralPurposeVariable = (layer) => {
return this.temp[layer - this.minRank - 1];
};
@ -166,8 +145,7 @@ getGeneralPurposeVariable = (layer)=>
*
* Set the value of temp for the specified layer
*/
setGeneralPurposeVariable = (layer, value)=>
{
setGeneralPurposeVariable = (layer, value) => {
this.temp[layer - this.minRank - 1] = value;
};
@ -176,12 +154,13 @@ setGeneralPurposeVariable = (layer, value)=>
*
* Gets the first core edge associated with this wrapper
*/
getCoreCell = ()=>
{
if (this.edges != null && this.edges.length > 0)
{
getCoreCell = () => {
if (this.edges != null && this.edges.length > 0) {
return this.edges[0];
}
return null;
};
}
export default mxGraphHierarchyEdge;

View File

@ -2,111 +2,10 @@
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
* Class: mxGraphHierarchyModel
*
* Internal model of a hierarchical graph. This model stores nodes and edges
* equivalent to the real graph nodes and edges, but also stores the rank of the
* cells, the order within the ranks and the new candidate locations of cells.
* The internal model also reverses edge direction were appropriate , ignores
* self-loop and groups parallels together under one edge object.
*
* Constructor: mxGraphHierarchyModel
*
* Creates an internal ordered graph model using the vertices passed in. If
* there are any, leftward edge need to be inverted in the internal model
*
* Arguments:
*
* graph - the facade describing the graph to be operated on
* vertices - the vertices for this hierarchy
* ordered - whether or not the vertices are already ordered
* deterministic - whether or not this layout should be deterministic on each
* tightenToSource - whether or not to tighten vertices towards the sources
* scanRanksFromSinks - Whether rank assignment is from the sinks or sources.
* usage
*/
function mxGraphHierarchyModel(layout, vertices, roots, parent, tightenToSource)
{
var graph = layout.getGraph();
this.tightenToSource = tightenToSource;
this.roots = roots;
this.parent = parent;
// map of cells to internal cell needed for second run through
// to setup the sink of edges correctly
this.vertexMapper = new mxDictionary();
this.edgeMapper = new mxDictionary();
this.maxRank = 0;
var internalVertices = [];
if (vertices == null)
{
vertices = this.graph.getChildVertices(parent);
}
this.maxRank = this.SOURCESCANSTARTRANK;
// map of cells to internal cell needed for second run through
// to setup the sink of edges correctly. Guess size by number
// of edges is roughly same as number of vertices.
this.createInternalCells(layout, vertices, internalVertices);
// Go through edges set their sink values. Also check the
// ordering if and invert edges if necessary
for (var i = 0; i < vertices.length; i++)
{
var edges = internalVertices[i].connectsAsSource;
for (var j = 0; j < edges.length; j++)
{
var internalEdge = edges[j];
var realEdges = internalEdge.edges;
// Only need to process the first real edge, since
// all the edges connect to the same other vertex
if (realEdges != null && realEdges.length > 0)
{
var realEdge = realEdges[0];
var targetCell = layout.getVisibleTerminal(
realEdge, false);
var internalTargetCell = this.vertexMapper.get(targetCell);
if (internalVertices[i] == internalTargetCell)
{
// If there are parallel edges going between two vertices and not all are in the same direction
// you can have navigated across one direction when doing the cycle reversal that isn't the same
// direction as the first real edge in the array above. When that happens the if above catches
// that and we correct the target cell before continuing.
// This branch only detects this single case
targetCell = layout.getVisibleTerminal(
realEdge, true);
internalTargetCell = this.vertexMapper.get(targetCell);
}
if (internalTargetCell != null
&& internalVertices[i] != internalTargetCell)
{
internalEdge.target = internalTargetCell;
if (internalTargetCell.connectsAsTarget.length == 0)
{
internalTargetCell.connectsAsTarget = [];
}
if (mxUtils.indexOf(internalTargetCell.connectsAsTarget, internalEdge) < 0)
{
internalTargetCell.connectsAsTarget.push(internalEdge);
}
}
}
}
// Use the temp variable in the internal nodes to mark this
// internal vertex as having been visited.
internalVertices[i].temp[0] = 1;
}
};
import mxDictionary from "FIXME";
class mxGraphHierarchyModel {
/**
* Variable: maxRank
*
@ -172,6 +71,102 @@ SOURCESCANSTARTRANK = 100000000;
*/
tightenToSource = false;
/**
* Class: mxGraphHierarchyModel
*
* Internal model of a hierarchical graph. This model stores nodes and edges
* equivalent to the real graph nodes and edges, but also stores the rank of the
* cells, the order within the ranks and the new candidate locations of cells.
* The internal model also reverses edge direction were appropriate , ignores
* self-loop and groups parallels together under one edge object.
*
* Constructor: mxGraphHierarchyModel
*
* Creates an internal ordered graph model using the vertices passed in. If
* there are any, leftward edge need to be inverted in the internal model
*
* Arguments:
*
* graph - the facade describing the graph to be operated on
* vertices - the vertices for this hierarchy
* ordered - whether or not the vertices are already ordered
* deterministic - whether or not this layout should be deterministic on each
* tightenToSource - whether or not to tighten vertices towards the sources
* scanRanksFromSinks - Whether rank assignment is from the sinks or sources.
* usage
*/
constructor(layout, vertices, roots, parent, tightenToSource) {
var graph = layout.getGraph();
this.tightenToSource = tightenToSource;
this.roots = roots;
this.parent = parent;
// map of cells to internal cell needed for second run through
// to setup the sink of edges correctly
this.vertexMapper = new mxDictionary();
this.edgeMapper = new mxDictionary();
this.maxRank = 0;
var internalVertices = [];
if (vertices == null) {
vertices = this.graph.getChildVertices(parent);
}
this.maxRank = this.SOURCESCANSTARTRANK;
// map of cells to internal cell needed for second run through
// to setup the sink of edges correctly. Guess size by number
// of edges is roughly same as number of vertices.
this.createInternalCells(layout, vertices, internalVertices);
// Go through edges set their sink values. Also check the
// ordering if and invert edges if necessary
for (var i = 0; i < vertices.length; i++) {
var edges = internalVertices[i].connectsAsSource;
for (var j = 0; j < edges.length; j++) {
var internalEdge = edges[j];
var realEdges = internalEdge.edges;
// Only need to process the first real edge, since
// all the edges connect to the same other vertex
if (realEdges != null && realEdges.length > 0) {
var realEdge = realEdges[0];
var targetCell = layout.getVisibleTerminal(
realEdge, false);
var internalTargetCell = this.vertexMapper.get(targetCell);
if (internalVertices[i] == internalTargetCell) {
// If there are parallel edges going between two vertices and not all are in the same direction
// you can have navigated across one direction when doing the cycle reversal that isn't the same
// direction as the first real edge in the array above. When that happens the if above catches
// that and we correct the target cell before continuing.
// This branch only detects this single case
targetCell = layout.getVisibleTerminal(
realEdge, true);
internalTargetCell = this.vertexMapper.get(targetCell);
}
if (internalTargetCell != null
&& internalVertices[i] != internalTargetCell) {
internalEdge.target = internalTargetCell;
if (internalTargetCell.connectsAsTarget.length == 0) {
internalTargetCell.connectsAsTarget = [];
}
if (mxUtils.indexOf(internalTargetCell.connectsAsTarget, internalEdge) < 0) {
internalTargetCell.connectsAsTarget.push(internalEdge);
}
}
}
}
// Use the temp variable in the internal nodes to mark this
// internal vertex as having been visited.
internalVertices[i].temp[0] = 1;
}
};
/**
* Function: createInternalCells
*
@ -185,13 +180,11 @@ tightenToSource = false;
* internalVertices - The array of <mxGraphHierarchyNodes> to have their
* information filled in using the real vertices.
*/
createInternalCells = (layout, vertices, internalVertices)=>
{
createInternalCells = (layout, vertices, internalVertices) => {
var graph = layout.getGraph();
// Create internal edges
for (var i = 0; i < vertices.length; i++)
{
for (var i = 0; i < vertices.length; i++) {
internalVertices[i] = new mxGraphHierarchyNode(vertices[i]);
this.vertexMapper.put(vertices[i], internalVertices[i]);
@ -203,14 +196,12 @@ createInternalCells = (layout, vertices, internalVertices)=>
// Create internal edges, but don't do any rank assignment yet
// First use the information from the greedy cycle remover to
// invert the leftward edges internally
for (var j = 0; j < conns.length; j++)
{
for (var j = 0; j < conns.length; j++) {
var cell = layout.getVisibleTerminal(conns[j], false);
// Looking for outgoing edges only
if (cell != vertices[i] && layout.graph.model.isVertex(cell) &&
!layout.isVertexIgnored(cell))
{
!layout.isVertexIgnored(cell)) {
// We process all edge between this source and its targets
// If there are edges going both ways, we need to collect
// them all into one internal edges to avoid looping problems
@ -232,12 +223,10 @@ createInternalCells = (layout, vertices, internalVertices)=>
if (undirectedEdges != null &&
undirectedEdges.length > 0 &&
this.edgeMapper.get(undirectedEdges[0]) == null &&
directedEdges.length * 2 >= undirectedEdges.length)
{
directedEdges.length * 2 >= undirectedEdges.length) {
var internalEdge = new mxGraphHierarchyEdge(undirectedEdges);
for (var k = 0; k < undirectedEdges.length; k++)
{
for (var k = 0; k < undirectedEdges.length; k++) {
var edge = undirectedEdges[k];
this.edgeMapper.put(edge, internalEdge);
@ -245,8 +234,7 @@ createInternalCells = (layout, vertices, internalVertices)=>
// without deleting it from the cell style
graph.resetEdge(edge);
if (layout.disableEdgeStyle)
{
if (layout.disableEdgeStyle) {
layout.setEdgeStyleEnabled(edge, false);
layout.setOrthogonalEdge(edge, true);
}
@ -254,8 +242,7 @@ createInternalCells = (layout, vertices, internalVertices)=>
internalEdge.source = internalVertices[i];
if (mxUtils.indexOf(internalVertices[i].connectsAsSource, internalEdge) < 0)
{
if (mxUtils.indexOf(internalVertices[i].connectsAsSource, internalEdge) < 0) {
internalVertices[i].connectsAsSource.push(internalEdge);
}
}
@ -274,18 +261,14 @@ createInternalCells = (layout, vertices, internalVertices)=>
* or sinks and working through each node in the relevant edge direction.
* Starting at the sinks is basically a longest path layering algorithm.
*/
initialRank = ()=>
{
initialRank = () => {
var startNodes = [];
if (this.roots != null)
{
for (var i = 0; i < this.roots.length; i++)
{
if (this.roots != null) {
for (var i = 0; i < this.roots.length; i++) {
var internalNode = this.vertexMapper.get(this.roots[i]);
if (internalNode != null)
{
if (internalNode != null) {
startNodes.push(internalNode);
}
}
@ -293,16 +276,14 @@ initialRank = ()=>
var internalNodes = this.vertexMapper.getValues();
for (var i=0; i < internalNodes.length; i++)
{
for (var i = 0; i < internalNodes.length; i++) {
// Mark the node as not having had a layer assigned
internalNodes[i].temp[0] = -1;
}
var startNodesCopy = startNodes.slice();
while (startNodes.length > 0)
{
while (startNodes.length > 0) {
var internalNode = startNodes[0];
var layerDeterminingEdges;
var edgesToBeMarked;
@ -319,19 +300,15 @@ initialRank = ()=>
// the layer determining edges variable
var minimumLayer = this.SOURCESCANSTARTRANK;
for (var i = 0; i < layerDeterminingEdges.length; i++)
{
for (var i = 0; i < layerDeterminingEdges.length; i++) {
var internalEdge = layerDeterminingEdges[i];
if (internalEdge.temp[0] == 5270620)
{
if (internalEdge.temp[0] == 5270620) {
// This edge has been scanned, get the layer of the
// node on the other end
var otherNode = internalEdge.source;
minimumLayer = Math.min(minimumLayer, otherNode.temp[0] - 1);
}
else
{
} else {
allEdgesScanned = false;
break;
@ -340,15 +317,12 @@ initialRank = ()=>
// If all edge have been scanned, assign the layer, mark all
// edges in the other direction and remove from the nodes list
if (allEdgesScanned)
{
if (allEdgesScanned) {
internalNode.temp[0] = minimumLayer;
this.maxRank = Math.min(this.maxRank, minimumLayer);
if (edgesToBeMarked != null)
{
for (var i = 0; i < edgesToBeMarked.length; i++)
{
if (edgesToBeMarked != null) {
for (var i = 0; i < edgesToBeMarked.length; i++) {
var internalEdge = edgesToBeMarked[i];
// Assign unique stamp ( y/m/d/h )
@ -359,8 +333,7 @@ initialRank = ()=>
var otherNode = internalEdge.target;
// Only add node if it hasn't been assigned a layer
if (otherNode.temp[0] == -1)
{
if (otherNode.temp[0] == -1) {
startNodes.push(otherNode);
// Mark this other node as neither being
@ -373,16 +346,13 @@ initialRank = ()=>
}
startNodes.shift();
}
else
{
} else {
// Not all the edges have been scanned, get to the back of
// the class and put the dunces cap on
var removedCell = startNodes.shift();
startNodes.push(internalNode);
if (removedCell == internalNode && startNodes.length == 1)
{
if (removedCell == internalNode && startNodes.length == 1) {
// This is an error condition, we can't get out of
// this loop. It could happen for more than one node
// but that's a lot harder to detect. Log the error
@ -394,21 +364,18 @@ initialRank = ()=>
// Normalize the ranks down from their large starting value to place
// at least 1 sink on layer 0
for (var i=0; i < internalNodes.length; i++)
{
for (var i = 0; i < internalNodes.length; i++) {
// Mark the node as not having had a layer assigned
internalNodes[i].temp[0] -= this.maxRank;
}
// Tighten the rank 0 nodes as far as possible
for ( var i = 0; i < startNodesCopy.length; i++)
{
for (var i = 0; i < startNodesCopy.length; i++) {
var internalNode = startNodesCopy[i];
var currentMaxLayer = 0;
var layerDeterminingEdges = internalNode.connectsAsSource;
for ( var j = 0; j < layerDeterminingEdges.length; j++)
{
for (var j = 0; j < layerDeterminingEdges.length; j++) {
var internalEdge = layerDeterminingEdges[j];
var otherNode = internalEdge.target;
internalNode.temp[0] = Math.max(currentMaxLayer,
@ -428,13 +395,11 @@ initialRank = ()=>
* Fixes the layer assignments to the values stored in the nodes. Also needs
* to create dummy nodes for edges that cross layers.
*/
fixRanks = ()=>
{
fixRanks = () => {
var rankList = [];
this.ranks = [];
for (var i = 0; i < this.maxRank + 1; i++)
{
for (var i = 0; i < this.maxRank + 1; i++) {
rankList[i] = [];
this.ranks[i] = rankList[i];
}
@ -444,23 +409,19 @@ fixRanks = ()=>
// crossings for a standard tree.
var rootsArray = null;
if (this.roots != null)
{
if (this.roots != null) {
var oldRootsArray = this.roots;
rootsArray = [];
for (var i = 0; i < oldRootsArray.length; i++)
{
for (var i = 0; i < oldRootsArray.length; i++) {
var cell = oldRootsArray[i];
var internalNode = this.vertexMapper.get(cell);
rootsArray[i] = internalNode;
}
}
this.visit((parent, node, edge, layer, seen)=>
{
if (seen == 0 && node.maxRank < 0 && node.minRank < 0)
{
this.visit((parent, node, edge, layer, seen) => {
if (seen == 0 && node.maxRank < 0 && node.minRank < 0) {
rankList[node.temp[0]].push(node);
node.maxRank = node.temp[0];
node.minRank = node.temp[0];
@ -469,12 +430,10 @@ fixRanks = ()=>
node.temp[0] = rankList[node.maxRank].length - 1;
}
if (parent != null && edge != null)
{
if (parent != null && edge != null) {
var parentToCellRankDifference = parent.maxRank - node.maxRank;
if (parentToCellRankDifference > 1)
{
if (parentToCellRankDifference > 1) {
// There are ranks in between the parent and current cell
edge.maxRank = parent.maxRank;
edge.minRank = node.maxRank;
@ -482,8 +441,7 @@ fixRanks = ()=>
edge.x = [];
edge.y = [];
for (var i = edge.minRank + 1; i < edge.maxRank; i++)
{
for (var i = edge.minRank + 1; i < edge.maxRank; i++) {
// The connecting edge must be added to the
// appropriate ranks
rankList[i].push(edge);
@ -506,33 +464,25 @@ fixRanks = ()=>
* trackAncestors - Whether or not the search is to keep track all nodes
* directly above this one in the search path.
*/
visit = (visitor, dfsRoots, trackAncestors, seenNodes)=>
{
visit = (visitor, dfsRoots, trackAncestors, seenNodes) => {
// Run dfs through on all roots
if (dfsRoots != null)
{
for (var i = 0; i < dfsRoots.length; i++)
{
if (dfsRoots != null) {
for (var i = 0; i < dfsRoots.length; i++) {
var internalNode = dfsRoots[i];
if (internalNode != null)
{
if (seenNodes == null)
{
if (internalNode != null) {
if (seenNodes == null) {
seenNodes = new Object();
}
if (trackAncestors)
{
if (trackAncestors) {
// Set up hash code for root
internalNode.hashCode = [];
internalNode.hashCode[0] = this.dfsCount;
internalNode.hashCode[1] = i;
this.extendedDfs(null, internalNode, null, visitor, seenNodes,
internalNode.hashCode, i, 0);
}
else
{
} else {
this.dfs(null, internalNode, null, visitor, seenNodes, 0);
}
}
@ -558,14 +508,11 @@ visit = (visitor, dfsRoots, trackAncestors, seenNodes)=>
* ancestor node of the current node
* layer - the layer on the dfs tree ( not the same as the model ranks )
*/
dfs = (parent, root, connectingEdge, visitor, seen, layer)=>
{
if (root != null)
{
dfs = (parent, root, connectingEdge, visitor, seen, layer) => {
if (root != null) {
var rootId = root.id;
if (seen[rootId] == null)
{
if (seen[rootId] == null) {
seen[rootId] = root;
visitor(parent, root, connectingEdge, layer, 0);
@ -573,8 +520,7 @@ dfs = (parent, root, connectingEdge, visitor, seen, layer)=>
// can change the original for edge direction inversions
var outgoingEdges = root.connectsAsSource.slice();
for (var i = 0; i< outgoingEdges.length; i++)
{
for (var i = 0; i < outgoingEdges.length; i++) {
var internalEdge = outgoingEdges[i];
var targetNode = internalEdge.target;
@ -582,9 +528,7 @@ dfs = (parent, root, connectingEdge, visitor, seen, layer)=>
this.dfs(root, targetNode, internalEdge, visitor, seen,
layer + 1);
}
}
else
{
} else {
// Use the int field to indicate this node has been seen
visitor(parent, root, connectingEdge, layer, 1);
}
@ -611,8 +555,7 @@ dfs = (parent, root, connectingEdge, visitor, seen, layer)=>
* childHash - the new hash code for this node
* layer - the layer on the dfs tree ( not the same as the model ranks )
*/
extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash, layer)=>
{
extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash, layer) => {
// Explanation of custom hash set. Previously, the ancestors variable
// was passed through the dfs as a HashSet. The ancestors were copied
// into a new HashSet and when the new child was processed it was also
@ -633,18 +576,15 @@ extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash
// through each run of the dfs algorithm (therefore the dfs is not
// thread safe). The hash code of each node is set if not already set,
// or if the first int does not match that of the current run.
if (root != null)
{
if (parent != null)
{
if (root != null) {
if (parent != null) {
// Form this nodes hash code if necessary, that is, if the
// hashCode variable has not been initialized or if the
// start of the parent hash code does not equal the start of
// this nodes hash code, indicating the code was set on a
// previous run of this dfs.
if (root.hashCode == null ||
root.hashCode[0] != parent.hashCode[0])
{
root.hashCode[0] != parent.hashCode[0]) {
var hashCodeLength = parent.hashCode.length + 1;
root.hashCode = parent.hashCode.slice();
root.hashCode[hashCodeLength - 1] = childHash;
@ -653,8 +593,7 @@ extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash
var rootId = root.id;
if (seen[rootId] == null)
{
if (seen[rootId] == null) {
seen[rootId] = root;
visitor(parent, root, connectingEdge, layer, 0);
@ -662,8 +601,7 @@ extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash
// can change the original for edge direction inversions
var outgoingEdges = root.connectsAsSource.slice();
for (var i = 0; i < outgoingEdges.length; i++)
{
for (var i = 0; i < outgoingEdges.length; i++) {
var internalEdge = outgoingEdges[i];
var targetNode = internalEdge.target;
@ -671,11 +609,12 @@ extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash
this.extendedDfs(root, targetNode, internalEdge, visitor, seen,
root.hashCode, i, layer + 1);
}
}
else
{
} else {
// Use the int field to indicate this node has been seen
visitor(parent, root, connectingEdge, layer, 1);
}
}
};
}
export default mxGraphHierarchyModel;

View File

@ -15,21 +15,15 @@
*
* cell - the real graph cell this node represents
*/
function mxGraphHierarchyNode(cell)
{
mxGraphAbstractHierarchyCell.apply(this, arguments);
class mxGraphHierarchyNode extends mxGraphAbstractHierarchyCell {
constructor(cell) {
super(cell);
this.cell = cell;
this.id = mxObjectIdentity.get(cell);
this.connectsAsTarget = [];
this.connectsAsSource = [];
};
/**
* Extends mxGraphAbstractHierarchyCell.
*/
mxGraphHierarchyNode.prototype = new mxGraphAbstractHierarchyCell();
constructor = mxGraphHierarchyNode;
/**
* Variable: cell
*
@ -71,8 +65,7 @@ hashCode = false;
*
* Returns the integer value of the layer that this node resides in
*/
getRankValue = (layer)=>
{
getRankValue = (layer) => {
return this.maxRank;
};
@ -81,25 +74,19 @@ getRankValue = (layer)=>
*
* Returns the cells this cell connects to on the next layer up
*/
getNextLayerConnectedCells = (layer)=>
{
if (this.nextLayerConnectedCells == null)
{
getNextLayerConnectedCells = (layer) => {
if (this.nextLayerConnectedCells == null) {
this.nextLayerConnectedCells = [];
this.nextLayerConnectedCells[0] = [];
for (var i = 0; i < this.connectsAsTarget.length; i++)
{
for (var i = 0; i < this.connectsAsTarget.length; i++) {
var edge = this.connectsAsTarget[i];
if (edge.maxRank == -1 || edge.maxRank == layer + 1)
{
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
this.nextLayerConnectedCells[0].push(edge.source);
}
else
{
} else {
// Edge spans at least two layers, add edge
this.nextLayerConnectedCells[0].push(edge);
}
@ -114,24 +101,18 @@ getNextLayerConnectedCells = (layer)=>
*
* Returns the cells this cell connects to on the next layer down
*/
getPreviousLayerConnectedCells = (layer)=>
{
if (this.previousLayerConnectedCells == null)
{
getPreviousLayerConnectedCells = (layer) => {
if (this.previousLayerConnectedCells == null) {
this.previousLayerConnectedCells = [];
this.previousLayerConnectedCells[0] = [];
for (var i = 0; i < this.connectsAsSource.length; i++)
{
for (var i = 0; i < this.connectsAsSource.length; i++) {
var edge = this.connectsAsSource[i];
if (edge.minRank == -1 || edge.minRank == layer - 1)
{
if (edge.minRank == -1 || edge.minRank == layer - 1) {
// No dummy nodes in edge, add node of other side of edge
this.previousLayerConnectedCells[0].push(edge.target);
}
else
{
} else {
// Edge spans at least two layers, add edge
this.previousLayerConnectedCells[0].push(edge);
}
@ -146,8 +127,7 @@ getPreviousLayerConnectedCells = (layer)=>
*
* Returns true.
*/
isVertex = ()=>
{
isVertex = () => {
return true;
};
@ -156,8 +136,7 @@ isVertex = ()=>
*
* Gets the value of temp for the specified layer
*/
getGeneralPurposeVariable = (layer)=>
{
getGeneralPurposeVariable = (layer) => {
return this.temp[0];
};
@ -166,28 +145,23 @@ getGeneralPurposeVariable = (layer)=>
*
* Set the value of temp for the specified layer
*/
setGeneralPurposeVariable = (layer, value)=>
{
setGeneralPurposeVariable = (layer, value) => {
this.temp[0] = value;
};
/**
* Function: isAncestor
*/
isAncestor = (otherNode)=>
{
isAncestor = (otherNode) => {
// Firstly, the hash code of this node needs to be shorter than the
// other node
if (otherNode != null && this.hashCode != null && otherNode.hashCode != null
&& this.hashCode.length < otherNode.hashCode.length)
{
if (this.hashCode == otherNode.hashCode)
{
&& this.hashCode.length < otherNode.hashCode.length) {
if (this.hashCode == otherNode.hashCode) {
return true;
}
if (this.hashCode == null || this.hashCode == null)
{
if (this.hashCode == null || this.hashCode == null) {
return false;
}
@ -195,10 +169,8 @@ isAncestor = (otherNode)=>
// node's hash code. Arrays.equals cannot be used here since
// the arrays are different length, and we do not want to
// perform another array copy.
for (var i = 0; i < this.hashCode.length; i++)
{
if (this.hashCode[i] != otherNode.hashCode[i])
{
for (var i = 0; i < this.hashCode.length; i++) {
if (this.hashCode[i] != otherNode.hashCode[i]) {
return false;
}
}
@ -214,7 +186,9 @@ isAncestor = (otherNode)=>
*
* Gets the core vertex associated with this wrapper
*/
getCoreCell = ()=>
{
getCoreCell = () => {
return this.cell;
};
}
export default mxGraphHierarchyNode;

View File

@ -2,111 +2,8 @@
* Copyright (c) 2006-2018, JGraph Ltd
* Copyright (c) 2006-2018, Gaudenz Alder
*/
/**
* Class: mxSwimlaneModel
*
* Internal model of a hierarchical graph. This model stores nodes and edges
* equivalent to the real graph nodes and edges, but also stores the rank of the
* cells, the order within the ranks and the new candidate locations of cells.
* The internal model also reverses edge direction were appropriate , ignores
* self-loop and groups parallels together under one edge object.
*
* Constructor: mxSwimlaneModel
*
* Creates an internal ordered graph model using the vertices passed in. If
* there are any, leftward edge need to be inverted in the internal model
*
* Arguments:
*
* graph - the facade describing the graph to be operated on
* vertices - the vertices for this hierarchy
* ordered - whether or not the vertices are already ordered
* deterministic - whether or not this layout should be deterministic on each
* tightenToSource - whether or not to tighten vertices towards the sources
* scanRanksFromSinks - Whether rank assignment is from the sinks or sources.
* usage
*/
function mxSwimlaneModel(layout, vertices, roots, parent, tightenToSource)
{
var graph = layout.getGraph();
this.tightenToSource = tightenToSource;
this.roots = roots;
this.parent = parent;
// map of cells to internal cell needed for second run through
// to setup the sink of edges correctly
this.vertexMapper = new mxDictionary();
this.edgeMapper = new mxDictionary();
this.maxRank = 0;
var internalVertices = [];
if (vertices == null)
{
vertices = this.graph.getChildVertices(parent);
}
this.maxRank = this.SOURCESCANSTARTRANK;
// map of cells to internal cell needed for second run through
// to setup the sink of edges correctly. Guess size by number
// of edges is roughly same as number of vertices.
this.createInternalCells(layout, vertices, internalVertices);
// Go through edges set their sink values. Also check the
// ordering if and invert edges if necessary
for (var i = 0; i < vertices.length; i++)
{
var edges = internalVertices[i].connectsAsSource;
for (var j = 0; j < edges.length; j++)
{
var internalEdge = edges[j];
var realEdges = internalEdge.edges;
// Only need to process the first real edge, since
// all the edges connect to the same other vertex
if (realEdges != null && realEdges.length > 0)
{
var realEdge = realEdges[0];
var targetCell = layout.getVisibleTerminal(
realEdge, false);
var internalTargetCell = this.vertexMapper.get(targetCell);
if (internalVertices[i] == internalTargetCell)
{
// If there are parallel edges going between two vertices and not all are in the same direction
// you can have navigated across one direction when doing the cycle reversal that isn't the same
// direction as the first real edge in the array above. When that happens the if above catches
// that and we correct the target cell before continuing.
// This branch only detects this single case
targetCell = layout.getVisibleTerminal(
realEdge, true);
internalTargetCell = this.vertexMapper.get(targetCell);
}
if (internalTargetCell != null
&& internalVertices[i] != internalTargetCell)
{
internalEdge.target = internalTargetCell;
if (internalTargetCell.connectsAsTarget.length == 0)
{
internalTargetCell.connectsAsTarget = [];
}
if (mxUtils.indexOf(internalTargetCell.connectsAsTarget, internalEdge) < 0)
{
internalTargetCell.connectsAsTarget.push(internalEdge);
}
}
}
}
// Use the temp variable in the internal nodes to mark this
// internal vertex as having been visited.
internalVertices[i].temp[0] = 1;
}
};
class mxSwimlaneModel {
/**
* Variable: maxRank
*
@ -179,6 +76,102 @@ tightenToSource = false;
*/
ranksPerGroup = null;
/**
* Class: mxSwimlaneModel
*
* Internal model of a hierarchical graph. This model stores nodes and edges
* equivalent to the real graph nodes and edges, but also stores the rank of the
* cells, the order within the ranks and the new candidate locations of cells.
* The internal model also reverses edge direction were appropriate , ignores
* self-loop and groups parallels together under one edge object.
*
* Constructor: mxSwimlaneModel
*
* Creates an internal ordered graph model using the vertices passed in. If
* there are any, leftward edge need to be inverted in the internal model
*
* Arguments:
*
* graph - the facade describing the graph to be operated on
* vertices - the vertices for this hierarchy
* ordered - whether or not the vertices are already ordered
* deterministic - whether or not this layout should be deterministic on each
* tightenToSource - whether or not to tighten vertices towards the sources
* scanRanksFromSinks - Whether rank assignment is from the sinks or sources.
* usage
*/
constructor(layout, vertices, roots, parent, tightenToSource) {
var graph = layout.getGraph();
this.tightenToSource = tightenToSource;
this.roots = roots;
this.parent = parent;
// map of cells to internal cell needed for second run through
// to setup the sink of edges correctly
this.vertexMapper = new mxDictionary();
this.edgeMapper = new mxDictionary();
this.maxRank = 0;
var internalVertices = [];
if (vertices == null) {
vertices = this.graph.getChildVertices(parent);
}
this.maxRank = this.SOURCESCANSTARTRANK;
// map of cells to internal cell needed for second run through
// to setup the sink of edges correctly. Guess size by number
// of edges is roughly same as number of vertices.
this.createInternalCells(layout, vertices, internalVertices);
// Go through edges set their sink values. Also check the
// ordering if and invert edges if necessary
for (var i = 0; i < vertices.length; i++) {
var edges = internalVertices[i].connectsAsSource;
for (var j = 0; j < edges.length; j++) {
var internalEdge = edges[j];
var realEdges = internalEdge.edges;
// Only need to process the first real edge, since
// all the edges connect to the same other vertex
if (realEdges != null && realEdges.length > 0) {
var realEdge = realEdges[0];
var targetCell = layout.getVisibleTerminal(
realEdge, false);
var internalTargetCell = this.vertexMapper.get(targetCell);
if (internalVertices[i] == internalTargetCell) {
// If there are parallel edges going between two vertices and not all are in the same direction
// you can have navigated across one direction when doing the cycle reversal that isn't the same
// direction as the first real edge in the array above. When that happens the if above catches
// that and we correct the target cell before continuing.
// This branch only detects this single case
targetCell = layout.getVisibleTerminal(
realEdge, true);
internalTargetCell = this.vertexMapper.get(targetCell);
}
if (internalTargetCell != null
&& internalVertices[i] != internalTargetCell) {
internalEdge.target = internalTargetCell;
if (internalTargetCell.connectsAsTarget.length == 0) {
internalTargetCell.connectsAsTarget = [];
}
if (mxUtils.indexOf(internalTargetCell.connectsAsTarget, internalEdge) < 0) {
internalTargetCell.connectsAsTarget.push(internalEdge);
}
}
}
}
// Use the temp variable in the internal nodes to mark this
// internal vertex as having been visited.
internalVertices[i].temp[0] = 1;
}
};
/**
* Function: createInternalCells
*
@ -192,22 +185,18 @@ ranksPerGroup = null;
* internalVertices - The array of <mxGraphHierarchyNodes> to have their
* information filled in using the real vertices.
*/
createInternalCells = (layout, vertices, internalVertices)=>
{
createInternalCells = (layout, vertices, internalVertices) => {
var graph = layout.getGraph();
var swimlanes = layout.swimlanes;
// Create internal edges
for (var i = 0; i < vertices.length; i++)
{
for (var i = 0; i < vertices.length; i++) {
internalVertices[i] = new mxGraphHierarchyNode(vertices[i]);
this.vertexMapper.put(vertices[i], internalVertices[i]);
internalVertices[i].swimlaneIndex = -1;
for (var ii = 0; ii < swimlanes.length; ii++)
{
if (graph.model.getParent(vertices[i]) == swimlanes[ii])
{
for (var ii = 0; ii < swimlanes.length; ii++) {
if (graph.model.getParent(vertices[i]) == swimlanes[ii]) {
internalVertices[i].swimlaneIndex = ii;
break;
}
@ -221,14 +210,12 @@ createInternalCells = (layout, vertices, internalVertices)=>
// Create internal edges, but don't do any rank assignment yet
// First use the information from the greedy cycle remover to
// invert the leftward edges internally
for (var j = 0; j < conns.length; j++)
{
for (var j = 0; j < conns.length; j++) {
var cell = layout.getVisibleTerminal(conns[j], false);
// Looking for outgoing edges only
if (cell != vertices[i] && layout.graph.model.isVertex(cell) &&
!layout.isVertexIgnored(cell))
{
!layout.isVertexIgnored(cell)) {
// We process all edge between this source and its targets
// If there are edges going both ways, we need to collect
// them all into one internal edges to avoid looping problems
@ -250,12 +237,10 @@ createInternalCells = (layout, vertices, internalVertices)=>
if (undirectedEdges != null &&
undirectedEdges.length > 0 &&
this.edgeMapper.get(undirectedEdges[0]) == null &&
directedEdges.length * 2 >= undirectedEdges.length)
{
directedEdges.length * 2 >= undirectedEdges.length) {
var internalEdge = new mxGraphHierarchyEdge(undirectedEdges);
for (var k = 0; k < undirectedEdges.length; k++)
{
for (var k = 0; k < undirectedEdges.length; k++) {
var edge = undirectedEdges[k];
this.edgeMapper.put(edge, internalEdge);
@ -263,8 +248,7 @@ createInternalCells = (layout, vertices, internalVertices)=>
// without deleting it from the cell style
graph.resetEdge(edge);
if (layout.disableEdgeStyle)
{
if (layout.disableEdgeStyle) {
layout.setEdgeStyleEnabled(edge, false);
layout.setOrthogonalEdge(edge, true);
}
@ -272,8 +256,7 @@ createInternalCells = (layout, vertices, internalVertices)=>
internalEdge.source = internalVertices[i];
if (mxUtils.indexOf(internalVertices[i].connectsAsSource, internalEdge) < 0)
{
if (mxUtils.indexOf(internalVertices[i].connectsAsSource, internalEdge) < 0) {
internalVertices[i].connectsAsSource.push(internalEdge);
}
}
@ -292,22 +275,18 @@ createInternalCells = (layout, vertices, internalVertices)=>
* or sinks and working through each node in the relevant edge direction.
* Starting at the sinks is basically a longest path layering algorithm.
*/
initialRank = ()=>
{
initialRank = () => {
this.ranksPerGroup = [];
var startNodes = [];
var seen = new Object();
if (this.roots != null)
{
for (var i = 0; i < this.roots.length; i++)
{
if (this.roots != null) {
for (var i = 0; i < this.roots.length; i++) {
var internalNode = this.vertexMapper.get(this.roots[i]);
this.maxChainDfs(null, internalNode, null, seen, 0);
if (internalNode != null)
{
if (internalNode != null) {
startNodes.push(internalNode);
}
}
@ -317,14 +296,10 @@ initialRank = ()=>
var lowerRank = [];
var upperRank = [];
for (var i = this.ranksPerGroup.length - 1; i >= 0; i--)
{
if (i == this.ranksPerGroup.length - 1)
{
for (var i = this.ranksPerGroup.length - 1; i >= 0; i--) {
if (i == this.ranksPerGroup.length - 1) {
lowerRank[i] = 0;
}
else
{
} else {
lowerRank[i] = upperRank[i + 1] + 1;
}
@ -335,16 +310,14 @@ initialRank = ()=>
var internalNodes = this.vertexMapper.getValues();
for (var i=0; i < internalNodes.length; i++)
{
for (var i = 0; i < internalNodes.length; i++) {
// Mark the node as not having had a layer assigned
internalNodes[i].temp[0] = -1;
}
var startNodesCopy = startNodes.slice();
while (startNodes.length > 0)
{
while (startNodes.length > 0) {
var internalNode = startNodes[0];
var layerDeterminingEdges;
var edgesToBeMarked;
@ -361,19 +334,15 @@ initialRank = ()=>
// the layer determining edges variable
var minimumLayer = upperRank[0];
for (var i = 0; i < layerDeterminingEdges.length; i++)
{
for (var i = 0; i < layerDeterminingEdges.length; i++) {
var internalEdge = layerDeterminingEdges[i];
if (internalEdge.temp[0] == 5270620)
{
if (internalEdge.temp[0] == 5270620) {
// This edge has been scanned, get the layer of the
// node on the other end
var otherNode = internalEdge.source;
minimumLayer = Math.min(minimumLayer, otherNode.temp[0] - 1);
}
else
{
} else {
allEdgesScanned = false;
break;
@ -382,19 +351,15 @@ initialRank = ()=>
// If all edge have been scanned, assign the layer, mark all
// edges in the other direction and remove from the nodes list
if (allEdgesScanned)
{
if (minimumLayer > upperRank[internalNode.swimlaneIndex])
{
if (allEdgesScanned) {
if (minimumLayer > upperRank[internalNode.swimlaneIndex]) {
minimumLayer = upperRank[internalNode.swimlaneIndex];
}
internalNode.temp[0] = minimumLayer;
if (edgesToBeMarked != null)
{
for (var i = 0; i < edgesToBeMarked.length; i++)
{
if (edgesToBeMarked != null) {
for (var i = 0; i < edgesToBeMarked.length; i++) {
var internalEdge = edgesToBeMarked[i];
// Assign unique stamp ( y/m/d/h )
@ -405,8 +370,7 @@ initialRank = ()=>
var otherNode = internalEdge.target;
// Only add node if it hasn't been assigned a layer
if (otherNode.temp[0] == -1)
{
if (otherNode.temp[0] == -1) {
startNodes.push(otherNode);
// Mark this other node as neither being
@ -419,16 +383,13 @@ initialRank = ()=>
}
startNodes.shift();
}
else
{
} else {
// Not all the edges have been scanned, get to the back of
// the class and put the dunces cap on
var removedCell = startNodes.shift();
startNodes.push(internalNode);
if (removedCell == internalNode && startNodes.length == 1)
{
if (removedCell == internalNode && startNodes.length == 1) {
// This is an error condition, we can't get out of
// this loop. It could happen for more than one node
// but that's a lot harder to detect. Log the error
@ -483,19 +444,15 @@ initialRank = ()=>
* chainCount - the number of edges in the chain of vertices going through
* the current swimlane
*/
maxChainDfs = (parent, root, connectingEdge, seen, chainCount)=>
{
if (root != null)
{
maxChainDfs = (parent, root, connectingEdge, seen, chainCount) => {
if (root != null) {
var rootId = mxCellPath.create(root.cell);
if (seen[rootId] == null)
{
if (seen[rootId] == null) {
seen[rootId] = root;
var slIndex = root.swimlaneIndex;
if (this.ranksPerGroup[slIndex] == null || this.ranksPerGroup[slIndex] < chainCount)
{
if (this.ranksPerGroup[slIndex] == null || this.ranksPerGroup[slIndex] < chainCount) {
this.ranksPerGroup[slIndex] = chainCount;
}
@ -503,19 +460,15 @@ maxChainDfs = (parent, root, connectingEdge, seen, chainCount)=>
// can change the original for edge direction inversions
var outgoingEdges = root.connectsAsSource.slice();
for (var i = 0; i < outgoingEdges.length; i++)
{
for (var i = 0; i < outgoingEdges.length; i++) {
var internalEdge = outgoingEdges[i];
var targetNode = internalEdge.target;
// Only navigate in source->target direction within the same
// swimlane, or from a lower index swimlane to a higher one
if (root.swimlaneIndex < targetNode.swimlaneIndex)
{
if (root.swimlaneIndex < targetNode.swimlaneIndex) {
this.maxChainDfs(root, targetNode, internalEdge, mxUtils.clone(seen, null, true), 0);
}
else if (root.swimlaneIndex == targetNode.swimlaneIndex)
{
} else if (root.swimlaneIndex == targetNode.swimlaneIndex) {
this.maxChainDfs(root, targetNode, internalEdge, mxUtils.clone(seen, null, true), chainCount + 1);
}
}
@ -529,13 +482,11 @@ maxChainDfs = (parent, root, connectingEdge, seen, chainCount)=>
* Fixes the layer assignments to the values stored in the nodes. Also needs
* to create dummy nodes for edges that cross layers.
*/
fixRanks = ()=>
{
fixRanks = () => {
var rankList = [];
this.ranks = [];
for (var i = 0; i < this.maxRank + 1; i++)
{
for (var i = 0; i < this.maxRank + 1; i++) {
rankList[i] = [];
this.ranks[i] = rankList[i];
}
@ -545,23 +496,19 @@ fixRanks = ()=>
// crossings for a standard tree.
var rootsArray = null;
if (this.roots != null)
{
if (this.roots != null) {
var oldRootsArray = this.roots;
rootsArray = [];
for (var i = 0; i < oldRootsArray.length; i++)
{
for (var i = 0; i < oldRootsArray.length; i++) {
var cell = oldRootsArray[i];
var internalNode = this.vertexMapper.get(cell);
rootsArray[i] = internalNode;
}
}
this.visit((parent, node, edge, layer, seen)=>
{
if (seen == 0 && node.maxRank < 0 && node.minRank < 0)
{
this.visit((parent, node, edge, layer, seen) => {
if (seen == 0 && node.maxRank < 0 && node.minRank < 0) {
rankList[node.temp[0]].push(node);
node.maxRank = node.temp[0];
node.minRank = node.temp[0];
@ -570,12 +517,10 @@ fixRanks = ()=>
node.temp[0] = rankList[node.maxRank].length - 1;
}
if (parent != null && edge != null)
{
if (parent != null && edge != null) {
var parentToCellRankDifference = parent.maxRank - node.maxRank;
if (parentToCellRankDifference > 1)
{
if (parentToCellRankDifference > 1) {
// There are ranks in between the parent and current cell
edge.maxRank = parent.maxRank;
edge.minRank = node.maxRank;
@ -583,8 +528,7 @@ fixRanks = ()=>
edge.x = [];
edge.y = [];
for (var i = edge.minRank + 1; i < edge.maxRank; i++)
{
for (var i = edge.minRank + 1; i < edge.maxRank; i++) {
// The connecting edge must be added to the
// appropriate ranks
rankList[i].push(edge);
@ -607,33 +551,25 @@ fixRanks = ()=>
* trackAncestors - Whether or not the search is to keep track all nodes
* directly above this one in the search path.
*/
visit = (visitor, dfsRoots, trackAncestors, seenNodes)=>
{
visit = (visitor, dfsRoots, trackAncestors, seenNodes) => {
// Run dfs through on all roots
if (dfsRoots != null)
{
for (var i = 0; i < dfsRoots.length; i++)
{
if (dfsRoots != null) {
for (var i = 0; i < dfsRoots.length; i++) {
var internalNode = dfsRoots[i];
if (internalNode != null)
{
if (seenNodes == null)
{
if (internalNode != null) {
if (seenNodes == null) {
seenNodes = new Object();
}
if (trackAncestors)
{
if (trackAncestors) {
// Set up hash code for root
internalNode.hashCode = [];
internalNode.hashCode[0] = this.dfsCount;
internalNode.hashCode[1] = i;
this.extendedDfs(null, internalNode, null, visitor, seenNodes,
internalNode.hashCode, i, 0);
}
else
{
} else {
this.dfs(null, internalNode, null, visitor, seenNodes, 0);
}
}
@ -659,14 +595,11 @@ visit = (visitor, dfsRoots, trackAncestors, seenNodes)=>
* ancestor node of the current node
* layer - the layer on the dfs tree ( not the same as the model ranks )
*/
dfs = (parent, root, connectingEdge, visitor, seen, layer)=>
{
if (root != null)
{
dfs = (parent, root, connectingEdge, visitor, seen, layer) => {
if (root != null) {
var rootId = root.id;
if (seen[rootId] == null)
{
if (seen[rootId] == null) {
seen[rootId] = root;
visitor(parent, root, connectingEdge, layer, 0);
@ -674,8 +607,7 @@ dfs = (parent, root, connectingEdge, visitor, seen, layer)=>
// can change the original for edge direction inversions
var outgoingEdges = root.connectsAsSource.slice();
for (var i = 0; i< outgoingEdges.length; i++)
{
for (var i = 0; i < outgoingEdges.length; i++) {
var internalEdge = outgoingEdges[i];
var targetNode = internalEdge.target;
@ -683,9 +615,7 @@ dfs = (parent, root, connectingEdge, visitor, seen, layer)=>
this.dfs(root, targetNode, internalEdge, visitor, seen,
layer + 1);
}
}
else
{
} else {
// Use the int field to indicate this node has been seen
visitor(parent, root, connectingEdge, layer, 1);
}
@ -712,8 +642,7 @@ dfs = (parent, root, connectingEdge, visitor, seen, layer)=>
* childHash - the new hash code for this node
* layer - the layer on the dfs tree ( not the same as the model ranks )
*/
extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash, layer)=>
{
extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash, layer) => {
// Explanation of custom hash set. Previously, the ancestors variable
// was passed through the dfs as a HashSet. The ancestors were copied
// into a new HashSet and when the new child was processed it was also
@ -734,18 +663,15 @@ extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash
// through each run of the dfs algorithm (therefore the dfs is not
// thread safe). The hash code of each node is set if not already set,
// or if the first int does not match that of the current run.
if (root != null)
{
if (parent != null)
{
if (root != null) {
if (parent != null) {
// Form this nodes hash code if necessary, that is, if the
// hashCode variable has not been initialized or if the
// start of the parent hash code does not equal the start of
// this nodes hash code, indicating the code was set on a
// previous run of this dfs.
if (root.hashCode == null ||
root.hashCode[0] != parent.hashCode[0])
{
root.hashCode[0] != parent.hashCode[0]) {
var hashCodeLength = parent.hashCode.length + 1;
root.hashCode = parent.hashCode.slice();
root.hashCode[hashCodeLength - 1] = childHash;
@ -754,8 +680,7 @@ extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash
var rootId = root.id;
if (seen[rootId] == null)
{
if (seen[rootId] == null) {
seen[rootId] = root;
visitor(parent, root, connectingEdge, layer, 0);
@ -764,38 +689,35 @@ extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash
var outgoingEdges = root.connectsAsSource.slice();
var incomingEdges = root.connectsAsTarget.slice();
for (var i = 0; i < outgoingEdges.length; i++)
{
for (var i = 0; i < outgoingEdges.length; i++) {
var internalEdge = outgoingEdges[i];
var targetNode = internalEdge.target;
// Only navigate in source->target direction within the same
// swimlane, or from a lower index swimlane to a higher one
if (root.swimlaneIndex <= targetNode.swimlaneIndex)
{
if (root.swimlaneIndex <= targetNode.swimlaneIndex) {
this.extendedDfs(root, targetNode, internalEdge, visitor, seen,
root.hashCode, i, layer + 1);
}
}
for (var i = 0; i < incomingEdges.length; i++)
{
for (var i = 0; i < incomingEdges.length; i++) {
var internalEdge = incomingEdges[i];
var targetNode = internalEdge.source;
// Only navigate in target->source direction from a lower index
// swimlane to a higher one
if (root.swimlaneIndex < targetNode.swimlaneIndex)
{
if (root.swimlaneIndex < targetNode.swimlaneIndex) {
this.extendedDfs(root, targetNode, internalEdge, visitor, seen,
root.hashCode, i, layer + 1);
}
}
}
else
{
} else {
// Use the int field to indicate this node has been seen
visitor(parent, root, connectingEdge, layer, 1);
}
}
};
}
export default mxSwimlaneModel;

View File

@ -2,36 +2,8 @@
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
* Class: mxSwimlaneLayout
*
* A hierarchical layout algorithm.
*
* Constructor: mxSwimlaneLayout
*
* Constructs a new hierarchical layout algorithm.
*
* Arguments:
*
* graph - Reference to the enclosing <mxGraph>.
* orientation - Optional constant that defines the orientation of this
* layout.
* deterministic - Optional boolean that specifies if this layout should be
* deterministic. Default is true.
*/
function mxSwimlaneLayout(graph, orientation, deterministic)
{
mxGraphLayout.call(this, graph);
this.orientation = (orientation != null) ? orientation : mxConstants.DIRECTION_NORTH;
this.deterministic = (deterministic != null) ? deterministic : true;
};
/**
* Extends mxGraphLayout.
*/
mxSwimlaneLayout.prototype = new mxGraphLayout();
constructor = mxSwimlaneLayout;
class mxSwimlaneLayout extends mxGraphLayout {
/**
* Variable: roots
*
@ -193,13 +165,35 @@ edgesTargetTermCache = null;
*/
edgeStyle = mxHierarchicalEdgeStyle.POLYLINE;
/**
* Class: mxSwimlaneLayout
*
* A hierarchical layout algorithm.
*
* Constructor: mxSwimlaneLayout
*
* Constructs a new hierarchical layout algorithm.
*
* Arguments:
*
* graph - Reference to the enclosing <mxGraph>.
* orientation - Optional constant that defines the orientation of this
* layout.
* deterministic - Optional boolean that specifies if this layout should be
* deterministic. Default is true.
*/
constructor(graph, orientation, deterministic) {
super(graph);
this.orientation = (orientation != null) ? orientation : mxConstants.DIRECTION_NORTH;
this.deterministic = (deterministic != null) ? deterministic : true;
};
/**
* Function: getModel
*
* Returns the internal <mxSwimlaneModel> for this layout algorithm.
*/
getModel = ()=>
{
getModel = () => {
return this.model;
};
@ -213,8 +207,7 @@ getModel = ()=>
* parent - Parent <mxCell> that contains the children to be laid out.
* swimlanes - Ordered array of swimlanes to be laid out
*/
execute = (parent, swimlanes)=>
{
execute = (parent, swimlanes) => {
this.parent = parent;
var model = this.graph.model;
this.edgesCache = new mxDictionary();
@ -228,14 +221,12 @@ execute = (parent, swimlanes)=>
// If just the parent is set use it's immediate
// children as the initial set
if (swimlanes == null || swimlanes.length < 1)
{
if (swimlanes == null || swimlanes.length < 1) {
// TODO indicate the problem
return;
}
if (parent == null)
{
if (parent == null) {
parent = model.getParent(swimlanes[0]);
}
@ -243,12 +234,10 @@ execute = (parent, swimlanes)=>
this.parentX = null;
this.parentY = null;
if (parent != this.root && model.isVertex(parent) != null && this.maintainParentLocation)
{
if (parent != this.root && model.isVertex(parent) != null && this.maintainParentLocation) {
var geo = this.graph.getCellGeometry(parent);
if (geo != null)
{
if (geo != null) {
this.parentX = geo.x;
this.parentY = geo.y;
}
@ -258,34 +247,28 @@ execute = (parent, swimlanes)=>
var dummyVertices = [];
// Check the swimlanes all have vertices
// in them
for (var i = 0; i < swimlanes.length; i++)
{
for (var i = 0; i < swimlanes.length; i++) {
var children = this.graph.getChildCells(swimlanes[i]);
if (children == null || children.length == 0)
{
if (children == null || children.length == 0) {
var vertex = this.graph.insertVertex(swimlanes[i], null, null, 0, 0, this.dummyVertexWidth, 0);
dummyVertices.push(vertex);
}
}
model.beginUpdate();
try
{
try {
this.run(parent);
if (this.resizeParent && !this.graph.isCellCollapsed(parent))
{
if (this.resizeParent && !this.graph.isCellCollapsed(parent)) {
this.graph.updateGroupBounds([parent], this.parentBorder, this.moveParent);
}
// Maintaining parent location
if (this.parentX != null && this.parentY != null)
{
if (this.parentX != null && this.parentY != null) {
var geo = this.graph.getCellGeometry(parent);
if (geo != null)
{
if (geo != null) {
geo = geo.clone();
geo.x = this.parentX;
geo.y = this.parentY;
@ -294,9 +277,7 @@ execute = (parent, swimlanes)=>
}
this.graph.removeCells(dummyVertices);
}
finally
{
} finally {
model.endUpdate();
}
};
@ -308,18 +289,15 @@ execute = (parent, swimlanes)=>
* all child vertices.
*
*/
updateGroupBounds = ()=>
{
updateGroupBounds = () => {
// Get all vertices and edge in the layout
var cells = [];
var model = this.model;
for (var key in model.edgeMapper)
{
for (var key in model.edgeMapper) {
var edge = model.edgeMapper[key];
for (var i = 0; i < edge.edges.length; i++)
{
for (var i = 0; i < edge.edges.length; i++) {
cells.push(edge.edges[i]);
}
}
@ -327,13 +305,11 @@ updateGroupBounds = ()=>
var layoutBounds = this.graph.getBoundingBoxFromGeometry(cells, true);
var childBounds = [];
for (var i = 0; i < this.swimlanes.length; i++)
{
for (var i = 0; i < this.swimlanes.length; i++) {
var lane = this.swimlanes[i];
var geo = this.graph.getCellGeometry(lane);
if (geo != null)
{
if (geo != null) {
var children = this.graph.getChildCells(lane);
var size = (this.graph.isSwimlane(lane)) ?
@ -344,12 +320,9 @@ updateGroupBounds = ()=>
var childrenY = bounds.y + geo.y - size.height - this.parentBorder;
var maxChildrenY = bounds.y + geo.y + bounds.height;
if (layoutBounds == null)
{
if (layoutBounds == null) {
layoutBounds = new mxRectangle(0, childrenY, 0, maxChildrenY - childrenY);
}
else
{
} else {
layoutBounds.y = Math.min(layoutBounds.y, childrenY);
var maxY = Math.max(layoutBounds.y + layoutBounds.height, maxChildrenY);
layoutBounds.height = maxY - layoutBounds.y;
@ -358,13 +331,11 @@ updateGroupBounds = ()=>
}
for (var i = 0; i < this.swimlanes.length; i++)
{
for (var i = 0; i < this.swimlanes.length; i++) {
var lane = this.swimlanes[i];
var geo = this.graph.getCellGeometry(lane);
if (geo != null)
{
if (geo != null) {
var children = this.graph.getChildCells(lane);
var size = (this.graph.isSwimlane(lane)) ?
@ -403,63 +374,51 @@ updateGroupBounds = ()=>
* parent - <mxCell> whose children should be checked.
* vertices - array of vertices to limit search to
*/
findRoots = (parent, vertices)=>
{
findRoots = (parent, vertices) => {
var roots = [];
if (parent != null && vertices != null)
{
if (parent != null && vertices != null) {
var model = this.graph.model;
var best = null;
var maxDiff = -100000;
for (var i in vertices)
{
for (var i in vertices) {
var cell = vertices[i];
if (cell != null && model.isVertex(cell) && this.graph.isCellVisible(cell) && model.isAncestor(parent, cell))
{
if (cell != null && model.isVertex(cell) && this.graph.isCellVisible(cell) && model.isAncestor(parent, cell)) {
var conns = this.getEdges(cell);
var fanOut = 0;
var fanIn = 0;
for (var k = 0; k < conns.length; k++)
{
for (var k = 0; k < conns.length; k++) {
var src = this.getVisibleTerminal(conns[k], true);
if (src == cell)
{
if (src == cell) {
// Only count connection within this swimlane
var other = this.getVisibleTerminal(conns[k], false);
if (model.isAncestor(parent, other))
{
if (model.isAncestor(parent, other)) {
fanOut++;
}
}
else if (model.isAncestor(parent, src))
{
} else if (model.isAncestor(parent, src)) {
fanIn++;
}
}
if (fanIn == 0 && fanOut > 0)
{
if (fanIn == 0 && fanOut > 0) {
roots.push(cell);
}
var diff = fanOut - fanIn;
if (diff > maxDiff)
{
if (diff > maxDiff) {
maxDiff = diff;
best = cell;
}
}
}
if (roots.length == 0 && best != null)
{
if (roots.length == 0 && best != null) {
roots.push(best);
}
}
@ -476,12 +435,10 @@ findRoots = (parent, vertices)=>
*
* cell - <mxCell> whose edges should be returned.
*/
getEdges = (cell)=>
{
getEdges = (cell) => {
var cachedEdges = this.edgesCache.get(cell);
if (cachedEdges != null)
{
if (cachedEdges != null) {
return cachedEdges;
}
@ -490,16 +447,12 @@ getEdges = (cell)=>
var isCollapsed = this.graph.isCellCollapsed(cell);
var childCount = model.getChildCount(cell);
for (var i = 0; i < childCount; i++)
{
for (var i = 0; i < childCount; i++) {
var child = model.getChildAt(cell, i);
if (this.isPort(child))
{
if (this.isPort(child)) {
edges = edges.concat(model.getEdges(child, true, true));
}
else if (isCollapsed || !this.graph.isCellVisible(child))
{
} else if (isCollapsed || !this.graph.isCellVisible(child)) {
edges = edges.concat(model.getEdges(child, true, true));
}
}
@ -507,15 +460,13 @@ getEdges = (cell)=>
edges = edges.concat(model.getEdges(cell, true, true));
var result = [];
for (var i = 0; i < edges.length; i++)
{
for (var i = 0; i < edges.length; i++) {
var source = this.getVisibleTerminal(edges[i], true);
var target = this.getVisibleTerminal(edges[i], false);
if ((source == target) || ((source != target) && ((target == cell && (this.parent == null || this.graph.isValidAncestor(source, this.parent, this.traverseAncestors))) ||
(source == cell && (this.parent == null ||
this.graph.isValidAncestor(target, this.parent, this.traverseAncestors))))))
{
this.graph.isValidAncestor(target, this.parent, this.traverseAncestors)))))) {
result.push(edges[i]);
}
}
@ -535,19 +486,16 @@ getEdges = (cell)=>
* edge - <mxCell> whose edges should be returned.
* source - Boolean that specifies whether the source or target terminal is to be returned
*/
getVisibleTerminal = (edge, source)=>
{
getVisibleTerminal = (edge, source) => {
var terminalCache = this.edgesTargetTermCache;
if (source)
{
if (source) {
terminalCache = this.edgeSourceTermCache;
}
var term = terminalCache.get(edge);
if (term != null)
{
if (term != null) {
return term;
}
@ -555,15 +503,12 @@ getVisibleTerminal = (edge, source)=>
var terminal = (state != null) ? state.getVisibleTerminal(source) : this.graph.view.getVisibleTerminal(edge, source);
if (terminal == null)
{
if (terminal == null) {
terminal = (state != null) ? state.getVisibleTerminal(source) : this.graph.view.getVisibleTerminal(edge, source);
}
if (terminal != null)
{
if (this.isPort(terminal))
{
if (terminal != null) {
if (this.isPort(terminal)) {
terminal = this.graph.model.getParent(terminal);
}
@ -581,18 +526,15 @@ getVisibleTerminal = (edge, source)=>
* routing changes made. It runs each stage of the layout that has been
* created.
*/
run = (parent)=>
{
run = (parent) => {
// Separate out unconnected hierarchies
var hierarchyVertices = [];
var allVertexSet = Object();
if (this.swimlanes != null && this.swimlanes.length > 0 && parent != null)
{
if (this.swimlanes != null && this.swimlanes.length > 0 && parent != null) {
var filledVertexSet = Object();
for (var i = 0; i < this.swimlanes.length; i++)
{
for (var i = 0; i < this.swimlanes.length; i++) {
this.filterDescendants(this.swimlanes[i], filledVertexSet);
}
@ -600,10 +542,8 @@ run = (parent)=>
var filledVertexSetEmpty = true;
// Poor man's isSetEmpty
for (var key in filledVertexSet)
{
if (filledVertexSet[key] != null)
{
for (var key in filledVertexSet) {
if (filledVertexSet[key] != null) {
filledVertexSetEmpty = false;
break;
}
@ -612,12 +552,10 @@ run = (parent)=>
// Only test for candidates in each swimlane in order
var laneCounter = 0;
while (!filledVertexSetEmpty && laneCounter < this.swimlanes.length)
{
while (!filledVertexSetEmpty && laneCounter < this.swimlanes.length) {
var candidateRoots = this.findRoots(this.swimlanes[laneCounter], filledVertexSet);
if (candidateRoots.length == 0)
{
if (candidateRoots.length == 0) {
laneCounter++;
continue;
}
@ -625,8 +563,7 @@ run = (parent)=>
// If the candidate root is an unconnected group cell, remove it from
// the layout. We may need a custom set that holds such groups and forces
// them to be processed for resizing and/or moving.
for (var i = 0; i < candidateRoots.length; i++)
{
for (var i = 0; i < candidateRoots.length; i++) {
var vertexSet = Object();
hierarchyVertices.push(vertexSet);
@ -634,30 +571,24 @@ run = (parent)=>
hierarchyVertices, filledVertexSet, laneCounter);
}
for (var i = 0; i < candidateRoots.length; i++)
{
for (var i = 0; i < candidateRoots.length; i++) {
this.roots.push(candidateRoots[i]);
}
filledVertexSetEmpty = true;
// Poor man's isSetEmpty
for (var key in filledVertexSet)
{
if (filledVertexSet[key] != null)
{
for (var key in filledVertexSet) {
if (filledVertexSet[key] != null) {
filledVertexSetEmpty = false;
break;
}
}
}
}
else
{
} else {
// Find vertex set as directed traversal from roots
for (var i = 0; i < this.roots.length; i++)
{
for (var i = 0; i < this.roots.length; i++) {
var vertexSet = Object();
hierarchyVertices.push(vertexSet);
@ -668,8 +599,7 @@ run = (parent)=>
var tmp = [];
for (var key in allVertexSet)
{
for (var key in allVertexSet) {
tmp.push(allVertexSet[key]);
}
@ -688,28 +618,23 @@ run = (parent)=>
*
* Creates an array of descendant cells
*/
filterDescendants = (cell, result)=>
{
filterDescendants = (cell, result) => {
var model = this.graph.model;
if (model.isVertex(cell) && cell != this.parent && model.getParent(cell) != this.parent && this.graph.isCellVisible(cell))
{
if (model.isVertex(cell) && cell != this.parent && model.getParent(cell) != this.parent && this.graph.isCellVisible(cell)) {
result[mxObjectIdentity.get(cell)] = cell;
}
if (this.traverseAncestors || cell == this.parent
&& this.graph.isCellVisible(cell))
{
&& this.graph.isCellVisible(cell)) {
var childCount = model.getChildCount(cell);
for (var i = 0; i < childCount; i++)
{
for (var i = 0; i < childCount; i++) {
var child = model.getChildAt(cell, i);
// Ignore ports in the layout vertex list, they are dealt with
// in the traversal mechanisms
if (!this.isPort(child))
{
if (!this.isPort(child)) {
this.filterDescendants(child, result);
}
}
@ -726,10 +651,8 @@ filterDescendants = (cell, result)=>
*
* cell - <mxCell> that represents the port.
*/
isPort = (cell)=>
{
if (cell.geometry.relative)
{
isPort = (cell) => {
if (cell.geometry.relative) {
return true;
}
@ -748,21 +671,18 @@ isPort = (cell)=>
* target -
* directed -
*/
getEdgesBetween = (source, target, directed)=>
{
getEdgesBetween = (source, target, directed) => {
directed = (directed != null) ? directed : false;
var edges = this.getEdges(source);
var result = [];
// Checks if the edge is connected to the correct
// cell and returns the first match
for (var i = 0; i < edges.length; i++)
{
for (var i = 0; i < edges.length; i++) {
var src = this.getVisibleTerminal(edges[i], true);
var trg = this.getVisibleTerminal(edges[i], false);
if ((src == source && trg == target) || (!directed && src == target && trg == source))
{
if ((src == source && trg == target) || (!directed && src == target && trg == source)) {
result.push(edges[i]);
}
}
@ -788,57 +708,46 @@ getEdgesBetween = (source, target, directed)=>
* swimlaneIndex - the laid out order index of the swimlane vertex is contained in
*/
traverse = function (vertex, directed, edge, allVertices, currentComp,
hierarchyVertices, filledVertexSet, swimlaneIndex)
{
if (vertex != null && allVertices != null)
{
hierarchyVertices, filledVertexSet, swimlaneIndex) {
if (vertex != null && allVertices != null) {
// Has this vertex been seen before in any traversal
// And if the filled vertex set is populated, only
// process vertices in that it contains
var vertexID = mxObjectIdentity.get(vertex);
if ((allVertices[vertexID] == null)
&& (filledVertexSet == null ? true : filledVertexSet[vertexID] != null))
{
if (currentComp[vertexID] == null)
{
&& (filledVertexSet == null ? true : filledVertexSet[vertexID] != null)) {
if (currentComp[vertexID] == null) {
currentComp[vertexID] = vertex;
}
if (allVertices[vertexID] == null)
{
if (allVertices[vertexID] == null) {
allVertices[vertexID] = vertex;
}
if (filledVertexSet !== null)
{
if (filledVertexSet !== null) {
delete filledVertexSet[vertexID];
}
var edges = this.getEdges(vertex);
var model = this.graph.model;
for (var i = 0; i < edges.length; i++)
{
for (var i = 0; i < edges.length; i++) {
var otherVertex = this.getVisibleTerminal(edges[i], true);
var isSource = otherVertex == vertex;
if (isSource)
{
if (isSource) {
otherVertex = this.getVisibleTerminal(edges[i], false);
}
var otherIndex = 0;
// Get the swimlane index of the other terminal
for (otherIndex = 0; otherIndex < this.swimlanes.length; otherIndex++)
{
if (model.isAncestor(this.swimlanes[otherIndex], otherVertex))
{
for (otherIndex = 0; otherIndex < this.swimlanes.length; otherIndex++) {
if (model.isAncestor(this.swimlanes[otherIndex], otherVertex)) {
break;
}
}
if (otherIndex >= this.swimlanes.length)
{
if (otherIndex >= this.swimlanes.length) {
continue;
}
@ -846,28 +755,21 @@ traverse = function(vertex, directed, edge, allVertices, currentComp,
// as the current vertex, or if the swimlane index of the other
// vertex is greater than that of this vertex
if ((otherIndex > swimlaneIndex) ||
((!directed || isSource) && otherIndex == swimlaneIndex))
{
((!directed || isSource) && otherIndex == swimlaneIndex)) {
currentComp = this.traverse(otherVertex, directed, edges[i], allVertices,
currentComp, hierarchyVertices,
filledVertexSet, otherIndex);
}
}
}
else
{
if (currentComp[vertexID] == null)
{
} 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++)
{
for (var i = 0; i < hierarchyVertices.length; i++) {
var comp = hierarchyVertices[i];
if (comp[vertexID] != null)
{
for (var key in comp)
{
if (comp[vertexID] != null) {
for (var key in comp) {
currentComp[key] = comp[key];
}
@ -888,8 +790,7 @@ traverse = function(vertex, directed, edge, allVertices, currentComp,
*
* Executes the cycle stage using mxMinimumCycleRemover.
*/
cycleStage = (parent)=>
{
cycleStage = (parent) => {
var cycleStage = new mxSwimlaneOrdering(this);
cycleStage.execute(parent);
};
@ -899,8 +800,7 @@ cycleStage = (parent)=>
*
* Implements first stage of a Sugiyama layout.
*/
layeringStage = ()=>
{
layeringStage = () => {
this.model.initialRank();
this.model.fixRanks();
};
@ -910,8 +810,7 @@ layeringStage = ()=>
*
* Executes the crossing stage using mxMedianHybridCrossingReduction.
*/
crossingStage = (parent)=>
{
crossingStage = (parent) => {
var crossingStage = new mxMedianHybridCrossingReduction(this);
crossingStage.execute(parent);
};
@ -921,8 +820,7 @@ crossingStage = (parent)=>
*
* Executes the placement stage using mxCoordinateAssignment.
*/
placementStage = (initialX, parent)=>
{
placementStage = (initialX, parent) => {
var placementStage = new mxCoordinateAssignment(this, this.intraCellSpacing,
this.interRankCellSpacing, this.orientation, initialX,
this.parallelEdgeSpacing);
@ -931,3 +829,6 @@ placementStage = (initialX, parent)=>
return placementStage.limitX + this.interHierarchySpacing;
};
}
export default mxSwimlaneLayout;

View File

@ -2,6 +2,8 @@
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
class mxHierarchicalLayoutStage {
/**
* Class: mxHierarchicalLayoutStage
*
@ -13,7 +15,8 @@
*
* Constructs a new hierarchical layout stage.
*/
function mxHierarchicalLayoutStage() { };
constructor() {
}
/**
* Function: execute
@ -23,3 +26,6 @@ function mxHierarchicalLayoutStage() { };
* use.
*/
execute = (parent)=> { };
}
export default mxHierarchicalLayoutStage;

View File

@ -2,6 +2,8 @@
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
class mxMedianHybridCrossingReduction extends mxHierarchicalLayoutStage {
/**
* Class: mxMedianHybridCrossingReduction
*
@ -20,17 +22,11 @@
* orientation - the position of the root node(s) relative to the graph
* initialX - the leftmost coordinate node placement starts at
*/
function mxMedianHybridCrossingReduction(layout)
{
constructor(layout) {
// super not called
this.layout = layout;
};
/**
* Extends mxMedianHybridCrossingReduction.
*/
mxMedianHybridCrossingReduction.prototype = new mxHierarchicalLayoutStage();
constructor = mxMedianHybridCrossingReduction;
/**
* Variable: layout
*
@ -81,15 +77,13 @@ maxNoImprovementIterations = 2;
* Performs a vertex ordering within ranks as described by Gansner et al
* 1993
*/
execute = (parent)=>
{
execute = (parent) => {
var model = this.layout.getModel();
// Stores initial ordering as being the best one found so far
this.nestedBestRanks = [];
for (var i = 0; i < model.ranks.length; i++)
{
for (var i = 0; i < model.ranks.length; i++) {
this.nestedBestRanks[i] = model.ranks[i].slice();
}
@ -97,50 +91,41 @@ execute = (parent)=>
var currentBestCrossings = this.calculateCrossings(model);
for (var i = 0; i < this.maxIterations &&
iterationsWithoutImprovement < this.maxNoImprovementIterations; i++)
{
iterationsWithoutImprovement < this.maxNoImprovementIterations; i++) {
this.weightedMedian(i, model);
this.transpose(i, model);
var candidateCrossings = this.calculateCrossings(model);
if (candidateCrossings < currentBestCrossings)
{
if (candidateCrossings < currentBestCrossings) {
currentBestCrossings = candidateCrossings;
iterationsWithoutImprovement = 0;
// Store the current rankings as the best ones
for (var j = 0; j < this.nestedBestRanks.length; j++)
{
for (var j = 0; j < this.nestedBestRanks.length; j++) {
var rank = model.ranks[j];
for (var k = 0; k < rank.length; k++)
{
for (var k = 0; k < rank.length; k++) {
var cell = rank[k];
this.nestedBestRanks[j][cell.getGeneralPurposeVariable(j)] = cell;
}
}
}
else
{
} else {
// Increase count of iterations where we haven't improved the
// layout
iterationsWithoutImprovement++;
// Restore the best values to the cells
for (var j = 0; j < this.nestedBestRanks.length; j++)
{
for (var j = 0; j < this.nestedBestRanks.length; j++) {
var rank = model.ranks[j];
for (var k = 0; k < rank.length; k++)
{
for (var k = 0; k < rank.length; k++) {
var cell = rank[k];
cell.setGeneralPurposeVariable(j, k);
}
}
}
if (currentBestCrossings == 0)
{
if (currentBestCrossings == 0) {
// Do nothing further
break;
}
@ -150,16 +135,13 @@ execute = (parent)=>
var ranks = [];
var rankList = [];
for (var i = 0; i < model.maxRank + 1; i++)
{
for (var i = 0; i < model.maxRank + 1; i++) {
rankList[i] = [];
ranks[i] = rankList[i];
}
for (var i = 0; i < this.nestedBestRanks.length; i++)
{
for (var j = 0; j < this.nestedBestRanks[i].length; j++)
{
for (var i = 0; i < this.nestedBestRanks.length; i++) {
for (var j = 0; j < this.nestedBestRanks[i].length; j++) {
rankList[i].push(this.nestedBestRanks[i][j]);
}
}
@ -179,13 +161,11 @@ execute = (parent)=>
*
* model - the internal model describing the hierarchy
*/
calculateCrossings = (model)=>
{
calculateCrossings = (model) => {
var numRanks = model.ranks.length;
var totalCrossings = 0;
for (var i = 1; i < numRanks; i++)
{
for (var i = 1; i < numRanks; i++) {
totalCrossings += this.calculateRankCrossing(i, model);
}
@ -204,8 +184,7 @@ calculateCrossings = (model)=>
* i - the topmost rank of the pair ( higher rank value )
* model - the internal model describing the hierarchy
*/
calculateRankCrossing = (i, model)=>
{
calculateRankCrossing = (i, model) => {
var totalCrossings = 0;
var rank = model.ranks[i];
var previousRank = model.ranks[i - 1];
@ -213,35 +192,33 @@ calculateRankCrossing = (i, model)=>
var tmpIndices = [];
// Iterate over the top rank and fill in the connection information
for (var j = 0; j < rank.length; j++)
{
for (var j = 0; j < rank.length; j++) {
var node = rank[j];
var rankPosition = node.getGeneralPurposeVariable(i);
var connectedCells = node.getPreviousLayerConnectedCells(i);
var nodeIndices = [];
for (var k = 0; k < connectedCells.length; k++)
{
for (var k = 0; k < connectedCells.length; k++) {
var connectedNode = connectedCells[k];
var otherCellRankPosition = connectedNode.getGeneralPurposeVariable(i - 1);
nodeIndices.push(otherCellRankPosition);
}
nodeIndices.sort((x, y)=> { return x - y; });
nodeIndices.sort((x, y) => {
return x - y;
});
tmpIndices[rankPosition] = nodeIndices;
}
var indices = [];
for (var j = 0; j < tmpIndices.length; j++)
{
for (var j = 0; j < tmpIndices.length; j++) {
indices = indices.concat(tmpIndices[j]);
}
var firstIndex = 1;
while (firstIndex < previousRank.length)
{
while (firstIndex < previousRank.length) {
firstIndex <<= 1;
}
@ -250,21 +227,17 @@ calculateRankCrossing = (i, model)=>
var tree = [];
for (var j = 0; j < treeSize; ++j)
{
for (var j = 0; j < treeSize; ++j) {
tree[j] = 0;
}
for (var j = 0; j < indices.length; j++)
{
for (var j = 0; j < indices.length; j++) {
var index = indices[j];
var treeIndex = index + firstIndex;
++tree[treeIndex];
while (treeIndex > 0)
{
if (treeIndex % 2)
{
while (treeIndex > 0) {
if (treeIndex % 2) {
totalCrossings += tree[treeIndex + 1];
}
@ -287,34 +260,29 @@ calculateRankCrossing = (i, model)=>
* mainLoopIteration - the iteration number of the main loop
* model - the internal model describing the hierarchy
*/
transpose = (mainLoopIteration, model)=>
{
transpose = (mainLoopIteration, model) => {
var improved = true;
// Track the number of iterations in case of looping
var count = 0;
var maxCount = 10;
while (improved && count++ < maxCount)
{
while (improved && count++ < maxCount) {
// On certain iterations allow allow swapping of cell pairs with
// equal edge crossings switched or not switched. This help to
// nudge a stuck layout into a lower crossing total.
var nudge = mainLoopIteration % 2 == 1 && count % 2 == 1;
improved = false;
for (var i = 0; i < model.ranks.length; i++)
{
for (var i = 0; i < model.ranks.length; i++) {
var rank = model.ranks[i];
var orderedCells = [];
for (var j = 0; j < rank.length; j++)
{
for (var j = 0; j < rank.length; j++) {
var cell = rank[j];
var tempRank = cell.getGeneralPurposeVariable(i);
// FIXME: Workaround to avoid negative tempRanks
if (tempRank < 0)
{
if (tempRank < 0) {
tempRank = j;
}
orderedCells[tempRank] = cell;
@ -333,16 +301,14 @@ transpose = (mainLoopIteration, model)=>
var leftCell = null;
var rightCell = null;
for (var j = 0; j < (rank.length - 1); j++)
{
for (var j = 0; j < (rank.length - 1); j++) {
// For each intra-rank adjacent pair of cells
// see if swapping them around would reduce the
// number of edges crossing they cause in total
// On every cell pair except the first on each rank, we
// can save processing using the previous values for the
// right cell on the new left cell
if (j == 0)
{
if (j == 0) {
leftCell = orderedCells[j];
leftCellAboveConnections = leftCell
.getNextLayerConnectedCells(i);
@ -351,18 +317,14 @@ transpose = (mainLoopIteration, model)=>
leftAbovePositions = [];
leftBelowPositions = [];
for (var k = 0; k < leftCellAboveConnections.length; k++)
{
for (var k = 0; k < leftCellAboveConnections.length; k++) {
leftAbovePositions[k] = leftCellAboveConnections[k].getGeneralPurposeVariable(i + 1);
}
for (var k = 0; k < leftCellBelowConnections.length; k++)
{
for (var k = 0; k < leftCellBelowConnections.length; k++) {
leftBelowPositions[k] = leftCellBelowConnections[k].getGeneralPurposeVariable(i - 1);
}
}
else
{
} else {
leftCellAboveConnections = rightCellAboveConnections;
leftCellBelowConnections = rightCellBelowConnections;
leftAbovePositions = rightAbovePositions;
@ -379,46 +341,36 @@ transpose = (mainLoopIteration, model)=>
rightAbovePositions = [];
rightBelowPositions = [];
for (var k = 0; k < rightCellAboveConnections.length; k++)
{
for (var k = 0; k < rightCellAboveConnections.length; k++) {
rightAbovePositions[k] = rightCellAboveConnections[k].getGeneralPurposeVariable(i + 1);
}
for (var k = 0; k < rightCellBelowConnections.length; k++)
{
for (var k = 0; k < rightCellBelowConnections.length; k++) {
rightBelowPositions[k] = rightCellBelowConnections[k].getGeneralPurposeVariable(i - 1);
}
var totalCurrentCrossings = 0;
var totalSwitchedCrossings = 0;
for (var k = 0; k < leftAbovePositions.length; k++)
{
for (var ik = 0; ik < rightAbovePositions.length; ik++)
{
if (leftAbovePositions[k] > rightAbovePositions[ik])
{
for (var k = 0; k < leftAbovePositions.length; k++) {
for (var ik = 0; ik < rightAbovePositions.length; ik++) {
if (leftAbovePositions[k] > rightAbovePositions[ik]) {
totalCurrentCrossings++;
}
if (leftAbovePositions[k] < rightAbovePositions[ik])
{
if (leftAbovePositions[k] < rightAbovePositions[ik]) {
totalSwitchedCrossings++;
}
}
}
for (var k = 0; k < leftBelowPositions.length; k++)
{
for (var ik = 0; ik < rightBelowPositions.length; ik++)
{
if (leftBelowPositions[k] > rightBelowPositions[ik])
{
for (var k = 0; k < leftBelowPositions.length; k++) {
for (var ik = 0; ik < rightBelowPositions.length; ik++) {
if (leftBelowPositions[k] > rightBelowPositions[ik]) {
totalCurrentCrossings++;
}
if (leftBelowPositions[k] < rightBelowPositions[ik])
{
if (leftBelowPositions[k] < rightBelowPositions[ik]) {
totalSwitchedCrossings++;
}
}
@ -426,8 +378,7 @@ transpose = (mainLoopIteration, model)=>
if ((totalSwitchedCrossings < totalCurrentCrossings) ||
(totalSwitchedCrossings == totalCurrentCrossings &&
nudge))
{
nudge)) {
var temp = leftCell.getGeneralPurposeVariable(i);
leftCell.setGeneralPurposeVariable(i, rightCell
.getGeneralPurposeVariable(i));
@ -443,8 +394,7 @@ transpose = (mainLoopIteration, model)=>
rightBelowPositions = leftBelowPositions;
rightCell = leftCell;
if (!nudge)
{
if (!nudge) {
// Don't count nudges as improvement or we'll end
// up stuck in two combinations and not finishing
// as early as we should
@ -467,21 +417,15 @@ transpose = (mainLoopIteration, model)=>
* iteration - the iteration number of the main loop
* model - the internal model describing the hierarchy
*/
weightedMedian = (iteration, model)=>
{
weightedMedian = (iteration, model) => {
// Reverse sweep direction each time through this method
var downwardSweep = (iteration % 2 == 0);
if (downwardSweep)
{
for (var j = model.maxRank - 1; j >= 0; j--)
{
if (downwardSweep) {
for (var j = model.maxRank - 1; j >= 0; j--) {
this.medianRank(j, downwardSweep);
}
}
else
{
for (var j = 1; j < model.maxRank; j++)
{
} else {
for (var j = 1; j < model.maxRank; j++) {
this.medianRank(j, downwardSweep);
}
}
@ -498,14 +442,12 @@ weightedMedian = (iteration, model)=>
* rankValue - the layer number of this rank
* downwardSweep - whether or not this is a downward sweep through the graph
*/
medianRank = (rankValue, downwardSweep)=>
{
medianRank = (rankValue, downwardSweep) => {
var numCellsForRank = this.nestedBestRanks[rankValue].length;
var medianValues = [];
var reservedPositions = [];
for (var i = 0; i < numCellsForRank; i++)
{
for (var i = 0; i < numCellsForRank; i++) {
var cell = this.nestedBestRanks[rankValue][i];
var sorterEntry = new MedianCellSorter();
sorterEntry.cell = cell;
@ -516,37 +458,28 @@ medianRank = (rankValue, downwardSweep)=>
// medianValues[i].nudge = !downwardSweep;
var nextLevelConnectedCells;
if (downwardSweep)
{
if (downwardSweep) {
nextLevelConnectedCells = cell
.getNextLayerConnectedCells(rankValue);
}
else
{
} else {
nextLevelConnectedCells = cell
.getPreviousLayerConnectedCells(rankValue);
}
var nextRankValue;
if (downwardSweep)
{
if (downwardSweep) {
nextRankValue = rankValue + 1;
}
else
{
} else {
nextRankValue = rankValue - 1;
}
if (nextLevelConnectedCells != null
&& nextLevelConnectedCells.length != 0)
{
&& nextLevelConnectedCells.length != 0) {
sorterEntry.medianValue = this.medianValue(
nextLevelConnectedCells, nextRankValue);
medianValues.push(sorterEntry);
}
else
{
} else {
// Nodes with no adjacent vertices are flagged in the reserved array
// to indicate they should be left in their current position.
reservedPositions[cell.getGeneralPurposeVariable(rankValue)] = true;
@ -557,10 +490,8 @@ medianRank = (rankValue, downwardSweep)=>
// Set the new position of each node within the rank using
// its temp variable
for (var i = 0; i < numCellsForRank; i++)
{
if (reservedPositions[i] == null)
{
for (var i = 0; i < numCellsForRank; i++) {
if (reservedPositions[i] == null) {
var cell = medianValues.shift().cell;
cell.setGeneralPurposeVariable(rankValue, i);
}
@ -580,32 +511,27 @@ medianRank = (rankValue, downwardSweep)=>
* specified cell
* rankValue - the rank that the connected cell lie upon
*/
medianValue = (connectedCells, rankValue)=>
{
medianValue = (connectedCells, rankValue) => {
var medianValues = [];
var arrayCount = 0;
for (var i = 0; i < connectedCells.length; i++)
{
for (var i = 0; i < connectedCells.length; i++) {
var cell = connectedCells[i];
medianValues[arrayCount++] = cell.getGeneralPurposeVariable(rankValue);
}
// Sort() sorts lexicographically by default (i.e. 11 before 9) so force
// numerical order sort
medianValues.sort((a,b)=>{return a - b;});
medianValues.sort((a, b) => {
return a - b;
});
if (arrayCount % 2 == 1)
{
if (arrayCount % 2 == 1) {
// For odd numbers of adjacent vertices return the median
return medianValues[Math.floor(arrayCount / 2)];
}
else if (arrayCount == 2)
{
} else if (arrayCount == 2) {
return ((medianValues[0] + medianValues[1]) / 2.0);
}
else
{
} else {
var medianPoint = arrayCount / 2;
var leftMedian = medianValues[medianPoint - 1] - medianValues[0];
var rightMedian = medianValues[arrayCount - 1]
@ -627,8 +553,9 @@ medianValue = (connectedCells, rankValue)=>
*
* Constructs a new median cell sorter.
*/
function MedianCellSorter()
{
function
MedianCellSorter() {
// empty
};
@ -651,25 +578,19 @@ cell = false;
*
* Compares two MedianCellSorters.
*/
compare = (a, b)=>
{
if (a != null && b != null)
{
if (b.medianValue > a.medianValue)
{
compare = (a, b) => {
if (a != null && b != null) {
if (b.medianValue > a.medianValue) {
return -1;
}
else if (b.medianValue < a.medianValue)
{
} else if (b.medianValue < a.medianValue) {
return 1;
}
else
{
} else {
return 0;
}
}
else
{
} else {
return 0;
}
};
}
export default mxMedianHybridCrossingReduction;

View File

@ -2,6 +2,15 @@
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
class mxMinimumCycleRemover extends mxHierarchicalLayoutStage {
/**
* Variable: layout
*
* Reference to the enclosing <mxHierarchicalLayout>.
*/
layout = null;
/**
* Class: mxMinimumCycleRemover
*
@ -12,24 +21,11 @@
*
* Creates a cycle remover for the given internal model.
*/
function mxMinimumCycleRemover(layout)
{
constructor(layout) {
// constructor not called
this.layout = layout;
};
/**
* Extends mxHierarchicalLayoutStage.
*/
mxMinimumCycleRemover.prototype = new mxHierarchicalLayoutStage();
constructor = mxMinimumCycleRemover;
/**
* Variable: layout
*
* Reference to the enclosing <mxHierarchicalLayout>.
*/
layout = null;
/**
* Function: execute
*
@ -37,15 +33,13 @@ layout = null;
* and creates the resulting laid out graph within that facade for further
* use.
*/
execute = (parent)=>
{
execute = (parent) => {
var model = this.layout.getModel();
var seenNodes = new Object();
var unseenNodesArray = model.vertexMapper.getValues();
var unseenNodes = new Object();
for (var i = 0; i < unseenNodesArray.length; i++)
{
for (var i = 0; i < unseenNodesArray.length; i++) {
unseenNodes[unseenNodesArray[i].id] = unseenNodesArray[i];
}
@ -53,24 +47,20 @@ execute = (parent)=>
// reverse it.
var rootsArray = null;
if (model.roots != null)
{
if (model.roots != null) {
var modelRoots = model.roots;
rootsArray = [];
for (var i = 0; i < modelRoots.length; i++)
{
for (var i = 0; i < modelRoots.length; i++) {
rootsArray[i] = model.vertexMapper.get(modelRoots[i]);
}
}
model.visit((parent, node, connectingEdge, layer, seen)=>
{
model.visit((parent, node, connectingEdge, layer, seen) => {
// Check if the cell is in it's own ancestor list, if so
// invert the connecting edge and reverse the target/source
// relationship to that edge in the parent and the cell
if (node.isAncestor(parent))
{
if (node.isAncestor(parent)) {
connectingEdge.invert();
mxUtils.remove(connectingEdge, parent.connectsAsSource);
parent.connectsAsTarget.push(connectingEdge);
@ -88,13 +78,11 @@ execute = (parent)=>
var seenNodesCopy = mxUtils.clone(seenNodes, null, true);
// Pick a random cell and dfs from it
model.visit((parent, node, connectingEdge, layer, seen)=>
{
model.visit((parent, node, connectingEdge, layer, seen) => {
// Check if the cell is in it's own ancestor list, if so
// invert the connecting edge and reverse the target/source
// relationship to that edge in the parent and the cell
if (node.isAncestor(parent))
{
if (node.isAncestor(parent)) {
connectingEdge.invert();
mxUtils.remove(connectingEdge, parent.connectsAsSource);
node.connectsAsSource.push(connectingEdge);
@ -106,3 +94,6 @@ execute = (parent)=>
delete unseenNodes[node.id];
}, unseenNodes, true, seenNodesCopy);
};
}
export default mxMinimumCycleRemover;

View File

@ -2,6 +2,15 @@
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
class mxSwimlaneOrdering extends mxHierarchicalLayoutStage {
/**
* Variable: layout
*
* Reference to the enclosing <mxHierarchicalLayout>.
*/
layout = null;
/**
* Class: mxSwimlaneOrdering
*
@ -12,24 +21,11 @@
*
* Creates a cycle remover for the given internal model.
*/
function mxSwimlaneOrdering(layout)
{
constructor(layout) {
// super not called
this.layout = layout;
};
/**
* Extends mxHierarchicalLayoutStage.
*/
mxSwimlaneOrdering.prototype = new mxHierarchicalLayoutStage();
constructor = mxSwimlaneOrdering;
/**
* Variable: layout
*
* Reference to the enclosing <mxHierarchicalLayout>.
*/
layout = null;
/**
* Function: execute
*
@ -37,8 +33,7 @@ layout = null;
* and creates the resulting laid out graph within that facade for further
* use.
*/
execute = (parent)=>
{
execute = (parent) => {
var model = this.layout.getModel();
var seenNodes = new Object();
var unseenNodes = mxUtils.clone(model.vertexMapper, null, true);
@ -47,19 +42,16 @@ execute = (parent)=>
// reverse it.
var rootsArray = null;
if (model.roots != null)
{
if (model.roots != null) {
var modelRoots = model.roots;
rootsArray = [];
for (var i = 0; i < modelRoots.length; i++)
{
for (var i = 0; i < modelRoots.length; i++) {
rootsArray[i] = model.vertexMapper.get(modelRoots[i]);
}
}
model.visit((parent, node, connectingEdge, layer, seen)=>
{
model.visit((parent, node, connectingEdge, layer, seen) => {
// Check if the cell is in it's own ancestor list, if so
// invert the connecting edge and reverse the target/source
// relationship to that edge in the parent and the cell
@ -71,16 +63,13 @@ execute = (parent)=>
var reversedOverSwimlane = parent != null && connectingEdge != null &&
parent.swimlaneIndex < node.swimlaneIndex && connectingEdge.source == node;
if (isAncestor)
{
if (isAncestor) {
connectingEdge.invert();
mxUtils.remove(connectingEdge, parent.connectsAsSource);
node.connectsAsSource.push(connectingEdge);
parent.connectsAsTarget.push(connectingEdge);
mxUtils.remove(connectingEdge, node.connectsAsTarget);
}
else if (reversedOverSwimlane)
{
} else if (reversedOverSwimlane) {
connectingEdge.invert();
mxUtils.remove(connectingEdge, parent.connectsAsTarget);
node.connectsAsTarget.push(connectingEdge);
@ -93,3 +82,6 @@ execute = (parent)=>
delete unseenNodes[cellId];
}, rootsArray, true, null);
};
}
export default mxSwimlaneOrdering;

View File

@ -7,6 +7,28 @@ import mxRectangle from "FIXME";
import mxDictionary from "FIXME";
class mxGraphLayout {
/**
* Variable: graph
*
* Reference to the enclosing <mxGraph>.
*/
graph = null;
/**
* Variable: useBoundingBox
*
* Boolean indicating if the bounding box of the label should be used if
* its available. Default is true.
*/
useBoundingBox = true;
/**
* Variable: parent
*
* The parent cell of the layout, if any
*/
parent = null;
/**
* Class: mxGraphLayout
*
@ -32,28 +54,6 @@ class mxGraphLayout {
this.graph = graph;
};
/**
* Variable: graph
*
* Reference to the enclosing <mxGraph>.
*/
graph = null;
/**
* Variable: useBoundingBox
*
* Boolean indicating if the bounding box of the label should be used if
* its available. Default is true.
*/
useBoundingBox = true;
/**
* Variable: parent
*
* The parent cell of the layout, if any
*/
parent = null;
/**
* Function: moveCell
*

View File

@ -471,7 +471,7 @@ class mxStencil extends mxShape {
var sx = w / this.w0;
var sy = h / this.h0;
var inverse = (direction == mxConstants.DIRECTION_NORTH || direction == mxConstants.DIRECTION_SOUTH);
var inverse = (direction === mxConstants.DIRECTION_NORTH || direction === mxConstants.DIRECTION_SOUTH);
if (inverse) {
sy = w / this.h0;
@ -483,7 +483,7 @@ class mxStencil extends mxShape {
y0 -= delta;
}
if (this.aspect == 'fixed') {
if (this.aspect === 'fixed') {
sy = Math.min(sx, sy);
sx = sy;
@ -594,7 +594,7 @@ class mxStencil extends mxShape {
y0 + Number(node.getAttribute('y1')) * sy,
x0 + Number(node.getAttribute('x2')) * sx,
y0 + Number(node.getAttribute('y2')) * sy);
} else if (name == 'curve') {
} else if (name === 'curve') {
canvas.curveTo(x0 + Number(node.getAttribute('x1')) * sx,
y0 + Number(node.getAttribute('y1')) * sy,
x0 + Number(node.getAttribute('x2')) * sx,