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,79 +2,65 @@
* 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
*
* The maximum rank this cell occupies. Default is -1.
*/
maxRank = -1;
maxRank = -1;
/**
/**
* Variable: minRank
*
* The minimum rank this cell occupies. Default is -1.
*/
minRank = -1;
minRank = -1;
/**
/**
* Variable: x
*
* The x position of this cell for each layer it occupies
*/
x = null;
x = null;
/**
/**
* Variable: y
*
* The y position of this cell for each layer it occupies
*/
y = null;
y = null;
/**
/**
* Variable: width
*
* The width of this cell. Default is 0.
*/
width = 0;
width = 0;
/**
/**
* Variable: height
*
* The height of this cell. Default is 0.
*/
height = 0;
height = 0;
/**
/**
* Variable: nextLayerConnectedCells
*
* A cached version of the cells this cell connects to on the next layer up
*/
nextLayerConnectedCells = null;
nextLayerConnectedCells = null;
/**
/**
* Variable: previousLayerConnectedCells
*
* A cached version of the cells this cell connects to on the next layer down
*/
previousLayerConnectedCells = null;
previousLayerConnectedCells = null;
/**
/**
* Variable: temp
*
* Temporary variable for general use. Generally, try to avoid
@ -84,117 +70,117 @@ previousLayerConnectedCells = null;
* be used for hashing the nodes in the model dfs and so hashCode
* was created
*/
temp = 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;
};
};
/**
/**
* Function: getPreviousLayerConnectedCells
*
* Returns the cells this cell connects to on the next layer down
*/
getPreviousLayerConnectedCells = (layer)=>
{
getPreviousLayerConnectedCells = (layer) => {
return null;
};
};
/**
/**
* Function: isEdge
*
* Returns whether or not this cell is an edge
*/
isEdge = ()=>
{
isEdge = () => {
return false;
};
};
/**
/**
* Function: isVertex
*
* Returns whether or not this cell is a node
*/
isVertex = ()=>
{
isVertex = () => {
return false;
};
};
/**
/**
* Function: getGeneralPurposeVariable
*
* Gets the value of temp for the specified layer
*/
getGeneralPurposeVariable = (layer)=>
{
getGeneralPurposeVariable = (layer) => {
return null;
};
};
/**
/**
* Function: setGeneralPurposeVariable
*
* Set the value of temp for the specified layer
*/
setGeneralPurposeVariable = (layer, value)=>
{
setGeneralPurposeVariable = (layer, value) => {
return null;
};
};
/**
/**
* Function: setX
*
* 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;
}
};
};
/**
/**
* Function: getX
*
* 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];
}
return 0.0;
};
};
/**
/**
* Function: setY
*
* 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()) {
this.y[layer - this.minRank - 1] = value;
}
else if (this.isEdge())
{
this.y[layer -this. minRank - 1] = value;
}
};
};
}
export default mxGraphAbstractHierarchyCell;

View File

@ -2,7 +2,46 @@
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
class mxGraphHierarchyEdge extends mxGraphAbstractHierarchyCell {
/**
* Variable: edges
*
* The graph edge(s) this object represents. Parallel edges are all grouped
* together within one hierarchy edge.
*/
edges = null;
/**
* Variable: ids
*
* The object identities of the wrapped cells
*/
ids = null;
/**
* Variable: source
*
* The node this edge is sourced at
*/
source = null;
/**
* Variable: target
*
* The node this edge targets
*/
target = null;
/**
* Variable: isReversed
*
* Whether or not the direction of this edge has been reversed
* internally to create a DAG for the hierarchical layout
*/
isReversed = false;
/**
* Class: mxGraphHierarchyEdge
*
* An abstraction of a hierarchical edge for the hierarchy layout
@ -15,173 +54,113 @@
*
* edges - a list of real graph edges this abstraction represents
*/
function mxGraphHierarchyEdge(edges)
{
mxGraphAbstractHierarchyCell.apply(this, arguments);
constructor(edges) {
super(edges);
this.edges = edges;
this.ids = [];
for (var i = 0; i < edges.length; i++)
{
for (var i = 0; i < edges.length; i++) {
this.ids.push(mxObjectIdentity.get(edges[i]));
}
};
};
/**
* Extends mxGraphAbstractHierarchyCell.
*/
mxGraphHierarchyEdge.prototype = new mxGraphAbstractHierarchyCell();
constructor = mxGraphHierarchyEdge;
/**
* Variable: edges
*
* The graph edge(s) this object represents. Parallel edges are all grouped
* together within one hierarchy edge.
*/
edges = null;
/**
* Variable: ids
*
* The object identities of the wrapped cells
*/
ids = null;
/**
* Variable: source
*
* The node this edge is sourced at
*/
source = null;
/**
* Variable: target
*
* The node this edge targets
*/
target = null;
/**
* Variable: isReversed
*
* Whether or not the direction of this edge has been reversed
* internally to create a DAG for the hierarchical layout
*/
isReversed = false;
/**
/**
* 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;
this.isReversed = !this.isReversed;
};
};
/**
/**
* Function: getNextLayerConnectedCells
*
* 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);
}
}
}
return this.nextLayerConnectedCells[layer - this.minRank - 1];
};
};
/**
/**
* Function: getPreviousLayerConnectedCells
*
* 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);
}
}
}
return this.previousLayerConnectedCells[layer - this.minRank - 1];
};
};
/**
/**
* Function: isEdge
*
* Returns true.
*/
isEdge = ()=>
{
isEdge = () => {
return true;
};
};
/**
/**
* Function: getGeneralPurposeVariable
*
* Gets the value of temp for the specified layer
*/
getGeneralPurposeVariable = (layer)=>
{
getGeneralPurposeVariable = (layer) => {
return this.temp[layer - this.minRank - 1];
};
};
/**
/**
* Function: setGeneralPurposeVariable
*
* Set the value of temp for the specified layer
*/
setGeneralPurposeVariable = (layer, value)=>
{
setGeneralPurposeVariable = (layer, value) => {
this.temp[layer - this.minRank - 1] = value;
};
};
/**
/**
* Function: getCoreCell
*
* 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,7 +2,76 @@
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
import mxDictionary from "FIXME";
class mxGraphHierarchyModel {
/**
* Variable: maxRank
*
* Stores the largest rank number allocated
*/
maxRank = null;
/**
* Variable: vertexMapper
*
* Map from graph vertices to internal model nodes.
*/
vertexMapper = null;
/**
* Variable: edgeMapper
*
* Map from graph edges to internal model edges
*/
edgeMapper = null;
/**
* Variable: ranks
*
* Mapping from rank number to actual rank
*/
ranks = null;
/**
* Variable: roots
*
* Store of roots of this hierarchy model, these are real graph cells, not
* internal cells
*/
roots = null;
/**
* Variable: parent
*
* The parent cell whose children are being laid out
*/
parent = null;
/**
* Variable: dfsCount
*
* Count of the number of times the ancestor dfs has been used.
*/
dfsCount = 0;
/**
* Variable: SOURCESCANSTARTRANK
*
* High value to start source layering scan rank value from.
*/
SOURCESCANSTARTRANK = 100000000;
/**
* Variable: tightenToSource
*
* Whether or not to tighten the assigned ranks of vertices up towards
* the source cells.
*/
tightenToSource = false;
/**
* Class: mxGraphHierarchyModel
*
* Internal model of a hierarchical graph. This model stores nodes and edges
@ -26,8 +95,7 @@
* scanRanksFromSinks - Whether rank assignment is from the sinks or sources.
* usage
*/
function mxGraphHierarchyModel(layout, vertices, roots, parent, tightenToSource)
{
constructor(layout, vertices, roots, parent, tightenToSource) {
var graph = layout.getGraph();
this.tightenToSource = tightenToSource;
this.roots = roots;
@ -40,8 +108,7 @@ function mxGraphHierarchyModel(layout, vertices, roots, parent, tightenToSource)
this.maxRank = 0;
var internalVertices = [];
if (vertices == null)
{
if (vertices == null) {
vertices = this.graph.getChildVertices(parent);
}
@ -53,26 +120,22 @@ function mxGraphHierarchyModel(layout, vertices, roots, parent, tightenToSource)
// 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++)
{
for (var i = 0; i < vertices.length; i++) {
var edges = internalVertices[i].connectsAsSource;
for (var j = 0; j < edges.length; j++)
{
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)
{
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 (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
@ -84,17 +147,14 @@ function mxGraphHierarchyModel(layout, vertices, roots, parent, tightenToSource)
}
if (internalTargetCell != null
&& internalVertices[i] != internalTargetCell)
{
&& internalVertices[i] != internalTargetCell) {
internalEdge.target = internalTargetCell;
if (internalTargetCell.connectsAsTarget.length == 0)
{
if (internalTargetCell.connectsAsTarget.length == 0) {
internalTargetCell.connectsAsTarget = [];
}
if (mxUtils.indexOf(internalTargetCell.connectsAsTarget, internalEdge) < 0)
{
if (mxUtils.indexOf(internalTargetCell.connectsAsTarget, internalEdge) < 0) {
internalTargetCell.connectsAsTarget.push(internalEdge);
}
}
@ -105,74 +165,9 @@ function mxGraphHierarchyModel(layout, vertices, roots, parent, tightenToSource)
// internal vertex as having been visited.
internalVertices[i].temp[0] = 1;
}
};
};
/**
* Variable: maxRank
*
* Stores the largest rank number allocated
*/
maxRank = null;
/**
* Variable: vertexMapper
*
* Map from graph vertices to internal model nodes.
*/
vertexMapper = null;
/**
* Variable: edgeMapper
*
* Map from graph edges to internal model edges
*/
edgeMapper = null;
/**
* Variable: ranks
*
* Mapping from rank number to actual rank
*/
ranks = null;
/**
* Variable: roots
*
* Store of roots of this hierarchy model, these are real graph cells, not
* internal cells
*/
roots = null;
/**
* Variable: parent
*
* The parent cell whose children are being laid out
*/
parent = null;
/**
* Variable: dfsCount
*
* Count of the number of times the ancestor dfs has been used.
*/
dfsCount = 0;
/**
* Variable: SOURCESCANSTARTRANK
*
* High value to start source layering scan rank value from.
*/
SOURCESCANSTARTRANK = 100000000;
/**
* Variable: tightenToSource
*
* Whether or not to tighten the assigned ranks of vertices up towards
* the source cells.
*/
tightenToSource = false;
/**
/**
* Function: createInternalCells
*
* Creates all edges in the internal model
@ -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,17 +234,15 @@ 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);
layout.setOrthogonalEdge(edge, true);
}
}
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);
}
}
@ -265,27 +252,23 @@ createInternalCells = (layout, vertices, internalVertices)=>
// Ensure temp variable is cleared from any previous use
internalVertices[i].temp[0] = 0;
}
};
};
/**
/**
* Function: initialRank
*
* Basic determination of minimum layer ranking by working from from sources
* 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,
@ -420,21 +387,19 @@ initialRank = ()=>
// Reset the maxRank to that which would be expected for a from-sink
// scan
this.maxRank = this.SOURCESCANSTARTRANK - this.maxRank;
};
};
/**
/**
* Function: fixRanks
*
* 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);
@ -493,9 +451,9 @@ fixRanks = ()=>
}
}
}, rootsArray, false, null);
};
};
/**
/**
* Function: visit
*
* A depth first search through the internal heirarchy model.
@ -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);
}
}
@ -540,9 +490,9 @@ visit = (visitor, dfsRoots, trackAncestors, seenNodes)=>
this.dfsCount++;
}
};
};
/**
/**
* Function: dfs
*
* Performs a depth first search on the internal hierarchy model
@ -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,16 +528,14 @@ 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);
}
}
};
};
/**
/**
* Function: extendedDfs
*
* Performs a depth first search on the internal hierarchy model. This dfs
@ -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,91 +15,78 @@
*
* 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
*
* The graph cell this object represents.
*/
cell = null;
cell = null;
/**
/**
* Variable: id
*
* The object identity of the wrapped cell
*/
id = null;
id = null;
/**
/**
* Variable: connectsAsTarget
*
* Collection of hierarchy edges that have this node as a target
*/
connectsAsTarget = null;
connectsAsTarget = null;
/**
/**
* Variable: connectsAsSource
*
* Collection of hierarchy edges that have this node as a source
*/
connectsAsSource = null;
connectsAsSource = null;
/**
/**
* Variable: hashCode
*
* Assigns a unique hashcode for each node. Used by the model dfs instead
* of copying HashSets
*/
hashCode = false;
hashCode = false;
/**
/**
* Function: getRankValue
*
* Returns the integer value of the layer that this node resides in
*/
getRankValue = (layer)=>
{
getRankValue = (layer) => {
return this.maxRank;
};
};
/**
/**
* Function: getNextLayerConnectedCells
*
* 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);
}
@ -107,31 +94,25 @@ getNextLayerConnectedCells = (layer)=>
}
return this.nextLayerConnectedCells[0];
};
};
/**
/**
* Function: getPreviousLayerConnectedCells
*
* 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);
}
@ -139,55 +120,48 @@ getPreviousLayerConnectedCells = (layer)=>
}
return this.previousLayerConnectedCells[0];
};
};
/**
/**
* Function: isVertex
*
* Returns true.
*/
isVertex = ()=>
{
isVertex = () => {
return true;
};
};
/**
/**
* Function: getGeneralPurposeVariable
*
* Gets the value of temp for the specified layer
*/
getGeneralPurposeVariable = (layer)=>
{
getGeneralPurposeVariable = (layer) => {
return this.temp[0];
};
};
/**
/**
* Function: setGeneralPurposeVariable
*
* 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;
}
}
@ -207,14 +179,16 @@ isAncestor = (otherNode)=>
}
return false;
};
};
/**
/**
* Function: getCoreCell
*
* Gets the core vertex associated with this wrapper
*/
getCoreCell = ()=>
{
getCoreCell = () => {
return this.cell;
};
};
}
export default mxGraphHierarchyNode;

View File

@ -2,7 +2,81 @@
* Copyright (c) 2006-2018, JGraph Ltd
* Copyright (c) 2006-2018, Gaudenz Alder
*/
/**
class mxSwimlaneModel {
/**
* Variable: maxRank
*
* Stores the largest rank number allocated
*/
maxRank = null;
/**
* Variable: vertexMapper
*
* Map from graph vertices to internal model nodes.
*/
vertexMapper = null;
/**
* Variable: edgeMapper
*
* Map from graph edges to internal model edges
*/
edgeMapper = null;
/**
* Variable: ranks
*
* Mapping from rank number to actual rank
*/
ranks = null;
/**
* Variable: roots
*
* Store of roots of this hierarchy model, these are real graph cells, not
* internal cells
*/
roots = null;
/**
* Variable: parent
*
* The parent cell whose children are being laid out
*/
parent = null;
/**
* Variable: dfsCount
*
* Count of the number of times the ancestor dfs has been used.
*/
dfsCount = 0;
/**
* Variable: SOURCESCANSTARTRANK
*
* High value to start source layering scan rank value from.
*/
SOURCESCANSTARTRANK = 100000000;
/**
* Variable: tightenToSource
*
* Whether or not to tighten the assigned ranks of vertices up towards
* the source cells.
*/
tightenToSource = false;
/**
* Variable: ranksPerGroup
*
* An array of the number of ranks within each swimlane
*/
ranksPerGroup = null;
/**
* Class: mxSwimlaneModel
*
* Internal model of a hierarchical graph. This model stores nodes and edges
@ -26,8 +100,7 @@
* scanRanksFromSinks - Whether rank assignment is from the sinks or sources.
* usage
*/
function mxSwimlaneModel(layout, vertices, roots, parent, tightenToSource)
{
constructor(layout, vertices, roots, parent, tightenToSource) {
var graph = layout.getGraph();
this.tightenToSource = tightenToSource;
this.roots = roots;
@ -40,8 +113,7 @@ function mxSwimlaneModel(layout, vertices, roots, parent, tightenToSource)
this.maxRank = 0;
var internalVertices = [];
if (vertices == null)
{
if (vertices == null) {
vertices = this.graph.getChildVertices(parent);
}
@ -53,26 +125,22 @@ function mxSwimlaneModel(layout, vertices, roots, parent, tightenToSource)
// 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++)
{
for (var i = 0; i < vertices.length; i++) {
var edges = internalVertices[i].connectsAsSource;
for (var j = 0; j < edges.length; j++)
{
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)
{
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 (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
@ -84,17 +152,14 @@ function mxSwimlaneModel(layout, vertices, roots, parent, tightenToSource)
}
if (internalTargetCell != null
&& internalVertices[i] != internalTargetCell)
{
&& internalVertices[i] != internalTargetCell) {
internalEdge.target = internalTargetCell;
if (internalTargetCell.connectsAsTarget.length == 0)
{
if (internalTargetCell.connectsAsTarget.length == 0) {
internalTargetCell.connectsAsTarget = [];
}
if (mxUtils.indexOf(internalTargetCell.connectsAsTarget, internalEdge) < 0)
{
if (mxUtils.indexOf(internalTargetCell.connectsAsTarget, internalEdge) < 0) {
internalTargetCell.connectsAsTarget.push(internalEdge);
}
}
@ -105,81 +170,9 @@ function mxSwimlaneModel(layout, vertices, roots, parent, tightenToSource)
// internal vertex as having been visited.
internalVertices[i].temp[0] = 1;
}
};
};
/**
* Variable: maxRank
*
* Stores the largest rank number allocated
*/
maxRank = null;
/**
* Variable: vertexMapper
*
* Map from graph vertices to internal model nodes.
*/
vertexMapper = null;
/**
* Variable: edgeMapper
*
* Map from graph edges to internal model edges
*/
edgeMapper = null;
/**
* Variable: ranks
*
* Mapping from rank number to actual rank
*/
ranks = null;
/**
* Variable: roots
*
* Store of roots of this hierarchy model, these are real graph cells, not
* internal cells
*/
roots = null;
/**
* Variable: parent
*
* The parent cell whose children are being laid out
*/
parent = null;
/**
* Variable: dfsCount
*
* Count of the number of times the ancestor dfs has been used.
*/
dfsCount = 0;
/**
* Variable: SOURCESCANSTARTRANK
*
* High value to start source layering scan rank value from.
*/
SOURCESCANSTARTRANK = 100000000;
/**
* Variable: tightenToSource
*
* Whether or not to tighten the assigned ranks of vertices up towards
* the source cells.
*/
tightenToSource = false;
/**
* Variable: ranksPerGroup
*
* An array of the number of ranks within each swimlane
*/
ranksPerGroup = null;
/**
/**
* Function: createInternalCells
*
* Creates all edges in the internal model
@ -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,17 +248,15 @@ 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);
layout.setOrthogonalEdge(edge, true);
}
}
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);
}
}
@ -283,31 +266,27 @@ createInternalCells = (layout, vertices, internalVertices)=>
// Ensure temp variable is cleared from any previous use
internalVertices[i].temp[0] = 0;
}
};
};
/**
/**
* Function: initialRank
*
* Basic determination of minimum layer ranking by working from from sources
* 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,15 +296,11 @@ 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
{
lowerRank[i] = upperRank[i+1] + 1;
} else {
lowerRank[i] = upperRank[i + 1] + 1;
}
upperRank[i] = lowerRank[i] + this.ranksPerGroup[i];
@ -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
@ -463,9 +424,9 @@ initialRank = ()=>
// currentMaxLayer = internalNode.temp[0];
// }
// }
};
};
/**
/**
* Function: maxChainDfs
*
* Performs a depth first search on the internal hierarchy model. This dfs
@ -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,39 +460,33 @@ 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)
{
this.maxChainDfs(root, targetNode, internalEdge, mxUtils.clone(seen, null , true), 0);
}
else if (root.swimlaneIndex == targetNode.swimlaneIndex)
{
this.maxChainDfs(root, targetNode, internalEdge, mxUtils.clone(seen, null , true), chainCount + 1);
if (root.swimlaneIndex < targetNode.swimlaneIndex) {
this.maxChainDfs(root, targetNode, internalEdge, mxUtils.clone(seen, null, true), 0);
} else if (root.swimlaneIndex == targetNode.swimlaneIndex) {
this.maxChainDfs(root, targetNode, internalEdge, mxUtils.clone(seen, null, true), chainCount + 1);
}
}
}
}
};
};
/**
/**
* Function: fixRanks
*
* 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);
@ -594,9 +538,9 @@ fixRanks = ()=>
}
}
}, rootsArray, false, null);
};
};
/**
/**
* Function: visit
*
* A depth first search through the internal heirarchy model.
@ -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);
}
}
@ -641,9 +577,9 @@ visit = (visitor, dfsRoots, trackAncestors, seenNodes)=>
this.dfsCount++;
}
};
};
/**
/**
* Function: dfs
*
* Performs a depth first search on the internal hierarchy model
@ -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,16 +615,14 @@ 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);
}
}
};
};
/**
/**
* Function: extendedDfs
*
* Performs a depth first search on the internal hierarchy model. This dfs
@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,9 @@
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
class mxHierarchicalLayoutStage {
/**
* Class: mxHierarchicalLayoutStage
*
* The specific layout interface for hierarchical layouts. It adds a
@ -13,13 +15,17 @@
*
* Constructs a new hierarchical layout stage.
*/
function mxHierarchicalLayoutStage() { };
constructor() {
}
/**
/**
* Function: execute
*
* Takes the graph detail and configuration information within the facade
* and creates the resulting laid out graph within that facade for further
* use.
*/
execute = (parent)=> { };
execute = (parent)=> { };
}
export default mxHierarchicalLayoutStage;

View File

@ -2,7 +2,9 @@
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
class mxMedianHybridCrossingReduction extends mxHierarchicalLayoutStage {
/**
* Class: mxMedianHybridCrossingReduction
*
* Sets the horizontal locations of node and edge dummy nodes on each layer.
@ -20,76 +22,68 @@
* 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
*
* Reference to the enclosing <mxHierarchicalLayout>.
*/
layout = null;
layout = null;
/**
/**
* Variable: maxIterations
*
* The maximum number of iterations to perform whilst reducing edge
* crossings. Default is 24.
*/
maxIterations = 24;
maxIterations = 24;
/**
/**
* Variable: nestedBestRanks
*
* Stores each rank as a collection of cells in the best order found for
* each layer so far
*/
nestedBestRanks = null;
nestedBestRanks = null;
/**
/**
* Variable: currentBestCrossings
*
* The total number of crossings found in the best configuration so far
*/
currentBestCrossings = 0;
currentBestCrossings = 0;
/**
/**
* Variable: iterationsWithoutImprovement
*
* The total number of crossings found in the best configuration so far
*/
iterationsWithoutImprovement = 0;
iterationsWithoutImprovement = 0;
/**
/**
* Variable: maxNoImprovementIterations
*
* The total number of crossings found in the best configuration so far
*/
maxNoImprovementIterations = 2;
maxNoImprovementIterations = 2;
/**
/**
* Function: execute
*
* 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,25 +135,22 @@ 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]);
}
}
model.ranks = ranks;
};
};
/**
/**
* Function: calculateCrossings
*
* Calculates the total number of edge crossing in the current graph.
@ -179,20 +161,18 @@ 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);
}
return totalCrossings;
};
};
/**
/**
* Function: calculateRankCrossing
*
* Calculates the number of edges crossings between the specified rank and
@ -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];
}
@ -274,9 +247,9 @@ calculateRankCrossing = (i, model)=>
}
return totalCrossings;
};
};
/**
/**
* Function: transpose
*
* Takes each possible adjacent cell pair on each rank and checks if
@ -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
@ -454,9 +404,9 @@ transpose = (mainLoopIteration, model)=>
}
}
}
};
};
/**
/**
* Function: weightedMedian
*
* Sweeps up or down the layout attempting to minimise the median placement
@ -467,27 +417,21 @@ 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++) {
this.medianRank(j, downwardSweep);
}
}
else
{
for (var j = 1; j < model.maxRank; j++)
{
this.medianRank(j, downwardSweep);
}
}
};
};
/**
/**
* Function: medianRank
*
* Attempts to minimise the median placement of connected cells on this rank
@ -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,17 +490,15 @@ 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);
}
}
};
};
/**
/**
* Function: medianValue
*
* Calculates the median rank order positioning for the specified cell using
@ -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]
@ -615,9 +541,9 @@ medianValue = (connectedCells, rankValue)=>
* leftMedian)
/ (leftMedian + rightMedian);
}
};
};
/**
/**
* Class: MedianCellSorter
*
* A utility class used to track cells whilst sorting occurs on the median
@ -627,49 +553,44 @@ medianValue = (connectedCells, rankValue)=>
*
* Constructs a new median cell sorter.
*/
function MedianCellSorter()
{
// empty
};
function
/**
MedianCellSorter() {
// empty
};
/**
* Variable: medianValue
*
* The weighted value of the cell stored.
*/
medianValue = 0;
medianValue = 0;
/**
/**
* Variable: cell
*
* The cell whose median value is being calculated
*/
cell = false;
cell = false;
/**
/**
* Function: compare
*
* 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,7 +2,16 @@
* 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
*
* An implementation of the first stage of the Sugiyama layout. Straightforward
@ -12,40 +21,25 @@
*
* 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
*
* Takes the graph detail and configuration information within the facade
* 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);
@ -105,4 +93,7 @@ execute = (parent)=>
seenNodes[node.id] = node;
delete unseenNodes[node.id];
}, unseenNodes, true, seenNodesCopy);
};
};
}
export default mxMinimumCycleRemover;

View File

@ -2,7 +2,16 @@
* 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
*
* An implementation of the first stage of the Sugiyama layout. Straightforward
@ -12,33 +21,19 @@
*
* 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
*
* Takes the graph detail and configuration information within the facade
* 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);
@ -92,4 +81,7 @@ execute = (parent)=>
seenNodes[cellId] = node;
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,