    package flare.vis.data.render
    {
        import flare.util.Geometry;
        import flare.util.Shapes;
        import flare.vis.data.DataSprite;
        import flare.vis.data.EdgeSprite;
        import flare.vis.data.NodeSprite;

        import flash.display.Graphics;
        import flash.geom.Point;
        import flash.geom.Rectangle;

        /**
         * Renderer that draws edges as lines. The EdgeRenderer supports straight
         * lines, poly lines, and curves as Bezier or cardinal splines. The type
         * of edge drawn is determined from an EdgeSprite's <code>shape</code>
         * property and control points found in the <code>points</code> property.
         * The line rendering properties are set by the <code>setLineStyle</code>
         * method, which can be overridden by subclasses. By default, the line
         * rendering settings are determined using default property values set
         * on this class (for example, the <code>scaleMode<code> and
         * <code>caps</code> properties).
         */
        public class EdgeRenderer implements IRenderer
        {
            private static const ROOT3:Number = Math.sqrt(3);

            private static var _instance:EdgeRenderer = new EdgeRenderer();
            /** Static EdgeRenderer instance. */
            public static function get instance():EdgeRenderer { return _instance; }

            /** Pixel hinting flag for line rendering. */
            public var pixelHinting:Boolean = false;
            /** Scale mode for line rendering (normal by default). */
            public var scaleMode:String = "normal";
            /** The joint style for line rendering. */
            public var joints:String = null;
            /** The caps style for line rendering. */
            public var caps:String = null;
            /** The miter limit for line rendering. */
            public var miterLimit:int = 3;

            // temporary variables
            private var _p:Point = new Point(), _q:Point = new Point();
            private var _pts:Array = new Array(20);

            /** @inheritDoc */
            public function render(d:DataSprite):void
            {
                var e:EdgeSprite = d as EdgeSprite;
                if (e == null) { return; } // TODO: throw exception?
                var s:NodeSprite = e.source;
                var t:NodeSprite = e.target;
                var g:Graphics = e.graphics;

                var ctrls:Array = e.points as Array;
                var x1:Number = e.x1, y1:Number = e.y1;
                var x2:Number = e.x2, y2:Number = e.y2;
                var xL:Number = ctrls==null ? x1 : ctrls[ctrls.length-2];
                var yL:Number = ctrls==null ? y1 : ctrls[ctrls.length-1];
                var dx:Number, dy:Number, dd:Number;

                // modify end points as needed to accomodate arrow
                if (e.arrowType != ArrowType.NONE)
                {
                    // determine arrow head size
                    var ah:Number = e.arrowHeight, aw:Number = e.arrowWidth/2;
                    if (ah < 0 && aw < 0)
                        aw = 1.5 * e.lineWidth;
                    if (ah < 0) {
                        ah = ROOT3 * aw;
                    } else if (aw < 0) {
                        aw = ah / ROOT3;
                    }

                    // get arrow tip point as intersection of edge with bounding box
                    if (t==null) {
                        _p.x = x2; _p.y = y2;
                    } else {
                        var r:Rectangle = t.getBounds(t.parent);
                        if (Geometry.intersectLineRect(xL,yL,x2,y2, r, _p,_q) <= 0)
                        {
                            _p.x = x2; _p.y = y2;
                        }
                    }

                    // get unit vector along arrow line
                    dx = _p.x - xL; dy = _p.y - yL;
                    dd = Math.sqrt(dx*dx + dy*dy);
                    dx /=