blob: de82c53dc972903db961a5de5c5bef75993ae7c6 [file] [log] [blame]
/**
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
* Class: mxCircleLayout
*
* Extends <mxGraphLayout> to implement a circluar layout for a given radius.
* The vertices do not need to be connected for this layout to work and all
* connections between vertices are not taken into account.
*
* Example:
*
* (code)
* var layout = new mxCircleLayout(graph);
* layout.execute(graph.getDefaultParent());
* (end)
*
* Constructor: mxCircleLayout
*
* Constructs a new circular layout for the specified radius.
*
* Arguments:
*
* graph - <mxGraph> that contains the cells.
* radius - Optional radius as an int. Default is 100.
*/
function mxCircleLayout(graph, radius)
{
mxGraphLayout.call(this, graph);
this.radius = (radius != null) ? radius : 100;
};
/**
* Extends mxGraphLayout.
*/
mxCircleLayout.prototype = new mxGraphLayout();
mxCircleLayout.prototype.constructor = mxCircleLayout;
/**
* Variable: radius
*
* Integer specifying the size of the radius. Default is 100.
*/
mxCircleLayout.prototype.radius = null;
/**
* Variable: moveCircle
*
* Boolean specifying if the circle should be moved to the top,
* left corner specified by <x0> and <y0>. Default is false.
*/
mxCircleLayout.prototype.moveCircle = false;
/**
* Variable: x0
*
* Integer specifying the left coordinate of the circle.
* Default is 0.
*/
mxCircleLayout.prototype.x0 = 0;
/**
* Variable: y0
*
* Integer specifying the top coordinate of the circle.
* Default is 0.
*/
mxCircleLayout.prototype.y0 = 0;
/**
* Variable: resetEdges
*
* Specifies if all edge points of traversed edges should be removed.
* Default is true.
*/
mxCircleLayout.prototype.resetEdges = true;
/**
* Variable: disableEdgeStyle
*
* Specifies if the STYLE_NOEDGESTYLE flag should be set on edges that are
* modified by the result. Default is true.
*/
mxCircleLayout.prototype.disableEdgeStyle = true;
/**
* Function: execute
*
* Implements <mxGraphLayout.execute>.
*/
mxCircleLayout.prototype.execute = function(parent)
{
var model = this.graph.getModel();
// Moves the vertices to build a circle. Makes sure the
// radius is large enough for the vertices to not
// overlap
model.beginUpdate();
try
{
// Gets all vertices inside the parent and finds
// the maximum dimension of the largest vertex
var max = 0;
var top = null;
var left = null;
var vertices = [];
var childCount = model.getChildCount(parent);
for (var i = 0; i < childCount; i++)
{
var cell = model.getChildAt(parent, i);
if (!this.isVertexIgnored(cell))
{
vertices.push(cell);
var bounds = this.getVertexBounds(cell);
if (top == null)
{
top = bounds.y;
}
else
{
top = Math.min(top, bounds.y);
}
if (left == null)
{
left = bounds.x;
}
else
{
left = Math.min(left, bounds.x);
}
max = Math.max(max, Math.max(bounds.width, bounds.height));
}
else if (!this.isEdgeIgnored(cell))
{
// Resets the points on the traversed edge
if (this.resetEdges)
{
this.graph.resetEdge(cell);
}
if (this.disableEdgeStyle)
{
this.setEdgeStyleEnabled(cell, false);
}
}
}
var r = this.getRadius(vertices.length, max);
// Moves the circle to the specified origin
if (this.moveCircle)
{
left = this.x0;
top = this.y0;
}
this.circle(vertices, r, left, top);
}
finally
{
model.endUpdate();
}
};
/**
* Function: getRadius
*
* Returns the radius to be used for the given vertex count. Max is the maximum
* width or height of all vertices in the layout.
*/
mxCircleLayout.prototype.getRadius = function(count, max)
{
return Math.max(count * max / Math.PI, this.radius);
};
/**
* Function: circle
*
* Executes the circular layout for the specified array
* of vertices and the given radius. This is called from
* <execute>.
*/
mxCircleLayout.prototype.circle = function(vertices, r, left, top)
{
var vertexCount = vertices.length;
var phi = 2 * Math.PI / vertexCount;
for (var i = 0; i < vertexCount; i++)
{
if (this.isVertexMovable(vertices[i]))
{
this.setVertexLocation(vertices[i],
left + r + r * Math.sin(i*phi),
top + r + r * Math.cos(i*phi));
}
}
};