manual js syntax updates
parent
0b8e1695f8
commit
5a241d7283
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue