123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- /**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
- /**
- * Class: mxRadialTreeLayout
- *
- * Extends <mxGraphLayout> to implement a radial tree algorithm. This
- * layout is suitable for graphs that have no cycles (trees). Vertices that are
- * not connected to the tree will be ignored by this layout.
- *
- * Example:
- *
- * (code)
- * var layout = new mxRadialTreeLayout(graph);
- * layout.execute(graph.getDefaultParent());
- * (end)
- *
- * Constructor: mxRadialTreeLayout
- *
- * Constructs a new radial tree layout for the specified graph
- */
- function mxRadialTreeLayout(graph)
- {
- mxCompactTreeLayout.call(this, graph , false);
- };
- /**
- * Extends mxGraphLayout.
- */
- mxUtils.extend(mxRadialTreeLayout, mxCompactTreeLayout);
- /**
- * Variable: angleOffset
- *
- * The initial offset to compute the angle position.
- */
- mxRadialTreeLayout.prototype.angleOffset = 0.5;
- /**
- * Variable: rootx
- *
- * The X co-ordinate of the root cell
- */
- mxRadialTreeLayout.prototype.rootx = 0;
- /**
- * Variable: rooty
- *
- * The Y co-ordinate of the root cell
- */
- mxRadialTreeLayout.prototype.rooty = 0;
- /**
- * Variable: levelDistance
- *
- * Holds the levelDistance. Default is 120.
- */
- mxRadialTreeLayout.prototype.levelDistance = 120;
- /**
- * Variable: nodeDistance
- *
- * Holds the nodeDistance. Default is 10.
- */
- mxRadialTreeLayout.prototype.nodeDistance = 10;
- /**
- * Variable: autoRadius
- *
- * Specifies if the radios should be computed automatically
- */
- mxRadialTreeLayout.prototype.autoRadius = false;
- /**
- * Variable: sortEdges
- *
- * Specifies if edges should be sorted according to the order of their
- * opposite terminal cell in the model.
- */
- mxRadialTreeLayout.prototype.sortEdges = false;
- /**
- * Variable: rowMinX
- *
- * Array of leftmost x coordinate of each row
- */
- mxRadialTreeLayout.prototype.rowMinX = [];
- /**
- * Variable: rowMaxX
- *
- * Array of rightmost x coordinate of each row
- */
- mxRadialTreeLayout.prototype.rowMaxX = [];
- /**
- * Variable: rowMinCenX
- *
- * Array of x coordinate of leftmost vertex of each row
- */
- mxRadialTreeLayout.prototype.rowMinCenX = [];
- /**
- * Variable: rowMaxCenX
- *
- * Array of x coordinate of rightmost vertex of each row
- */
- mxRadialTreeLayout.prototype.rowMaxCenX = [];
- /**
- * Variable: rowRadi
- *
- * Array of y deltas of each row behind root vertex, also the radius in the tree
- */
- mxRadialTreeLayout.prototype.rowRadi = [];
- /**
- * Variable: row
- *
- * Array of vertices on each row
- */
- mxRadialTreeLayout.prototype.row = [];
- /**
- * Function: isVertexIgnored
- *
- * Returns a boolean indicating if the given <mxCell> should be ignored as a
- * vertex. This returns true if the cell has no connections.
- *
- * Parameters:
- *
- * vertex - <mxCell> whose ignored state should be returned.
- */
- mxRadialTreeLayout.prototype.isVertexIgnored = function(vertex)
- {
- return mxGraphLayout.prototype.isVertexIgnored.apply(this, arguments) ||
- this.graph.getConnections(vertex).length == 0;
- };
- /**
- * Function: execute
- *
- * Implements <mxGraphLayout.execute>.
- *
- * If the parent has any connected edges, then it is used as the root of
- * the tree. Else, <mxGraph.findTreeRoots> will be used to find a suitable
- * root node within the set of children of the given parent.
- *
- * Parameters:
- *
- * parent - <mxCell> whose children should be laid out.
- * root - Optional <mxCell> that will be used as the root of the tree.
- */
- mxRadialTreeLayout.prototype.execute = function(parent, root)
- {
- this.parent = parent;
-
- this.useBoundingBox = false;
- this.edgeRouting = false;
- //this.horizontal = false;
- mxCompactTreeLayout.prototype.execute.apply(this, arguments);
-
- var bounds = null;
- var rootBounds = this.getVertexBounds(this.root);
- this.centerX = rootBounds.x + rootBounds.width / 2;
- this.centerY = rootBounds.y + rootBounds.height / 2;
- // Calculate the bounds of the involved vertices directly from the values set in the compact tree
- for (var vertex in this.visited)
- {
- var vertexBounds = this.getVertexBounds(this.visited[vertex]);
- bounds = (bounds != null) ? bounds : vertexBounds.clone();
- bounds.add(vertexBounds);
- }
-
- this.calcRowDims([this.node], 0);
-
- var maxLeftGrad = 0;
- var maxRightGrad = 0;
- // Find the steepest left and right gradients
- for (var i = 0; i < this.row.length; i++)
- {
- var leftGrad = (this.centerX - this.rowMinX[i] - this.nodeDistance) / this.rowRadi[i];
- var rightGrad = (this.rowMaxX[i] - this.centerX - this.nodeDistance) / this.rowRadi[i];
-
- maxLeftGrad = Math.max (maxLeftGrad, leftGrad);
- maxRightGrad = Math.max (maxRightGrad, rightGrad);
- }
-
- // Extend out row so they meet the maximum gradient and convert to polar co-ords
- for (var i = 0; i < this.row.length; i++)
- {
- var xLeftLimit = this.centerX - this.nodeDistance - maxLeftGrad * this.rowRadi[i];
- var xRightLimit = this.centerX + this.nodeDistance + maxRightGrad * this.rowRadi[i];
- var fullWidth = xRightLimit - xLeftLimit;
-
- for (var j = 0; j < this.row[i].length; j ++)
- {
- var row = this.row[i];
- var node = row[j];
- var vertexBounds = this.getVertexBounds(node.cell);
- var xProportion = (vertexBounds.x + vertexBounds.width / 2 - xLeftLimit) / (fullWidth);
- var theta = 2 * Math.PI * xProportion;
- node.theta = theta;
- }
- }
- // Post-process from outside inwards to try to align parents with children
- for (var i = this.row.length - 2; i >= 0; i--)
- {
- var row = this.row[i];
-
- for (var j = 0; j < row.length; j++)
- {
- var node = row[j];
- var child = node.child;
- var counter = 0;
- var totalTheta = 0;
-
- while (child != null)
- {
- totalTheta += child.theta;
- counter++;
- child = child.next;
- }
-
- if (counter > 0)
- {
- var averTheta = totalTheta / counter;
-
- if (averTheta > node.theta && j < row.length - 1)
- {
- var nextTheta = row[j+1].theta;
- node.theta = Math.min (averTheta, nextTheta - Math.PI/10);
- }
- else if (averTheta < node.theta && j > 0 )
- {
- var lastTheta = row[j-1].theta;
- node.theta = Math.max (averTheta, lastTheta + Math.PI/10);
- }
- }
- }
- }
-
- // Set locations
- for (var i = 0; i < this.row.length; i++)
- {
- for (var j = 0; j < this.row[i].length; j ++)
- {
- var row = this.row[i];
- var node = row[j];
- var vertexBounds = this.getVertexBounds(node.cell);
- this.setVertexLocation(node.cell,
- this.centerX - vertexBounds.width / 2 + this.rowRadi[i] * Math.cos(node.theta),
- this.centerY - vertexBounds.height / 2 + this.rowRadi[i] * Math.sin(node.theta));
- }
- }
- };
- /**
- * Function: calcRowDims
- *
- * Recursive function to calculate the dimensions of each row
- *
- * Parameters:
- *
- * row - Array of internal nodes, the children of which are to be processed.
- * rowNum - Integer indicating which row is being processed.
- */
- mxRadialTreeLayout.prototype.calcRowDims = function(row, rowNum)
- {
- if (row == null || row.length == 0)
- {
- return;
- }
- // Place root's children proportionally around the first level
- this.rowMinX[rowNum] = this.centerX;
- this.rowMaxX[rowNum] = this.centerX;
- this.rowMinCenX[rowNum] = this.centerX;
- this.rowMaxCenX[rowNum] = this.centerX;
- this.row[rowNum] = [];
- var rowHasChildren = false;
- for (var i = 0; i < row.length; i++)
- {
- var child = row[i] != null ? row[i].child : null;
- while (child != null)
- {
- var cell = child.cell;
- var vertexBounds = this.getVertexBounds(cell);
-
- this.rowMinX[rowNum] = Math.min(vertexBounds.x, this.rowMinX[rowNum]);
- this.rowMaxX[rowNum] = Math.max(vertexBounds.x + vertexBounds.width, this.rowMaxX[rowNum]);
- this.rowMinCenX[rowNum] = Math.min(vertexBounds.x + vertexBounds.width / 2, this.rowMinCenX[rowNum]);
- this.rowMaxCenX[rowNum] = Math.max(vertexBounds.x + vertexBounds.width / 2, this.rowMaxCenX[rowNum]);
- this.rowRadi[rowNum] = vertexBounds.y - this.getVertexBounds(this.root).y;
-
- if (child.child != null)
- {
- rowHasChildren = true;
- }
-
- this.row[rowNum].push(child);
- child = child.next;
- }
- }
-
- if (rowHasChildren)
- {
- this.calcRowDims(this.row[rowNum], rowNum + 1);
- }
- };
- __mxOutput.mxRadialTreeLayout = typeof mxRadialTreeLayout !== 'undefined' ? mxRadialTreeLayout : undefined;
|