package flare.vis.operator.layout {
import flare.util.Orientation;
import flare.vis.data.NodeSprite;
import flash.geom.Rectangle;
/**
* Layout that places nodes in an icicle layout, distributing nodes
* evenly within the display bounds. To get a standard icicle view, set
* the nodes shape
property to Shapes.BLOCK
and
* hide all edges. By default, this operator will attempt to scale the
* layout to fit within the display bounds. By setting
* fitToBounds
false, the current depthSpacing
* will be preserved, allowing the layout to exceed the bounds along
* the depth dimension.
*/
public class IcicleTreeLayout extends Layout
{
private var _orient:String = Orientation.TOP_TO_BOTTOM; // orientation
private var _dspace:Number = 50; // the spacing between depth levels
private var _maxDepth:int = 0;
private var _vertical:Boolean = true;
/** Indicates if the layout should be scaled to fit in the bounds. */
public var fitToBounds:Boolean = true;
/** The orientation of the layout. */
public function get orientation():String { return _orient; }
public function set orientation(o:String):void { _orient = o; }
/** The space between successive depth levels of the tree. */
public function get depthSpacing():Number { return _dspace; }
public function set depthSpacing(s:Number):void { _dspace = s; }
// --------------------------------------------------------------------
/**
* Creates a new IcicleTreeLayout.
* @param orientation the orientation of the layout
*/
public function IcicleTreeLayout(orientation:String=null)
{
if (orientation) this.orientation = orientation;
}
/** @inheritDoc */
protected override function layout():void
{
// get bounds parameters
var root:NodeSprite = layoutRoot as NodeSprite;
var b:Rectangle = layoutBounds;
var bMin:Number, bMax:Number, dMax:Number, d:Number, dInc:Number;
switch (_orient) {
case Orientation.LEFT_TO_RIGHT:
case Orientation.RIGHT_TO_LEFT:
bMin = b.bottom;
bMax = b.top;
dMax = b.width;
_vertical = false;
break;
case Orientation.TOP_TO_BOTTOM:
case Orientation.BOTTOM_TO_TOP:
bMin = b.left;
bMax = b.right;
dMax = b.height;
_vertical = true;
break;
default:
throw new Error("Unrecognized orientation value");
}
switch (_orient) {
case Orientation.LEFT_TO_RIGHT:
d = b.left; dInc = _dspace; break;
case Orientation.TOP_TO_BOTTOM:
d = b.top; dInc = _dspace; break;
case Orientation.RIGHT_TO_LEFT:
d = b.right; dInc = -_dspace; break;
case Orientation.BOTTOM_TO_TOP:
d = b.bottom; dInc = -_dspace; break;
default:
throw new Error("Unrecognized orientation value");
}
// calculate depth and width
_maxDepth = 0;
firstPass(root, 0);
// scale the depth to fit as needed
if (fitToBounds && _maxDepth * _dspace > dMax) {
dInc *= dMax / (_maxDepth * _dspace);
}
// perform the layout
doLayout(root, bMin, bMax, d, dInc);
updateEdgePoints(_t);
}
private function firstPass(n:NodeSprite, d:int):Number
{
if (d > _maxDepth) _maxDepth = d;
var extent:Number = 0;
if (n.childDegree == 0 || !n.expanded) {
extent = 1;
} else {
for (var i:int=0; i 0 && !n.expanded) {
n.visitTreeDepthFirst(function(c:NodeSprite):void {
update(c, b1 + w/2, b1 + w/2, d+dInc, dInc, false);
});
} else {
for (var i:int=0; i