package flare.vis.data {
import flare.vis.events.DataEvent;
/**
* Data structure for managing a collection of visual data objects in a
* tree (hierarchy) structure. This class extends the functionality of
* the Data class to model a hierarchy. The class can be used as an
* alternative to the Data class when the data forms a strict tree, or to
* model a spanning tree over a general graph.
*/
public class Tree extends Data
{
/**
* Sets the root node of this tree. This property can only be set
* when the tree does not yet have a root node. */
public override function set root(n:NodeSprite):void {
if (_root == null) {
super.addNode(n); _root = n; _root.parentIndex = -1;
addDescendants(_root);
} else {
throw new ArgumentError("Can't set root unless the tree is empty." +
"If you want to set an entirely new root, call clear() first.");
}
}
/** This property simply points back to this object. */
public override function get tree():Tree { return this; }
public override function set tree(t:Tree):void { /* do nothing */ }
// -- Methods ---------------------------------------------------------
/**
* Creates and returns a new root NodeSprite for the tree. If the
* tree already has a root, this method throws an error.
* @return the newly added root
*/
public function addRoot():NodeSprite
{
if (_root != null) throw new Error(
"addRoot can only be called on an empty tree!");
return (_root = super.addNode());
}
/**
* Add a node to the tree. If the child node argument is null,
* a new NodeSprite will be created. A new EdgeSprite connecting
* the nodes will be created. Returns the new child node.
* @param p the parent node
* @param c the child node. If null, a new child node is created.
* @return the new child node
*/
public function addChild(p:NodeSprite, c:NodeSprite=null):NodeSprite
{
if (!_nodes.contains(p)) {
throw new ArgumentError("Parent node must be in the tree!");
}
c = super.addNode(c);
var e:EdgeSprite = newEdge(p, c, directedEdges, null);
c.parentIndex = p.addChildEdge(e);
c.parentEdge = e;
super.addEdge(e);
return c;
}
/**
* Adds the given edge as a child edge between a node already
* in the tree and another node not yet in the tree.
* @param e the edge to add to the tree
* @return the newly added edge
*/
public function addChildEdge(e:EdgeSprite):EdgeSprite
{
var n1:NodeSprite = e.source, b1:Boolean = _nodes.contains(n1);
var n2:NodeSprite = e.target, b2:Boolean = _nodes.contains(n2);
if (b1 && b2) {
throw new ArgumentError("One node must not be in the tree");
}
if (!(b1 || b2))
throw new ArgumentError("One node must already be in the tree");
var p:NodeSprite = b1 ? n1 : n2;
var c:NodeSprite = b1 ? n2 : n1;
c.parentEdge = e;
c.parentIndex = p.addChildEdge(e);
super.addNode(c);
return super.addEdge(e);
}
/**
* Registers all tree descendants with this Tree instance.
* @param n a sub-tree root that has already been added to this Tree
*/
protected function addDescendants(n:NodeSprite):void
{
for (var i:int=0; i
* WARNING: this method causes connecting edges to be * reconfigured. If this tree is a spanning tree, this can cause havoc. *
* @param n the node to swap with its parent */ public function swapWithParent(n:NodeSprite):void { var p:NodeSprite = n.parentNode, gp:NodeSprite; var e:EdgeSprite, ge:EdgeSprite, idx:int; if (p==null) return; gp = p.parentNode; ge = p.parentEdge; idx = p.parentIndex; // swap parent edge e = n.parentEdge; p.removeChild(n); p.parentEdge = e; p.parentIndex = n.addChildEdge(e); // connect to grandparents if (gp==null) { n.parentIndex = -1; n.parentEdge = null; } else { if (ge.source == gp) { ge.target = n; } else { ge.source = n; } n.parentIndex = idx; n.parentEdge = ge; } } } // end of class Tree }