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