package flare.vis.operator.layout { import flare.animate.Transitioner; import flare.util.Shapes; import flare.util.Vectors; import flare.vis.data.DataSprite; import flare.vis.data.EdgeSprite; import flare.vis.data.NodeSprite; import flare.vis.operator.Operator; /** * Layout that routes edges in a graph so that they form groups, reducing * clutter. This operator requires that a tree structure (for example, a * computed spanning tree) be defined over the graph. The class also sets * all edges' shape property to Shapes.BSPLINE * and can optionally compute alpha values to improve edge * visibility. * *

The algorithm uses the tree path between two nodes to define control * points for routing a b-spline curve. The technique is adapted from * Danny Holten's work on * * Hierarchical Edge Bundles, InfoVis 2006.

*/ public class BundledEdgeRouter extends Operator { /** Determines how "tight" the edges are bundled. At 0, all edges are * unbundled straight lines. At 1, the edges bundle together tightly. * The default is 0.85. */ public var bundling:Number = 0.85; /** Removes the shared ancestor along a node path. */ public var removeSharedAncestor:Boolean = false; /** * Creates a new BundledEdgeRouter * @param bundling the tightness of edge bundles */ public function BundledEdgeRouter(bundling:Number=0.85, removeSharedAncestor:Boolean=false) { this.bundling = bundling; this.removeSharedAncestor = removeSharedAncestor; } /** @inheritDoc */ public override function operate(t:Transitioner=null):void { t = (t==null ? Transitioner.DEFAULT : t); var u:NodeSprite, v:NodeSprite, pu:NodeSprite, pv:NodeSprite; var p1:Vector. = new Vector.(); var p2:Vector. = new Vector.(); var d1:int, d2:int, o:Object; var ux:Number, uy:Number, dx:Number, dy:Number; // compute edge bundles for each (var e:EdgeSprite in visualization.data.edges) { u = e.source; p1.push(pu=u as NodeSprite); d1 = u.depth; v = e.target; p2.push(pv=v as NodeSprite); d2 = v.depth; // trace paths to the least common ancestor of u,v while (d1 > d2) { p1.push(pu=pu.parentNode); --d1; } while (d2 > d1) { p2.push(pv=pv.parentNode); --d2; } while (pu != pv) { p1.push(pu=pu.parentNode); p2.push(pv=pv.parentNode); } // collect b-spline control points var p:Vector. = t.$(e).points; if (p==null) { p = new Vector.; } else { Vectors.clear(p); } d1 = p1.length; d2 = p2.length; if ((d1+d2)==4 && d1>1 && d2>1) { // shared parent addPoint(p, p1[1], t); } else { var off:int = removeSharedAncestor ? 1 : 0; for (var i:int=1; i=1;) addPoint(p, p2[i], t); } // set the bundling strength by adjusting control points var b:Number = bundling, ib:Number = 1-b, N:int = p.length; if (b < 1) { o = t.$(u); ux = o.x; uy = o.y; o = t.$(v); dx = o.x; dy = o.y; dx = (dx-ux)/(N+2); dy = (dy-uy)/(N+2); for (i=0; i, d:DataSprite, t:Transitioner):void { var o:Object = t.$(d); p.push(o.x); p.push(o.y); } } // end of class BundledEdgeRouter }