📄 circlelayout.as
字号:
package flare.vis.operator.layout
{
import flare.util.Property;
import flare.vis.data.Data;
import flare.vis.data.DataList;
import flare.vis.data.NodeSprite;
import flare.vis.data.ScaleBinding;
import flash.geom.Point;
import flash.geom.Rectangle;
/**
* Layout that places items in a circular layout. This operator is quite
* flexible and offers a number of layout options:
* <ul>
* <li>By default, all items are arranged along the circumference of the
* circle using the sort order of the underlying data list.</li>
* <li>If a data field for either the radius or angle is provided, this
* layout will act as a radial scatter plot, using the data fields
* to determine the radius or angle values of the layout.</li>
* <li>If no data field is provided but the <code>treeLayout</code>
* property is set to <code>true</code>, the layout will use an
* underlying tree structure to layout the data. Leaf nodes will be
* placed along the circumference of the circle, but parent nodes will
* be placed in the interior. Also, the layout will add spacing to
* differentiate sibling groups along the circumference.</li>
* </ul>
*
* <p>The layout also supports mixes of the above modes. For example, if
* <code>treeLayout</code> is set to <code>true</code> and a data field for
* the radius is set, the angles in the layout will be determined as in
* a normal ciruclar tree layout, but the radius values will be derived
* using the data field.</p>
*/
public class CircleLayout extends Layout
{
/** The padding around the circumference of the circle, in pixels. */
public var padding:Number = 50;
/** The starting angle for the layout, in radians. */
public var startAngle:Number = Math.PI / 2;
/** The angular width of the layout, in radians (default is 2 pi). */
public var angleWidth:Number = 2 * Math.PI;
/** Flag indicating if tree structure should inform the layout. */
public var treeLayout:Boolean = false;
protected var _inner:Number = 0, _innerFrac:Number = NaN;
protected var _outer:Number;
protected var _group:String;
protected var _rField:Property;
protected var _aField:Property;
protected var _rBinding:ScaleBinding;
protected var _aBinding:ScaleBinding;
/** The starting (inner) radius at which to place items.
* Setting this value also overrides the
* <code>startRadiusFraction</code> property. */
public function get startRadius():Number { return _inner; }
public function set startRadius(r:Number):void {
_inner = r; _innerFrac = NaN;
}
/** The starting (inner) radius as a fraction of the outer radius.
* Setting this value also overrides the
* <code>startRadius</code> property. When this property is set to
* <code>NaN</code>, the current value of <code>startRadius</code>
* will be used directly. */
public function get startRadiusFraction():Number { return _innerFrac; }
public function set startRadiusFraction(f:Number):void {
_innerFrac = f;
}
/** The radius source property. */
public function get radiusField():String { return _rBinding.property; }
public function set radiusField(f:String):void { _rBinding.property = f; }
/** The angle source property. */
public function get angleField():String { return _aBinding.property; }
public function set angleField(f:String):void { _aBinding.property = f; }
/** The scale binding for the radius. */
public function get radiusScale():ScaleBinding { return _rBinding; }
public function set radiusScale(b:ScaleBinding):void {
if (_rBinding) {
if (!b.property) b.property = _rBinding.property;
if (!b.group) b.group = _rBinding.group;
if (!b.data) b.data = _rBinding.data;
}
_rBinding = b;
}
/** The scale binding for the angle. */
public function get angleScale():ScaleBinding { return _aBinding; }
public function set angleScale(b:ScaleBinding):void {
if (_aBinding) {
if (!b.property) b.property = _aBinding.property;
if (!b.group) b.group = _aBinding.group;
if (!b.data) b.data = _aBinding.data;
}
_aBinding = b;
}
// --------------------------------------------------------------------
/**
* Creates a new CircleLayout.
* @param radiusField optional data field to encode as radius length
* @param angleField optional data field to encode as angle
* @param treeLayout boolean flag indicating if any tree-structure in
* the data should be used to inform the layout
* @param group the data group to process. If tree layout is set to
* true, this value may get ignored.
*/
public function CircleLayout(
radiusField:String=null, angleField:String=null,
treeLayout:Boolean=false, group:String=Data.NODES)
{
layoutType = POLAR;
_group = group;
this.treeLayout = treeLayout;
_rBinding = new ScaleBinding();
_rBinding.group = _group;
_rBinding.property = radiusField;
_aBinding = new ScaleBinding();
_aBinding.group = _group;
_aBinding.property = angleField;
}
/** @inheritDoc */
public override function setup():void
{
if (visualization==null) return;
_rBinding.data = visualization.data;
_aBinding.data = visualization.data;
}
/** @inheritDoc */
protected override function layout():void
{
var list:DataList = visualization.data.group(_group);
var i:int = 0, N:int = list.length, dr:Number;
var visitor:Function = null;
// determine radius
var b:Rectangle = layoutBounds;
_outer = Math.min(b.width, b.height)/2 - padding;
_inner = isNaN(_innerFrac) ? _inner : _outer * _innerFrac;
// set the anchor point
var anchor:Point = layoutAnchor;
list.visit(function(n:NodeSprite):void { n.origin = anchor; });
// compute angles
if (_aBinding.property) {
// if angle property, get scale binding and do layout
_aBinding.updateBinding();
_aField = Property.$(_aBinding.property);
visitor = function(n:NodeSprite):void {
var f:Number = _aBinding.interpolate(_aField.getValue(n));
_t.$(n).angle = minAngle(n.angle,
startAngle - f*angleWidth);
};
} else if (treeLayout) {
// if tree mode, use tree order
setTreeAngles();
} else {
// if nothing use total sort order
i = 0;
visitor = function(n:NodeSprite):void {
_t.$(n).angle = minAngle(n.angle,
startAngle - (i/N)*angleWidth);
i++;
};
}
if (visitor != null) list.visit(visitor);
// compute radii
visitor = null;
if (_rBinding.property) {
// if radius property, get scale binding and do layout
_rBinding.updateBinding();
_rField = Property.$(_rBinding.property);
dr = _outer - _inner;
visitor = function(n:NodeSprite):void {
var f:Number = _rBinding.interpolate(_rField.getValue(n));
_t.$(n).radius = _inner + f * dr;
};
} else if (treeLayout) {
// if tree-mode, use tree depth
setTreeRadii();
} else {
// if nothing, use outer radius
visitor = function(n:NodeSprite):void {
_t.$(n).radius = _outer;
};
}
if (visitor != null) list.visit(visitor);
if (treeLayout) _t.$(visualization.data.tree.root).radius = 0;
// finish up
updateEdgePoints(_t);
}
private function setTreeAngles():void
{
// first pass, determine the angular spacing
var root:NodeSprite = visualization.tree.root, p:NodeSprite = null;
var leafCount:int = 0, parentCount:int = 0;
root.visitTreeDepthFirst(function(n:NodeSprite):void {
if (n.childDegree == 0) {
if (p != n.parentNode) {
p = n.parentNode;
++parentCount;
}
++leafCount;
}
});
var inc:Number = (-angleWidth) / (leafCount + parentCount);
var angle:Number = startAngle;
// second pass, set the angles
root.visitTreeDepthFirst(function(n:NodeSprite):void {
var a:Number = 0, b:Number;
if (n.childDegree == 0) {
if (p != n.parentNode) {
p = n.parentNode;
angle += inc;
}
a = angle;
angle += inc;
} else if (n.parent != null) {
a = _t.$(n.firstChildNode).angle;
b = _t.$(n.lastChildNode).angle - a;
while (b > Math.PI) b -= 2*Math.PI;
while (b < -Math.PI) b += 2*Math.PI;
a += b / 2;
}
_t.$(n).angle = minAngle(n.angle, a);
});
}
private function setTreeRadii():void
{
var n:NodeSprite;
var depth:Number = 0, dr:Number = _outer - _inner;
for each (n in visualization.tree.nodes) {
if (n.childDegree == 0) {
depth = Math.max(n.depth, depth);
_t.$(n).radius = _outer;
}
}
for each (n in visualization.tree.nodes) {
if (n.childDegree != 0) {
_t.$(n).radius = _inner + (n.depth/depth) * dr;
}
}
n = visualization.tree.root;
if (!_t.immediate) {
delete _t._(n).values.radius;
delete _t._(n).values.angle;
}
_t.$(n).x = n.origin.x;
_t.$(n).y = n.origin.y;
}
} // end of class CircleLayout
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -