    package flare.vis.operator.distortion
    {
        import flash.geom.Rectangle;

        /**
         * Computes a graphical fisheye distortion of a graph view. This distortion
         * allocates more space to items near the layout anchor and less space to
         * items further away, magnifying space near the anchor and demagnifying
         * distant space in a continuous fashion.
         *
         * <p>
         * For more details on this form of transformation, see Manojit Sarkar and
         * Marc H. Brown, "Graphical Fisheye Views of Graphs", in Proceedings of
         * CHI'92, Human Factors in Computing Systems, p. 83-91, 1992. Available
         * online at <a href="http://citeseer.ist.psu.edu/sarkar92graphical.html">
         * http://citeseer.ist.psu.edu/sarkar92graphical.html</a>.
         * </p>
         */
        public class FisheyeDistortion extends Distortion
        {
            private var _dx:Number; // x distortion factor
            private var _dy:Number; // y distortion factor
            private var _ds:Number; // size distortion factor

            // --------------------------------------------------------------------

            /**
             * Create a new FisheyeDistortion with the given distortion factors
             * along the x and y directions.
             * @param dx the distortion factor along the x axis (0 for none)
             * @param dy the distortion factor along the y axis (0 for none)
             * @param ds the distortion factor to use for sizes (0 for none)
             */
            public function FisheyeDistortion(dx:Number=4, dy:Number=4, ds:Number=3) {
                _dx = dx;
                _dy = dy;
                _ds = ds;
                super(_dx>0, _dy>0, _ds>0);
            }

            /** @inheritDoc */
            protected override function xDistort(x:Number):Number
            {
                return fisheye(x, layoutAnchor.x, _dx, _b.left, _b.right);
            }

            /** @inheritDoc */
            protected override function yDistort(y:Number):Number
            {
                return fisheye(y, layoutAnchor.y, _dy, _b.top, _b.bottom);
            }

            /** @inheritDoc */
            protected override function sizeDistort(bb:Rectangle, x:Number, y:Number):Number
            {
                if (!distortX && !distortY) return 1;
                var fx:Number=1, fy:Number=1;
                var a:Number, min:Number, max:Number, v:Number;

                if (distortX) {
                    a = layoutAnchor.x;
                    min = bb.left;
                    max = bb.right;
                    v = Math.abs(min-a) > Math.abs(max-a) ? min : max;
                    if (v < _b.left || v > _b.right) v = (v==min ? max : min);
                    fx = fisheye(v, a, _dx, _b.left, _b.right);
                    fx = Math.abs(x-fx) / (max - min);
                }

                if (distortY) {
                    a = layoutAnchor.y;
                    min = bb.top;
                    max = bb.bottom;
                    v = Math.abs(min-a) > Math.abs(max-a) ? min : max;
                    if (v < _b.top || v > _b.bottom) v = (v==min ? max : min);
                    fy = fisheye(v, a, _dy, _b.top, _b.bottom);
                    fy = Math.abs(y-fy) / (max - min);
                }

                var sf:Number = (!distortY ? fx : (!distortX ? fy : Math.min(fx,fy)));
                return (!isFinite(sf) || isNaN(sf)) ? 1 : _ds * sf;
            }

            private function fisheye(x:Number, a:Number, d:Number,
                min:Number, max:Number) : Number
            {
                if (d == 0) return x;

                var left:Boolean = x<a;
                var v:Number, m:Number = (left ? a-min : max-a);
                if ( m == 0 ) m = max-min;
                v = Math.abs(x - a) / m;
                v = (d+1)/(d+(1/v));
                return (left?-1:1)*m*v + a;
            }

        } // end of class FisheyeDistortion
    }