package flare.vis.operator.layout { import flare.util.Shapes; import flare.util.Sort; import flare.vis.data.Data; import flare.vis.data.NodeSprite; import flare.vis.data.render.ShapeRenderer; import flash.geom.Rectangle; /** * Layout that places nodes as circles compacted into a larger circle. * *
Circle sizes are determined by a node's size
property.
* It is assumed that the sizes are set before this operator is run,
* for example, by placing a SizeEncoder
prior to this layout
* in an operator list.
If the treeLayout
property is false
, all
* nodes will be treated the same and the result will be a "bubble" chart.
* If the treeLayout
property is
true
, circles will
* be nested inside each other according to the tree structure of the data.
*
The results of this layout can vary dramatically based on the sort
* order of the nodes. For example, sorting the nodes by the
* size
property (in either ascending or descending order)
* can result in much cleaner layouts. Use the sort
property
* of this class to set a preferred sorting routine. By default, this
* operator will not perform any sorting.
NOTE: This operator will set a node's renderer
and
* shape
properties, overriding any previous values.
The algorithm used to perform the circle packing is adapted from * W. Wang, H. Wang, G. Dai, and H. Wang's * Visualization of large hierarchical data by circle packing, * ACM CHI 2006.
*/ public class CirclePackingLayout extends Layout { private var _sort:Sort = null; private var _order:int; private var _b:Rectangle = new Rectangle(); /** The data group to process. This setting will be ignored if tree mode * is set to true. */ public var group:String = Data.NODES; /** The amount of spacing between neighboring circles. * The default value is 4 pixels. */ public var spacing:Number = 4; /** Indicates if the view should be scaled to fit within the * display bounds. The default is true. */ public var fitInBounds:Boolean = true; /** Indicates if a tree layout (circles nested within circles) should * be computed. The default is false. Any data group settings will * be ignored if this operator uses a tree layout. */ public var treeLayout:Boolean = false; /** A sort criteria for ordering nodes in this layout. * Ordered nodes are placed in a spiral starting at the center. * The default is null, meaning no sorting is performed. */ public function get sort():Sort { return _sort; } public function set sort(s:*):void { _sort = s==null ? s : (s is Sort ? Sort(s) : new Sort(s)); } // -------------------------------------------------------------------- /** * Creates a new CirclePackingLayout. * @param spacing the minimum spacing between neighboring circles * @param treeLayout if true, a hierarchical circles-within-circles * layout will be peformed; if false (the default) all nodes will be * considered equally * @param sort a sort criteria for ordering nodes in the layout. * Ordered nodes are placed in a spiral starting at the center. */ public function CirclePackingLayout(spacing:Number=4, treeLayout:Boolean=false, sort:*=null) { this.spacing = spacing; this.treeLayout = treeLayout; this.sort = sort; } /** @inheritDoc */ protected override function layout():void { _order = 0; var data:Data = visualization.data; data.nodes.setProperty("shape", Shapes.CIRCLE, _t); data.nodes.setProperty("renderer", ShapeRenderer.instance, _t); // determine layout anchor from bounds var bounds:Rectangle = layoutBounds; _anchor.x = (bounds.left + bounds.right) / 2; _anchor.y = (bounds.top + bounds.bottom) / 2; // compute the circle packing(s) var radius:Number; if (treeLayout) { // perform hierarchial tree layout var root:NodeSprite = layoutRoot as NodeSprite; var cn:ChainNode = getChainNode(root); cn.x = 0; cn.y = 0; if (root.childDegree > 0) { radius = (cn.r = packTree(root)); _t.$(root).size = radius / ShapeRenderer.instance.defaultSize; } shiftTree(root, cn.x, cn.y); } else { // perform flat layout var list:Vector.