package flare.vis.operator.distortion { import flare.animate.Transitioner; import flare.vis.axis.Axis; import flare.vis.axis.CartesianAxes; import flare.vis.data.Data; import flare.vis.data.DataSprite; import flare.vis.events.VisualizationEvent; import flare.vis.operator.layout.Layout; import flash.display.Sprite; import flash.geom.Point; import flash.geom.Rectangle; /** * Base class for distortion operators that adjust the position and size * of visual objects in a visualization. */ public class Distortion extends Layout { /** A bounding rectangle for storing the layout bounds. */ protected var _b:Rectangle; /** Flag indicating if x-coordinates should be distorted. */ public var distortX:Boolean; /** Flag indicating if y-coordinates should be distorted. */ public var distortY:Boolean; /** Flag indicating if sizes should be distorted. */ public var distortSize:Boolean; /** Flag indicating if axes should be distorted. */ public var distortAxes:Boolean; /** Flag indicating if the DataSprite.size field should be * distorted. If false (the default), the scaleX and scaleY properties * are used instead. */ public var useSizeField:Boolean = false; /** Flag indicating if the size or scale values should be reset to 1 * upon each invocation of the distortion. This avoids the need to * manually reset the size values on each update. The default value * is false. */ public var resetSize:Boolean = false; /** Flag indicating if distortion anchor points outside the layout * bounds should be considered by the distortion. If true, external * anchors will be mapped to nearest point on the border of the layout * bounds. */ public var anchorInBounds:Boolean = true; /** @inheritDoc */ public override function set layoutAnchor(p:Point):void { if (p != null && anchorInBounds) { var b:Rectangle = layoutBounds, x:Number, y:Number; x = (p.x < b.left ? b.left : (p.x > b.right ? b.right : p.x)); y = (p.y < b.top ? b.top : (p.y > b.bottom ? b.bottom : p.y)); p = new Point(x, y); } super.layoutAnchor = p; } // -------------------------------------------------------------------- /** * Creates a new Distortion. * @param distortX Flag indicating if x-coordinates should be distorted * @param distortY Flag indicating if y-coordinates should be distorted * @param distortSize Flag indicating is sizes should be distorted */ public function Distortion(distortX:Boolean=true, distortY:Boolean=true, distortSize:Boolean=true, distortAxes:Boolean=true) { this.distortX = distortX; this.distortY = distortY; this.distortSize = distortSize; this.distortAxes = distortAxes; } /** @inheritDoc */ public override function operate(t:Transitioner=null):void { _t = (t!=null ? t : Transitioner.DEFAULT); _b = layoutBounds; visualization.data.visit(distort, Data.NODES); _t = null; if (distortAxes && visualization.xyAxes) visualization.addEventListener( VisualizationEvent.UPDATE, axesDistort); } /** * Distortion method for processing a DataSprite. * @param d a DataSprite to distort */ protected function distort(d:DataSprite):void { var o:Object = _t.$(d), ss:Number; if (resetSize) { if (useSizeField) { o.size = 1; } else { o.scaleX = 1; o.scaleY = 1; } } var bb:Rectangle = _t.endBounds(d, d.parent); if (distortX) o.x = xDistort(o.x); if (distortY) o.y = yDistort(o.y); if (distortSize) { ss = sizeDistort(bb, o.x, o.y); if (useSizeField) { o.size *= ss; } else { o.scaleX *= ss; o.scaleY *= ss; } } } /** * Compute a distorted x-coordinate. * @param x the initial x-coordinate * @return the distorted s-coordinate */ protected function xDistort(x:Number):Number { // for sub-classes return x; } /** * Compute a distorted y-coordinate. * @param y the initial y-coordinate * @return the distorted y-coordinate */ protected function yDistort(y:Number):Number { // for sub-classes return y; } /** * Compute a distorted size value. * @param bb an object's bounding box * @param x the initial x-coordinate * @param y the initial y-coordinate * @return the distorted size value */ protected function sizeDistort(bb:Rectangle, x:Number, y:Number):Number { // for sub-classes return 1; } /** * Performs distortion of Cartesian axes. As axis layout is recomputed * after the operators have run, we must distort the axes in * a separate step. This is accomplished by adding an update listener * on the visualization that invokes the axis distortion after the * axis layout has completed. This method is registered as the * listener callback. * @param evt the visualization update event */ protected function axesDistort(evt:VisualizationEvent):void { _t = evt.transitioner; _t = (_t==null ? Transitioner.DEFAULT : _t); var axes:CartesianAxes = visualization.xyAxes; if (axes != null) { distortAxis(axes.xAxis, true, false); distortAxis(axes.yAxis, false, true); } visualization.removeEventListener( VisualizationEvent.UPDATE, axesDistort); _t = null; } private function distortAxis(axis:Axis, xb:Boolean, yb:Boolean):void { var i:int, o:Object; // distort the axis labels var labels:Sprite = axis.labels; for (i=0; i