manual js syntax updates
parent
0b8e1695f8
commit
5a241d7283
|
@ -2,22 +2,8 @@
|
|||
* Copyright (c) 2006-2015, JGraph Ltd
|
||||
* Copyright (c) 2006-2015, Gaudenz Alder
|
||||
*/
|
||||
/**
|
||||
* Class: mxGraphAbstractHierarchyCell
|
||||
*
|
||||
* An abstraction of an internal hierarchy node or edge
|
||||
*
|
||||
* Constructor: mxGraphAbstractHierarchyCell
|
||||
*
|
||||
* Constructs a new hierarchical layout algorithm.
|
||||
*/
|
||||
function mxGraphAbstractHierarchyCell()
|
||||
{
|
||||
this.x = [];
|
||||
this.y = [];
|
||||
this.temp = [];
|
||||
};
|
||||
|
||||
class mxGraphAbstractHierarchyCell {
|
||||
/**
|
||||
* Variable: maxRank
|
||||
*
|
||||
|
@ -86,13 +72,27 @@ previousLayerConnectedCells = null;
|
|||
*/
|
||||
temp = null;
|
||||
|
||||
/**
|
||||
* Class: mxGraphAbstractHierarchyCell
|
||||
*
|
||||
* An abstraction of an internal hierarchy node or edge
|
||||
*
|
||||
* Constructor: mxGraphAbstractHierarchyCell
|
||||
*
|
||||
* Constructs a new hierarchical layout algorithm.
|
||||
*/
|
||||
constructor() {
|
||||
this.x = [];
|
||||
this.y = [];
|
||||
this.temp = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Function: getNextLayerConnectedCells
|
||||
*
|
||||
* Returns the cells this cell connects to on the next layer up
|
||||
*/
|
||||
getNextLayerConnectedCells = (layer)=>
|
||||
{
|
||||
getNextLayerConnectedCells = (layer) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
|
@ -101,8 +101,7 @@ getNextLayerConnectedCells = (layer)=>
|
|||
*
|
||||
* Returns the cells this cell connects to on the next layer down
|
||||
*/
|
||||
getPreviousLayerConnectedCells = (layer)=>
|
||||
{
|
||||
getPreviousLayerConnectedCells = (layer) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
|
@ -111,8 +110,7 @@ getPreviousLayerConnectedCells = (layer)=>
|
|||
*
|
||||
* Returns whether or not this cell is an edge
|
||||
*/
|
||||
isEdge = ()=>
|
||||
{
|
||||
isEdge = () => {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
@ -121,8 +119,7 @@ isEdge = ()=>
|
|||
*
|
||||
* Returns whether or not this cell is a node
|
||||
*/
|
||||
isVertex = ()=>
|
||||
{
|
||||
isVertex = () => {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
@ -131,8 +128,7 @@ isVertex = ()=>
|
|||
*
|
||||
* Gets the value of temp for the specified layer
|
||||
*/
|
||||
getGeneralPurposeVariable = (layer)=>
|
||||
{
|
||||
getGeneralPurposeVariable = (layer) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
|
@ -141,8 +137,7 @@ getGeneralPurposeVariable = (layer)=>
|
|||
*
|
||||
* Set the value of temp for the specified layer
|
||||
*/
|
||||
setGeneralPurposeVariable = (layer, value)=>
|
||||
{
|
||||
setGeneralPurposeVariable = (layer, value) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
|
@ -151,14 +146,10 @@ setGeneralPurposeVariable = (layer, value)=>
|
|||
*
|
||||
* Set the value of x for the specified layer
|
||||
*/
|
||||
setX = (layer, value)=>
|
||||
{
|
||||
if (this.isVertex())
|
||||
{
|
||||
setX = (layer, value) => {
|
||||
if (this.isVertex()) {
|
||||
this.x[0] = value;
|
||||
}
|
||||
else if (this.isEdge())
|
||||
{
|
||||
} else if (this.isEdge()) {
|
||||
this.x[layer - this.minRank - 1] = value;
|
||||
}
|
||||
};
|
||||
|
@ -168,14 +159,10 @@ setX = (layer, value)=>
|
|||
*
|
||||
* Gets the value of x on the specified layer
|
||||
*/
|
||||
getX = (layer)=>
|
||||
{
|
||||
if (this.isVertex())
|
||||
{
|
||||
getX = (layer) => {
|
||||
if (this.isVertex()) {
|
||||
return this.x[0];
|
||||
}
|
||||
else if (this.isEdge())
|
||||
{
|
||||
} else if (this.isEdge()) {
|
||||
return this.x[layer - this.minRank - 1];
|
||||
}
|
||||
|
||||
|
@ -187,14 +174,13 @@ getX = (layer)=>
|
|||
*
|
||||
* Set the value of y for the specified layer
|
||||
*/
|
||||
setY = (layer, value)=>
|
||||
{
|
||||
if (this.isVertex())
|
||||
{
|
||||
setY = (layer, value) => {
|
||||
if (this.isVertex()) {
|
||||
this.y[0] = value;
|
||||
}
|
||||
else if (this.isEdge())
|
||||
{
|
||||
} else if (this.isEdge()) {
|
||||
this.y[layer - this.minRank - 1] = value;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default mxGraphAbstractHierarchyCell;
|
||||
|
|
|
@ -2,37 +2,8 @@
|
|||
* Copyright (c) 2006-2015, JGraph Ltd
|
||||
* Copyright (c) 2006-2015, Gaudenz Alder
|
||||
*/
|
||||
/**
|
||||
* Class: mxGraphHierarchyEdge
|
||||
*
|
||||
* An abstraction of a hierarchical edge for the hierarchy layout
|
||||
*
|
||||
* Constructor: mxGraphHierarchyEdge
|
||||
*
|
||||
* Constructs a hierarchy edge
|
||||
*
|
||||
* Arguments:
|
||||
*
|
||||
* edges - a list of real graph edges this abstraction represents
|
||||
*/
|
||||
function mxGraphHierarchyEdge(edges)
|
||||
{
|
||||
mxGraphAbstractHierarchyCell.apply(this, arguments);
|
||||
this.edges = edges;
|
||||
this.ids = [];
|
||||
|
||||
for (var i = 0; i < edges.length; i++)
|
||||
{
|
||||
this.ids.push(mxObjectIdentity.get(edges[i]));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Extends mxGraphAbstractHierarchyCell.
|
||||
*/
|
||||
mxGraphHierarchyEdge.prototype = new mxGraphAbstractHierarchyCell();
|
||||
constructor = mxGraphHierarchyEdge;
|
||||
|
||||
class mxGraphHierarchyEdge extends mxGraphAbstractHierarchyCell {
|
||||
/**
|
||||
* Variable: edges
|
||||
*
|
||||
|
@ -70,13 +41,35 @@ target = null;
|
|||
*/
|
||||
isReversed = false;
|
||||
|
||||
/**
|
||||
* Class: mxGraphHierarchyEdge
|
||||
*
|
||||
* An abstraction of a hierarchical edge for the hierarchy layout
|
||||
*
|
||||
* Constructor: mxGraphHierarchyEdge
|
||||
*
|
||||
* Constructs a hierarchy edge
|
||||
*
|
||||
* Arguments:
|
||||
*
|
||||
* edges - a list of real graph edges this abstraction represents
|
||||
*/
|
||||
constructor(edges) {
|
||||
super(edges);
|
||||
this.edges = edges;
|
||||
this.ids = [];
|
||||
|
||||
for (var i = 0; i < edges.length; i++) {
|
||||
this.ids.push(mxObjectIdentity.get(edges[i]));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Function: invert
|
||||
*
|
||||
* Inverts the direction of this internal edge(s)
|
||||
*/
|
||||
invert = (layer)=>
|
||||
{
|
||||
invert = (layer) => {
|
||||
var temp = this.source;
|
||||
this.source = this.target;
|
||||
this.target = temp;
|
||||
|
@ -88,22 +81,16 @@ invert = (layer)=>
|
|||
*
|
||||
* Returns the cells this cell connects to on the next layer up
|
||||
*/
|
||||
getNextLayerConnectedCells = (layer)=>
|
||||
{
|
||||
if (this.nextLayerConnectedCells == null)
|
||||
{
|
||||
getNextLayerConnectedCells = (layer) => {
|
||||
if (this.nextLayerConnectedCells == null) {
|
||||
this.nextLayerConnectedCells = [];
|
||||
|
||||
for (var i = 0; i < this.temp.length; i++)
|
||||
{
|
||||
for (var i = 0; i < this.temp.length; i++) {
|
||||
this.nextLayerConnectedCells[i] = [];
|
||||
|
||||
if (i == this.temp.length - 1)
|
||||
{
|
||||
if (i === this.temp.length - 1) {
|
||||
this.nextLayerConnectedCells[i].push(this.source);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
this.nextLayerConnectedCells[i].push(this);
|
||||
}
|
||||
}
|
||||
|
@ -117,22 +104,16 @@ getNextLayerConnectedCells = (layer)=>
|
|||
*
|
||||
* Returns the cells this cell connects to on the next layer down
|
||||
*/
|
||||
getPreviousLayerConnectedCells = (layer)=>
|
||||
{
|
||||
if (this.previousLayerConnectedCells == null)
|
||||
{
|
||||
getPreviousLayerConnectedCells = (layer) => {
|
||||
if (this.previousLayerConnectedCells == null) {
|
||||
this.previousLayerConnectedCells = [];
|
||||
|
||||
for (var i = 0; i < this.temp.length; i++)
|
||||
{
|
||||
for (var i = 0; i < this.temp.length; i++) {
|
||||
this.previousLayerConnectedCells[i] = [];
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
if (i === 0) {
|
||||
this.previousLayerConnectedCells[i].push(this.target);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
this.previousLayerConnectedCells[i].push(this);
|
||||
}
|
||||
}
|
||||
|
@ -146,8 +127,7 @@ getPreviousLayerConnectedCells = (layer)=>
|
|||
*
|
||||
* Returns true.
|
||||
*/
|
||||
isEdge = ()=>
|
||||
{
|
||||
isEdge = () => {
|
||||
return true;
|
||||
};
|
||||
|
||||
|
@ -156,8 +136,7 @@ isEdge = ()=>
|
|||
*
|
||||
* Gets the value of temp for the specified layer
|
||||
*/
|
||||
getGeneralPurposeVariable = (layer)=>
|
||||
{
|
||||
getGeneralPurposeVariable = (layer) => {
|
||||
return this.temp[layer - this.minRank - 1];
|
||||
};
|
||||
|
||||
|
@ -166,8 +145,7 @@ getGeneralPurposeVariable = (layer)=>
|
|||
*
|
||||
* Set the value of temp for the specified layer
|
||||
*/
|
||||
setGeneralPurposeVariable = (layer, value)=>
|
||||
{
|
||||
setGeneralPurposeVariable = (layer, value) => {
|
||||
this.temp[layer - this.minRank - 1] = value;
|
||||
};
|
||||
|
||||
|
@ -176,12 +154,13 @@ setGeneralPurposeVariable = (layer, value)=>
|
|||
*
|
||||
* Gets the first core edge associated with this wrapper
|
||||
*/
|
||||
getCoreCell = ()=>
|
||||
{
|
||||
if (this.edges != null && this.edges.length > 0)
|
||||
{
|
||||
getCoreCell = () => {
|
||||
if (this.edges != null && this.edges.length > 0) {
|
||||
return this.edges[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
export default mxGraphHierarchyEdge;
|
||||
|
|
|
@ -2,111 +2,10 @@
|
|||
* Copyright (c) 2006-2015, JGraph Ltd
|
||||
* Copyright (c) 2006-2015, Gaudenz Alder
|
||||
*/
|
||||
/**
|
||||
* Class: mxGraphHierarchyModel
|
||||
*
|
||||
* Internal model of a hierarchical graph. This model stores nodes and edges
|
||||
* equivalent to the real graph nodes and edges, but also stores the rank of the
|
||||
* cells, the order within the ranks and the new candidate locations of cells.
|
||||
* The internal model also reverses edge direction were appropriate , ignores
|
||||
* self-loop and groups parallels together under one edge object.
|
||||
*
|
||||
* Constructor: mxGraphHierarchyModel
|
||||
*
|
||||
* Creates an internal ordered graph model using the vertices passed in. If
|
||||
* there are any, leftward edge need to be inverted in the internal model
|
||||
*
|
||||
* Arguments:
|
||||
*
|
||||
* graph - the facade describing the graph to be operated on
|
||||
* vertices - the vertices for this hierarchy
|
||||
* ordered - whether or not the vertices are already ordered
|
||||
* deterministic - whether or not this layout should be deterministic on each
|
||||
* tightenToSource - whether or not to tighten vertices towards the sources
|
||||
* scanRanksFromSinks - Whether rank assignment is from the sinks or sources.
|
||||
* usage
|
||||
*/
|
||||
function mxGraphHierarchyModel(layout, vertices, roots, parent, tightenToSource)
|
||||
{
|
||||
var graph = layout.getGraph();
|
||||
this.tightenToSource = tightenToSource;
|
||||
this.roots = roots;
|
||||
this.parent = parent;
|
||||
|
||||
// map of cells to internal cell needed for second run through
|
||||
// to setup the sink of edges correctly
|
||||
this.vertexMapper = new mxDictionary();
|
||||
this.edgeMapper = new mxDictionary();
|
||||
this.maxRank = 0;
|
||||
var internalVertices = [];
|
||||
|
||||
if (vertices == null)
|
||||
{
|
||||
vertices = this.graph.getChildVertices(parent);
|
||||
}
|
||||
|
||||
this.maxRank = this.SOURCESCANSTARTRANK;
|
||||
// map of cells to internal cell needed for second run through
|
||||
// to setup the sink of edges correctly. Guess size by number
|
||||
// of edges is roughly same as number of vertices.
|
||||
this.createInternalCells(layout, vertices, internalVertices);
|
||||
|
||||
// Go through edges set their sink values. Also check the
|
||||
// ordering if and invert edges if necessary
|
||||
for (var i = 0; i < vertices.length; i++)
|
||||
{
|
||||
var edges = internalVertices[i].connectsAsSource;
|
||||
|
||||
for (var j = 0; j < edges.length; j++)
|
||||
{
|
||||
var internalEdge = edges[j];
|
||||
var realEdges = internalEdge.edges;
|
||||
|
||||
// Only need to process the first real edge, since
|
||||
// all the edges connect to the same other vertex
|
||||
if (realEdges != null && realEdges.length > 0)
|
||||
{
|
||||
var realEdge = realEdges[0];
|
||||
var targetCell = layout.getVisibleTerminal(
|
||||
realEdge, false);
|
||||
var internalTargetCell = this.vertexMapper.get(targetCell);
|
||||
|
||||
if (internalVertices[i] == internalTargetCell)
|
||||
{
|
||||
// If there are parallel edges going between two vertices and not all are in the same direction
|
||||
// you can have navigated across one direction when doing the cycle reversal that isn't the same
|
||||
// direction as the first real edge in the array above. When that happens the if above catches
|
||||
// that and we correct the target cell before continuing.
|
||||
// This branch only detects this single case
|
||||
targetCell = layout.getVisibleTerminal(
|
||||
realEdge, true);
|
||||
internalTargetCell = this.vertexMapper.get(targetCell);
|
||||
}
|
||||
|
||||
if (internalTargetCell != null
|
||||
&& internalVertices[i] != internalTargetCell)
|
||||
{
|
||||
internalEdge.target = internalTargetCell;
|
||||
|
||||
if (internalTargetCell.connectsAsTarget.length == 0)
|
||||
{
|
||||
internalTargetCell.connectsAsTarget = [];
|
||||
}
|
||||
|
||||
if (mxUtils.indexOf(internalTargetCell.connectsAsTarget, internalEdge) < 0)
|
||||
{
|
||||
internalTargetCell.connectsAsTarget.push(internalEdge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the temp variable in the internal nodes to mark this
|
||||
// internal vertex as having been visited.
|
||||
internalVertices[i].temp[0] = 1;
|
||||
}
|
||||
};
|
||||
import mxDictionary from "FIXME";
|
||||
|
||||
class mxGraphHierarchyModel {
|
||||
/**
|
||||
* Variable: maxRank
|
||||
*
|
||||
|
@ -172,6 +71,102 @@ SOURCESCANSTARTRANK = 100000000;
|
|||
*/
|
||||
tightenToSource = false;
|
||||
|
||||
/**
|
||||
* Class: mxGraphHierarchyModel
|
||||
*
|
||||
* Internal model of a hierarchical graph. This model stores nodes and edges
|
||||
* equivalent to the real graph nodes and edges, but also stores the rank of the
|
||||
* cells, the order within the ranks and the new candidate locations of cells.
|
||||
* The internal model also reverses edge direction were appropriate , ignores
|
||||
* self-loop and groups parallels together under one edge object.
|
||||
*
|
||||
* Constructor: mxGraphHierarchyModel
|
||||
*
|
||||
* Creates an internal ordered graph model using the vertices passed in. If
|
||||
* there are any, leftward edge need to be inverted in the internal model
|
||||
*
|
||||
* Arguments:
|
||||
*
|
||||
* graph - the facade describing the graph to be operated on
|
||||
* vertices - the vertices for this hierarchy
|
||||
* ordered - whether or not the vertices are already ordered
|
||||
* deterministic - whether or not this layout should be deterministic on each
|
||||
* tightenToSource - whether or not to tighten vertices towards the sources
|
||||
* scanRanksFromSinks - Whether rank assignment is from the sinks or sources.
|
||||
* usage
|
||||
*/
|
||||
constructor(layout, vertices, roots, parent, tightenToSource) {
|
||||
var graph = layout.getGraph();
|
||||
this.tightenToSource = tightenToSource;
|
||||
this.roots = roots;
|
||||
this.parent = parent;
|
||||
|
||||
// map of cells to internal cell needed for second run through
|
||||
// to setup the sink of edges correctly
|
||||
this.vertexMapper = new mxDictionary();
|
||||
this.edgeMapper = new mxDictionary();
|
||||
this.maxRank = 0;
|
||||
var internalVertices = [];
|
||||
|
||||
if (vertices == null) {
|
||||
vertices = this.graph.getChildVertices(parent);
|
||||
}
|
||||
|
||||
this.maxRank = this.SOURCESCANSTARTRANK;
|
||||
// map of cells to internal cell needed for second run through
|
||||
// to setup the sink of edges correctly. Guess size by number
|
||||
// of edges is roughly same as number of vertices.
|
||||
this.createInternalCells(layout, vertices, internalVertices);
|
||||
|
||||
// Go through edges set their sink values. Also check the
|
||||
// ordering if and invert edges if necessary
|
||||
for (var i = 0; i < vertices.length; i++) {
|
||||
var edges = internalVertices[i].connectsAsSource;
|
||||
|
||||
for (var j = 0; j < edges.length; j++) {
|
||||
var internalEdge = edges[j];
|
||||
var realEdges = internalEdge.edges;
|
||||
|
||||
// Only need to process the first real edge, since
|
||||
// all the edges connect to the same other vertex
|
||||
if (realEdges != null && realEdges.length > 0) {
|
||||
var realEdge = realEdges[0];
|
||||
var targetCell = layout.getVisibleTerminal(
|
||||
realEdge, false);
|
||||
var internalTargetCell = this.vertexMapper.get(targetCell);
|
||||
|
||||
if (internalVertices[i] == internalTargetCell) {
|
||||
// If there are parallel edges going between two vertices and not all are in the same direction
|
||||
// you can have navigated across one direction when doing the cycle reversal that isn't the same
|
||||
// direction as the first real edge in the array above. When that happens the if above catches
|
||||
// that and we correct the target cell before continuing.
|
||||
// This branch only detects this single case
|
||||
targetCell = layout.getVisibleTerminal(
|
||||
realEdge, true);
|
||||
internalTargetCell = this.vertexMapper.get(targetCell);
|
||||
}
|
||||
|
||||
if (internalTargetCell != null
|
||||
&& internalVertices[i] != internalTargetCell) {
|
||||
internalEdge.target = internalTargetCell;
|
||||
|
||||
if (internalTargetCell.connectsAsTarget.length == 0) {
|
||||
internalTargetCell.connectsAsTarget = [];
|
||||
}
|
||||
|
||||
if (mxUtils.indexOf(internalTargetCell.connectsAsTarget, internalEdge) < 0) {
|
||||
internalTargetCell.connectsAsTarget.push(internalEdge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the temp variable in the internal nodes to mark this
|
||||
// internal vertex as having been visited.
|
||||
internalVertices[i].temp[0] = 1;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Function: createInternalCells
|
||||
*
|
||||
|
@ -185,13 +180,11 @@ tightenToSource = false;
|
|||
* internalVertices - The array of <mxGraphHierarchyNodes> to have their
|
||||
* information filled in using the real vertices.
|
||||
*/
|
||||
createInternalCells = (layout, vertices, internalVertices)=>
|
||||
{
|
||||
createInternalCells = (layout, vertices, internalVertices) => {
|
||||
var graph = layout.getGraph();
|
||||
|
||||
// Create internal edges
|
||||
for (var i = 0; i < vertices.length; i++)
|
||||
{
|
||||
for (var i = 0; i < vertices.length; i++) {
|
||||
internalVertices[i] = new mxGraphHierarchyNode(vertices[i]);
|
||||
this.vertexMapper.put(vertices[i], internalVertices[i]);
|
||||
|
||||
|
@ -203,14 +196,12 @@ createInternalCells = (layout, vertices, internalVertices)=>
|
|||
// Create internal edges, but don't do any rank assignment yet
|
||||
// First use the information from the greedy cycle remover to
|
||||
// invert the leftward edges internally
|
||||
for (var j = 0; j < conns.length; j++)
|
||||
{
|
||||
for (var j = 0; j < conns.length; j++) {
|
||||
var cell = layout.getVisibleTerminal(conns[j], false);
|
||||
|
||||
// Looking for outgoing edges only
|
||||
if (cell != vertices[i] && layout.graph.model.isVertex(cell) &&
|
||||
!layout.isVertexIgnored(cell))
|
||||
{
|
||||
!layout.isVertexIgnored(cell)) {
|
||||
// We process all edge between this source and its targets
|
||||
// If there are edges going both ways, we need to collect
|
||||
// them all into one internal edges to avoid looping problems
|
||||
|
@ -232,12 +223,10 @@ createInternalCells = (layout, vertices, internalVertices)=>
|
|||
if (undirectedEdges != null &&
|
||||
undirectedEdges.length > 0 &&
|
||||
this.edgeMapper.get(undirectedEdges[0]) == null &&
|
||||
directedEdges.length * 2 >= undirectedEdges.length)
|
||||
{
|
||||
directedEdges.length * 2 >= undirectedEdges.length) {
|
||||
var internalEdge = new mxGraphHierarchyEdge(undirectedEdges);
|
||||
|
||||
for (var k = 0; k < undirectedEdges.length; k++)
|
||||
{
|
||||
for (var k = 0; k < undirectedEdges.length; k++) {
|
||||
var edge = undirectedEdges[k];
|
||||
this.edgeMapper.put(edge, internalEdge);
|
||||
|
||||
|
@ -245,8 +234,7 @@ createInternalCells = (layout, vertices, internalVertices)=>
|
|||
// without deleting it from the cell style
|
||||
graph.resetEdge(edge);
|
||||
|
||||
if (layout.disableEdgeStyle)
|
||||
{
|
||||
if (layout.disableEdgeStyle) {
|
||||
layout.setEdgeStyleEnabled(edge, false);
|
||||
layout.setOrthogonalEdge(edge, true);
|
||||
}
|
||||
|
@ -254,8 +242,7 @@ createInternalCells = (layout, vertices, internalVertices)=>
|
|||
|
||||
internalEdge.source = internalVertices[i];
|
||||
|
||||
if (mxUtils.indexOf(internalVertices[i].connectsAsSource, internalEdge) < 0)
|
||||
{
|
||||
if (mxUtils.indexOf(internalVertices[i].connectsAsSource, internalEdge) < 0) {
|
||||
internalVertices[i].connectsAsSource.push(internalEdge);
|
||||
}
|
||||
}
|
||||
|
@ -274,18 +261,14 @@ createInternalCells = (layout, vertices, internalVertices)=>
|
|||
* or sinks and working through each node in the relevant edge direction.
|
||||
* Starting at the sinks is basically a longest path layering algorithm.
|
||||
*/
|
||||
initialRank = ()=>
|
||||
{
|
||||
initialRank = () => {
|
||||
var startNodes = [];
|
||||
|
||||
if (this.roots != null)
|
||||
{
|
||||
for (var i = 0; i < this.roots.length; i++)
|
||||
{
|
||||
if (this.roots != null) {
|
||||
for (var i = 0; i < this.roots.length; i++) {
|
||||
var internalNode = this.vertexMapper.get(this.roots[i]);
|
||||
|
||||
if (internalNode != null)
|
||||
{
|
||||
if (internalNode != null) {
|
||||
startNodes.push(internalNode);
|
||||
}
|
||||
}
|
||||
|
@ -293,16 +276,14 @@ initialRank = ()=>
|
|||
|
||||
var internalNodes = this.vertexMapper.getValues();
|
||||
|
||||
for (var i=0; i < internalNodes.length; i++)
|
||||
{
|
||||
for (var i = 0; i < internalNodes.length; i++) {
|
||||
// Mark the node as not having had a layer assigned
|
||||
internalNodes[i].temp[0] = -1;
|
||||
}
|
||||
|
||||
var startNodesCopy = startNodes.slice();
|
||||
|
||||
while (startNodes.length > 0)
|
||||
{
|
||||
while (startNodes.length > 0) {
|
||||
var internalNode = startNodes[0];
|
||||
var layerDeterminingEdges;
|
||||
var edgesToBeMarked;
|
||||
|
@ -319,19 +300,15 @@ initialRank = ()=>
|
|||
// the layer determining edges variable
|
||||
var minimumLayer = this.SOURCESCANSTARTRANK;
|
||||
|
||||
for (var i = 0; i < layerDeterminingEdges.length; i++)
|
||||
{
|
||||
for (var i = 0; i < layerDeterminingEdges.length; i++) {
|
||||
var internalEdge = layerDeterminingEdges[i];
|
||||
|
||||
if (internalEdge.temp[0] == 5270620)
|
||||
{
|
||||
if (internalEdge.temp[0] == 5270620) {
|
||||
// This edge has been scanned, get the layer of the
|
||||
// node on the other end
|
||||
var otherNode = internalEdge.source;
|
||||
minimumLayer = Math.min(minimumLayer, otherNode.temp[0] - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
allEdgesScanned = false;
|
||||
|
||||
break;
|
||||
|
@ -340,15 +317,12 @@ initialRank = ()=>
|
|||
|
||||
// If all edge have been scanned, assign the layer, mark all
|
||||
// edges in the other direction and remove from the nodes list
|
||||
if (allEdgesScanned)
|
||||
{
|
||||
if (allEdgesScanned) {
|
||||
internalNode.temp[0] = minimumLayer;
|
||||
this.maxRank = Math.min(this.maxRank, minimumLayer);
|
||||
|
||||
if (edgesToBeMarked != null)
|
||||
{
|
||||
for (var i = 0; i < edgesToBeMarked.length; i++)
|
||||
{
|
||||
if (edgesToBeMarked != null) {
|
||||
for (var i = 0; i < edgesToBeMarked.length; i++) {
|
||||
var internalEdge = edgesToBeMarked[i];
|
||||
|
||||
// Assign unique stamp ( y/m/d/h )
|
||||
|
@ -359,8 +333,7 @@ initialRank = ()=>
|
|||
var otherNode = internalEdge.target;
|
||||
|
||||
// Only add node if it hasn't been assigned a layer
|
||||
if (otherNode.temp[0] == -1)
|
||||
{
|
||||
if (otherNode.temp[0] == -1) {
|
||||
startNodes.push(otherNode);
|
||||
|
||||
// Mark this other node as neither being
|
||||
|
@ -373,16 +346,13 @@ initialRank = ()=>
|
|||
}
|
||||
|
||||
startNodes.shift();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Not all the edges have been scanned, get to the back of
|
||||
// the class and put the dunces cap on
|
||||
var removedCell = startNodes.shift();
|
||||
startNodes.push(internalNode);
|
||||
|
||||
if (removedCell == internalNode && startNodes.length == 1)
|
||||
{
|
||||
if (removedCell == internalNode && startNodes.length == 1) {
|
||||
// This is an error condition, we can't get out of
|
||||
// this loop. It could happen for more than one node
|
||||
// but that's a lot harder to detect. Log the error
|
||||
|
@ -394,21 +364,18 @@ initialRank = ()=>
|
|||
|
||||
// Normalize the ranks down from their large starting value to place
|
||||
// at least 1 sink on layer 0
|
||||
for (var i=0; i < internalNodes.length; i++)
|
||||
{
|
||||
for (var i = 0; i < internalNodes.length; i++) {
|
||||
// Mark the node as not having had a layer assigned
|
||||
internalNodes[i].temp[0] -= this.maxRank;
|
||||
}
|
||||
|
||||
// Tighten the rank 0 nodes as far as possible
|
||||
for ( var i = 0; i < startNodesCopy.length; i++)
|
||||
{
|
||||
for (var i = 0; i < startNodesCopy.length; i++) {
|
||||
var internalNode = startNodesCopy[i];
|
||||
var currentMaxLayer = 0;
|
||||
var layerDeterminingEdges = internalNode.connectsAsSource;
|
||||
|
||||
for ( var j = 0; j < layerDeterminingEdges.length; j++)
|
||||
{
|
||||
for (var j = 0; j < layerDeterminingEdges.length; j++) {
|
||||
var internalEdge = layerDeterminingEdges[j];
|
||||
var otherNode = internalEdge.target;
|
||||
internalNode.temp[0] = Math.max(currentMaxLayer,
|
||||
|
@ -428,13 +395,11 @@ initialRank = ()=>
|
|||
* Fixes the layer assignments to the values stored in the nodes. Also needs
|
||||
* to create dummy nodes for edges that cross layers.
|
||||
*/
|
||||
fixRanks = ()=>
|
||||
{
|
||||
fixRanks = () => {
|
||||
var rankList = [];
|
||||
this.ranks = [];
|
||||
|
||||
for (var i = 0; i < this.maxRank + 1; i++)
|
||||
{
|
||||
for (var i = 0; i < this.maxRank + 1; i++) {
|
||||
rankList[i] = [];
|
||||
this.ranks[i] = rankList[i];
|
||||
}
|
||||
|
@ -444,23 +409,19 @@ fixRanks = ()=>
|
|||
// crossings for a standard tree.
|
||||
var rootsArray = null;
|
||||
|
||||
if (this.roots != null)
|
||||
{
|
||||
if (this.roots != null) {
|
||||
var oldRootsArray = this.roots;
|
||||
rootsArray = [];
|
||||
|
||||
for (var i = 0; i < oldRootsArray.length; i++)
|
||||
{
|
||||
for (var i = 0; i < oldRootsArray.length; i++) {
|
||||
var cell = oldRootsArray[i];
|
||||
var internalNode = this.vertexMapper.get(cell);
|
||||
rootsArray[i] = internalNode;
|
||||
}
|
||||
}
|
||||
|
||||
this.visit((parent, node, edge, layer, seen)=>
|
||||
{
|
||||
if (seen == 0 && node.maxRank < 0 && node.minRank < 0)
|
||||
{
|
||||
this.visit((parent, node, edge, layer, seen) => {
|
||||
if (seen == 0 && node.maxRank < 0 && node.minRank < 0) {
|
||||
rankList[node.temp[0]].push(node);
|
||||
node.maxRank = node.temp[0];
|
||||
node.minRank = node.temp[0];
|
||||
|
@ -469,12 +430,10 @@ fixRanks = ()=>
|
|||
node.temp[0] = rankList[node.maxRank].length - 1;
|
||||
}
|
||||
|
||||
if (parent != null && edge != null)
|
||||
{
|
||||
if (parent != null && edge != null) {
|
||||
var parentToCellRankDifference = parent.maxRank - node.maxRank;
|
||||
|
||||
if (parentToCellRankDifference > 1)
|
||||
{
|
||||
if (parentToCellRankDifference > 1) {
|
||||
// There are ranks in between the parent and current cell
|
||||
edge.maxRank = parent.maxRank;
|
||||
edge.minRank = node.maxRank;
|
||||
|
@ -482,8 +441,7 @@ fixRanks = ()=>
|
|||
edge.x = [];
|
||||
edge.y = [];
|
||||
|
||||
for (var i = edge.minRank + 1; i < edge.maxRank; i++)
|
||||
{
|
||||
for (var i = edge.minRank + 1; i < edge.maxRank; i++) {
|
||||
// The connecting edge must be added to the
|
||||
// appropriate ranks
|
||||
rankList[i].push(edge);
|
||||
|
@ -506,33 +464,25 @@ fixRanks = ()=>
|
|||
* trackAncestors - Whether or not the search is to keep track all nodes
|
||||
* directly above this one in the search path.
|
||||
*/
|
||||
visit = (visitor, dfsRoots, trackAncestors, seenNodes)=>
|
||||
{
|
||||
visit = (visitor, dfsRoots, trackAncestors, seenNodes) => {
|
||||
// Run dfs through on all roots
|
||||
if (dfsRoots != null)
|
||||
{
|
||||
for (var i = 0; i < dfsRoots.length; i++)
|
||||
{
|
||||
if (dfsRoots != null) {
|
||||
for (var i = 0; i < dfsRoots.length; i++) {
|
||||
var internalNode = dfsRoots[i];
|
||||
|
||||
if (internalNode != null)
|
||||
{
|
||||
if (seenNodes == null)
|
||||
{
|
||||
if (internalNode != null) {
|
||||
if (seenNodes == null) {
|
||||
seenNodes = new Object();
|
||||
}
|
||||
|
||||
if (trackAncestors)
|
||||
{
|
||||
if (trackAncestors) {
|
||||
// Set up hash code for root
|
||||
internalNode.hashCode = [];
|
||||
internalNode.hashCode[0] = this.dfsCount;
|
||||
internalNode.hashCode[1] = i;
|
||||
this.extendedDfs(null, internalNode, null, visitor, seenNodes,
|
||||
internalNode.hashCode, i, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
this.dfs(null, internalNode, null, visitor, seenNodes, 0);
|
||||
}
|
||||
}
|
||||
|
@ -558,14 +508,11 @@ visit = (visitor, dfsRoots, trackAncestors, seenNodes)=>
|
|||
* ancestor node of the current node
|
||||
* layer - the layer on the dfs tree ( not the same as the model ranks )
|
||||
*/
|
||||
dfs = (parent, root, connectingEdge, visitor, seen, layer)=>
|
||||
{
|
||||
if (root != null)
|
||||
{
|
||||
dfs = (parent, root, connectingEdge, visitor, seen, layer) => {
|
||||
if (root != null) {
|
||||
var rootId = root.id;
|
||||
|
||||
if (seen[rootId] == null)
|
||||
{
|
||||
if (seen[rootId] == null) {
|
||||
seen[rootId] = root;
|
||||
visitor(parent, root, connectingEdge, layer, 0);
|
||||
|
||||
|
@ -573,8 +520,7 @@ dfs = (parent, root, connectingEdge, visitor, seen, layer)=>
|
|||
// can change the original for edge direction inversions
|
||||
var outgoingEdges = root.connectsAsSource.slice();
|
||||
|
||||
for (var i = 0; i< outgoingEdges.length; i++)
|
||||
{
|
||||
for (var i = 0; i < outgoingEdges.length; i++) {
|
||||
var internalEdge = outgoingEdges[i];
|
||||
var targetNode = internalEdge.target;
|
||||
|
||||
|
@ -582,9 +528,7 @@ dfs = (parent, root, connectingEdge, visitor, seen, layer)=>
|
|||
this.dfs(root, targetNode, internalEdge, visitor, seen,
|
||||
layer + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Use the int field to indicate this node has been seen
|
||||
visitor(parent, root, connectingEdge, layer, 1);
|
||||
}
|
||||
|
@ -611,8 +555,7 @@ dfs = (parent, root, connectingEdge, visitor, seen, layer)=>
|
|||
* childHash - the new hash code for this node
|
||||
* layer - the layer on the dfs tree ( not the same as the model ranks )
|
||||
*/
|
||||
extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash, layer)=>
|
||||
{
|
||||
extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash, layer) => {
|
||||
// Explanation of custom hash set. Previously, the ancestors variable
|
||||
// was passed through the dfs as a HashSet. The ancestors were copied
|
||||
// into a new HashSet and when the new child was processed it was also
|
||||
|
@ -633,18 +576,15 @@ extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash
|
|||
// through each run of the dfs algorithm (therefore the dfs is not
|
||||
// thread safe). The hash code of each node is set if not already set,
|
||||
// or if the first int does not match that of the current run.
|
||||
if (root != null)
|
||||
{
|
||||
if (parent != null)
|
||||
{
|
||||
if (root != null) {
|
||||
if (parent != null) {
|
||||
// Form this nodes hash code if necessary, that is, if the
|
||||
// hashCode variable has not been initialized or if the
|
||||
// start of the parent hash code does not equal the start of
|
||||
// this nodes hash code, indicating the code was set on a
|
||||
// previous run of this dfs.
|
||||
if (root.hashCode == null ||
|
||||
root.hashCode[0] != parent.hashCode[0])
|
||||
{
|
||||
root.hashCode[0] != parent.hashCode[0]) {
|
||||
var hashCodeLength = parent.hashCode.length + 1;
|
||||
root.hashCode = parent.hashCode.slice();
|
||||
root.hashCode[hashCodeLength - 1] = childHash;
|
||||
|
@ -653,8 +593,7 @@ extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash
|
|||
|
||||
var rootId = root.id;
|
||||
|
||||
if (seen[rootId] == null)
|
||||
{
|
||||
if (seen[rootId] == null) {
|
||||
seen[rootId] = root;
|
||||
visitor(parent, root, connectingEdge, layer, 0);
|
||||
|
||||
|
@ -662,8 +601,7 @@ extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash
|
|||
// can change the original for edge direction inversions
|
||||
var outgoingEdges = root.connectsAsSource.slice();
|
||||
|
||||
for (var i = 0; i < outgoingEdges.length; i++)
|
||||
{
|
||||
for (var i = 0; i < outgoingEdges.length; i++) {
|
||||
var internalEdge = outgoingEdges[i];
|
||||
var targetNode = internalEdge.target;
|
||||
|
||||
|
@ -671,11 +609,12 @@ extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash
|
|||
this.extendedDfs(root, targetNode, internalEdge, visitor, seen,
|
||||
root.hashCode, i, layer + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Use the int field to indicate this node has been seen
|
||||
visitor(parent, root, connectingEdge, layer, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default mxGraphHierarchyModel;
|
||||
|
|
|
@ -15,21 +15,15 @@
|
|||
*
|
||||
* cell - the real graph cell this node represents
|
||||
*/
|
||||
function mxGraphHierarchyNode(cell)
|
||||
{
|
||||
mxGraphAbstractHierarchyCell.apply(this, arguments);
|
||||
class mxGraphHierarchyNode extends mxGraphAbstractHierarchyCell {
|
||||
constructor(cell) {
|
||||
super(cell);
|
||||
this.cell = cell;
|
||||
this.id = mxObjectIdentity.get(cell);
|
||||
this.connectsAsTarget = [];
|
||||
this.connectsAsSource = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Extends mxGraphAbstractHierarchyCell.
|
||||
*/
|
||||
mxGraphHierarchyNode.prototype = new mxGraphAbstractHierarchyCell();
|
||||
constructor = mxGraphHierarchyNode;
|
||||
|
||||
/**
|
||||
* Variable: cell
|
||||
*
|
||||
|
@ -71,8 +65,7 @@ hashCode = false;
|
|||
*
|
||||
* Returns the integer value of the layer that this node resides in
|
||||
*/
|
||||
getRankValue = (layer)=>
|
||||
{
|
||||
getRankValue = (layer) => {
|
||||
return this.maxRank;
|
||||
};
|
||||
|
||||
|
@ -81,25 +74,19 @@ getRankValue = (layer)=>
|
|||
*
|
||||
* Returns the cells this cell connects to on the next layer up
|
||||
*/
|
||||
getNextLayerConnectedCells = (layer)=>
|
||||
{
|
||||
if (this.nextLayerConnectedCells == null)
|
||||
{
|
||||
getNextLayerConnectedCells = (layer) => {
|
||||
if (this.nextLayerConnectedCells == null) {
|
||||
this.nextLayerConnectedCells = [];
|
||||
this.nextLayerConnectedCells[0] = [];
|
||||
|
||||
for (var i = 0; i < this.connectsAsTarget.length; i++)
|
||||
{
|
||||
for (var i = 0; i < this.connectsAsTarget.length; i++) {
|
||||
var edge = this.connectsAsTarget[i];
|
||||
|
||||
if (edge.maxRank == -1 || edge.maxRank == layer + 1)
|
||||
{
|
||||
if (edge.maxRank == -1 || edge.maxRank == layer + 1) {
|
||||
// Either edge is not in any rank or
|
||||
// no dummy nodes in edge, add node of other side of edge
|
||||
this.nextLayerConnectedCells[0].push(edge.source);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Edge spans at least two layers, add edge
|
||||
this.nextLayerConnectedCells[0].push(edge);
|
||||
}
|
||||
|
@ -114,24 +101,18 @@ getNextLayerConnectedCells = (layer)=>
|
|||
*
|
||||
* Returns the cells this cell connects to on the next layer down
|
||||
*/
|
||||
getPreviousLayerConnectedCells = (layer)=>
|
||||
{
|
||||
if (this.previousLayerConnectedCells == null)
|
||||
{
|
||||
getPreviousLayerConnectedCells = (layer) => {
|
||||
if (this.previousLayerConnectedCells == null) {
|
||||
this.previousLayerConnectedCells = [];
|
||||
this.previousLayerConnectedCells[0] = [];
|
||||
|
||||
for (var i = 0; i < this.connectsAsSource.length; i++)
|
||||
{
|
||||
for (var i = 0; i < this.connectsAsSource.length; i++) {
|
||||
var edge = this.connectsAsSource[i];
|
||||
|
||||
if (edge.minRank == -1 || edge.minRank == layer - 1)
|
||||
{
|
||||
if (edge.minRank == -1 || edge.minRank == layer - 1) {
|
||||
// No dummy nodes in edge, add node of other side of edge
|
||||
this.previousLayerConnectedCells[0].push(edge.target);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Edge spans at least two layers, add edge
|
||||
this.previousLayerConnectedCells[0].push(edge);
|
||||
}
|
||||
|
@ -146,8 +127,7 @@ getPreviousLayerConnectedCells = (layer)=>
|
|||
*
|
||||
* Returns true.
|
||||
*/
|
||||
isVertex = ()=>
|
||||
{
|
||||
isVertex = () => {
|
||||
return true;
|
||||
};
|
||||
|
||||
|
@ -156,8 +136,7 @@ isVertex = ()=>
|
|||
*
|
||||
* Gets the value of temp for the specified layer
|
||||
*/
|
||||
getGeneralPurposeVariable = (layer)=>
|
||||
{
|
||||
getGeneralPurposeVariable = (layer) => {
|
||||
return this.temp[0];
|
||||
};
|
||||
|
||||
|
@ -166,28 +145,23 @@ getGeneralPurposeVariable = (layer)=>
|
|||
*
|
||||
* Set the value of temp for the specified layer
|
||||
*/
|
||||
setGeneralPurposeVariable = (layer, value)=>
|
||||
{
|
||||
setGeneralPurposeVariable = (layer, value) => {
|
||||
this.temp[0] = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Function: isAncestor
|
||||
*/
|
||||
isAncestor = (otherNode)=>
|
||||
{
|
||||
isAncestor = (otherNode) => {
|
||||
// Firstly, the hash code of this node needs to be shorter than the
|
||||
// other node
|
||||
if (otherNode != null && this.hashCode != null && otherNode.hashCode != null
|
||||
&& this.hashCode.length < otherNode.hashCode.length)
|
||||
{
|
||||
if (this.hashCode == otherNode.hashCode)
|
||||
{
|
||||
&& this.hashCode.length < otherNode.hashCode.length) {
|
||||
if (this.hashCode == otherNode.hashCode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.hashCode == null || this.hashCode == null)
|
||||
{
|
||||
if (this.hashCode == null || this.hashCode == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -195,10 +169,8 @@ isAncestor = (otherNode)=>
|
|||
// node's hash code. Arrays.equals cannot be used here since
|
||||
// the arrays are different length, and we do not want to
|
||||
// perform another array copy.
|
||||
for (var i = 0; i < this.hashCode.length; i++)
|
||||
{
|
||||
if (this.hashCode[i] != otherNode.hashCode[i])
|
||||
{
|
||||
for (var i = 0; i < this.hashCode.length; i++) {
|
||||
if (this.hashCode[i] != otherNode.hashCode[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -214,7 +186,9 @@ isAncestor = (otherNode)=>
|
|||
*
|
||||
* Gets the core vertex associated with this wrapper
|
||||
*/
|
||||
getCoreCell = ()=>
|
||||
{
|
||||
getCoreCell = () => {
|
||||
return this.cell;
|
||||
};
|
||||
}
|
||||
|
||||
export default mxGraphHierarchyNode;
|
||||
|
|
|
@ -2,111 +2,8 @@
|
|||
* Copyright (c) 2006-2018, JGraph Ltd
|
||||
* Copyright (c) 2006-2018, Gaudenz Alder
|
||||
*/
|
||||
/**
|
||||
* Class: mxSwimlaneModel
|
||||
*
|
||||
* Internal model of a hierarchical graph. This model stores nodes and edges
|
||||
* equivalent to the real graph nodes and edges, but also stores the rank of the
|
||||
* cells, the order within the ranks and the new candidate locations of cells.
|
||||
* The internal model also reverses edge direction were appropriate , ignores
|
||||
* self-loop and groups parallels together under one edge object.
|
||||
*
|
||||
* Constructor: mxSwimlaneModel
|
||||
*
|
||||
* Creates an internal ordered graph model using the vertices passed in. If
|
||||
* there are any, leftward edge need to be inverted in the internal model
|
||||
*
|
||||
* Arguments:
|
||||
*
|
||||
* graph - the facade describing the graph to be operated on
|
||||
* vertices - the vertices for this hierarchy
|
||||
* ordered - whether or not the vertices are already ordered
|
||||
* deterministic - whether or not this layout should be deterministic on each
|
||||
* tightenToSource - whether or not to tighten vertices towards the sources
|
||||
* scanRanksFromSinks - Whether rank assignment is from the sinks or sources.
|
||||
* usage
|
||||
*/
|
||||
function mxSwimlaneModel(layout, vertices, roots, parent, tightenToSource)
|
||||
{
|
||||
var graph = layout.getGraph();
|
||||
this.tightenToSource = tightenToSource;
|
||||
this.roots = roots;
|
||||
this.parent = parent;
|
||||
|
||||
// map of cells to internal cell needed for second run through
|
||||
// to setup the sink of edges correctly
|
||||
this.vertexMapper = new mxDictionary();
|
||||
this.edgeMapper = new mxDictionary();
|
||||
this.maxRank = 0;
|
||||
var internalVertices = [];
|
||||
|
||||
if (vertices == null)
|
||||
{
|
||||
vertices = this.graph.getChildVertices(parent);
|
||||
}
|
||||
|
||||
this.maxRank = this.SOURCESCANSTARTRANK;
|
||||
// map of cells to internal cell needed for second run through
|
||||
// to setup the sink of edges correctly. Guess size by number
|
||||
// of edges is roughly same as number of vertices.
|
||||
this.createInternalCells(layout, vertices, internalVertices);
|
||||
|
||||
// Go through edges set their sink values. Also check the
|
||||
// ordering if and invert edges if necessary
|
||||
for (var i = 0; i < vertices.length; i++)
|
||||
{
|
||||
var edges = internalVertices[i].connectsAsSource;
|
||||
|
||||
for (var j = 0; j < edges.length; j++)
|
||||
{
|
||||
var internalEdge = edges[j];
|
||||
var realEdges = internalEdge.edges;
|
||||
|
||||
// Only need to process the first real edge, since
|
||||
// all the edges connect to the same other vertex
|
||||
if (realEdges != null && realEdges.length > 0)
|
||||
{
|
||||
var realEdge = realEdges[0];
|
||||
var targetCell = layout.getVisibleTerminal(
|
||||
realEdge, false);
|
||||
var internalTargetCell = this.vertexMapper.get(targetCell);
|
||||
|
||||
if (internalVertices[i] == internalTargetCell)
|
||||
{
|
||||
// If there are parallel edges going between two vertices and not all are in the same direction
|
||||
// you can have navigated across one direction when doing the cycle reversal that isn't the same
|
||||
// direction as the first real edge in the array above. When that happens the if above catches
|
||||
// that and we correct the target cell before continuing.
|
||||
// This branch only detects this single case
|
||||
targetCell = layout.getVisibleTerminal(
|
||||
realEdge, true);
|
||||
internalTargetCell = this.vertexMapper.get(targetCell);
|
||||
}
|
||||
|
||||
if (internalTargetCell != null
|
||||
&& internalVertices[i] != internalTargetCell)
|
||||
{
|
||||
internalEdge.target = internalTargetCell;
|
||||
|
||||
if (internalTargetCell.connectsAsTarget.length == 0)
|
||||
{
|
||||
internalTargetCell.connectsAsTarget = [];
|
||||
}
|
||||
|
||||
if (mxUtils.indexOf(internalTargetCell.connectsAsTarget, internalEdge) < 0)
|
||||
{
|
||||
internalTargetCell.connectsAsTarget.push(internalEdge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the temp variable in the internal nodes to mark this
|
||||
// internal vertex as having been visited.
|
||||
internalVertices[i].temp[0] = 1;
|
||||
}
|
||||
};
|
||||
|
||||
class mxSwimlaneModel {
|
||||
/**
|
||||
* Variable: maxRank
|
||||
*
|
||||
|
@ -179,6 +76,102 @@ tightenToSource = false;
|
|||
*/
|
||||
ranksPerGroup = null;
|
||||
|
||||
/**
|
||||
* Class: mxSwimlaneModel
|
||||
*
|
||||
* Internal model of a hierarchical graph. This model stores nodes and edges
|
||||
* equivalent to the real graph nodes and edges, but also stores the rank of the
|
||||
* cells, the order within the ranks and the new candidate locations of cells.
|
||||
* The internal model also reverses edge direction were appropriate , ignores
|
||||
* self-loop and groups parallels together under one edge object.
|
||||
*
|
||||
* Constructor: mxSwimlaneModel
|
||||
*
|
||||
* Creates an internal ordered graph model using the vertices passed in. If
|
||||
* there are any, leftward edge need to be inverted in the internal model
|
||||
*
|
||||
* Arguments:
|
||||
*
|
||||
* graph - the facade describing the graph to be operated on
|
||||
* vertices - the vertices for this hierarchy
|
||||
* ordered - whether or not the vertices are already ordered
|
||||
* deterministic - whether or not this layout should be deterministic on each
|
||||
* tightenToSource - whether or not to tighten vertices towards the sources
|
||||
* scanRanksFromSinks - Whether rank assignment is from the sinks or sources.
|
||||
* usage
|
||||
*/
|
||||
constructor(layout, vertices, roots, parent, tightenToSource) {
|
||||
var graph = layout.getGraph();
|
||||
this.tightenToSource = tightenToSource;
|
||||
this.roots = roots;
|
||||
this.parent = parent;
|
||||
|
||||
// map of cells to internal cell needed for second run through
|
||||
// to setup the sink of edges correctly
|
||||
this.vertexMapper = new mxDictionary();
|
||||
this.edgeMapper = new mxDictionary();
|
||||
this.maxRank = 0;
|
||||
var internalVertices = [];
|
||||
|
||||
if (vertices == null) {
|
||||
vertices = this.graph.getChildVertices(parent);
|
||||
}
|
||||
|
||||
this.maxRank = this.SOURCESCANSTARTRANK;
|
||||
// map of cells to internal cell needed for second run through
|
||||
// to setup the sink of edges correctly. Guess size by number
|
||||
// of edges is roughly same as number of vertices.
|
||||
this.createInternalCells(layout, vertices, internalVertices);
|
||||
|
||||
// Go through edges set their sink values. Also check the
|
||||
// ordering if and invert edges if necessary
|
||||
for (var i = 0; i < vertices.length; i++) {
|
||||
var edges = internalVertices[i].connectsAsSource;
|
||||
|
||||
for (var j = 0; j < edges.length; j++) {
|
||||
var internalEdge = edges[j];
|
||||
var realEdges = internalEdge.edges;
|
||||
|
||||
// Only need to process the first real edge, since
|
||||
// all the edges connect to the same other vertex
|
||||
if (realEdges != null && realEdges.length > 0) {
|
||||
var realEdge = realEdges[0];
|
||||
var targetCell = layout.getVisibleTerminal(
|
||||
realEdge, false);
|
||||
var internalTargetCell = this.vertexMapper.get(targetCell);
|
||||
|
||||
if (internalVertices[i] == internalTargetCell) {
|
||||
// If there are parallel edges going between two vertices and not all are in the same direction
|
||||
// you can have navigated across one direction when doing the cycle reversal that isn't the same
|
||||
// direction as the first real edge in the array above. When that happens the if above catches
|
||||
// that and we correct the target cell before continuing.
|
||||
// This branch only detects this single case
|
||||
targetCell = layout.getVisibleTerminal(
|
||||
realEdge, true);
|
||||
internalTargetCell = this.vertexMapper.get(targetCell);
|
||||
}
|
||||
|
||||
if (internalTargetCell != null
|
||||
&& internalVertices[i] != internalTargetCell) {
|
||||
internalEdge.target = internalTargetCell;
|
||||
|
||||
if (internalTargetCell.connectsAsTarget.length == 0) {
|
||||
internalTargetCell.connectsAsTarget = [];
|
||||
}
|
||||
|
||||
if (mxUtils.indexOf(internalTargetCell.connectsAsTarget, internalEdge) < 0) {
|
||||
internalTargetCell.connectsAsTarget.push(internalEdge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the temp variable in the internal nodes to mark this
|
||||
// internal vertex as having been visited.
|
||||
internalVertices[i].temp[0] = 1;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Function: createInternalCells
|
||||
*
|
||||
|
@ -192,22 +185,18 @@ ranksPerGroup = null;
|
|||
* internalVertices - The array of <mxGraphHierarchyNodes> to have their
|
||||
* information filled in using the real vertices.
|
||||
*/
|
||||
createInternalCells = (layout, vertices, internalVertices)=>
|
||||
{
|
||||
createInternalCells = (layout, vertices, internalVertices) => {
|
||||
var graph = layout.getGraph();
|
||||
var swimlanes = layout.swimlanes;
|
||||
|
||||
// Create internal edges
|
||||
for (var i = 0; i < vertices.length; i++)
|
||||
{
|
||||
for (var i = 0; i < vertices.length; i++) {
|
||||
internalVertices[i] = new mxGraphHierarchyNode(vertices[i]);
|
||||
this.vertexMapper.put(vertices[i], internalVertices[i]);
|
||||
internalVertices[i].swimlaneIndex = -1;
|
||||
|
||||
for (var ii = 0; ii < swimlanes.length; ii++)
|
||||
{
|
||||
if (graph.model.getParent(vertices[i]) == swimlanes[ii])
|
||||
{
|
||||
for (var ii = 0; ii < swimlanes.length; ii++) {
|
||||
if (graph.model.getParent(vertices[i]) == swimlanes[ii]) {
|
||||
internalVertices[i].swimlaneIndex = ii;
|
||||
break;
|
||||
}
|
||||
|
@ -221,14 +210,12 @@ createInternalCells = (layout, vertices, internalVertices)=>
|
|||
// Create internal edges, but don't do any rank assignment yet
|
||||
// First use the information from the greedy cycle remover to
|
||||
// invert the leftward edges internally
|
||||
for (var j = 0; j < conns.length; j++)
|
||||
{
|
||||
for (var j = 0; j < conns.length; j++) {
|
||||
var cell = layout.getVisibleTerminal(conns[j], false);
|
||||
|
||||
// Looking for outgoing edges only
|
||||
if (cell != vertices[i] && layout.graph.model.isVertex(cell) &&
|
||||
!layout.isVertexIgnored(cell))
|
||||
{
|
||||
!layout.isVertexIgnored(cell)) {
|
||||
// We process all edge between this source and its targets
|
||||
// If there are edges going both ways, we need to collect
|
||||
// them all into one internal edges to avoid looping problems
|
||||
|
@ -250,12 +237,10 @@ createInternalCells = (layout, vertices, internalVertices)=>
|
|||
if (undirectedEdges != null &&
|
||||
undirectedEdges.length > 0 &&
|
||||
this.edgeMapper.get(undirectedEdges[0]) == null &&
|
||||
directedEdges.length * 2 >= undirectedEdges.length)
|
||||
{
|
||||
directedEdges.length * 2 >= undirectedEdges.length) {
|
||||
var internalEdge = new mxGraphHierarchyEdge(undirectedEdges);
|
||||
|
||||
for (var k = 0; k < undirectedEdges.length; k++)
|
||||
{
|
||||
for (var k = 0; k < undirectedEdges.length; k++) {
|
||||
var edge = undirectedEdges[k];
|
||||
this.edgeMapper.put(edge, internalEdge);
|
||||
|
||||
|
@ -263,8 +248,7 @@ createInternalCells = (layout, vertices, internalVertices)=>
|
|||
// without deleting it from the cell style
|
||||
graph.resetEdge(edge);
|
||||
|
||||
if (layout.disableEdgeStyle)
|
||||
{
|
||||
if (layout.disableEdgeStyle) {
|
||||
layout.setEdgeStyleEnabled(edge, false);
|
||||
layout.setOrthogonalEdge(edge, true);
|
||||
}
|
||||
|
@ -272,8 +256,7 @@ createInternalCells = (layout, vertices, internalVertices)=>
|
|||
|
||||
internalEdge.source = internalVertices[i];
|
||||
|
||||
if (mxUtils.indexOf(internalVertices[i].connectsAsSource, internalEdge) < 0)
|
||||
{
|
||||
if (mxUtils.indexOf(internalVertices[i].connectsAsSource, internalEdge) < 0) {
|
||||
internalVertices[i].connectsAsSource.push(internalEdge);
|
||||
}
|
||||
}
|
||||
|
@ -292,22 +275,18 @@ createInternalCells = (layout, vertices, internalVertices)=>
|
|||
* or sinks and working through each node in the relevant edge direction.
|
||||
* Starting at the sinks is basically a longest path layering algorithm.
|
||||
*/
|
||||
initialRank = ()=>
|
||||
{
|
||||
initialRank = () => {
|
||||
this.ranksPerGroup = [];
|
||||
|
||||
var startNodes = [];
|
||||
var seen = new Object();
|
||||
|
||||
if (this.roots != null)
|
||||
{
|
||||
for (var i = 0; i < this.roots.length; i++)
|
||||
{
|
||||
if (this.roots != null) {
|
||||
for (var i = 0; i < this.roots.length; i++) {
|
||||
var internalNode = this.vertexMapper.get(this.roots[i]);
|
||||
this.maxChainDfs(null, internalNode, null, seen, 0);
|
||||
|
||||
if (internalNode != null)
|
||||
{
|
||||
if (internalNode != null) {
|
||||
startNodes.push(internalNode);
|
||||
}
|
||||
}
|
||||
|
@ -317,14 +296,10 @@ initialRank = ()=>
|
|||
var lowerRank = [];
|
||||
var upperRank = [];
|
||||
|
||||
for (var i = this.ranksPerGroup.length - 1; i >= 0; i--)
|
||||
{
|
||||
if (i == this.ranksPerGroup.length - 1)
|
||||
{
|
||||
for (var i = this.ranksPerGroup.length - 1; i >= 0; i--) {
|
||||
if (i == this.ranksPerGroup.length - 1) {
|
||||
lowerRank[i] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
lowerRank[i] = upperRank[i + 1] + 1;
|
||||
}
|
||||
|
||||
|
@ -335,16 +310,14 @@ initialRank = ()=>
|
|||
|
||||
var internalNodes = this.vertexMapper.getValues();
|
||||
|
||||
for (var i=0; i < internalNodes.length; i++)
|
||||
{
|
||||
for (var i = 0; i < internalNodes.length; i++) {
|
||||
// Mark the node as not having had a layer assigned
|
||||
internalNodes[i].temp[0] = -1;
|
||||
}
|
||||
|
||||
var startNodesCopy = startNodes.slice();
|
||||
|
||||
while (startNodes.length > 0)
|
||||
{
|
||||
while (startNodes.length > 0) {
|
||||
var internalNode = startNodes[0];
|
||||
var layerDeterminingEdges;
|
||||
var edgesToBeMarked;
|
||||
|
@ -361,19 +334,15 @@ initialRank = ()=>
|
|||
// the layer determining edges variable
|
||||
var minimumLayer = upperRank[0];
|
||||
|
||||
for (var i = 0; i < layerDeterminingEdges.length; i++)
|
||||
{
|
||||
for (var i = 0; i < layerDeterminingEdges.length; i++) {
|
||||
var internalEdge = layerDeterminingEdges[i];
|
||||
|
||||
if (internalEdge.temp[0] == 5270620)
|
||||
{
|
||||
if (internalEdge.temp[0] == 5270620) {
|
||||
// This edge has been scanned, get the layer of the
|
||||
// node on the other end
|
||||
var otherNode = internalEdge.source;
|
||||
minimumLayer = Math.min(minimumLayer, otherNode.temp[0] - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
allEdgesScanned = false;
|
||||
|
||||
break;
|
||||
|
@ -382,19 +351,15 @@ initialRank = ()=>
|
|||
|
||||
// If all edge have been scanned, assign the layer, mark all
|
||||
// edges in the other direction and remove from the nodes list
|
||||
if (allEdgesScanned)
|
||||
{
|
||||
if (minimumLayer > upperRank[internalNode.swimlaneIndex])
|
||||
{
|
||||
if (allEdgesScanned) {
|
||||
if (minimumLayer > upperRank[internalNode.swimlaneIndex]) {
|
||||
minimumLayer = upperRank[internalNode.swimlaneIndex];
|
||||
}
|
||||
|
||||
internalNode.temp[0] = minimumLayer;
|
||||
|
||||
if (edgesToBeMarked != null)
|
||||
{
|
||||
for (var i = 0; i < edgesToBeMarked.length; i++)
|
||||
{
|
||||
if (edgesToBeMarked != null) {
|
||||
for (var i = 0; i < edgesToBeMarked.length; i++) {
|
||||
var internalEdge = edgesToBeMarked[i];
|
||||
|
||||
// Assign unique stamp ( y/m/d/h )
|
||||
|
@ -405,8 +370,7 @@ initialRank = ()=>
|
|||
var otherNode = internalEdge.target;
|
||||
|
||||
// Only add node if it hasn't been assigned a layer
|
||||
if (otherNode.temp[0] == -1)
|
||||
{
|
||||
if (otherNode.temp[0] == -1) {
|
||||
startNodes.push(otherNode);
|
||||
|
||||
// Mark this other node as neither being
|
||||
|
@ -419,16 +383,13 @@ initialRank = ()=>
|
|||
}
|
||||
|
||||
startNodes.shift();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Not all the edges have been scanned, get to the back of
|
||||
// the class and put the dunces cap on
|
||||
var removedCell = startNodes.shift();
|
||||
startNodes.push(internalNode);
|
||||
|
||||
if (removedCell == internalNode && startNodes.length == 1)
|
||||
{
|
||||
if (removedCell == internalNode && startNodes.length == 1) {
|
||||
// This is an error condition, we can't get out of
|
||||
// this loop. It could happen for more than one node
|
||||
// but that's a lot harder to detect. Log the error
|
||||
|
@ -483,19 +444,15 @@ initialRank = ()=>
|
|||
* chainCount - the number of edges in the chain of vertices going through
|
||||
* the current swimlane
|
||||
*/
|
||||
maxChainDfs = (parent, root, connectingEdge, seen, chainCount)=>
|
||||
{
|
||||
if (root != null)
|
||||
{
|
||||
maxChainDfs = (parent, root, connectingEdge, seen, chainCount) => {
|
||||
if (root != null) {
|
||||
var rootId = mxCellPath.create(root.cell);
|
||||
|
||||
if (seen[rootId] == null)
|
||||
{
|
||||
if (seen[rootId] == null) {
|
||||
seen[rootId] = root;
|
||||
var slIndex = root.swimlaneIndex;
|
||||
|
||||
if (this.ranksPerGroup[slIndex] == null || this.ranksPerGroup[slIndex] < chainCount)
|
||||
{
|
||||
if (this.ranksPerGroup[slIndex] == null || this.ranksPerGroup[slIndex] < chainCount) {
|
||||
this.ranksPerGroup[slIndex] = chainCount;
|
||||
}
|
||||
|
||||
|
@ -503,19 +460,15 @@ maxChainDfs = (parent, root, connectingEdge, seen, chainCount)=>
|
|||
// can change the original for edge direction inversions
|
||||
var outgoingEdges = root.connectsAsSource.slice();
|
||||
|
||||
for (var i = 0; i < outgoingEdges.length; i++)
|
||||
{
|
||||
for (var i = 0; i < outgoingEdges.length; i++) {
|
||||
var internalEdge = outgoingEdges[i];
|
||||
var targetNode = internalEdge.target;
|
||||
|
||||
// Only navigate in source->target direction within the same
|
||||
// swimlane, or from a lower index swimlane to a higher one
|
||||
if (root.swimlaneIndex < targetNode.swimlaneIndex)
|
||||
{
|
||||
if (root.swimlaneIndex < targetNode.swimlaneIndex) {
|
||||
this.maxChainDfs(root, targetNode, internalEdge, mxUtils.clone(seen, null, true), 0);
|
||||
}
|
||||
else if (root.swimlaneIndex == targetNode.swimlaneIndex)
|
||||
{
|
||||
} else if (root.swimlaneIndex == targetNode.swimlaneIndex) {
|
||||
this.maxChainDfs(root, targetNode, internalEdge, mxUtils.clone(seen, null, true), chainCount + 1);
|
||||
}
|
||||
}
|
||||
|
@ -529,13 +482,11 @@ maxChainDfs = (parent, root, connectingEdge, seen, chainCount)=>
|
|||
* Fixes the layer assignments to the values stored in the nodes. Also needs
|
||||
* to create dummy nodes for edges that cross layers.
|
||||
*/
|
||||
fixRanks = ()=>
|
||||
{
|
||||
fixRanks = () => {
|
||||
var rankList = [];
|
||||
this.ranks = [];
|
||||
|
||||
for (var i = 0; i < this.maxRank + 1; i++)
|
||||
{
|
||||
for (var i = 0; i < this.maxRank + 1; i++) {
|
||||
rankList[i] = [];
|
||||
this.ranks[i] = rankList[i];
|
||||
}
|
||||
|
@ -545,23 +496,19 @@ fixRanks = ()=>
|
|||
// crossings for a standard tree.
|
||||
var rootsArray = null;
|
||||
|
||||
if (this.roots != null)
|
||||
{
|
||||
if (this.roots != null) {
|
||||
var oldRootsArray = this.roots;
|
||||
rootsArray = [];
|
||||
|
||||
for (var i = 0; i < oldRootsArray.length; i++)
|
||||
{
|
||||
for (var i = 0; i < oldRootsArray.length; i++) {
|
||||
var cell = oldRootsArray[i];
|
||||
var internalNode = this.vertexMapper.get(cell);
|
||||
rootsArray[i] = internalNode;
|
||||
}
|
||||
}
|
||||
|
||||
this.visit((parent, node, edge, layer, seen)=>
|
||||
{
|
||||
if (seen == 0 && node.maxRank < 0 && node.minRank < 0)
|
||||
{
|
||||
this.visit((parent, node, edge, layer, seen) => {
|
||||
if (seen == 0 && node.maxRank < 0 && node.minRank < 0) {
|
||||
rankList[node.temp[0]].push(node);
|
||||
node.maxRank = node.temp[0];
|
||||
node.minRank = node.temp[0];
|
||||
|
@ -570,12 +517,10 @@ fixRanks = ()=>
|
|||
node.temp[0] = rankList[node.maxRank].length - 1;
|
||||
}
|
||||
|
||||
if (parent != null && edge != null)
|
||||
{
|
||||
if (parent != null && edge != null) {
|
||||
var parentToCellRankDifference = parent.maxRank - node.maxRank;
|
||||
|
||||
if (parentToCellRankDifference > 1)
|
||||
{
|
||||
if (parentToCellRankDifference > 1) {
|
||||
// There are ranks in between the parent and current cell
|
||||
edge.maxRank = parent.maxRank;
|
||||
edge.minRank = node.maxRank;
|
||||
|
@ -583,8 +528,7 @@ fixRanks = ()=>
|
|||
edge.x = [];
|
||||
edge.y = [];
|
||||
|
||||
for (var i = edge.minRank + 1; i < edge.maxRank; i++)
|
||||
{
|
||||
for (var i = edge.minRank + 1; i < edge.maxRank; i++) {
|
||||
// The connecting edge must be added to the
|
||||
// appropriate ranks
|
||||
rankList[i].push(edge);
|
||||
|
@ -607,33 +551,25 @@ fixRanks = ()=>
|
|||
* trackAncestors - Whether or not the search is to keep track all nodes
|
||||
* directly above this one in the search path.
|
||||
*/
|
||||
visit = (visitor, dfsRoots, trackAncestors, seenNodes)=>
|
||||
{
|
||||
visit = (visitor, dfsRoots, trackAncestors, seenNodes) => {
|
||||
// Run dfs through on all roots
|
||||
if (dfsRoots != null)
|
||||
{
|
||||
for (var i = 0; i < dfsRoots.length; i++)
|
||||
{
|
||||
if (dfsRoots != null) {
|
||||
for (var i = 0; i < dfsRoots.length; i++) {
|
||||
var internalNode = dfsRoots[i];
|
||||
|
||||
if (internalNode != null)
|
||||
{
|
||||
if (seenNodes == null)
|
||||
{
|
||||
if (internalNode != null) {
|
||||
if (seenNodes == null) {
|
||||
seenNodes = new Object();
|
||||
}
|
||||
|
||||
if (trackAncestors)
|
||||
{
|
||||
if (trackAncestors) {
|
||||
// Set up hash code for root
|
||||
internalNode.hashCode = [];
|
||||
internalNode.hashCode[0] = this.dfsCount;
|
||||
internalNode.hashCode[1] = i;
|
||||
this.extendedDfs(null, internalNode, null, visitor, seenNodes,
|
||||
internalNode.hashCode, i, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
this.dfs(null, internalNode, null, visitor, seenNodes, 0);
|
||||
}
|
||||
}
|
||||
|
@ -659,14 +595,11 @@ visit = (visitor, dfsRoots, trackAncestors, seenNodes)=>
|
|||
* ancestor node of the current node
|
||||
* layer - the layer on the dfs tree ( not the same as the model ranks )
|
||||
*/
|
||||
dfs = (parent, root, connectingEdge, visitor, seen, layer)=>
|
||||
{
|
||||
if (root != null)
|
||||
{
|
||||
dfs = (parent, root, connectingEdge, visitor, seen, layer) => {
|
||||
if (root != null) {
|
||||
var rootId = root.id;
|
||||
|
||||
if (seen[rootId] == null)
|
||||
{
|
||||
if (seen[rootId] == null) {
|
||||
seen[rootId] = root;
|
||||
visitor(parent, root, connectingEdge, layer, 0);
|
||||
|
||||
|
@ -674,8 +607,7 @@ dfs = (parent, root, connectingEdge, visitor, seen, layer)=>
|
|||
// can change the original for edge direction inversions
|
||||
var outgoingEdges = root.connectsAsSource.slice();
|
||||
|
||||
for (var i = 0; i< outgoingEdges.length; i++)
|
||||
{
|
||||
for (var i = 0; i < outgoingEdges.length; i++) {
|
||||
var internalEdge = outgoingEdges[i];
|
||||
var targetNode = internalEdge.target;
|
||||
|
||||
|
@ -683,9 +615,7 @@ dfs = (parent, root, connectingEdge, visitor, seen, layer)=>
|
|||
this.dfs(root, targetNode, internalEdge, visitor, seen,
|
||||
layer + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Use the int field to indicate this node has been seen
|
||||
visitor(parent, root, connectingEdge, layer, 1);
|
||||
}
|
||||
|
@ -712,8 +642,7 @@ dfs = (parent, root, connectingEdge, visitor, seen, layer)=>
|
|||
* childHash - the new hash code for this node
|
||||
* layer - the layer on the dfs tree ( not the same as the model ranks )
|
||||
*/
|
||||
extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash, layer)=>
|
||||
{
|
||||
extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash, layer) => {
|
||||
// Explanation of custom hash set. Previously, the ancestors variable
|
||||
// was passed through the dfs as a HashSet. The ancestors were copied
|
||||
// into a new HashSet and when the new child was processed it was also
|
||||
|
@ -734,18 +663,15 @@ extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash
|
|||
// through each run of the dfs algorithm (therefore the dfs is not
|
||||
// thread safe). The hash code of each node is set if not already set,
|
||||
// or if the first int does not match that of the current run.
|
||||
if (root != null)
|
||||
{
|
||||
if (parent != null)
|
||||
{
|
||||
if (root != null) {
|
||||
if (parent != null) {
|
||||
// Form this nodes hash code if necessary, that is, if the
|
||||
// hashCode variable has not been initialized or if the
|
||||
// start of the parent hash code does not equal the start of
|
||||
// this nodes hash code, indicating the code was set on a
|
||||
// previous run of this dfs.
|
||||
if (root.hashCode == null ||
|
||||
root.hashCode[0] != parent.hashCode[0])
|
||||
{
|
||||
root.hashCode[0] != parent.hashCode[0]) {
|
||||
var hashCodeLength = parent.hashCode.length + 1;
|
||||
root.hashCode = parent.hashCode.slice();
|
||||
root.hashCode[hashCodeLength - 1] = childHash;
|
||||
|
@ -754,8 +680,7 @@ extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash
|
|||
|
||||
var rootId = root.id;
|
||||
|
||||
if (seen[rootId] == null)
|
||||
{
|
||||
if (seen[rootId] == null) {
|
||||
seen[rootId] = root;
|
||||
visitor(parent, root, connectingEdge, layer, 0);
|
||||
|
||||
|
@ -764,38 +689,35 @@ extendedDfs = (parent, root, connectingEdge, visitor, seen, ancestors, childHash
|
|||
var outgoingEdges = root.connectsAsSource.slice();
|
||||
var incomingEdges = root.connectsAsTarget.slice();
|
||||
|
||||
for (var i = 0; i < outgoingEdges.length; i++)
|
||||
{
|
||||
for (var i = 0; i < outgoingEdges.length; i++) {
|
||||
var internalEdge = outgoingEdges[i];
|
||||
var targetNode = internalEdge.target;
|
||||
|
||||
// Only navigate in source->target direction within the same
|
||||
// swimlane, or from a lower index swimlane to a higher one
|
||||
if (root.swimlaneIndex <= targetNode.swimlaneIndex)
|
||||
{
|
||||
if (root.swimlaneIndex <= targetNode.swimlaneIndex) {
|
||||
this.extendedDfs(root, targetNode, internalEdge, visitor, seen,
|
||||
root.hashCode, i, layer + 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < incomingEdges.length; i++)
|
||||
{
|
||||
for (var i = 0; i < incomingEdges.length; i++) {
|
||||
var internalEdge = incomingEdges[i];
|
||||
var targetNode = internalEdge.source;
|
||||
|
||||
// Only navigate in target->source direction from a lower index
|
||||
// swimlane to a higher one
|
||||
if (root.swimlaneIndex < targetNode.swimlaneIndex)
|
||||
{
|
||||
if (root.swimlaneIndex < targetNode.swimlaneIndex) {
|
||||
this.extendedDfs(root, targetNode, internalEdge, visitor, seen,
|
||||
root.hashCode, i, layer + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Use the int field to indicate this node has been seen
|
||||
visitor(parent, root, connectingEdge, layer, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default mxSwimlaneModel;
|
||||
|
|
|
@ -2,36 +2,8 @@
|
|||
* Copyright (c) 2006-2015, JGraph Ltd
|
||||
* Copyright (c) 2006-2015, Gaudenz Alder
|
||||
*/
|
||||
/**
|
||||
* Class: mxSwimlaneLayout
|
||||
*
|
||||
* A hierarchical layout algorithm.
|
||||
*
|
||||
* Constructor: mxSwimlaneLayout
|
||||
*
|
||||
* Constructs a new hierarchical layout algorithm.
|
||||
*
|
||||
* Arguments:
|
||||
*
|
||||
* graph - Reference to the enclosing <mxGraph>.
|
||||
* orientation - Optional constant that defines the orientation of this
|
||||
* layout.
|
||||
* deterministic - Optional boolean that specifies if this layout should be
|
||||
* deterministic. Default is true.
|
||||
*/
|
||||
function mxSwimlaneLayout(graph, orientation, deterministic)
|
||||
{
|
||||
mxGraphLayout.call(this, graph);
|
||||
this.orientation = (orientation != null) ? orientation : mxConstants.DIRECTION_NORTH;
|
||||
this.deterministic = (deterministic != null) ? deterministic : true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extends mxGraphLayout.
|
||||
*/
|
||||
mxSwimlaneLayout.prototype = new mxGraphLayout();
|
||||
constructor = mxSwimlaneLayout;
|
||||
|
||||
class mxSwimlaneLayout extends mxGraphLayout {
|
||||
/**
|
||||
* Variable: roots
|
||||
*
|
||||
|
@ -193,13 +165,35 @@ edgesTargetTermCache = null;
|
|||
*/
|
||||
edgeStyle = mxHierarchicalEdgeStyle.POLYLINE;
|
||||
|
||||
/**
|
||||
* Class: mxSwimlaneLayout
|
||||
*
|
||||
* A hierarchical layout algorithm.
|
||||
*
|
||||
* Constructor: mxSwimlaneLayout
|
||||
*
|
||||
* Constructs a new hierarchical layout algorithm.
|
||||
*
|
||||
* Arguments:
|
||||
*
|
||||
* graph - Reference to the enclosing <mxGraph>.
|
||||
* orientation - Optional constant that defines the orientation of this
|
||||
* layout.
|
||||
* deterministic - Optional boolean that specifies if this layout should be
|
||||
* deterministic. Default is true.
|
||||
*/
|
||||
constructor(graph, orientation, deterministic) {
|
||||
super(graph);
|
||||
this.orientation = (orientation != null) ? orientation : mxConstants.DIRECTION_NORTH;
|
||||
this.deterministic = (deterministic != null) ? deterministic : true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Function: getModel
|
||||
*
|
||||
* Returns the internal <mxSwimlaneModel> for this layout algorithm.
|
||||
*/
|
||||
getModel = ()=>
|
||||
{
|
||||
getModel = () => {
|
||||
return this.model;
|
||||
};
|
||||
|
||||
|
@ -213,8 +207,7 @@ getModel = ()=>
|
|||
* parent - Parent <mxCell> that contains the children to be laid out.
|
||||
* swimlanes - Ordered array of swimlanes to be laid out
|
||||
*/
|
||||
execute = (parent, swimlanes)=>
|
||||
{
|
||||
execute = (parent, swimlanes) => {
|
||||
this.parent = parent;
|
||||
var model = this.graph.model;
|
||||
this.edgesCache = new mxDictionary();
|
||||
|
@ -228,14 +221,12 @@ execute = (parent, swimlanes)=>
|
|||
// If just the parent is set use it's immediate
|
||||
// children as the initial set
|
||||
|
||||
if (swimlanes == null || swimlanes.length < 1)
|
||||
{
|
||||
if (swimlanes == null || swimlanes.length < 1) {
|
||||
// TODO indicate the problem
|
||||
return;
|
||||
}
|
||||
|
||||
if (parent == null)
|
||||
{
|
||||
if (parent == null) {
|
||||
parent = model.getParent(swimlanes[0]);
|
||||
}
|
||||
|
||||
|
@ -243,12 +234,10 @@ execute = (parent, swimlanes)=>
|
|||
this.parentX = null;
|
||||
this.parentY = null;
|
||||
|
||||
if (parent != this.root && model.isVertex(parent) != null && this.maintainParentLocation)
|
||||
{
|
||||
if (parent != this.root && model.isVertex(parent) != null && this.maintainParentLocation) {
|
||||
var geo = this.graph.getCellGeometry(parent);
|
||||
|
||||
if (geo != null)
|
||||
{
|
||||
if (geo != null) {
|
||||
this.parentX = geo.x;
|
||||
this.parentY = geo.y;
|
||||
}
|
||||
|
@ -258,34 +247,28 @@ execute = (parent, swimlanes)=>
|
|||
var dummyVertices = [];
|
||||
// Check the swimlanes all have vertices
|
||||
// in them
|
||||
for (var i = 0; i < swimlanes.length; i++)
|
||||
{
|
||||
for (var i = 0; i < swimlanes.length; i++) {
|
||||
var children = this.graph.getChildCells(swimlanes[i]);
|
||||
|
||||
if (children == null || children.length == 0)
|
||||
{
|
||||
if (children == null || children.length == 0) {
|
||||
var vertex = this.graph.insertVertex(swimlanes[i], null, null, 0, 0, this.dummyVertexWidth, 0);
|
||||
dummyVertices.push(vertex);
|
||||
}
|
||||
}
|
||||
|
||||
model.beginUpdate();
|
||||
try
|
||||
{
|
||||
try {
|
||||
this.run(parent);
|
||||
|
||||
if (this.resizeParent && !this.graph.isCellCollapsed(parent))
|
||||
{
|
||||
if (this.resizeParent && !this.graph.isCellCollapsed(parent)) {
|
||||
this.graph.updateGroupBounds([parent], this.parentBorder, this.moveParent);
|
||||
}
|
||||
|
||||
// Maintaining parent location
|
||||
if (this.parentX != null && this.parentY != null)
|
||||
{
|
||||
if (this.parentX != null && this.parentY != null) {
|
||||
var geo = this.graph.getCellGeometry(parent);
|
||||
|
||||
if (geo != null)
|
||||
{
|
||||
if (geo != null) {
|
||||
geo = geo.clone();
|
||||
geo.x = this.parentX;
|
||||
geo.y = this.parentY;
|
||||
|
@ -294,9 +277,7 @@ execute = (parent, swimlanes)=>
|
|||
}
|
||||
|
||||
this.graph.removeCells(dummyVertices);
|
||||
}
|
||||
finally
|
||||
{
|
||||
} finally {
|
||||
model.endUpdate();
|
||||
}
|
||||
};
|
||||
|
@ -308,18 +289,15 @@ execute = (parent, swimlanes)=>
|
|||
* all child vertices.
|
||||
*
|
||||
*/
|
||||
updateGroupBounds = ()=>
|
||||
{
|
||||
updateGroupBounds = () => {
|
||||
// Get all vertices and edge in the layout
|
||||
var cells = [];
|
||||
var model = this.model;
|
||||
|
||||
for (var key in model.edgeMapper)
|
||||
{
|
||||
for (var key in model.edgeMapper) {
|
||||
var edge = model.edgeMapper[key];
|
||||
|
||||
for (var i = 0; i < edge.edges.length; i++)
|
||||
{
|
||||
for (var i = 0; i < edge.edges.length; i++) {
|
||||
cells.push(edge.edges[i]);
|
||||
}
|
||||
}
|
||||
|
@ -327,13 +305,11 @@ updateGroupBounds = ()=>
|
|||
var layoutBounds = this.graph.getBoundingBoxFromGeometry(cells, true);
|
||||
var childBounds = [];
|
||||
|
||||
for (var i = 0; i < this.swimlanes.length; i++)
|
||||
{
|
||||
for (var i = 0; i < this.swimlanes.length; i++) {
|
||||
var lane = this.swimlanes[i];
|
||||
var geo = this.graph.getCellGeometry(lane);
|
||||
|
||||
if (geo != null)
|
||||
{
|
||||
if (geo != null) {
|
||||
var children = this.graph.getChildCells(lane);
|
||||
|
||||
var size = (this.graph.isSwimlane(lane)) ?
|
||||
|
@ -344,12 +320,9 @@ updateGroupBounds = ()=>
|
|||
var childrenY = bounds.y + geo.y - size.height - this.parentBorder;
|
||||
var maxChildrenY = bounds.y + geo.y + bounds.height;
|
||||
|
||||
if (layoutBounds == null)
|
||||
{
|
||||
if (layoutBounds == null) {
|
||||
layoutBounds = new mxRectangle(0, childrenY, 0, maxChildrenY - childrenY);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
layoutBounds.y = Math.min(layoutBounds.y, childrenY);
|
||||
var maxY = Math.max(layoutBounds.y + layoutBounds.height, maxChildrenY);
|
||||
layoutBounds.height = maxY - layoutBounds.y;
|
||||
|
@ -358,13 +331,11 @@ updateGroupBounds = ()=>
|
|||
}
|
||||
|
||||
|
||||
for (var i = 0; i < this.swimlanes.length; i++)
|
||||
{
|
||||
for (var i = 0; i < this.swimlanes.length; i++) {
|
||||
var lane = this.swimlanes[i];
|
||||
var geo = this.graph.getCellGeometry(lane);
|
||||
|
||||
if (geo != null)
|
||||
{
|
||||
if (geo != null) {
|
||||
var children = this.graph.getChildCells(lane);
|
||||
|
||||
var size = (this.graph.isSwimlane(lane)) ?
|
||||
|
@ -403,63 +374,51 @@ updateGroupBounds = ()=>
|
|||
* parent - <mxCell> whose children should be checked.
|
||||
* vertices - array of vertices to limit search to
|
||||
*/
|
||||
findRoots = (parent, vertices)=>
|
||||
{
|
||||
findRoots = (parent, vertices) => {
|
||||
var roots = [];
|
||||
|
||||
if (parent != null && vertices != null)
|
||||
{
|
||||
if (parent != null && vertices != null) {
|
||||
var model = this.graph.model;
|
||||
var best = null;
|
||||
var maxDiff = -100000;
|
||||
|
||||
for (var i in vertices)
|
||||
{
|
||||
for (var i in vertices) {
|
||||
var cell = vertices[i];
|
||||
|
||||
if (cell != null && model.isVertex(cell) && this.graph.isCellVisible(cell) && model.isAncestor(parent, cell))
|
||||
{
|
||||
if (cell != null && model.isVertex(cell) && this.graph.isCellVisible(cell) && model.isAncestor(parent, cell)) {
|
||||
var conns = this.getEdges(cell);
|
||||
var fanOut = 0;
|
||||
var fanIn = 0;
|
||||
|
||||
for (var k = 0; k < conns.length; k++)
|
||||
{
|
||||
for (var k = 0; k < conns.length; k++) {
|
||||
var src = this.getVisibleTerminal(conns[k], true);
|
||||
|
||||
if (src == cell)
|
||||
{
|
||||
if (src == cell) {
|
||||
// Only count connection within this swimlane
|
||||
var other = this.getVisibleTerminal(conns[k], false);
|
||||
|
||||
if (model.isAncestor(parent, other))
|
||||
{
|
||||
if (model.isAncestor(parent, other)) {
|
||||
fanOut++;
|
||||
}
|
||||
}
|
||||
else if (model.isAncestor(parent, src))
|
||||
{
|
||||
} else if (model.isAncestor(parent, src)) {
|
||||
fanIn++;
|
||||
}
|
||||
}
|
||||
|
||||
if (fanIn == 0 && fanOut > 0)
|
||||
{
|
||||
if (fanIn == 0 && fanOut > 0) {
|
||||
roots.push(cell);
|
||||
}
|
||||
|
||||
var diff = fanOut - fanIn;
|
||||
|
||||
if (diff > maxDiff)
|
||||
{
|
||||
if (diff > maxDiff) {
|
||||
maxDiff = diff;
|
||||
best = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (roots.length == 0 && best != null)
|
||||
{
|
||||
if (roots.length == 0 && best != null) {
|
||||
roots.push(best);
|
||||
}
|
||||
}
|
||||
|
@ -476,12 +435,10 @@ findRoots = (parent, vertices)=>
|
|||
*
|
||||
* cell - <mxCell> whose edges should be returned.
|
||||
*/
|
||||
getEdges = (cell)=>
|
||||
{
|
||||
getEdges = (cell) => {
|
||||
var cachedEdges = this.edgesCache.get(cell);
|
||||
|
||||
if (cachedEdges != null)
|
||||
{
|
||||
if (cachedEdges != null) {
|
||||
return cachedEdges;
|
||||
}
|
||||
|
||||
|
@ -490,16 +447,12 @@ getEdges = (cell)=>
|
|||
var isCollapsed = this.graph.isCellCollapsed(cell);
|
||||
var childCount = model.getChildCount(cell);
|
||||
|
||||
for (var i = 0; i < childCount; i++)
|
||||
{
|
||||
for (var i = 0; i < childCount; i++) {
|
||||
var child = model.getChildAt(cell, i);
|
||||
|
||||
if (this.isPort(child))
|
||||
{
|
||||
if (this.isPort(child)) {
|
||||
edges = edges.concat(model.getEdges(child, true, true));
|
||||
}
|
||||
else if (isCollapsed || !this.graph.isCellVisible(child))
|
||||
{
|
||||
} else if (isCollapsed || !this.graph.isCellVisible(child)) {
|
||||
edges = edges.concat(model.getEdges(child, true, true));
|
||||
}
|
||||
}
|
||||
|
@ -507,15 +460,13 @@ getEdges = (cell)=>
|
|||
edges = edges.concat(model.getEdges(cell, true, true));
|
||||
var result = [];
|
||||
|
||||
for (var i = 0; i < edges.length; i++)
|
||||
{
|
||||
for (var i = 0; i < edges.length; i++) {
|
||||
var source = this.getVisibleTerminal(edges[i], true);
|
||||
var target = this.getVisibleTerminal(edges[i], false);
|
||||
|
||||
if ((source == target) || ((source != target) && ((target == cell && (this.parent == null || this.graph.isValidAncestor(source, this.parent, this.traverseAncestors))) ||
|
||||
(source == cell && (this.parent == null ||
|
||||
this.graph.isValidAncestor(target, this.parent, this.traverseAncestors))))))
|
||||
{
|
||||
this.graph.isValidAncestor(target, this.parent, this.traverseAncestors)))))) {
|
||||
result.push(edges[i]);
|
||||
}
|
||||
}
|
||||
|
@ -535,19 +486,16 @@ getEdges = (cell)=>
|
|||
* edge - <mxCell> whose edges should be returned.
|
||||
* source - Boolean that specifies whether the source or target terminal is to be returned
|
||||
*/
|
||||
getVisibleTerminal = (edge, source)=>
|
||||
{
|
||||
getVisibleTerminal = (edge, source) => {
|
||||
var terminalCache = this.edgesTargetTermCache;
|
||||
|
||||
if (source)
|
||||
{
|
||||
if (source) {
|
||||
terminalCache = this.edgeSourceTermCache;
|
||||
}
|
||||
|
||||
var term = terminalCache.get(edge);
|
||||
|
||||
if (term != null)
|
||||
{
|
||||
if (term != null) {
|
||||
return term;
|
||||
}
|
||||
|
||||
|
@ -555,15 +503,12 @@ getVisibleTerminal = (edge, source)=>
|
|||
|
||||
var terminal = (state != null) ? state.getVisibleTerminal(source) : this.graph.view.getVisibleTerminal(edge, source);
|
||||
|
||||
if (terminal == null)
|
||||
{
|
||||
if (terminal == null) {
|
||||
terminal = (state != null) ? state.getVisibleTerminal(source) : this.graph.view.getVisibleTerminal(edge, source);
|
||||
}
|
||||
|
||||
if (terminal != null)
|
||||
{
|
||||
if (this.isPort(terminal))
|
||||
{
|
||||
if (terminal != null) {
|
||||
if (this.isPort(terminal)) {
|
||||
terminal = this.graph.model.getParent(terminal);
|
||||
}
|
||||
|
||||
|
@ -581,18 +526,15 @@ getVisibleTerminal = (edge, source)=>
|
|||
* routing changes made. It runs each stage of the layout that has been
|
||||
* created.
|
||||
*/
|
||||
run = (parent)=>
|
||||
{
|
||||
run = (parent) => {
|
||||
// Separate out unconnected hierarchies
|
||||
var hierarchyVertices = [];
|
||||
var allVertexSet = Object();
|
||||
|
||||
if (this.swimlanes != null && this.swimlanes.length > 0 && parent != null)
|
||||
{
|
||||
if (this.swimlanes != null && this.swimlanes.length > 0 && parent != null) {
|
||||
var filledVertexSet = Object();
|
||||
|
||||
for (var i = 0; i < this.swimlanes.length; i++)
|
||||
{
|
||||
for (var i = 0; i < this.swimlanes.length; i++) {
|
||||
this.filterDescendants(this.swimlanes[i], filledVertexSet);
|
||||
}
|
||||
|
||||
|
@ -600,10 +542,8 @@ run = (parent)=>
|
|||
var filledVertexSetEmpty = true;
|
||||
|
||||
// Poor man's isSetEmpty
|
||||
for (var key in filledVertexSet)
|
||||
{
|
||||
if (filledVertexSet[key] != null)
|
||||
{
|
||||
for (var key in filledVertexSet) {
|
||||
if (filledVertexSet[key] != null) {
|
||||
filledVertexSetEmpty = false;
|
||||
break;
|
||||
}
|
||||
|
@ -612,12 +552,10 @@ run = (parent)=>
|
|||
// Only test for candidates in each swimlane in order
|
||||
var laneCounter = 0;
|
||||
|
||||
while (!filledVertexSetEmpty && laneCounter < this.swimlanes.length)
|
||||
{
|
||||
while (!filledVertexSetEmpty && laneCounter < this.swimlanes.length) {
|
||||
var candidateRoots = this.findRoots(this.swimlanes[laneCounter], filledVertexSet);
|
||||
|
||||
if (candidateRoots.length == 0)
|
||||
{
|
||||
if (candidateRoots.length == 0) {
|
||||
laneCounter++;
|
||||
continue;
|
||||
}
|
||||
|
@ -625,8 +563,7 @@ run = (parent)=>
|
|||
// If the candidate root is an unconnected group cell, remove it from
|
||||
// the layout. We may need a custom set that holds such groups and forces
|
||||
// them to be processed for resizing and/or moving.
|
||||
for (var i = 0; i < candidateRoots.length; i++)
|
||||
{
|
||||
for (var i = 0; i < candidateRoots.length; i++) {
|
||||
var vertexSet = Object();
|
||||
hierarchyVertices.push(vertexSet);
|
||||
|
||||
|
@ -634,30 +571,24 @@ run = (parent)=>
|
|||
hierarchyVertices, filledVertexSet, laneCounter);
|
||||
}
|
||||
|
||||
for (var i = 0; i < candidateRoots.length; i++)
|
||||
{
|
||||
for (var i = 0; i < candidateRoots.length; i++) {
|
||||
this.roots.push(candidateRoots[i]);
|
||||
}
|
||||
|
||||
filledVertexSetEmpty = true;
|
||||
|
||||
// Poor man's isSetEmpty
|
||||
for (var key in filledVertexSet)
|
||||
{
|
||||
if (filledVertexSet[key] != null)
|
||||
{
|
||||
for (var key in filledVertexSet) {
|
||||
if (filledVertexSet[key] != null) {
|
||||
filledVertexSetEmpty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Find vertex set as directed traversal from roots
|
||||
|
||||
for (var i = 0; i < this.roots.length; i++)
|
||||
{
|
||||
for (var i = 0; i < this.roots.length; i++) {
|
||||
var vertexSet = Object();
|
||||
hierarchyVertices.push(vertexSet);
|
||||
|
||||
|
@ -668,8 +599,7 @@ run = (parent)=>
|
|||
|
||||
var tmp = [];
|
||||
|
||||
for (var key in allVertexSet)
|
||||
{
|
||||
for (var key in allVertexSet) {
|
||||
tmp.push(allVertexSet[key]);
|
||||
}
|
||||
|
||||
|
@ -688,28 +618,23 @@ run = (parent)=>
|
|||
*
|
||||
* Creates an array of descendant cells
|
||||
*/
|
||||
filterDescendants = (cell, result)=>
|
||||
{
|
||||
filterDescendants = (cell, result) => {
|
||||
var model = this.graph.model;
|
||||
|
||||
if (model.isVertex(cell) && cell != this.parent && model.getParent(cell) != this.parent && this.graph.isCellVisible(cell))
|
||||
{
|
||||
if (model.isVertex(cell) && cell != this.parent && model.getParent(cell) != this.parent && this.graph.isCellVisible(cell)) {
|
||||
result[mxObjectIdentity.get(cell)] = cell;
|
||||
}
|
||||
|
||||
if (this.traverseAncestors || cell == this.parent
|
||||
&& this.graph.isCellVisible(cell))
|
||||
{
|
||||
&& this.graph.isCellVisible(cell)) {
|
||||
var childCount = model.getChildCount(cell);
|
||||
|
||||
for (var i = 0; i < childCount; i++)
|
||||
{
|
||||
for (var i = 0; i < childCount; i++) {
|
||||
var child = model.getChildAt(cell, i);
|
||||
|
||||
// Ignore ports in the layout vertex list, they are dealt with
|
||||
// in the traversal mechanisms
|
||||
if (!this.isPort(child))
|
||||
{
|
||||
if (!this.isPort(child)) {
|
||||
this.filterDescendants(child, result);
|
||||
}
|
||||
}
|
||||
|
@ -726,10 +651,8 @@ filterDescendants = (cell, result)=>
|
|||
*
|
||||
* cell - <mxCell> that represents the port.
|
||||
*/
|
||||
isPort = (cell)=>
|
||||
{
|
||||
if (cell.geometry.relative)
|
||||
{
|
||||
isPort = (cell) => {
|
||||
if (cell.geometry.relative) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -748,21 +671,18 @@ isPort = (cell)=>
|
|||
* target -
|
||||
* directed -
|
||||
*/
|
||||
getEdgesBetween = (source, target, directed)=>
|
||||
{
|
||||
getEdgesBetween = (source, target, directed) => {
|
||||
directed = (directed != null) ? directed : false;
|
||||
var edges = this.getEdges(source);
|
||||
var result = [];
|
||||
|
||||
// Checks if the edge is connected to the correct
|
||||
// cell and returns the first match
|
||||
for (var i = 0; i < edges.length; i++)
|
||||
{
|
||||
for (var i = 0; i < edges.length; i++) {
|
||||
var src = this.getVisibleTerminal(edges[i], true);
|
||||
var trg = this.getVisibleTerminal(edges[i], false);
|
||||
|
||||
if ((src == source && trg == target) || (!directed && src == target && trg == source))
|
||||
{
|
||||
if ((src == source && trg == target) || (!directed && src == target && trg == source)) {
|
||||
result.push(edges[i]);
|
||||
}
|
||||
}
|
||||
|
@ -788,57 +708,46 @@ getEdgesBetween = (source, target, directed)=>
|
|||
* swimlaneIndex - the laid out order index of the swimlane vertex is contained in
|
||||
*/
|
||||
traverse = function (vertex, directed, edge, allVertices, currentComp,
|
||||
hierarchyVertices, filledVertexSet, swimlaneIndex)
|
||||
{
|
||||
if (vertex != null && allVertices != null)
|
||||
{
|
||||
hierarchyVertices, filledVertexSet, swimlaneIndex) {
|
||||
if (vertex != null && allVertices != null) {
|
||||
// Has this vertex been seen before in any traversal
|
||||
// And if the filled vertex set is populated, only
|
||||
// process vertices in that it contains
|
||||
var vertexID = mxObjectIdentity.get(vertex);
|
||||
|
||||
if ((allVertices[vertexID] == null)
|
||||
&& (filledVertexSet == null ? true : filledVertexSet[vertexID] != null))
|
||||
{
|
||||
if (currentComp[vertexID] == null)
|
||||
{
|
||||
&& (filledVertexSet == null ? true : filledVertexSet[vertexID] != null)) {
|
||||
if (currentComp[vertexID] == null) {
|
||||
currentComp[vertexID] = vertex;
|
||||
}
|
||||
if (allVertices[vertexID] == null)
|
||||
{
|
||||
if (allVertices[vertexID] == null) {
|
||||
allVertices[vertexID] = vertex;
|
||||
}
|
||||
|
||||
if (filledVertexSet !== null)
|
||||
{
|
||||
if (filledVertexSet !== null) {
|
||||
delete filledVertexSet[vertexID];
|
||||
}
|
||||
|
||||
var edges = this.getEdges(vertex);
|
||||
var model = this.graph.model;
|
||||
|
||||
for (var i = 0; i < edges.length; i++)
|
||||
{
|
||||
for (var i = 0; i < edges.length; i++) {
|
||||
var otherVertex = this.getVisibleTerminal(edges[i], true);
|
||||
var isSource = otherVertex == vertex;
|
||||
|
||||
if (isSource)
|
||||
{
|
||||
if (isSource) {
|
||||
otherVertex = this.getVisibleTerminal(edges[i], false);
|
||||
}
|
||||
|
||||
var otherIndex = 0;
|
||||
// Get the swimlane index of the other terminal
|
||||
for (otherIndex = 0; otherIndex < this.swimlanes.length; otherIndex++)
|
||||
{
|
||||
if (model.isAncestor(this.swimlanes[otherIndex], otherVertex))
|
||||
{
|
||||
for (otherIndex = 0; otherIndex < this.swimlanes.length; otherIndex++) {
|
||||
if (model.isAncestor(this.swimlanes[otherIndex], otherVertex)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (otherIndex >= this.swimlanes.length)
|
||||
{
|
||||
if (otherIndex >= this.swimlanes.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -846,28 +755,21 @@ traverse = function(vertex, directed, edge, allVertices, currentComp,
|
|||
// as the current vertex, or if the swimlane index of the other
|
||||
// vertex is greater than that of this vertex
|
||||
if ((otherIndex > swimlaneIndex) ||
|
||||
((!directed || isSource) && otherIndex == swimlaneIndex))
|
||||
{
|
||||
((!directed || isSource) && otherIndex == swimlaneIndex)) {
|
||||
currentComp = this.traverse(otherVertex, directed, edges[i], allVertices,
|
||||
currentComp, hierarchyVertices,
|
||||
filledVertexSet, otherIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentComp[vertexID] == null)
|
||||
{
|
||||
} else {
|
||||
if (currentComp[vertexID] == null) {
|
||||
// We've seen this vertex before, but not in the current component
|
||||
// This component and the one it's in need to be merged
|
||||
for (var i = 0; i < hierarchyVertices.length; i++)
|
||||
{
|
||||
for (var i = 0; i < hierarchyVertices.length; i++) {
|
||||
var comp = hierarchyVertices[i];
|
||||
|
||||
if (comp[vertexID] != null)
|
||||
{
|
||||
for (var key in comp)
|
||||
{
|
||||
if (comp[vertexID] != null) {
|
||||
for (var key in comp) {
|
||||
currentComp[key] = comp[key];
|
||||
}
|
||||
|
||||
|
@ -888,8 +790,7 @@ traverse = function(vertex, directed, edge, allVertices, currentComp,
|
|||
*
|
||||
* Executes the cycle stage using mxMinimumCycleRemover.
|
||||
*/
|
||||
cycleStage = (parent)=>
|
||||
{
|
||||
cycleStage = (parent) => {
|
||||
var cycleStage = new mxSwimlaneOrdering(this);
|
||||
cycleStage.execute(parent);
|
||||
};
|
||||
|
@ -899,8 +800,7 @@ cycleStage = (parent)=>
|
|||
*
|
||||
* Implements first stage of a Sugiyama layout.
|
||||
*/
|
||||
layeringStage = ()=>
|
||||
{
|
||||
layeringStage = () => {
|
||||
this.model.initialRank();
|
||||
this.model.fixRanks();
|
||||
};
|
||||
|
@ -910,8 +810,7 @@ layeringStage = ()=>
|
|||
*
|
||||
* Executes the crossing stage using mxMedianHybridCrossingReduction.
|
||||
*/
|
||||
crossingStage = (parent)=>
|
||||
{
|
||||
crossingStage = (parent) => {
|
||||
var crossingStage = new mxMedianHybridCrossingReduction(this);
|
||||
crossingStage.execute(parent);
|
||||
};
|
||||
|
@ -921,8 +820,7 @@ crossingStage = (parent)=>
|
|||
*
|
||||
* Executes the placement stage using mxCoordinateAssignment.
|
||||
*/
|
||||
placementStage = (initialX, parent)=>
|
||||
{
|
||||
placementStage = (initialX, parent) => {
|
||||
var placementStage = new mxCoordinateAssignment(this, this.intraCellSpacing,
|
||||
this.interRankCellSpacing, this.orientation, initialX,
|
||||
this.parallelEdgeSpacing);
|
||||
|
@ -931,3 +829,6 @@ placementStage = (initialX, parent)=>
|
|||
|
||||
return placementStage.limitX + this.interHierarchySpacing;
|
||||
};
|
||||
}
|
||||
|
||||
export default mxSwimlaneLayout;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,6 +2,8 @@
|
|||
* Copyright (c) 2006-2015, JGraph Ltd
|
||||
* Copyright (c) 2006-2015, Gaudenz Alder
|
||||
*/
|
||||
|
||||
class mxHierarchicalLayoutStage {
|
||||
/**
|
||||
* Class: mxHierarchicalLayoutStage
|
||||
*
|
||||
|
@ -13,7 +15,8 @@
|
|||
*
|
||||
* Constructs a new hierarchical layout stage.
|
||||
*/
|
||||
function mxHierarchicalLayoutStage() { };
|
||||
constructor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Function: execute
|
||||
|
@ -23,3 +26,6 @@ function mxHierarchicalLayoutStage() { };
|
|||
* use.
|
||||
*/
|
||||
execute = (parent)=> { };
|
||||
}
|
||||
|
||||
export default mxHierarchicalLayoutStage;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* Copyright (c) 2006-2015, JGraph Ltd
|
||||
* Copyright (c) 2006-2015, Gaudenz Alder
|
||||
*/
|
||||
|
||||
class mxMedianHybridCrossingReduction extends mxHierarchicalLayoutStage {
|
||||
/**
|
||||
* Class: mxMedianHybridCrossingReduction
|
||||
*
|
||||
|
@ -20,17 +22,11 @@
|
|||
* orientation - the position of the root node(s) relative to the graph
|
||||
* initialX - the leftmost coordinate node placement starts at
|
||||
*/
|
||||
function mxMedianHybridCrossingReduction(layout)
|
||||
{
|
||||
constructor(layout) {
|
||||
// super not called
|
||||
this.layout = layout;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extends mxMedianHybridCrossingReduction.
|
||||
*/
|
||||
mxMedianHybridCrossingReduction.prototype = new mxHierarchicalLayoutStage();
|
||||
constructor = mxMedianHybridCrossingReduction;
|
||||
|
||||
/**
|
||||
* Variable: layout
|
||||
*
|
||||
|
@ -81,15 +77,13 @@ maxNoImprovementIterations = 2;
|
|||
* Performs a vertex ordering within ranks as described by Gansner et al
|
||||
* 1993
|
||||
*/
|
||||
execute = (parent)=>
|
||||
{
|
||||
execute = (parent) => {
|
||||
var model = this.layout.getModel();
|
||||
|
||||
// Stores initial ordering as being the best one found so far
|
||||
this.nestedBestRanks = [];
|
||||
|
||||
for (var i = 0; i < model.ranks.length; i++)
|
||||
{
|
||||
for (var i = 0; i < model.ranks.length; i++) {
|
||||
this.nestedBestRanks[i] = model.ranks[i].slice();
|
||||
}
|
||||
|
||||
|
@ -97,50 +91,41 @@ execute = (parent)=>
|
|||
var currentBestCrossings = this.calculateCrossings(model);
|
||||
|
||||
for (var i = 0; i < this.maxIterations &&
|
||||
iterationsWithoutImprovement < this.maxNoImprovementIterations; i++)
|
||||
{
|
||||
iterationsWithoutImprovement < this.maxNoImprovementIterations; i++) {
|
||||
this.weightedMedian(i, model);
|
||||
this.transpose(i, model);
|
||||
var candidateCrossings = this.calculateCrossings(model);
|
||||
|
||||
if (candidateCrossings < currentBestCrossings)
|
||||
{
|
||||
if (candidateCrossings < currentBestCrossings) {
|
||||
currentBestCrossings = candidateCrossings;
|
||||
iterationsWithoutImprovement = 0;
|
||||
|
||||
// Store the current rankings as the best ones
|
||||
for (var j = 0; j < this.nestedBestRanks.length; j++)
|
||||
{
|
||||
for (var j = 0; j < this.nestedBestRanks.length; j++) {
|
||||
var rank = model.ranks[j];
|
||||
|
||||
for (var k = 0; k < rank.length; k++)
|
||||
{
|
||||
for (var k = 0; k < rank.length; k++) {
|
||||
var cell = rank[k];
|
||||
this.nestedBestRanks[j][cell.getGeneralPurposeVariable(j)] = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Increase count of iterations where we haven't improved the
|
||||
// layout
|
||||
iterationsWithoutImprovement++;
|
||||
|
||||
// Restore the best values to the cells
|
||||
for (var j = 0; j < this.nestedBestRanks.length; j++)
|
||||
{
|
||||
for (var j = 0; j < this.nestedBestRanks.length; j++) {
|
||||
var rank = model.ranks[j];
|
||||
|
||||
for (var k = 0; k < rank.length; k++)
|
||||
{
|
||||
for (var k = 0; k < rank.length; k++) {
|
||||
var cell = rank[k];
|
||||
cell.setGeneralPurposeVariable(j, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentBestCrossings == 0)
|
||||
{
|
||||
if (currentBestCrossings == 0) {
|
||||
// Do nothing further
|
||||
break;
|
||||
}
|
||||
|
@ -150,16 +135,13 @@ execute = (parent)=>
|
|||
var ranks = [];
|
||||
var rankList = [];
|
||||
|
||||
for (var i = 0; i < model.maxRank + 1; i++)
|
||||
{
|
||||
for (var i = 0; i < model.maxRank + 1; i++) {
|
||||
rankList[i] = [];
|
||||
ranks[i] = rankList[i];
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.nestedBestRanks.length; i++)
|
||||
{
|
||||
for (var j = 0; j < this.nestedBestRanks[i].length; j++)
|
||||
{
|
||||
for (var i = 0; i < this.nestedBestRanks.length; i++) {
|
||||
for (var j = 0; j < this.nestedBestRanks[i].length; j++) {
|
||||
rankList[i].push(this.nestedBestRanks[i][j]);
|
||||
}
|
||||
}
|
||||
|
@ -179,13 +161,11 @@ execute = (parent)=>
|
|||
*
|
||||
* model - the internal model describing the hierarchy
|
||||
*/
|
||||
calculateCrossings = (model)=>
|
||||
{
|
||||
calculateCrossings = (model) => {
|
||||
var numRanks = model.ranks.length;
|
||||
var totalCrossings = 0;
|
||||
|
||||
for (var i = 1; i < numRanks; i++)
|
||||
{
|
||||
for (var i = 1; i < numRanks; i++) {
|
||||
totalCrossings += this.calculateRankCrossing(i, model);
|
||||
}
|
||||
|
||||
|
@ -204,8 +184,7 @@ calculateCrossings = (model)=>
|
|||
* i - the topmost rank of the pair ( higher rank value )
|
||||
* model - the internal model describing the hierarchy
|
||||
*/
|
||||
calculateRankCrossing = (i, model)=>
|
||||
{
|
||||
calculateRankCrossing = (i, model) => {
|
||||
var totalCrossings = 0;
|
||||
var rank = model.ranks[i];
|
||||
var previousRank = model.ranks[i - 1];
|
||||
|
@ -213,35 +192,33 @@ calculateRankCrossing = (i, model)=>
|
|||
var tmpIndices = [];
|
||||
|
||||
// Iterate over the top rank and fill in the connection information
|
||||
for (var j = 0; j < rank.length; j++)
|
||||
{
|
||||
for (var j = 0; j < rank.length; j++) {
|
||||
var node = rank[j];
|
||||
var rankPosition = node.getGeneralPurposeVariable(i);
|
||||
var connectedCells = node.getPreviousLayerConnectedCells(i);
|
||||
var nodeIndices = [];
|
||||
|
||||
for (var k = 0; k < connectedCells.length; k++)
|
||||
{
|
||||
for (var k = 0; k < connectedCells.length; k++) {
|
||||
var connectedNode = connectedCells[k];
|
||||
var otherCellRankPosition = connectedNode.getGeneralPurposeVariable(i - 1);
|
||||
nodeIndices.push(otherCellRankPosition);
|
||||
}
|
||||
|
||||
nodeIndices.sort((x, y)=> { return x - y; });
|
||||
nodeIndices.sort((x, y) => {
|
||||
return x - y;
|
||||
});
|
||||
tmpIndices[rankPosition] = nodeIndices;
|
||||
}
|
||||
|
||||
var indices = [];
|
||||
|
||||
for (var j = 0; j < tmpIndices.length; j++)
|
||||
{
|
||||
for (var j = 0; j < tmpIndices.length; j++) {
|
||||
indices = indices.concat(tmpIndices[j]);
|
||||
}
|
||||
|
||||
var firstIndex = 1;
|
||||
|
||||
while (firstIndex < previousRank.length)
|
||||
{
|
||||
while (firstIndex < previousRank.length) {
|
||||
firstIndex <<= 1;
|
||||
}
|
||||
|
||||
|
@ -250,21 +227,17 @@ calculateRankCrossing = (i, model)=>
|
|||
|
||||
var tree = [];
|
||||
|
||||
for (var j = 0; j < treeSize; ++j)
|
||||
{
|
||||
for (var j = 0; j < treeSize; ++j) {
|
||||
tree[j] = 0;
|
||||
}
|
||||
|
||||
for (var j = 0; j < indices.length; j++)
|
||||
{
|
||||
for (var j = 0; j < indices.length; j++) {
|
||||
var index = indices[j];
|
||||
var treeIndex = index + firstIndex;
|
||||
++tree[treeIndex];
|
||||
|
||||
while (treeIndex > 0)
|
||||
{
|
||||
if (treeIndex % 2)
|
||||
{
|
||||
while (treeIndex > 0) {
|
||||
if (treeIndex % 2) {
|
||||
totalCrossings += tree[treeIndex + 1];
|
||||
}
|
||||
|
||||
|
@ -287,34 +260,29 @@ calculateRankCrossing = (i, model)=>
|
|||
* mainLoopIteration - the iteration number of the main loop
|
||||
* model - the internal model describing the hierarchy
|
||||
*/
|
||||
transpose = (mainLoopIteration, model)=>
|
||||
{
|
||||
transpose = (mainLoopIteration, model) => {
|
||||
var improved = true;
|
||||
|
||||
// Track the number of iterations in case of looping
|
||||
var count = 0;
|
||||
var maxCount = 10;
|
||||
while (improved && count++ < maxCount)
|
||||
{
|
||||
while (improved && count++ < maxCount) {
|
||||
// On certain iterations allow allow swapping of cell pairs with
|
||||
// equal edge crossings switched or not switched. This help to
|
||||
// nudge a stuck layout into a lower crossing total.
|
||||
var nudge = mainLoopIteration % 2 == 1 && count % 2 == 1;
|
||||
improved = false;
|
||||
|
||||
for (var i = 0; i < model.ranks.length; i++)
|
||||
{
|
||||
for (var i = 0; i < model.ranks.length; i++) {
|
||||
var rank = model.ranks[i];
|
||||
var orderedCells = [];
|
||||
|
||||
for (var j = 0; j < rank.length; j++)
|
||||
{
|
||||
for (var j = 0; j < rank.length; j++) {
|
||||
var cell = rank[j];
|
||||
var tempRank = cell.getGeneralPurposeVariable(i);
|
||||
|
||||
// FIXME: Workaround to avoid negative tempRanks
|
||||
if (tempRank < 0)
|
||||
{
|
||||
if (tempRank < 0) {
|
||||
tempRank = j;
|
||||
}
|
||||
orderedCells[tempRank] = cell;
|
||||
|
@ -333,16 +301,14 @@ transpose = (mainLoopIteration, model)=>
|
|||
var leftCell = null;
|
||||
var rightCell = null;
|
||||
|
||||
for (var j = 0; j < (rank.length - 1); j++)
|
||||
{
|
||||
for (var j = 0; j < (rank.length - 1); j++) {
|
||||
// For each intra-rank adjacent pair of cells
|
||||
// see if swapping them around would reduce the
|
||||
// number of edges crossing they cause in total
|
||||
// On every cell pair except the first on each rank, we
|
||||
// can save processing using the previous values for the
|
||||
// right cell on the new left cell
|
||||
if (j == 0)
|
||||
{
|
||||
if (j == 0) {
|
||||
leftCell = orderedCells[j];
|
||||
leftCellAboveConnections = leftCell
|
||||
.getNextLayerConnectedCells(i);
|
||||
|
@ -351,18 +317,14 @@ transpose = (mainLoopIteration, model)=>
|
|||
leftAbovePositions = [];
|
||||
leftBelowPositions = [];
|
||||
|
||||
for (var k = 0; k < leftCellAboveConnections.length; k++)
|
||||
{
|
||||
for (var k = 0; k < leftCellAboveConnections.length; k++) {
|
||||
leftAbovePositions[k] = leftCellAboveConnections[k].getGeneralPurposeVariable(i + 1);
|
||||
}
|
||||
|
||||
for (var k = 0; k < leftCellBelowConnections.length; k++)
|
||||
{
|
||||
for (var k = 0; k < leftCellBelowConnections.length; k++) {
|
||||
leftBelowPositions[k] = leftCellBelowConnections[k].getGeneralPurposeVariable(i - 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
leftCellAboveConnections = rightCellAboveConnections;
|
||||
leftCellBelowConnections = rightCellBelowConnections;
|
||||
leftAbovePositions = rightAbovePositions;
|
||||
|
@ -379,46 +341,36 @@ transpose = (mainLoopIteration, model)=>
|
|||
rightAbovePositions = [];
|
||||
rightBelowPositions = [];
|
||||
|
||||
for (var k = 0; k < rightCellAboveConnections.length; k++)
|
||||
{
|
||||
for (var k = 0; k < rightCellAboveConnections.length; k++) {
|
||||
rightAbovePositions[k] = rightCellAboveConnections[k].getGeneralPurposeVariable(i + 1);
|
||||
}
|
||||
|
||||
for (var k = 0; k < rightCellBelowConnections.length; k++)
|
||||
{
|
||||
for (var k = 0; k < rightCellBelowConnections.length; k++) {
|
||||
rightBelowPositions[k] = rightCellBelowConnections[k].getGeneralPurposeVariable(i - 1);
|
||||
}
|
||||
|
||||
var totalCurrentCrossings = 0;
|
||||
var totalSwitchedCrossings = 0;
|
||||
|
||||
for (var k = 0; k < leftAbovePositions.length; k++)
|
||||
{
|
||||
for (var ik = 0; ik < rightAbovePositions.length; ik++)
|
||||
{
|
||||
if (leftAbovePositions[k] > rightAbovePositions[ik])
|
||||
{
|
||||
for (var k = 0; k < leftAbovePositions.length; k++) {
|
||||
for (var ik = 0; ik < rightAbovePositions.length; ik++) {
|
||||
if (leftAbovePositions[k] > rightAbovePositions[ik]) {
|
||||
totalCurrentCrossings++;
|
||||
}
|
||||
|
||||
if (leftAbovePositions[k] < rightAbovePositions[ik])
|
||||
{
|
||||
if (leftAbovePositions[k] < rightAbovePositions[ik]) {
|
||||
totalSwitchedCrossings++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var k = 0; k < leftBelowPositions.length; k++)
|
||||
{
|
||||
for (var ik = 0; ik < rightBelowPositions.length; ik++)
|
||||
{
|
||||
if (leftBelowPositions[k] > rightBelowPositions[ik])
|
||||
{
|
||||
for (var k = 0; k < leftBelowPositions.length; k++) {
|
||||
for (var ik = 0; ik < rightBelowPositions.length; ik++) {
|
||||
if (leftBelowPositions[k] > rightBelowPositions[ik]) {
|
||||
totalCurrentCrossings++;
|
||||
}
|
||||
|
||||
if (leftBelowPositions[k] < rightBelowPositions[ik])
|
||||
{
|
||||
if (leftBelowPositions[k] < rightBelowPositions[ik]) {
|
||||
totalSwitchedCrossings++;
|
||||
}
|
||||
}
|
||||
|
@ -426,8 +378,7 @@ transpose = (mainLoopIteration, model)=>
|
|||
|
||||
if ((totalSwitchedCrossings < totalCurrentCrossings) ||
|
||||
(totalSwitchedCrossings == totalCurrentCrossings &&
|
||||
nudge))
|
||||
{
|
||||
nudge)) {
|
||||
var temp = leftCell.getGeneralPurposeVariable(i);
|
||||
leftCell.setGeneralPurposeVariable(i, rightCell
|
||||
.getGeneralPurposeVariable(i));
|
||||
|
@ -443,8 +394,7 @@ transpose = (mainLoopIteration, model)=>
|
|||
rightBelowPositions = leftBelowPositions;
|
||||
rightCell = leftCell;
|
||||
|
||||
if (!nudge)
|
||||
{
|
||||
if (!nudge) {
|
||||
// Don't count nudges as improvement or we'll end
|
||||
// up stuck in two combinations and not finishing
|
||||
// as early as we should
|
||||
|
@ -467,21 +417,15 @@ transpose = (mainLoopIteration, model)=>
|
|||
* iteration - the iteration number of the main loop
|
||||
* model - the internal model describing the hierarchy
|
||||
*/
|
||||
weightedMedian = (iteration, model)=>
|
||||
{
|
||||
weightedMedian = (iteration, model) => {
|
||||
// Reverse sweep direction each time through this method
|
||||
var downwardSweep = (iteration % 2 == 0);
|
||||
if (downwardSweep)
|
||||
{
|
||||
for (var j = model.maxRank - 1; j >= 0; j--)
|
||||
{
|
||||
if (downwardSweep) {
|
||||
for (var j = model.maxRank - 1; j >= 0; j--) {
|
||||
this.medianRank(j, downwardSweep);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var j = 1; j < model.maxRank; j++)
|
||||
{
|
||||
} else {
|
||||
for (var j = 1; j < model.maxRank; j++) {
|
||||
this.medianRank(j, downwardSweep);
|
||||
}
|
||||
}
|
||||
|
@ -498,14 +442,12 @@ weightedMedian = (iteration, model)=>
|
|||
* rankValue - the layer number of this rank
|
||||
* downwardSweep - whether or not this is a downward sweep through the graph
|
||||
*/
|
||||
medianRank = (rankValue, downwardSweep)=>
|
||||
{
|
||||
medianRank = (rankValue, downwardSweep) => {
|
||||
var numCellsForRank = this.nestedBestRanks[rankValue].length;
|
||||
var medianValues = [];
|
||||
var reservedPositions = [];
|
||||
|
||||
for (var i = 0; i < numCellsForRank; i++)
|
||||
{
|
||||
for (var i = 0; i < numCellsForRank; i++) {
|
||||
var cell = this.nestedBestRanks[rankValue][i];
|
||||
var sorterEntry = new MedianCellSorter();
|
||||
sorterEntry.cell = cell;
|
||||
|
@ -516,37 +458,28 @@ medianRank = (rankValue, downwardSweep)=>
|
|||
// medianValues[i].nudge = !downwardSweep;
|
||||
var nextLevelConnectedCells;
|
||||
|
||||
if (downwardSweep)
|
||||
{
|
||||
if (downwardSweep) {
|
||||
nextLevelConnectedCells = cell
|
||||
.getNextLayerConnectedCells(rankValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
nextLevelConnectedCells = cell
|
||||
.getPreviousLayerConnectedCells(rankValue);
|
||||
}
|
||||
|
||||
var nextRankValue;
|
||||
|
||||
if (downwardSweep)
|
||||
{
|
||||
if (downwardSweep) {
|
||||
nextRankValue = rankValue + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
nextRankValue = rankValue - 1;
|
||||
}
|
||||
|
||||
if (nextLevelConnectedCells != null
|
||||
&& nextLevelConnectedCells.length != 0)
|
||||
{
|
||||
&& nextLevelConnectedCells.length != 0) {
|
||||
sorterEntry.medianValue = this.medianValue(
|
||||
nextLevelConnectedCells, nextRankValue);
|
||||
medianValues.push(sorterEntry);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Nodes with no adjacent vertices are flagged in the reserved array
|
||||
// to indicate they should be left in their current position.
|
||||
reservedPositions[cell.getGeneralPurposeVariable(rankValue)] = true;
|
||||
|
@ -557,10 +490,8 @@ medianRank = (rankValue, downwardSweep)=>
|
|||
|
||||
// Set the new position of each node within the rank using
|
||||
// its temp variable
|
||||
for (var i = 0; i < numCellsForRank; i++)
|
||||
{
|
||||
if (reservedPositions[i] == null)
|
||||
{
|
||||
for (var i = 0; i < numCellsForRank; i++) {
|
||||
if (reservedPositions[i] == null) {
|
||||
var cell = medianValues.shift().cell;
|
||||
cell.setGeneralPurposeVariable(rankValue, i);
|
||||
}
|
||||
|
@ -580,32 +511,27 @@ medianRank = (rankValue, downwardSweep)=>
|
|||
* specified cell
|
||||
* rankValue - the rank that the connected cell lie upon
|
||||
*/
|
||||
medianValue = (connectedCells, rankValue)=>
|
||||
{
|
||||
medianValue = (connectedCells, rankValue) => {
|
||||
var medianValues = [];
|
||||
var arrayCount = 0;
|
||||
|
||||
for (var i = 0; i < connectedCells.length; i++)
|
||||
{
|
||||
for (var i = 0; i < connectedCells.length; i++) {
|
||||
var cell = connectedCells[i];
|
||||
medianValues[arrayCount++] = cell.getGeneralPurposeVariable(rankValue);
|
||||
}
|
||||
|
||||
// Sort() sorts lexicographically by default (i.e. 11 before 9) so force
|
||||
// numerical order sort
|
||||
medianValues.sort((a,b)=>{return a - b;});
|
||||
medianValues.sort((a, b) => {
|
||||
return a - b;
|
||||
});
|
||||
|
||||
if (arrayCount % 2 == 1)
|
||||
{
|
||||
if (arrayCount % 2 == 1) {
|
||||
// For odd numbers of adjacent vertices return the median
|
||||
return medianValues[Math.floor(arrayCount / 2)];
|
||||
}
|
||||
else if (arrayCount == 2)
|
||||
{
|
||||
} else if (arrayCount == 2) {
|
||||
return ((medianValues[0] + medianValues[1]) / 2.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
var medianPoint = arrayCount / 2;
|
||||
var leftMedian = medianValues[medianPoint - 1] - medianValues[0];
|
||||
var rightMedian = medianValues[arrayCount - 1]
|
||||
|
@ -627,8 +553,9 @@ medianValue = (connectedCells, rankValue)=>
|
|||
*
|
||||
* Constructs a new median cell sorter.
|
||||
*/
|
||||
function MedianCellSorter()
|
||||
{
|
||||
function
|
||||
|
||||
MedianCellSorter() {
|
||||
// empty
|
||||
};
|
||||
|
||||
|
@ -651,25 +578,19 @@ cell = false;
|
|||
*
|
||||
* Compares two MedianCellSorters.
|
||||
*/
|
||||
compare = (a, b)=>
|
||||
{
|
||||
if (a != null && b != null)
|
||||
{
|
||||
if (b.medianValue > a.medianValue)
|
||||
{
|
||||
compare = (a, b) => {
|
||||
if (a != null && b != null) {
|
||||
if (b.medianValue > a.medianValue) {
|
||||
return -1;
|
||||
}
|
||||
else if (b.medianValue < a.medianValue)
|
||||
{
|
||||
} else if (b.medianValue < a.medianValue) {
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default mxMedianHybridCrossingReduction;
|
||||
|
|
|
@ -2,6 +2,15 @@
|
|||
* Copyright (c) 2006-2015, JGraph Ltd
|
||||
* Copyright (c) 2006-2015, Gaudenz Alder
|
||||
*/
|
||||
|
||||
class mxMinimumCycleRemover extends mxHierarchicalLayoutStage {
|
||||
/**
|
||||
* Variable: layout
|
||||
*
|
||||
* Reference to the enclosing <mxHierarchicalLayout>.
|
||||
*/
|
||||
layout = null;
|
||||
|
||||
/**
|
||||
* Class: mxMinimumCycleRemover
|
||||
*
|
||||
|
@ -12,24 +21,11 @@
|
|||
*
|
||||
* Creates a cycle remover for the given internal model.
|
||||
*/
|
||||
function mxMinimumCycleRemover(layout)
|
||||
{
|
||||
constructor(layout) {
|
||||
// constructor not called
|
||||
this.layout = layout;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extends mxHierarchicalLayoutStage.
|
||||
*/
|
||||
mxMinimumCycleRemover.prototype = new mxHierarchicalLayoutStage();
|
||||
constructor = mxMinimumCycleRemover;
|
||||
|
||||
/**
|
||||
* Variable: layout
|
||||
*
|
||||
* Reference to the enclosing <mxHierarchicalLayout>.
|
||||
*/
|
||||
layout = null;
|
||||
|
||||
/**
|
||||
* Function: execute
|
||||
*
|
||||
|
@ -37,15 +33,13 @@ layout = null;
|
|||
* and creates the resulting laid out graph within that facade for further
|
||||
* use.
|
||||
*/
|
||||
execute = (parent)=>
|
||||
{
|
||||
execute = (parent) => {
|
||||
var model = this.layout.getModel();
|
||||
var seenNodes = new Object();
|
||||
var unseenNodesArray = model.vertexMapper.getValues();
|
||||
var unseenNodes = new Object();
|
||||
|
||||
for (var i = 0; i < unseenNodesArray.length; i++)
|
||||
{
|
||||
for (var i = 0; i < unseenNodesArray.length; i++) {
|
||||
unseenNodes[unseenNodesArray[i].id] = unseenNodesArray[i];
|
||||
}
|
||||
|
||||
|
@ -53,24 +47,20 @@ execute = (parent)=>
|
|||
// reverse it.
|
||||
var rootsArray = null;
|
||||
|
||||
if (model.roots != null)
|
||||
{
|
||||
if (model.roots != null) {
|
||||
var modelRoots = model.roots;
|
||||
rootsArray = [];
|
||||
|
||||
for (var i = 0; i < modelRoots.length; i++)
|
||||
{
|
||||
for (var i = 0; i < modelRoots.length; i++) {
|
||||
rootsArray[i] = model.vertexMapper.get(modelRoots[i]);
|
||||
}
|
||||
}
|
||||
|
||||
model.visit((parent, node, connectingEdge, layer, seen)=>
|
||||
{
|
||||
model.visit((parent, node, connectingEdge, layer, seen) => {
|
||||
// Check if the cell is in it's own ancestor list, if so
|
||||
// invert the connecting edge and reverse the target/source
|
||||
// relationship to that edge in the parent and the cell
|
||||
if (node.isAncestor(parent))
|
||||
{
|
||||
if (node.isAncestor(parent)) {
|
||||
connectingEdge.invert();
|
||||
mxUtils.remove(connectingEdge, parent.connectsAsSource);
|
||||
parent.connectsAsTarget.push(connectingEdge);
|
||||
|
@ -88,13 +78,11 @@ execute = (parent)=>
|
|||
var seenNodesCopy = mxUtils.clone(seenNodes, null, true);
|
||||
|
||||
// Pick a random cell and dfs from it
|
||||
model.visit((parent, node, connectingEdge, layer, seen)=>
|
||||
{
|
||||
model.visit((parent, node, connectingEdge, layer, seen) => {
|
||||
// Check if the cell is in it's own ancestor list, if so
|
||||
// invert the connecting edge and reverse the target/source
|
||||
// relationship to that edge in the parent and the cell
|
||||
if (node.isAncestor(parent))
|
||||
{
|
||||
if (node.isAncestor(parent)) {
|
||||
connectingEdge.invert();
|
||||
mxUtils.remove(connectingEdge, parent.connectsAsSource);
|
||||
node.connectsAsSource.push(connectingEdge);
|
||||
|
@ -106,3 +94,6 @@ execute = (parent)=>
|
|||
delete unseenNodes[node.id];
|
||||
}, unseenNodes, true, seenNodesCopy);
|
||||
};
|
||||
}
|
||||
|
||||
export default mxMinimumCycleRemover;
|
|
@ -2,6 +2,15 @@
|
|||
* Copyright (c) 2006-2015, JGraph Ltd
|
||||
* Copyright (c) 2006-2015, Gaudenz Alder
|
||||
*/
|
||||
|
||||
class mxSwimlaneOrdering extends mxHierarchicalLayoutStage {
|
||||
/**
|
||||
* Variable: layout
|
||||
*
|
||||
* Reference to the enclosing <mxHierarchicalLayout>.
|
||||
*/
|
||||
layout = null;
|
||||
|
||||
/**
|
||||
* Class: mxSwimlaneOrdering
|
||||
*
|
||||
|
@ -12,24 +21,11 @@
|
|||
*
|
||||
* Creates a cycle remover for the given internal model.
|
||||
*/
|
||||
function mxSwimlaneOrdering(layout)
|
||||
{
|
||||
constructor(layout) {
|
||||
// super not called
|
||||
this.layout = layout;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extends mxHierarchicalLayoutStage.
|
||||
*/
|
||||
mxSwimlaneOrdering.prototype = new mxHierarchicalLayoutStage();
|
||||
constructor = mxSwimlaneOrdering;
|
||||
|
||||
/**
|
||||
* Variable: layout
|
||||
*
|
||||
* Reference to the enclosing <mxHierarchicalLayout>.
|
||||
*/
|
||||
layout = null;
|
||||
|
||||
/**
|
||||
* Function: execute
|
||||
*
|
||||
|
@ -37,8 +33,7 @@ layout = null;
|
|||
* and creates the resulting laid out graph within that facade for further
|
||||
* use.
|
||||
*/
|
||||
execute = (parent)=>
|
||||
{
|
||||
execute = (parent) => {
|
||||
var model = this.layout.getModel();
|
||||
var seenNodes = new Object();
|
||||
var unseenNodes = mxUtils.clone(model.vertexMapper, null, true);
|
||||
|
@ -47,19 +42,16 @@ execute = (parent)=>
|
|||
// reverse it.
|
||||
var rootsArray = null;
|
||||
|
||||
if (model.roots != null)
|
||||
{
|
||||
if (model.roots != null) {
|
||||
var modelRoots = model.roots;
|
||||
rootsArray = [];
|
||||
|
||||
for (var i = 0; i < modelRoots.length; i++)
|
||||
{
|
||||
for (var i = 0; i < modelRoots.length; i++) {
|
||||
rootsArray[i] = model.vertexMapper.get(modelRoots[i]);
|
||||
}
|
||||
}
|
||||
|
||||
model.visit((parent, node, connectingEdge, layer, seen)=>
|
||||
{
|
||||
model.visit((parent, node, connectingEdge, layer, seen) => {
|
||||
// Check if the cell is in it's own ancestor list, if so
|
||||
// invert the connecting edge and reverse the target/source
|
||||
// relationship to that edge in the parent and the cell
|
||||
|
@ -71,16 +63,13 @@ execute = (parent)=>
|
|||
var reversedOverSwimlane = parent != null && connectingEdge != null &&
|
||||
parent.swimlaneIndex < node.swimlaneIndex && connectingEdge.source == node;
|
||||
|
||||
if (isAncestor)
|
||||
{
|
||||
if (isAncestor) {
|
||||
connectingEdge.invert();
|
||||
mxUtils.remove(connectingEdge, parent.connectsAsSource);
|
||||
node.connectsAsSource.push(connectingEdge);
|
||||
parent.connectsAsTarget.push(connectingEdge);
|
||||
mxUtils.remove(connectingEdge, node.connectsAsTarget);
|
||||
}
|
||||
else if (reversedOverSwimlane)
|
||||
{
|
||||
} else if (reversedOverSwimlane) {
|
||||
connectingEdge.invert();
|
||||
mxUtils.remove(connectingEdge, parent.connectsAsTarget);
|
||||
node.connectsAsTarget.push(connectingEdge);
|
||||
|
@ -93,3 +82,6 @@ execute = (parent)=>
|
|||
delete unseenNodes[cellId];
|
||||
}, rootsArray, true, null);
|
||||
};
|
||||
}
|
||||
|
||||
export default mxSwimlaneOrdering;
|
||||
|
|
|
@ -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